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