1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2008-2010 |
4 | * |
5 | * - Kurt Van Dijck, EIA Electronics |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/slab.h> |
11 | |
12 | #include <pcmcia/cistpl.h> |
13 | #include <pcmcia/ds.h> |
14 | |
15 | #include "softing_platform.h" |
16 | |
17 | static int softingcs_index; |
18 | static DEFINE_SPINLOCK(softingcs_index_lock); |
19 | |
20 | static int softingcs_reset(struct platform_device *pdev, int v); |
21 | static int softingcs_enable_irq(struct platform_device *pdev, int v); |
22 | |
23 | /* |
24 | * platform_data descriptions |
25 | */ |
26 | #define MHZ (1000*1000) |
27 | static const struct softing_platform_data softingcs_platform_data[] = { |
28 | { |
29 | .name = "CANcard" , |
30 | .manf = 0x0168, .prod = 0x001, |
31 | .generation = 1, |
32 | .nbus = 2, |
33 | .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, |
34 | .dpram_size = 0x0800, |
35 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
36 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
37 | .app = {0x0010, 0x0d0000, fw_dir "cancard.bin" ,}, |
38 | .reset = softingcs_reset, |
39 | .enable_irq = softingcs_enable_irq, |
40 | }, { |
41 | .name = "CANcard-NEC" , |
42 | .manf = 0x0168, .prod = 0x002, |
43 | .generation = 1, |
44 | .nbus = 2, |
45 | .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, |
46 | .dpram_size = 0x0800, |
47 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
48 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
49 | .app = {0x0010, 0x0d0000, fw_dir "cancard.bin" ,}, |
50 | .reset = softingcs_reset, |
51 | .enable_irq = softingcs_enable_irq, |
52 | }, { |
53 | .name = "CANcard-SJA" , |
54 | .manf = 0x0168, .prod = 0x004, |
55 | .generation = 1, |
56 | .nbus = 2, |
57 | .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, |
58 | .dpram_size = 0x0800, |
59 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
60 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
61 | .app = {0x0010, 0x0d0000, fw_dir "cansja.bin" ,}, |
62 | .reset = softingcs_reset, |
63 | .enable_irq = softingcs_enable_irq, |
64 | }, { |
65 | .name = "CANcard-2" , |
66 | .manf = 0x0168, .prod = 0x005, |
67 | .generation = 2, |
68 | .nbus = 2, |
69 | .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, |
70 | .dpram_size = 0x1000, |
71 | .boot = {0x0000, 0x000000, fw_dir "bcard2.bin" ,}, |
72 | .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin" ,}, |
73 | .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin" ,}, |
74 | .reset = softingcs_reset, |
75 | .enable_irq = NULL, |
76 | }, { |
77 | .name = "Vector-CANcard" , |
78 | .manf = 0x0168, .prod = 0x081, |
79 | .generation = 1, |
80 | .nbus = 2, |
81 | .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, |
82 | .dpram_size = 0x0800, |
83 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
84 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
85 | .app = {0x0010, 0x0d0000, fw_dir "cancard.bin" ,}, |
86 | .reset = softingcs_reset, |
87 | .enable_irq = softingcs_enable_irq, |
88 | }, { |
89 | .name = "Vector-CANcard-SJA" , |
90 | .manf = 0x0168, .prod = 0x084, |
91 | .generation = 1, |
92 | .nbus = 2, |
93 | .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, |
94 | .dpram_size = 0x0800, |
95 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
96 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
97 | .app = {0x0010, 0x0d0000, fw_dir "cansja.bin" ,}, |
98 | .reset = softingcs_reset, |
99 | .enable_irq = softingcs_enable_irq, |
100 | }, { |
101 | .name = "Vector-CANcard-2" , |
102 | .manf = 0x0168, .prod = 0x085, |
103 | .generation = 2, |
104 | .nbus = 2, |
105 | .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, |
106 | .dpram_size = 0x1000, |
107 | .boot = {0x0000, 0x000000, fw_dir "bcard2.bin" ,}, |
108 | .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin" ,}, |
109 | .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin" ,}, |
110 | .reset = softingcs_reset, |
111 | .enable_irq = NULL, |
112 | }, { |
113 | .name = "EDICcard-NEC" , |
114 | .manf = 0x0168, .prod = 0x102, |
115 | .generation = 1, |
116 | .nbus = 2, |
117 | .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, |
118 | .dpram_size = 0x0800, |
119 | .boot = {0x0000, 0x000000, fw_dir "bcard.bin" ,}, |
120 | .load = {0x0120, 0x00f600, fw_dir "ldcard.bin" ,}, |
121 | .app = {0x0010, 0x0d0000, fw_dir "cancard.bin" ,}, |
122 | .reset = softingcs_reset, |
123 | .enable_irq = softingcs_enable_irq, |
124 | }, { |
125 | .name = "EDICcard-2" , |
126 | .manf = 0x0168, .prod = 0x105, |
127 | .generation = 2, |
128 | .nbus = 2, |
129 | .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, |
130 | .dpram_size = 0x1000, |
131 | .boot = {0x0000, 0x000000, fw_dir "bcard2.bin" ,}, |
132 | .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin" ,}, |
133 | .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin" ,}, |
134 | .reset = softingcs_reset, |
135 | .enable_irq = NULL, |
136 | }, { |
137 | 0, 0, |
138 | }, |
139 | }; |
140 | |
141 | MODULE_FIRMWARE(fw_dir "bcard.bin" ); |
142 | MODULE_FIRMWARE(fw_dir "ldcard.bin" ); |
143 | MODULE_FIRMWARE(fw_dir "cancard.bin" ); |
144 | MODULE_FIRMWARE(fw_dir "cansja.bin" ); |
145 | |
146 | MODULE_FIRMWARE(fw_dir "bcard2.bin" ); |
147 | MODULE_FIRMWARE(fw_dir "ldcard2.bin" ); |
148 | MODULE_FIRMWARE(fw_dir "cancrd2.bin" ); |
149 | |
150 | static const struct softing_platform_data |
151 | *softingcs_find_platform_data(unsigned int manf, unsigned int prod) |
152 | { |
153 | const struct softing_platform_data *lp; |
154 | |
155 | for (lp = softingcs_platform_data; lp->manf; ++lp) { |
156 | if ((lp->manf == manf) && (lp->prod == prod)) |
157 | return lp; |
158 | } |
159 | return NULL; |
160 | } |
161 | |
162 | /* |
163 | * platformdata callbacks |
164 | */ |
165 | static int softingcs_reset(struct platform_device *pdev, int v) |
166 | { |
167 | struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); |
168 | |
169 | dev_dbg(&pdev->dev, "pcmcia config [2] %02x\n" , v ? 0 : 0x20); |
170 | return pcmcia_write_config_byte(p_dev: pcmcia, where: 2, val: v ? 0 : 0x20); |
171 | } |
172 | |
173 | static int softingcs_enable_irq(struct platform_device *pdev, int v) |
174 | { |
175 | struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); |
176 | |
177 | dev_dbg(&pdev->dev, "pcmcia config [0] %02x\n" , v ? 0x60 : 0); |
178 | return pcmcia_write_config_byte(p_dev: pcmcia, where: 0, val: v ? 0x60 : 0); |
179 | } |
180 | |
181 | /* |
182 | * pcmcia check |
183 | */ |
184 | static int softingcs_probe_config(struct pcmcia_device *pcmcia, void *priv_data) |
185 | { |
186 | struct softing_platform_data *pdat = priv_data; |
187 | struct resource *pres; |
188 | int memspeed = 0; |
189 | |
190 | WARN_ON(!pdat); |
191 | pres = pcmcia->resource[PCMCIA_IOMEM_0]; |
192 | if (resource_size(res: pres) < 0x1000) |
193 | return -ERANGE; |
194 | |
195 | pres->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; |
196 | if (pdat->generation < 2) { |
197 | pres->flags |= WIN_USE_WAIT | WIN_DATA_WIDTH_8; |
198 | memspeed = 3; |
199 | } else { |
200 | pres->flags |= WIN_DATA_WIDTH_16; |
201 | } |
202 | return pcmcia_request_window(p_dev: pcmcia, res: pres, speed: memspeed); |
203 | } |
204 | |
205 | static void softingcs_remove(struct pcmcia_device *pcmcia) |
206 | { |
207 | struct platform_device *pdev = pcmcia->priv; |
208 | |
209 | /* free bits */ |
210 | platform_device_unregister(pdev); |
211 | /* release pcmcia stuff */ |
212 | pcmcia_disable_device(p_dev: pcmcia); |
213 | } |
214 | |
215 | /* |
216 | * platform_device wrapper |
217 | * pdev->resource has 2 entries: io & irq |
218 | */ |
219 | static void softingcs_pdev_release(struct device *dev) |
220 | { |
221 | struct platform_device *pdev = to_platform_device(dev); |
222 | kfree(objp: pdev); |
223 | } |
224 | |
225 | static int softingcs_probe(struct pcmcia_device *pcmcia) |
226 | { |
227 | int ret; |
228 | struct platform_device *pdev; |
229 | const struct softing_platform_data *pdat; |
230 | struct resource *pres; |
231 | struct dev { |
232 | struct platform_device pdev; |
233 | struct resource res[2]; |
234 | } *dev; |
235 | |
236 | /* find matching platform_data */ |
237 | pdat = softingcs_find_platform_data(manf: pcmcia->manf_id, prod: pcmcia->card_id); |
238 | if (!pdat) |
239 | return -ENOTTY; |
240 | |
241 | /* setup pcmcia device */ |
242 | pcmcia->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IOMEM | |
243 | CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; |
244 | ret = pcmcia_loop_config(p_dev: pcmcia, conf_check: softingcs_probe_config, priv_data: (void *)pdat); |
245 | if (ret) |
246 | goto pcmcia_failed; |
247 | |
248 | ret = pcmcia_enable_device(p_dev: pcmcia); |
249 | if (ret < 0) |
250 | goto pcmcia_failed; |
251 | |
252 | pres = pcmcia->resource[PCMCIA_IOMEM_0]; |
253 | if (!pres) { |
254 | ret = -EBADF; |
255 | goto pcmcia_bad; |
256 | } |
257 | |
258 | /* create softing platform device */ |
259 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
260 | if (!dev) { |
261 | ret = -ENOMEM; |
262 | goto mem_failed; |
263 | } |
264 | dev->pdev.resource = dev->res; |
265 | dev->pdev.num_resources = ARRAY_SIZE(dev->res); |
266 | dev->pdev.dev.release = softingcs_pdev_release; |
267 | |
268 | pdev = &dev->pdev; |
269 | pdev->dev.platform_data = (void *)pdat; |
270 | pdev->dev.parent = &pcmcia->dev; |
271 | pcmcia->priv = pdev; |
272 | |
273 | /* platform device resources */ |
274 | pdev->resource[0].flags = IORESOURCE_MEM; |
275 | pdev->resource[0].start = pres->start; |
276 | pdev->resource[0].end = pres->end; |
277 | |
278 | pdev->resource[1].flags = IORESOURCE_IRQ; |
279 | pdev->resource[1].start = pcmcia->irq; |
280 | pdev->resource[1].end = pdev->resource[1].start; |
281 | |
282 | /* platform device setup */ |
283 | spin_lock(lock: &softingcs_index_lock); |
284 | pdev->id = softingcs_index++; |
285 | spin_unlock(lock: &softingcs_index_lock); |
286 | pdev->name = "softing" ; |
287 | dev_set_name(dev: &pdev->dev, name: "softingcs.%i" , pdev->id); |
288 | ret = platform_device_register(pdev); |
289 | if (ret < 0) |
290 | goto platform_failed; |
291 | |
292 | dev_info(&pcmcia->dev, "created %s\n" , dev_name(&pdev->dev)); |
293 | return 0; |
294 | |
295 | platform_failed: |
296 | platform_device_put(pdev); |
297 | mem_failed: |
298 | pcmcia_bad: |
299 | pcmcia_failed: |
300 | pcmcia_disable_device(p_dev: pcmcia); |
301 | pcmcia->priv = NULL; |
302 | return ret; |
303 | } |
304 | |
305 | static const struct pcmcia_device_id softingcs_ids[] = { |
306 | /* softing */ |
307 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001), |
308 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002), |
309 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004), |
310 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005), |
311 | /* vector, manufacturer? */ |
312 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081), |
313 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084), |
314 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085), |
315 | /* EDIC */ |
316 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102), |
317 | PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105), |
318 | PCMCIA_DEVICE_NULL, |
319 | }; |
320 | |
321 | MODULE_DEVICE_TABLE(pcmcia, softingcs_ids); |
322 | |
323 | static struct pcmcia_driver softingcs_driver = { |
324 | .owner = THIS_MODULE, |
325 | .name = "softingcs" , |
326 | .id_table = softingcs_ids, |
327 | .probe = softingcs_probe, |
328 | .remove = softingcs_remove, |
329 | }; |
330 | |
331 | module_pcmcia_driver(softingcs_driver); |
332 | |
333 | MODULE_DESCRIPTION("softing CANcard driver" |
334 | ", links PCMCIA card to softing driver" ); |
335 | MODULE_LICENSE("GPL v2" ); |
336 | |