1ab06418bSAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2*1f2d109eSYang Shen /*
3baf6ee81SDavid Wu * drivers/net/phy/rockchip.c
4baf6ee81SDavid Wu *
5baf6ee81SDavid Wu * Driver for ROCKCHIP Ethernet PHYs
6baf6ee81SDavid Wu *
7baf6ee81SDavid Wu * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
8baf6ee81SDavid Wu *
9baf6ee81SDavid Wu * David Wu <david.wu@rock-chips.com>
10baf6ee81SDavid Wu */
11baf6ee81SDavid Wu
12baf6ee81SDavid Wu #include <linux/ethtool.h>
13baf6ee81SDavid Wu #include <linux/kernel.h>
14baf6ee81SDavid Wu #include <linux/module.h>
15baf6ee81SDavid Wu #include <linux/mii.h>
16baf6ee81SDavid Wu #include <linux/netdevice.h>
17baf6ee81SDavid Wu #include <linux/phy.h>
18baf6ee81SDavid Wu
19baf6ee81SDavid Wu #define INTERNAL_EPHY_ID 0x1234d400
20baf6ee81SDavid Wu
21baf6ee81SDavid Wu #define MII_INTERNAL_CTRL_STATUS 17
22baf6ee81SDavid Wu #define SMI_ADDR_TSTCNTL 20
23baf6ee81SDavid Wu #define SMI_ADDR_TSTREAD1 21
24baf6ee81SDavid Wu #define SMI_ADDR_TSTREAD2 22
25baf6ee81SDavid Wu #define SMI_ADDR_TSTWRITE 23
26baf6ee81SDavid Wu #define MII_SPECIAL_CONTROL_STATUS 31
27baf6ee81SDavid Wu
28baf6ee81SDavid Wu #define MII_AUTO_MDIX_EN BIT(7)
29baf6ee81SDavid Wu #define MII_MDIX_EN BIT(6)
30baf6ee81SDavid Wu
31baf6ee81SDavid Wu #define MII_SPEED_10 BIT(2)
32baf6ee81SDavid Wu #define MII_SPEED_100 BIT(3)
33baf6ee81SDavid Wu
34baf6ee81SDavid Wu #define TSTCNTL_RD (BIT(15) | BIT(10))
35baf6ee81SDavid Wu #define TSTCNTL_WR (BIT(14) | BIT(10))
36baf6ee81SDavid Wu
37baf6ee81SDavid Wu #define TSTMODE_ENABLE 0x400
38baf6ee81SDavid Wu #define TSTMODE_DISABLE 0x0
39baf6ee81SDavid Wu
40baf6ee81SDavid Wu #define WR_ADDR_A7CFG 0x18
41baf6ee81SDavid Wu
rockchip_init_tstmode(struct phy_device * phydev)42baf6ee81SDavid Wu static int rockchip_init_tstmode(struct phy_device *phydev)
43baf6ee81SDavid Wu {
44baf6ee81SDavid Wu int ret;
45baf6ee81SDavid Wu
46baf6ee81SDavid Wu /* Enable access to Analog and DSP register banks */
47baf6ee81SDavid Wu ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
48baf6ee81SDavid Wu if (ret)
49baf6ee81SDavid Wu return ret;
50baf6ee81SDavid Wu
51baf6ee81SDavid Wu ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
52baf6ee81SDavid Wu if (ret)
53baf6ee81SDavid Wu return ret;
54baf6ee81SDavid Wu
55baf6ee81SDavid Wu return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
56baf6ee81SDavid Wu }
57baf6ee81SDavid Wu
rockchip_close_tstmode(struct phy_device * phydev)58baf6ee81SDavid Wu static int rockchip_close_tstmode(struct phy_device *phydev)
59baf6ee81SDavid Wu {
60baf6ee81SDavid Wu /* Back to basic register bank */
61baf6ee81SDavid Wu return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
62baf6ee81SDavid Wu }
63baf6ee81SDavid Wu
rockchip_integrated_phy_analog_init(struct phy_device * phydev)64baf6ee81SDavid Wu static int rockchip_integrated_phy_analog_init(struct phy_device *phydev)
65baf6ee81SDavid Wu {
66baf6ee81SDavid Wu int ret;
67baf6ee81SDavid Wu
68baf6ee81SDavid Wu ret = rockchip_init_tstmode(phydev);
69baf6ee81SDavid Wu if (ret)
70baf6ee81SDavid Wu return ret;
71baf6ee81SDavid Wu
72baf6ee81SDavid Wu /*
73baf6ee81SDavid Wu * Adjust tx amplitude to make sginal better,
74baf6ee81SDavid Wu * the default value is 0x8.
75baf6ee81SDavid Wu */
76baf6ee81SDavid Wu ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB);
77baf6ee81SDavid Wu if (ret)
78baf6ee81SDavid Wu return ret;
79baf6ee81SDavid Wu ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG);
80baf6ee81SDavid Wu if (ret)
81baf6ee81SDavid Wu return ret;
82baf6ee81SDavid Wu
83baf6ee81SDavid Wu return rockchip_close_tstmode(phydev);
84baf6ee81SDavid Wu }
85baf6ee81SDavid Wu
rockchip_integrated_phy_config_init(struct phy_device * phydev)86baf6ee81SDavid Wu static int rockchip_integrated_phy_config_init(struct phy_device *phydev)
87baf6ee81SDavid Wu {
88baf6ee81SDavid Wu int val, ret;
89baf6ee81SDavid Wu
90baf6ee81SDavid Wu /*
91baf6ee81SDavid Wu * The auto MIDX has linked problem on some board,
92baf6ee81SDavid Wu * workround to disable auto MDIX.
93baf6ee81SDavid Wu */
94baf6ee81SDavid Wu val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
95baf6ee81SDavid Wu if (val < 0)
96baf6ee81SDavid Wu return val;
97baf6ee81SDavid Wu val &= ~MII_AUTO_MDIX_EN;
98baf6ee81SDavid Wu ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
99baf6ee81SDavid Wu if (ret)
100baf6ee81SDavid Wu return ret;
101baf6ee81SDavid Wu
102baf6ee81SDavid Wu return rockchip_integrated_phy_analog_init(phydev);
103baf6ee81SDavid Wu }
104baf6ee81SDavid Wu
rockchip_link_change_notify(struct phy_device * phydev)105baf6ee81SDavid Wu static void rockchip_link_change_notify(struct phy_device *phydev)
106baf6ee81SDavid Wu {
107baf6ee81SDavid Wu /*
108baf6ee81SDavid Wu * If mode switch happens from 10BT to 100BT, all DSP/AFE
109baf6ee81SDavid Wu * registers are set to default values. So any AFE/DSP
110baf6ee81SDavid Wu * registers have to be re-initialized in this case.
111baf6ee81SDavid Wu */
1125c5f626bSHeiner Kallweit if (phydev->state == PHY_RUNNING && phydev->speed == SPEED_100) {
113baf6ee81SDavid Wu int ret = rockchip_integrated_phy_analog_init(phydev);
1145c5f626bSHeiner Kallweit
115baf6ee81SDavid Wu if (ret)
116baf6ee81SDavid Wu phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n",
117baf6ee81SDavid Wu ret);
118baf6ee81SDavid Wu }
119baf6ee81SDavid Wu }
120baf6ee81SDavid Wu
rockchip_set_polarity(struct phy_device * phydev,int polarity)121baf6ee81SDavid Wu static int rockchip_set_polarity(struct phy_device *phydev, int polarity)
122baf6ee81SDavid Wu {
123baf6ee81SDavid Wu int reg, err, val;
124baf6ee81SDavid Wu
125baf6ee81SDavid Wu /* get the current settings */
126baf6ee81SDavid Wu reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
127baf6ee81SDavid Wu if (reg < 0)
128baf6ee81SDavid Wu return reg;
129baf6ee81SDavid Wu
130baf6ee81SDavid Wu reg &= ~MII_AUTO_MDIX_EN;
131baf6ee81SDavid Wu val = reg;
132baf6ee81SDavid Wu switch (polarity) {
133baf6ee81SDavid Wu case ETH_TP_MDI:
134baf6ee81SDavid Wu val &= ~MII_MDIX_EN;
135baf6ee81SDavid Wu break;
136baf6ee81SDavid Wu case ETH_TP_MDI_X:
137baf6ee81SDavid Wu val |= MII_MDIX_EN;
138baf6ee81SDavid Wu break;
139baf6ee81SDavid Wu case ETH_TP_MDI_AUTO:
140baf6ee81SDavid Wu case ETH_TP_MDI_INVALID:
141baf6ee81SDavid Wu default:
142baf6ee81SDavid Wu return 0;
143baf6ee81SDavid Wu }
144baf6ee81SDavid Wu
145baf6ee81SDavid Wu if (val != reg) {
146baf6ee81SDavid Wu /* Set the new polarity value in the register */
147baf6ee81SDavid Wu err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
148baf6ee81SDavid Wu if (err)
149baf6ee81SDavid Wu return err;
150baf6ee81SDavid Wu }
151baf6ee81SDavid Wu
152baf6ee81SDavid Wu return 0;
153baf6ee81SDavid Wu }
154baf6ee81SDavid Wu
rockchip_config_aneg(struct phy_device * phydev)155baf6ee81SDavid Wu static int rockchip_config_aneg(struct phy_device *phydev)
156baf6ee81SDavid Wu {
157baf6ee81SDavid Wu int err;
158baf6ee81SDavid Wu
159baf6ee81SDavid Wu err = rockchip_set_polarity(phydev, phydev->mdix);
160baf6ee81SDavid Wu if (err < 0)
161baf6ee81SDavid Wu return err;
162baf6ee81SDavid Wu
163baf6ee81SDavid Wu return genphy_config_aneg(phydev);
164baf6ee81SDavid Wu }
165baf6ee81SDavid Wu
rockchip_phy_resume(struct phy_device * phydev)166baf6ee81SDavid Wu static int rockchip_phy_resume(struct phy_device *phydev)
167baf6ee81SDavid Wu {
168baf6ee81SDavid Wu genphy_resume(phydev);
169baf6ee81SDavid Wu
170baf6ee81SDavid Wu return rockchip_integrated_phy_config_init(phydev);
171baf6ee81SDavid Wu }
172baf6ee81SDavid Wu
173baf6ee81SDavid Wu static struct phy_driver rockchip_phy_driver[] = {
174baf6ee81SDavid Wu {
175baf6ee81SDavid Wu .phy_id = INTERNAL_EPHY_ID,
176baf6ee81SDavid Wu .phy_id_mask = 0xfffffff0,
177baf6ee81SDavid Wu .name = "Rockchip integrated EPHY",
178dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */
179baf6ee81SDavid Wu .flags = 0,
180baf6ee81SDavid Wu .link_change_notify = rockchip_link_change_notify,
181baf6ee81SDavid Wu .soft_reset = genphy_soft_reset,
182baf6ee81SDavid Wu .config_init = rockchip_integrated_phy_config_init,
183baf6ee81SDavid Wu .config_aneg = rockchip_config_aneg,
184baf6ee81SDavid Wu .suspend = genphy_suspend,
185baf6ee81SDavid Wu .resume = rockchip_phy_resume,
186baf6ee81SDavid Wu },
187baf6ee81SDavid Wu };
188baf6ee81SDavid Wu
189baf6ee81SDavid Wu module_phy_driver(rockchip_phy_driver);
190baf6ee81SDavid Wu
191baf6ee81SDavid Wu static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = {
192baf6ee81SDavid Wu { INTERNAL_EPHY_ID, 0xfffffff0 },
193baf6ee81SDavid Wu { }
194baf6ee81SDavid Wu };
195baf6ee81SDavid Wu
196baf6ee81SDavid Wu MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl);
197baf6ee81SDavid Wu
198baf6ee81SDavid Wu MODULE_AUTHOR("David Wu <david.wu@rock-chips.com>");
199baf6ee81SDavid Wu MODULE_DESCRIPTION("Rockchip Ethernet PHY driver");
200ab06418bSAndrew Lunn MODULE_LICENSE("GPL");
201