blob: 2349cab130ff0ddbcdffcccbe824488860a511e7 [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>
xf.li2230aac2022-09-09 00:23:23 -07008//xf.li 2022/9/9 modify for LPSD mode start
9#include <linux/of_gpio.h>
10#include <linux/gpio/consumer.h>
11//xf.li 2022/9/9 modify for LPSD mode end
ll3e22ca62022-06-22 10:50:31 +080012#include <linux/delay.h>
13
xjb04a4022021-11-25 15:01:52 +080014
c.chenbaa06e92022-04-17 23:27:27 -070015#define PHY_ID_88Q2220 0x002b0b21
xjb04a4022021-11-25 15:01:52 +080016
17#define Q2110_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10000)
18/* 1 = PMA/PMD reset, 0 = Normal */
19#define Q2110_PMA_PMD_RST BIT(15)
20
21#define Q2110_T1_PMA_PMD_CTRL (MII_ADDR_C45 | 0x10834)
22/* 1 = PHY as master, 0 = PHY as slave */
23#define Q2110_T1_MASTER_SLAVE_CFG BIT(14)
24/* 0 = 100BASE-T1, 1 = 1000BASE-T1 */
25#define Q2110_T1_LINK_TYPE BIT(0)
26
27#define Q2110_RST_CTRL (MII_ADDR_C45 | 0x038000)
28/* software reset of T-unit */
29#define Q2110_RGMII_SW_RESET BIT(15)
30
31#define Q2110_PCS_CTRL (MII_ADDR_C45 | 0x030900)
32#define Q2110_LOOPBACK BIT(14)
33
34#define Q2110_T1_AN_STATUS (MII_ADDR_C45 | 0x070201)
35/* 1 = Link Up, 0 = Link Down */
36#define Q2110_T1_LINK_STATUS BIT(2)
37
c.chenbaa06e92022-04-17 23:27:27 -070038#define Q2110_COM_PORT_CTRL (MII_ADDR_C45 | 0x4A001)
xjb04a4022021-11-25 15:01:52 +080039/* Add delay on TX_CLK */
40#define Q2110_RGMII_TX_TIMING_CTRL BIT(15)
41/* Add delay on RX_CLK */
42#define Q2110_RGMII_RX_TIMING_CTRL BIT(14)
43
xf.li9d095322023-02-07 18:04:49 -080044/* NAD_WAKEUP_PHY1 : GPIO7 */
45#define NAD_WAKEUP_PHY1 275
46/* NAD_RESET_PHY1 : GPIO26 */
47#define NAD_RESET_PHY1 294
xf.li742b5c02023-04-01 02:05:52 -070048/* PHY_POWER_SUPPLY : GPIO178 */
49#define PHY_POWER_SUPPLY 446
xf.li9d095322023-02-07 18:04:49 -080050
51#define Q2220_LPSD_CTRL_1 (MII_ADDR_C45 | 0x038021)
52#define Q2220_LPSD_DISABLE_REMOTE_WAKE_UP BIT(15) | BIT(11)
53#define Q2220_LPSD_STATUS (MII_ADDR_C45 | 0x038020)
54#define Q2220_LPSD_POWER_DOWN BIT(0)
55
xf.liec75fdd2022-11-09 18:24:10 -080056int if_suspend = 0;
57
xjb04a4022021-11-25 15:01:52 +080058/* Set and/or override some configuration registers based on the
59 * marvell,88q2110 property stored in the of_node for the phydev.
60 * marvell,88q2110 = <speed master>,...;
61 * speed: 1000Mbps or 100Mbps.
62 * master: 1-master, 0-slave.
63 */
64static int q2110_dts_init(struct phy_device *phydev)
65{
66 const __be32 *paddr;
67 u32 speed;
68 u32 master;
69 int val, len;
70
71 if (!phydev->mdio.dev.of_node)
72 return 0;
73
74 paddr = of_get_property(phydev->mdio.dev.of_node,
c.chenbaa06e92022-04-17 23:27:27 -070075 "marvell,88q2220", &len);
xjb04a4022021-11-25 15:01:52 +080076 if (!paddr)
77 return 0;
78
79 speed = be32_to_cpup(paddr);
80 master = be32_to_cpup(paddr + 1);
xjb04a4022021-11-25 15:01:52 +080081 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
82 if (val < 0)
83 return val;
84 val &= ~(Q2110_T1_MASTER_SLAVE_CFG | Q2110_T1_LINK_TYPE);
85 if (speed == SPEED_1000)
86 val |= Q2110_T1_LINK_TYPE;
87 if (master)
88 val |= Q2110_T1_MASTER_SLAVE_CFG;
89 val = phy_write(phydev, Q2110_T1_PMA_PMD_CTRL, val);
90 if (val < 0)
91 return val;
c.chenbaa06e92022-04-17 23:27:27 -070092
ll3e22ca62022-06-22 10:50:31 +080093 phy_write(phydev, (MII_ADDR_C45 | 0x030900), 0x8000);
94 phy_write(phydev, (MII_ADDR_C45 | 0x03FFE4), 0x000C);
c.chenbaa06e92022-04-17 23:27:27 -070095
96 printk("q2110 dts init ok!!\n");
xjb04a4022021-11-25 15:01:52 +080097
98 return 0;
99}
100
101static int q2110_timing_init(struct phy_device *phydev)
102{
103 int val;
104
105 if (phy_interface_is_rgmii(phydev)) {
106 val = phy_read(phydev, Q2110_COM_PORT_CTRL);
107 if (val < 0)
108 return val;
109
110 val &= ~(Q2110_RGMII_TX_TIMING_CTRL |
111 Q2110_RGMII_RX_TIMING_CTRL);
112
113 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
114 val |= (Q2110_RGMII_TX_TIMING_CTRL |
115 Q2110_RGMII_RX_TIMING_CTRL);
116 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
117 val |= Q2110_RGMII_RX_TIMING_CTRL;
118 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
119 val |= Q2110_RGMII_TX_TIMING_CTRL;
120
121 val = phy_write(phydev, Q2110_COM_PORT_CTRL, val);
122 if (val < 0)
123 return val;
124 }
125
c.chenbaa06e92022-04-17 23:27:27 -0700126
xjb04a4022021-11-25 15:01:52 +0800127 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
xjb04a4022021-11-25 15:01:52 +0800135 val = phy_read(phydev, Q2110_RST_CTRL);
136 if (val < 0)
137 return val;
138 val &= ~Q2110_RGMII_SW_RESET;
139 val = phy_write(phydev, Q2110_RST_CTRL, val);
140 if (val < 0)
141 return val;
142
143 return 0;
144}
145
146static int q2110_config_init(struct phy_device *phydev)
147{
ll3e22ca62022-06-22 10:50:31 +0800148 int i=0,mrvlId;
xjb04a4022021-11-25 15:01:52 +0800149 phydev->supported =
150 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
151 phydev->advertising =
152 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
153 phydev->state = PHY_NOLINK;
154 phydev->autoneg = AUTONEG_DISABLE;
c.chenfaec22c2022-07-27 19:38:31 -0700155 phy_write(phydev, MII_ADDR_C45 | 0x038033, 0x6801);
xf.li57bccfc2023-07-18 23:51:06 -0700156 phy_write(phydev, MII_ADDR_C45 | 0x070200, 0x0000U);
ll3e22ca62022-06-22 10:50:31 +0800157 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x840);
158 phy_write(phydev, MII_ADDR_C45 | 0x03FE1B, 0x48);
159 phy_write(phydev, MII_ADDR_C45 | 0x01FFE4, 0x6B6);
160 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x0);
161 phy_write(phydev, MII_ADDR_C45 | 0x030000, 0x0);
162 while (i < 5) {
163 mrvlId= phy_read(phydev, MII_ADDR_C45 | 0x03FFE4);
164 if (mrvlId == 0x06BA) {
165 break;
166 }
167 i++;
168 msleep(1);
169 }
170 phy_write(phydev, MII_ADDR_C45 | 0x078032, 0x2020);
171 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xA28);
172 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xC28);
xf.li57bccfc2023-07-18 23:51:06 -0700173 phy_write(phydev, MII_ADDR_C45 | 0x03FFDB, 0xFC10);
174 phy_write(phydev, MII_ADDR_C45 | 0x03FE1B, 0x58);
175
c.chenfaec22c2022-07-27 19:38:31 -0700176 phy_write(phydev, MII_ADDR_C45 | 0x03803A, 0xDA44);
177 phy_write(phydev, MII_ADDR_C45 | 0x038039, 0x2C0B);
c.chendf3ef622022-07-13 00:57:58 -0700178
179 phy_write(phydev, MII_ADDR_C45 | 0x038027, 0x1);//TC10 sleep/wakeup capability support
xjb04a4022021-11-25 15:01:52 +0800180 q2110_timing_init(phydev);
181
ll3e22ca62022-06-22 10:50:31 +0800182 q2110_dts_init(phydev);
xf.li57bccfc2023-07-18 23:51:06 -0700183 printk("phy init ok\n");
xjb04a4022021-11-25 15:01:52 +0800184 return 0;
185}
186
187static int q2110_loopback(struct phy_device *phydev, bool enable)
188{
189 int val;
190
191 val = phy_read(phydev, Q2110_PCS_CTRL);
192 if (val < 0)
193 return val;
194
195 val &= ~Q2110_LOOPBACK;
196 if (enable)
197 val |= Q2110_LOOPBACK;
198
199 val = phy_write(phydev, Q2110_PCS_CTRL, val);
200 if (val < 0)
201 return val;
202
203 return 0;
204}
205
c.chenbaa06e92022-04-17 23:27:27 -0700206
xjb04a4022021-11-25 15:01:52 +0800207static int q2110_read_status(struct phy_device *phydev)
208{
209 int val;
210
211 phydev->duplex = 1;
212 phydev->pause = 0;
213
214 val = phy_read(phydev, Q2110_T1_AN_STATUS);
215 if (val < 0)
216 return val;
217
xjb04a4022021-11-25 15:01:52 +0800218 if (val & Q2110_T1_LINK_STATUS)
219 phydev->link = 1;
220 else
221 phydev->link = 0;
222
223 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
224 if (val < 0)
225 return val;
226
227 if (val & Q2110_T1_LINK_TYPE)
228 phydev->speed = SPEED_1000;
229 else
230 phydev->speed = SPEED_100;
231
232 return 0;
233}
234
c.chenae2fc652022-07-11 03:54:53 -0700235int q2220_suspend(struct phy_device *phydev)
236{
xf.liec75fdd2022-11-09 18:24:10 -0800237 //xf.li 2022/11/9 modify for API-647
xf.li2230aac2022-09-09 00:23:23 -0700238 printk("phy sleep start\n");
xf.li55334fa2023-04-01 03:28:51 -0700239 gpio_direction_output(205 + 268, 0);
xf.li9d095322023-02-07 18:04:49 -0800240 gpio_direction_output(NAD_WAKEUP_PHY1, 0);
241 mdelay(1);//ensure do not wake up
242 //phy_write(phydev, MII_ADDR_C45 | 0x038022, 0x1);
243 phy_write(phydev, Q2220_LPSD_CTRL_1, Q2220_LPSD_DISABLE_REMOTE_WAKE_UP);
244 mdelay(10);//for enter T1 port sleep, need link parter respond
245 printk("reg 038021 = %x\n", phy_read(phydev, Q2220_LPSD_CTRL_1));
246 phy_write(phydev, Q2220_LPSD_STATUS, Q2220_LPSD_POWER_DOWN);// enter LPSD sleep mode
247 printk("reg 038020 = %x\n", phy_read(phydev, Q2220_LPSD_STATUS));
248 mdelay(50);
249 printk("reg 038020 = %x\n", phy_read(phydev, Q2220_LPSD_STATUS));
250 gpio_direction_output(NAD_RESET_PHY1, 0);
xf.li742b5c02023-04-01 02:05:52 -0700251 mdelay(1);
252 gpio_direction_output(PHY_POWER_SUPPLY, 0);
xf.liec75fdd2022-11-09 18:24:10 -0800253 if_suspend = 1;
xf.li2230aac2022-09-09 00:23:23 -0700254 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800255 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700256}
257int q2220_resume(struct phy_device *phydev)
258{
xf.liec75fdd2022-11-09 18:24:10 -0800259 //xf.li 2022/11/9 modify for API-647
260 if(if_suspend == 1)
261 {
262 printk("phy awake start\n");
xf.li0fdb2c82023-07-19 04:51:07 -0700263 gpio_direction_output(205 + 268, 1);
xf.li742b5c02023-04-01 02:05:52 -0700264 gpio_direction_output(PHY_POWER_SUPPLY, 1);
265 mdelay(1);
xf.li9d095322023-02-07 18:04:49 -0800266 gpio_direction_output(NAD_WAKEUP_PHY1, 1);
xf.li0fdb2c82023-07-19 04:51:07 -0700267 mdelay(10);
xf.li9d095322023-02-07 18:04:49 -0800268 gpio_direction_output(NAD_WAKEUP_PHY1, 0);
xf.li0fdb2c82023-07-19 04:51:07 -0700269 mdelay(5);
270 gpio_direction_output(NAD_RESET_PHY1, 1);
271 mdelay(1000);
272 gpio_direction_output(NAD_RESET_PHY1, 0);
273 mdelay(30);
xf.li9d095322023-02-07 18:04:49 -0800274 gpio_direction_output(NAD_RESET_PHY1, 1);
xf.li55334fa2023-04-01 03:28:51 -0700275 gpio_direction_output(205 + 268, 0);
xf.li9d095322023-02-07 18:04:49 -0800276 mdelay(10);//at lest 4ms for reset phy
277 q2110_config_init(phydev);
xf.li0fdb2c82023-07-19 04:51:07 -0700278 if_suspend = 0;
xf.liec75fdd2022-11-09 18:24:10 -0800279 }
280 else
281 {
282 printk("q2220_resume: no suspend! In boot state.");
283 }
xf.li2230aac2022-09-09 00:23:23 -0700284 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800285 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700286}
287
288
289
xjb04a4022021-11-25 15:01:52 +0800290static int q2110_match_phy_device(struct phy_device *phydev)
291{
c.chenbaa06e92022-04-17 23:27:27 -0700292 return (phydev->c45_ids.device_ids[1] & 0xffffffff) == PHY_ID_88Q2220;
xjb04a4022021-11-25 15:01:52 +0800293}
294
295static struct phy_driver marvell_88q_driver[] = {
296 {
c.chenbaa06e92022-04-17 23:27:27 -0700297 .phy_id = PHY_ID_88Q2220,
xjb04a4022021-11-25 15:01:52 +0800298 .phy_id_mask = 0xfffffff0,
c.chenbaa06e92022-04-17 23:27:27 -0700299 .name = "Marvell 88Q2220",
xjb04a4022021-11-25 15:01:52 +0800300 .config_init = q2110_config_init,
301 .match_phy_device = q2110_match_phy_device,
302 .set_loopback = &q2110_loopback,
303 .read_status = q2110_read_status,
c.chenae2fc652022-07-11 03:54:53 -0700304 .suspend = q2220_suspend,
305 .resume = q2220_resume,
xjb04a4022021-11-25 15:01:52 +0800306 }
307};
308
309module_phy_driver(marvell_88q_driver);