Commit | Line | Data |
---|---|---|
aaa7cb26 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2a367c3a WG |
2 | /* |
3 | * Core driver for the CC770 and AN82527 CAN controllers | |
4 | * | |
5 | * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> | |
2a367c3a WG |
6 | */ |
7 | ||
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/types.h> | |
15 | #include <linux/fcntl.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/ptrace.h> | |
18 | #include <linux/string.h> | |
19 | #include <linux/errno.h> | |
409c188c | 20 | #include <linux/ethtool.h> |
2a367c3a WG |
21 | #include <linux/netdevice.h> |
22 | #include <linux/if_arp.h> | |
23 | #include <linux/if_ether.h> | |
24 | #include <linux/skbuff.h> | |
25 | #include <linux/delay.h> | |
26 | ||
27 | #include <linux/can.h> | |
28 | #include <linux/can/dev.h> | |
29 | #include <linux/can/error.h> | |
2a367c3a WG |
30 | #include <linux/can/platform/cc770.h> |
31 | ||
32 | #include "cc770.h" | |
33 | ||
34 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | |
35 | MODULE_LICENSE("GPL v2"); | |
36 | MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver"); | |
37 | ||
38 | /* | |
39 | * The CC770 is a CAN controller from Bosch, which is 100% compatible | |
40 | * with the AN82527 from Intel, but with "bugs" being fixed and some | |
41 | * additional functionality, mainly: | |
42 | * | |
43 | * 1. RX and TX error counters are readable. | |
44 | * 2. Support of silent (listen-only) mode. | |
45 | * 3. Message object 15 can receive all types of frames, also RTR and EFF. | |
46 | * | |
47 | * Details are available from Bosch's "CC770_Product_Info_2007-01.pdf", | |
48 | * which explains in detail the compatibility between the CC770 and the | |
49 | * 82527. This driver use the additional functionality 3. on real CC770 | |
50 | * devices. Unfortunately, the CC770 does still not store the message | |
51 | * identifier of received remote transmission request frames and | |
52 | * therefore it's set to 0. | |
53 | * | |
54 | * The message objects 1..14 can be used for TX and RX while the message | |
55 | * objects 15 is optimized for RX. It has a shadow register for reliable | |
069f8457 | 56 | * data reception under heavy bus load. Therefore it makes sense to use |
2a367c3a WG |
57 | * this message object for the needed use case. The frame type (EFF/SFF) |
58 | * for the message object 15 can be defined via kernel module parameter | |
59 | * "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames, | |
60 | * otherwise 11 bit SFF messages. | |
61 | */ | |
62 | static int msgobj15_eff; | |
d61e4038 | 63 | module_param(msgobj15_eff, int, 0444); |
2a367c3a WG |
64 | MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 " |
65 | "(default: 11-bit standard frames)"); | |
66 | ||
67 | static int i82527_compat; | |
d61e4038 | 68 | module_param(i82527_compat, int, 0444); |
b7cc4f3e | 69 | MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 compatibility mode " |
2a367c3a WG |
70 | "without using additional functions"); |
71 | ||
72 | /* | |
73 | * This driver uses the last 5 message objects 11..15. The definitions | |
74 | * and structure below allows to configure and assign them to the real | |
75 | * message object. | |
76 | */ | |
77 | static unsigned char cc770_obj_flags[CC770_OBJ_MAX] = { | |
78 | [CC770_OBJ_RX0] = CC770_OBJ_FLAG_RX, | |
79 | [CC770_OBJ_RX1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_EFF, | |
80 | [CC770_OBJ_RX_RTR0] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR, | |
81 | [CC770_OBJ_RX_RTR1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR | | |
82 | CC770_OBJ_FLAG_EFF, | |
83 | [CC770_OBJ_TX] = 0, | |
84 | }; | |
85 | ||
194b9a4c | 86 | static const struct can_bittiming_const cc770_bittiming_const = { |
2a367c3a WG |
87 | .name = KBUILD_MODNAME, |
88 | .tseg1_min = 1, | |
89 | .tseg1_max = 16, | |
90 | .tseg2_min = 1, | |
91 | .tseg2_max = 8, | |
92 | .sjw_max = 4, | |
93 | .brp_min = 1, | |
94 | .brp_max = 64, | |
95 | .brp_inc = 1, | |
96 | }; | |
97 | ||
98 | static inline int intid2obj(unsigned int intid) | |
99 | { | |
100 | if (intid == 2) | |
101 | return 0; | |
102 | else | |
103 | return MSGOBJ_LAST + 2 - intid; | |
104 | } | |
105 | ||
106 | static void enable_all_objs(const struct net_device *dev) | |
107 | { | |
108 | struct cc770_priv *priv = netdev_priv(dev); | |
109 | u8 msgcfg; | |
110 | unsigned char obj_flags; | |
111 | unsigned int o, mo; | |
112 | ||
113 | for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) { | |
114 | obj_flags = priv->obj_flags[o]; | |
115 | mo = obj2msgobj(o); | |
116 | ||
117 | if (obj_flags & CC770_OBJ_FLAG_RX) { | |
118 | /* | |
119 | * We don't need extra objects for RTR and EFF if | |
120 | * the additional CC770 functions are enabled. | |
121 | */ | |
122 | if (priv->control_normal_mode & CTRL_EAF) { | |
123 | if (o > 0) | |
124 | continue; | |
125 | netdev_dbg(dev, "Message object %d for " | |
126 | "RX data, RTR, SFF and EFF\n", mo); | |
127 | } else { | |
128 | netdev_dbg(dev, | |
129 | "Message object %d for RX %s %s\n", | |
130 | mo, obj_flags & CC770_OBJ_FLAG_RTR ? | |
131 | "RTR" : "data", | |
132 | obj_flags & CC770_OBJ_FLAG_EFF ? | |
133 | "EFF" : "SFF"); | |
134 | } | |
135 | ||
136 | if (obj_flags & CC770_OBJ_FLAG_EFF) | |
137 | msgcfg = MSGCFG_XTD; | |
138 | else | |
139 | msgcfg = 0; | |
140 | if (obj_flags & CC770_OBJ_FLAG_RTR) | |
141 | msgcfg |= MSGCFG_DIR; | |
142 | ||
143 | cc770_write_reg(priv, msgobj[mo].config, msgcfg); | |
144 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
145 | MSGVAL_SET | TXIE_RES | | |
146 | RXIE_SET | INTPND_RES); | |
147 | ||
148 | if (obj_flags & CC770_OBJ_FLAG_RTR) | |
149 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
150 | NEWDAT_RES | CPUUPD_SET | | |
151 | TXRQST_RES | RMTPND_RES); | |
152 | else | |
153 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
154 | NEWDAT_RES | MSGLST_RES | | |
155 | TXRQST_RES | RMTPND_RES); | |
156 | } else { | |
157 | netdev_dbg(dev, "Message object %d for " | |
158 | "TX data, RTR, SFF and EFF\n", mo); | |
159 | ||
160 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
161 | RMTPND_RES | TXRQST_RES | | |
162 | CPUUPD_RES | NEWDAT_RES); | |
163 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
164 | MSGVAL_RES | TXIE_RES | | |
165 | RXIE_RES | INTPND_RES); | |
166 | } | |
167 | } | |
168 | } | |
169 | ||
170 | static void disable_all_objs(const struct cc770_priv *priv) | |
171 | { | |
172 | int o, mo; | |
173 | ||
174 | for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) { | |
175 | mo = obj2msgobj(o); | |
176 | ||
177 | if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) { | |
178 | if (o > 0 && priv->control_normal_mode & CTRL_EAF) | |
179 | continue; | |
180 | ||
181 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
182 | NEWDAT_RES | MSGLST_RES | | |
183 | TXRQST_RES | RMTPND_RES); | |
184 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
185 | MSGVAL_RES | TXIE_RES | | |
186 | RXIE_RES | INTPND_RES); | |
187 | } else { | |
188 | /* Clear message object for send */ | |
189 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
190 | RMTPND_RES | TXRQST_RES | | |
191 | CPUUPD_RES | NEWDAT_RES); | |
192 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
193 | MSGVAL_RES | TXIE_RES | | |
194 | RXIE_RES | INTPND_RES); | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | static void set_reset_mode(struct net_device *dev) | |
200 | { | |
201 | struct cc770_priv *priv = netdev_priv(dev); | |
202 | ||
203 | /* Enable configuration and puts chip in bus-off, disable interrupts */ | |
204 | cc770_write_reg(priv, control, CTRL_CCE | CTRL_INI); | |
205 | ||
206 | priv->can.state = CAN_STATE_STOPPED; | |
207 | ||
208 | /* Clear interrupts */ | |
209 | cc770_read_reg(priv, interrupt); | |
210 | ||
211 | /* Clear status register */ | |
212 | cc770_write_reg(priv, status, 0); | |
213 | ||
214 | /* Disable all used message objects */ | |
215 | disable_all_objs(priv); | |
216 | } | |
217 | ||
218 | static void set_normal_mode(struct net_device *dev) | |
219 | { | |
220 | struct cc770_priv *priv = netdev_priv(dev); | |
221 | ||
222 | /* Clear interrupts */ | |
223 | cc770_read_reg(priv, interrupt); | |
224 | ||
225 | /* Clear status register and pre-set last error code */ | |
226 | cc770_write_reg(priv, status, STAT_LEC_MASK); | |
227 | ||
228 | /* Enable all used message objects*/ | |
229 | enable_all_objs(dev); | |
230 | ||
231 | /* | |
232 | * Clear bus-off, interrupts only for errors, | |
233 | * not for status change | |
234 | */ | |
235 | cc770_write_reg(priv, control, priv->control_normal_mode); | |
236 | ||
237 | priv->can.state = CAN_STATE_ERROR_ACTIVE; | |
238 | } | |
239 | ||
240 | static void chipset_init(struct cc770_priv *priv) | |
241 | { | |
242 | int mo, id, data; | |
243 | ||
244 | /* Enable configuration and put chip in bus-off, disable interrupts */ | |
245 | cc770_write_reg(priv, control, (CTRL_CCE | CTRL_INI)); | |
246 | ||
247 | /* Set CLKOUT divider and slew rates */ | |
248 | cc770_write_reg(priv, clkout, priv->clkout); | |
249 | ||
250 | /* Configure CPU interface / CLKOUT enable */ | |
251 | cc770_write_reg(priv, cpu_interface, priv->cpu_interface); | |
252 | ||
253 | /* Set bus configuration */ | |
254 | cc770_write_reg(priv, bus_config, priv->bus_config); | |
255 | ||
256 | /* Clear interrupts */ | |
257 | cc770_read_reg(priv, interrupt); | |
258 | ||
259 | /* Clear status register */ | |
260 | cc770_write_reg(priv, status, 0); | |
261 | ||
262 | /* Clear and invalidate message objects */ | |
263 | for (mo = MSGOBJ_FIRST; mo <= MSGOBJ_LAST; mo++) { | |
264 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
265 | INTPND_UNC | RXIE_RES | | |
266 | TXIE_RES | MSGVAL_RES); | |
267 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
268 | INTPND_RES | RXIE_RES | | |
269 | TXIE_RES | MSGVAL_RES); | |
270 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
271 | NEWDAT_RES | MSGLST_RES | | |
272 | TXRQST_RES | RMTPND_RES); | |
273 | for (data = 0; data < 8; data++) | |
274 | cc770_write_reg(priv, msgobj[mo].data[data], 0); | |
275 | for (id = 0; id < 4; id++) | |
276 | cc770_write_reg(priv, msgobj[mo].id[id], 0); | |
277 | cc770_write_reg(priv, msgobj[mo].config, 0); | |
278 | } | |
279 | ||
280 | /* Set all global ID masks to "don't care" */ | |
281 | cc770_write_reg(priv, global_mask_std[0], 0); | |
282 | cc770_write_reg(priv, global_mask_std[1], 0); | |
283 | cc770_write_reg(priv, global_mask_ext[0], 0); | |
284 | cc770_write_reg(priv, global_mask_ext[1], 0); | |
285 | cc770_write_reg(priv, global_mask_ext[2], 0); | |
286 | cc770_write_reg(priv, global_mask_ext[3], 0); | |
287 | ||
288 | } | |
289 | ||
290 | static int cc770_probe_chip(struct net_device *dev) | |
291 | { | |
292 | struct cc770_priv *priv = netdev_priv(dev); | |
293 | ||
294 | /* Enable configuration, put chip in bus-off, disable ints */ | |
295 | cc770_write_reg(priv, control, CTRL_CCE | CTRL_EAF | CTRL_INI); | |
296 | /* Configure cpu interface / CLKOUT disable */ | |
297 | cc770_write_reg(priv, cpu_interface, priv->cpu_interface); | |
298 | ||
299 | /* | |
300 | * Check if hardware reset is still inactive or maybe there | |
301 | * is no chip in this address space | |
302 | */ | |
303 | if (cc770_read_reg(priv, cpu_interface) & CPUIF_RST) { | |
304 | netdev_info(dev, "probing @0x%p failed (reset)\n", | |
305 | priv->reg_base); | |
306 | return -ENODEV; | |
307 | } | |
308 | ||
309 | /* Write and read back test pattern (some arbitrary values) */ | |
310 | cc770_write_reg(priv, msgobj[1].data[1], 0x25); | |
311 | cc770_write_reg(priv, msgobj[2].data[3], 0x52); | |
312 | cc770_write_reg(priv, msgobj[10].data[6], 0xc3); | |
313 | if ((cc770_read_reg(priv, msgobj[1].data[1]) != 0x25) || | |
314 | (cc770_read_reg(priv, msgobj[2].data[3]) != 0x52) || | |
315 | (cc770_read_reg(priv, msgobj[10].data[6]) != 0xc3)) { | |
316 | netdev_info(dev, "probing @0x%p failed (pattern)\n", | |
317 | priv->reg_base); | |
318 | return -ENODEV; | |
319 | } | |
320 | ||
321 | /* Check if this chip is a CC770 supporting additional functions */ | |
322 | if (cc770_read_reg(priv, control) & CTRL_EAF) | |
323 | priv->control_normal_mode |= CTRL_EAF; | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
328 | static void cc770_start(struct net_device *dev) | |
329 | { | |
330 | struct cc770_priv *priv = netdev_priv(dev); | |
331 | ||
332 | /* leave reset mode */ | |
333 | if (priv->can.state != CAN_STATE_STOPPED) | |
334 | set_reset_mode(dev); | |
335 | ||
336 | /* leave reset mode */ | |
337 | set_normal_mode(dev); | |
338 | } | |
339 | ||
340 | static int cc770_set_mode(struct net_device *dev, enum can_mode mode) | |
341 | { | |
342 | switch (mode) { | |
343 | case CAN_MODE_START: | |
344 | cc770_start(dev); | |
345 | netif_wake_queue(dev); | |
346 | break; | |
347 | ||
348 | default: | |
349 | return -EOPNOTSUPP; | |
350 | } | |
351 | ||
352 | return 0; | |
353 | } | |
354 | ||
355 | static int cc770_set_bittiming(struct net_device *dev) | |
356 | { | |
357 | struct cc770_priv *priv = netdev_priv(dev); | |
358 | struct can_bittiming *bt = &priv->can.bittiming; | |
359 | u8 btr0, btr1; | |
360 | ||
361 | btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); | |
362 | btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | | |
363 | (((bt->phase_seg2 - 1) & 0x7) << 4); | |
364 | if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) | |
365 | btr1 |= 0x80; | |
366 | ||
367 | netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); | |
368 | ||
369 | cc770_write_reg(priv, bit_timing_0, btr0); | |
370 | cc770_write_reg(priv, bit_timing_1, btr1); | |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
375 | static int cc770_get_berr_counter(const struct net_device *dev, | |
376 | struct can_berr_counter *bec) | |
377 | { | |
378 | struct cc770_priv *priv = netdev_priv(dev); | |
379 | ||
380 | bec->txerr = cc770_read_reg(priv, tx_error_counter); | |
381 | bec->rxerr = cc770_read_reg(priv, rx_error_counter); | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
74620123 | 386 | static void cc770_tx(struct net_device *dev, int mo) |
2a367c3a WG |
387 | { |
388 | struct cc770_priv *priv = netdev_priv(dev); | |
74620123 | 389 | struct can_frame *cf = (struct can_frame *)priv->tx_skb->data; |
2a367c3a WG |
390 | u8 dlc, rtr; |
391 | u32 id; | |
392 | int i; | |
393 | ||
c7b74967 | 394 | dlc = cf->len; |
2a367c3a | 395 | id = cf->can_id; |
74620123 AY |
396 | rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR; |
397 | ||
398 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
399 | MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); | |
2a367c3a WG |
400 | cc770_write_reg(priv, msgobj[mo].ctrl1, |
401 | RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES); | |
74620123 | 402 | |
2a367c3a WG |
403 | if (id & CAN_EFF_FLAG) { |
404 | id &= CAN_EFF_MASK; | |
405 | cc770_write_reg(priv, msgobj[mo].config, | |
406 | (dlc << 4) | rtr | MSGCFG_XTD); | |
407 | cc770_write_reg(priv, msgobj[mo].id[3], id << 3); | |
408 | cc770_write_reg(priv, msgobj[mo].id[2], id >> 5); | |
409 | cc770_write_reg(priv, msgobj[mo].id[1], id >> 13); | |
410 | cc770_write_reg(priv, msgobj[mo].id[0], id >> 21); | |
411 | } else { | |
412 | id &= CAN_SFF_MASK; | |
413 | cc770_write_reg(priv, msgobj[mo].config, (dlc << 4) | rtr); | |
414 | cc770_write_reg(priv, msgobj[mo].id[0], id >> 3); | |
415 | cc770_write_reg(priv, msgobj[mo].id[1], id << 5); | |
416 | } | |
417 | ||
418 | for (i = 0; i < dlc; i++) | |
419 | cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); | |
420 | ||
421 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
74620123 AY |
422 | RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); |
423 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
424 | MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC); | |
425 | } | |
426 | ||
427 | static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) | |
428 | { | |
429 | struct cc770_priv *priv = netdev_priv(dev); | |
430 | unsigned int mo = obj2msgobj(CC770_OBJ_TX); | |
431 | ||
ae64438b | 432 | if (can_dev_dropped_skb(dev, skb)) |
74620123 | 433 | return NETDEV_TX_OK; |
2a367c3a | 434 | |
74620123 AY |
435 | netif_stop_queue(dev); |
436 | ||
437 | if ((cc770_read_reg(priv, | |
438 | msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) { | |
439 | netdev_err(dev, "TX register is still occupied!\n"); | |
440 | return NETDEV_TX_BUSY; | |
441 | } | |
442 | ||
443 | priv->tx_skb = skb; | |
444 | cc770_tx(dev, mo); | |
2a367c3a | 445 | |
2a367c3a WG |
446 | return NETDEV_TX_OK; |
447 | } | |
448 | ||
449 | static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1) | |
450 | { | |
451 | struct cc770_priv *priv = netdev_priv(dev); | |
452 | struct net_device_stats *stats = &dev->stats; | |
453 | struct can_frame *cf; | |
454 | struct sk_buff *skb; | |
455 | u8 config; | |
456 | u32 id; | |
457 | int i; | |
458 | ||
459 | skb = alloc_can_skb(dev, &cf); | |
460 | if (!skb) | |
461 | return; | |
462 | ||
463 | config = cc770_read_reg(priv, msgobj[mo].config); | |
464 | ||
465 | if (ctrl1 & RMTPND_SET) { | |
466 | /* | |
467 | * Unfortunately, the chip does not store the real message | |
468 | * identifier of the received remote transmission request | |
469 | * frame. Therefore we set it to 0. | |
470 | */ | |
471 | cf->can_id = CAN_RTR_FLAG; | |
472 | if (config & MSGCFG_XTD) | |
473 | cf->can_id |= CAN_EFF_FLAG; | |
c7b74967 | 474 | cf->len = 0; |
2a367c3a WG |
475 | } else { |
476 | if (config & MSGCFG_XTD) { | |
477 | id = cc770_read_reg(priv, msgobj[mo].id[3]); | |
478 | id |= cc770_read_reg(priv, msgobj[mo].id[2]) << 8; | |
479 | id |= cc770_read_reg(priv, msgobj[mo].id[1]) << 16; | |
480 | id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 24; | |
481 | id >>= 3; | |
482 | id |= CAN_EFF_FLAG; | |
483 | } else { | |
484 | id = cc770_read_reg(priv, msgobj[mo].id[1]); | |
485 | id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 8; | |
486 | id >>= 5; | |
487 | } | |
488 | ||
489 | cf->can_id = id; | |
c7b74967 OH |
490 | cf->len = can_cc_dlc2len((config & 0xf0) >> 4); |
491 | for (i = 0; i < cf->len; i++) | |
2a367c3a | 492 | cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]); |
2a367c3a | 493 | |
8e674ca7 VM |
494 | stats->rx_bytes += cf->len; |
495 | } | |
2a367c3a | 496 | stats->rx_packets++; |
8e674ca7 | 497 | |
ef934e89 | 498 | netif_rx(skb); |
2a367c3a WG |
499 | } |
500 | ||
501 | static int cc770_err(struct net_device *dev, u8 status) | |
502 | { | |
503 | struct cc770_priv *priv = netdev_priv(dev); | |
2a367c3a WG |
504 | struct can_frame *cf; |
505 | struct sk_buff *skb; | |
506 | u8 lec; | |
507 | ||
508 | netdev_dbg(dev, "status interrupt (%#x)\n", status); | |
509 | ||
510 | skb = alloc_can_err_skb(dev, &cf); | |
511 | if (!skb) | |
512 | return -ENOMEM; | |
513 | ||
514 | /* Use extended functions of the CC770 */ | |
515 | if (priv->control_normal_mode & CTRL_EAF) { | |
3e5c291c | 516 | cf->can_id |= CAN_ERR_CNT; |
2a367c3a WG |
517 | cf->data[6] = cc770_read_reg(priv, tx_error_counter); |
518 | cf->data[7] = cc770_read_reg(priv, rx_error_counter); | |
519 | } | |
520 | ||
521 | if (status & STAT_BOFF) { | |
522 | /* Disable interrupts */ | |
523 | cc770_write_reg(priv, control, CTRL_INI); | |
524 | cf->can_id |= CAN_ERR_BUSOFF; | |
525 | priv->can.state = CAN_STATE_BUS_OFF; | |
be38a6f9 | 526 | priv->can.can_stats.bus_off++; |
2a367c3a WG |
527 | can_bus_off(dev); |
528 | } else if (status & STAT_WARN) { | |
529 | cf->can_id |= CAN_ERR_CRTL; | |
530 | /* Only the CC770 does show error passive */ | |
531 | if (cf->data[7] > 127) { | |
532 | cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE | | |
533 | CAN_ERR_CRTL_TX_PASSIVE; | |
534 | priv->can.state = CAN_STATE_ERROR_PASSIVE; | |
535 | priv->can.can_stats.error_passive++; | |
536 | } else { | |
537 | cf->data[1] = CAN_ERR_CRTL_RX_WARNING | | |
538 | CAN_ERR_CRTL_TX_WARNING; | |
539 | priv->can.state = CAN_STATE_ERROR_WARNING; | |
540 | priv->can.can_stats.error_warning++; | |
541 | } | |
542 | } else { | |
88bfb9a7 | 543 | /* Back to error active */ |
2a367c3a WG |
544 | cf->can_id |= CAN_ERR_PROT; |
545 | cf->data[2] = CAN_ERR_PROT_ACTIVE; | |
546 | priv->can.state = CAN_STATE_ERROR_ACTIVE; | |
547 | } | |
548 | ||
549 | lec = status & STAT_LEC_MASK; | |
550 | if (lec < 7 && lec > 0) { | |
551 | if (lec == STAT_LEC_ACK) { | |
552 | cf->can_id |= CAN_ERR_ACK; | |
553 | } else { | |
554 | cf->can_id |= CAN_ERR_PROT; | |
555 | switch (lec) { | |
556 | case STAT_LEC_STUFF: | |
557 | cf->data[2] |= CAN_ERR_PROT_STUFF; | |
558 | break; | |
559 | case STAT_LEC_FORM: | |
560 | cf->data[2] |= CAN_ERR_PROT_FORM; | |
561 | break; | |
562 | case STAT_LEC_BIT1: | |
563 | cf->data[2] |= CAN_ERR_PROT_BIT1; | |
564 | break; | |
565 | case STAT_LEC_BIT0: | |
566 | cf->data[2] |= CAN_ERR_PROT_BIT0; | |
567 | break; | |
568 | case STAT_LEC_CRC: | |
ffd461f8 | 569 | cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; |
2a367c3a WG |
570 | break; |
571 | } | |
572 | } | |
573 | } | |
574 | ||
2a367c3a | 575 | |
ef934e89 | 576 | netif_rx(skb); |
2a367c3a WG |
577 | |
578 | return 0; | |
579 | } | |
580 | ||
581 | static int cc770_status_interrupt(struct net_device *dev) | |
582 | { | |
583 | struct cc770_priv *priv = netdev_priv(dev); | |
584 | u8 status; | |
585 | ||
586 | status = cc770_read_reg(priv, status); | |
587 | /* Reset the status register including RXOK and TXOK */ | |
588 | cc770_write_reg(priv, status, STAT_LEC_MASK); | |
589 | ||
590 | if (status & (STAT_WARN | STAT_BOFF) || | |
591 | (status & STAT_LEC_MASK) != STAT_LEC_MASK) { | |
592 | cc770_err(dev, status); | |
593 | return status & STAT_BOFF; | |
594 | } | |
595 | ||
596 | return 0; | |
597 | } | |
598 | ||
599 | static void cc770_rx_interrupt(struct net_device *dev, unsigned int o) | |
600 | { | |
601 | struct cc770_priv *priv = netdev_priv(dev); | |
602 | struct net_device_stats *stats = &dev->stats; | |
603 | unsigned int mo = obj2msgobj(o); | |
604 | u8 ctrl1; | |
605 | int n = CC770_MAX_MSG; | |
606 | ||
607 | while (n--) { | |
608 | ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); | |
609 | ||
610 | if (!(ctrl1 & NEWDAT_SET)) { | |
611 | /* Check for RTR if additional functions are enabled */ | |
612 | if (priv->control_normal_mode & CTRL_EAF) { | |
613 | if (!(cc770_read_reg(priv, msgobj[mo].ctrl0) & | |
614 | INTPND_SET)) | |
615 | break; | |
616 | } else { | |
617 | break; | |
618 | } | |
619 | } | |
620 | ||
621 | if (ctrl1 & MSGLST_SET) { | |
622 | stats->rx_over_errors++; | |
623 | stats->rx_errors++; | |
624 | } | |
625 | if (mo < MSGOBJ_LAST) | |
626 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
627 | NEWDAT_RES | MSGLST_RES | | |
628 | TXRQST_UNC | RMTPND_UNC); | |
629 | cc770_rx(dev, mo, ctrl1); | |
630 | ||
631 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
632 | MSGVAL_SET | TXIE_RES | | |
633 | RXIE_SET | INTPND_RES); | |
634 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
635 | NEWDAT_RES | MSGLST_RES | | |
636 | TXRQST_RES | RMTPND_RES); | |
637 | } | |
638 | } | |
639 | ||
640 | static void cc770_rtr_interrupt(struct net_device *dev, unsigned int o) | |
641 | { | |
642 | struct cc770_priv *priv = netdev_priv(dev); | |
643 | unsigned int mo = obj2msgobj(o); | |
644 | u8 ctrl0, ctrl1; | |
645 | int n = CC770_MAX_MSG; | |
646 | ||
647 | while (n--) { | |
648 | ctrl0 = cc770_read_reg(priv, msgobj[mo].ctrl0); | |
649 | if (!(ctrl0 & INTPND_SET)) | |
650 | break; | |
651 | ||
652 | ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); | |
653 | cc770_rx(dev, mo, ctrl1); | |
654 | ||
655 | cc770_write_reg(priv, msgobj[mo].ctrl0, | |
656 | MSGVAL_SET | TXIE_RES | | |
657 | RXIE_SET | INTPND_RES); | |
658 | cc770_write_reg(priv, msgobj[mo].ctrl1, | |
659 | NEWDAT_RES | CPUUPD_SET | | |
660 | TXRQST_RES | RMTPND_RES); | |
661 | } | |
662 | } | |
663 | ||
664 | static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) | |
665 | { | |
666 | struct cc770_priv *priv = netdev_priv(dev); | |
667 | struct net_device_stats *stats = &dev->stats; | |
668 | unsigned int mo = obj2msgobj(o); | |
74620123 AY |
669 | u8 ctrl1; |
670 | ||
671 | ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); | |
2a367c3a | 672 | |
2a367c3a WG |
673 | cc770_write_reg(priv, msgobj[mo].ctrl0, |
674 | MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); | |
74620123 AY |
675 | cc770_write_reg(priv, msgobj[mo].ctrl1, |
676 | RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES); | |
2a367c3a | 677 | |
74620123 AY |
678 | if (unlikely(!priv->tx_skb)) { |
679 | netdev_err(dev, "missing tx skb in tx interrupt\n"); | |
680 | return; | |
681 | } | |
682 | ||
683 | if (unlikely(ctrl1 & MSGLST_SET)) { | |
684 | stats->rx_over_errors++; | |
685 | stats->rx_errors++; | |
686 | } | |
687 | ||
688 | /* When the CC770 is sending an RTR message and it receives a regular | |
689 | * message that matches the id of the RTR message, it will overwrite the | |
690 | * outgoing message in the TX register. When this happens we must | |
691 | * process the received message and try to transmit the outgoing skb | |
692 | * again. | |
693 | */ | |
694 | if (unlikely(ctrl1 & NEWDAT_SET)) { | |
695 | cc770_rx(dev, mo, ctrl1); | |
696 | cc770_tx(dev, mo); | |
697 | return; | |
698 | } | |
699 | ||
1dcb6e57 | 700 | can_put_echo_skb(priv->tx_skb, dev, 0, 0); |
cc4b08c3 VM |
701 | stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); |
702 | stats->tx_packets++; | |
74620123 AY |
703 | priv->tx_skb = NULL; |
704 | ||
2a367c3a WG |
705 | netif_wake_queue(dev); |
706 | } | |
707 | ||
ea9f0719 | 708 | static irqreturn_t cc770_interrupt(int irq, void *dev_id) |
2a367c3a WG |
709 | { |
710 | struct net_device *dev = (struct net_device *)dev_id; | |
711 | struct cc770_priv *priv = netdev_priv(dev); | |
712 | u8 intid; | |
713 | int o, n = 0; | |
714 | ||
715 | /* Shared interrupts and IRQ off? */ | |
716 | if (priv->can.state == CAN_STATE_STOPPED) | |
717 | return IRQ_NONE; | |
718 | ||
719 | if (priv->pre_irq) | |
720 | priv->pre_irq(priv); | |
721 | ||
722 | while (n < CC770_MAX_IRQ) { | |
723 | /* Read the highest pending interrupt request */ | |
724 | intid = cc770_read_reg(priv, interrupt); | |
725 | if (!intid) | |
726 | break; | |
727 | n++; | |
728 | ||
729 | if (intid == 1) { | |
730 | /* Exit in case of bus-off */ | |
731 | if (cc770_status_interrupt(dev)) | |
732 | break; | |
733 | } else { | |
734 | o = intid2obj(intid); | |
735 | ||
736 | if (o >= CC770_OBJ_MAX) { | |
737 | netdev_err(dev, "Unexpected interrupt id %d\n", | |
738 | intid); | |
739 | continue; | |
740 | } | |
741 | ||
742 | if (priv->obj_flags[o] & CC770_OBJ_FLAG_RTR) | |
743 | cc770_rtr_interrupt(dev, o); | |
744 | else if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) | |
745 | cc770_rx_interrupt(dev, o); | |
746 | else | |
747 | cc770_tx_interrupt(dev, o); | |
748 | } | |
749 | } | |
750 | ||
751 | if (priv->post_irq) | |
752 | priv->post_irq(priv); | |
753 | ||
754 | if (n >= CC770_MAX_IRQ) | |
755 | netdev_dbg(dev, "%d messages handled in ISR", n); | |
756 | ||
757 | return (n) ? IRQ_HANDLED : IRQ_NONE; | |
758 | } | |
759 | ||
760 | static int cc770_open(struct net_device *dev) | |
761 | { | |
762 | struct cc770_priv *priv = netdev_priv(dev); | |
763 | int err; | |
764 | ||
765 | /* set chip into reset mode */ | |
766 | set_reset_mode(dev); | |
767 | ||
768 | /* common open */ | |
769 | err = open_candev(dev); | |
770 | if (err) | |
771 | return err; | |
772 | ||
773 | err = request_irq(dev->irq, &cc770_interrupt, priv->irq_flags, | |
774 | dev->name, dev); | |
775 | if (err) { | |
776 | close_candev(dev); | |
777 | return -EAGAIN; | |
778 | } | |
779 | ||
780 | /* init and start chip */ | |
781 | cc770_start(dev); | |
782 | ||
783 | netif_start_queue(dev); | |
784 | ||
785 | return 0; | |
786 | } | |
787 | ||
788 | static int cc770_close(struct net_device *dev) | |
789 | { | |
790 | netif_stop_queue(dev); | |
791 | set_reset_mode(dev); | |
792 | ||
793 | free_irq(dev->irq, dev); | |
794 | close_candev(dev); | |
795 | ||
796 | return 0; | |
797 | } | |
798 | ||
799 | struct net_device *alloc_cc770dev(int sizeof_priv) | |
800 | { | |
801 | struct net_device *dev; | |
802 | struct cc770_priv *priv; | |
803 | ||
804 | dev = alloc_candev(sizeof(struct cc770_priv) + sizeof_priv, | |
805 | CC770_ECHO_SKB_MAX); | |
806 | if (!dev) | |
807 | return NULL; | |
808 | ||
809 | priv = netdev_priv(dev); | |
810 | ||
811 | priv->dev = dev; | |
812 | priv->can.bittiming_const = &cc770_bittiming_const; | |
813 | priv->can.do_set_bittiming = cc770_set_bittiming; | |
814 | priv->can.do_set_mode = cc770_set_mode; | |
815 | priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; | |
74620123 | 816 | priv->tx_skb = NULL; |
2a367c3a WG |
817 | |
818 | memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags)); | |
819 | ||
820 | if (sizeof_priv) | |
821 | priv->priv = (void *)priv + sizeof(struct cc770_priv); | |
822 | ||
823 | return dev; | |
824 | } | |
825 | EXPORT_SYMBOL_GPL(alloc_cc770dev); | |
826 | ||
827 | void free_cc770dev(struct net_device *dev) | |
828 | { | |
829 | free_candev(dev); | |
830 | } | |
831 | EXPORT_SYMBOL_GPL(free_cc770dev); | |
832 | ||
833 | static const struct net_device_ops cc770_netdev_ops = { | |
834 | .ndo_open = cc770_open, | |
835 | .ndo_stop = cc770_close, | |
836 | .ndo_start_xmit = cc770_start_xmit, | |
c971fa2a | 837 | .ndo_change_mtu = can_change_mtu, |
2a367c3a WG |
838 | }; |
839 | ||
409c188c VM |
840 | static const struct ethtool_ops cc770_ethtool_ops = { |
841 | .get_ts_info = ethtool_op_get_ts_info, | |
842 | }; | |
843 | ||
2a367c3a WG |
844 | int register_cc770dev(struct net_device *dev) |
845 | { | |
846 | struct cc770_priv *priv = netdev_priv(dev); | |
847 | int err; | |
848 | ||
849 | err = cc770_probe_chip(dev); | |
850 | if (err) | |
851 | return err; | |
852 | ||
853 | dev->netdev_ops = &cc770_netdev_ops; | |
409c188c | 854 | dev->ethtool_ops = &cc770_ethtool_ops; |
2a367c3a WG |
855 | |
856 | dev->flags |= IFF_ECHO; /* we support local echo */ | |
857 | ||
858 | /* Should we use additional functions? */ | |
859 | if (!i82527_compat && priv->control_normal_mode & CTRL_EAF) { | |
860 | priv->can.do_get_berr_counter = cc770_get_berr_counter; | |
861 | priv->control_normal_mode = CTRL_IE | CTRL_EAF | CTRL_EIE; | |
862 | netdev_dbg(dev, "i82527 mode with additional functions\n"); | |
863 | } else { | |
864 | priv->control_normal_mode = CTRL_IE | CTRL_EIE; | |
865 | netdev_dbg(dev, "strict i82527 compatibility mode\n"); | |
866 | } | |
867 | ||
868 | chipset_init(priv); | |
869 | set_reset_mode(dev); | |
870 | ||
871 | return register_candev(dev); | |
872 | } | |
873 | EXPORT_SYMBOL_GPL(register_cc770dev); | |
874 | ||
875 | void unregister_cc770dev(struct net_device *dev) | |
876 | { | |
877 | set_reset_mode(dev); | |
878 | unregister_candev(dev); | |
879 | } | |
880 | EXPORT_SYMBOL_GPL(unregister_cc770dev); | |
881 | ||
882 | static __init int cc770_init(void) | |
883 | { | |
884 | if (msgobj15_eff) { | |
885 | cc770_obj_flags[CC770_OBJ_RX0] |= CC770_OBJ_FLAG_EFF; | |
886 | cc770_obj_flags[CC770_OBJ_RX1] &= ~CC770_OBJ_FLAG_EFF; | |
887 | } | |
888 | ||
889 | pr_info("CAN netdevice driver\n"); | |
890 | ||
891 | return 0; | |
892 | } | |
893 | module_init(cc770_init); | |
894 | ||
895 | static __exit void cc770_exit(void) | |
896 | { | |
897 | pr_info("driver removed\n"); | |
898 | } | |
899 | module_exit(cc770_exit); |