mdio-mscc-miim.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. // SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2. /*
  3. * Driver for the MDIO interface of Microsemi network switches.
  4. *
  5. * Author: Alexandre Belloni <[email protected]>
  6. * Copyright (c) 2017 Microsemi Corporation
  7. */
  8. #include <linux/bitops.h>
  9. #include <linux/clk.h>
  10. #include <linux/io.h>
  11. #include <linux/iopoll.h>
  12. #include <linux/kernel.h>
  13. #include <linux/mdio/mdio-mscc-miim.h>
  14. #include <linux/mfd/ocelot.h>
  15. #include <linux/module.h>
  16. #include <linux/of_mdio.h>
  17. #include <linux/phy.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/property.h>
  20. #include <linux/regmap.h>
  21. #define MSCC_MIIM_REG_STATUS 0x0
  22. #define MSCC_MIIM_STATUS_STAT_PENDING BIT(2)
  23. #define MSCC_MIIM_STATUS_STAT_BUSY BIT(3)
  24. #define MSCC_MIIM_REG_CMD 0x8
  25. #define MSCC_MIIM_CMD_OPR_WRITE BIT(1)
  26. #define MSCC_MIIM_CMD_OPR_READ BIT(2)
  27. #define MSCC_MIIM_CMD_WRDATA_SHIFT 4
  28. #define MSCC_MIIM_CMD_REGAD_SHIFT 20
  29. #define MSCC_MIIM_CMD_PHYAD_SHIFT 25
  30. #define MSCC_MIIM_CMD_VLD BIT(31)
  31. #define MSCC_MIIM_REG_DATA 0xC
  32. #define MSCC_MIIM_DATA_ERROR (BIT(16) | BIT(17))
  33. #define MSCC_MIIM_REG_CFG 0x10
  34. #define MSCC_MIIM_CFG_PRESCALE_MASK GENMASK(7, 0)
  35. #define MSCC_PHY_REG_PHY_CFG 0x0
  36. #define PHY_CFG_PHY_ENA (BIT(0) | BIT(1) | BIT(2) | BIT(3))
  37. #define PHY_CFG_PHY_COMMON_RESET BIT(4)
  38. #define PHY_CFG_PHY_RESET (BIT(5) | BIT(6) | BIT(7) | BIT(8))
  39. #define MSCC_PHY_REG_PHY_STATUS 0x4
  40. #define LAN966X_CUPHY_COMMON_CFG 0x0
  41. #define CUPHY_COMMON_CFG_RESET_N BIT(0)
  42. struct mscc_miim_info {
  43. unsigned int phy_reset_offset;
  44. unsigned int phy_reset_bits;
  45. };
  46. struct mscc_miim_dev {
  47. struct regmap *regs;
  48. int mii_status_offset;
  49. bool ignore_read_errors;
  50. struct regmap *phy_regs;
  51. const struct mscc_miim_info *info;
  52. struct clk *clk;
  53. u32 bus_freq;
  54. };
  55. /* When high resolution timers aren't built-in: we can't use usleep_range() as
  56. * we would sleep way too long. Use udelay() instead.
  57. */
  58. #define mscc_readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us)\
  59. ({ \
  60. if (!IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) \
  61. readx_poll_timeout_atomic(op, addr, val, cond, delay_us, \
  62. timeout_us); \
  63. readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us); \
  64. })
  65. static int mscc_miim_status(struct mii_bus *bus)
  66. {
  67. struct mscc_miim_dev *miim = bus->priv;
  68. int val, ret;
  69. ret = regmap_read(miim->regs,
  70. MSCC_MIIM_REG_STATUS + miim->mii_status_offset, &val);
  71. if (ret < 0) {
  72. WARN_ONCE(1, "mscc miim status read error %d\n", ret);
  73. return ret;
  74. }
  75. return val;
  76. }
  77. static int mscc_miim_wait_ready(struct mii_bus *bus)
  78. {
  79. u32 val;
  80. return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
  81. !(val & MSCC_MIIM_STATUS_STAT_BUSY), 50,
  82. 10000);
  83. }
  84. static int mscc_miim_wait_pending(struct mii_bus *bus)
  85. {
  86. u32 val;
  87. return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
  88. !(val & MSCC_MIIM_STATUS_STAT_PENDING),
  89. 50, 10000);
  90. }
  91. static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
  92. {
  93. struct mscc_miim_dev *miim = bus->priv;
  94. u32 val;
  95. int ret;
  96. if (regnum & MII_ADDR_C45)
  97. return -EOPNOTSUPP;
  98. ret = mscc_miim_wait_pending(bus);
  99. if (ret)
  100. goto out;
  101. ret = regmap_write(miim->regs,
  102. MSCC_MIIM_REG_CMD + miim->mii_status_offset,
  103. MSCC_MIIM_CMD_VLD |
  104. (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
  105. (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
  106. MSCC_MIIM_CMD_OPR_READ);
  107. if (ret < 0) {
  108. WARN_ONCE(1, "mscc miim write cmd reg error %d\n", ret);
  109. goto out;
  110. }
  111. ret = mscc_miim_wait_ready(bus);
  112. if (ret)
  113. goto out;
  114. ret = regmap_read(miim->regs,
  115. MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val);
  116. if (ret < 0) {
  117. WARN_ONCE(1, "mscc miim read data reg error %d\n", ret);
  118. goto out;
  119. }
  120. if (!miim->ignore_read_errors && !!(val & MSCC_MIIM_DATA_ERROR)) {
  121. ret = -EIO;
  122. goto out;
  123. }
  124. ret = val & 0xFFFF;
  125. out:
  126. return ret;
  127. }
  128. static int mscc_miim_write(struct mii_bus *bus, int mii_id,
  129. int regnum, u16 value)
  130. {
  131. struct mscc_miim_dev *miim = bus->priv;
  132. int ret;
  133. if (regnum & MII_ADDR_C45)
  134. return -EOPNOTSUPP;
  135. ret = mscc_miim_wait_pending(bus);
  136. if (ret < 0)
  137. goto out;
  138. ret = regmap_write(miim->regs,
  139. MSCC_MIIM_REG_CMD + miim->mii_status_offset,
  140. MSCC_MIIM_CMD_VLD |
  141. (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
  142. (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
  143. (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
  144. MSCC_MIIM_CMD_OPR_WRITE);
  145. if (ret < 0)
  146. WARN_ONCE(1, "mscc miim write error %d\n", ret);
  147. out:
  148. return ret;
  149. }
  150. static int mscc_miim_reset(struct mii_bus *bus)
  151. {
  152. struct mscc_miim_dev *miim = bus->priv;
  153. unsigned int offset, bits;
  154. int ret;
  155. if (!miim->phy_regs)
  156. return 0;
  157. offset = miim->info->phy_reset_offset;
  158. bits = miim->info->phy_reset_bits;
  159. ret = regmap_update_bits(miim->phy_regs, offset, bits, 0);
  160. if (ret < 0) {
  161. WARN_ONCE(1, "mscc reset set error %d\n", ret);
  162. return ret;
  163. }
  164. ret = regmap_update_bits(miim->phy_regs, offset, bits, bits);
  165. if (ret < 0) {
  166. WARN_ONCE(1, "mscc reset clear error %d\n", ret);
  167. return ret;
  168. }
  169. mdelay(500);
  170. return 0;
  171. }
  172. static const struct regmap_config mscc_miim_regmap_config = {
  173. .reg_bits = 32,
  174. .val_bits = 32,
  175. .reg_stride = 4,
  176. };
  177. static const struct regmap_config mscc_miim_phy_regmap_config = {
  178. .reg_bits = 32,
  179. .val_bits = 32,
  180. .reg_stride = 4,
  181. .name = "phy",
  182. };
  183. int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
  184. struct regmap *mii_regmap, int status_offset,
  185. bool ignore_read_errors)
  186. {
  187. struct mscc_miim_dev *miim;
  188. struct mii_bus *bus;
  189. bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
  190. if (!bus)
  191. return -ENOMEM;
  192. bus->name = name;
  193. bus->read = mscc_miim_read;
  194. bus->write = mscc_miim_write;
  195. bus->reset = mscc_miim_reset;
  196. snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
  197. bus->parent = dev;
  198. miim = bus->priv;
  199. *pbus = bus;
  200. miim->regs = mii_regmap;
  201. miim->mii_status_offset = status_offset;
  202. miim->ignore_read_errors = ignore_read_errors;
  203. *pbus = bus;
  204. return 0;
  205. }
  206. EXPORT_SYMBOL(mscc_miim_setup);
  207. static int mscc_miim_clk_set(struct mii_bus *bus)
  208. {
  209. struct mscc_miim_dev *miim = bus->priv;
  210. unsigned long rate;
  211. u32 div;
  212. /* Keep the current settings */
  213. if (!miim->bus_freq)
  214. return 0;
  215. rate = clk_get_rate(miim->clk);
  216. div = DIV_ROUND_UP(rate, 2 * miim->bus_freq) - 1;
  217. if (div == 0 || div & ~MSCC_MIIM_CFG_PRESCALE_MASK) {
  218. dev_err(&bus->dev, "Incorrect MDIO clock frequency\n");
  219. return -EINVAL;
  220. }
  221. return regmap_update_bits(miim->regs, MSCC_MIIM_REG_CFG,
  222. MSCC_MIIM_CFG_PRESCALE_MASK, div);
  223. }
  224. static int mscc_miim_probe(struct platform_device *pdev)
  225. {
  226. struct device_node *np = pdev->dev.of_node;
  227. struct regmap *mii_regmap, *phy_regmap;
  228. struct device *dev = &pdev->dev;
  229. struct mscc_miim_dev *miim;
  230. struct mii_bus *bus;
  231. int ret;
  232. mii_regmap = ocelot_regmap_from_resource(pdev, 0,
  233. &mscc_miim_regmap_config);
  234. if (IS_ERR(mii_regmap))
  235. return dev_err_probe(dev, PTR_ERR(mii_regmap),
  236. "Unable to create MIIM regmap\n");
  237. /* This resource is optional */
  238. phy_regmap = ocelot_regmap_from_resource_optional(pdev, 1,
  239. &mscc_miim_phy_regmap_config);
  240. if (IS_ERR(phy_regmap))
  241. return dev_err_probe(dev, PTR_ERR(phy_regmap),
  242. "Unable to create phy register regmap\n");
  243. ret = mscc_miim_setup(dev, &bus, "mscc_miim", mii_regmap, 0, false);
  244. if (ret < 0) {
  245. dev_err(dev, "Unable to setup the MDIO bus\n");
  246. return ret;
  247. }
  248. miim = bus->priv;
  249. miim->phy_regs = phy_regmap;
  250. miim->info = device_get_match_data(dev);
  251. if (!miim->info)
  252. return -EINVAL;
  253. miim->clk = devm_clk_get_optional(dev, NULL);
  254. if (IS_ERR(miim->clk))
  255. return PTR_ERR(miim->clk);
  256. of_property_read_u32(np, "clock-frequency", &miim->bus_freq);
  257. if (miim->bus_freq && !miim->clk) {
  258. dev_err(dev, "cannot use clock-frequency without a clock\n");
  259. return -EINVAL;
  260. }
  261. ret = clk_prepare_enable(miim->clk);
  262. if (ret)
  263. return ret;
  264. ret = mscc_miim_clk_set(bus);
  265. if (ret)
  266. goto out_disable_clk;
  267. ret = of_mdiobus_register(bus, np);
  268. if (ret < 0) {
  269. dev_err(dev, "Cannot register MDIO bus (%d)\n", ret);
  270. goto out_disable_clk;
  271. }
  272. platform_set_drvdata(pdev, bus);
  273. return 0;
  274. out_disable_clk:
  275. clk_disable_unprepare(miim->clk);
  276. return ret;
  277. }
  278. static int mscc_miim_remove(struct platform_device *pdev)
  279. {
  280. struct mii_bus *bus = platform_get_drvdata(pdev);
  281. struct mscc_miim_dev *miim = bus->priv;
  282. clk_disable_unprepare(miim->clk);
  283. mdiobus_unregister(bus);
  284. return 0;
  285. }
  286. static const struct mscc_miim_info mscc_ocelot_miim_info = {
  287. .phy_reset_offset = MSCC_PHY_REG_PHY_CFG,
  288. .phy_reset_bits = PHY_CFG_PHY_ENA | PHY_CFG_PHY_COMMON_RESET |
  289. PHY_CFG_PHY_RESET,
  290. };
  291. static const struct mscc_miim_info microchip_lan966x_miim_info = {
  292. .phy_reset_offset = LAN966X_CUPHY_COMMON_CFG,
  293. .phy_reset_bits = CUPHY_COMMON_CFG_RESET_N,
  294. };
  295. static const struct of_device_id mscc_miim_match[] = {
  296. {
  297. .compatible = "mscc,ocelot-miim",
  298. .data = &mscc_ocelot_miim_info
  299. }, {
  300. .compatible = "microchip,lan966x-miim",
  301. .data = &microchip_lan966x_miim_info
  302. },
  303. { }
  304. };
  305. MODULE_DEVICE_TABLE(of, mscc_miim_match);
  306. static struct platform_driver mscc_miim_driver = {
  307. .probe = mscc_miim_probe,
  308. .remove = mscc_miim_remove,
  309. .driver = {
  310. .name = "mscc-miim",
  311. .of_match_table = mscc_miim_match,
  312. },
  313. };
  314. module_platform_driver(mscc_miim_driver);
  315. MODULE_DESCRIPTION("Microsemi MIIM driver");
  316. MODULE_AUTHOR("Alexandre Belloni <[email protected]>");
  317. MODULE_LICENSE("Dual MIT/GPL");