1 /* 2 * Driver for CC770 and AN82527 CAN controllers on the platform bus 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 /* 17 * If platform data are used you should have similar definitions 18 * in your board-specific code: 19 * 20 * static struct cc770_platform_data myboard_cc770_pdata = { 21 * .osc_freq = 16000000, 22 * .cir = 0x41, 23 * .cor = 0x20, 24 * .bcr = 0x40, 25 * }; 26 * 27 * Please see include/linux/can/platform/cc770.h for description of 28 * above fields. 29 * 30 * If the device tree is used, you need a CAN node definition in your 31 * DTS file similar to: 32 * 33 * can@3,100 { 34 * compatible = "bosch,cc770"; 35 * reg = <3 0x100 0x80>; 36 * interrupts = <2 0>; 37 * interrupt-parent = <&mpic>; 38 * bosch,external-clock-frequency = <16000000>; 39 * }; 40 * 41 * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further 42 * information. 43 */ 44 45 #include <linux/kernel.h> 46 #include <linux/module.h> 47 #include <linux/interrupt.h> 48 #include <linux/netdevice.h> 49 #include <linux/delay.h> 50 #include <linux/platform_device.h> 51 #include <linux/of.h> 52 #include <linux/can.h> 53 #include <linux/can/dev.h> 54 #include <linux/can/platform/cc770.h> 55 56 #include "cc770.h" 57 58 #define DRV_NAME "cc770_platform" 59 60 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 61 MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus"); 62 MODULE_LICENSE("GPL v2"); 63 MODULE_ALIAS("platform:" DRV_NAME); 64 65 #define CC770_PLATFORM_CAN_CLOCK 16000000 66 67 static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg) 68 { 69 return ioread8(priv->reg_base + reg); 70 } 71 72 static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg, 73 u8 val) 74 { 75 iowrite8(val, priv->reg_base + reg); 76 } 77 78 static int cc770_get_of_node_data(struct platform_device *pdev, 79 struct cc770_priv *priv) 80 { 81 struct device_node *np = pdev->dev.of_node; 82 const u32 *prop; 83 int prop_size; 84 u32 clkext; 85 86 prop = of_get_property(np, "bosch,external-clock-frequency", 87 &prop_size); 88 if (prop && (prop_size == sizeof(u32))) 89 clkext = *prop; 90 else 91 clkext = CC770_PLATFORM_CAN_CLOCK; /* default */ 92 priv->can.clock.freq = clkext; 93 94 /* The system clock may not exceed 10 MHz */ 95 if (priv->can.clock.freq > 10000000) { 96 priv->cpu_interface |= CPUIF_DSC; 97 priv->can.clock.freq /= 2; 98 } 99 100 /* The memory clock may not exceed 8 MHz */ 101 if (priv->can.clock.freq > 8000000) 102 priv->cpu_interface |= CPUIF_DMC; 103 104 if (of_get_property(np, "bosch,divide-memory-clock", NULL)) 105 priv->cpu_interface |= CPUIF_DMC; 106 if (of_get_property(np, "bosch,iso-low-speed-mux", NULL)) 107 priv->cpu_interface |= CPUIF_MUX; 108 109 if (!of_get_property(np, "bosch,no-comperator-bypass", NULL)) 110 priv->bus_config |= BUSCFG_CBY; 111 if (of_get_property(np, "bosch,disconnect-rx0-input", NULL)) 112 priv->bus_config |= BUSCFG_DR0; 113 if (of_get_property(np, "bosch,disconnect-rx1-input", NULL)) 114 priv->bus_config |= BUSCFG_DR1; 115 if (of_get_property(np, "bosch,disconnect-tx1-output", NULL)) 116 priv->bus_config |= BUSCFG_DT1; 117 if (of_get_property(np, "bosch,polarity-dominant", NULL)) 118 priv->bus_config |= BUSCFG_POL; 119 120 prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size); 121 if (prop && (prop_size == sizeof(u32)) && *prop > 0) { 122 u32 cdv = clkext / *prop; 123 int slew; 124 125 if (cdv > 0 && cdv < 16) { 126 priv->cpu_interface |= CPUIF_CEN; 127 priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK; 128 129 prop = of_get_property(np, "bosch,slew-rate", 130 &prop_size); 131 if (prop && (prop_size == sizeof(u32))) { 132 slew = *prop; 133 } else { 134 /* Determine default slew rate */ 135 slew = (CLKOUT_SL_MASK >> 136 CLKOUT_SL_SHIFT) - 137 ((cdv * clkext - 1) / 8000000); 138 if (slew < 0) 139 slew = 0; 140 } 141 priv->clkout |= (slew << CLKOUT_SL_SHIFT) & 142 CLKOUT_SL_MASK; 143 } else { 144 dev_dbg(&pdev->dev, "invalid clock-out-frequency\n"); 145 } 146 } 147 148 return 0; 149 } 150 151 static int cc770_get_platform_data(struct platform_device *pdev, 152 struct cc770_priv *priv) 153 { 154 155 struct cc770_platform_data *pdata = pdev->dev.platform_data; 156 157 priv->can.clock.freq = pdata->osc_freq; 158 if (priv->cpu_interface & CPUIF_DSC) 159 priv->can.clock.freq /= 2; 160 priv->clkout = pdata->cor; 161 priv->bus_config = pdata->bcr; 162 priv->cpu_interface = pdata->cir; 163 164 return 0; 165 } 166 167 static int cc770_platform_probe(struct platform_device *pdev) 168 { 169 struct net_device *dev; 170 struct cc770_priv *priv; 171 struct resource *mem; 172 resource_size_t mem_size; 173 void __iomem *base; 174 int err, irq; 175 176 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 177 irq = platform_get_irq(pdev, 0); 178 if (!mem || irq <= 0) 179 return -ENODEV; 180 181 mem_size = resource_size(mem); 182 if (!request_mem_region(mem->start, mem_size, pdev->name)) 183 return -EBUSY; 184 185 base = ioremap(mem->start, mem_size); 186 if (!base) { 187 err = -ENOMEM; 188 goto exit_release_mem; 189 } 190 191 dev = alloc_cc770dev(0); 192 if (!dev) { 193 err = -ENOMEM; 194 goto exit_unmap_mem; 195 } 196 197 dev->irq = irq; 198 priv = netdev_priv(dev); 199 priv->read_reg = cc770_platform_read_reg; 200 priv->write_reg = cc770_platform_write_reg; 201 priv->irq_flags = IRQF_SHARED; 202 priv->reg_base = base; 203 204 if (pdev->dev.of_node) 205 err = cc770_get_of_node_data(pdev, priv); 206 else if (pdev->dev.platform_data) 207 err = cc770_get_platform_data(pdev, priv); 208 else 209 err = -ENODEV; 210 if (err) 211 goto exit_free_cc770; 212 213 dev_dbg(&pdev->dev, 214 "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x " 215 "bus_config=0x%02x clkout=0x%02x\n", 216 priv->reg_base, dev->irq, priv->can.clock.freq, 217 priv->cpu_interface, priv->bus_config, priv->clkout); 218 219 platform_set_drvdata(pdev, dev); 220 SET_NETDEV_DEV(dev, &pdev->dev); 221 222 err = register_cc770dev(dev); 223 if (err) { 224 dev_err(&pdev->dev, 225 "couldn't register CC700 device (err=%d)\n", err); 226 goto exit_free_cc770; 227 } 228 229 return 0; 230 231 exit_free_cc770: 232 free_cc770dev(dev); 233 exit_unmap_mem: 234 iounmap(base); 235 exit_release_mem: 236 release_mem_region(mem->start, mem_size); 237 238 return err; 239 } 240 241 static int cc770_platform_remove(struct platform_device *pdev) 242 { 243 struct net_device *dev = platform_get_drvdata(pdev); 244 struct cc770_priv *priv = netdev_priv(dev); 245 struct resource *mem; 246 247 unregister_cc770dev(dev); 248 iounmap(priv->reg_base); 249 free_cc770dev(dev); 250 251 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 252 release_mem_region(mem->start, resource_size(mem)); 253 254 return 0; 255 } 256 257 static struct of_device_id cc770_platform_table[] = { 258 {.compatible = "bosch,cc770"}, /* CC770 from Bosch */ 259 {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ 260 {}, 261 }; 262 MODULE_DEVICE_TABLE(of, cc770_platform_table); 263 264 static struct platform_driver cc770_platform_driver = { 265 .driver = { 266 .name = DRV_NAME, 267 .owner = THIS_MODULE, 268 .of_match_table = cc770_platform_table, 269 }, 270 .probe = cc770_platform_probe, 271 .remove = cc770_platform_remove, 272 }; 273 274 module_platform_driver(cc770_platform_driver); 275