blob: ab00fb0e397eda113b782890d51726bac64e3ee5 [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);
ll3e22ca62022-06-22 10:50:31 +0800156 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x840);
157 phy_write(phydev, MII_ADDR_C45 | 0x03FE1B, 0x48);
158 phy_write(phydev, MII_ADDR_C45 | 0x01FFE4, 0x6B6);
159 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x0);
160 phy_write(phydev, MII_ADDR_C45 | 0x030000, 0x0);
161 while (i < 5) {
162 mrvlId= phy_read(phydev, MII_ADDR_C45 | 0x03FFE4);
163 if (mrvlId == 0x06BA) {
164 break;
165 }
166 i++;
167 msleep(1);
168 }
169 phy_write(phydev, MII_ADDR_C45 | 0x078032, 0x2020);
170 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xA28);
171 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xC28);
c.chenfaec22c2022-07-27 19:38:31 -0700172 phy_write(phydev, MII_ADDR_C45 | 0x03803A, 0xDA44);
173 phy_write(phydev, MII_ADDR_C45 | 0x038039, 0x2C0B);
c.chendf3ef622022-07-13 00:57:58 -0700174
175 phy_write(phydev, MII_ADDR_C45 | 0x038027, 0x1);//TC10 sleep/wakeup capability support
xjb04a4022021-11-25 15:01:52 +0800176 q2110_timing_init(phydev);
177
ll3e22ca62022-06-22 10:50:31 +0800178 q2110_dts_init(phydev);
179
xjb04a4022021-11-25 15:01:52 +0800180 return 0;
181}
182
183static int q2110_loopback(struct phy_device *phydev, bool enable)
184{
185 int val;
186
187 val = phy_read(phydev, Q2110_PCS_CTRL);
188 if (val < 0)
189 return val;
190
191 val &= ~Q2110_LOOPBACK;
192 if (enable)
193 val |= Q2110_LOOPBACK;
194
195 val = phy_write(phydev, Q2110_PCS_CTRL, val);
196 if (val < 0)
197 return val;
198
199 return 0;
200}
201
c.chenbaa06e92022-04-17 23:27:27 -0700202
xjb04a4022021-11-25 15:01:52 +0800203static int q2110_read_status(struct phy_device *phydev)
204{
205 int val;
206
207 phydev->duplex = 1;
208 phydev->pause = 0;
209
210 val = phy_read(phydev, Q2110_T1_AN_STATUS);
211 if (val < 0)
212 return val;
213
xjb04a4022021-11-25 15:01:52 +0800214 if (val & Q2110_T1_LINK_STATUS)
215 phydev->link = 1;
216 else
217 phydev->link = 0;
218
219 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
220 if (val < 0)
221 return val;
222
223 if (val & Q2110_T1_LINK_TYPE)
224 phydev->speed = SPEED_1000;
225 else
226 phydev->speed = SPEED_100;
227
228 return 0;
229}
230
c.chenae2fc652022-07-11 03:54:53 -0700231int q2220_suspend(struct phy_device *phydev)
232{
xf.liec75fdd2022-11-09 18:24:10 -0800233 //xf.li 2022/11/9 modify for API-647
xf.li2230aac2022-09-09 00:23:23 -0700234 printk("phy sleep start\n");
xf.li55334fa2023-04-01 03:28:51 -0700235 gpio_direction_output(205 + 268, 0);
xf.li9d095322023-02-07 18:04:49 -0800236 gpio_direction_output(NAD_WAKEUP_PHY1, 0);
237 mdelay(1);//ensure do not wake up
238 //phy_write(phydev, MII_ADDR_C45 | 0x038022, 0x1);
239 phy_write(phydev, Q2220_LPSD_CTRL_1, Q2220_LPSD_DISABLE_REMOTE_WAKE_UP);
240 mdelay(10);//for enter T1 port sleep, need link parter respond
241 printk("reg 038021 = %x\n", phy_read(phydev, Q2220_LPSD_CTRL_1));
242 phy_write(phydev, Q2220_LPSD_STATUS, Q2220_LPSD_POWER_DOWN);// enter LPSD sleep mode
243 printk("reg 038020 = %x\n", phy_read(phydev, Q2220_LPSD_STATUS));
244 mdelay(50);
245 printk("reg 038020 = %x\n", phy_read(phydev, Q2220_LPSD_STATUS));
246 gpio_direction_output(NAD_RESET_PHY1, 0);
xf.li742b5c02023-04-01 02:05:52 -0700247 mdelay(1);
248 gpio_direction_output(PHY_POWER_SUPPLY, 0);
xf.liec75fdd2022-11-09 18:24:10 -0800249 if_suspend = 1;
xf.li2230aac2022-09-09 00:23:23 -0700250 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800251 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700252}
253int q2220_resume(struct phy_device *phydev)
254{
xf.liec75fdd2022-11-09 18:24:10 -0800255 //xf.li 2022/11/9 modify for API-647
256 if(if_suspend == 1)
257 {
258 printk("phy awake start\n");
xf.li55334fa2023-04-01 03:28:51 -0700259 gpio_direction_output(205 + 268, 1);
xf.li742b5c02023-04-01 02:05:52 -0700260 gpio_direction_output(PHY_POWER_SUPPLY, 1);
261 mdelay(1);
xf.li9d095322023-02-07 18:04:49 -0800262 gpio_direction_output(NAD_WAKEUP_PHY1, 1);
xf.li55334fa2023-04-01 03:28:51 -0700263 mdelay(1000);
xf.li9d095322023-02-07 18:04:49 -0800264 gpio_direction_output(NAD_WAKEUP_PHY1, 0);
265 mdelay(1);
266 gpio_direction_output(NAD_RESET_PHY1, 1);
xf.li55334fa2023-04-01 03:28:51 -0700267 gpio_direction_output(205 + 268, 0);
xf.li9d095322023-02-07 18:04:49 -0800268 mdelay(10);//at lest 4ms for reset phy
269 q2110_config_init(phydev);
xf.liec75fdd2022-11-09 18:24:10 -0800270 if_suspend = 0;
271 }
272 else
273 {
274 printk("q2220_resume: no suspend! In boot state.");
275 }
xf.li2230aac2022-09-09 00:23:23 -0700276 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800277 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700278}
279
280
281
xjb04a4022021-11-25 15:01:52 +0800282static int q2110_match_phy_device(struct phy_device *phydev)
283{
c.chenbaa06e92022-04-17 23:27:27 -0700284 return (phydev->c45_ids.device_ids[1] & 0xffffffff) == PHY_ID_88Q2220;
xjb04a4022021-11-25 15:01:52 +0800285}
286
287static struct phy_driver marvell_88q_driver[] = {
288 {
c.chenbaa06e92022-04-17 23:27:27 -0700289 .phy_id = PHY_ID_88Q2220,
xjb04a4022021-11-25 15:01:52 +0800290 .phy_id_mask = 0xfffffff0,
c.chenbaa06e92022-04-17 23:27:27 -0700291 .name = "Marvell 88Q2220",
xjb04a4022021-11-25 15:01:52 +0800292 .config_init = q2110_config_init,
293 .match_phy_device = q2110_match_phy_device,
294 .set_loopback = &q2110_loopback,
295 .read_status = q2110_read_status,
c.chenae2fc652022-07-11 03:54:53 -0700296 .suspend = q2220_suspend,
297 .resume = q2220_resume,
xjb04a4022021-11-25 15:01:52 +0800298 }
299};
300
301module_phy_driver(marvell_88q_driver);