1*cf51737fSDavid Wu // SPDX-License-Identifier: GPL-2.0+
2*cf51737fSDavid Wu /**
3*cf51737fSDavid Wu *
4*cf51737fSDavid Wu * Driver for ROCKCHIP Fast Ethernet PHYs
5*cf51737fSDavid Wu *
6*cf51737fSDavid Wu * Copyright (c) 2025, Rockchip Electronics Co., Ltd
7*cf51737fSDavid Wu *
8*cf51737fSDavid Wu * David Wu <david.wu@rock-chips.com>
9*cf51737fSDavid Wu *
10*cf51737fSDavid Wu * This program is free software; you can redistribute it and/or modify
11*cf51737fSDavid Wu * it under the terms of the GNU General Public License as published by
12*cf51737fSDavid Wu * the Free Software Foundation; either version 2 of the License, or
13*cf51737fSDavid Wu * (at your option) any later version.
14*cf51737fSDavid Wu *
15*cf51737fSDavid Wu */
16*cf51737fSDavid Wu
17*cf51737fSDavid Wu #include <config.h>
18*cf51737fSDavid Wu #include <common.h>
19*cf51737fSDavid Wu #include <misc.h>
20*cf51737fSDavid Wu #include <phy.h>
21*cf51737fSDavid Wu
22*cf51737fSDavid Wu #define ROCKCHIP_FEPHY_ID 0x06808101
23*cf51737fSDavid Wu
24*cf51737fSDavid Wu #define MII_INTERNAL_CTRL_STATUS 17
25*cf51737fSDavid Wu #define SMI_ADDR_CFGCNTL 20
26*cf51737fSDavid Wu #define SMI_ADDR_TSTREAD1 21
27*cf51737fSDavid Wu #define SMI_ADDR_TSTREAD2 22
28*cf51737fSDavid Wu #define SMI_ADDR_TSTWRITE 23
29*cf51737fSDavid Wu #define MII_LED_CTRL 25
30*cf51737fSDavid Wu #define MII_INT_STATUS 29
31*cf51737fSDavid Wu #define MII_INT_MASK 30
32*cf51737fSDavid Wu #define MII_SPECIAL_CONTROL_STATUS 31
33*cf51737fSDavid Wu
34*cf51737fSDavid Wu #define MII_AUTO_MDIX_EN BIT(7)
35*cf51737fSDavid Wu #define MII_MDIX_EN BIT(6)
36*cf51737fSDavid Wu
37*cf51737fSDavid Wu #define MII_SPEED_10 BIT(2)
38*cf51737fSDavid Wu #define MII_SPEED_100 BIT(3)
39*cf51737fSDavid Wu
40*cf51737fSDavid Wu #define CFGCNTL_WRITE_ADDR 0
41*cf51737fSDavid Wu #define CFGCNTL_READ_ADDR 5
42*cf51737fSDavid Wu #define CFGCNTL_GROUP_SEL 11
43*cf51737fSDavid Wu #define CFGCNTL_RD (BIT(15) | BIT(10))
44*cf51737fSDavid Wu #define CFGCNTL_WR (BIT(14) | BIT(10))
45*cf51737fSDavid Wu
46*cf51737fSDavid Wu #define CFGCNTL_WRITE(group, reg) (CFGCNTL_WR | ((group) << CFGCNTL_GROUP_SEL) \
47*cf51737fSDavid Wu | ((reg) << CFGCNTL_WRITE_ADDR))
48*cf51737fSDavid Wu #define CFGCNTL_READ(group, reg) (CFGCNTL_RD | ((group) << CFGCNTL_GROUP_SEL) \
49*cf51737fSDavid Wu | ((reg) << CFGCNTL_READ_ADDR))
50*cf51737fSDavid Wu
51*cf51737fSDavid Wu #define GAIN_PRE GENMASK(5, 2)
52*cf51737fSDavid Wu #define WR_ADDR_A7CFG 0x18
53*cf51737fSDavid Wu
54*cf51737fSDavid Wu enum {
55*cf51737fSDavid Wu GROUP_CFG0 = 0,
56*cf51737fSDavid Wu GROUP_WOL,
57*cf51737fSDavid Wu GROUP_CFG0_READ,
58*cf51737fSDavid Wu GROUP_BIST,
59*cf51737fSDavid Wu GROUP_AFE,
60*cf51737fSDavid Wu GROUP_CFG1
61*cf51737fSDavid Wu };
62*cf51737fSDavid Wu
rockchip_fephy_group_read(struct phy_device * phydev,u8 group,u32 reg)63*cf51737fSDavid Wu static int rockchip_fephy_group_read(struct phy_device *phydev, u8 group, u32 reg)
64*cf51737fSDavid Wu {
65*cf51737fSDavid Wu int ret;
66*cf51737fSDavid Wu
67*cf51737fSDavid Wu ret = phy_write(phydev, MDIO_DEVAD_NONE, SMI_ADDR_CFGCNTL, CFGCNTL_READ(group, reg));
68*cf51737fSDavid Wu if (ret)
69*cf51737fSDavid Wu return ret;
70*cf51737fSDavid Wu
71*cf51737fSDavid Wu if (group)
72*cf51737fSDavid Wu return phy_read(phydev, MDIO_DEVAD_NONE, SMI_ADDR_TSTREAD1);
73*cf51737fSDavid Wu else
74*cf51737fSDavid Wu return (phy_read(phydev, MDIO_DEVAD_NONE, SMI_ADDR_TSTREAD1) |
75*cf51737fSDavid Wu (phy_read(phydev, MDIO_DEVAD_NONE, SMI_ADDR_TSTREAD2) << 16));
76*cf51737fSDavid Wu }
77*cf51737fSDavid Wu
rockchip_fephy_group_write(struct phy_device * phydev,u8 group,u32 reg,u16 val)78*cf51737fSDavid Wu static int rockchip_fephy_group_write(struct phy_device *phydev, u8 group,
79*cf51737fSDavid Wu u32 reg, u16 val)
80*cf51737fSDavid Wu {
81*cf51737fSDavid Wu int ret;
82*cf51737fSDavid Wu
83*cf51737fSDavid Wu ret = phy_write(phydev, MDIO_DEVAD_NONE, SMI_ADDR_TSTWRITE, val);
84*cf51737fSDavid Wu if (ret)
85*cf51737fSDavid Wu return ret;
86*cf51737fSDavid Wu
87*cf51737fSDavid Wu return phy_write(phydev, MDIO_DEVAD_NONE, SMI_ADDR_CFGCNTL, CFGCNTL_WRITE(group, reg));
88*cf51737fSDavid Wu }
89*cf51737fSDavid Wu
rockchip_fephy_startup(struct phy_device * phydev)90*cf51737fSDavid Wu static int rockchip_fephy_startup(struct phy_device *phydev)
91*cf51737fSDavid Wu {
92*cf51737fSDavid Wu int ret;
93*cf51737fSDavid Wu
94*cf51737fSDavid Wu /* Read the Status (2x to make sure link is right) */
95*cf51737fSDavid Wu ret = genphy_update_link(phydev);
96*cf51737fSDavid Wu if (ret)
97*cf51737fSDavid Wu return ret;
98*cf51737fSDavid Wu
99*cf51737fSDavid Wu /* Read the Status (2x to make sure link is right) */
100*cf51737fSDavid Wu phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
101*cf51737fSDavid Wu
102*cf51737fSDavid Wu return genphy_parse_link(phydev);
103*cf51737fSDavid Wu }
104*cf51737fSDavid Wu
rockchip_fephy_config_init(struct phy_device * phydev)105*cf51737fSDavid Wu static int rockchip_fephy_config_init(struct phy_device *phydev)
106*cf51737fSDavid Wu {
107*cf51737fSDavid Wu int ret;
108*cf51737fSDavid Wu
109*cf51737fSDavid Wu /* LED Control, default:0x7f */
110*cf51737fSDavid Wu ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LED_CTRL, 0x7aa);
111*cf51737fSDavid Wu if (ret)
112*cf51737fSDavid Wu return ret;
113*cf51737fSDavid Wu
114*cf51737fSDavid Wu /* off-energy level0 threshold */
115*cf51737fSDavid Wu ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6664);
116*cf51737fSDavid Wu if (ret)
117*cf51737fSDavid Wu return ret;
118*cf51737fSDavid Wu
119*cf51737fSDavid Wu /* 100M amplitude control */
120*cf51737fSDavid Wu ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x18, 0xc);
121*cf51737fSDavid Wu if (ret)
122*cf51737fSDavid Wu return ret;
123*cf51737fSDavid Wu
124*cf51737fSDavid Wu /* 24M */
125*cf51737fSDavid Wu {
126*cf51737fSDavid Wu int sel;
127*cf51737fSDavid Wu
128*cf51737fSDavid Wu /* pll cp cur sel */
129*cf51737fSDavid Wu sel = rockchip_fephy_group_read(phydev, GROUP_AFE, 0x3);
130*cf51737fSDavid Wu if (sel < 0)
131*cf51737fSDavid Wu return sel;
132*cf51737fSDavid Wu ret = rockchip_fephy_group_write(phydev, GROUP_AFE, 0x3, sel | 0x2);
133*cf51737fSDavid Wu if (ret)
134*cf51737fSDavid Wu return ret;
135*cf51737fSDavid Wu
136*cf51737fSDavid Wu /* pll lpf res sel */
137*cf51737fSDavid Wu ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x1a, 0x6);
138*cf51737fSDavid Wu if (ret)
139*cf51737fSDavid Wu return ret;
140*cf51737fSDavid Wu }
141*cf51737fSDavid Wu
142*cf51737fSDavid Wu return ret;
143*cf51737fSDavid Wu }
144*cf51737fSDavid Wu
145*cf51737fSDavid Wu static struct phy_driver rockchip_fephy_driver = {
146*cf51737fSDavid Wu .name = "Rockchip FEPHY",
147*cf51737fSDavid Wu .uid = ROCKCHIP_FEPHY_ID,
148*cf51737fSDavid Wu .mask = 0xfffffff,
149*cf51737fSDavid Wu .features = PHY_BASIC_FEATURES,
150*cf51737fSDavid Wu .config = &rockchip_fephy_config_init,
151*cf51737fSDavid Wu .startup = &rockchip_fephy_startup,
152*cf51737fSDavid Wu };
153*cf51737fSDavid Wu
phy_rockchip_fephy_init(void)154*cf51737fSDavid Wu int phy_rockchip_fephy_init(void)
155*cf51737fSDavid Wu {
156*cf51737fSDavid Wu phy_register(&rockchip_fephy_driver);
157*cf51737fSDavid Wu return 0;
158*cf51737fSDavid Wu }
159