1bf9a0f37SDavid Wu // SPDX-License-Identifier: GPL-2.0+
2bf9a0f37SDavid Wu /**
3bf9a0f37SDavid Wu *
4bf9a0f37SDavid Wu * Driver for ROCKCHIP RK630 Ethernet PHYs
5bf9a0f37SDavid Wu *
6bf9a0f37SDavid Wu * Copyright (c) 2020, Fuzhou Rockchip Electronics Co., Ltd
7bf9a0f37SDavid Wu *
8bf9a0f37SDavid Wu * David Wu <david.wu@rock-chips.com>
9bf9a0f37SDavid Wu *
10bf9a0f37SDavid Wu * This program is free software; you can redistribute it and/or modify
11bf9a0f37SDavid Wu * it under the terms of the GNU General Public License as published by
12bf9a0f37SDavid Wu * the Free Software Foundation; either version 2 of the License, or
13bf9a0f37SDavid Wu * (at your option) any later version.
14bf9a0f37SDavid Wu *
15bf9a0f37SDavid Wu */
16bf9a0f37SDavid Wu
17bf9a0f37SDavid Wu #include <config.h>
18bf9a0f37SDavid Wu #include <common.h>
19eaf55bebSDavid Wu #include <misc.h>
20bf9a0f37SDavid Wu #include <phy.h>
21bf9a0f37SDavid Wu
22bf9a0f37SDavid Wu #define RK630_PHY_ID 0x00441400
23bf9a0f37SDavid Wu
24bf9a0f37SDavid Wu /* PAGE 0 */
25cf8faefbSDavid Wu #define REG_MMD_ACCESS_CONTROL 0x0d
26cf8faefbSDavid Wu #define REG_MMD_ACCESS_DATA_ADDRESS 0x0e
27bf9a0f37SDavid Wu #define REG_INTERRUPT_STATUS 0X10
28bf9a0f37SDavid Wu #define REG_INTERRUPT_MASK 0X11
29bf9a0f37SDavid Wu #define REG_GLOBAL_CONFIGURATION 0X13
30bf9a0f37SDavid Wu #define REG_MAC_ADDRESS0 0x16
31bf9a0f37SDavid Wu #define REG_MAC_ADDRESS1 0x17
32bf9a0f37SDavid Wu #define REG_MAC_ADDRESS2 0x18
33bf9a0f37SDavid Wu
34bf9a0f37SDavid Wu #define REG_PAGE_SEL 0x1F
35bf9a0f37SDavid Wu
36bf9a0f37SDavid Wu /* PAGE 1 */
37bf9a0f37SDavid Wu #define REG_PAGE1_APS_CTRL 0x12
38bf9a0f37SDavid Wu #define REG_PAGE1_UAPS_CONFIGURE 0X13
39bf9a0f37SDavid Wu #define REG_PAGE1_EEE_CONFIGURE 0x17
40bf9a0f37SDavid Wu
41bf9a0f37SDavid Wu /* PAGE 2 */
42bf9a0f37SDavid Wu #define REG_PAGE2_AFE_CTRL 0x18
43bf9a0f37SDavid Wu
44bf9a0f37SDavid Wu /* PAGE 6 */
45bf9a0f37SDavid Wu #define REG_PAGE6_ADC_ANONTROL 0x10
46b6667345SDavid Wu #define REG_PAGE6_GAIN_ANONTROL 0x12
47bf9a0f37SDavid Wu #define REG_PAGE6_AFE_RX_CTRL 0x13
48bf9a0f37SDavid Wu #define REG_PAGE6_AFE_TX_CTRL 0x14
49bf9a0f37SDavid Wu #define REG_PAGE6_AFE_DRIVER2 0x15
50b6667345SDavid Wu #define REG_PAGE6_CP_CURRENT 0x17
51b6667345SDavid Wu #define REG_PAGE6_ADC_OP_BIAS 0x18
52b6667345SDavid Wu #define REG_PAGE6_RX_DECTOR 0x19
5326326087SDavid Wu #define REG_PAGE6_TX_MOS_DRV 0x1B
54b6667345SDavid Wu #define REG_PAGE6_AFE_PDCW 0x1c
55bf9a0f37SDavid Wu
56bf9a0f37SDavid Wu /* PAGE 8 */
57bf9a0f37SDavid Wu #define REG_PAGE8_AFE_CTRL 0x18
58b6667345SDavid Wu #define REG_PAGE8_AUTO_CAL 0x1d
59b6667345SDavid Wu
60b6667345SDavid Wu /*
61b6667345SDavid Wu * Fixed address:
62b6667345SDavid Wu * Addr: 1 --- RK630@S40
63b6667345SDavid Wu * 2 --- RV1106@T22
64b6667345SDavid Wu */
65b6667345SDavid Wu #define PHY_ADDR_S40 1
66b6667345SDavid Wu #define PHY_ADDR_T22 2
67b6667345SDavid Wu
68eaf55bebSDavid Wu #define T22_TX_LEVEL_100M 0x2d
69eaf55bebSDavid Wu #define T22_TX_LEVEL_10M 0x32
70eaf55bebSDavid Wu
rk630_phy_t22_get_txlevel_from_efuse(unsigned char * txlevel_100,unsigned char * txlevel_10)71eaf55bebSDavid Wu static int rk630_phy_t22_get_txlevel_from_efuse(unsigned char *txlevel_100,
72eaf55bebSDavid Wu unsigned char *txlevel_10)
73eaf55bebSDavid Wu {
74eaf55bebSDavid Wu #if defined(CONFIG_ROCKCHIP_EFUSE) || defined(CONFIG_ROCKCHIP_OTP)
75eaf55bebSDavid Wu unsigned char tx_level[2];
76eaf55bebSDavid Wu struct udevice *dev;
77eaf55bebSDavid Wu u32 regs[2] = {0};
78eaf55bebSDavid Wu ofnode node;
79eaf55bebSDavid Wu int ret;
80eaf55bebSDavid Wu
81eaf55bebSDavid Wu /* retrieve the device */
82eaf55bebSDavid Wu if (IS_ENABLED(CONFIG_ROCKCHIP_EFUSE))
83eaf55bebSDavid Wu ret = uclass_get_device_by_driver(UCLASS_MISC,
84eaf55bebSDavid Wu DM_GET_DRIVER(rockchip_efuse),
85eaf55bebSDavid Wu &dev);
86eaf55bebSDavid Wu else
87eaf55bebSDavid Wu ret = uclass_get_device_by_driver(UCLASS_MISC,
88eaf55bebSDavid Wu DM_GET_DRIVER(rockchip_otp),
89eaf55bebSDavid Wu &dev);
90eaf55bebSDavid Wu
91eaf55bebSDavid Wu if (ret) {
92eaf55bebSDavid Wu printf("%s: could not find efuse/otp device\n", __func__);
93eaf55bebSDavid Wu return ret;
94eaf55bebSDavid Wu }
95eaf55bebSDavid Wu
96eaf55bebSDavid Wu node = dev_read_subnode(dev, "macphy-txlevel");
97eaf55bebSDavid Wu if (!ofnode_valid(node))
98eaf55bebSDavid Wu return -EINVAL;
99eaf55bebSDavid Wu
100eaf55bebSDavid Wu ret = ofnode_read_u32_array(node, "reg", regs, 2);
101eaf55bebSDavid Wu if (ret) {
102eaf55bebSDavid Wu printf("Cannot get efuse reg\n");
103eaf55bebSDavid Wu return -EINVAL;
104eaf55bebSDavid Wu }
105eaf55bebSDavid Wu
106eaf55bebSDavid Wu /* read the txlevel from the efuses */
107eaf55bebSDavid Wu ret = misc_read(dev, regs[0], &tx_level, 2);
108eaf55bebSDavid Wu if (ret) {
109eaf55bebSDavid Wu printf("%s: read txlevel from efuse/otp failed, ret=%d\n",
110eaf55bebSDavid Wu __func__, ret);
111eaf55bebSDavid Wu return ret;
112eaf55bebSDavid Wu }
1138e75fedbSDavid Wu *txlevel_100 = tx_level[1];
1148e75fedbSDavid Wu *txlevel_10 = tx_level[0];
115eaf55bebSDavid Wu
116eaf55bebSDavid Wu return 0;
117eaf55bebSDavid Wu #else
118eaf55bebSDavid Wu return -EINVAL;
119eaf55bebSDavid Wu #endif
120eaf55bebSDavid Wu }
121eaf55bebSDavid Wu
rk630_phy_startup(struct phy_device * phydev)122b6667345SDavid Wu static int rk630_phy_startup(struct phy_device *phydev)
123b6667345SDavid Wu {
124b6667345SDavid Wu int ret;
125b6667345SDavid Wu
126b6667345SDavid Wu /* Read the Status (2x to make sure link is right) */
127b6667345SDavid Wu ret = genphy_update_link(phydev);
128b6667345SDavid Wu if (ret)
129b6667345SDavid Wu return ret;
130cf8faefbSDavid Wu
131b6667345SDavid Wu /* Read the Status (2x to make sure link is right) */
132b6667345SDavid Wu phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
133b6667345SDavid Wu
134b6667345SDavid Wu return genphy_parse_link(phydev);
135b6667345SDavid Wu }
136bf9a0f37SDavid Wu
rk630_phy_s40_config_init(struct phy_device * phydev)137b6667345SDavid Wu static void rk630_phy_s40_config_init(struct phy_device *phydev)
138bf9a0f37SDavid Wu {
139bf9a0f37SDavid Wu phy_write(phydev, 0, MDIO_DEVAD_NONE,
140bf9a0f37SDavid Wu phy_read(phydev, MDIO_DEVAD_NONE, 0) & ~BIT(13));
141bf9a0f37SDavid Wu
142bf9a0f37SDavid Wu /* Switch to page 1 */
143bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0100);
144bf9a0f37SDavid Wu /* Disable APS */
145bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE1_APS_CTRL, 0x4824);
146bf9a0f37SDavid Wu /* Switch to page 2 */
147bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0200);
148bf9a0f37SDavid Wu /* PHYAFE TRX optimization */
149bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE2_AFE_CTRL, 0x0000);
150bf9a0f37SDavid Wu /* Switch to page 6 */
151bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0600);
152bf9a0f37SDavid Wu /* PHYAFE TX optimization */
153bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_TX_CTRL, 0x708f);
154bf9a0f37SDavid Wu /* PHYAFE RX optimization */
155bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_RX_CTRL, 0xf000);
156bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_DRIVER2, 0x1530);
157bf9a0f37SDavid Wu
158bf9a0f37SDavid Wu /* Switch to page 8 */
159bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0800);
160bf9a0f37SDavid Wu /* PHYAFE TRX optimization */
161bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE8_AFE_CTRL, 0x00bc);
162bf9a0f37SDavid Wu
163b6667345SDavid Wu /* Switch to page 0 */
164b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0000);
165b6667345SDavid Wu }
166b6667345SDavid Wu
rk630_phy_t22_config_init(struct phy_device * phydev)167b6667345SDavid Wu static void rk630_phy_t22_config_init(struct phy_device *phydev)
168b6667345SDavid Wu {
169eaf55bebSDavid Wu unsigned char tx_level_100M = T22_TX_LEVEL_100M;
170eaf55bebSDavid Wu unsigned char tx_level_10M = T22_TX_LEVEL_10M;
171eaf55bebSDavid Wu
172b6667345SDavid Wu /* Switch to page 1 */
173b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0100);
174b6667345SDavid Wu /* Disable APS */
175b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE1_APS_CTRL, 0x4824);
176b6667345SDavid Wu /* Switch to page 2 */
177b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0200);
178b6667345SDavid Wu /* PHYAFE TRX optimization */
179b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE2_AFE_CTRL, 0x0000);
180b6667345SDavid Wu /* Switch to page 6 */
181bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0600);
182b6667345SDavid Wu /* PHYAFE ADC optimization */
183b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_ADC_ANONTROL, 0x5540);
184b6667345SDavid Wu /* PHYAFE Gain optimization */
185b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_GAIN_ANONTROL, 0x0400);
186b6667345SDavid Wu /* PHYAFE EQ optimization */
187b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_TX_CTRL, 0x1088);
188eaf55bebSDavid Wu
189eaf55bebSDavid Wu if (rk630_phy_t22_get_txlevel_from_efuse(&tx_level_100M, &tx_level_10M)) {
190eaf55bebSDavid Wu tx_level_100M = T22_TX_LEVEL_100M;
191eaf55bebSDavid Wu tx_level_10M = T22_TX_LEVEL_10M;
192eaf55bebSDavid Wu }
193b6667345SDavid Wu /* PHYAFE TX optimization */
194eaf55bebSDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_DRIVER2,
195eaf55bebSDavid Wu (tx_level_100M << 8) | tx_level_10M);
196eaf55bebSDavid Wu
197b6667345SDavid Wu /* PHYAFE CP current optimization */
198b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_CP_CURRENT, 0x0575);
199b6667345SDavid Wu /* ADC OP BIAS optimization */
200b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_ADC_OP_BIAS, 0x0000);
201b6667345SDavid Wu /* Rx signal detctor level optimization */
202b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_RX_DECTOR, 0x0408);
203b6667345SDavid Wu /* PHYAFE PDCW optimization */
204b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_PDCW, 0x8880);
20526326087SDavid Wu /* Add PHY Tx mos drive, reduce power noise/jitter */
206*a1b1cb77SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_TX_MOS_DRV, 0x888e);
207b6667345SDavid Wu
208b6667345SDavid Wu /* Switch to page 8 */
209b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0800);
210b6667345SDavid Wu /* Disable auto-cal */
211b6667345SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE8_AUTO_CAL, 0x0844);
212bf9a0f37SDavid Wu
213bf9a0f37SDavid Wu /* Switch to page 0 */
214bf9a0f37SDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0000);
215cf8faefbSDavid Wu
216cf8faefbSDavid Wu /* Disable eee mode advertised */
217cf8faefbSDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_MMD_ACCESS_CONTROL, 0x0007);
218cf8faefbSDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_MMD_ACCESS_DATA_ADDRESS, 0x003c);
219cf8faefbSDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_MMD_ACCESS_CONTROL, 0x4007);
220cf8faefbSDavid Wu phy_write(phydev, MDIO_DEVAD_NONE, REG_MMD_ACCESS_DATA_ADDRESS, 0x0000);
221b6667345SDavid Wu }
rk630_phy_config_init(struct phy_device * phydev)222b6667345SDavid Wu static int rk630_phy_config_init(struct phy_device *phydev)
223b6667345SDavid Wu {
224b6667345SDavid Wu switch (phydev->addr) {
225b6667345SDavid Wu case PHY_ADDR_S40:
226b6667345SDavid Wu rk630_phy_s40_config_init(phydev);
227b6667345SDavid Wu break;
228b6667345SDavid Wu case PHY_ADDR_T22:
229b6667345SDavid Wu rk630_phy_t22_config_init(phydev);
230b6667345SDavid Wu break;
231b6667345SDavid Wu default:
232b6667345SDavid Wu printf("Unsupported address for current phy: %d\n",
233b6667345SDavid Wu phydev->addr);
234b6667345SDavid Wu return -EINVAL;
235b6667345SDavid Wu }
236bf9a0f37SDavid Wu
237cf8faefbSDavid Wu genphy_config_aneg(phydev);
238bf9a0f37SDavid Wu return 0;
239bf9a0f37SDavid Wu }
240bf9a0f37SDavid Wu
241bf9a0f37SDavid Wu static struct phy_driver RK630_driver = {
242bf9a0f37SDavid Wu .name = "Rockchip RK630",
243bf9a0f37SDavid Wu .uid = RK630_PHY_ID,
244bf9a0f37SDavid Wu .mask = 0xffffff,
245bf9a0f37SDavid Wu .features = PHY_BASIC_FEATURES,
246bf9a0f37SDavid Wu .config = &rk630_phy_config_init,
247b6667345SDavid Wu .startup = &rk630_phy_startup,
248bf9a0f37SDavid Wu };
249bf9a0f37SDavid Wu
phy_rk630_init(void)250bf9a0f37SDavid Wu int phy_rk630_init(void)
251bf9a0f37SDavid Wu {
252bf9a0f37SDavid Wu phy_register(&RK630_driver);
253bf9a0f37SDavid Wu return 0;
254bf9a0f37SDavid Wu }
255