| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * flexcan.c - FLEXCAN CAN controller driver | 
|  | 3 | * | 
|  | 4 | * Copyright (c) 2005-2006 Varma Electronics Oy | 
|  | 5 | * Copyright (c) 2009 Sascha Hauer, Pengutronix | 
|  | 6 | * Copyright (c) 2010 Marc Kleine-Budde, Pengutronix | 
|  | 7 | * | 
|  | 8 | * Based on code originally by Andrey Volkov <avolkov@varma-el.com> | 
|  | 9 | * | 
|  | 10 | * LICENCE: | 
|  | 11 | * This program is free software; you can redistribute it and/or | 
|  | 12 | * modify it under the terms of the GNU General Public License as | 
|  | 13 | * published by the Free Software Foundation version 2. | 
|  | 14 | * | 
|  | 15 | * This program is distributed in the hope that it will be useful, | 
|  | 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 18 | * GNU General Public License for more details. | 
|  | 19 | * | 
|  | 20 | */ | 
|  | 21 |  | 
|  | 22 | #include <linux/netdevice.h> | 
|  | 23 | #include <linux/can.h> | 
|  | 24 | #include <linux/can/dev.h> | 
|  | 25 | #include <linux/can/error.h> | 
|  | 26 | #include <linux/can/platform/flexcan.h> | 
|  | 27 | #include <linux/clk.h> | 
|  | 28 | #include <linux/delay.h> | 
|  | 29 | #include <linux/if_arp.h> | 
|  | 30 | #include <linux/if_ether.h> | 
|  | 31 | #include <linux/interrupt.h> | 
|  | 32 | #include <linux/io.h> | 
|  | 33 | #include <linux/kernel.h> | 
|  | 34 | #include <linux/list.h> | 
|  | 35 | #include <linux/module.h> | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 36 | #include <linux/of.h> | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 37 | #include <linux/platform_device.h> | 
|  | 38 |  | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 39 | #define DRV_NAME			"flexcan" | 
|  | 40 |  | 
|  | 41 | /* 8 for RX fifo and 2 error handling */ | 
|  | 42 | #define FLEXCAN_NAPI_WEIGHT		(8 + 2) | 
|  | 43 |  | 
|  | 44 | /* FLEXCAN module configuration register (CANMCR) bits */ | 
|  | 45 | #define FLEXCAN_MCR_MDIS		BIT(31) | 
|  | 46 | #define FLEXCAN_MCR_FRZ			BIT(30) | 
|  | 47 | #define FLEXCAN_MCR_FEN			BIT(29) | 
|  | 48 | #define FLEXCAN_MCR_HALT		BIT(28) | 
|  | 49 | #define FLEXCAN_MCR_NOT_RDY		BIT(27) | 
|  | 50 | #define FLEXCAN_MCR_WAK_MSK		BIT(26) | 
|  | 51 | #define FLEXCAN_MCR_SOFTRST		BIT(25) | 
|  | 52 | #define FLEXCAN_MCR_FRZ_ACK		BIT(24) | 
|  | 53 | #define FLEXCAN_MCR_SUPV		BIT(23) | 
|  | 54 | #define FLEXCAN_MCR_SLF_WAK		BIT(22) | 
|  | 55 | #define FLEXCAN_MCR_WRN_EN		BIT(21) | 
|  | 56 | #define FLEXCAN_MCR_LPM_ACK		BIT(20) | 
|  | 57 | #define FLEXCAN_MCR_WAK_SRC		BIT(19) | 
|  | 58 | #define FLEXCAN_MCR_DOZE		BIT(18) | 
|  | 59 | #define FLEXCAN_MCR_SRX_DIS		BIT(17) | 
|  | 60 | #define FLEXCAN_MCR_BCC			BIT(16) | 
|  | 61 | #define FLEXCAN_MCR_LPRIO_EN		BIT(13) | 
|  | 62 | #define FLEXCAN_MCR_AEN			BIT(12) | 
| Marc Kleine-Budde | f49c173 | 2013-10-04 10:52:36 +0200 | [diff] [blame] | 63 | #define FLEXCAN_MCR_MAXMB(x)		((x) & 0x1f) | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 64 | #define FLEXCAN_MCR_IDAM_A		(0 << 8) | 
|  | 65 | #define FLEXCAN_MCR_IDAM_B		(1 << 8) | 
|  | 66 | #define FLEXCAN_MCR_IDAM_C		(2 << 8) | 
|  | 67 | #define FLEXCAN_MCR_IDAM_D		(3 << 8) | 
|  | 68 |  | 
|  | 69 | /* FLEXCAN control register (CANCTRL) bits */ | 
|  | 70 | #define FLEXCAN_CTRL_PRESDIV(x)		(((x) & 0xff) << 24) | 
|  | 71 | #define FLEXCAN_CTRL_RJW(x)		(((x) & 0x03) << 22) | 
|  | 72 | #define FLEXCAN_CTRL_PSEG1(x)		(((x) & 0x07) << 19) | 
|  | 73 | #define FLEXCAN_CTRL_PSEG2(x)		(((x) & 0x07) << 16) | 
|  | 74 | #define FLEXCAN_CTRL_BOFF_MSK		BIT(15) | 
|  | 75 | #define FLEXCAN_CTRL_ERR_MSK		BIT(14) | 
|  | 76 | #define FLEXCAN_CTRL_CLK_SRC		BIT(13) | 
|  | 77 | #define FLEXCAN_CTRL_LPB		BIT(12) | 
|  | 78 | #define FLEXCAN_CTRL_TWRN_MSK		BIT(11) | 
|  | 79 | #define FLEXCAN_CTRL_RWRN_MSK		BIT(10) | 
|  | 80 | #define FLEXCAN_CTRL_SMP		BIT(7) | 
|  | 81 | #define FLEXCAN_CTRL_BOFF_REC		BIT(6) | 
|  | 82 | #define FLEXCAN_CTRL_TSYN		BIT(5) | 
|  | 83 | #define FLEXCAN_CTRL_LBUF		BIT(4) | 
|  | 84 | #define FLEXCAN_CTRL_LOM		BIT(3) | 
|  | 85 | #define FLEXCAN_CTRL_PROPSEG(x)		((x) & 0x07) | 
|  | 86 | #define FLEXCAN_CTRL_ERR_BUS		(FLEXCAN_CTRL_ERR_MSK) | 
|  | 87 | #define FLEXCAN_CTRL_ERR_STATE \ | 
|  | 88 | (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \ | 
|  | 89 | FLEXCAN_CTRL_BOFF_MSK) | 
|  | 90 | #define FLEXCAN_CTRL_ERR_ALL \ | 
|  | 91 | (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE) | 
|  | 92 |  | 
|  | 93 | /* FLEXCAN error and status register (ESR) bits */ | 
|  | 94 | #define FLEXCAN_ESR_TWRN_INT		BIT(17) | 
|  | 95 | #define FLEXCAN_ESR_RWRN_INT		BIT(16) | 
|  | 96 | #define FLEXCAN_ESR_BIT1_ERR		BIT(15) | 
|  | 97 | #define FLEXCAN_ESR_BIT0_ERR		BIT(14) | 
|  | 98 | #define FLEXCAN_ESR_ACK_ERR		BIT(13) | 
|  | 99 | #define FLEXCAN_ESR_CRC_ERR		BIT(12) | 
|  | 100 | #define FLEXCAN_ESR_FRM_ERR		BIT(11) | 
|  | 101 | #define FLEXCAN_ESR_STF_ERR		BIT(10) | 
|  | 102 | #define FLEXCAN_ESR_TX_WRN		BIT(9) | 
|  | 103 | #define FLEXCAN_ESR_RX_WRN		BIT(8) | 
|  | 104 | #define FLEXCAN_ESR_IDLE		BIT(7) | 
|  | 105 | #define FLEXCAN_ESR_TXRX		BIT(6) | 
|  | 106 | #define FLEXCAN_EST_FLT_CONF_SHIFT	(4) | 
|  | 107 | #define FLEXCAN_ESR_FLT_CONF_MASK	(0x3 << FLEXCAN_EST_FLT_CONF_SHIFT) | 
|  | 108 | #define FLEXCAN_ESR_FLT_CONF_ACTIVE	(0x0 << FLEXCAN_EST_FLT_CONF_SHIFT) | 
|  | 109 | #define FLEXCAN_ESR_FLT_CONF_PASSIVE	(0x1 << FLEXCAN_EST_FLT_CONF_SHIFT) | 
|  | 110 | #define FLEXCAN_ESR_BOFF_INT		BIT(2) | 
|  | 111 | #define FLEXCAN_ESR_ERR_INT		BIT(1) | 
|  | 112 | #define FLEXCAN_ESR_WAK_INT		BIT(0) | 
|  | 113 | #define FLEXCAN_ESR_ERR_BUS \ | 
|  | 114 | (FLEXCAN_ESR_BIT1_ERR | FLEXCAN_ESR_BIT0_ERR | \ | 
|  | 115 | FLEXCAN_ESR_ACK_ERR | FLEXCAN_ESR_CRC_ERR | \ | 
|  | 116 | FLEXCAN_ESR_FRM_ERR | FLEXCAN_ESR_STF_ERR) | 
|  | 117 | #define FLEXCAN_ESR_ERR_STATE \ | 
|  | 118 | (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | FLEXCAN_ESR_BOFF_INT) | 
|  | 119 | #define FLEXCAN_ESR_ERR_ALL \ | 
|  | 120 | (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) | 
| Wolfgang Grandegger | 6e9d554 | 2011-12-12 16:09:28 +0100 | [diff] [blame] | 121 | #define FLEXCAN_ESR_ALL_INT \ | 
|  | 122 | (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ | 
|  | 123 | FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 124 |  | 
|  | 125 | /* FLEXCAN interrupt flag register (IFLAG) bits */ | 
|  | 126 | #define FLEXCAN_TX_BUF_ID		8 | 
|  | 127 | #define FLEXCAN_IFLAG_BUF(x)		BIT(x) | 
|  | 128 | #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW	BIT(7) | 
|  | 129 | #define FLEXCAN_IFLAG_RX_FIFO_WARN	BIT(6) | 
|  | 130 | #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE	BIT(5) | 
|  | 131 | #define FLEXCAN_IFLAG_DEFAULT \ | 
|  | 132 | (FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \ | 
|  | 133 | FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID)) | 
|  | 134 |  | 
|  | 135 | /* FLEXCAN message buffers */ | 
|  | 136 | #define FLEXCAN_MB_CNT_CODE(x)		(((x) & 0xf) << 24) | 
|  | 137 | #define FLEXCAN_MB_CNT_SRR		BIT(22) | 
|  | 138 | #define FLEXCAN_MB_CNT_IDE		BIT(21) | 
|  | 139 | #define FLEXCAN_MB_CNT_RTR		BIT(20) | 
|  | 140 | #define FLEXCAN_MB_CNT_LENGTH(x)	(((x) & 0xf) << 16) | 
|  | 141 | #define FLEXCAN_MB_CNT_TIMESTAMP(x)	((x) & 0xffff) | 
|  | 142 |  | 
|  | 143 | #define FLEXCAN_MB_CODE_MASK		(0xf0ffffff) | 
|  | 144 |  | 
|  | 145 | /* Structure of the message buffer */ | 
|  | 146 | struct flexcan_mb { | 
|  | 147 | u32 can_ctrl; | 
|  | 148 | u32 can_id; | 
|  | 149 | u32 data[2]; | 
|  | 150 | }; | 
|  | 151 |  | 
|  | 152 | /* Structure of the hardware registers */ | 
|  | 153 | struct flexcan_regs { | 
|  | 154 | u32 mcr;		/* 0x00 */ | 
|  | 155 | u32 ctrl;		/* 0x04 */ | 
|  | 156 | u32 timer;		/* 0x08 */ | 
|  | 157 | u32 _reserved1;		/* 0x0c */ | 
|  | 158 | u32 rxgmask;		/* 0x10 */ | 
|  | 159 | u32 rx14mask;		/* 0x14 */ | 
|  | 160 | u32 rx15mask;		/* 0x18 */ | 
|  | 161 | u32 ecr;		/* 0x1c */ | 
|  | 162 | u32 esr;		/* 0x20 */ | 
|  | 163 | u32 imask2;		/* 0x24 */ | 
|  | 164 | u32 imask1;		/* 0x28 */ | 
|  | 165 | u32 iflag2;		/* 0x2c */ | 
|  | 166 | u32 iflag1;		/* 0x30 */ | 
|  | 167 | u32 _reserved2[19]; | 
|  | 168 | struct flexcan_mb cantxfg[64]; | 
|  | 169 | }; | 
|  | 170 |  | 
|  | 171 | struct flexcan_priv { | 
|  | 172 | struct can_priv can; | 
|  | 173 | struct net_device *dev; | 
|  | 174 | struct napi_struct napi; | 
|  | 175 |  | 
|  | 176 | void __iomem *base; | 
|  | 177 | u32 reg_esr; | 
|  | 178 | u32 reg_ctrl_default; | 
|  | 179 |  | 
|  | 180 | struct clk *clk; | 
|  | 181 | struct flexcan_platform_data *pdata; | 
|  | 182 | }; | 
|  | 183 |  | 
|  | 184 | static struct can_bittiming_const flexcan_bittiming_const = { | 
|  | 185 | .name = DRV_NAME, | 
|  | 186 | .tseg1_min = 4, | 
|  | 187 | .tseg1_max = 16, | 
|  | 188 | .tseg2_min = 2, | 
|  | 189 | .tseg2_max = 8, | 
|  | 190 | .sjw_max = 4, | 
|  | 191 | .brp_min = 1, | 
|  | 192 | .brp_max = 256, | 
|  | 193 | .brp_inc = 1, | 
|  | 194 | }; | 
|  | 195 |  | 
|  | 196 | /* | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 197 | * Abstract off the read/write for arm versus ppc. | 
|  | 198 | */ | 
|  | 199 | #if defined(__BIG_ENDIAN) | 
|  | 200 | static inline u32 flexcan_read(void __iomem *addr) | 
|  | 201 | { | 
|  | 202 | return in_be32(addr); | 
|  | 203 | } | 
|  | 204 |  | 
|  | 205 | static inline void flexcan_write(u32 val, void __iomem *addr) | 
|  | 206 | { | 
|  | 207 | out_be32(addr, val); | 
|  | 208 | } | 
|  | 209 | #else | 
|  | 210 | static inline u32 flexcan_read(void __iomem *addr) | 
|  | 211 | { | 
|  | 212 | return readl(addr); | 
|  | 213 | } | 
|  | 214 |  | 
|  | 215 | static inline void flexcan_write(u32 val, void __iomem *addr) | 
|  | 216 | { | 
|  | 217 | writel(val, addr); | 
|  | 218 | } | 
|  | 219 | #endif | 
|  | 220 |  | 
|  | 221 | /* | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 222 | * Swtich transceiver on or off | 
|  | 223 | */ | 
|  | 224 | static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on) | 
|  | 225 | { | 
|  | 226 | if (priv->pdata && priv->pdata->transceiver_switch) | 
|  | 227 | priv->pdata->transceiver_switch(on); | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, | 
|  | 231 | u32 reg_esr) | 
|  | 232 | { | 
|  | 233 | return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && | 
|  | 234 | (reg_esr & FLEXCAN_ESR_ERR_BUS); | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | static inline void flexcan_chip_enable(struct flexcan_priv *priv) | 
|  | 238 | { | 
|  | 239 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 240 | u32 reg; | 
|  | 241 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 242 | reg = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 243 | reg &= ~FLEXCAN_MCR_MDIS; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 244 | flexcan_write(reg, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 245 |  | 
|  | 246 | udelay(10); | 
|  | 247 | } | 
|  | 248 |  | 
|  | 249 | static inline void flexcan_chip_disable(struct flexcan_priv *priv) | 
|  | 250 | { | 
|  | 251 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 252 | u32 reg; | 
|  | 253 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 254 | reg = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 255 | reg |= FLEXCAN_MCR_MDIS; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 256 | flexcan_write(reg, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 257 | } | 
|  | 258 |  | 
|  | 259 | static int flexcan_get_berr_counter(const struct net_device *dev, | 
|  | 260 | struct can_berr_counter *bec) | 
|  | 261 | { | 
|  | 262 | const struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 263 | struct flexcan_regs __iomem *regs = priv->base; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 264 | u32 reg = flexcan_read(®s->ecr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 265 |  | 
|  | 266 | bec->txerr = (reg >> 0) & 0xff; | 
|  | 267 | bec->rxerr = (reg >> 8) & 0xff; | 
|  | 268 |  | 
|  | 269 | return 0; | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) | 
|  | 273 | { | 
|  | 274 | const struct flexcan_priv *priv = netdev_priv(dev); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 275 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 276 | struct can_frame *cf = (struct can_frame *)skb->data; | 
|  | 277 | u32 can_id; | 
|  | 278 | u32 ctrl = FLEXCAN_MB_CNT_CODE(0xc) | (cf->can_dlc << 16); | 
|  | 279 |  | 
|  | 280 | if (can_dropped_invalid_skb(dev, skb)) | 
|  | 281 | return NETDEV_TX_OK; | 
|  | 282 |  | 
|  | 283 | netif_stop_queue(dev); | 
|  | 284 |  | 
|  | 285 | if (cf->can_id & CAN_EFF_FLAG) { | 
|  | 286 | can_id = cf->can_id & CAN_EFF_MASK; | 
|  | 287 | ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR; | 
|  | 288 | } else { | 
|  | 289 | can_id = (cf->can_id & CAN_SFF_MASK) << 18; | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | if (cf->can_id & CAN_RTR_FLAG) | 
|  | 293 | ctrl |= FLEXCAN_MB_CNT_RTR; | 
|  | 294 |  | 
|  | 295 | if (cf->can_dlc > 0) { | 
|  | 296 | u32 data = be32_to_cpup((__be32 *)&cf->data[0]); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 297 | flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 298 | } | 
|  | 299 | if (cf->can_dlc > 3) { | 
|  | 300 | u32 data = be32_to_cpup((__be32 *)&cf->data[4]); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 301 | flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 302 | } | 
|  | 303 |  | 
| Reuben Dowle | 9a12349 | 2011-11-01 11:18:03 +1300 | [diff] [blame] | 304 | can_put_echo_skb(skb, dev, 0); | 
|  | 305 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 306 | flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); | 
|  | 307 | flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 308 |  | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 309 | return NETDEV_TX_OK; | 
|  | 310 | } | 
|  | 311 |  | 
|  | 312 | static void do_bus_err(struct net_device *dev, | 
|  | 313 | struct can_frame *cf, u32 reg_esr) | 
|  | 314 | { | 
|  | 315 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 316 | int rx_errors = 0, tx_errors = 0; | 
|  | 317 |  | 
|  | 318 | cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; | 
|  | 319 |  | 
|  | 320 | if (reg_esr & FLEXCAN_ESR_BIT1_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 321 | netdev_dbg(dev, "BIT1_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 322 | cf->data[2] |= CAN_ERR_PROT_BIT1; | 
|  | 323 | tx_errors = 1; | 
|  | 324 | } | 
|  | 325 | if (reg_esr & FLEXCAN_ESR_BIT0_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 326 | netdev_dbg(dev, "BIT0_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 327 | cf->data[2] |= CAN_ERR_PROT_BIT0; | 
|  | 328 | tx_errors = 1; | 
|  | 329 | } | 
|  | 330 | if (reg_esr & FLEXCAN_ESR_ACK_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 331 | netdev_dbg(dev, "ACK_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 332 | cf->can_id |= CAN_ERR_ACK; | 
|  | 333 | cf->data[3] |= CAN_ERR_PROT_LOC_ACK; | 
|  | 334 | tx_errors = 1; | 
|  | 335 | } | 
|  | 336 | if (reg_esr & FLEXCAN_ESR_CRC_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 337 | netdev_dbg(dev, "CRC_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 338 | cf->data[2] |= CAN_ERR_PROT_BIT; | 
|  | 339 | cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; | 
|  | 340 | rx_errors = 1; | 
|  | 341 | } | 
|  | 342 | if (reg_esr & FLEXCAN_ESR_FRM_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 343 | netdev_dbg(dev, "FRM_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 344 | cf->data[2] |= CAN_ERR_PROT_FORM; | 
|  | 345 | rx_errors = 1; | 
|  | 346 | } | 
|  | 347 | if (reg_esr & FLEXCAN_ESR_STF_ERR) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 348 | netdev_dbg(dev, "STF_ERR irq\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 349 | cf->data[2] |= CAN_ERR_PROT_STUFF; | 
|  | 350 | rx_errors = 1; | 
|  | 351 | } | 
|  | 352 |  | 
|  | 353 | priv->can.can_stats.bus_error++; | 
|  | 354 | if (rx_errors) | 
|  | 355 | dev->stats.rx_errors++; | 
|  | 356 | if (tx_errors) | 
|  | 357 | dev->stats.tx_errors++; | 
|  | 358 | } | 
|  | 359 |  | 
|  | 360 | static int flexcan_poll_bus_err(struct net_device *dev, u32 reg_esr) | 
|  | 361 | { | 
|  | 362 | struct sk_buff *skb; | 
|  | 363 | struct can_frame *cf; | 
|  | 364 |  | 
|  | 365 | skb = alloc_can_err_skb(dev, &cf); | 
|  | 366 | if (unlikely(!skb)) | 
|  | 367 | return 0; | 
|  | 368 |  | 
|  | 369 | do_bus_err(dev, cf, reg_esr); | 
|  | 370 | netif_receive_skb(skb); | 
|  | 371 |  | 
|  | 372 | dev->stats.rx_packets++; | 
|  | 373 | dev->stats.rx_bytes += cf->can_dlc; | 
|  | 374 |  | 
|  | 375 | return 1; | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | static void do_state(struct net_device *dev, | 
|  | 379 | struct can_frame *cf, enum can_state new_state) | 
|  | 380 | { | 
|  | 381 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 382 | struct can_berr_counter bec; | 
|  | 383 |  | 
|  | 384 | flexcan_get_berr_counter(dev, &bec); | 
|  | 385 |  | 
|  | 386 | switch (priv->can.state) { | 
|  | 387 | case CAN_STATE_ERROR_ACTIVE: | 
|  | 388 | /* | 
|  | 389 | * from: ERROR_ACTIVE | 
|  | 390 | * to  : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF | 
|  | 391 | * =>  : there was a warning int | 
|  | 392 | */ | 
|  | 393 | if (new_state >= CAN_STATE_ERROR_WARNING && | 
|  | 394 | new_state <= CAN_STATE_BUS_OFF) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 395 | netdev_dbg(dev, "Error Warning IRQ\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 396 | priv->can.can_stats.error_warning++; | 
|  | 397 |  | 
|  | 398 | cf->can_id |= CAN_ERR_CRTL; | 
|  | 399 | cf->data[1] = (bec.txerr > bec.rxerr) ? | 
|  | 400 | CAN_ERR_CRTL_TX_WARNING : | 
|  | 401 | CAN_ERR_CRTL_RX_WARNING; | 
|  | 402 | } | 
|  | 403 | case CAN_STATE_ERROR_WARNING:	/* fallthrough */ | 
|  | 404 | /* | 
|  | 405 | * from: ERROR_ACTIVE, ERROR_WARNING | 
|  | 406 | * to  : ERROR_PASSIVE, BUS_OFF | 
|  | 407 | * =>  : error passive int | 
|  | 408 | */ | 
|  | 409 | if (new_state >= CAN_STATE_ERROR_PASSIVE && | 
|  | 410 | new_state <= CAN_STATE_BUS_OFF) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 411 | netdev_dbg(dev, "Error Passive IRQ\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 412 | priv->can.can_stats.error_passive++; | 
|  | 413 |  | 
|  | 414 | cf->can_id |= CAN_ERR_CRTL; | 
|  | 415 | cf->data[1] = (bec.txerr > bec.rxerr) ? | 
|  | 416 | CAN_ERR_CRTL_TX_PASSIVE : | 
|  | 417 | CAN_ERR_CRTL_RX_PASSIVE; | 
|  | 418 | } | 
|  | 419 | break; | 
|  | 420 | case CAN_STATE_BUS_OFF: | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 421 | netdev_err(dev, "BUG! " | 
|  | 422 | "hardware recovered automatically from BUS_OFF\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 423 | break; | 
|  | 424 | default: | 
|  | 425 | break; | 
|  | 426 | } | 
|  | 427 |  | 
|  | 428 | /* process state changes depending on the new state */ | 
|  | 429 | switch (new_state) { | 
|  | 430 | case CAN_STATE_ERROR_ACTIVE: | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 431 | netdev_dbg(dev, "Error Active\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 432 | cf->can_id |= CAN_ERR_PROT; | 
|  | 433 | cf->data[2] = CAN_ERR_PROT_ACTIVE; | 
|  | 434 | break; | 
|  | 435 | case CAN_STATE_BUS_OFF: | 
|  | 436 | cf->can_id |= CAN_ERR_BUSOFF; | 
|  | 437 | can_bus_off(dev); | 
|  | 438 | break; | 
|  | 439 | default: | 
|  | 440 | break; | 
|  | 441 | } | 
|  | 442 | } | 
|  | 443 |  | 
|  | 444 | static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) | 
|  | 445 | { | 
|  | 446 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 447 | struct sk_buff *skb; | 
|  | 448 | struct can_frame *cf; | 
|  | 449 | enum can_state new_state; | 
|  | 450 | int flt; | 
|  | 451 |  | 
|  | 452 | flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; | 
|  | 453 | if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { | 
|  | 454 | if (likely(!(reg_esr & (FLEXCAN_ESR_TX_WRN | | 
|  | 455 | FLEXCAN_ESR_RX_WRN)))) | 
|  | 456 | new_state = CAN_STATE_ERROR_ACTIVE; | 
|  | 457 | else | 
|  | 458 | new_state = CAN_STATE_ERROR_WARNING; | 
|  | 459 | } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) | 
|  | 460 | new_state = CAN_STATE_ERROR_PASSIVE; | 
|  | 461 | else | 
|  | 462 | new_state = CAN_STATE_BUS_OFF; | 
|  | 463 |  | 
|  | 464 | /* state hasn't changed */ | 
|  | 465 | if (likely(new_state == priv->can.state)) | 
|  | 466 | return 0; | 
|  | 467 |  | 
|  | 468 | skb = alloc_can_err_skb(dev, &cf); | 
|  | 469 | if (unlikely(!skb)) | 
|  | 470 | return 0; | 
|  | 471 |  | 
|  | 472 | do_state(dev, cf, new_state); | 
|  | 473 | priv->can.state = new_state; | 
|  | 474 | netif_receive_skb(skb); | 
|  | 475 |  | 
|  | 476 | dev->stats.rx_packets++; | 
|  | 477 | dev->stats.rx_bytes += cf->can_dlc; | 
|  | 478 |  | 
|  | 479 | return 1; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | static void flexcan_read_fifo(const struct net_device *dev, | 
|  | 483 | struct can_frame *cf) | 
|  | 484 | { | 
|  | 485 | const struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 486 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 487 | struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; | 
|  | 488 | u32 reg_ctrl, reg_id; | 
|  | 489 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 490 | reg_ctrl = flexcan_read(&mb->can_ctrl); | 
|  | 491 | reg_id = flexcan_read(&mb->can_id); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 492 | if (reg_ctrl & FLEXCAN_MB_CNT_IDE) | 
|  | 493 | cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; | 
|  | 494 | else | 
|  | 495 | cf->can_id = (reg_id >> 18) & CAN_SFF_MASK; | 
|  | 496 |  | 
|  | 497 | if (reg_ctrl & FLEXCAN_MB_CNT_RTR) | 
|  | 498 | cf->can_id |= CAN_RTR_FLAG; | 
|  | 499 | cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); | 
|  | 500 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 501 | *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0])); | 
|  | 502 | *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 503 |  | 
|  | 504 | /* mark as read */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 505 | flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); | 
|  | 506 | flexcan_read(®s->timer); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 507 | } | 
|  | 508 |  | 
|  | 509 | static int flexcan_read_frame(struct net_device *dev) | 
|  | 510 | { | 
|  | 511 | struct net_device_stats *stats = &dev->stats; | 
|  | 512 | struct can_frame *cf; | 
|  | 513 | struct sk_buff *skb; | 
|  | 514 |  | 
|  | 515 | skb = alloc_can_skb(dev, &cf); | 
|  | 516 | if (unlikely(!skb)) { | 
|  | 517 | stats->rx_dropped++; | 
|  | 518 | return 0; | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | flexcan_read_fifo(dev, cf); | 
|  | 522 | netif_receive_skb(skb); | 
|  | 523 |  | 
|  | 524 | stats->rx_packets++; | 
|  | 525 | stats->rx_bytes += cf->can_dlc; | 
|  | 526 |  | 
|  | 527 | return 1; | 
|  | 528 | } | 
|  | 529 |  | 
|  | 530 | static int flexcan_poll(struct napi_struct *napi, int quota) | 
|  | 531 | { | 
|  | 532 | struct net_device *dev = napi->dev; | 
|  | 533 | const struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 534 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 535 | u32 reg_iflag1, reg_esr; | 
|  | 536 | int work_done = 0; | 
|  | 537 |  | 
|  | 538 | /* | 
|  | 539 | * The error bits are cleared on read, | 
|  | 540 | * use saved value from irq handler. | 
|  | 541 | */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 542 | reg_esr = flexcan_read(®s->esr) | priv->reg_esr; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 543 |  | 
|  | 544 | /* handle state changes */ | 
|  | 545 | work_done += flexcan_poll_state(dev, reg_esr); | 
|  | 546 |  | 
|  | 547 | /* handle RX-FIFO */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 548 | reg_iflag1 = flexcan_read(®s->iflag1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 549 | while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE && | 
|  | 550 | work_done < quota) { | 
|  | 551 | work_done += flexcan_read_frame(dev); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 552 | reg_iflag1 = flexcan_read(®s->iflag1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 553 | } | 
|  | 554 |  | 
|  | 555 | /* report bus errors */ | 
|  | 556 | if (flexcan_has_and_handle_berr(priv, reg_esr) && work_done < quota) | 
|  | 557 | work_done += flexcan_poll_bus_err(dev, reg_esr); | 
|  | 558 |  | 
|  | 559 | if (work_done < quota) { | 
|  | 560 | napi_complete(napi); | 
|  | 561 | /* enable IRQs */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 562 | flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); | 
|  | 563 | flexcan_write(priv->reg_ctrl_default, ®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 564 | } | 
|  | 565 |  | 
|  | 566 | return work_done; | 
|  | 567 | } | 
|  | 568 |  | 
|  | 569 | static irqreturn_t flexcan_irq(int irq, void *dev_id) | 
|  | 570 | { | 
|  | 571 | struct net_device *dev = dev_id; | 
|  | 572 | struct net_device_stats *stats = &dev->stats; | 
|  | 573 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 574 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 575 | u32 reg_iflag1, reg_esr; | 
|  | 576 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 577 | reg_iflag1 = flexcan_read(®s->iflag1); | 
|  | 578 | reg_esr = flexcan_read(®s->esr); | 
| Wolfgang Grandegger | 6e9d554 | 2011-12-12 16:09:28 +0100 | [diff] [blame] | 579 | /* ACK all bus error and state change IRQ sources */ | 
|  | 580 | if (reg_esr & FLEXCAN_ESR_ALL_INT) | 
|  | 581 | flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 582 |  | 
|  | 583 | /* | 
|  | 584 | * schedule NAPI in case of: | 
|  | 585 | * - rx IRQ | 
|  | 586 | * - state change IRQ | 
|  | 587 | * - bus error IRQ and bus error reporting is activated | 
|  | 588 | */ | 
|  | 589 | if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) || | 
|  | 590 | (reg_esr & FLEXCAN_ESR_ERR_STATE) || | 
|  | 591 | flexcan_has_and_handle_berr(priv, reg_esr)) { | 
|  | 592 | /* | 
|  | 593 | * The error bits are cleared on read, | 
|  | 594 | * save them for later use. | 
|  | 595 | */ | 
|  | 596 | priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 597 | flexcan_write(FLEXCAN_IFLAG_DEFAULT & | 
|  | 598 | ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); | 
|  | 599 | flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 600 | ®s->ctrl); | 
|  | 601 | napi_schedule(&priv->napi); | 
|  | 602 | } | 
|  | 603 |  | 
|  | 604 | /* FIFO overflow */ | 
|  | 605 | if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 606 | flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 607 | dev->stats.rx_over_errors++; | 
|  | 608 | dev->stats.rx_errors++; | 
|  | 609 | } | 
|  | 610 |  | 
|  | 611 | /* transmission complete interrupt */ | 
|  | 612 | if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) { | 
| Reuben Dowle | 9a12349 | 2011-11-01 11:18:03 +1300 | [diff] [blame] | 613 | stats->tx_bytes += can_get_echo_skb(dev, 0); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 614 | stats->tx_packets++; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 615 | flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 616 | netif_wake_queue(dev); | 
|  | 617 | } | 
|  | 618 |  | 
|  | 619 | return IRQ_HANDLED; | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | static void flexcan_set_bittiming(struct net_device *dev) | 
|  | 623 | { | 
|  | 624 | const struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 625 | const struct can_bittiming *bt = &priv->can.bittiming; | 
|  | 626 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 627 | u32 reg; | 
|  | 628 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 629 | reg = flexcan_read(®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 630 | reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) | | 
|  | 631 | FLEXCAN_CTRL_RJW(0x3) | | 
|  | 632 | FLEXCAN_CTRL_PSEG1(0x7) | | 
|  | 633 | FLEXCAN_CTRL_PSEG2(0x7) | | 
|  | 634 | FLEXCAN_CTRL_PROPSEG(0x7) | | 
|  | 635 | FLEXCAN_CTRL_LPB | | 
|  | 636 | FLEXCAN_CTRL_SMP | | 
|  | 637 | FLEXCAN_CTRL_LOM); | 
|  | 638 |  | 
|  | 639 | reg |= FLEXCAN_CTRL_PRESDIV(bt->brp - 1) | | 
|  | 640 | FLEXCAN_CTRL_PSEG1(bt->phase_seg1 - 1) | | 
|  | 641 | FLEXCAN_CTRL_PSEG2(bt->phase_seg2 - 1) | | 
|  | 642 | FLEXCAN_CTRL_RJW(bt->sjw - 1) | | 
|  | 643 | FLEXCAN_CTRL_PROPSEG(bt->prop_seg - 1); | 
|  | 644 |  | 
|  | 645 | if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) | 
|  | 646 | reg |= FLEXCAN_CTRL_LPB; | 
|  | 647 | if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) | 
|  | 648 | reg |= FLEXCAN_CTRL_LOM; | 
|  | 649 | if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) | 
|  | 650 | reg |= FLEXCAN_CTRL_SMP; | 
|  | 651 |  | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 652 | netdev_info(dev, "writing ctrl=0x%08x\n", reg); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 653 | flexcan_write(reg, ®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 654 |  | 
|  | 655 | /* print chip status */ | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 656 | netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, | 
|  | 657 | flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 658 | } | 
|  | 659 |  | 
|  | 660 | /* | 
|  | 661 | * flexcan_chip_start | 
|  | 662 | * | 
|  | 663 | * this functions is entered with clocks enabled | 
|  | 664 | * | 
|  | 665 | */ | 
|  | 666 | static int flexcan_chip_start(struct net_device *dev) | 
|  | 667 | { | 
|  | 668 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 669 | struct flexcan_regs __iomem *regs = priv->base; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 670 | int err; | 
|  | 671 | u32 reg_mcr, reg_ctrl; | 
|  | 672 |  | 
|  | 673 | /* enable module */ | 
|  | 674 | flexcan_chip_enable(priv); | 
|  | 675 |  | 
|  | 676 | /* soft reset */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 677 | flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 678 | udelay(10); | 
|  | 679 |  | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 680 | reg_mcr = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 681 | if (reg_mcr & FLEXCAN_MCR_SOFTRST) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 682 | netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n", | 
|  | 683 | reg_mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 684 | err = -ENODEV; | 
|  | 685 | goto out; | 
|  | 686 | } | 
|  | 687 |  | 
|  | 688 | flexcan_set_bittiming(dev); | 
|  | 689 |  | 
|  | 690 | /* | 
|  | 691 | * MCR | 
|  | 692 | * | 
|  | 693 | * enable freeze | 
|  | 694 | * enable fifo | 
|  | 695 | * halt now | 
|  | 696 | * only supervisor access | 
|  | 697 | * enable warning int | 
|  | 698 | * choose format C | 
| Reuben Dowle | 9a12349 | 2011-11-01 11:18:03 +1300 | [diff] [blame] | 699 | * disable local echo | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 700 | * | 
|  | 701 | */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 702 | reg_mcr = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | f49c173 | 2013-10-04 10:52:36 +0200 | [diff] [blame] | 703 | reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 704 | reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | | 
|  | 705 | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | | 
| Marc Kleine-Budde | f49c173 | 2013-10-04 10:52:36 +0200 | [diff] [blame] | 706 | FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | | 
|  | 707 | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 708 | netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 709 | flexcan_write(reg_mcr, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 710 |  | 
|  | 711 | /* | 
|  | 712 | * CTRL | 
|  | 713 | * | 
|  | 714 | * disable timer sync feature | 
|  | 715 | * | 
|  | 716 | * disable auto busoff recovery | 
|  | 717 | * transmit lowest buffer first | 
|  | 718 | * | 
|  | 719 | * enable tx and rx warning interrupt | 
|  | 720 | * enable bus off interrupt | 
|  | 721 | * (== FLEXCAN_CTRL_ERR_STATE) | 
|  | 722 | * | 
|  | 723 | * _note_: we enable the "error interrupt" | 
|  | 724 | * (FLEXCAN_CTRL_ERR_MSK), too. Otherwise we don't get any | 
|  | 725 | * warning or bus passive interrupts. | 
|  | 726 | */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 727 | reg_ctrl = flexcan_read(®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 728 | reg_ctrl &= ~FLEXCAN_CTRL_TSYN; | 
|  | 729 | reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | | 
|  | 730 | FLEXCAN_CTRL_ERR_STATE | FLEXCAN_CTRL_ERR_MSK; | 
|  | 731 |  | 
|  | 732 | /* save for later use */ | 
|  | 733 | priv->reg_ctrl_default = reg_ctrl; | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 734 | netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 735 | flexcan_write(reg_ctrl, ®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 736 |  | 
| Marc Kleine-Budde | f49c173 | 2013-10-04 10:52:36 +0200 | [diff] [blame] | 737 | /* Abort any pending TX, mark Mailbox as INACTIVE */ | 
|  | 738 | flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), | 
|  | 739 | ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); | 
|  | 740 |  | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 741 | /* acceptance mask/acceptance code (accept everything) */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 742 | flexcan_write(0x0, ®s->rxgmask); | 
|  | 743 | flexcan_write(0x0, ®s->rx14mask); | 
|  | 744 | flexcan_write(0x0, ®s->rx15mask); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 745 |  | 
|  | 746 | flexcan_transceiver_switch(priv, 1); | 
|  | 747 |  | 
|  | 748 | /* synchronize with the can bus */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 749 | reg_mcr = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 750 | reg_mcr &= ~FLEXCAN_MCR_HALT; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 751 | flexcan_write(reg_mcr, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 752 |  | 
|  | 753 | priv->can.state = CAN_STATE_ERROR_ACTIVE; | 
|  | 754 |  | 
|  | 755 | /* enable FIFO interrupts */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 756 | flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 757 |  | 
|  | 758 | /* print chip status */ | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 759 | netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, | 
|  | 760 | flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 761 |  | 
|  | 762 | return 0; | 
|  | 763 |  | 
|  | 764 | out: | 
|  | 765 | flexcan_chip_disable(priv); | 
|  | 766 | return err; | 
|  | 767 | } | 
|  | 768 |  | 
|  | 769 | /* | 
|  | 770 | * flexcan_chip_stop | 
|  | 771 | * | 
|  | 772 | * this functions is entered with clocks enabled | 
|  | 773 | * | 
|  | 774 | */ | 
|  | 775 | static void flexcan_chip_stop(struct net_device *dev) | 
|  | 776 | { | 
|  | 777 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 778 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 779 | u32 reg; | 
|  | 780 |  | 
|  | 781 | /* Disable all interrupts */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 782 | flexcan_write(0, ®s->imask1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 783 |  | 
|  | 784 | /* Disable + halt module */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 785 | reg = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 786 | reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 787 | flexcan_write(reg, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 788 |  | 
|  | 789 | flexcan_transceiver_switch(priv, 0); | 
|  | 790 | priv->can.state = CAN_STATE_STOPPED; | 
|  | 791 |  | 
|  | 792 | return; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | static int flexcan_open(struct net_device *dev) | 
|  | 796 | { | 
|  | 797 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 798 | int err; | 
|  | 799 |  | 
| Shawn Guo | e735489 | 2011-12-20 14:05:52 +0800 | [diff] [blame] | 800 | clk_prepare_enable(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 801 |  | 
|  | 802 | err = open_candev(dev); | 
|  | 803 | if (err) | 
|  | 804 | goto out; | 
|  | 805 |  | 
|  | 806 | err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev); | 
|  | 807 | if (err) | 
| Marc Kleine-Budde | f8d17b6 | 2014-02-28 14:52:01 +0100 | [diff] [blame] | 808 | goto out_free_irq; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 809 |  | 
|  | 810 | /* start chip and queuing */ | 
|  | 811 | err = flexcan_chip_start(dev); | 
|  | 812 | if (err) | 
|  | 813 | goto out_close; | 
|  | 814 | napi_enable(&priv->napi); | 
|  | 815 | netif_start_queue(dev); | 
|  | 816 |  | 
|  | 817 | return 0; | 
|  | 818 |  | 
| Marc Kleine-Budde | f8d17b6 | 2014-02-28 14:52:01 +0100 | [diff] [blame] | 819 | out_free_irq: | 
|  | 820 | free_irq(dev->irq, dev); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 821 | out_close: | 
|  | 822 | close_candev(dev); | 
|  | 823 | out: | 
| Shawn Guo | e735489 | 2011-12-20 14:05:52 +0800 | [diff] [blame] | 824 | clk_disable_unprepare(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 825 |  | 
|  | 826 | return err; | 
|  | 827 | } | 
|  | 828 |  | 
|  | 829 | static int flexcan_close(struct net_device *dev) | 
|  | 830 | { | 
|  | 831 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 832 |  | 
|  | 833 | netif_stop_queue(dev); | 
|  | 834 | napi_disable(&priv->napi); | 
|  | 835 | flexcan_chip_stop(dev); | 
|  | 836 |  | 
|  | 837 | free_irq(dev->irq, dev); | 
| Shawn Guo | e735489 | 2011-12-20 14:05:52 +0800 | [diff] [blame] | 838 | clk_disable_unprepare(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 839 |  | 
|  | 840 | close_candev(dev); | 
|  | 841 |  | 
|  | 842 | return 0; | 
|  | 843 | } | 
|  | 844 |  | 
|  | 845 | static int flexcan_set_mode(struct net_device *dev, enum can_mode mode) | 
|  | 846 | { | 
|  | 847 | int err; | 
|  | 848 |  | 
|  | 849 | switch (mode) { | 
|  | 850 | case CAN_MODE_START: | 
|  | 851 | err = flexcan_chip_start(dev); | 
|  | 852 | if (err) | 
|  | 853 | return err; | 
|  | 854 |  | 
|  | 855 | netif_wake_queue(dev); | 
|  | 856 | break; | 
|  | 857 |  | 
|  | 858 | default: | 
|  | 859 | return -EOPNOTSUPP; | 
|  | 860 | } | 
|  | 861 |  | 
|  | 862 | return 0; | 
|  | 863 | } | 
|  | 864 |  | 
|  | 865 | static const struct net_device_ops flexcan_netdev_ops = { | 
|  | 866 | .ndo_open	= flexcan_open, | 
|  | 867 | .ndo_stop	= flexcan_close, | 
|  | 868 | .ndo_start_xmit	= flexcan_start_xmit, | 
|  | 869 | }; | 
|  | 870 |  | 
|  | 871 | static int __devinit register_flexcandev(struct net_device *dev) | 
|  | 872 | { | 
|  | 873 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 874 | struct flexcan_regs __iomem *regs = priv->base; | 
|  | 875 | u32 reg, err; | 
|  | 876 |  | 
| Shawn Guo | e735489 | 2011-12-20 14:05:52 +0800 | [diff] [blame] | 877 | clk_prepare_enable(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 878 |  | 
|  | 879 | /* select "bus clock", chip must be disabled */ | 
|  | 880 | flexcan_chip_disable(priv); | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 881 | reg = flexcan_read(®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 882 | reg |= FLEXCAN_CTRL_CLK_SRC; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 883 | flexcan_write(reg, ®s->ctrl); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 884 |  | 
|  | 885 | flexcan_chip_enable(priv); | 
|  | 886 |  | 
|  | 887 | /* set freeze, halt and activate FIFO, restrict register access */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 888 | reg = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 889 | reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | | 
|  | 890 | FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 891 | flexcan_write(reg, ®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 892 |  | 
|  | 893 | /* | 
|  | 894 | * Currently we only support newer versions of this core | 
|  | 895 | * featuring a RX FIFO. Older cores found on some Coldfire | 
|  | 896 | * derivates are not yet supported. | 
|  | 897 | */ | 
| holt@sgi.com | 61e271e | 2011-08-16 17:32:20 +0000 | [diff] [blame] | 898 | reg = flexcan_read(®s->mcr); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 899 | if (!(reg & FLEXCAN_MCR_FEN)) { | 
| Wolfgang Grandegger | aabdfd6 | 2012-02-01 11:02:05 +0100 | [diff] [blame] | 900 | netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 901 | err = -ENODEV; | 
|  | 902 | goto out; | 
|  | 903 | } | 
|  | 904 |  | 
|  | 905 | err = register_candev(dev); | 
|  | 906 |  | 
|  | 907 | out: | 
|  | 908 | /* disable core and turn off clocks */ | 
|  | 909 | flexcan_chip_disable(priv); | 
| Shawn Guo | e735489 | 2011-12-20 14:05:52 +0800 | [diff] [blame] | 910 | clk_disable_unprepare(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 911 |  | 
|  | 912 | return err; | 
|  | 913 | } | 
|  | 914 |  | 
|  | 915 | static void __devexit unregister_flexcandev(struct net_device *dev) | 
|  | 916 | { | 
|  | 917 | unregister_candev(dev); | 
|  | 918 | } | 
|  | 919 |  | 
|  | 920 | static int __devinit flexcan_probe(struct platform_device *pdev) | 
|  | 921 | { | 
|  | 922 | struct net_device *dev; | 
|  | 923 | struct flexcan_priv *priv; | 
|  | 924 | struct resource *mem; | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 925 | struct clk *clk = NULL; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 926 | void __iomem *base; | 
|  | 927 | resource_size_t mem_size; | 
|  | 928 | int err, irq; | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 929 | u32 clock_freq = 0; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 930 |  | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 931 | if (pdev->dev.of_node) { | 
| Hui Wang | 70f6c28 | 2012-06-27 16:19:18 +0800 | [diff] [blame] | 932 | const __be32 *clock_freq_p; | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 933 |  | 
|  | 934 | clock_freq_p = of_get_property(pdev->dev.of_node, | 
|  | 935 | "clock-frequency", NULL); | 
|  | 936 | if (clock_freq_p) | 
| Hui Wang | 70f6c28 | 2012-06-27 16:19:18 +0800 | [diff] [blame] | 937 | clock_freq = be32_to_cpup(clock_freq_p); | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 938 | } | 
|  | 939 |  | 
|  | 940 | if (!clock_freq) { | 
|  | 941 | clk = clk_get(&pdev->dev, NULL); | 
|  | 942 | if (IS_ERR(clk)) { | 
|  | 943 | dev_err(&pdev->dev, "no clock defined\n"); | 
|  | 944 | err = PTR_ERR(clk); | 
|  | 945 | goto failed_clock; | 
|  | 946 | } | 
|  | 947 | clock_freq = clk_get_rate(clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 948 | } | 
|  | 949 |  | 
|  | 950 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 951 | irq = platform_get_irq(pdev, 0); | 
|  | 952 | if (!mem || irq <= 0) { | 
|  | 953 | err = -ENODEV; | 
|  | 954 | goto failed_get; | 
|  | 955 | } | 
|  | 956 |  | 
|  | 957 | mem_size = resource_size(mem); | 
|  | 958 | if (!request_mem_region(mem->start, mem_size, pdev->name)) { | 
|  | 959 | err = -EBUSY; | 
| Julia Lawall | 2e4ceec | 2011-06-01 19:48:50 +0000 | [diff] [blame] | 960 | goto failed_get; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 961 | } | 
|  | 962 |  | 
|  | 963 | base = ioremap(mem->start, mem_size); | 
|  | 964 | if (!base) { | 
|  | 965 | err = -ENOMEM; | 
|  | 966 | goto failed_map; | 
|  | 967 | } | 
|  | 968 |  | 
| Reuben Dowle | 9a12349 | 2011-11-01 11:18:03 +1300 | [diff] [blame] | 969 | dev = alloc_candev(sizeof(struct flexcan_priv), 1); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 970 | if (!dev) { | 
|  | 971 | err = -ENOMEM; | 
|  | 972 | goto failed_alloc; | 
|  | 973 | } | 
|  | 974 |  | 
|  | 975 | dev->netdev_ops = &flexcan_netdev_ops; | 
|  | 976 | dev->irq = irq; | 
| Reuben Dowle | 9a12349 | 2011-11-01 11:18:03 +1300 | [diff] [blame] | 977 | dev->flags |= IFF_ECHO; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 978 |  | 
|  | 979 | priv = netdev_priv(dev); | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 980 | priv->can.clock.freq = clock_freq; | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 981 | priv->can.bittiming_const = &flexcan_bittiming_const; | 
|  | 982 | priv->can.do_set_mode = flexcan_set_mode; | 
|  | 983 | priv->can.do_get_berr_counter = flexcan_get_berr_counter; | 
|  | 984 | priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | | 
|  | 985 | CAN_CTRLMODE_LISTENONLY	| CAN_CTRLMODE_3_SAMPLES | | 
|  | 986 | CAN_CTRLMODE_BERR_REPORTING; | 
|  | 987 | priv->base = base; | 
|  | 988 | priv->dev = dev; | 
|  | 989 | priv->clk = clk; | 
|  | 990 | priv->pdata = pdev->dev.platform_data; | 
|  | 991 |  | 
|  | 992 | netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); | 
|  | 993 |  | 
|  | 994 | dev_set_drvdata(&pdev->dev, dev); | 
|  | 995 | SET_NETDEV_DEV(dev, &pdev->dev); | 
|  | 996 |  | 
|  | 997 | err = register_flexcandev(dev); | 
|  | 998 | if (err) { | 
|  | 999 | dev_err(&pdev->dev, "registering netdev failed\n"); | 
|  | 1000 | goto failed_register; | 
|  | 1001 | } | 
|  | 1002 |  | 
|  | 1003 | dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", | 
|  | 1004 | priv->base, dev->irq); | 
|  | 1005 |  | 
|  | 1006 | return 0; | 
|  | 1007 |  | 
|  | 1008 | failed_register: | 
|  | 1009 | free_candev(dev); | 
|  | 1010 | failed_alloc: | 
|  | 1011 | iounmap(base); | 
|  | 1012 | failed_map: | 
|  | 1013 | release_mem_region(mem->start, mem_size); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1014 | failed_get: | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 1015 | if (clk) | 
|  | 1016 | clk_put(clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1017 | failed_clock: | 
|  | 1018 | return err; | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | static int __devexit flexcan_remove(struct platform_device *pdev) | 
|  | 1022 | { | 
|  | 1023 | struct net_device *dev = platform_get_drvdata(pdev); | 
|  | 1024 | struct flexcan_priv *priv = netdev_priv(dev); | 
|  | 1025 | struct resource *mem; | 
|  | 1026 |  | 
|  | 1027 | unregister_flexcandev(dev); | 
|  | 1028 | platform_set_drvdata(pdev, NULL); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1029 | iounmap(priv->base); | 
|  | 1030 |  | 
|  | 1031 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 1032 | release_mem_region(mem->start, resource_size(mem)); | 
|  | 1033 |  | 
| holt@sgi.com | 97efe9a | 2011-08-16 17:32:23 +0000 | [diff] [blame] | 1034 | if (priv->clk) | 
|  | 1035 | clk_put(priv->clk); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1036 |  | 
| Marc Kleine-Budde | 9a27586 | 2010-10-21 05:07:58 +0000 | [diff] [blame] | 1037 | free_candev(dev); | 
|  | 1038 |  | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1039 | return 0; | 
|  | 1040 | } | 
|  | 1041 |  | 
| holt@sgi.com | c8aef4c | 2011-08-16 17:32:22 +0000 | [diff] [blame] | 1042 | static struct of_device_id flexcan_of_match[] = { | 
|  | 1043 | { | 
|  | 1044 | .compatible = "fsl,p1010-flexcan", | 
|  | 1045 | }, | 
|  | 1046 | {}, | 
|  | 1047 | }; | 
|  | 1048 |  | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1049 | static struct platform_driver flexcan_driver = { | 
| holt@sgi.com | c8aef4c | 2011-08-16 17:32:22 +0000 | [diff] [blame] | 1050 | .driver = { | 
|  | 1051 | .name = DRV_NAME, | 
|  | 1052 | .owner = THIS_MODULE, | 
|  | 1053 | .of_match_table = flexcan_of_match, | 
|  | 1054 | }, | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1055 | .probe = flexcan_probe, | 
|  | 1056 | .remove = __devexit_p(flexcan_remove), | 
|  | 1057 | }; | 
|  | 1058 |  | 
| Axel Lin | 871d337 | 2011-11-27 15:42:31 +0000 | [diff] [blame] | 1059 | module_platform_driver(flexcan_driver); | 
| Marc Kleine-Budde | e955cea | 2009-07-29 10:20:10 +0200 | [diff] [blame] | 1060 |  | 
|  | 1061 | MODULE_AUTHOR("Sascha Hauer <kernel@pengutronix.de>, " | 
|  | 1062 | "Marc Kleine-Budde <kernel@pengutronix.de>"); | 
|  | 1063 | MODULE_LICENSE("GPL v2"); | 
|  | 1064 | MODULE_DESCRIPTION("CAN port driver for flexcan based chip"); |