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