cc770_isa.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the version 2 of the GNU General Public License
  6. * as published by the Free Software Foundation
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. */
  17. #include <linux/kernel.h>
  18. #include <linux/version.h>
  19. #include <linux/module.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/interrupt.h>
  22. #include <linux/netdevice.h>
  23. #include <linux/delay.h>
  24. #include <linux/irq.h>
  25. #include <linux/io.h>
  26. #include <linux/can.h>
  27. #include <linux/can/dev.h>
  28. #include "cc770.h"
  29. #define DRV_NAME "cc770_isa"
  30. #define MAXDEV 8
  31. MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  32. MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
  33. MODULE_LICENSE("GPL v2");
  34. #define CLK_DEFAULT 16000000 /* 16 MHz */
  35. #define BCR_DEFAULT 0x00
  36. #define COR_DEFAULT 0x00
  37. static unsigned long port[MAXDEV];
  38. static unsigned long mem[MAXDEV];
  39. static int __devinitdata irq[MAXDEV];
  40. static int __devinitdata clk[MAXDEV];
  41. static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  42. static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  43. static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  44. static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
  45. module_param_array(port, ulong, NULL, S_IRUGO);
  46. MODULE_PARM_DESC(port, "I/O port number");
  47. module_param_array(mem, ulong, NULL, S_IRUGO);
  48. MODULE_PARM_DESC(mem, "I/O memory address");
  49. module_param_array(indirect, int, NULL, S_IRUGO);
  50. MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
  51. module_param_array(irq, int, NULL, S_IRUGO);
  52. MODULE_PARM_DESC(irq, "IRQ number");
  53. module_param_array(clk, int, NULL, S_IRUGO);
  54. MODULE_PARM_DESC(clk, "External oscillator clock frequency "
  55. "(default=16000000 [16 MHz])");
  56. module_param_array(cir, byte, NULL, S_IRUGO);
  57. MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [CPU_DSC])");
  58. module_param_array(bcr, byte, NULL, S_IRUGO);
  59. MODULE_PARM_DESC(ocr, "Bus configuration register (default=0x00)");
  60. module_param_array(cor, byte, NULL, S_IRUGO);
  61. MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
  62. #define CC770_IOSIZE 0x20
  63. #define CC770_IOSIZE_INDIRECT 0x02
  64. static struct platform_device *cc770_isa_devs[MAXDEV];
  65. static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
  66. {
  67. return readb(priv->reg_base + reg);
  68. }
  69. static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
  70. int reg, u8 val)
  71. {
  72. writeb(val, priv->reg_base + reg);
  73. }
  74. static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
  75. {
  76. return inb((unsigned long)priv->reg_base + reg);
  77. }
  78. static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
  79. int reg, u8 val)
  80. {
  81. outb(val, (unsigned long)priv->reg_base + reg);
  82. }
  83. static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
  84. int reg)
  85. {
  86. unsigned long base = (unsigned long)priv->reg_base;
  87. outb(reg, base);
  88. return inb(base + 1);
  89. }
  90. static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
  91. int reg, u8 val)
  92. {
  93. unsigned long base = (unsigned long)priv->reg_base;
  94. outb(reg, base);
  95. outb(val, base + 1);
  96. }
  97. static int __devinit cc770_isa_probe(struct platform_device *pdev)
  98. {
  99. struct net_device *dev;
  100. struct cc770_priv *priv;
  101. void __iomem *base = NULL;
  102. int iosize = CC770_IOSIZE;
  103. int idx = pdev->id;
  104. int err;
  105. u32 clktmp;
  106. dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
  107. idx, port[idx], mem[idx], irq[idx]);
  108. if (mem[idx]) {
  109. if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
  110. err = -EBUSY;
  111. goto exit;
  112. }
  113. base = ioremap_nocache(mem[idx], iosize);
  114. if (!base) {
  115. err = -ENOMEM;
  116. goto exit_release;
  117. }
  118. } else {
  119. if (indirect[idx] > 0 ||
  120. (indirect[idx] == -1 && indirect[0] > 0))
  121. iosize = CC770_IOSIZE_INDIRECT;
  122. if (!request_region(port[idx], iosize, DRV_NAME)) {
  123. err = -EBUSY;
  124. goto exit;
  125. }
  126. }
  127. dev = alloc_cc770dev(0);
  128. if (!dev) {
  129. err = -ENOMEM;
  130. goto exit_unmap;
  131. }
  132. priv = netdev_priv(dev);
  133. dev->irq = irq[idx];
  134. priv->irq_flags = IRQF_SHARED;
  135. if (mem[idx]) {
  136. priv->reg_base = base;
  137. dev->base_addr = mem[idx];
  138. priv->read_reg = cc770_isa_mem_read_reg;
  139. priv->write_reg = cc770_isa_mem_write_reg;
  140. } else {
  141. priv->reg_base = (void __iomem *)port[idx];
  142. dev->base_addr = port[idx];
  143. if (iosize == CC770_IOSIZE_INDIRECT) {
  144. priv->read_reg = cc770_isa_port_read_reg_indirect;
  145. priv->write_reg = cc770_isa_port_write_reg_indirect;
  146. } else {
  147. priv->read_reg = cc770_isa_port_read_reg;
  148. priv->write_reg = cc770_isa_port_write_reg;
  149. }
  150. }
  151. if (clk[idx])
  152. clktmp = clk[idx];
  153. else if (clk[0])
  154. clktmp = clk[0];
  155. else
  156. clktmp = CLK_DEFAULT;
  157. priv->can.clock.freq = clktmp;
  158. if (cir[idx] != 0xff) {
  159. priv->cpu_interface = cir[idx] & 0xff;
  160. } else if (cir[0] != 0xff) {
  161. priv->cpu_interface = cir[0] & 0xff;
  162. } else {
  163. /* The system clock may not exceed 10 MHz */
  164. if (clktmp > 10000000) {
  165. priv->cpu_interface |= CPUIF_DSC;
  166. clktmp /= 2;
  167. }
  168. /* The memory clock may not exceed 8 MHz */
  169. if (clktmp > 8000000)
  170. priv->cpu_interface |= CPUIF_DMC;
  171. }
  172. if (priv->cpu_interface & CPUIF_DSC)
  173. priv->can.clock.freq /= 2;
  174. if (bcr[idx] != 0xff)
  175. priv->bus_config = bcr[idx] & 0xff;
  176. else if (bcr[0] != 0xff)
  177. priv->bus_config = bcr[0] & 0xff;
  178. else
  179. priv->bus_config = BCR_DEFAULT;
  180. if (cor[idx] != 0xff)
  181. priv->clkout = cor[idx];
  182. else if (cor[0] != 0xff)
  183. priv->clkout = cor[0] & 0xff;
  184. else
  185. priv->clkout = COR_DEFAULT;
  186. dev_set_drvdata(&pdev->dev, dev);
  187. SET_NETDEV_DEV(dev, &pdev->dev);
  188. err = register_cc770dev(dev);
  189. if (err) {
  190. dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
  191. DRV_NAME, err);
  192. goto exit_unmap;
  193. }
  194. dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n",
  195. DRV_NAME, priv->reg_base, dev->irq);
  196. return 0;
  197. exit_unmap:
  198. if (mem[idx])
  199. iounmap(base);
  200. exit_release:
  201. if (mem[idx])
  202. release_mem_region(mem[idx], iosize);
  203. else
  204. release_region(port[idx], iosize);
  205. exit:
  206. return err;
  207. }
  208. static int __devexit cc770_isa_remove(struct platform_device *pdev)
  209. {
  210. struct net_device *dev = dev_get_drvdata(&pdev->dev);
  211. struct cc770_priv *priv = netdev_priv(dev);
  212. int idx = pdev->id;
  213. unregister_cc770dev(dev);
  214. dev_set_drvdata(&pdev->dev, NULL);
  215. if (mem[idx]) {
  216. iounmap(priv->reg_base);
  217. release_mem_region(mem[idx], CC770_IOSIZE);
  218. } else {
  219. if (priv->read_reg == cc770_isa_port_read_reg_indirect)
  220. release_region(port[idx], CC770_IOSIZE_INDIRECT);
  221. else
  222. release_region(port[idx], CC770_IOSIZE);
  223. }
  224. free_cc770dev(dev);
  225. return 0;
  226. }
  227. static struct platform_driver cc770_isa_driver = {
  228. .probe = cc770_isa_probe,
  229. .remove = __devexit_p(cc770_isa_remove),
  230. .driver = {
  231. .name = DRV_NAME,
  232. .owner = THIS_MODULE,
  233. },
  234. };
  235. static int __init cc770_isa_init(void)
  236. {
  237. int idx, err;
  238. for (idx = 0; idx < MAXDEV; idx++) {
  239. if ((port[idx] || mem[idx]) && irq[idx]) {
  240. cc770_isa_devs[idx] =
  241. platform_device_alloc(DRV_NAME, idx);
  242. if (!cc770_isa_devs[idx]) {
  243. err = -ENOMEM;
  244. goto exit_free_devices;
  245. }
  246. err = platform_device_add(cc770_isa_devs[idx]);
  247. if (err) {
  248. platform_device_put(cc770_isa_devs[idx]);
  249. goto exit_free_devices;
  250. }
  251. pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, "
  252. "irq=%d\n",
  253. DRV_NAME, idx, port[idx], mem[idx], irq[idx]);
  254. } else if (idx == 0 || port[idx] || mem[idx]) {
  255. pr_err("%s: insufficient parameters supplied\n",
  256. DRV_NAME);
  257. err = -EINVAL;
  258. goto exit_free_devices;
  259. }
  260. }
  261. err = platform_driver_register(&cc770_isa_driver);
  262. if (err)
  263. goto exit_free_devices;
  264. pr_info("Legacy %s driver for max. %d devices registered\n",
  265. DRV_NAME, MAXDEV);
  266. return 0;
  267. exit_free_devices:
  268. while (--idx >= 0) {
  269. if (cc770_isa_devs[idx])
  270. platform_device_unregister(cc770_isa_devs[idx]);
  271. }
  272. return err;
  273. }
  274. module_init(cc770_isa_init);
  275. static void __exit cc770_isa_exit(void)
  276. {
  277. int idx;
  278. platform_driver_unregister(&cc770_isa_driver);
  279. for (idx = 0; idx < MAXDEV; idx++) {
  280. if (cc770_isa_devs[idx])
  281. platform_device_unregister(cc770_isa_devs[idx]);
  282. }
  283. }
  284. module_exit(cc770_isa_exit);