1 // SPDX-License-Identifier: GPL-2.0
3 * Texas Instruments CPSW Port's PHY Interface Mode selection Driver
5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
7 * Based on cpsw-phy-sel.c driver created by Mugunthan V N <mugunthanvnm@ti.com>
10 #include <linux/platform_device.h>
11 #include <linux/module.h>
12 #include <linux/mfd/syscon.h>
14 #include <linux/of_address.h>
15 #include <linux/of_net.h>
16 #include <linux/phy.h>
17 #include <linux/phy/phy.h>
18 #include <linux/regmap.h>
20 /* AM33xx SoC specific definitions for the CONTROL port */
21 #define AM33XX_GMII_SEL_MODE_MII 0
22 #define AM33XX_GMII_SEL_MODE_RMII 1
23 #define AM33XX_GMII_SEL_MODE_RGMII 2
26 PHY_GMII_SEL_PORT_MODE
= 0,
27 PHY_GMII_SEL_RGMII_ID_MODE
,
28 PHY_GMII_SEL_RMII_IO_CLK_EN
,
32 struct phy_gmii_sel_phy_priv
{
33 struct phy_gmii_sel_priv
*priv
;
36 int rmii_clock_external
;
38 struct regmap_field
*fields
[PHY_GMII_SEL_LAST
];
41 struct phy_gmii_sel_soc_data
{
44 const struct reg_field (*regfields
)[PHY_GMII_SEL_LAST
];
48 struct phy_gmii_sel_priv
{
50 const struct phy_gmii_sel_soc_data
*soc_data
;
51 struct regmap
*regmap
;
52 struct phy_provider
*phy_provider
;
53 struct phy_gmii_sel_phy_priv
*if_phys
;
58 static int phy_gmii_sel_mode(struct phy
*phy
, enum phy_mode mode
, int submode
)
60 struct phy_gmii_sel_phy_priv
*if_phy
= phy_get_drvdata(phy
);
61 const struct phy_gmii_sel_soc_data
*soc_data
= if_phy
->priv
->soc_data
;
62 struct device
*dev
= if_phy
->priv
->dev
;
63 struct regmap_field
*regfield
;
64 int ret
, rgmii_id
= 0;
65 u32 gmii_sel_mode
= 0;
67 if (mode
!= PHY_MODE_ETHERNET
)
71 case PHY_INTERFACE_MODE_RMII
:
72 gmii_sel_mode
= AM33XX_GMII_SEL_MODE_RMII
;
75 case PHY_INTERFACE_MODE_RGMII
:
76 case PHY_INTERFACE_MODE_RGMII_RXID
:
77 gmii_sel_mode
= AM33XX_GMII_SEL_MODE_RGMII
;
80 case PHY_INTERFACE_MODE_RGMII_ID
:
81 case PHY_INTERFACE_MODE_RGMII_TXID
:
82 gmii_sel_mode
= AM33XX_GMII_SEL_MODE_RGMII
;
86 case PHY_INTERFACE_MODE_MII
:
87 case PHY_INTERFACE_MODE_GMII
:
88 gmii_sel_mode
= AM33XX_GMII_SEL_MODE_MII
;
92 dev_warn(dev
, "port%u: unsupported mode: \"%s\"\n",
93 if_phy
->id
, phy_modes(submode
));
97 if_phy
->phy_if_mode
= submode
;
99 dev_dbg(dev
, "%s id:%u mode:%u rgmii_id:%d rmii_clk_ext:%d\n",
100 __func__
, if_phy
->id
, submode
, rgmii_id
,
101 if_phy
->rmii_clock_external
);
103 regfield
= if_phy
->fields
[PHY_GMII_SEL_PORT_MODE
];
104 ret
= regmap_field_write(regfield
, gmii_sel_mode
);
106 dev_err(dev
, "port%u: set mode fail %d", if_phy
->id
, ret
);
110 if (soc_data
->features
& BIT(PHY_GMII_SEL_RGMII_ID_MODE
) &&
111 if_phy
->fields
[PHY_GMII_SEL_RGMII_ID_MODE
]) {
112 regfield
= if_phy
->fields
[PHY_GMII_SEL_RGMII_ID_MODE
];
113 ret
= regmap_field_write(regfield
, rgmii_id
);
118 if (soc_data
->features
& BIT(PHY_GMII_SEL_RMII_IO_CLK_EN
) &&
119 if_phy
->fields
[PHY_GMII_SEL_RMII_IO_CLK_EN
]) {
120 regfield
= if_phy
->fields
[PHY_GMII_SEL_RMII_IO_CLK_EN
];
121 ret
= regmap_field_write(regfield
,
122 if_phy
->rmii_clock_external
);
129 struct reg_field phy_gmii_sel_fields_am33xx
[][PHY_GMII_SEL_LAST
] = {
131 [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x650, 0, 1),
132 [PHY_GMII_SEL_RGMII_ID_MODE
] = REG_FIELD(0x650, 4, 4),
133 [PHY_GMII_SEL_RMII_IO_CLK_EN
] = REG_FIELD(0x650, 6, 6),
136 [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x650, 2, 3),
137 [PHY_GMII_SEL_RGMII_ID_MODE
] = REG_FIELD(0x650, 5, 5),
138 [PHY_GMII_SEL_RMII_IO_CLK_EN
] = REG_FIELD(0x650, 7, 7),
143 struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am33xx
= {
145 .features
= BIT(PHY_GMII_SEL_RGMII_ID_MODE
) |
146 BIT(PHY_GMII_SEL_RMII_IO_CLK_EN
),
147 .regfields
= phy_gmii_sel_fields_am33xx
,
151 struct reg_field phy_gmii_sel_fields_dra7
[][PHY_GMII_SEL_LAST
] = {
153 [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x554, 0, 1),
156 [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x554, 4, 5),
161 struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dra7
= {
163 .regfields
= phy_gmii_sel_fields_dra7
,
167 struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814
= {
169 .features
= BIT(PHY_GMII_SEL_RGMII_ID_MODE
),
170 .regfields
= phy_gmii_sel_fields_am33xx
,
174 struct reg_field phy_gmii_sel_fields_am654
[][PHY_GMII_SEL_LAST
] = {
175 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x0, 0, 2), },
176 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x4, 0, 2), },
177 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x8, 0, 2), },
178 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0xC, 0, 2), },
179 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x10, 0, 2), },
180 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x14, 0, 2), },
181 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x18, 0, 2), },
182 { [PHY_GMII_SEL_PORT_MODE
] = REG_FIELD(0x1C, 0, 2), },
186 struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654
= {
188 .regfields
= phy_gmii_sel_fields_am654
,
191 static const struct of_device_id phy_gmii_sel_id_table
[] = {
193 .compatible
= "ti,am3352-phy-gmii-sel",
194 .data
= &phy_gmii_sel_soc_am33xx
,
197 .compatible
= "ti,dra7xx-phy-gmii-sel",
198 .data
= &phy_gmii_sel_soc_dra7
,
201 .compatible
= "ti,am43xx-phy-gmii-sel",
202 .data
= &phy_gmii_sel_soc_am33xx
,
205 .compatible
= "ti,dm814-phy-gmii-sel",
206 .data
= &phy_gmii_sel_soc_dm814
,
209 .compatible
= "ti,am654-phy-gmii-sel",
210 .data
= &phy_gmii_sel_soc_am654
,
214 MODULE_DEVICE_TABLE(of
, phy_gmii_sel_id_table
);
216 static const struct phy_ops phy_gmii_sel_ops
= {
217 .set_mode
= phy_gmii_sel_mode
,
218 .owner
= THIS_MODULE
,
221 static struct phy
*phy_gmii_sel_of_xlate(struct device
*dev
,
222 struct of_phandle_args
*args
)
224 struct phy_gmii_sel_priv
*priv
= dev_get_drvdata(dev
);
225 int phy_id
= args
->args
[0];
227 if (args
->args_count
< 1)
228 return ERR_PTR(-EINVAL
);
229 if (!priv
|| !priv
->if_phys
)
230 return ERR_PTR(-ENODEV
);
231 if (priv
->soc_data
->features
& BIT(PHY_GMII_SEL_RMII_IO_CLK_EN
) &&
232 args
->args_count
< 2)
233 return ERR_PTR(-EINVAL
);
234 if (phy_id
> priv
->num_ports
)
235 return ERR_PTR(-EINVAL
);
236 if (phy_id
!= priv
->if_phys
[phy_id
- 1].id
)
237 return ERR_PTR(-EINVAL
);
240 if (priv
->soc_data
->features
& BIT(PHY_GMII_SEL_RMII_IO_CLK_EN
))
241 priv
->if_phys
[phy_id
].rmii_clock_external
= args
->args
[1];
242 dev_dbg(dev
, "%s id:%u ext:%d\n", __func__
,
243 priv
->if_phys
[phy_id
].id
, args
->args
[1]);
245 return priv
->if_phys
[phy_id
].if_phy
;
248 static int phy_gmii_init_phy(struct phy_gmii_sel_priv
*priv
, int port
,
249 struct phy_gmii_sel_phy_priv
*if_phy
)
251 const struct phy_gmii_sel_soc_data
*soc_data
= priv
->soc_data
;
252 struct device
*dev
= priv
->dev
;
253 const struct reg_field
*fields
;
254 struct regmap_field
*regfield
;
255 struct reg_field field
;
261 fields
= soc_data
->regfields
[port
- 1];
263 field
.reg
+= priv
->reg_offset
;
264 dev_dbg(dev
, "%s field %x %d %d\n", __func__
,
265 field
.reg
, field
.msb
, field
.lsb
);
267 regfield
= devm_regmap_field_alloc(dev
, priv
->regmap
, field
);
268 if (IS_ERR(regfield
))
269 return PTR_ERR(regfield
);
270 if_phy
->fields
[PHY_GMII_SEL_PORT_MODE
] = regfield
;
273 field
.reg
+= priv
->reg_offset
;
274 if (soc_data
->features
& BIT(PHY_GMII_SEL_RGMII_ID_MODE
)) {
275 regfield
= devm_regmap_field_alloc(dev
,
278 if (IS_ERR(regfield
))
279 return PTR_ERR(regfield
);
280 if_phy
->fields
[PHY_GMII_SEL_RGMII_ID_MODE
] = regfield
;
281 dev_dbg(dev
, "%s field %x %d %d\n", __func__
,
282 field
.reg
, field
.msb
, field
.lsb
);
286 field
.reg
+= priv
->reg_offset
;
287 if (soc_data
->features
& BIT(PHY_GMII_SEL_RMII_IO_CLK_EN
)) {
288 regfield
= devm_regmap_field_alloc(dev
,
291 if (IS_ERR(regfield
))
292 return PTR_ERR(regfield
);
293 if_phy
->fields
[PHY_GMII_SEL_RMII_IO_CLK_EN
] = regfield
;
294 dev_dbg(dev
, "%s field %x %d %d\n", __func__
,
295 field
.reg
, field
.msb
, field
.lsb
);
298 if_phy
->if_phy
= devm_phy_create(dev
,
301 if (IS_ERR(if_phy
->if_phy
)) {
302 ret
= PTR_ERR(if_phy
->if_phy
);
303 dev_err(dev
, "Failed to create phy%d %d\n", port
, ret
);
306 phy_set_drvdata(if_phy
->if_phy
, if_phy
);
311 static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv
*priv
)
313 const struct phy_gmii_sel_soc_data
*soc_data
= priv
->soc_data
;
314 struct phy_gmii_sel_phy_priv
*if_phys
;
315 struct device
*dev
= priv
->dev
;
318 if (soc_data
->use_of_data
) {
319 const __be32
*offset
;
322 offset
= of_get_address(dev
->of_node
, 0, &size
, NULL
);
323 priv
->num_ports
= size
/ sizeof(u32
);
324 if (!priv
->num_ports
)
326 priv
->reg_offset
= __be32_to_cpu(*offset
);
329 if_phys
= devm_kcalloc(dev
, priv
->num_ports
,
330 sizeof(*if_phys
), GFP_KERNEL
);
333 dev_dbg(dev
, "%s %d\n", __func__
, priv
->num_ports
);
335 for (i
= 0; i
< priv
->num_ports
; i
++) {
336 ret
= phy_gmii_init_phy(priv
, i
+ 1, &if_phys
[i
]);
341 priv
->if_phys
= if_phys
;
345 static int phy_gmii_sel_probe(struct platform_device
*pdev
)
347 struct device
*dev
= &pdev
->dev
;
348 struct device_node
*node
= dev
->of_node
;
349 const struct of_device_id
*of_id
;
350 struct phy_gmii_sel_priv
*priv
;
353 of_id
= of_match_node(phy_gmii_sel_id_table
, pdev
->dev
.of_node
);
357 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
361 priv
->dev
= &pdev
->dev
;
362 priv
->soc_data
= of_id
->data
;
363 priv
->num_ports
= priv
->soc_data
->num_ports
;
365 priv
->regmap
= syscon_node_to_regmap(node
->parent
);
366 if (IS_ERR(priv
->regmap
)) {
367 ret
= PTR_ERR(priv
->regmap
);
368 dev_err(dev
, "Failed to get syscon %d\n", ret
);
372 ret
= phy_gmii_sel_init_ports(priv
);
376 dev_set_drvdata(&pdev
->dev
, priv
);
379 devm_of_phy_provider_register(dev
,
380 phy_gmii_sel_of_xlate
);
381 if (IS_ERR(priv
->phy_provider
)) {
382 ret
= PTR_ERR(priv
->phy_provider
);
383 dev_err(dev
, "Failed to create phy provider %d\n", ret
);
390 static struct platform_driver phy_gmii_sel_driver
= {
391 .probe
= phy_gmii_sel_probe
,
393 .name
= "phy-gmii-sel",
394 .of_match_table
= phy_gmii_sel_id_table
,
397 module_platform_driver(phy_gmii_sel_driver
);
399 MODULE_LICENSE("GPL v2");
400 MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
401 MODULE_DESCRIPTION("TI CPSW Port's PHY Interface Mode selection Driver");