Commit | Line | Data |
---|---|---|
5b92da04 FV |
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> | |
5b92da04 FV |
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; | |
1aa2d1da | 32 | /* Set the frequency */ |
5b92da04 FV |
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 | ||
3c8ac0f2 | 66 | static int c_can_pci_probe(struct pci_dev *pdev, |
1dd06ae8 | 67 | const struct pci_device_id *ent) |
5b92da04 FV |
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; | |
5b92da04 FV |
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; | |
165cc033 | 112 | priv->device = &pdev->dev; |
5b92da04 FV |
113 | |
114 | if (!c_can_pci_data->freq) { | |
1aa2d1da MKB |
115 | dev_err(&pdev->dev, "no clock frequency defined\n"); |
116 | ret = -ENODEV; | |
117 | goto out_free_c_can; | |
5b92da04 FV |
118 | } else { |
119 | priv->can.clock.freq = c_can_pci_data->freq; | |
5b92da04 FV |
120 | } |
121 | ||
122 | /* Configure CAN type */ | |
123 | switch (c_can_pci_data->type) { | |
f27b1db9 | 124 | case BOSCH_C_CAN: |
5b92da04 FV |
125 | priv->regs = reg_map_c_can; |
126 | break; | |
f27b1db9 | 127 | case BOSCH_D_CAN: |
5b92da04 FV |
128 | priv->regs = reg_map_d_can; |
129 | priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; | |
130 | break; | |
131 | default: | |
132 | ret = -EINVAL; | |
1aa2d1da | 133 | goto out_free_c_can; |
5b92da04 FV |
134 | } |
135 | ||
136 | /* Configure access to registers */ | |
137 | switch (c_can_pci_data->reg_align) { | |
138 | case C_CAN_REG_ALIGN_32: | |
139 | priv->read_reg = c_can_pci_read_reg_aligned_to_32bit; | |
140 | priv->write_reg = c_can_pci_write_reg_aligned_to_32bit; | |
141 | break; | |
142 | case C_CAN_REG_ALIGN_16: | |
143 | priv->read_reg = c_can_pci_read_reg_aligned_to_16bit; | |
144 | priv->write_reg = c_can_pci_write_reg_aligned_to_16bit; | |
145 | break; | |
146 | default: | |
147 | ret = -EINVAL; | |
1aa2d1da | 148 | goto out_free_c_can; |
5b92da04 FV |
149 | } |
150 | ||
151 | ret = register_c_can_dev(dev); | |
152 | if (ret) { | |
153 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", | |
154 | KBUILD_MODNAME, ret); | |
1aa2d1da | 155 | goto out_free_c_can; |
5b92da04 FV |
156 | } |
157 | ||
158 | dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", | |
159 | KBUILD_MODNAME, priv->regs, dev->irq); | |
160 | ||
161 | return 0; | |
162 | ||
5b92da04 FV |
163 | out_free_c_can: |
164 | pci_set_drvdata(pdev, NULL); | |
165 | free_c_can_dev(dev); | |
166 | out_iounmap: | |
167 | pci_iounmap(pdev, addr); | |
168 | out_release_regions: | |
169 | pci_disable_msi(pdev); | |
170 | pci_clear_master(pdev); | |
171 | pci_release_regions(pdev); | |
172 | out_disable_device: | |
173 | pci_disable_device(pdev); | |
174 | out: | |
175 | return ret; | |
176 | } | |
177 | ||
3c8ac0f2 | 178 | static void c_can_pci_remove(struct pci_dev *pdev) |
5b92da04 FV |
179 | { |
180 | struct net_device *dev = pci_get_drvdata(pdev); | |
181 | struct c_can_priv *priv = netdev_priv(dev); | |
182 | ||
183 | unregister_c_can_dev(dev); | |
184 | ||
5b92da04 FV |
185 | pci_set_drvdata(pdev, NULL); |
186 | free_c_can_dev(dev); | |
187 | ||
188 | pci_iounmap(pdev, priv->base); | |
189 | pci_disable_msi(pdev); | |
190 | pci_clear_master(pdev); | |
191 | pci_release_regions(pdev); | |
192 | pci_disable_device(pdev); | |
193 | } | |
194 | ||
195 | static struct c_can_pci_data c_can_sta2x11= { | |
f27b1db9 | 196 | .type = BOSCH_C_CAN, |
5b92da04 FV |
197 | .reg_align = C_CAN_REG_ALIGN_32, |
198 | .freq = 52000000, /* 52 Mhz */ | |
199 | }; | |
200 | ||
201 | #define C_CAN_ID(_vend, _dev, _driverdata) { \ | |
202 | PCI_DEVICE(_vend, _dev), \ | |
203 | .driver_data = (unsigned long)&_driverdata, \ | |
204 | } | |
205 | static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = { | |
206 | C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN, | |
207 | c_can_sta2x11), | |
208 | {}, | |
209 | }; | |
210 | static struct pci_driver c_can_pci_driver = { | |
211 | .name = KBUILD_MODNAME, | |
212 | .id_table = c_can_pci_tbl, | |
213 | .probe = c_can_pci_probe, | |
3c8ac0f2 | 214 | .remove = c_can_pci_remove, |
5b92da04 FV |
215 | }; |
216 | ||
217 | module_pci_driver(c_can_pci_driver); | |
218 | ||
219 | MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>"); | |
220 | MODULE_LICENSE("GPL v2"); | |
221 | MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller"); | |
222 | MODULE_DEVICE_TABLE(pci, c_can_pci_tbl); |