blob: 297a56b5f8d75efff202362ef641c67be6866abe [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 */
5#include <linux/module.h>
6#include <linux/phy.h>
7#include <linux/of.h>
8
9#define PHY_ID_88Q2110 0x002b0980
10
11#define Q2110_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10000)
12/* 1 = PMA/PMD reset, 0 = Normal */
13#define Q2110_PMA_PMD_RST BIT(15)
14
15#define Q2110_T1_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10834)
16/* 1 = PHY as master, 0 = PHY as slave */
17#define Q2110_T1_MASTER_SLAVE_CFG BIT(14)
18/* 0 = 100BASE-T1, 1 = 1000BASE-T1 */
19#define Q2110_T1_LINK_TYPE BIT(0)
20
21#define Q2110_RST_CTRL (MII_ADDR_C45 | 0x038000)
22/* software reset of T-unit */
23#define Q2110_RGMII_SW_RESET BIT(15)
24
25#define Q2110_PCS_CTRL (MII_ADDR_C45 | 0x030900)
26#define Q2110_LOOPBACK BIT(14)
27
28#define Q2110_T1_AN_STATUS (MII_ADDR_C45 | 0x070201)
29/* 1 = Link Up, 0 = Link Down */
30#define Q2110_T1_LINK_STATUS BIT(2)
31
32#define Q2110_COM_PORT_CTRL (MII_ADDR_C45 | 0x1f8001)
33/* Add delay on TX_CLK */
34#define Q2110_RGMII_TX_TIMING_CTRL BIT(15)
35/* Add delay on RX_CLK */
36#define Q2110_RGMII_RX_TIMING_CTRL BIT(14)
37
38/* Set and/or override some configuration registers based on the
39 * marvell,88q2110 property stored in the of_node for the phydev.
40 * marvell,88q2110 = <speed master>,...;
41 * speed: 1000Mbps or 100Mbps.
42 * master: 1-master, 0-slave.
43 */
44static int q2110_dts_init(struct phy_device *phydev)
45{
46 const __be32 *paddr;
47 u32 speed;
48 u32 master;
49 int val, len;
50
51 if (!phydev->mdio.dev.of_node)
52 return 0;
53
54 paddr = of_get_property(phydev->mdio.dev.of_node,
55 "marvell,88q2110", &len);
56 if (!paddr)
57 return 0;
58
59 speed = be32_to_cpup(paddr);
60 master = be32_to_cpup(paddr + 1);
61
62 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
63 if (val < 0)
64 return val;
65 val &= ~(Q2110_T1_MASTER_SLAVE_CFG | Q2110_T1_LINK_TYPE);
66 if (speed == SPEED_1000)
67 val |= Q2110_T1_LINK_TYPE;
68 if (master)
69 val |= Q2110_T1_MASTER_SLAVE_CFG;
70 val = phy_write(phydev, Q2110_T1_PMA_PMD_CTRL, val);
71 if (val < 0)
72 return val;
73
74 /* Software Reset PHY */
75 val = phy_read(phydev, Q2110_PMA_PMD_CTRL);
76 if (val < 0)
77 return val;
78 val |= Q2110_PMA_PMD_RST;
79 val = phy_write(phydev, Q2110_PMA_PMD_CTRL, val);
80 if (val < 0)
81 return val;
82
83 do {
84 val = phy_read(phydev, Q2110_PMA_PMD_CTRL);
85 if (val < 0)
86 return val;
87 } while (val & Q2110_PMA_PMD_RST);
88
89 return 0;
90}
91
92static int q2110_timing_init(struct phy_device *phydev)
93{
94 int val;
95
96 if (phy_interface_is_rgmii(phydev)) {
97 val = phy_read(phydev, Q2110_COM_PORT_CTRL);
98 if (val < 0)
99 return val;
100
101 val &= ~(Q2110_RGMII_TX_TIMING_CTRL |
102 Q2110_RGMII_RX_TIMING_CTRL);
103
104 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
105 val |= (Q2110_RGMII_TX_TIMING_CTRL |
106 Q2110_RGMII_RX_TIMING_CTRL);
107 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
108 val |= Q2110_RGMII_RX_TIMING_CTRL;
109 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
110 val |= Q2110_RGMII_TX_TIMING_CTRL;
111
112 val = phy_write(phydev, Q2110_COM_PORT_CTRL, val);
113 if (val < 0)
114 return val;
115 }
116
117 /* Software Reset of T-unit */
118 val = phy_read(phydev, Q2110_RST_CTRL);
119 if (val < 0)
120 return val;
121 val |= Q2110_RGMII_SW_RESET;
122 val = phy_write(phydev, Q2110_RST_CTRL, val);
123 if (val < 0)
124 return val;
125
126 /* not self-clearing */
127 val = phy_read(phydev, Q2110_RST_CTRL);
128 if (val < 0)
129 return val;
130 val &= ~Q2110_RGMII_SW_RESET;
131 val = phy_write(phydev, Q2110_RST_CTRL, val);
132 if (val < 0)
133 return val;
134
135 return 0;
136}
137
138static int q2110_config_init(struct phy_device *phydev)
139{
140 phydev->supported =
141 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
142 phydev->advertising =
143 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
144 phydev->state = PHY_NOLINK;
145 phydev->autoneg = AUTONEG_DISABLE;
146
147 q2110_dts_init(phydev);
148 q2110_timing_init(phydev);
149
150 return 0;
151}
152
153static int q2110_loopback(struct phy_device *phydev, bool enable)
154{
155 int val;
156
157 val = phy_read(phydev, Q2110_PCS_CTRL);
158 if (val < 0)
159 return val;
160
161 val &= ~Q2110_LOOPBACK;
162 if (enable)
163 val |= Q2110_LOOPBACK;
164
165 val = phy_write(phydev, Q2110_PCS_CTRL, val);
166 if (val < 0)
167 return val;
168
169 return 0;
170}
171
xjb04a4022021-11-25 15:01:52 +0800172static int q2110_read_status(struct phy_device *phydev)
173{
174 int val;
175
176 phydev->duplex = 1;
177 phydev->pause = 0;
178
rjw2e8229f2022-02-15 21:08:12 +0800179 if (!phy_polling_mode(phydev) || !phydev->link) {
180 val = phy_read(phydev, Q2110_T1_AN_STATUS);
181 if (val < 0)
182 return val;
183 else if (val & Q2110_T1_LINK_STATUS)
184 goto done;
185 }
186
xjb04a4022021-11-25 15:01:52 +0800187 val = phy_read(phydev, Q2110_T1_AN_STATUS);
188 if (val < 0)
189 return val;
190
rjw2e8229f2022-02-15 21:08:12 +0800191done:
xjb04a4022021-11-25 15:01:52 +0800192 if (val & Q2110_T1_LINK_STATUS)
193 phydev->link = 1;
194 else
195 phydev->link = 0;
196
197 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
198 if (val < 0)
199 return val;
200
201 if (val & Q2110_T1_LINK_TYPE)
202 phydev->speed = SPEED_1000;
203 else
204 phydev->speed = SPEED_100;
205
206 return 0;
207}
208
209static int q2110_match_phy_device(struct phy_device *phydev)
210{
211 return (phydev->c45_ids.device_ids[1] & 0xfffffff0) == PHY_ID_88Q2110;
212}
213
214static struct phy_driver marvell_88q_driver[] = {
215 {
216 .phy_id = PHY_ID_88Q2110,
217 .phy_id_mask = 0xfffffff0,
218 .name = "Marvell 88Q2110",
219 .config_init = q2110_config_init,
220 .match_phy_device = q2110_match_phy_device,
221 .set_loopback = &q2110_loopback,
222 .read_status = q2110_read_status,
223 }
224};
225
226module_phy_driver(marvell_88q_driver);