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
softingcs_find_platform_data(unsigned int manf,unsigned int prod)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  */
softingcs_reset(struct platform_device * pdev,int v)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(pcmcia, 2, v ? 0 : 0x20);
171 }
172 
softingcs_enable_irq(struct platform_device * pdev,int v)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(pcmcia, 0, v ? 0x60 : 0);
179 }
180 
181 /*
182  * pcmcia check
183  */
softingcs_probe_config(struct pcmcia_device * pcmcia,void * priv_data)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(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(pcmcia, pres, memspeed);
203 }
204 
softingcs_remove(struct pcmcia_device * pcmcia)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(pcmcia);
213 }
214 
215 /*
216  * platform_device wrapper
217  * pdev->resource has 2 entries: io & irq
218  */
softingcs_pdev_release(struct device * dev)219 static void softingcs_pdev_release(struct device *dev)
220 {
221 	struct platform_device *pdev = to_platform_device(dev);
222 	kfree(pdev);
223 }
224 
softingcs_probe(struct pcmcia_device * pcmcia)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(pcmcia->manf_id, 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(pcmcia, softingcs_probe_config, (void *)pdat);
245 	if (ret)
246 		goto pcmcia_failed;
247 
248 	ret = pcmcia_enable_device(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(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(&softingcs_index_lock);
284 	pdev->id = softingcs_index++;
285 	spin_unlock(&softingcs_index_lock);
286 	pdev->name = "softing";
287 	dev_set_name(&pdev->dev, "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 	kfree(dev);
297 mem_failed:
298 pcmcia_bad:
299 pcmcia_failed:
300 	pcmcia_disable_device(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