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