blob: d37df6754c1a6b145fd5b9a2c9507d35ede4e126 [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
c.chenbaa06e92022-04-17 23:27:27 -07009#define PHY_ID_88Q2220 0x002b0b21
xjb04a4022021-11-25 15:01:52 +080010
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
c.chenbaa06e92022-04-17 23:27:27 -070032#define Q2110_COM_PORT_CTRL (MII_ADDR_C45 | 0x4A001)
xjb04a4022021-11-25 15:01:52 +080033/* 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,
c.chenbaa06e92022-04-17 23:27:27 -070055 "marvell,88q2220", &len);
xjb04a4022021-11-25 15:01:52 +080056 if (!paddr)
57 return 0;
58
59 speed = be32_to_cpup(paddr);
60 master = be32_to_cpup(paddr + 1);
c.chenbaa06e92022-04-17 23:27:27 -070061printk("speed = %d\n",speed);
62printk("master = %d\n",master);
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
xjb04a4022021-11-25 15:01:52 +080075 /* Software Reset PHY */
c.chenbaa06e92022-04-17 23:27:27 -070076 //phy_write(phydev, (MII_ADDR_C45 | 0x030900), 0x8000);
77 //phy_write(phydev, (MII_ADDR_C45 | 0x03FFE4), 0x000C);
78
xjb04a4022021-11-25 15:01:52 +080079 val = phy_read(phydev, Q2110_PMA_PMD_CTRL);
80 if (val < 0)
81 return val;
82 val |= Q2110_PMA_PMD_RST;
83 val = phy_write(phydev, Q2110_PMA_PMD_CTRL, val);
84 if (val < 0)
85 return val;
86
87 do {
88 val = phy_read(phydev, Q2110_PMA_PMD_CTRL);
89 if (val < 0)
90 return val;
91 } while (val & Q2110_PMA_PMD_RST);
c.chenbaa06e92022-04-17 23:27:27 -070092
93 printk("q2110 dts init ok!!\n");
xjb04a4022021-11-25 15:01:52 +080094
95 return 0;
96}
97
98static int q2110_timing_init(struct phy_device *phydev)
99{
100 int val;
101
102 if (phy_interface_is_rgmii(phydev)) {
103 val = phy_read(phydev, Q2110_COM_PORT_CTRL);
104 if (val < 0)
105 return val;
106
107 val &= ~(Q2110_RGMII_TX_TIMING_CTRL |
108 Q2110_RGMII_RX_TIMING_CTRL);
109
110 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
111 val |= (Q2110_RGMII_TX_TIMING_CTRL |
112 Q2110_RGMII_RX_TIMING_CTRL);
113 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
114 val |= Q2110_RGMII_RX_TIMING_CTRL;
115 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
116 val |= Q2110_RGMII_TX_TIMING_CTRL;
117
118 val = phy_write(phydev, Q2110_COM_PORT_CTRL, val);
119 if (val < 0)
120 return val;
121 }
122
c.chenbaa06e92022-04-17 23:27:27 -0700123
xjb04a4022021-11-25 15:01:52 +0800124 val = phy_read(phydev, Q2110_RST_CTRL);
125 if (val < 0)
126 return val;
127 val |= Q2110_RGMII_SW_RESET;
128 val = phy_write(phydev, Q2110_RST_CTRL, val);
129 if (val < 0)
130 return val;
131
xjb04a4022021-11-25 15:01:52 +0800132 val = phy_read(phydev, Q2110_RST_CTRL);
133 if (val < 0)
134 return val;
135 val &= ~Q2110_RGMII_SW_RESET;
136 val = phy_write(phydev, Q2110_RST_CTRL, val);
137 if (val < 0)
138 return val;
139
140 return 0;
141}
142
143static int q2110_config_init(struct phy_device *phydev)
144{
145 phydev->supported =
146 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
147 phydev->advertising =
148 SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full;
149 phydev->state = PHY_NOLINK;
150 phydev->autoneg = AUTONEG_DISABLE;
c.chenbaa06e92022-04-17 23:27:27 -0700151
152printk("q2110_config_init \n");
xjb04a4022021-11-25 15:01:52 +0800153 q2110_dts_init(phydev);
154 q2110_timing_init(phydev);
155
156 return 0;
157}
158
159static int q2110_loopback(struct phy_device *phydev, bool enable)
160{
161 int val;
162
163 val = phy_read(phydev, Q2110_PCS_CTRL);
164 if (val < 0)
165 return val;
166
167 val &= ~Q2110_LOOPBACK;
168 if (enable)
169 val |= Q2110_LOOPBACK;
170
171 val = phy_write(phydev, Q2110_PCS_CTRL, val);
172 if (val < 0)
173 return val;
174
175 return 0;
176}
177
c.chenbaa06e92022-04-17 23:27:27 -0700178
xjb04a4022021-11-25 15:01:52 +0800179static int q2110_read_status(struct phy_device *phydev)
180{
181 int val;
182
183 phydev->duplex = 1;
184 phydev->pause = 0;
185
186 val = phy_read(phydev, Q2110_T1_AN_STATUS);
187 if (val < 0)
188 return val;
189
xjb04a4022021-11-25 15:01:52 +0800190 if (val & Q2110_T1_LINK_STATUS)
191 phydev->link = 1;
192 else
193 phydev->link = 0;
194
195 val = phy_read(phydev, Q2110_T1_PMA_PMD_CTRL);
196 if (val < 0)
197 return val;
198
199 if (val & Q2110_T1_LINK_TYPE)
200 phydev->speed = SPEED_1000;
201 else
202 phydev->speed = SPEED_100;
203
204 return 0;
205}
206
207static int q2110_match_phy_device(struct phy_device *phydev)
208{
c.chenbaa06e92022-04-17 23:27:27 -0700209 return (phydev->c45_ids.device_ids[1] & 0xffffffff) == PHY_ID_88Q2220;
xjb04a4022021-11-25 15:01:52 +0800210}
211
212static struct phy_driver marvell_88q_driver[] = {
213 {
c.chenbaa06e92022-04-17 23:27:27 -0700214 .phy_id = PHY_ID_88Q2220,
xjb04a4022021-11-25 15:01:52 +0800215 .phy_id_mask = 0xfffffff0,
c.chenbaa06e92022-04-17 23:27:27 -0700216 .name = "Marvell 88Q2220",
xjb04a4022021-11-25 15:01:52 +0800217 .config_init = q2110_config_init,
218 .match_phy_device = q2110_match_phy_device,
219 .set_loopback = &q2110_loopback,
220 .read_status = q2110_read_status,
221 }
222};
223
224module_phy_driver(marvell_88q_driver);