739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001
  2. From: Daniel Golle <[email protected]>
  3. Date: Tue, 12 Dec 2023 03:47:18 +0000
  4. Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988
  5. Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which
  6. is going to initially be used for the MT7988 SoC.
  7. Signed-off-by: Daniel Golle <[email protected]>
  8. ---
  9. drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++--
  10. include/linux/pcs/pcs-mtk-lynxi.h | 11 ++
  11. 2 files changed, 227 insertions(+), 11 deletions(-)
  12. --- a/drivers/net/pcs/pcs-mtk-lynxi.c
  13. +++ b/drivers/net/pcs/pcs-mtk-lynxi.c
  14. @@ -1,6 +1,6 @@
  15. // SPDX-License-Identifier: GPL-2.0
  16. // Copyright (c) 2018-2019 MediaTek Inc.
  17. -/* A library for MediaTek SGMII circuit
  18. +/* A library and platform driver for the MediaTek LynxI SGMII circuit
  19. *
  20. * Author: Sean Wang <[email protected]>
  21. * Author: Alexander Couzens <[email protected]>
  22. @@ -8,11 +8,17 @@
  23. *
  24. */
  25. +#include <linux/clk.h>
  26. #include <linux/mdio.h>
  27. +#include <linux/mfd/syscon.h>
  28. +#include <linux/mutex.h>
  29. #include <linux/of.h>
  30. +#include <linux/of_platform.h>
  31. #include <linux/pcs/pcs-mtk-lynxi.h>
  32. #include <linux/phylink.h>
  33. +#include <linux/platform_device.h>
  34. #include <linux/regmap.h>
  35. +#include <linux/reset.h>
  36. /* SGMII subsystem config registers */
  37. /* BMCR (low 16) BMSR (high 16) */
  38. @@ -65,6 +71,8 @@
  39. #define SGMII_PN_SWAP_MASK GENMASK(1, 0)
  40. #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
  41. +#define MTK_NETSYS_V3_AMA_RGC3 0x128
  42. +
  43. /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
  44. * data
  45. * @regmap: The register map pointing at the range used to setup
  46. @@ -74,15 +82,29 @@
  47. * @interface: Currently configured interface mode
  48. * @pcs: Phylink PCS structure
  49. * @flags: Flags indicating hardware properties
  50. + * @rstc: Reset controller
  51. + * @sgmii_sel: SGMII Register Clock
  52. + * @sgmii_rx: SGMII RX Clock
  53. + * @sgmii_tx: SGMII TX Clock
  54. + * @node: List node
  55. */
  56. struct mtk_pcs_lynxi {
  57. struct regmap *regmap;
  58. + struct device *dev;
  59. u32 ana_rgc3;
  60. phy_interface_t interface;
  61. struct phylink_pcs pcs;
  62. u32 flags;
  63. + struct reset_control *rstc;
  64. + struct clk *sgmii_sel;
  65. + struct clk *sgmii_rx;
  66. + struct clk *sgmii_tx;
  67. + struct list_head node;
  68. };
  69. +static LIST_HEAD(mtk_pcs_lynxi_instances);
  70. +static DEFINE_MUTEX(instance_mutex);
  71. +
  72. static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
  73. {
  74. return container_of(pcs, struct mtk_pcs_lynxi, pcs);
  75. @@ -102,6 +124,17 @@ static void mtk_pcs_lynxi_get_state(stru
  76. FIELD_GET(SGMII_LPA, adv));
  77. }
  78. +static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
  79. +{
  80. + if (!mpcs->rstc)
  81. + return;
  82. +
  83. + reset_control_assert(mpcs->rstc);
  84. + udelay(100);
  85. + reset_control_deassert(mpcs->rstc);
  86. + mdelay(1);
  87. +}
  88. +
  89. static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
  90. phy_interface_t interface,
  91. const unsigned long *advertising,
  92. @@ -147,6 +180,7 @@ static int mtk_pcs_lynxi_config(struct p
  93. SGMII_PHYA_PWD);
  94. /* Reset SGMII PCS state */
  95. + mtk_sgmii_reset(mpcs);
  96. regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
  97. SGMII_SW_RESET);
  98. @@ -233,10 +267,29 @@ static void mtk_pcs_lynxi_link_up(struct
  99. }
  100. }
  101. +static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs)
  102. +{
  103. + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  104. +
  105. + if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
  106. + clk_prepare_enable(mpcs->sgmii_rx);
  107. + clk_prepare_enable(mpcs->sgmii_tx);
  108. + }
  109. +
  110. + return 0;
  111. +}
  112. +
  113. static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
  114. {
  115. struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  116. + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
  117. +
  118. + if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
  119. + clk_disable_unprepare(mpcs->sgmii_tx);
  120. + clk_disable_unprepare(mpcs->sgmii_rx);
  121. + }
  122. +
  123. mpcs->interface = PHY_INTERFACE_MODE_NA;
  124. }
  125. @@ -246,11 +299,12 @@ static const struct phylink_pcs_ops mtk_
  126. .pcs_an_restart = mtk_pcs_lynxi_restart_an,
  127. .pcs_link_up = mtk_pcs_lynxi_link_up,
  128. .pcs_disable = mtk_pcs_lynxi_disable,
  129. + .pcs_enable = mtk_pcs_lynxi_enable,
  130. };
  131. -struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
  132. - struct regmap *regmap, u32 ana_rgc3,
  133. - u32 flags)
  134. +static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap,
  135. + u32 ana_rgc3, u32 flags,
  136. + struct mtk_pcs_lynxi *prealloc)
  137. {
  138. struct mtk_pcs_lynxi *mpcs;
  139. u32 id, ver;
  140. @@ -258,29 +312,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create
  141. ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
  142. if (ret < 0)
  143. - return NULL;
  144. + return ERR_PTR(ret);
  145. if (id != SGMII_LYNXI_DEV_ID) {
  146. dev_err(dev, "unknown PCS device id %08x\n", id);
  147. - return NULL;
  148. + return ERR_PTR(-ENODEV);
  149. }
  150. ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
  151. if (ret < 0)
  152. - return NULL;
  153. + return ERR_PTR(ret);
  154. ver = FIELD_GET(SGMII_DEV_VERSION, ver);
  155. if (ver != 0x1) {
  156. dev_err(dev, "unknown PCS device version %04x\n", ver);
  157. - return NULL;
  158. + return ERR_PTR(-ENODEV);
  159. }
  160. dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
  161. ver);
  162. - mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
  163. - if (!mpcs)
  164. - return NULL;
  165. + if (prealloc) {
  166. + mpcs = prealloc;
  167. + } else {
  168. + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
  169. + if (!mpcs)
  170. + return ERR_PTR(-ENOMEM);
  171. + };
  172. mpcs->ana_rgc3 = ana_rgc3;
  173. mpcs->regmap = regmap;
  174. @@ -291,6 +349,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create
  175. mpcs->interface = PHY_INTERFACE_MODE_NA;
  176. return &mpcs->pcs;
  177. +};
  178. +
  179. +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
  180. + struct regmap *regmap, u32 ana_rgc3,
  181. + u32 flags)
  182. +{
  183. + return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL);
  184. }
  185. EXPORT_SYMBOL(mtk_pcs_lynxi_create);
  186. @@ -303,4 +368,144 @@ void mtk_pcs_lynxi_destroy(struct phylin
  187. }
  188. EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
  189. +static int mtk_pcs_lynxi_probe(struct platform_device *pdev)
  190. +{
  191. + struct device *dev = &pdev->dev;
  192. + struct device_node *np = dev->of_node;
  193. + struct mtk_pcs_lynxi *mpcs;
  194. + struct phylink_pcs *pcs;
  195. + struct regmap *regmap;
  196. + u32 flags = 0;
  197. +
  198. + mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
  199. + if (!mpcs)
  200. + return -ENOMEM;
  201. +
  202. + mpcs->dev = dev;
  203. + regmap = syscon_node_to_regmap(np->parent);
  204. + if (IS_ERR(regmap))
  205. + return PTR_ERR(regmap);
  206. +
  207. + if (of_property_read_bool(np->parent, "mediatek,pnswap"))
  208. + flags |= MTK_SGMII_FLAG_PN_SWAP;
  209. +
  210. + mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
  211. + if (IS_ERR(mpcs->rstc))
  212. + return PTR_ERR(mpcs->rstc);
  213. +
  214. + reset_control_deassert(mpcs->rstc);
  215. + mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel");
  216. + if (IS_ERR(mpcs->sgmii_sel))
  217. + return PTR_ERR(mpcs->sgmii_sel);
  218. +
  219. + mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx");
  220. + if (IS_ERR(mpcs->sgmii_rx))
  221. + return PTR_ERR(mpcs->sgmii_rx);
  222. +
  223. + mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx");
  224. + if (IS_ERR(mpcs->sgmii_tx))
  225. + return PTR_ERR(mpcs->sgmii_tx);
  226. +
  227. + pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev),
  228. + flags, mpcs);
  229. + if (IS_ERR(pcs))
  230. + return PTR_ERR(pcs);
  231. +
  232. + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
  233. +
  234. + platform_set_drvdata(pdev, mpcs);
  235. +
  236. + mutex_lock(&instance_mutex);
  237. + list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances);
  238. + mutex_unlock(&instance_mutex);
  239. +
  240. + return 0;
  241. +}
  242. +
  243. +static int mtk_pcs_lynxi_remove(struct platform_device *pdev)
  244. +{
  245. + struct device *dev = &pdev->dev;
  246. + struct mtk_pcs_lynxi *cur, *tmp;
  247. +
  248. + mutex_lock(&instance_mutex);
  249. + list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node)
  250. + if (cur->dev == dev) {
  251. + list_del(&cur->node);
  252. + kfree(cur);
  253. + break;
  254. + }
  255. + mutex_unlock(&instance_mutex);
  256. +
  257. + return 0;
  258. +}
  259. +
  260. +static const struct of_device_id mtk_pcs_lynxi_of_match[] = {
  261. + { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
  262. + { /* sentinel */ },
  263. +};
  264. +MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match);
  265. +
  266. +struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
  267. +{
  268. + struct platform_device *pdev;
  269. + struct mtk_pcs_lynxi *mpcs;
  270. +
  271. + if (!np)
  272. + return NULL;
  273. +
  274. + if (!of_device_is_available(np))
  275. + return ERR_PTR(-ENODEV);
  276. +
  277. + if (!of_match_node(mtk_pcs_lynxi_of_match, np))
  278. + return ERR_PTR(-EINVAL);
  279. +
  280. + pdev = of_find_device_by_node(np);
  281. + if (!pdev || !platform_get_drvdata(pdev)) {
  282. + if (pdev)
  283. + put_device(&pdev->dev);
  284. + return ERR_PTR(-EPROBE_DEFER);
  285. + }
  286. +
  287. + mpcs = platform_get_drvdata(pdev);
  288. + device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
  289. +
  290. + return &mpcs->pcs;
  291. +}
  292. +EXPORT_SYMBOL(mtk_pcs_lynxi_get);
  293. +
  294. +void mtk_pcs_lynxi_put(struct phylink_pcs *pcs)
  295. +{
  296. + struct mtk_pcs_lynxi *cur, *mpcs = NULL;
  297. +
  298. + if (!pcs)
  299. + return;
  300. +
  301. + mutex_lock(&instance_mutex);
  302. + list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node)
  303. + if (pcs == &cur->pcs) {
  304. + mpcs = cur;
  305. + break;
  306. + }
  307. + mutex_unlock(&instance_mutex);
  308. +
  309. + if (WARN_ON(!mpcs))
  310. + return;
  311. +
  312. + put_device(mpcs->dev);
  313. +}
  314. +EXPORT_SYMBOL(mtk_pcs_lynxi_put);
  315. +
  316. +static struct platform_driver mtk_pcs_lynxi_driver = {
  317. + .driver = {
  318. + .name = "mtk-pcs-lynxi",
  319. + .suppress_bind_attrs = true,
  320. + .of_match_table = mtk_pcs_lynxi_of_match,
  321. + },
  322. + .probe = mtk_pcs_lynxi_probe,
  323. + .remove = mtk_pcs_lynxi_remove,
  324. +};
  325. +module_platform_driver(mtk_pcs_lynxi_driver);
  326. +
  327. +MODULE_AUTHOR("Daniel Golle <[email protected]>");
  328. +MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS");
  329. MODULE_LICENSE("GPL");
  330. --- a/include/linux/pcs/pcs-mtk-lynxi.h
  331. +++ b/include/linux/pcs/pcs-mtk-lynxi.h
  332. @@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create
  333. struct regmap *regmap,
  334. u32 ana_rgc3, u32 flags);
  335. void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
  336. +
  337. +#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI)
  338. +struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np);
  339. +void mtk_pcs_lynxi_put(struct phylink_pcs *pcs);
  340. +#else
  341. +static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
  342. +{
  343. + return NULL;
  344. +}
  345. +static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { }
  346. +#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */
  347. #endif