File Index
Files and directories
Sign in
FILE

dwmac-ingenic.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * dwmac-ingenic.c - Ingenic SoCs DWMAC specific glue layer
  4 *
  5 * Copyright (c) 2021 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
  6 */
  7
  8#include <linux/bitfield.h>
  9#include <linux/clk.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_net.h>
 15#include <linux/phy.h>
 16#include <linux/platform_device.h>
 17#include <linux/regmap.h>
 18#include <linux/slab.h>
 19#include <linux/stmmac.h>
 20
 21#include "stmmac_platform.h"
 22
 23#define MACPHYC_TXCLK_SEL_MASK		GENMASK(31, 31)
 24#define MACPHYC_TXCLK_SEL_OUTPUT	0x1
 25#define MACPHYC_TXCLK_SEL_INPUT		0x0
 26#define MACPHYC_MODE_SEL_MASK		GENMASK(31, 31)
 27#define MACPHYC_MODE_SEL_RMII		0x0
 28#define MACPHYC_TX_SEL_MASK			GENMASK(19, 19)
 29#define MACPHYC_TX_SEL_ORIGIN		0x0
 30#define MACPHYC_TX_SEL_DELAY		0x1
 31#define MACPHYC_TX_DELAY_MASK		GENMASK(18, 12)
 32#define MACPHYC_RX_SEL_MASK			GENMASK(11, 11)
 33#define MACPHYC_RX_SEL_ORIGIN		0x0
 34#define MACPHYC_RX_SEL_DELAY		0x1
 35#define MACPHYC_RX_DELAY_MASK		GENMASK(10, 4)
 36#define MACPHYC_SOFT_RST_MASK		GENMASK(3, 3)
 37#define MACPHYC_PHY_INFT_MASK		GENMASK(2, 0)
 38
 39#define MACPHYC_TX_DELAY_PS_MAX		2496
 40#define MACPHYC_TX_DELAY_PS_MIN		20
 41
 42#define MACPHYC_RX_DELAY_PS_MAX		2496
 43#define MACPHYC_RX_DELAY_PS_MIN		20
 44
 45enum ingenic_mac_version {
 46	ID_JZ4775,
 47	ID_X1000,
 48	ID_X1600,
 49	ID_X1830,
 50	ID_X2000,
 51};
 52
 53struct ingenic_mac {
 54	const struct ingenic_soc_info *soc_info;
 55	struct plat_stmmacenet_data *plat_dat;
 56	struct device *dev;
 57	struct regmap *regmap;
 58
 59	int rx_delay;
 60	int tx_delay;
 61};
 62
 63struct ingenic_soc_info {
 64	enum ingenic_mac_version version;
 65	u32 mask;
 66
 67	int (*set_mode)(struct ingenic_mac *mac, u8 phy_intf_sel);
 68
 69	u8 valid_phy_intf_sel;
 70};
 71
 72static int jz4775_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
 73{
 74	unsigned int val;
 75
 76	val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel) |
 77	      FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT);
 78
 79	/* Update MAC PHY control register */
 80	return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
 81}
 82
 83static int x1000_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
 84{
 85	/* Update MAC PHY control register */
 86	return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, 0);
 87}
 88
 89static int x1600_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
 90{
 91	unsigned int val;
 92
 93	val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
 94
 95	/* Update MAC PHY control register */
 96	return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
 97}
 98
 99static int x1830_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
100{
101	unsigned int val;
102
103	val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) |
104	      FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
105
106	/* Update MAC PHY control register */
107	return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
108}
109
110static int x2000_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
111{
112	unsigned int val;
113
114	val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
115
116	if (phy_intf_sel == PHY_INTF_SEL_RMII) {
117		val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) |
118		       FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
119	} else if (phy_intf_sel == PHY_INTF_SEL_RGMII) {
120		if (mac->tx_delay == 0)
121			val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN);
122		else
123			val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_DELAY) |
124			       FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1);
125
126		if (mac->rx_delay == 0)
127			val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
128		else
129			val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_DELAY) |
130				   FIELD_PREP(MACPHYC_RX_DELAY_MASK, (mac->rx_delay + 9750) / 19500 - 1);
131	}
132
133	/* Update MAC PHY control register */
134	return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
135}
136
137static int ingenic_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
138{
139	struct ingenic_mac *mac = bsp_priv;
140
141	if (!mac->soc_info->set_mode)
142		return 0;
143
144	if (phy_intf_sel >= BITS_PER_BYTE ||
145	    ~mac->soc_info->valid_phy_intf_sel & BIT(phy_intf_sel))
146		return -EINVAL;
147
148	dev_dbg(mac->dev, "MAC PHY control register: interface %s\n",
149		phy_modes(mac->plat_dat->phy_interface));
150
151	return mac->soc_info->set_mode(mac, phy_intf_sel);
152}
153
154static int ingenic_mac_probe(struct platform_device *pdev)
155{
156	struct plat_stmmacenet_data *plat_dat;
157	struct stmmac_resources stmmac_res;
158	struct ingenic_mac *mac;
159	const struct ingenic_soc_info *data;
160	u32 tx_delay_ps, rx_delay_ps;
161	int ret;
162
163	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
164	if (ret)
165		return ret;
166
167	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
168	if (IS_ERR(plat_dat))
169		return PTR_ERR(plat_dat);
170
171	mac = devm_kzalloc(&pdev->dev, sizeof(*mac), GFP_KERNEL);
172	if (!mac)
173		return -ENOMEM;
174
175	data = of_device_get_match_data(&pdev->dev);
176	if (!data) {
177		dev_err(&pdev->dev, "No of match data provided\n");
178		return -EINVAL;
179	}
180
181	/* Get MAC PHY control register */
182	mac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "mode-reg");
183	if (IS_ERR(mac->regmap)) {
184		dev_err(&pdev->dev, "%s: Failed to get syscon regmap\n", __func__);
185		return PTR_ERR(mac->regmap);
186	}
187
188	if (!of_property_read_u32(pdev->dev.of_node, "tx-clk-delay-ps", &tx_delay_ps)) {
189		if (tx_delay_ps >= MACPHYC_TX_DELAY_PS_MIN &&
190			tx_delay_ps <= MACPHYC_TX_DELAY_PS_MAX) {
191			mac->tx_delay = tx_delay_ps * 1000;
192		} else {
193			dev_err(&pdev->dev, "Invalid TX clock delay: %dps\n", tx_delay_ps);
194			return -EINVAL;
195		}
196	}
197
198	if (!of_property_read_u32(pdev->dev.of_node, "rx-clk-delay-ps", &rx_delay_ps)) {
199		if (rx_delay_ps >= MACPHYC_RX_DELAY_PS_MIN &&
200			rx_delay_ps <= MACPHYC_RX_DELAY_PS_MAX) {
201			mac->rx_delay = rx_delay_ps * 1000;
202		} else {
203			dev_err(&pdev->dev, "Invalid RX clock delay: %dps\n", rx_delay_ps);
204			return -EINVAL;
205		}
206	}
207
208	mac->soc_info = data;
209	mac->dev = &pdev->dev;
210	mac->plat_dat = plat_dat;
211
212	plat_dat->bsp_priv = mac;
213	plat_dat->set_phy_intf_sel = ingenic_set_phy_intf_sel;
214
215	return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
216}
217
218static struct ingenic_soc_info jz4775_soc_info = {
219	.version = ID_JZ4775,
220	.mask = MACPHYC_TXCLK_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
221
222	.set_mode = jz4775_mac_set_mode,
223	.valid_phy_intf_sel = BIT(PHY_INTF_SEL_GMII_MII) |
224			      BIT(PHY_INTF_SEL_RGMII) |
225			      BIT(PHY_INTF_SEL_RMII),
226};
227
228static struct ingenic_soc_info x1000_soc_info = {
229	.version = ID_X1000,
230	.mask = MACPHYC_SOFT_RST_MASK,
231
232	.set_mode = x1000_mac_set_mode,
233	.valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
234};
235
236static struct ingenic_soc_info x1600_soc_info = {
237	.version = ID_X1600,
238	.mask = MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
239
240	.set_mode = x1600_mac_set_mode,
241	.valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
242};
243
244static struct ingenic_soc_info x1830_soc_info = {
245	.version = ID_X1830,
246	.mask = MACPHYC_MODE_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
247
248	.set_mode = x1830_mac_set_mode,
249	.valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
250};
251
252static struct ingenic_soc_info x2000_soc_info = {
253	.version = ID_X2000,
254	.mask = MACPHYC_TX_SEL_MASK | MACPHYC_TX_DELAY_MASK | MACPHYC_RX_SEL_MASK |
255			MACPHYC_RX_DELAY_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
256
257	.set_mode = x2000_mac_set_mode,
258	.valid_phy_intf_sel = BIT(PHY_INTF_SEL_RGMII) |
259			      BIT(PHY_INTF_SEL_RMII),
260};
261
262static const struct of_device_id ingenic_mac_of_matches[] = {
263	{ .compatible = "ingenic,jz4775-mac", .data = &jz4775_soc_info },
264	{ .compatible = "ingenic,x1000-mac", .data = &x1000_soc_info },
265	{ .compatible = "ingenic,x1600-mac", .data = &x1600_soc_info },
266	{ .compatible = "ingenic,x1830-mac", .data = &x1830_soc_info },
267	{ .compatible = "ingenic,x2000-mac", .data = &x2000_soc_info },
268	{ }
269};
270MODULE_DEVICE_TABLE(of, ingenic_mac_of_matches);
271
272static struct platform_driver ingenic_mac_driver = {
273	.probe		= ingenic_mac_probe,
274	.driver		= {
275		.name	= "ingenic-mac",
276		.pm		= &stmmac_pltfr_pm_ops,
277		.of_match_table = ingenic_mac_of_matches,
278	},
279};
280module_platform_driver(ingenic_mac_driver);
281
282MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
283MODULE_DESCRIPTION("Ingenic SoCs DWMAC specific glue layer");
284MODULE_LICENSE("GPL v2");
~/ Read-only file preview.