2 * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
4 * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the version 2 of the GNU General Public License
8 * as published by the Free Software Foundation
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
17 * Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus.
18 * The I/O port or memory address and the IRQ number must be specified via
21 * insmod cc770_isa.ko port=0x310,0x380 irq=7,11
23 * for ISA devices using I/O ports or:
25 * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
27 * for memory mapped ISA devices.
29 * Indirect access via address and data port is supported as well:
31 * insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11
33 * Furthermore, the following mode parameter can be defined:
35 * clk: External oscillator clock frequency (default=16000000 [16 MHz])
36 * cir: CPU interface register (default=0x40 [DSC])
37 * bcr: Bus configuration register (default=0x40 [CBY])
38 * cor: Clockout register (default=0x00)
40 * Note: for clk, cir, bcr and cor, the first argument re-defines the
41 * default for all other devices, e.g.:
43 * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
47 * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
50 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
52 #include <linux/kernel.h>
53 #include <linux/module.h>
54 #include <linux/platform_device.h>
55 #include <linux/interrupt.h>
56 #include <linux/netdevice.h>
57 #include <linux/delay.h>
58 #include <linux/irq.h>
60 #include <linux/can.h>
61 #include <linux/can/dev.h>
62 #include <linux/can/platform/cc770.h>
68 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
69 MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
70 MODULE_LICENSE("GPL v2");
72 #define CLK_DEFAULT 16000000 /* 16 MHz */
73 #define COR_DEFAULT 0x00
74 #define BCR_DEFAULT BUSCFG_CBY
76 static unsigned long port
[MAXDEV
];
77 static unsigned long mem
[MAXDEV
];
78 static int __devinitdata irq
[MAXDEV
];
79 static int __devinitdata clk
[MAXDEV
];
80 static u8 __devinitdata cir
[MAXDEV
] = {[0 ... (MAXDEV
- 1)] = 0xff};
81 static u8 __devinitdata cor
[MAXDEV
] = {[0 ... (MAXDEV
- 1)] = 0xff};
82 static u8 __devinitdata bcr
[MAXDEV
] = {[0 ... (MAXDEV
- 1)] = 0xff};
83 static int __devinitdata indirect
[MAXDEV
] = {[0 ... (MAXDEV
- 1)] = -1};
85 module_param_array(port
, ulong
, NULL
, S_IRUGO
);
86 MODULE_PARM_DESC(port
, "I/O port number");
88 module_param_array(mem
, ulong
, NULL
, S_IRUGO
);
89 MODULE_PARM_DESC(mem
, "I/O memory address");
91 module_param_array(indirect
, int, NULL
, S_IRUGO
);
92 MODULE_PARM_DESC(indirect
, "Indirect access via address and data port");
94 module_param_array(irq
, int, NULL
, S_IRUGO
);
95 MODULE_PARM_DESC(irq
, "IRQ number");
97 module_param_array(clk
, int, NULL
, S_IRUGO
);
98 MODULE_PARM_DESC(clk
, "External oscillator clock frequency "
99 "(default=16000000 [16 MHz])");
101 module_param_array(cir
, byte
, NULL
, S_IRUGO
);
102 MODULE_PARM_DESC(cir
, "CPU interface register (default=0x40 [DSC])");
104 module_param_array(cor
, byte
, NULL
, S_IRUGO
);
105 MODULE_PARM_DESC(cor
, "Clockout register (default=0x00)");
107 module_param_array(bcr
, byte
, NULL
, S_IRUGO
);
108 MODULE_PARM_DESC(bcr
, "Bus configuration register (default=0x40 [CBY])");
110 #define CC770_IOSIZE 0x20
111 #define CC770_IOSIZE_INDIRECT 0x02
113 /* Spinlock for cc770_isa_port_write_reg_indirect
114 * and cc770_isa_port_read_reg_indirect
116 static DEFINE_SPINLOCK(cc770_isa_port_lock
);
118 static struct platform_device
*cc770_isa_devs
[MAXDEV
];
120 static u8
cc770_isa_mem_read_reg(const struct cc770_priv
*priv
, int reg
)
122 return readb(priv
->reg_base
+ reg
);
125 static void cc770_isa_mem_write_reg(const struct cc770_priv
*priv
,
128 writeb(val
, priv
->reg_base
+ reg
);
131 static u8
cc770_isa_port_read_reg(const struct cc770_priv
*priv
, int reg
)
133 return inb((unsigned long)priv
->reg_base
+ reg
);
136 static void cc770_isa_port_write_reg(const struct cc770_priv
*priv
,
139 outb(val
, (unsigned long)priv
->reg_base
+ reg
);
142 static u8
cc770_isa_port_read_reg_indirect(const struct cc770_priv
*priv
,
145 unsigned long base
= (unsigned long)priv
->reg_base
;
149 spin_lock_irqsave(&cc770_isa_port_lock
, flags
);
152 spin_unlock_irqrestore(&cc770_isa_port_lock
, flags
);
157 static void cc770_isa_port_write_reg_indirect(const struct cc770_priv
*priv
,
160 unsigned long base
= (unsigned long)priv
->reg_base
;
163 spin_lock_irqsave(&cc770_isa_port_lock
, flags
);
166 spin_unlock_irqrestore(&cc770_isa_port_lock
, flags
);
169 static int __devinit
cc770_isa_probe(struct platform_device
*pdev
)
171 struct net_device
*dev
;
172 struct cc770_priv
*priv
;
173 void __iomem
*base
= NULL
;
174 int iosize
= CC770_IOSIZE
;
179 dev_dbg(&pdev
->dev
, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
180 idx
, port
[idx
], mem
[idx
], irq
[idx
]);
182 if (!request_mem_region(mem
[idx
], iosize
, KBUILD_MODNAME
)) {
186 base
= ioremap_nocache(mem
[idx
], iosize
);
192 if (indirect
[idx
] > 0 ||
193 (indirect
[idx
] == -1 && indirect
[0] > 0))
194 iosize
= CC770_IOSIZE_INDIRECT
;
195 if (!request_region(port
[idx
], iosize
, KBUILD_MODNAME
)) {
201 dev
= alloc_cc770dev(0);
206 priv
= netdev_priv(dev
);
209 priv
->irq_flags
= IRQF_SHARED
;
211 priv
->reg_base
= base
;
212 dev
->base_addr
= mem
[idx
];
213 priv
->read_reg
= cc770_isa_mem_read_reg
;
214 priv
->write_reg
= cc770_isa_mem_write_reg
;
216 priv
->reg_base
= (void __iomem
*)port
[idx
];
217 dev
->base_addr
= port
[idx
];
219 if (iosize
== CC770_IOSIZE_INDIRECT
) {
220 priv
->read_reg
= cc770_isa_port_read_reg_indirect
;
221 priv
->write_reg
= cc770_isa_port_write_reg_indirect
;
223 priv
->read_reg
= cc770_isa_port_read_reg
;
224 priv
->write_reg
= cc770_isa_port_write_reg
;
233 clktmp
= CLK_DEFAULT
;
234 priv
->can
.clock
.freq
= clktmp
;
236 if (cir
[idx
] != 0xff) {
237 priv
->cpu_interface
= cir
[idx
];
238 } else if (cir
[0] != 0xff) {
239 priv
->cpu_interface
= cir
[0];
241 /* The system clock may not exceed 10 MHz */
242 if (clktmp
> 10000000) {
243 priv
->cpu_interface
|= CPUIF_DSC
;
246 /* The memory clock may not exceed 8 MHz */
247 if (clktmp
> 8000000)
248 priv
->cpu_interface
|= CPUIF_DMC
;
251 if (priv
->cpu_interface
& CPUIF_DSC
)
252 priv
->can
.clock
.freq
/= 2;
254 if (bcr
[idx
] != 0xff)
255 priv
->bus_config
= bcr
[idx
];
256 else if (bcr
[0] != 0xff)
257 priv
->bus_config
= bcr
[0];
259 priv
->bus_config
= BCR_DEFAULT
;
261 if (cor
[idx
] != 0xff)
262 priv
->clkout
= cor
[idx
];
263 else if (cor
[0] != 0xff)
264 priv
->clkout
= cor
[0];
266 priv
->clkout
= COR_DEFAULT
;
268 dev_set_drvdata(&pdev
->dev
, dev
);
269 SET_NETDEV_DEV(dev
, &pdev
->dev
);
271 err
= register_cc770dev(dev
);
274 "couldn't register device (err=%d)\n", err
);
278 dev_info(&pdev
->dev
, "device registered (reg_base=0x%p, irq=%d)\n",
279 priv
->reg_base
, dev
->irq
);
287 release_mem_region(mem
[idx
], iosize
);
289 release_region(port
[idx
], iosize
);
294 static int __devexit
cc770_isa_remove(struct platform_device
*pdev
)
296 struct net_device
*dev
= dev_get_drvdata(&pdev
->dev
);
297 struct cc770_priv
*priv
= netdev_priv(dev
);
300 unregister_cc770dev(dev
);
301 dev_set_drvdata(&pdev
->dev
, NULL
);
304 iounmap(priv
->reg_base
);
305 release_mem_region(mem
[idx
], CC770_IOSIZE
);
307 if (priv
->read_reg
== cc770_isa_port_read_reg_indirect
)
308 release_region(port
[idx
], CC770_IOSIZE_INDIRECT
);
310 release_region(port
[idx
], CC770_IOSIZE
);
317 static struct platform_driver cc770_isa_driver
= {
318 .probe
= cc770_isa_probe
,
319 .remove
= __devexit_p(cc770_isa_remove
),
321 .name
= KBUILD_MODNAME
,
322 .owner
= THIS_MODULE
,
326 static int __init
cc770_isa_init(void)
330 for (idx
= 0; idx
< ARRAY_SIZE(cc770_isa_devs
); idx
++) {
331 if ((port
[idx
] || mem
[idx
]) && irq
[idx
]) {
332 cc770_isa_devs
[idx
] =
333 platform_device_alloc(KBUILD_MODNAME
, idx
);
334 if (!cc770_isa_devs
[idx
]) {
336 goto exit_free_devices
;
338 err
= platform_device_add(cc770_isa_devs
[idx
]);
340 platform_device_put(cc770_isa_devs
[idx
]);
341 goto exit_free_devices
;
343 pr_debug("platform device %d: port=%#lx, mem=%#lx, "
345 idx
, port
[idx
], mem
[idx
], irq
[idx
]);
346 } else if (idx
== 0 || port
[idx
] || mem
[idx
]) {
347 pr_err("insufficient parameters supplied\n");
349 goto exit_free_devices
;
353 err
= platform_driver_register(&cc770_isa_driver
);
355 goto exit_free_devices
;
357 pr_info("driver for max. %d devices registered\n", MAXDEV
);
363 if (cc770_isa_devs
[idx
])
364 platform_device_unregister(cc770_isa_devs
[idx
]);
369 module_init(cc770_isa_init
);
371 static void __exit
cc770_isa_exit(void)
375 platform_driver_unregister(&cc770_isa_driver
);
376 for (idx
= 0; idx
< ARRAY_SIZE(cc770_isa_devs
); idx
++) {
377 if (cc770_isa_devs
[idx
])
378 platform_device_unregister(cc770_isa_devs
[idx
]);
381 module_exit(cc770_isa_exit
);