cc770_isa.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
  3. *
  4. * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
  5. *
  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
  9. *
  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.
  14. */
  15. /*
  16. * Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus.
  17. * The I/O port or memory address and the IRQ number must be specified via
  18. * module parameters:
  19. *
  20. * insmod cc770_isa.ko port=0x310,0x380 irq=7,11
  21. *
  22. * for ISA devices using I/O ports or:
  23. *
  24. * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
  25. *
  26. * for memory mapped ISA devices.
  27. *
  28. * Indirect access via address and data port is supported as well:
  29. *
  30. * insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11
  31. *
  32. * Furthermore, the following mode parameter can be defined:
  33. *
  34. * clk: External oscillator clock frequency (default=16000000 [16 MHz])
  35. * cir: CPU interface register (default=0x40 [DSC])
  36. * bcr: Bus configuration register (default=0x40 [CBY])
  37. * cor: Clockout register (default=0x00)
  38. *
  39. * Note: for clk, cir, bcr and cor, the first argument re-defines the
  40. * default for all other devices, e.g.:
  41. *
  42. * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
  43. *
  44. * is equivalent to
  45. *
  46. * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
  47. */
  48. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  49. #include <linux/kernel.h>
  50. #include <linux/module.h>
  51. #include <linux/platform_device.h>
  52. #include <linux/interrupt.h>
  53. #include <linux/netdevice.h>
  54. #include <linux/delay.h>
  55. #include <linux/irq.h>
  56. #include <linux/io.h>
  57. #include <linux/can.h>
  58. #include <linux/can/dev.h>
  59. #include <linux/can/platform/cc770.h>
  60. #include "cc770.h"
  61. #define MAXDEV 8
  62. MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  63. MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
  64. MODULE_LICENSE("GPL v2");
  65. #define CLK_DEFAULT 16000000 /* 16 MHz */
  66. #define COR_DEFAULT 0x00
  67. #define BCR_DEFAULT BUSCFG_CBY
  68. static unsigned long port[MAXDEV];
  69. static unsigned long mem[MAXDEV];
  70. static int irq[MAXDEV];
  71. static int clk[MAXDEV];
  72. static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  73. static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  74. static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  75. static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
  76. module_param_array(port, ulong, NULL, S_IRUGO);
  77. MODULE_PARM_DESC(port, "I/O port number");
  78. module_param_array(mem, ulong, NULL, S_IRUGO);
  79. MODULE_PARM_DESC(mem, "I/O memory address");
  80. module_param_array(indirect, int, NULL, S_IRUGO);
  81. MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
  82. module_param_array(irq, int, NULL, S_IRUGO);
  83. MODULE_PARM_DESC(irq, "IRQ number");
  84. module_param_array(clk, int, NULL, S_IRUGO);
  85. MODULE_PARM_DESC(clk, "External oscillator clock frequency "
  86. "(default=16000000 [16 MHz])");
  87. module_param_array(cir, byte, NULL, S_IRUGO);
  88. MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
  89. module_param_array(cor, byte, NULL, S_IRUGO);
  90. MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
  91. module_param_array(bcr, byte, NULL, S_IRUGO);
  92. MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
  93. #define CC770_IOSIZE 0x20
  94. #define CC770_IOSIZE_INDIRECT 0x02
  95. /* Spinlock for cc770_isa_port_write_reg_indirect
  96. * and cc770_isa_port_read_reg_indirect
  97. */
  98. static DEFINE_SPINLOCK(cc770_isa_port_lock);
  99. static struct platform_device *cc770_isa_devs[MAXDEV];
  100. static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
  101. {
  102. return readb(priv->reg_base + reg);
  103. }
  104. static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
  105. int reg, u8 val)
  106. {
  107. writeb(val, priv->reg_base + reg);
  108. }
  109. static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
  110. {
  111. return inb((unsigned long)priv->reg_base + reg);
  112. }
  113. static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
  114. int reg, u8 val)
  115. {
  116. outb(val, (unsigned long)priv->reg_base + reg);
  117. }
  118. static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
  119. int reg)
  120. {
  121. unsigned long base = (unsigned long)priv->reg_base;
  122. unsigned long flags;
  123. u8 val;
  124. spin_lock_irqsave(&cc770_isa_port_lock, flags);
  125. outb(reg, base);
  126. val = inb(base + 1);
  127. spin_unlock_irqrestore(&cc770_isa_port_lock, flags);
  128. return val;
  129. }
  130. static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
  131. int reg, u8 val)
  132. {
  133. unsigned long base = (unsigned long)priv->reg_base;
  134. unsigned long flags;
  135. spin_lock_irqsave(&cc770_isa_port_lock, flags);
  136. outb(reg, base);
  137. outb(val, base + 1);
  138. spin_unlock_irqrestore(&cc770_isa_port_lock, flags);
  139. }
  140. static int cc770_isa_probe(struct platform_device *pdev)
  141. {
  142. struct net_device *dev;
  143. struct cc770_priv *priv;
  144. void __iomem *base = NULL;
  145. int iosize = CC770_IOSIZE;
  146. int idx = pdev->id;
  147. int err;
  148. u32 clktmp;
  149. dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
  150. idx, port[idx], mem[idx], irq[idx]);
  151. if (mem[idx]) {
  152. if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) {
  153. err = -EBUSY;
  154. goto exit;
  155. }
  156. base = ioremap_nocache(mem[idx], iosize);
  157. if (!base) {
  158. err = -ENOMEM;
  159. goto exit_release;
  160. }
  161. } else {
  162. if (indirect[idx] > 0 ||
  163. (indirect[idx] == -1 && indirect[0] > 0))
  164. iosize = CC770_IOSIZE_INDIRECT;
  165. if (!request_region(port[idx], iosize, KBUILD_MODNAME)) {
  166. err = -EBUSY;
  167. goto exit;
  168. }
  169. }
  170. dev = alloc_cc770dev(0);
  171. if (!dev) {
  172. err = -ENOMEM;
  173. goto exit_unmap;
  174. }
  175. priv = netdev_priv(dev);
  176. dev->irq = irq[idx];
  177. priv->irq_flags = IRQF_SHARED;
  178. if (mem[idx]) {
  179. priv->reg_base = base;
  180. dev->base_addr = mem[idx];
  181. priv->read_reg = cc770_isa_mem_read_reg;
  182. priv->write_reg = cc770_isa_mem_write_reg;
  183. } else {
  184. priv->reg_base = (void __iomem *)port[idx];
  185. dev->base_addr = port[idx];
  186. if (iosize == CC770_IOSIZE_INDIRECT) {
  187. priv->read_reg = cc770_isa_port_read_reg_indirect;
  188. priv->write_reg = cc770_isa_port_write_reg_indirect;
  189. } else {
  190. priv->read_reg = cc770_isa_port_read_reg;
  191. priv->write_reg = cc770_isa_port_write_reg;
  192. }
  193. }
  194. if (clk[idx])
  195. clktmp = clk[idx];
  196. else if (clk[0])
  197. clktmp = clk[0];
  198. else
  199. clktmp = CLK_DEFAULT;
  200. priv->can.clock.freq = clktmp;
  201. if (cir[idx] != 0xff) {
  202. priv->cpu_interface = cir[idx];
  203. } else if (cir[0] != 0xff) {
  204. priv->cpu_interface = cir[0];
  205. } else {
  206. /* The system clock may not exceed 10 MHz */
  207. if (clktmp > 10000000) {
  208. priv->cpu_interface |= CPUIF_DSC;
  209. clktmp /= 2;
  210. }
  211. /* The memory clock may not exceed 8 MHz */
  212. if (clktmp > 8000000)
  213. priv->cpu_interface |= CPUIF_DMC;
  214. }
  215. if (priv->cpu_interface & CPUIF_DSC)
  216. priv->can.clock.freq /= 2;
  217. if (bcr[idx] != 0xff)
  218. priv->bus_config = bcr[idx];
  219. else if (bcr[0] != 0xff)
  220. priv->bus_config = bcr[0];
  221. else
  222. priv->bus_config = BCR_DEFAULT;
  223. if (cor[idx] != 0xff)
  224. priv->clkout = cor[idx];
  225. else if (cor[0] != 0xff)
  226. priv->clkout = cor[0];
  227. else
  228. priv->clkout = COR_DEFAULT;
  229. platform_set_drvdata(pdev, dev);
  230. SET_NETDEV_DEV(dev, &pdev->dev);
  231. err = register_cc770dev(dev);
  232. if (err) {
  233. dev_err(&pdev->dev,
  234. "couldn't register device (err=%d)\n", err);
  235. goto exit_unmap;
  236. }
  237. dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n",
  238. priv->reg_base, dev->irq);
  239. return 0;
  240. exit_unmap:
  241. if (mem[idx])
  242. iounmap(base);
  243. exit_release:
  244. if (mem[idx])
  245. release_mem_region(mem[idx], iosize);
  246. else
  247. release_region(port[idx], iosize);
  248. exit:
  249. return err;
  250. }
  251. static int cc770_isa_remove(struct platform_device *pdev)
  252. {
  253. struct net_device *dev = platform_get_drvdata(pdev);
  254. struct cc770_priv *priv = netdev_priv(dev);
  255. int idx = pdev->id;
  256. unregister_cc770dev(dev);
  257. if (mem[idx]) {
  258. iounmap(priv->reg_base);
  259. release_mem_region(mem[idx], CC770_IOSIZE);
  260. } else {
  261. if (priv->read_reg == cc770_isa_port_read_reg_indirect)
  262. release_region(port[idx], CC770_IOSIZE_INDIRECT);
  263. else
  264. release_region(port[idx], CC770_IOSIZE);
  265. }
  266. free_cc770dev(dev);
  267. return 0;
  268. }
  269. static struct platform_driver cc770_isa_driver = {
  270. .probe = cc770_isa_probe,
  271. .remove = cc770_isa_remove,
  272. .driver = {
  273. .name = KBUILD_MODNAME,
  274. },
  275. };
  276. static int __init cc770_isa_init(void)
  277. {
  278. int idx, err;
  279. for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
  280. if ((port[idx] || mem[idx]) && irq[idx]) {
  281. cc770_isa_devs[idx] =
  282. platform_device_alloc(KBUILD_MODNAME, idx);
  283. if (!cc770_isa_devs[idx]) {
  284. err = -ENOMEM;
  285. goto exit_free_devices;
  286. }
  287. err = platform_device_add(cc770_isa_devs[idx]);
  288. if (err) {
  289. platform_device_put(cc770_isa_devs[idx]);
  290. goto exit_free_devices;
  291. }
  292. pr_debug("platform device %d: port=%#lx, mem=%#lx, "
  293. "irq=%d\n",
  294. idx, port[idx], mem[idx], irq[idx]);
  295. } else if (idx == 0 || port[idx] || mem[idx]) {
  296. pr_err("insufficient parameters supplied\n");
  297. err = -EINVAL;
  298. goto exit_free_devices;
  299. }
  300. }
  301. err = platform_driver_register(&cc770_isa_driver);
  302. if (err)
  303. goto exit_free_devices;
  304. pr_info("driver for max. %d devices registered\n", MAXDEV);
  305. return 0;
  306. exit_free_devices:
  307. while (--idx >= 0) {
  308. if (cc770_isa_devs[idx])
  309. platform_device_unregister(cc770_isa_devs[idx]);
  310. }
  311. return err;
  312. }
  313. module_init(cc770_isa_init);
  314. static void __exit cc770_isa_exit(void)
  315. {
  316. int idx;
  317. platform_driver_unregister(&cc770_isa_driver);
  318. for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
  319. if (cc770_isa_devs[idx])
  320. platform_device_unregister(cc770_isa_devs[idx]);
  321. }
  322. }
  323. module_exit(cc770_isa_exit);