1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * StarFive DWMAC platform driver |
4 | * |
5 | * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> |
6 | * Copyright (C) 2022 StarFive Technology Co., Ltd. |
7 | * |
8 | */ |
9 | |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/property.h> |
13 | #include <linux/mfd/syscon.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | #include "stmmac_platform.h" |
17 | |
18 | #define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 |
19 | #define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 |
20 | #define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U |
21 | |
22 | #define JH7100_SYSMAIN_REGISTER49_DLYCHAIN 0xc8 |
23 | |
24 | struct starfive_dwmac_data { |
25 | unsigned int gtxclk_dlychain; |
26 | }; |
27 | |
28 | struct starfive_dwmac { |
29 | struct device *dev; |
30 | struct clk *clk_tx; |
31 | const struct starfive_dwmac_data *data; |
32 | }; |
33 | |
34 | static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode) |
35 | { |
36 | struct starfive_dwmac *dwmac = priv; |
37 | unsigned long rate; |
38 | int err; |
39 | |
40 | rate = clk_get_rate(clk: dwmac->clk_tx); |
41 | |
42 | switch (speed) { |
43 | case SPEED_1000: |
44 | rate = 125000000; |
45 | break; |
46 | case SPEED_100: |
47 | rate = 25000000; |
48 | break; |
49 | case SPEED_10: |
50 | rate = 2500000; |
51 | break; |
52 | default: |
53 | dev_err(dwmac->dev, "invalid speed %u\n" , speed); |
54 | break; |
55 | } |
56 | |
57 | err = clk_set_rate(clk: dwmac->clk_tx, rate); |
58 | if (err) |
59 | dev_err(dwmac->dev, "failed to set tx rate %lu\n" , rate); |
60 | } |
61 | |
62 | static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) |
63 | { |
64 | struct starfive_dwmac *dwmac = plat_dat->bsp_priv; |
65 | struct regmap *regmap; |
66 | unsigned int args[2]; |
67 | unsigned int mode; |
68 | int err; |
69 | |
70 | switch (plat_dat->mac_interface) { |
71 | case PHY_INTERFACE_MODE_RMII: |
72 | mode = STARFIVE_DWMAC_PHY_INFT_RMII; |
73 | break; |
74 | |
75 | case PHY_INTERFACE_MODE_RGMII: |
76 | case PHY_INTERFACE_MODE_RGMII_ID: |
77 | case PHY_INTERFACE_MODE_RGMII_RXID: |
78 | case PHY_INTERFACE_MODE_RGMII_TXID: |
79 | mode = STARFIVE_DWMAC_PHY_INFT_RGMII; |
80 | break; |
81 | |
82 | default: |
83 | dev_err(dwmac->dev, "unsupported interface %d\n" , |
84 | plat_dat->mac_interface); |
85 | return -EINVAL; |
86 | } |
87 | |
88 | regmap = syscon_regmap_lookup_by_phandle_args(np: dwmac->dev->of_node, |
89 | property: "starfive,syscon" , |
90 | arg_count: 2, out_args: args); |
91 | if (IS_ERR(ptr: regmap)) |
92 | return dev_err_probe(dev: dwmac->dev, err: PTR_ERR(ptr: regmap), fmt: "getting the regmap failed\n" ); |
93 | |
94 | /* args[0]:offset args[1]: shift */ |
95 | err = regmap_update_bits(map: regmap, reg: args[0], |
96 | STARFIVE_DWMAC_PHY_INFT_FIELD << args[1], |
97 | val: mode << args[1]); |
98 | if (err) |
99 | return dev_err_probe(dev: dwmac->dev, err, fmt: "error setting phy mode\n" ); |
100 | |
101 | if (dwmac->data) { |
102 | err = regmap_write(map: regmap, JH7100_SYSMAIN_REGISTER49_DLYCHAIN, |
103 | val: dwmac->data->gtxclk_dlychain); |
104 | if (err) |
105 | return dev_err_probe(dev: dwmac->dev, err, |
106 | fmt: "error selecting gtxclk delay chain\n" ); |
107 | } |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int starfive_dwmac_probe(struct platform_device *pdev) |
113 | { |
114 | struct plat_stmmacenet_data *plat_dat; |
115 | struct stmmac_resources stmmac_res; |
116 | struct starfive_dwmac *dwmac; |
117 | struct clk *clk_gtx; |
118 | int err; |
119 | |
120 | err = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
121 | if (err) |
122 | return dev_err_probe(dev: &pdev->dev, err, |
123 | fmt: "failed to get resources\n" ); |
124 | |
125 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
126 | if (IS_ERR(ptr: plat_dat)) |
127 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: plat_dat), |
128 | fmt: "dt configuration failed\n" ); |
129 | |
130 | dwmac = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dwmac), GFP_KERNEL); |
131 | if (!dwmac) |
132 | return -ENOMEM; |
133 | |
134 | dwmac->data = device_get_match_data(dev: &pdev->dev); |
135 | |
136 | dwmac->clk_tx = devm_clk_get_enabled(dev: &pdev->dev, id: "tx" ); |
137 | if (IS_ERR(ptr: dwmac->clk_tx)) |
138 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: dwmac->clk_tx), |
139 | fmt: "error getting tx clock\n" ); |
140 | |
141 | clk_gtx = devm_clk_get_enabled(dev: &pdev->dev, id: "gtx" ); |
142 | if (IS_ERR(ptr: clk_gtx)) |
143 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: clk_gtx), |
144 | fmt: "error getting gtx clock\n" ); |
145 | |
146 | /* Generally, the rgmii_tx clock is provided by the internal clock, |
147 | * which needs to match the corresponding clock frequency according |
148 | * to different speeds. If the rgmii_tx clock is provided by the |
149 | * external rgmii_rxin, there is no need to configure the clock |
150 | * internally, because rgmii_rxin will be adaptively adjusted. |
151 | */ |
152 | if (!device_property_read_bool(dev: &pdev->dev, propname: "starfive,tx-use-rgmii-clk" )) |
153 | plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed; |
154 | |
155 | dwmac->dev = &pdev->dev; |
156 | plat_dat->bsp_priv = dwmac; |
157 | plat_dat->dma_cfg->dche = true; |
158 | |
159 | err = starfive_dwmac_set_mode(plat_dat); |
160 | if (err) |
161 | return err; |
162 | |
163 | return stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
164 | } |
165 | |
166 | static const struct starfive_dwmac_data jh7100_data = { |
167 | .gtxclk_dlychain = 4, |
168 | }; |
169 | |
170 | static const struct of_device_id starfive_dwmac_match[] = { |
171 | { .compatible = "starfive,jh7100-dwmac" , .data = &jh7100_data }, |
172 | { .compatible = "starfive,jh7110-dwmac" }, |
173 | { /* sentinel */ } |
174 | }; |
175 | MODULE_DEVICE_TABLE(of, starfive_dwmac_match); |
176 | |
177 | static struct platform_driver starfive_dwmac_driver = { |
178 | .probe = starfive_dwmac_probe, |
179 | .remove_new = stmmac_pltfr_remove, |
180 | .driver = { |
181 | .name = "starfive-dwmac" , |
182 | .pm = &stmmac_pltfr_pm_ops, |
183 | .of_match_table = starfive_dwmac_match, |
184 | }, |
185 | }; |
186 | module_platform_driver(starfive_dwmac_driver); |
187 | |
188 | MODULE_LICENSE("GPL" ); |
189 | MODULE_DESCRIPTION("StarFive DWMAC platform driver" ); |
190 | MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>" ); |
191 | MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>" ); |
192 | |