1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/platform_device.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/netdevice.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/io.h> |
14 | #include <linux/can/dev.h> |
15 | #include <linux/can/platform/sja1000.h> |
16 | |
17 | #include "sja1000.h" |
18 | |
19 | #define DRV_NAME "sja1000_isa" |
20 | |
21 | #define MAXDEV 8 |
22 | |
23 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>" ); |
24 | MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus" ); |
25 | MODULE_LICENSE("GPL v2" ); |
26 | |
27 | #define CLK_DEFAULT 16000000 /* 16 MHz */ |
28 | #define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF) |
29 | #define OCR_DEFAULT OCR_TX0_PUSHPULL |
30 | |
31 | static unsigned long port[MAXDEV]; |
32 | static unsigned long mem[MAXDEV]; |
33 | static int irq[MAXDEV]; |
34 | static int clk[MAXDEV]; |
35 | static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; |
36 | static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; |
37 | static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; |
38 | static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */ |
39 | |
40 | module_param_hw_array(port, ulong, ioport, NULL, 0444); |
41 | MODULE_PARM_DESC(port, "I/O port number" ); |
42 | |
43 | module_param_hw_array(mem, ulong, iomem, NULL, 0444); |
44 | MODULE_PARM_DESC(mem, "I/O memory address" ); |
45 | |
46 | module_param_hw_array(indirect, int, ioport, NULL, 0444); |
47 | MODULE_PARM_DESC(indirect, "Indirect access via address and data port" ); |
48 | |
49 | module_param_hw_array(irq, int, irq, NULL, 0444); |
50 | MODULE_PARM_DESC(irq, "IRQ number" ); |
51 | |
52 | module_param_array(clk, int, NULL, 0444); |
53 | MODULE_PARM_DESC(clk, "External oscillator clock frequency " |
54 | "(default=16000000 [16 MHz])" ); |
55 | |
56 | module_param_array(cdr, byte, NULL, 0444); |
57 | MODULE_PARM_DESC(cdr, "Clock divider register " |
58 | "(default=0x48 [CDR_CBP | CDR_CLK_OFF])" ); |
59 | |
60 | module_param_array(ocr, byte, NULL, 0444); |
61 | MODULE_PARM_DESC(ocr, "Output control register " |
62 | "(default=0x18 [OCR_TX0_PUSHPULL])" ); |
63 | |
64 | #define SJA1000_IOSIZE 0x20 |
65 | #define SJA1000_IOSIZE_INDIRECT 0x02 |
66 | |
67 | static struct platform_device *sja1000_isa_devs[MAXDEV]; |
68 | |
69 | static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg) |
70 | { |
71 | return readb(addr: priv->reg_base + reg); |
72 | } |
73 | |
74 | static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv, |
75 | int reg, u8 val) |
76 | { |
77 | writeb(val, addr: priv->reg_base + reg); |
78 | } |
79 | |
80 | static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg) |
81 | { |
82 | return inb(port: (unsigned long)priv->reg_base + reg); |
83 | } |
84 | |
85 | static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, |
86 | int reg, u8 val) |
87 | { |
88 | outb(value: val, port: (unsigned long)priv->reg_base + reg); |
89 | } |
90 | |
91 | static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, |
92 | int reg) |
93 | { |
94 | unsigned long flags, base = (unsigned long)priv->reg_base; |
95 | u8 readval; |
96 | |
97 | spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); |
98 | outb(value: reg, port: base); |
99 | readval = inb(port: base + 1); |
100 | spin_unlock_irqrestore(lock: &indirect_lock[priv->dev->dev_id], flags); |
101 | |
102 | return readval; |
103 | } |
104 | |
105 | static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, |
106 | int reg, u8 val) |
107 | { |
108 | unsigned long flags, base = (unsigned long)priv->reg_base; |
109 | |
110 | spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); |
111 | outb(value: reg, port: base); |
112 | outb(value: val, port: base + 1); |
113 | spin_unlock_irqrestore(lock: &indirect_lock[priv->dev->dev_id], flags); |
114 | } |
115 | |
116 | static int sja1000_isa_probe(struct platform_device *pdev) |
117 | { |
118 | struct net_device *dev; |
119 | struct sja1000_priv *priv; |
120 | void __iomem *base = NULL; |
121 | int iosize = SJA1000_IOSIZE; |
122 | int idx = pdev->id; |
123 | int err; |
124 | |
125 | dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n" , |
126 | idx, port[idx], mem[idx], irq[idx]); |
127 | |
128 | if (mem[idx]) { |
129 | if (!request_mem_region(mem[idx], iosize, DRV_NAME)) { |
130 | err = -EBUSY; |
131 | goto exit; |
132 | } |
133 | base = ioremap(offset: mem[idx], size: iosize); |
134 | if (!base) { |
135 | err = -ENOMEM; |
136 | goto exit_release; |
137 | } |
138 | } else { |
139 | if (indirect[idx] > 0 || |
140 | (indirect[idx] == -1 && indirect[0] > 0)) |
141 | iosize = SJA1000_IOSIZE_INDIRECT; |
142 | if (!request_region(port[idx], iosize, DRV_NAME)) { |
143 | err = -EBUSY; |
144 | goto exit; |
145 | } |
146 | } |
147 | |
148 | dev = alloc_sja1000dev(sizeof_priv: 0); |
149 | if (!dev) { |
150 | err = -ENOMEM; |
151 | goto exit_unmap; |
152 | } |
153 | priv = netdev_priv(dev); |
154 | |
155 | dev->irq = irq[idx]; |
156 | priv->irq_flags = IRQF_SHARED; |
157 | if (mem[idx]) { |
158 | priv->reg_base = base; |
159 | dev->base_addr = mem[idx]; |
160 | priv->read_reg = sja1000_isa_mem_read_reg; |
161 | priv->write_reg = sja1000_isa_mem_write_reg; |
162 | } else { |
163 | priv->reg_base = (void __iomem *)port[idx]; |
164 | dev->base_addr = port[idx]; |
165 | |
166 | if (iosize == SJA1000_IOSIZE_INDIRECT) { |
167 | priv->read_reg = sja1000_isa_port_read_reg_indirect; |
168 | priv->write_reg = sja1000_isa_port_write_reg_indirect; |
169 | spin_lock_init(&indirect_lock[idx]); |
170 | } else { |
171 | priv->read_reg = sja1000_isa_port_read_reg; |
172 | priv->write_reg = sja1000_isa_port_write_reg; |
173 | } |
174 | } |
175 | |
176 | if (clk[idx]) |
177 | priv->can.clock.freq = clk[idx] / 2; |
178 | else if (clk[0]) |
179 | priv->can.clock.freq = clk[0] / 2; |
180 | else |
181 | priv->can.clock.freq = CLK_DEFAULT / 2; |
182 | |
183 | if (ocr[idx] != 0xff) |
184 | priv->ocr = ocr[idx]; |
185 | else if (ocr[0] != 0xff) |
186 | priv->ocr = ocr[0]; |
187 | else |
188 | priv->ocr = OCR_DEFAULT; |
189 | |
190 | if (cdr[idx] != 0xff) |
191 | priv->cdr = cdr[idx]; |
192 | else if (cdr[0] != 0xff) |
193 | priv->cdr = cdr[0]; |
194 | else |
195 | priv->cdr = CDR_DEFAULT; |
196 | |
197 | platform_set_drvdata(pdev, data: dev); |
198 | SET_NETDEV_DEV(dev, &pdev->dev); |
199 | dev->dev_id = idx; |
200 | |
201 | err = register_sja1000dev(dev); |
202 | if (err) { |
203 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n" , |
204 | DRV_NAME, err); |
205 | goto exit_free; |
206 | } |
207 | |
208 | dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n" , |
209 | DRV_NAME, priv->reg_base, dev->irq); |
210 | return 0; |
211 | |
212 | exit_free: |
213 | free_sja1000dev(dev); |
214 | exit_unmap: |
215 | if (mem[idx]) |
216 | iounmap(addr: base); |
217 | exit_release: |
218 | if (mem[idx]) |
219 | release_mem_region(mem[idx], iosize); |
220 | else |
221 | release_region(port[idx], iosize); |
222 | exit: |
223 | return err; |
224 | } |
225 | |
226 | static void sja1000_isa_remove(struct platform_device *pdev) |
227 | { |
228 | struct net_device *dev = platform_get_drvdata(pdev); |
229 | struct sja1000_priv *priv = netdev_priv(dev); |
230 | int idx = pdev->id; |
231 | |
232 | unregister_sja1000dev(dev); |
233 | |
234 | if (mem[idx]) { |
235 | iounmap(addr: priv->reg_base); |
236 | release_mem_region(mem[idx], SJA1000_IOSIZE); |
237 | } else { |
238 | if (priv->read_reg == sja1000_isa_port_read_reg_indirect) |
239 | release_region(port[idx], SJA1000_IOSIZE_INDIRECT); |
240 | else |
241 | release_region(port[idx], SJA1000_IOSIZE); |
242 | } |
243 | free_sja1000dev(dev); |
244 | } |
245 | |
246 | static struct platform_driver sja1000_isa_driver = { |
247 | .probe = sja1000_isa_probe, |
248 | .remove_new = sja1000_isa_remove, |
249 | .driver = { |
250 | .name = DRV_NAME, |
251 | }, |
252 | }; |
253 | |
254 | static int __init sja1000_isa_init(void) |
255 | { |
256 | int idx, err; |
257 | |
258 | for (idx = 0; idx < MAXDEV; idx++) { |
259 | if ((port[idx] || mem[idx]) && irq[idx]) { |
260 | sja1000_isa_devs[idx] = |
261 | platform_device_alloc(DRV_NAME, id: idx); |
262 | if (!sja1000_isa_devs[idx]) { |
263 | err = -ENOMEM; |
264 | goto exit_free_devices; |
265 | } |
266 | err = platform_device_add(pdev: sja1000_isa_devs[idx]); |
267 | if (err) { |
268 | platform_device_put(pdev: sja1000_isa_devs[idx]); |
269 | goto exit_free_devices; |
270 | } |
271 | pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, " |
272 | "irq=%d\n" , |
273 | DRV_NAME, idx, port[idx], mem[idx], irq[idx]); |
274 | } else if (idx == 0 || port[idx] || mem[idx]) { |
275 | pr_err("%s: insufficient parameters supplied\n" , |
276 | DRV_NAME); |
277 | err = -EINVAL; |
278 | goto exit_free_devices; |
279 | } |
280 | } |
281 | |
282 | err = platform_driver_register(&sja1000_isa_driver); |
283 | if (err) |
284 | goto exit_free_devices; |
285 | |
286 | pr_info("Legacy %s driver for max. %d devices registered\n" , |
287 | DRV_NAME, MAXDEV); |
288 | |
289 | return 0; |
290 | |
291 | exit_free_devices: |
292 | while (--idx >= 0) { |
293 | if (sja1000_isa_devs[idx]) |
294 | platform_device_unregister(sja1000_isa_devs[idx]); |
295 | } |
296 | |
297 | return err; |
298 | } |
299 | |
300 | static void __exit sja1000_isa_exit(void) |
301 | { |
302 | int idx; |
303 | |
304 | platform_driver_unregister(&sja1000_isa_driver); |
305 | for (idx = 0; idx < MAXDEV; idx++) { |
306 | if (sja1000_isa_devs[idx]) |
307 | platform_device_unregister(sja1000_isa_devs[idx]); |
308 | } |
309 | } |
310 | |
311 | module_init(sja1000_isa_init); |
312 | module_exit(sja1000_isa_exit); |
313 | |