cc770_platform.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Driver for CC770 and AN82527 CAN controllers on the platform bus
  4. *
  5. * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
  6. */
  7. /*
  8. * If platform data are used you should have similar definitions
  9. * in your board-specific code:
  10. *
  11. * static struct cc770_platform_data myboard_cc770_pdata = {
  12. * .osc_freq = 16000000,
  13. * .cir = 0x41,
  14. * .cor = 0x20,
  15. * .bcr = 0x40,
  16. * };
  17. *
  18. * Please see include/linux/can/platform/cc770.h for description of
  19. * above fields.
  20. *
  21. * If the device tree is used, you need a CAN node definition in your
  22. * DTS file similar to:
  23. *
  24. * can@3,100 {
  25. * compatible = "bosch,cc770";
  26. * reg = <3 0x100 0x80>;
  27. * interrupts = <2 0>;
  28. * interrupt-parent = <&mpic>;
  29. * bosch,external-clock-frequency = <16000000>;
  30. * };
  31. *
  32. * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further
  33. * information.
  34. */
  35. #include <linux/kernel.h>
  36. #include <linux/module.h>
  37. #include <linux/interrupt.h>
  38. #include <linux/netdevice.h>
  39. #include <linux/delay.h>
  40. #include <linux/platform_device.h>
  41. #include <linux/of.h>
  42. #include <linux/can.h>
  43. #include <linux/can/dev.h>
  44. #include <linux/can/platform/cc770.h>
  45. #include "cc770.h"
  46. #define DRV_NAME "cc770_platform"
  47. MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  48. MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus");
  49. MODULE_LICENSE("GPL v2");
  50. MODULE_ALIAS("platform:" DRV_NAME);
  51. #define CC770_PLATFORM_CAN_CLOCK 16000000
  52. static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg)
  53. {
  54. return ioread8(priv->reg_base + reg);
  55. }
  56. static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg,
  57. u8 val)
  58. {
  59. iowrite8(val, priv->reg_base + reg);
  60. }
  61. static int cc770_get_of_node_data(struct platform_device *pdev,
  62. struct cc770_priv *priv)
  63. {
  64. u32 clkext = CC770_PLATFORM_CAN_CLOCK, clkout = 0;
  65. struct device_node *np = pdev->dev.of_node;
  66. of_property_read_u32(np, "bosch,external-clock-frequency", &clkext);
  67. priv->can.clock.freq = clkext;
  68. /* The system clock may not exceed 10 MHz */
  69. if (priv->can.clock.freq > 10000000) {
  70. priv->cpu_interface |= CPUIF_DSC;
  71. priv->can.clock.freq /= 2;
  72. }
  73. /* The memory clock may not exceed 8 MHz */
  74. if (priv->can.clock.freq > 8000000)
  75. priv->cpu_interface |= CPUIF_DMC;
  76. if (of_property_read_bool(np, "bosch,divide-memory-clock"))
  77. priv->cpu_interface |= CPUIF_DMC;
  78. if (of_property_read_bool(np, "bosch,iso-low-speed-mux"))
  79. priv->cpu_interface |= CPUIF_MUX;
  80. if (!of_property_read_bool(np, "bosch,no-comperator-bypass"))
  81. priv->bus_config |= BUSCFG_CBY;
  82. if (of_property_read_bool(np, "bosch,disconnect-rx0-input"))
  83. priv->bus_config |= BUSCFG_DR0;
  84. if (of_property_read_bool(np, "bosch,disconnect-rx1-input"))
  85. priv->bus_config |= BUSCFG_DR1;
  86. if (of_property_read_bool(np, "bosch,disconnect-tx1-output"))
  87. priv->bus_config |= BUSCFG_DT1;
  88. if (of_property_read_bool(np, "bosch,polarity-dominant"))
  89. priv->bus_config |= BUSCFG_POL;
  90. of_property_read_u32(np, "bosch,clock-out-frequency", &clkout);
  91. if (clkout > 0) {
  92. u32 cdv = clkext / clkout;
  93. if (cdv > 0 && cdv < 16) {
  94. u32 slew;
  95. priv->cpu_interface |= CPUIF_CEN;
  96. priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
  97. if (of_property_read_u32(np, "bosch,slew-rate", &slew)) {
  98. /* Determine default slew rate */
  99. slew = (CLKOUT_SL_MASK >>
  100. CLKOUT_SL_SHIFT) -
  101. ((cdv * clkext - 1) / 8000000);
  102. if (slew > (CLKOUT_SL_MASK >> CLKOUT_SL_SHIFT))
  103. slew = 0;
  104. }
  105. priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
  106. CLKOUT_SL_MASK;
  107. } else {
  108. dev_dbg(&pdev->dev, "invalid clock-out-frequency\n");
  109. }
  110. }
  111. return 0;
  112. }
  113. static int cc770_get_platform_data(struct platform_device *pdev,
  114. struct cc770_priv *priv)
  115. {
  116. struct cc770_platform_data *pdata = dev_get_platdata(&pdev->dev);
  117. priv->can.clock.freq = pdata->osc_freq;
  118. if (priv->cpu_interface & CPUIF_DSC)
  119. priv->can.clock.freq /= 2;
  120. priv->clkout = pdata->cor;
  121. priv->bus_config = pdata->bcr;
  122. priv->cpu_interface = pdata->cir;
  123. return 0;
  124. }
  125. static int cc770_platform_probe(struct platform_device *pdev)
  126. {
  127. struct net_device *dev;
  128. struct cc770_priv *priv;
  129. struct resource *mem;
  130. resource_size_t mem_size;
  131. void __iomem *base;
  132. int err, irq;
  133. mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  134. irq = platform_get_irq(pdev, 0);
  135. if (!mem || irq <= 0)
  136. return -ENODEV;
  137. mem_size = resource_size(mem);
  138. if (!request_mem_region(mem->start, mem_size, pdev->name))
  139. return -EBUSY;
  140. base = ioremap(mem->start, mem_size);
  141. if (!base) {
  142. err = -ENOMEM;
  143. goto exit_release_mem;
  144. }
  145. dev = alloc_cc770dev(0);
  146. if (!dev) {
  147. err = -ENOMEM;
  148. goto exit_unmap_mem;
  149. }
  150. dev->irq = irq;
  151. priv = netdev_priv(dev);
  152. priv->read_reg = cc770_platform_read_reg;
  153. priv->write_reg = cc770_platform_write_reg;
  154. priv->irq_flags = IRQF_SHARED;
  155. priv->reg_base = base;
  156. if (pdev->dev.of_node)
  157. err = cc770_get_of_node_data(pdev, priv);
  158. else if (dev_get_platdata(&pdev->dev))
  159. err = cc770_get_platform_data(pdev, priv);
  160. else
  161. err = -ENODEV;
  162. if (err)
  163. goto exit_free_cc770;
  164. dev_dbg(&pdev->dev,
  165. "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
  166. "bus_config=0x%02x clkout=0x%02x\n",
  167. priv->reg_base, dev->irq, priv->can.clock.freq,
  168. priv->cpu_interface, priv->bus_config, priv->clkout);
  169. platform_set_drvdata(pdev, dev);
  170. SET_NETDEV_DEV(dev, &pdev->dev);
  171. err = register_cc770dev(dev);
  172. if (err) {
  173. dev_err(&pdev->dev,
  174. "couldn't register CC700 device (err=%d)\n", err);
  175. goto exit_free_cc770;
  176. }
  177. return 0;
  178. exit_free_cc770:
  179. free_cc770dev(dev);
  180. exit_unmap_mem:
  181. iounmap(base);
  182. exit_release_mem:
  183. release_mem_region(mem->start, mem_size);
  184. return err;
  185. }
  186. static void cc770_platform_remove(struct platform_device *pdev)
  187. {
  188. struct net_device *dev = platform_get_drvdata(pdev);
  189. struct cc770_priv *priv = netdev_priv(dev);
  190. struct resource *mem;
  191. unregister_cc770dev(dev);
  192. iounmap(priv->reg_base);
  193. free_cc770dev(dev);
  194. mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  195. release_mem_region(mem->start, resource_size(mem));
  196. }
  197. static const struct of_device_id cc770_platform_table[] = {
  198. {.compatible = "bosch,cc770"}, /* CC770 from Bosch */
  199. {.compatible = "intc,82527"}, /* AN82527 from Intel CP */
  200. {},
  201. };
  202. MODULE_DEVICE_TABLE(of, cc770_platform_table);
  203. static struct platform_driver cc770_platform_driver = {
  204. .driver = {
  205. .name = DRV_NAME,
  206. .of_match_table = cc770_platform_table,
  207. },
  208. .probe = cc770_platform_probe,
  209. .remove = cc770_platform_remove,
  210. };
  211. module_platform_driver(cc770_platform_driver);