1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // SPI to CAN driver for the Texas Instruments TCAN4x5x |
3 | // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ |
4 | |
5 | #include "tcan4x5x.h" |
6 | |
7 | #define TCAN4X5X_EXT_CLK_DEF 40000000 |
8 | |
9 | #define TCAN4X5X_DEV_ID1 0x00 |
10 | #define TCAN4X5X_DEV_ID1_TCAN 0x4e414354 /* ASCII TCAN */ |
11 | #define TCAN4X5X_DEV_ID2 0x04 |
12 | #define TCAN4X5X_REV 0x08 |
13 | #define TCAN4X5X_STATUS 0x0C |
14 | #define TCAN4X5X_ERROR_STATUS_MASK 0x10 |
15 | #define TCAN4X5X_CONTROL 0x14 |
16 | |
17 | #define TCAN4X5X_CONFIG 0x800 |
18 | #define TCAN4X5X_TS_PRESCALE 0x804 |
19 | #define TCAN4X5X_TEST_REG 0x808 |
20 | #define TCAN4X5X_INT_FLAGS 0x820 |
21 | #define TCAN4X5X_MCAN_INT_REG 0x824 |
22 | #define TCAN4X5X_INT_EN 0x830 |
23 | |
24 | /* Interrupt bits */ |
25 | #define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30) |
26 | #define TCAN4X5X_CANHCANL_INT_EN BIT(29) |
27 | #define TCAN4X5X_CANHBAT_INT_EN BIT(28) |
28 | #define TCAN4X5X_CANLGND_INT_EN BIT(27) |
29 | #define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26) |
30 | #define TCAN4X5X_CANBUSGND_INT_EN BIT(25) |
31 | #define TCAN4X5X_CANBUSBAT_INT_EN BIT(24) |
32 | #define TCAN4X5X_UVSUP_INT_EN BIT(22) |
33 | #define TCAN4X5X_UVIO_INT_EN BIT(21) |
34 | #define TCAN4X5X_TSD_INT_EN BIT(19) |
35 | #define TCAN4X5X_ECCERR_INT_EN BIT(16) |
36 | #define TCAN4X5X_CANINT_INT_EN BIT(15) |
37 | #define TCAN4X5X_LWU_INT_EN BIT(14) |
38 | #define TCAN4X5X_CANSLNT_INT_EN BIT(10) |
39 | #define TCAN4X5X_CANDOM_INT_EN BIT(8) |
40 | #define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5) |
41 | #define TCAN4X5X_BUS_FAULT BIT(4) |
42 | #define TCAN4X5X_MCAN_INT BIT(1) |
43 | #define TCAN4X5X_ENABLE_TCAN_INT \ |
44 | (TCAN4X5X_MCAN_INT | TCAN4X5X_BUS_FAULT | \ |
45 | TCAN4X5X_CANBUS_ERR_INT_EN | TCAN4X5X_CANINT_INT_EN) |
46 | |
47 | /* MCAN Interrupt bits */ |
48 | #define TCAN4X5X_MCAN_IR_ARA BIT(29) |
49 | #define TCAN4X5X_MCAN_IR_PED BIT(28) |
50 | #define TCAN4X5X_MCAN_IR_PEA BIT(27) |
51 | #define TCAN4X5X_MCAN_IR_WD BIT(26) |
52 | #define TCAN4X5X_MCAN_IR_BO BIT(25) |
53 | #define TCAN4X5X_MCAN_IR_EW BIT(24) |
54 | #define TCAN4X5X_MCAN_IR_EP BIT(23) |
55 | #define TCAN4X5X_MCAN_IR_ELO BIT(22) |
56 | #define TCAN4X5X_MCAN_IR_BEU BIT(21) |
57 | #define TCAN4X5X_MCAN_IR_BEC BIT(20) |
58 | #define TCAN4X5X_MCAN_IR_DRX BIT(19) |
59 | #define TCAN4X5X_MCAN_IR_TOO BIT(18) |
60 | #define TCAN4X5X_MCAN_IR_MRAF BIT(17) |
61 | #define TCAN4X5X_MCAN_IR_TSW BIT(16) |
62 | #define TCAN4X5X_MCAN_IR_TEFL BIT(15) |
63 | #define TCAN4X5X_MCAN_IR_TEFF BIT(14) |
64 | #define TCAN4X5X_MCAN_IR_TEFW BIT(13) |
65 | #define TCAN4X5X_MCAN_IR_TEFN BIT(12) |
66 | #define TCAN4X5X_MCAN_IR_TFE BIT(11) |
67 | #define TCAN4X5X_MCAN_IR_TCF BIT(10) |
68 | #define TCAN4X5X_MCAN_IR_TC BIT(9) |
69 | #define TCAN4X5X_MCAN_IR_HPM BIT(8) |
70 | #define TCAN4X5X_MCAN_IR_RF1L BIT(7) |
71 | #define TCAN4X5X_MCAN_IR_RF1F BIT(6) |
72 | #define TCAN4X5X_MCAN_IR_RF1W BIT(5) |
73 | #define TCAN4X5X_MCAN_IR_RF1N BIT(4) |
74 | #define TCAN4X5X_MCAN_IR_RF0L BIT(3) |
75 | #define TCAN4X5X_MCAN_IR_RF0F BIT(2) |
76 | #define TCAN4X5X_MCAN_IR_RF0W BIT(1) |
77 | #define TCAN4X5X_MCAN_IR_RF0N BIT(0) |
78 | #define TCAN4X5X_ENABLE_MCAN_INT \ |
79 | (TCAN4X5X_MCAN_IR_TC | TCAN4X5X_MCAN_IR_RF0N | \ |
80 | TCAN4X5X_MCAN_IR_RF1N | TCAN4X5X_MCAN_IR_RF0F | \ |
81 | TCAN4X5X_MCAN_IR_RF1F) |
82 | |
83 | #define TCAN4X5X_MRAM_START 0x8000 |
84 | #define TCAN4X5X_MRAM_SIZE 0x800 |
85 | #define TCAN4X5X_MCAN_OFFSET 0x1000 |
86 | |
87 | #define TCAN4X5X_CLEAR_ALL_INT 0xffffffff |
88 | #define TCAN4X5X_SET_ALL_INT 0xffffffff |
89 | |
90 | #define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6)) |
91 | #define TCAN4X5X_MODE_SLEEP 0x00 |
92 | #define TCAN4X5X_MODE_STANDBY BIT(6) |
93 | #define TCAN4X5X_MODE_NORMAL BIT(7) |
94 | |
95 | #define TCAN4X5X_DISABLE_WAKE_MSK (BIT(31) | BIT(30)) |
96 | #define TCAN4X5X_DISABLE_INH_MSK BIT(9) |
97 | |
98 | #define TCAN4X5X_SW_RESET BIT(2) |
99 | |
100 | #define TCAN4X5X_MCAN_CONFIGURED BIT(5) |
101 | #define TCAN4X5X_WATCHDOG_EN BIT(3) |
102 | #define TCAN4X5X_WD_60_MS_TIMER 0 |
103 | #define TCAN4X5X_WD_600_MS_TIMER BIT(28) |
104 | #define TCAN4X5X_WD_3_S_TIMER BIT(29) |
105 | #define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29)) |
106 | |
107 | struct tcan4x5x_version_info { |
108 | const char *name; |
109 | u32 id2_register; |
110 | |
111 | bool has_wake_pin; |
112 | bool has_state_pin; |
113 | }; |
114 | |
115 | enum { |
116 | TCAN4552 = 0, |
117 | TCAN4553, |
118 | TCAN4X5X, |
119 | }; |
120 | |
121 | static const struct tcan4x5x_version_info tcan4x5x_versions[] = { |
122 | [TCAN4552] = { |
123 | .name = "4552" , |
124 | .id2_register = 0x32353534, |
125 | }, |
126 | [TCAN4553] = { |
127 | .name = "4553" , |
128 | .id2_register = 0x33353534, |
129 | }, |
130 | /* generic version with no id2_register at the end */ |
131 | [TCAN4X5X] = { |
132 | .name = "generic" , |
133 | .has_wake_pin = true, |
134 | .has_state_pin = true, |
135 | }, |
136 | }; |
137 | |
138 | static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev) |
139 | { |
140 | return container_of(cdev, struct tcan4x5x_priv, cdev); |
141 | } |
142 | |
143 | static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv) |
144 | { |
145 | int wake_state = 0; |
146 | |
147 | if (priv->device_state_gpio) |
148 | wake_state = gpiod_get_value(desc: priv->device_state_gpio); |
149 | |
150 | if (priv->device_wake_gpio && wake_state) { |
151 | gpiod_set_value(desc: priv->device_wake_gpio, value: 0); |
152 | usleep_range(min: 5, max: 50); |
153 | gpiod_set_value(desc: priv->device_wake_gpio, value: 1); |
154 | } |
155 | } |
156 | |
157 | static int tcan4x5x_reset(struct tcan4x5x_priv *priv) |
158 | { |
159 | int ret = 0; |
160 | |
161 | if (priv->reset_gpio) { |
162 | gpiod_set_value(desc: priv->reset_gpio, value: 1); |
163 | |
164 | /* tpulse_width minimum 30us */ |
165 | usleep_range(min: 30, max: 100); |
166 | gpiod_set_value(desc: priv->reset_gpio, value: 0); |
167 | } else { |
168 | ret = regmap_write(map: priv->regmap, TCAN4X5X_CONFIG, |
169 | TCAN4X5X_SW_RESET); |
170 | if (ret) |
171 | return ret; |
172 | } |
173 | |
174 | usleep_range(min: 700, max: 1000); |
175 | |
176 | return ret; |
177 | } |
178 | |
179 | static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg) |
180 | { |
181 | struct tcan4x5x_priv *priv = cdev_to_priv(cdev); |
182 | u32 val; |
183 | |
184 | regmap_read(map: priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val: &val); |
185 | |
186 | return val; |
187 | } |
188 | |
189 | static int tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset, |
190 | void *val, size_t val_count) |
191 | { |
192 | struct tcan4x5x_priv *priv = cdev_to_priv(cdev); |
193 | |
194 | return regmap_bulk_read(map: priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count); |
195 | } |
196 | |
197 | static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val) |
198 | { |
199 | struct tcan4x5x_priv *priv = cdev_to_priv(cdev); |
200 | |
201 | return regmap_write(map: priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val); |
202 | } |
203 | |
204 | static int tcan4x5x_write_fifo(struct m_can_classdev *cdev, |
205 | int addr_offset, const void *val, size_t val_count) |
206 | { |
207 | struct tcan4x5x_priv *priv = cdev_to_priv(cdev); |
208 | |
209 | return regmap_bulk_write(map: priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count); |
210 | } |
211 | |
212 | static int tcan4x5x_power_enable(struct regulator *reg, int enable) |
213 | { |
214 | if (IS_ERR_OR_NULL(ptr: reg)) |
215 | return 0; |
216 | |
217 | if (enable) |
218 | return regulator_enable(regulator: reg); |
219 | else |
220 | return regulator_disable(regulator: reg); |
221 | } |
222 | |
223 | static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev, |
224 | int reg, int val) |
225 | { |
226 | struct tcan4x5x_priv *priv = cdev_to_priv(cdev); |
227 | |
228 | return regmap_write(map: priv->regmap, reg, val); |
229 | } |
230 | |
231 | static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev) |
232 | { |
233 | int ret; |
234 | |
235 | ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_STATUS, |
236 | TCAN4X5X_CLEAR_ALL_INT); |
237 | if (ret) |
238 | return ret; |
239 | |
240 | return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, |
241 | TCAN4X5X_CLEAR_ALL_INT); |
242 | } |
243 | |
244 | static int tcan4x5x_init(struct m_can_classdev *cdev) |
245 | { |
246 | struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev); |
247 | int ret; |
248 | |
249 | tcan4x5x_check_wake(priv: tcan4x5x); |
250 | |
251 | ret = tcan4x5x_clear_interrupts(cdev); |
252 | if (ret) |
253 | return ret; |
254 | |
255 | ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_EN, |
256 | TCAN4X5X_ENABLE_TCAN_INT); |
257 | if (ret) |
258 | return ret; |
259 | |
260 | ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS_MASK, |
261 | TCAN4X5X_CLEAR_ALL_INT); |
262 | if (ret) |
263 | return ret; |
264 | |
265 | ret = regmap_update_bits(map: tcan4x5x->regmap, TCAN4X5X_CONFIG, |
266 | TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL); |
267 | if (ret) |
268 | return ret; |
269 | |
270 | return ret; |
271 | } |
272 | |
273 | static int tcan4x5x_disable_wake(struct m_can_classdev *cdev) |
274 | { |
275 | struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev); |
276 | |
277 | return regmap_update_bits(map: tcan4x5x->regmap, TCAN4X5X_CONFIG, |
278 | TCAN4X5X_DISABLE_WAKE_MSK, val: 0x00); |
279 | } |
280 | |
281 | static int tcan4x5x_disable_state(struct m_can_classdev *cdev) |
282 | { |
283 | struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev); |
284 | |
285 | return regmap_update_bits(map: tcan4x5x->regmap, TCAN4X5X_CONFIG, |
286 | TCAN4X5X_DISABLE_INH_MSK, val: 0x01); |
287 | } |
288 | |
289 | static const struct tcan4x5x_version_info |
290 | *tcan4x5x_find_version(struct tcan4x5x_priv *priv) |
291 | { |
292 | u32 val; |
293 | int ret; |
294 | |
295 | ret = regmap_read(map: priv->regmap, TCAN4X5X_DEV_ID1, val: &val); |
296 | if (ret) |
297 | return ERR_PTR(error: ret); |
298 | |
299 | if (val != TCAN4X5X_DEV_ID1_TCAN) { |
300 | dev_err(&priv->spi->dev, "Not a tcan device %x\n" , val); |
301 | return ERR_PTR(error: -ENODEV); |
302 | } |
303 | |
304 | ret = regmap_read(map: priv->regmap, TCAN4X5X_DEV_ID2, val: &val); |
305 | if (ret) |
306 | return ERR_PTR(error: ret); |
307 | |
308 | for (int i = 0; i != ARRAY_SIZE(tcan4x5x_versions); ++i) { |
309 | const struct tcan4x5x_version_info *vinfo = &tcan4x5x_versions[i]; |
310 | |
311 | if (!vinfo->id2_register || val == vinfo->id2_register) { |
312 | dev_info(&priv->spi->dev, "Detected TCAN device version %s\n" , |
313 | vinfo->name); |
314 | return vinfo; |
315 | } |
316 | } |
317 | |
318 | return &tcan4x5x_versions[TCAN4X5X]; |
319 | } |
320 | |
321 | static int tcan4x5x_get_gpios(struct m_can_classdev *cdev, |
322 | const struct tcan4x5x_version_info *version_info) |
323 | { |
324 | struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev); |
325 | int ret; |
326 | |
327 | if (version_info->has_wake_pin) { |
328 | tcan4x5x->device_wake_gpio = devm_gpiod_get(dev: cdev->dev, con_id: "device-wake" , |
329 | flags: GPIOD_OUT_HIGH); |
330 | if (IS_ERR(ptr: tcan4x5x->device_wake_gpio)) { |
331 | if (PTR_ERR(ptr: tcan4x5x->device_wake_gpio) == -EPROBE_DEFER) |
332 | return -EPROBE_DEFER; |
333 | |
334 | tcan4x5x_disable_wake(cdev); |
335 | } |
336 | } |
337 | |
338 | tcan4x5x->reset_gpio = devm_gpiod_get_optional(dev: cdev->dev, con_id: "reset" , |
339 | flags: GPIOD_OUT_LOW); |
340 | if (IS_ERR(ptr: tcan4x5x->reset_gpio)) |
341 | tcan4x5x->reset_gpio = NULL; |
342 | |
343 | ret = tcan4x5x_reset(priv: tcan4x5x); |
344 | if (ret) |
345 | return ret; |
346 | |
347 | if (version_info->has_state_pin) { |
348 | tcan4x5x->device_state_gpio = devm_gpiod_get_optional(dev: cdev->dev, |
349 | con_id: "device-state" , |
350 | flags: GPIOD_IN); |
351 | if (IS_ERR(ptr: tcan4x5x->device_state_gpio)) { |
352 | tcan4x5x->device_state_gpio = NULL; |
353 | tcan4x5x_disable_state(cdev); |
354 | } |
355 | } |
356 | |
357 | return 0; |
358 | } |
359 | |
360 | static struct m_can_ops tcan4x5x_ops = { |
361 | .init = tcan4x5x_init, |
362 | .read_reg = tcan4x5x_read_reg, |
363 | .write_reg = tcan4x5x_write_reg, |
364 | .write_fifo = tcan4x5x_write_fifo, |
365 | .read_fifo = tcan4x5x_read_fifo, |
366 | .clear_interrupts = tcan4x5x_clear_interrupts, |
367 | }; |
368 | |
369 | static int tcan4x5x_can_probe(struct spi_device *spi) |
370 | { |
371 | const struct tcan4x5x_version_info *version_info; |
372 | struct tcan4x5x_priv *priv; |
373 | struct m_can_classdev *mcan_class; |
374 | int freq, ret; |
375 | |
376 | mcan_class = m_can_class_allocate_dev(dev: &spi->dev, |
377 | sizeof_priv: sizeof(struct tcan4x5x_priv)); |
378 | if (!mcan_class) |
379 | return -ENOMEM; |
380 | |
381 | ret = m_can_check_mram_cfg(cdev: mcan_class, TCAN4X5X_MRAM_SIZE); |
382 | if (ret) |
383 | goto out_m_can_class_free_dev; |
384 | |
385 | priv = cdev_to_priv(cdev: mcan_class); |
386 | |
387 | priv->power = devm_regulator_get_optional(dev: &spi->dev, id: "vsup" ); |
388 | if (PTR_ERR(ptr: priv->power) == -EPROBE_DEFER) { |
389 | ret = -EPROBE_DEFER; |
390 | goto out_m_can_class_free_dev; |
391 | } else { |
392 | priv->power = NULL; |
393 | } |
394 | |
395 | m_can_class_get_clocks(cdev: mcan_class); |
396 | if (IS_ERR(ptr: mcan_class->cclk)) { |
397 | dev_err(&spi->dev, "no CAN clock source defined\n" ); |
398 | freq = TCAN4X5X_EXT_CLK_DEF; |
399 | } else { |
400 | freq = clk_get_rate(clk: mcan_class->cclk); |
401 | } |
402 | |
403 | /* Sanity check */ |
404 | if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) { |
405 | dev_err(&spi->dev, "Clock frequency is out of supported range %d\n" , |
406 | freq); |
407 | ret = -ERANGE; |
408 | goto out_m_can_class_free_dev; |
409 | } |
410 | |
411 | priv->spi = spi; |
412 | |
413 | mcan_class->pm_clock_support = 0; |
414 | mcan_class->pm_wake_source = device_property_read_bool(dev: &spi->dev, propname: "wakeup-source" ); |
415 | mcan_class->can.clock.freq = freq; |
416 | mcan_class->dev = &spi->dev; |
417 | mcan_class->ops = &tcan4x5x_ops; |
418 | mcan_class->is_peripheral = true; |
419 | mcan_class->net->irq = spi->irq; |
420 | |
421 | spi_set_drvdata(spi, data: priv); |
422 | |
423 | /* Configure the SPI bus */ |
424 | spi->bits_per_word = 8; |
425 | ret = spi_setup(spi); |
426 | if (ret) { |
427 | dev_err(&spi->dev, "SPI setup failed %pe\n" , ERR_PTR(ret)); |
428 | goto out_m_can_class_free_dev; |
429 | } |
430 | |
431 | ret = tcan4x5x_regmap_init(priv); |
432 | if (ret) { |
433 | dev_err(&spi->dev, "regmap init failed %pe\n" , ERR_PTR(ret)); |
434 | goto out_m_can_class_free_dev; |
435 | } |
436 | |
437 | ret = tcan4x5x_power_enable(reg: priv->power, enable: 1); |
438 | if (ret) { |
439 | dev_err(&spi->dev, "Enabling regulator failed %pe\n" , |
440 | ERR_PTR(ret)); |
441 | goto out_m_can_class_free_dev; |
442 | } |
443 | |
444 | version_info = tcan4x5x_find_version(priv); |
445 | if (IS_ERR(ptr: version_info)) { |
446 | ret = PTR_ERR(ptr: version_info); |
447 | goto out_power; |
448 | } |
449 | |
450 | ret = tcan4x5x_get_gpios(cdev: mcan_class, version_info); |
451 | if (ret) { |
452 | dev_err(&spi->dev, "Getting gpios failed %pe\n" , ERR_PTR(ret)); |
453 | goto out_power; |
454 | } |
455 | |
456 | ret = tcan4x5x_init(cdev: mcan_class); |
457 | if (ret) { |
458 | dev_err(&spi->dev, "tcan initialization failed %pe\n" , |
459 | ERR_PTR(ret)); |
460 | goto out_power; |
461 | } |
462 | |
463 | if (mcan_class->pm_wake_source) |
464 | device_init_wakeup(dev: &spi->dev, enable: true); |
465 | |
466 | ret = m_can_class_register(cdev: mcan_class); |
467 | if (ret) { |
468 | dev_err(&spi->dev, "Failed registering m_can device %pe\n" , |
469 | ERR_PTR(ret)); |
470 | goto out_power; |
471 | } |
472 | |
473 | netdev_info(dev: mcan_class->net, format: "TCAN4X5X successfully initialized.\n" ); |
474 | return 0; |
475 | |
476 | out_power: |
477 | tcan4x5x_power_enable(reg: priv->power, enable: 0); |
478 | out_m_can_class_free_dev: |
479 | m_can_class_free_dev(net: mcan_class->net); |
480 | return ret; |
481 | } |
482 | |
483 | static void tcan4x5x_can_remove(struct spi_device *spi) |
484 | { |
485 | struct tcan4x5x_priv *priv = spi_get_drvdata(spi); |
486 | |
487 | m_can_class_unregister(cdev: &priv->cdev); |
488 | |
489 | tcan4x5x_power_enable(reg: priv->power, enable: 0); |
490 | |
491 | m_can_class_free_dev(net: priv->cdev.net); |
492 | } |
493 | |
494 | static int __maybe_unused tcan4x5x_suspend(struct device *dev) |
495 | { |
496 | struct m_can_classdev *cdev = dev_get_drvdata(dev); |
497 | struct spi_device *spi = to_spi_device(dev); |
498 | |
499 | if (cdev->pm_wake_source) |
500 | enable_irq_wake(irq: spi->irq); |
501 | |
502 | return m_can_class_suspend(dev); |
503 | } |
504 | |
505 | static int __maybe_unused tcan4x5x_resume(struct device *dev) |
506 | { |
507 | struct m_can_classdev *cdev = dev_get_drvdata(dev); |
508 | struct spi_device *spi = to_spi_device(dev); |
509 | int ret = m_can_class_resume(dev); |
510 | |
511 | if (cdev->pm_wake_source) |
512 | disable_irq_wake(irq: spi->irq); |
513 | |
514 | return ret; |
515 | } |
516 | |
517 | static const struct of_device_id tcan4x5x_of_match[] = { |
518 | { |
519 | .compatible = "ti,tcan4x5x" , |
520 | }, { |
521 | /* sentinel */ |
522 | }, |
523 | }; |
524 | MODULE_DEVICE_TABLE(of, tcan4x5x_of_match); |
525 | |
526 | static const struct spi_device_id tcan4x5x_id_table[] = { |
527 | { |
528 | .name = "tcan4x5x" , |
529 | }, { |
530 | /* sentinel */ |
531 | }, |
532 | }; |
533 | MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table); |
534 | |
535 | static const struct dev_pm_ops tcan4x5x_pm_ops = { |
536 | SET_SYSTEM_SLEEP_PM_OPS(tcan4x5x_suspend, tcan4x5x_resume) |
537 | }; |
538 | |
539 | static struct spi_driver tcan4x5x_can_driver = { |
540 | .driver = { |
541 | .name = KBUILD_MODNAME, |
542 | .of_match_table = tcan4x5x_of_match, |
543 | .pm = &tcan4x5x_pm_ops, |
544 | }, |
545 | .id_table = tcan4x5x_id_table, |
546 | .probe = tcan4x5x_can_probe, |
547 | .remove = tcan4x5x_can_remove, |
548 | }; |
549 | module_spi_driver(tcan4x5x_can_driver); |
550 | |
551 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>" ); |
552 | MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver" ); |
553 | MODULE_LICENSE("GPL v2" ); |
554 | |