blob: cff841ab3dd53d4f21769fa4c83c8387ca832d9b [file] [log] [blame]
Nate Drudea9521ea2022-04-08 11:28:14 -05001// SPDX-License-Identifier: GPL-2.0+
2/**
3 * Driver for Analog Devices Industrial Ethernet PHYs
4 *
5 * Copyright 2019 Analog Devices Inc.
6 * Copyright 2022 Variscite Ltd.
7 */
8#include <common.h>
9#include <phy.h>
10#include <linux/bitops.h>
11#include <linux/bitfield.h>
12
13#define PHY_ID_ADIN1300 0x0283bc30
14#define ADIN1300_EXT_REG_PTR 0x10
15#define ADIN1300_EXT_REG_DATA 0x11
16#define ADIN1300_GE_RGMII_CFG 0xff23
17#define ADIN1300_GE_RGMII_RX_MSK GENMASK(8, 6)
18#define ADIN1300_GE_RGMII_RX_SEL(x) \
19 FIELD_PREP(ADIN1300_GE_RGMII_RX_MSK, x)
20#define ADIN1300_GE_RGMII_GTX_MSK GENMASK(5, 3)
21#define ADIN1300_GE_RGMII_GTX_SEL(x) \
22 FIELD_PREP(ADIN1300_GE_RGMII_GTX_MSK, x)
23#define ADIN1300_GE_RGMII_RXID_EN BIT(2)
24#define ADIN1300_GE_RGMII_TXID_EN BIT(1)
25#define ADIN1300_GE_RGMII_EN BIT(0)
26
27/* RGMII internal delay settings for rx and tx for ADIN1300 */
28#define ADIN1300_RGMII_1_60_NS 0x0001
29#define ADIN1300_RGMII_1_80_NS 0x0002
30#define ADIN1300_RGMII_2_00_NS 0x0000
31#define ADIN1300_RGMII_2_20_NS 0x0006
32#define ADIN1300_RGMII_2_40_NS 0x0007
33
34/**
35 * struct adin_cfg_reg_map - map a config value to aregister value
36 * @cfg value in device configuration
37 * @reg value in the register
38 */
39struct adin_cfg_reg_map {
40 int cfg;
41 int reg;
42};
43
44static const struct adin_cfg_reg_map adin_rgmii_delays[] = {
45 { 1600, ADIN1300_RGMII_1_60_NS },
46 { 1800, ADIN1300_RGMII_1_80_NS },
47 { 2000, ADIN1300_RGMII_2_00_NS },
48 { 2200, ADIN1300_RGMII_2_20_NS },
49 { 2400, ADIN1300_RGMII_2_40_NS },
50 { },
51};
52
53static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
54{
55 size_t i;
56
57 for (i = 0; tbl[i].cfg; i++) {
58 if (tbl[i].cfg == cfg)
59 return tbl[i].reg;
60 }
61
62 return -EINVAL;
63}
64
65static u32 adin_get_reg_value(struct phy_device *phydev,
66 const char *prop_name,
67 const struct adin_cfg_reg_map *tbl,
68 u32 dflt)
69{
70 u32 val;
71 int rc;
72
73 ofnode node = phy_get_ofnode(phydev);
74 if (!ofnode_valid(node)) {
75 printf("%s: failed to get node\n", __func__);
76 return -EINVAL;
77 }
78
79 if (ofnode_read_u32(node, prop_name, &val)) {
80 printf("%s: failed to find %s, using default %d\n",
81 __func__, prop_name, dflt);
82 return dflt;
83 }
84
85 debug("%s: %s = '%d'\n", __func__, prop_name, val);
86
87 rc = adin_lookup_reg_value(tbl, val);
88 if (rc < 0) {
89 printf("%s: Unsupported value %u for %s using default (%u)\n",
90 __func__, val, prop_name, dflt);
91 return dflt;
92 }
93
94 return rc;
95}
96
97/**
98 * adin_get_phy_mode_override - Get phy-mode override for adin PHY
99 *
100 * The function gets phy-mode string from property 'adi,phy-mode-override'
101 * and return its index in phy_interface_strings table, or -1 in error case.
102 */
103int adin_get_phy_mode_override(struct phy_device *phydev)
104{
105 ofnode node = phy_get_ofnode(phydev);
106 const char *phy_mode_override;
107 const char *prop_phy_mode_override = "adi,phy-mode-override";
108 int override_interface;
109
110 phy_mode_override = ofnode_read_string(node, prop_phy_mode_override);
111 if (!phy_mode_override)
112 return -ENODEV;
113
114 debug("%s: %s = '%s'\n",
115 __func__, prop_phy_mode_override, phy_mode_override);
116
117 override_interface = phy_get_interface_by_name(phy_mode_override);
118
119 if (override_interface < 0)
120 printf("%s: %s = '%s' is not valid\n",
121 __func__, prop_phy_mode_override, phy_mode_override);
122
123 return override_interface;
124}
125
126static u16 adin_ext_read(struct phy_device *phydev, const u32 regnum)
127{
128 u16 val;
129
130 phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);
131 val = phy_read(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA);
132
133 debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);
134
135 return val;
136}
137
138static int adin_ext_write(struct phy_device *phydev, const u32 regnum, const u16 val)
139{
140 debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);
141
142 phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);
143
144 return phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA, val);
145}
146
147static int adin_config_rgmii_mode(struct phy_device *phydev)
148{
149 u16 reg_val;
150 u32 val;
151 int phy_mode_override = adin_get_phy_mode_override(phydev);
152
153 if (phy_mode_override >= 0) {
154 phydev->interface = (phy_interface_t) phy_mode_override;
155 }
156
157 reg_val = adin_ext_read(phydev, ADIN1300_GE_RGMII_CFG);
158
159 if (!phy_interface_is_rgmii(phydev)) {
160 /* Disable RGMII */
161 reg_val &= ~ADIN1300_GE_RGMII_EN;
162 return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
163 }
164
165 /* Enable RGMII */
166 reg_val |= ADIN1300_GE_RGMII_EN;
167
168 /* Enable / Disable RGMII RX Delay */
169 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
170 phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
171 reg_val |= ADIN1300_GE_RGMII_RXID_EN;
172
173 val = adin_get_reg_value(phydev, "adi,rx-internal-delay-ps",
174 adin_rgmii_delays,
175 ADIN1300_RGMII_2_00_NS);
176 reg_val &= ~ADIN1300_GE_RGMII_RX_MSK;
177 reg_val |= ADIN1300_GE_RGMII_RX_SEL(val);
178 } else {
179 reg_val &= ~ADIN1300_GE_RGMII_RXID_EN;
180 }
181
182 /* Enable / Disable RGMII RX Delay */
183 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
184 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
185 reg_val |= ADIN1300_GE_RGMII_TXID_EN;
186
187 val = adin_get_reg_value(phydev, "adi,tx-internal-delay-ps",
188 adin_rgmii_delays,
189 ADIN1300_RGMII_2_00_NS);
190 reg_val &= ~ADIN1300_GE_RGMII_GTX_MSK;
191 reg_val |= ADIN1300_GE_RGMII_GTX_SEL(val);
192 } else {
193 reg_val &= ~ADIN1300_GE_RGMII_TXID_EN;
194 }
195
196 return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
197}
198
199static int adin1300_config(struct phy_device *phydev)
200{
201 int ret;
202
203 printf("ADIN1300 PHY detected at addr %d\n", phydev->addr);
204
205 ret = adin_config_rgmii_mode(phydev);
206
207 if (ret < 0)
208 return ret;
209
210 return genphy_config(phydev);
211}
212
213static struct phy_driver ADIN1300_driver = {
214 .name = "ADIN1300",
215 .uid = PHY_ID_ADIN1300,
216 .mask = 0xffffffff,
217 .features = PHY_GBIT_FEATURES,
218 .config = adin1300_config,
219 .startup = genphy_startup,
220 .shutdown = genphy_shutdown,
221};
222
223int phy_adin_init(void)
224{
225 phy_register(&ADIN1300_driver);
226
227 return 0;
228}