blob: 54336d35d17460b2bd4be881a14d5394fc13f595 [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>
ll3e22ca62022-06-22 10:50:31 +08008#include <linux/delay.h>
9
xjb04a4022021-11-25 15:01:52 +080010
c.chenbaa06e92022-04-17 23:27:27 -070011#define PHY_ID_88Q2220 0x002b0b21
xjb04a4022021-11-25 15:01:52 +080012
13#define Q2110_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10000)
14/* 1 = PMA/PMD reset, 0 = Normal */
15#define Q2110_PMA_PMD_RST BIT(15)
16
17#define Q2110_T1_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10834)
18/* 1 = PHY as master, 0 = PHY as slave */
19#define Q2110_T1_MASTER_SLAVE_CFG BIT(14)
20/* 0 = 100BASE-T1, 1 = 1000BASE-T1 */
21#define Q2110_T1_LINK_TYPE BIT(0)
22
23#define Q2110_RST_CTRL (MII_ADDR_C45 | 0x038000)
24/* software reset of T-unit */
25#define Q2110_RGMII_SW_RESET BIT(15)
26
27#define Q2110_PCS_CTRL (MII_ADDR_C45 | 0x030900)
28#define Q2110_LOOPBACK BIT(14)
29
30#define Q2110_T1_AN_STATUS (MII_ADDR_C45 | 0x070201)
31/* 1 = Link Up, 0 = Link Down */
32#define Q2110_T1_LINK_STATUS BIT(2)
33
c.chenbaa06e92022-04-17 23:27:27 -070034#define Q2110_COM_PORT_CTRL (MII_ADDR_C45 | 0x4A001)
xjb04a4022021-11-25 15:01:52 +080035/* Add delay on TX_CLK */
36#define Q2110_RGMII_TX_TIMING_CTRL BIT(15)
37/* Add delay on RX_CLK */
38#define Q2110_RGMII_RX_TIMING_CTRL BIT(14)
39
40/* Set and/or override some configuration registers based on the
41 * marvell,88q2110 property stored in the of_node for the phydev.
42 * marvell,88q2110 = <speed master>,...;
43 * speed: 1000Mbps or 100Mbps.
44 * master: 1-master, 0-slave.
45 */
46static int q2110_dts_init(struct phy_device *phydev)
47{
48 const __be32 *paddr;
49 u32 speed;
50 u32 master;
51 int val, len;
52
53 if (!phydev->mdio.dev.of_node)
54 return 0;
55
56 paddr = of_get_property(phydev->mdio.dev.of_node,
c.chenbaa06e92022-04-17 23:27:27 -070057 "marvell,88q2220", &len);
xjb04a4022021-11-25 15:01:52 +080058 if (!paddr)
59 return 0;
60
61 speed = be32_to_cpup(paddr);
62 master = be32_to_cpup(paddr + 1);
xjb04a4022021-11-25 15:01:52 +080063 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
64 if (val < 0)
65 return val;
66 val &= ~(Q2110_T1_MASTER_SLAVE_CFG | Q2110_T1_LINK_TYPE);
67 if (speed == SPEED_1000)
68 val |= Q2110_T1_LINK_TYPE;
69 if (master)
70 val |= Q2110_T1_MASTER_SLAVE_CFG;
71 val = phy_write(phydev, Q2110_T1_PMA_PMD_CTRL, val);
72 if (val < 0)
73 return val;
c.chenbaa06e92022-04-17 23:27:27 -070074
ll3e22ca62022-06-22 10:50:31 +080075 phy_write(phydev, (MII_ADDR_C45 | 0x030900), 0x8000);
76 phy_write(phydev, (MII_ADDR_C45 | 0x03FFE4), 0x000C);
c.chenbaa06e92022-04-17 23:27:27 -070077
78 printk("q2110 dts init ok!!\n");
xjb04a4022021-11-25 15:01:52 +080079
80 return 0;
81}
82
83static int q2110_timing_init(struct phy_device *phydev)
84{
85 int val;
86
87 if (phy_interface_is_rgmii(phydev)) {
88 val = phy_read(phydev, Q2110_COM_PORT_CTRL);
89 if (val < 0)
90 return val;
91
92 val &= ~(Q2110_RGMII_TX_TIMING_CTRL |
93 Q2110_RGMII_RX_TIMING_CTRL);
94
95 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
96 val |= (Q2110_RGMII_TX_TIMING_CTRL |
97 Q2110_RGMII_RX_TIMING_CTRL);
98 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
99 val |= Q2110_RGMII_RX_TIMING_CTRL;
100 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
101 val |= Q2110_RGMII_TX_TIMING_CTRL;
102
103 val = phy_write(phydev, Q2110_COM_PORT_CTRL, val);
104 if (val < 0)
105 return val;
106 }
107
c.chenbaa06e92022-04-17 23:27:27 -0700108
xjb04a4022021-11-25 15:01:52 +0800109 val = phy_read(phydev, Q2110_RST_CTRL);
110 if (val < 0)
111 return val;
112 val |= Q2110_RGMII_SW_RESET;
113 val = phy_write(phydev, Q2110_RST_CTRL, val);
114 if (val < 0)
115 return val;
116
xjb04a4022021-11-25 15:01:52 +0800117 val = phy_read(phydev, Q2110_RST_CTRL);
118 if (val < 0)
119 return val;
120 val &= ~Q2110_RGMII_SW_RESET;
121 val = phy_write(phydev, Q2110_RST_CTRL, val);
122 if (val < 0)
123 return val;
124
125 return 0;
126}
127
128static int q2110_config_init(struct phy_device *phydev)
129{
ll3e22ca62022-06-22 10:50:31 +0800130 int i=0,mrvlId;
xjb04a4022021-11-25 15:01:52 +0800131 phydev->supported =
132 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
133 phydev->advertising =
134 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
135 phydev->state = PHY_NOLINK;
136 phydev->autoneg = AUTONEG_DISABLE;
c.chenfaec22c2022-07-27 19:38:31 -0700137 phy_write(phydev, MII_ADDR_C45 | 0x038033, 0x6801);
ll3e22ca62022-06-22 10:50:31 +0800138 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x840);
139 phy_write(phydev, MII_ADDR_C45 | 0x03FE1B, 0x48);
140 phy_write(phydev, MII_ADDR_C45 | 0x01FFE4, 0x6B6);
141 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x0);
142 phy_write(phydev, MII_ADDR_C45 | 0x030000, 0x0);
143 while (i < 5) {
144 mrvlId= phy_read(phydev, MII_ADDR_C45 | 0x03FFE4);
145 if (mrvlId == 0x06BA) {
146 break;
147 }
148 i++;
149 msleep(1);
150 }
151 phy_write(phydev, MII_ADDR_C45 | 0x078032, 0x2020);
152 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xA28);
153 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xC28);
c.chenfaec22c2022-07-27 19:38:31 -0700154 phy_write(phydev, MII_ADDR_C45 | 0x03803A, 0xDA44);
155 phy_write(phydev, MII_ADDR_C45 | 0x038039, 0x2C0B);
c.chendf3ef622022-07-13 00:57:58 -0700156
157 phy_write(phydev, MII_ADDR_C45 | 0x038027, 0x1);//TC10 sleep/wakeup capability support
xjb04a4022021-11-25 15:01:52 +0800158 q2110_timing_init(phydev);
159
ll3e22ca62022-06-22 10:50:31 +0800160 q2110_dts_init(phydev);
161
xjb04a4022021-11-25 15:01:52 +0800162 return 0;
163}
164
165static int q2110_loopback(struct phy_device *phydev, bool enable)
166{
167 int val;
168
169 val = phy_read(phydev, Q2110_PCS_CTRL);
170 if (val < 0)
171 return val;
172
173 val &= ~Q2110_LOOPBACK;
174 if (enable)
175 val |= Q2110_LOOPBACK;
176
177 val = phy_write(phydev, Q2110_PCS_CTRL, val);
178 if (val < 0)
179 return val;
180
181 return 0;
182}
183
c.chenbaa06e92022-04-17 23:27:27 -0700184
xjb04a4022021-11-25 15:01:52 +0800185static int q2110_read_status(struct phy_device *phydev)
186{
187 int val;
188
189 phydev->duplex = 1;
190 phydev->pause = 0;
191
192 val = phy_read(phydev, Q2110_T1_AN_STATUS);
193 if (val < 0)
194 return val;
195
xjb04a4022021-11-25 15:01:52 +0800196 if (val & Q2110_T1_LINK_STATUS)
197 phydev->link = 1;
198 else
199 phydev->link = 0;
200
201 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
202 if (val < 0)
203 return val;
204
205 if (val & Q2110_T1_LINK_TYPE)
206 phydev->speed = SPEED_1000;
207 else
208 phydev->speed = SPEED_100;
209
210 return 0;
211}
212
c.chenae2fc652022-07-11 03:54:53 -0700213int q2220_suspend(struct phy_device *phydev)
214{
215 return phy_write(phydev, MII_ADDR_C45 | 0x038022, 0x1);
216}
217int q2220_resume(struct phy_device *phydev)
218{
219 return phy_write(phydev, MII_ADDR_C45 | 0x038022, 0x10);
220}
221
222
223
xjb04a4022021-11-25 15:01:52 +0800224static int q2110_match_phy_device(struct phy_device *phydev)
225{
c.chenbaa06e92022-04-17 23:27:27 -0700226 return (phydev->c45_ids.device_ids[1] & 0xffffffff) == PHY_ID_88Q2220;
xjb04a4022021-11-25 15:01:52 +0800227}
228
229static struct phy_driver marvell_88q_driver[] = {
230 {
c.chenbaa06e92022-04-17 23:27:27 -0700231 .phy_id = PHY_ID_88Q2220,
xjb04a4022021-11-25 15:01:52 +0800232 .phy_id_mask = 0xfffffff0,
c.chenbaa06e92022-04-17 23:27:27 -0700233 .name = "Marvell 88Q2220",
xjb04a4022021-11-25 15:01:52 +0800234 .config_init = q2110_config_init,
235 .match_phy_device = q2110_match_phy_device,
236 .set_loopback = &q2110_loopback,
237 .read_status = q2110_read_status,
c.chenae2fc652022-07-11 03:54:53 -0700238 .suspend = q2220_suspend,
239 .resume = q2220_resume,
xjb04a4022021-11-25 15:01:52 +0800240 }
241};
242
243module_phy_driver(marvell_88q_driver);