Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 1 | /* |
| 2 | * PCI bus driver for Bosch C_CAN/D_CAN controller |
| 3 | * |
| 4 | * Copyright (C) 2012 Federico Vaga <federico.vaga@gmail.com> |
| 5 | * |
| 6 | * Borrowed from c_can_platform.c |
| 7 | * |
| 8 | * This file is licensed under the terms of the GNU General Public |
| 9 | * License version 2. This program is licensed "as is" without any |
| 10 | * warranty of any kind, whether express or implied. |
| 11 | */ |
| 12 | |
| 13 | #include <linux/kernel.h> |
| 14 | #include <linux/module.h> |
| 15 | #include <linux/netdevice.h> |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 16 | #include <linux/pci.h> |
| 17 | |
| 18 | #include <linux/can/dev.h> |
| 19 | |
| 20 | #include "c_can.h" |
| 21 | |
| 22 | enum c_can_pci_reg_align { |
| 23 | C_CAN_REG_ALIGN_16, |
| 24 | C_CAN_REG_ALIGN_32, |
| 25 | }; |
| 26 | |
| 27 | struct c_can_pci_data { |
| 28 | /* Specify if is C_CAN or D_CAN */ |
| 29 | enum c_can_dev_id type; |
| 30 | /* Set the register alignment in the memory */ |
| 31 | enum c_can_pci_reg_align reg_align; |
Marc Kleine-Budde | 1aa2d1d | 2012-06-20 06:04:26 +0000 | [diff] [blame] | 32 | /* Set the frequency */ |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 33 | unsigned int freq; |
| 34 | }; |
| 35 | |
| 36 | /* |
| 37 | * 16-bit c_can registers can be arranged differently in the memory |
| 38 | * architecture of different implementations. For example: 16-bit |
| 39 | * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. |
| 40 | * Handle the same by providing a common read/write interface. |
| 41 | */ |
| 42 | static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv, |
| 43 | enum reg index) |
| 44 | { |
| 45 | return readw(priv->base + priv->regs[index]); |
| 46 | } |
| 47 | |
| 48 | static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv, |
| 49 | enum reg index, u16 val) |
| 50 | { |
| 51 | writew(val, priv->base + priv->regs[index]); |
| 52 | } |
| 53 | |
| 54 | static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv, |
| 55 | enum reg index) |
| 56 | { |
| 57 | return readw(priv->base + 2 * priv->regs[index]); |
| 58 | } |
| 59 | |
| 60 | static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv, |
| 61 | enum reg index, u16 val) |
| 62 | { |
| 63 | writew(val, priv->base + 2 * priv->regs[index]); |
| 64 | } |
| 65 | |
Bill Pemberton | 3c8ac0f | 2012-12-03 09:22:44 -0500 | [diff] [blame] | 66 | static int c_can_pci_probe(struct pci_dev *pdev, |
Greg Kroah-Hartman | 1dd06ae | 2012-12-06 14:30:56 +0000 | [diff] [blame] | 67 | const struct pci_device_id *ent) |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 68 | { |
| 69 | struct c_can_pci_data *c_can_pci_data = (void *)ent->driver_data; |
| 70 | struct c_can_priv *priv; |
| 71 | struct net_device *dev; |
| 72 | void __iomem *addr; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 73 | int ret; |
| 74 | |
| 75 | ret = pci_enable_device(pdev); |
| 76 | if (ret) { |
| 77 | dev_err(&pdev->dev, "pci_enable_device FAILED\n"); |
| 78 | goto out; |
| 79 | } |
| 80 | |
| 81 | ret = pci_request_regions(pdev, KBUILD_MODNAME); |
| 82 | if (ret) { |
| 83 | dev_err(&pdev->dev, "pci_request_regions FAILED\n"); |
| 84 | goto out_disable_device; |
| 85 | } |
| 86 | |
| 87 | pci_set_master(pdev); |
| 88 | pci_enable_msi(pdev); |
| 89 | |
| 90 | addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); |
| 91 | if (!addr) { |
| 92 | dev_err(&pdev->dev, |
| 93 | "device has no PCI memory resources, " |
| 94 | "failing adapter\n"); |
| 95 | ret = -ENOMEM; |
| 96 | goto out_release_regions; |
| 97 | } |
| 98 | |
| 99 | /* allocate the c_can device */ |
| 100 | dev = alloc_c_can_dev(); |
| 101 | if (!dev) { |
| 102 | ret = -ENOMEM; |
| 103 | goto out_iounmap; |
| 104 | } |
| 105 | |
| 106 | priv = netdev_priv(dev); |
| 107 | pci_set_drvdata(pdev, dev); |
| 108 | SET_NETDEV_DEV(dev, &pdev->dev); |
| 109 | |
| 110 | dev->irq = pdev->irq; |
| 111 | priv->base = addr; |
| 112 | |
| 113 | if (!c_can_pci_data->freq) { |
Marc Kleine-Budde | 1aa2d1d | 2012-06-20 06:04:26 +0000 | [diff] [blame] | 114 | dev_err(&pdev->dev, "no clock frequency defined\n"); |
| 115 | ret = -ENODEV; |
| 116 | goto out_free_c_can; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 117 | } else { |
| 118 | priv->can.clock.freq = c_can_pci_data->freq; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | /* Configure CAN type */ |
| 122 | switch (c_can_pci_data->type) { |
AnilKumar Ch | f27b1db | 2012-08-02 18:43:09 +0530 | [diff] [blame] | 123 | case BOSCH_C_CAN: |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 124 | priv->regs = reg_map_c_can; |
| 125 | break; |
AnilKumar Ch | f27b1db | 2012-08-02 18:43:09 +0530 | [diff] [blame] | 126 | case BOSCH_D_CAN: |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 127 | priv->regs = reg_map_d_can; |
| 128 | priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; |
| 129 | break; |
| 130 | default: |
| 131 | ret = -EINVAL; |
Marc Kleine-Budde | 1aa2d1d | 2012-06-20 06:04:26 +0000 | [diff] [blame] | 132 | goto out_free_c_can; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 133 | } |
| 134 | |
| 135 | /* Configure access to registers */ |
| 136 | switch (c_can_pci_data->reg_align) { |
| 137 | case C_CAN_REG_ALIGN_32: |
| 138 | priv->read_reg = c_can_pci_read_reg_aligned_to_32bit; |
| 139 | priv->write_reg = c_can_pci_write_reg_aligned_to_32bit; |
| 140 | break; |
| 141 | case C_CAN_REG_ALIGN_16: |
| 142 | priv->read_reg = c_can_pci_read_reg_aligned_to_16bit; |
| 143 | priv->write_reg = c_can_pci_write_reg_aligned_to_16bit; |
| 144 | break; |
| 145 | default: |
| 146 | ret = -EINVAL; |
Marc Kleine-Budde | 1aa2d1d | 2012-06-20 06:04:26 +0000 | [diff] [blame] | 147 | goto out_free_c_can; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | ret = register_c_can_dev(dev); |
| 151 | if (ret) { |
| 152 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", |
| 153 | KBUILD_MODNAME, ret); |
Marc Kleine-Budde | 1aa2d1d | 2012-06-20 06:04:26 +0000 | [diff] [blame] | 154 | goto out_free_c_can; |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", |
| 158 | KBUILD_MODNAME, priv->regs, dev->irq); |
| 159 | |
| 160 | return 0; |
| 161 | |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 162 | out_free_c_can: |
| 163 | pci_set_drvdata(pdev, NULL); |
| 164 | free_c_can_dev(dev); |
| 165 | out_iounmap: |
| 166 | pci_iounmap(pdev, addr); |
| 167 | out_release_regions: |
| 168 | pci_disable_msi(pdev); |
| 169 | pci_clear_master(pdev); |
| 170 | pci_release_regions(pdev); |
| 171 | out_disable_device: |
| 172 | pci_disable_device(pdev); |
| 173 | out: |
| 174 | return ret; |
| 175 | } |
| 176 | |
Bill Pemberton | 3c8ac0f | 2012-12-03 09:22:44 -0500 | [diff] [blame] | 177 | static void c_can_pci_remove(struct pci_dev *pdev) |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 178 | { |
| 179 | struct net_device *dev = pci_get_drvdata(pdev); |
| 180 | struct c_can_priv *priv = netdev_priv(dev); |
| 181 | |
| 182 | unregister_c_can_dev(dev); |
| 183 | |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 184 | pci_set_drvdata(pdev, NULL); |
| 185 | free_c_can_dev(dev); |
| 186 | |
| 187 | pci_iounmap(pdev, priv->base); |
| 188 | pci_disable_msi(pdev); |
| 189 | pci_clear_master(pdev); |
| 190 | pci_release_regions(pdev); |
| 191 | pci_disable_device(pdev); |
| 192 | } |
| 193 | |
| 194 | static struct c_can_pci_data c_can_sta2x11= { |
AnilKumar Ch | f27b1db | 2012-08-02 18:43:09 +0530 | [diff] [blame] | 195 | .type = BOSCH_C_CAN, |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 196 | .reg_align = C_CAN_REG_ALIGN_32, |
| 197 | .freq = 52000000, /* 52 Mhz */ |
| 198 | }; |
| 199 | |
| 200 | #define C_CAN_ID(_vend, _dev, _driverdata) { \ |
| 201 | PCI_DEVICE(_vend, _dev), \ |
| 202 | .driver_data = (unsigned long)&_driverdata, \ |
| 203 | } |
| 204 | static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = { |
| 205 | C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN, |
| 206 | c_can_sta2x11), |
| 207 | {}, |
| 208 | }; |
| 209 | static struct pci_driver c_can_pci_driver = { |
| 210 | .name = KBUILD_MODNAME, |
| 211 | .id_table = c_can_pci_tbl, |
| 212 | .probe = c_can_pci_probe, |
Bill Pemberton | 3c8ac0f | 2012-12-03 09:22:44 -0500 | [diff] [blame] | 213 | .remove = c_can_pci_remove, |
Federico Vaga | 5b92da0 | 2012-06-14 13:43:42 +0200 | [diff] [blame] | 214 | }; |
| 215 | |
| 216 | module_pci_driver(c_can_pci_driver); |
| 217 | |
| 218 | MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>"); |
| 219 | MODULE_LICENSE("GPL v2"); |
| 220 | MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller"); |
| 221 | MODULE_DEVICE_TABLE(pci, c_can_pci_tbl); |