blob: 467f4ae00e068d88b614d6bb3adc96cbed340174 [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.liec75fdd2022-11-09 18:24:10 -080044int if_suspend = 0;
45
xjb04a4022021-11-25 15:01:52 +080046/* Set and/or override some configuration registers based on the
47 * marvell,88q2110 property stored in the of_node for the phydev.
48 * marvell,88q2110 = <speed master>,...;
49 * speed: 1000Mbps or 100Mbps.
50 * master: 1-master, 0-slave.
51 */
52static int q2110_dts_init(struct phy_device *phydev)
53{
54 const __be32 *paddr;
55 u32 speed;
56 u32 master;
57 int val, len;
58
59 if (!phydev->mdio.dev.of_node)
60 return 0;
61
62 paddr = of_get_property(phydev->mdio.dev.of_node,
c.chenbaa06e92022-04-17 23:27:27 -070063 "marvell,88q2220", &len);
xjb04a4022021-11-25 15:01:52 +080064 if (!paddr)
65 return 0;
66
67 speed = be32_to_cpup(paddr);
68 master = be32_to_cpup(paddr + 1);
xjb04a4022021-11-25 15:01:52 +080069 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
70 if (val < 0)
71 return val;
72 val &= ~(Q2110_T1_MASTER_SLAVE_CFG | Q2110_T1_LINK_TYPE);
73 if (speed == SPEED_1000)
74 val |= Q2110_T1_LINK_TYPE;
75 if (master)
76 val |= Q2110_T1_MASTER_SLAVE_CFG;
77 val = phy_write(phydev, Q2110_T1_PMA_PMD_CTRL, val);
78 if (val < 0)
79 return val;
c.chenbaa06e92022-04-17 23:27:27 -070080
ll3e22ca62022-06-22 10:50:31 +080081 phy_write(phydev, (MII_ADDR_C45 | 0x030900), 0x8000);
82 phy_write(phydev, (MII_ADDR_C45 | 0x03FFE4), 0x000C);
c.chenbaa06e92022-04-17 23:27:27 -070083
84 printk("q2110 dts init ok!!\n");
xjb04a4022021-11-25 15:01:52 +080085
86 return 0;
87}
88
89static int q2110_timing_init(struct phy_device *phydev)
90{
91 int val;
92
93 if (phy_interface_is_rgmii(phydev)) {
94 val = phy_read(phydev, Q2110_COM_PORT_CTRL);
95 if (val < 0)
96 return val;
97
98 val &= ~(Q2110_RGMII_TX_TIMING_CTRL |
99 Q2110_RGMII_RX_TIMING_CTRL);
100
101 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
102 val |= (Q2110_RGMII_TX_TIMING_CTRL |
103 Q2110_RGMII_RX_TIMING_CTRL);
104 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
105 val |= Q2110_RGMII_RX_TIMING_CTRL;
106 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
107 val |= Q2110_RGMII_TX_TIMING_CTRL;
108
109 val = phy_write(phydev, Q2110_COM_PORT_CTRL, val);
110 if (val < 0)
111 return val;
112 }
113
c.chenbaa06e92022-04-17 23:27:27 -0700114
xjb04a4022021-11-25 15:01:52 +0800115 val = phy_read(phydev, Q2110_RST_CTRL);
116 if (val < 0)
117 return val;
118 val |= Q2110_RGMII_SW_RESET;
119 val = phy_write(phydev, Q2110_RST_CTRL, val);
120 if (val < 0)
121 return val;
122
xjb04a4022021-11-25 15:01:52 +0800123 val = phy_read(phydev, Q2110_RST_CTRL);
124 if (val < 0)
125 return val;
126 val &= ~Q2110_RGMII_SW_RESET;
127 val = phy_write(phydev, Q2110_RST_CTRL, val);
128 if (val < 0)
129 return val;
130
131 return 0;
132}
133
134static int q2110_config_init(struct phy_device *phydev)
135{
ll3e22ca62022-06-22 10:50:31 +0800136 int i=0,mrvlId;
xjb04a4022021-11-25 15:01:52 +0800137 phydev->supported =
138 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
139 phydev->advertising =
140 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
141 phydev->state = PHY_NOLINK;
142 phydev->autoneg = AUTONEG_DISABLE;
c.chenfaec22c2022-07-27 19:38:31 -0700143 phy_write(phydev, MII_ADDR_C45 | 0x038033, 0x6801);
ll3e22ca62022-06-22 10:50:31 +0800144 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x840);
145 phy_write(phydev, MII_ADDR_C45 | 0x03FE1B, 0x48);
146 phy_write(phydev, MII_ADDR_C45 | 0x01FFE4, 0x6B6);
147 phy_write(phydev, MII_ADDR_C45 | 0x010000, 0x0);
148 phy_write(phydev, MII_ADDR_C45 | 0x030000, 0x0);
149 while (i < 5) {
150 mrvlId= phy_read(phydev, MII_ADDR_C45 | 0x03FFE4);
151 if (mrvlId == 0x06BA) {
152 break;
153 }
154 i++;
155 msleep(1);
156 }
157 phy_write(phydev, MII_ADDR_C45 | 0x078032, 0x2020);
158 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xA28);
159 phy_write(phydev, MII_ADDR_C45 | 0x078031, 0xC28);
c.chenfaec22c2022-07-27 19:38:31 -0700160 phy_write(phydev, MII_ADDR_C45 | 0x03803A, 0xDA44);
161 phy_write(phydev, MII_ADDR_C45 | 0x038039, 0x2C0B);
c.chendf3ef622022-07-13 00:57:58 -0700162
163 phy_write(phydev, MII_ADDR_C45 | 0x038027, 0x1);//TC10 sleep/wakeup capability support
xjb04a4022021-11-25 15:01:52 +0800164 q2110_timing_init(phydev);
165
ll3e22ca62022-06-22 10:50:31 +0800166 q2110_dts_init(phydev);
167
xjb04a4022021-11-25 15:01:52 +0800168 return 0;
169}
170
171static int q2110_loopback(struct phy_device *phydev, bool enable)
172{
173 int val;
174
175 val = phy_read(phydev, Q2110_PCS_CTRL);
176 if (val < 0)
177 return val;
178
179 val &= ~Q2110_LOOPBACK;
180 if (enable)
181 val |= Q2110_LOOPBACK;
182
183 val = phy_write(phydev, Q2110_PCS_CTRL, val);
184 if (val < 0)
185 return val;
186
187 return 0;
188}
189
c.chenbaa06e92022-04-17 23:27:27 -0700190
xjb04a4022021-11-25 15:01:52 +0800191static int q2110_read_status(struct phy_device *phydev)
192{
193 int val;
194
195 phydev->duplex = 1;
196 phydev->pause = 0;
197
198 val = phy_read(phydev, Q2110_T1_AN_STATUS);
199 if (val < 0)
200 return val;
201
xjb04a4022021-11-25 15:01:52 +0800202 if (val & Q2110_T1_LINK_STATUS)
203 phydev->link = 1;
204 else
205 phydev->link = 0;
206
207 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
208 if (val < 0)
209 return val;
210
211 if (val & Q2110_T1_LINK_TYPE)
212 phydev->speed = SPEED_1000;
213 else
214 phydev->speed = SPEED_100;
215
216 return 0;
217}
218
c.chenae2fc652022-07-11 03:54:53 -0700219int q2220_suspend(struct phy_device *phydev)
220{
xf.liec75fdd2022-11-09 18:24:10 -0800221 //xf.li 2022/11/9 modify for API-647
xf.li2230aac2022-09-09 00:23:23 -0700222 printk("phy sleep start\n");
xf.liec75fdd2022-11-09 18:24:10 -0800223 gpio_direction_output(7 + 268, 0);
224 mdelay(1);
xf.li2230aac2022-09-09 00:23:23 -0700225 phy_write(phydev, MII_ADDR_C45 | 0x038022, 0x1);
226 mdelay(10);
227 printk("reg 038022 = %x\n", phy_read(phydev, (MII_ADDR_C45 | 0x038022)));
228 phy_write(phydev, (MII_ADDR_C45 | 0x038020), 0x1);
229 printk("reg 038020 = %x\n", phy_read(phydev, (MII_ADDR_C45 | 0x038020)));
230 gpio_direction_output(26 + 268, 0);
xf.liec75fdd2022-11-09 18:24:10 -0800231 if_suspend = 1;
xf.li2230aac2022-09-09 00:23:23 -0700232 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800233 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700234}
235int q2220_resume(struct phy_device *phydev)
236{
xf.liec75fdd2022-11-09 18:24:10 -0800237 //xf.li 2022/11/9 modify for API-647
238 if(if_suspend == 1)
239 {
240 printk("phy awake start\n");
241 gpio_direction_output(7 + 268, 1);
242 udelay(1100);
243 gpio_direction_output(26 + 268, 1);
244 gpio_direction_output(7 + 268, 0);
245 if_suspend = 0;
246 }
247 else
248 {
249 printk("q2220_resume: no suspend! In boot state.");
250 }
xf.li2230aac2022-09-09 00:23:23 -0700251 return 0;
xf.liec75fdd2022-11-09 18:24:10 -0800252 //xf.li 2022/11/9 modify for API-647
c.chenae2fc652022-07-11 03:54:53 -0700253}
254
255
256
xjb04a4022021-11-25 15:01:52 +0800257static int q2110_match_phy_device(struct phy_device *phydev)
258{
c.chenbaa06e92022-04-17 23:27:27 -0700259 return (phydev->c45_ids.device_ids[1] & 0xffffffff) == PHY_ID_88Q2220;
xjb04a4022021-11-25 15:01:52 +0800260}
261
262static struct phy_driver marvell_88q_driver[] = {
263 {
c.chenbaa06e92022-04-17 23:27:27 -0700264 .phy_id = PHY_ID_88Q2220,
xjb04a4022021-11-25 15:01:52 +0800265 .phy_id_mask = 0xfffffff0,
c.chenbaa06e92022-04-17 23:27:27 -0700266 .name = "Marvell 88Q2220",
xjb04a4022021-11-25 15:01:52 +0800267 .config_init = q2110_config_init,
268 .match_phy_device = q2110_match_phy_device,
269 .set_loopback = &q2110_loopback,
270 .read_status = q2110_read_status,
c.chenae2fc652022-07-11 03:54:53 -0700271 .suspend = q2220_suspend,
272 .resume = q2220_resume,
xjb04a4022021-11-25 15:01:52 +0800273 }
274};
275
276module_phy_driver(marvell_88q_driver);