dwmac-meson8b.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
  3. *
  4. * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. *
  10. * You should have received a copy of the GNU General Public License
  11. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. #include <linux/clk.h>
  14. #include <linux/clk-provider.h>
  15. #include <linux/device.h>
  16. #include <linux/ethtool.h>
  17. #include <linux/io.h>
  18. #include <linux/ioport.h>
  19. #include <linux/module.h>
  20. #include <linux/of_device.h>
  21. #include <linux/of_net.h>
  22. #include <linux/mfd/syscon.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/stmmac.h>
  25. #include "stmmac_platform.h"
  26. #define PRG_ETH0 0x0
  27. #define PRG_ETH0_RGMII_MODE BIT(0)
  28. #define PRG_ETH0_EXT_PHY_MODE_MASK GENMASK(2, 0)
  29. #define PRG_ETH0_EXT_RGMII_MODE 1
  30. #define PRG_ETH0_EXT_RMII_MODE 4
  31. /* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
  32. #define PRG_ETH0_CLK_M250_SEL_SHIFT 4
  33. #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
  34. #define PRG_ETH0_TXDLY_SHIFT 5
  35. #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5)
  36. /* divider for the result of m250_sel */
  37. #define PRG_ETH0_CLK_M250_DIV_SHIFT 7
  38. #define PRG_ETH0_CLK_M250_DIV_WIDTH 3
  39. #define PRG_ETH0_RGMII_TX_CLK_EN 10
  40. #define PRG_ETH0_INVERTED_RMII_CLK BIT(11)
  41. #define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12)
  42. #define MUX_CLK_NUM_PARENTS 2
  43. struct meson8b_dwmac;
  44. struct meson8b_dwmac_data {
  45. int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
  46. };
  47. struct meson8b_dwmac {
  48. struct device *dev;
  49. void __iomem *regs;
  50. const struct meson8b_dwmac_data *data;
  51. phy_interface_t phy_mode;
  52. struct clk *rgmii_tx_clk;
  53. u32 tx_delay_ns;
  54. };
  55. struct meson8b_dwmac_clk_configs {
  56. struct clk_mux m250_mux;
  57. struct clk_divider m250_div;
  58. struct clk_fixed_factor fixed_div2;
  59. struct clk_gate rgmii_tx_en;
  60. };
  61. static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
  62. u32 mask, u32 value)
  63. {
  64. u32 data;
  65. data = readl(dwmac->regs + reg);
  66. data &= ~mask;
  67. data |= (value & mask);
  68. writel(data, dwmac->regs + reg);
  69. }
  70. static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac,
  71. const char *name_suffix,
  72. const char **parent_names,
  73. int num_parents,
  74. const struct clk_ops *ops,
  75. struct clk_hw *hw)
  76. {
  77. struct clk_init_data init;
  78. char clk_name[32];
  79. snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dwmac->dev),
  80. name_suffix);
  81. init.name = clk_name;
  82. init.ops = ops;
  83. init.flags = CLK_SET_RATE_PARENT;
  84. init.parent_names = parent_names;
  85. init.num_parents = num_parents;
  86. hw->init = &init;
  87. return devm_clk_register(dwmac->dev, hw);
  88. }
  89. static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
  90. {
  91. int i, ret;
  92. struct clk *clk;
  93. struct device *dev = dwmac->dev;
  94. const char *parent_name, *mux_parent_names[MUX_CLK_NUM_PARENTS];
  95. struct meson8b_dwmac_clk_configs *clk_configs;
  96. clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
  97. if (!clk_configs)
  98. return -ENOMEM;
  99. /* get the mux parents from DT */
  100. for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
  101. char name[16];
  102. snprintf(name, sizeof(name), "clkin%d", i);
  103. clk = devm_clk_get(dev, name);
  104. if (IS_ERR(clk)) {
  105. ret = PTR_ERR(clk);
  106. if (ret != -EPROBE_DEFER)
  107. dev_err(dev, "Missing clock %s\n", name);
  108. return ret;
  109. }
  110. mux_parent_names[i] = __clk_get_name(clk);
  111. }
  112. clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0;
  113. clk_configs->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
  114. clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
  115. clk = meson8b_dwmac_register_clk(dwmac, "m250_sel", mux_parent_names,
  116. MUX_CLK_NUM_PARENTS, &clk_mux_ops,
  117. &clk_configs->m250_mux.hw);
  118. if (WARN_ON(IS_ERR(clk)))
  119. return PTR_ERR(clk);
  120. parent_name = __clk_get_name(clk);
  121. clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
  122. clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
  123. clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
  124. clk_configs->m250_div.flags = CLK_DIVIDER_ONE_BASED |
  125. CLK_DIVIDER_ALLOW_ZERO |
  126. CLK_DIVIDER_ROUND_CLOSEST;
  127. clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_name, 1,
  128. &clk_divider_ops,
  129. &clk_configs->m250_div.hw);
  130. if (WARN_ON(IS_ERR(clk)))
  131. return PTR_ERR(clk);
  132. parent_name = __clk_get_name(clk);
  133. clk_configs->fixed_div2.mult = 1;
  134. clk_configs->fixed_div2.div = 2;
  135. clk = meson8b_dwmac_register_clk(dwmac, "fixed_div2", &parent_name, 1,
  136. &clk_fixed_factor_ops,
  137. &clk_configs->fixed_div2.hw);
  138. if (WARN_ON(IS_ERR(clk)))
  139. return PTR_ERR(clk);
  140. parent_name = __clk_get_name(clk);
  141. clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
  142. clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
  143. clk = meson8b_dwmac_register_clk(dwmac, "rgmii_tx_en", &parent_name, 1,
  144. &clk_gate_ops,
  145. &clk_configs->rgmii_tx_en.hw);
  146. if (WARN_ON(IS_ERR(clk)))
  147. return PTR_ERR(clk);
  148. dwmac->rgmii_tx_clk = clk;
  149. return 0;
  150. }
  151. static int meson8b_set_phy_mode(struct meson8b_dwmac *dwmac)
  152. {
  153. switch (dwmac->phy_mode) {
  154. case PHY_INTERFACE_MODE_RGMII:
  155. case PHY_INTERFACE_MODE_RGMII_RXID:
  156. case PHY_INTERFACE_MODE_RGMII_ID:
  157. case PHY_INTERFACE_MODE_RGMII_TXID:
  158. /* enable RGMII mode */
  159. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  160. PRG_ETH0_RGMII_MODE,
  161. PRG_ETH0_RGMII_MODE);
  162. break;
  163. case PHY_INTERFACE_MODE_RMII:
  164. /* disable RGMII mode -> enables RMII mode */
  165. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  166. PRG_ETH0_RGMII_MODE, 0);
  167. break;
  168. default:
  169. dev_err(dwmac->dev, "fail to set phy-mode %s\n",
  170. phy_modes(dwmac->phy_mode));
  171. return -EINVAL;
  172. }
  173. return 0;
  174. }
  175. static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac)
  176. {
  177. switch (dwmac->phy_mode) {
  178. case PHY_INTERFACE_MODE_RGMII:
  179. case PHY_INTERFACE_MODE_RGMII_RXID:
  180. case PHY_INTERFACE_MODE_RGMII_ID:
  181. case PHY_INTERFACE_MODE_RGMII_TXID:
  182. /* enable RGMII mode */
  183. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  184. PRG_ETH0_EXT_PHY_MODE_MASK,
  185. PRG_ETH0_EXT_RGMII_MODE);
  186. break;
  187. case PHY_INTERFACE_MODE_RMII:
  188. /* disable RGMII mode -> enables RMII mode */
  189. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  190. PRG_ETH0_EXT_PHY_MODE_MASK,
  191. PRG_ETH0_EXT_RMII_MODE);
  192. break;
  193. default:
  194. dev_err(dwmac->dev, "fail to set phy-mode %s\n",
  195. phy_modes(dwmac->phy_mode));
  196. return -EINVAL;
  197. }
  198. return 0;
  199. }
  200. static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
  201. {
  202. int ret;
  203. u8 tx_dly_val = 0;
  204. switch (dwmac->phy_mode) {
  205. case PHY_INTERFACE_MODE_RGMII:
  206. case PHY_INTERFACE_MODE_RGMII_RXID:
  207. /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where
  208. * 8ns are exactly one cycle of the 125MHz RGMII TX clock):
  209. * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
  210. */
  211. tx_dly_val = dwmac->tx_delay_ns >> 1;
  212. /* fall through */
  213. case PHY_INTERFACE_MODE_RGMII_ID:
  214. case PHY_INTERFACE_MODE_RGMII_TXID:
  215. /* only relevant for RMII mode -> disable in RGMII mode */
  216. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  217. PRG_ETH0_INVERTED_RMII_CLK, 0);
  218. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
  219. tx_dly_val << PRG_ETH0_TXDLY_SHIFT);
  220. /* Configure the 125MHz RGMII TX clock, the IP block changes
  221. * the output automatically (= without us having to configure
  222. * a register) based on the line-speed (125MHz for Gbit speeds,
  223. * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
  224. */
  225. ret = clk_set_rate(dwmac->rgmii_tx_clk, 125 * 1000 * 1000);
  226. if (ret) {
  227. dev_err(dwmac->dev,
  228. "failed to set RGMII TX clock\n");
  229. return ret;
  230. }
  231. ret = clk_prepare_enable(dwmac->rgmii_tx_clk);
  232. if (ret) {
  233. dev_err(dwmac->dev,
  234. "failed to enable the RGMII TX clock\n");
  235. return ret;
  236. }
  237. devm_add_action_or_reset(dwmac->dev,
  238. (void(*)(void *))clk_disable_unprepare,
  239. dwmac->rgmii_tx_clk);
  240. break;
  241. case PHY_INTERFACE_MODE_RMII:
  242. /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
  243. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
  244. PRG_ETH0_INVERTED_RMII_CLK,
  245. PRG_ETH0_INVERTED_RMII_CLK);
  246. /* TX clock delay cannot be configured in RMII mode */
  247. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
  248. 0);
  249. break;
  250. default:
  251. dev_err(dwmac->dev, "unsupported phy-mode %s\n",
  252. phy_modes(dwmac->phy_mode));
  253. return -EINVAL;
  254. }
  255. /* enable TX_CLK and PHY_REF_CLK generator */
  256. meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
  257. PRG_ETH0_TX_AND_PHY_REF_CLK);
  258. return 0;
  259. }
  260. static int meson8b_dwmac_probe(struct platform_device *pdev)
  261. {
  262. struct plat_stmmacenet_data *plat_dat;
  263. struct stmmac_resources stmmac_res;
  264. struct resource *res;
  265. struct meson8b_dwmac *dwmac;
  266. int ret;
  267. ret = stmmac_get_platform_resources(pdev, &stmmac_res);
  268. if (ret)
  269. return ret;
  270. plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
  271. if (IS_ERR(plat_dat))
  272. return PTR_ERR(plat_dat);
  273. dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
  274. if (!dwmac) {
  275. ret = -ENOMEM;
  276. goto err_remove_config_dt;
  277. }
  278. dwmac->data = (const struct meson8b_dwmac_data *)
  279. of_device_get_match_data(&pdev->dev);
  280. if (!dwmac->data) {
  281. ret = -EINVAL;
  282. goto err_remove_config_dt;
  283. }
  284. res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  285. dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
  286. if (IS_ERR(dwmac->regs)) {
  287. ret = PTR_ERR(dwmac->regs);
  288. goto err_remove_config_dt;
  289. }
  290. dwmac->dev = &pdev->dev;
  291. dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
  292. if (dwmac->phy_mode < 0) {
  293. dev_err(&pdev->dev, "missing phy-mode property\n");
  294. ret = -EINVAL;
  295. goto err_remove_config_dt;
  296. }
  297. /* use 2ns as fallback since this value was previously hardcoded */
  298. if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
  299. &dwmac->tx_delay_ns))
  300. dwmac->tx_delay_ns = 2;
  301. ret = meson8b_init_rgmii_tx_clk(dwmac);
  302. if (ret)
  303. goto err_remove_config_dt;
  304. ret = dwmac->data->set_phy_mode(dwmac);
  305. if (ret)
  306. goto err_remove_config_dt;
  307. ret = meson8b_init_prg_eth(dwmac);
  308. if (ret)
  309. goto err_remove_config_dt;
  310. plat_dat->bsp_priv = dwmac;
  311. ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
  312. if (ret)
  313. goto err_remove_config_dt;
  314. return 0;
  315. err_remove_config_dt:
  316. stmmac_remove_config_dt(pdev, plat_dat);
  317. return ret;
  318. }
  319. static const struct meson8b_dwmac_data meson8b_dwmac_data = {
  320. .set_phy_mode = meson8b_set_phy_mode,
  321. };
  322. static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
  323. .set_phy_mode = meson_axg_set_phy_mode,
  324. };
  325. static const struct of_device_id meson8b_dwmac_match[] = {
  326. {
  327. .compatible = "amlogic,meson8b-dwmac",
  328. .data = &meson8b_dwmac_data,
  329. },
  330. {
  331. .compatible = "amlogic,meson8m2-dwmac",
  332. .data = &meson8b_dwmac_data,
  333. },
  334. {
  335. .compatible = "amlogic,meson-gxbb-dwmac",
  336. .data = &meson8b_dwmac_data,
  337. },
  338. {
  339. .compatible = "amlogic,meson-axg-dwmac",
  340. .data = &meson_axg_dwmac_data,
  341. },
  342. { }
  343. };
  344. MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
  345. static struct platform_driver meson8b_dwmac_driver = {
  346. .probe = meson8b_dwmac_probe,
  347. .remove = stmmac_pltfr_remove,
  348. .driver = {
  349. .name = "meson8b-dwmac",
  350. .pm = &stmmac_pltfr_pm_ops,
  351. .of_match_table = meson8b_dwmac_match,
  352. },
  353. };
  354. module_platform_driver(meson8b_dwmac_driver);
  355. MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
  356. MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
  357. MODULE_LICENSE("GPL v2");