dwmac-imx.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * dwmac-imx.c - DWMAC Specific Glue layer for NXP imx8
  4. *
  5. * Copyright 2020 NXP
  6. *
  7. */
  8. #include <linux/clk.h>
  9. #include <linux/gpio/consumer.h>
  10. #include <linux/kernel.h>
  11. #include <linux/mfd/syscon.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <linux/of_device.h>
  15. #include <linux/of_net.h>
  16. #include <linux/phy.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/pm_wakeirq.h>
  19. #include <linux/regmap.h>
  20. #include <linux/slab.h>
  21. #include <linux/stmmac.h>
  22. #include "stmmac_platform.h"
  23. #define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16)
  24. #define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16)
  25. #define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16)
  26. #define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16)
  27. #define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19)
  28. #define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20)
  29. #define GPR_ENET_QOS_RGMII_EN (0x1 << 21)
  30. struct imx_dwmac_ops {
  31. u32 addr_width;
  32. bool mac_rgmii_txclk_auto_adj;
  33. int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
  34. };
  35. struct imx_priv_data {
  36. struct device *dev;
  37. struct clk *clk_tx;
  38. struct clk *clk_mem;
  39. struct regmap *intf_regmap;
  40. u32 intf_reg_off;
  41. bool rmii_refclk_ext;
  42. const struct imx_dwmac_ops *ops;
  43. struct plat_stmmacenet_data *plat_dat;
  44. };
  45. static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
  46. {
  47. struct imx_priv_data *dwmac = plat_dat->bsp_priv;
  48. int val;
  49. switch (plat_dat->interface) {
  50. case PHY_INTERFACE_MODE_MII:
  51. val = GPR_ENET_QOS_INTF_SEL_MII;
  52. break;
  53. case PHY_INTERFACE_MODE_RMII:
  54. val = GPR_ENET_QOS_INTF_SEL_RMII;
  55. val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
  56. break;
  57. case PHY_INTERFACE_MODE_RGMII:
  58. case PHY_INTERFACE_MODE_RGMII_ID:
  59. case PHY_INTERFACE_MODE_RGMII_RXID:
  60. case PHY_INTERFACE_MODE_RGMII_TXID:
  61. val = GPR_ENET_QOS_INTF_SEL_RGMII |
  62. GPR_ENET_QOS_RGMII_EN;
  63. break;
  64. default:
  65. pr_debug("imx dwmac doesn't support %d interface\n",
  66. plat_dat->interface);
  67. return -EINVAL;
  68. }
  69. val |= GPR_ENET_QOS_CLK_GEN_EN;
  70. return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
  71. GPR_ENET_QOS_INTF_MODE_MASK, val);
  72. };
  73. static int
  74. imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
  75. {
  76. int ret = 0;
  77. /* TBD: depends on imx8dxl scu interfaces to be upstreamed */
  78. return ret;
  79. }
  80. static int imx_dwmac_clks_config(void *priv, bool enabled)
  81. {
  82. struct imx_priv_data *dwmac = priv;
  83. int ret = 0;
  84. if (enabled) {
  85. ret = clk_prepare_enable(dwmac->clk_mem);
  86. if (ret) {
  87. dev_err(dwmac->dev, "mem clock enable failed\n");
  88. return ret;
  89. }
  90. ret = clk_prepare_enable(dwmac->clk_tx);
  91. if (ret) {
  92. dev_err(dwmac->dev, "tx clock enable failed\n");
  93. clk_disable_unprepare(dwmac->clk_mem);
  94. return ret;
  95. }
  96. } else {
  97. clk_disable_unprepare(dwmac->clk_tx);
  98. clk_disable_unprepare(dwmac->clk_mem);
  99. }
  100. return ret;
  101. }
  102. static int imx_dwmac_init(struct platform_device *pdev, void *priv)
  103. {
  104. struct plat_stmmacenet_data *plat_dat;
  105. struct imx_priv_data *dwmac = priv;
  106. int ret;
  107. plat_dat = dwmac->plat_dat;
  108. if (dwmac->ops->set_intf_mode) {
  109. ret = dwmac->ops->set_intf_mode(plat_dat);
  110. if (ret)
  111. return ret;
  112. }
  113. return 0;
  114. }
  115. static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
  116. {
  117. /* nothing to do now */
  118. }
  119. static void imx_dwmac_fix_speed(void *priv, unsigned int speed)
  120. {
  121. struct plat_stmmacenet_data *plat_dat;
  122. struct imx_priv_data *dwmac = priv;
  123. unsigned long rate;
  124. int err;
  125. plat_dat = dwmac->plat_dat;
  126. if (dwmac->ops->mac_rgmii_txclk_auto_adj ||
  127. (plat_dat->interface == PHY_INTERFACE_MODE_RMII) ||
  128. (plat_dat->interface == PHY_INTERFACE_MODE_MII))
  129. return;
  130. switch (speed) {
  131. case SPEED_1000:
  132. rate = 125000000;
  133. break;
  134. case SPEED_100:
  135. rate = 25000000;
  136. break;
  137. case SPEED_10:
  138. rate = 2500000;
  139. break;
  140. default:
  141. dev_err(dwmac->dev, "invalid speed %u\n", speed);
  142. return;
  143. }
  144. err = clk_set_rate(dwmac->clk_tx, rate);
  145. if (err < 0)
  146. dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
  147. }
  148. static int
  149. imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev)
  150. {
  151. struct device_node *np = dev->of_node;
  152. int err = 0;
  153. if (of_get_property(np, "snps,rmii_refclk_ext", NULL))
  154. dwmac->rmii_refclk_ext = true;
  155. dwmac->clk_tx = devm_clk_get(dev, "tx");
  156. if (IS_ERR(dwmac->clk_tx)) {
  157. dev_err(dev, "failed to get tx clock\n");
  158. return PTR_ERR(dwmac->clk_tx);
  159. }
  160. dwmac->clk_mem = NULL;
  161. if (of_machine_is_compatible("fsl,imx8dxl")) {
  162. dwmac->clk_mem = devm_clk_get(dev, "mem");
  163. if (IS_ERR(dwmac->clk_mem)) {
  164. dev_err(dev, "failed to get mem clock\n");
  165. return PTR_ERR(dwmac->clk_mem);
  166. }
  167. }
  168. if (of_machine_is_compatible("fsl,imx8mp")) {
  169. /* Binding doc describes the property:
  170. is required by i.MX8MP.
  171. is optional for i.MX8DXL.
  172. */
  173. dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode");
  174. if (IS_ERR(dwmac->intf_regmap))
  175. return PTR_ERR(dwmac->intf_regmap);
  176. err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off);
  177. if (err) {
  178. dev_err(dev, "Can't get intf mode reg offset (%d)\n", err);
  179. return err;
  180. }
  181. }
  182. return err;
  183. }
  184. static int imx_dwmac_probe(struct platform_device *pdev)
  185. {
  186. struct plat_stmmacenet_data *plat_dat;
  187. struct stmmac_resources stmmac_res;
  188. struct imx_priv_data *dwmac;
  189. const struct imx_dwmac_ops *data;
  190. int ret;
  191. ret = stmmac_get_platform_resources(pdev, &stmmac_res);
  192. if (ret)
  193. return ret;
  194. dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
  195. if (!dwmac)
  196. return -ENOMEM;
  197. plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
  198. if (IS_ERR(plat_dat))
  199. return PTR_ERR(plat_dat);
  200. data = of_device_get_match_data(&pdev->dev);
  201. if (!data) {
  202. dev_err(&pdev->dev, "failed to get match data\n");
  203. ret = -EINVAL;
  204. goto err_match_data;
  205. }
  206. dwmac->ops = data;
  207. dwmac->dev = &pdev->dev;
  208. ret = imx_dwmac_parse_dt(dwmac, &pdev->dev);
  209. if (ret) {
  210. dev_err(&pdev->dev, "failed to parse OF data\n");
  211. goto err_parse_dt;
  212. }
  213. plat_dat->host_dma_width = dwmac->ops->addr_width;
  214. plat_dat->init = imx_dwmac_init;
  215. plat_dat->exit = imx_dwmac_exit;
  216. plat_dat->clks_config = imx_dwmac_clks_config;
  217. plat_dat->fix_mac_speed = imx_dwmac_fix_speed;
  218. plat_dat->bsp_priv = dwmac;
  219. dwmac->plat_dat = plat_dat;
  220. ret = imx_dwmac_clks_config(dwmac, true);
  221. if (ret)
  222. goto err_clks_config;
  223. ret = imx_dwmac_init(pdev, dwmac);
  224. if (ret)
  225. goto err_dwmac_init;
  226. ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
  227. if (ret)
  228. goto err_drv_probe;
  229. return 0;
  230. err_drv_probe:
  231. imx_dwmac_exit(pdev, plat_dat->bsp_priv);
  232. err_dwmac_init:
  233. imx_dwmac_clks_config(dwmac, false);
  234. err_clks_config:
  235. err_parse_dt:
  236. err_match_data:
  237. stmmac_remove_config_dt(pdev, plat_dat);
  238. return ret;
  239. }
  240. static struct imx_dwmac_ops imx8mp_dwmac_data = {
  241. .addr_width = 34,
  242. .mac_rgmii_txclk_auto_adj = false,
  243. .set_intf_mode = imx8mp_set_intf_mode,
  244. };
  245. static struct imx_dwmac_ops imx8dxl_dwmac_data = {
  246. .addr_width = 32,
  247. .mac_rgmii_txclk_auto_adj = true,
  248. .set_intf_mode = imx8dxl_set_intf_mode,
  249. };
  250. static const struct of_device_id imx_dwmac_match[] = {
  251. { .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data },
  252. { .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data },
  253. { }
  254. };
  255. MODULE_DEVICE_TABLE(of, imx_dwmac_match);
  256. static struct platform_driver imx_dwmac_driver = {
  257. .probe = imx_dwmac_probe,
  258. .remove = stmmac_pltfr_remove,
  259. .driver = {
  260. .name = "imx-dwmac",
  261. .pm = &stmmac_pltfr_pm_ops,
  262. .of_match_table = imx_dwmac_match,
  263. },
  264. };
  265. module_platform_driver(imx_dwmac_driver);
  266. MODULE_AUTHOR("NXP");
  267. MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer");
  268. MODULE_LICENSE("GPL v2");