1 /*
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Description: hieth mii operation
18 */
19
20
21 #include "hieth.h"
22 #include "mdio.h"
23 #include "mii_drv.h"
24 #include <config.h>
25
26 /* MDIO Bus Interface */
hieth_mdiobus_read(struct mii_dev * bus,int addr,int devad,int reg)27 static int hieth_mdiobus_read(struct mii_dev *bus, int addr, int devad, int reg)
28 {
29 struct hieth_netdev_local *ld = (struct hieth_netdev_local *)bus->priv;
30
31 return hieth_mdio_read(ld, addr, reg);
32 }
33
hieth_mdiobus_write(struct mii_dev * bus,int addr,int devad,int reg,u16 value)34 static int hieth_mdiobus_write(struct mii_dev *bus, int addr, int devad,
35 int reg, u16 value)
36 {
37 struct hieth_netdev_local *ld = (struct hieth_netdev_local *)bus->priv;
38
39 hieth_mdio_write(ld, addr, reg, value);
40
41 return 0;
42 }
43
44 #define PHY_ID_KSZ8051 0x00221550
45 #define PHY_ID_KSZ8081 0x00221560
46 #define PHY_ID_MASK 0xFFFFFFF0
47
get_fephy_id(const char * devname,unsigned char phyaddr,u32 * phy_id)48 static bool get_fephy_id(const char *devname, unsigned char phyaddr, u32 *phy_id)
49 {
50 u16 id1 = 0;
51 u16 id2 = 0;
52
53 if (miiphy_read(devname, phyaddr, MII_PHYSID1, &id1)) {
54 printf("%s,%d:PHY_PHYIDR1 read failed!\n", __func__, __LINE__);
55 return false;
56 }
57 if (miiphy_read(devname, phyaddr, MII_PHYSID2, &id2)) {
58 printf("%s,%d:PHY_PHYIDR2 read failed!\n", __func__, __LINE__);
59 return false;
60 }
61
62 *phy_id = (id1 & 0xffff) << 16; /* high 16 bit */
63 *phy_id |= (id2 & 0xffff);
64
65 /* If the phy_id is all Fs, there is no device there */
66 if (*phy_id == 0xffffffff || *phy_id == 0 || *phy_id == 0xFFFF || *phy_id == 0xFFFF0000) {
67 return false;
68 }
69
70 return true;
71 }
72
phy_detected(const char * devname,unsigned char phyaddr)73 bool phy_detected(const char *devname, unsigned char phyaddr)
74 {
75 u32 phy_id = 0;
76
77 if (!get_fephy_id(devname, phyaddr, &phy_id)) return false;
78
79 /* run this at RMII mode */
80 if (HIETH_MII_RMII_MODE_U == 1) {
81 /* PHY-KSZ8051RNL */
82 if ((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8051) {
83 unsigned short reg = 0;
84
85 if (miiphy_read(devname, phyaddr, 0x1F, ®)) {
86 printf("PHY 0x1F read failed\n");
87 return false;
88 }
89 reg |= bit(7); /* bit7:set phy RMII 50MHz clk; */
90 if (miiphy_write(devname, phyaddr, 0x1F, reg)) {
91 printf("PHY 0x1F write failed\n");
92 return false;
93 }
94
95 if (miiphy_read(devname, phyaddr, 0x16, ®)) {
96 printf("PHY 0x16 read failed\n");
97 return false;
98 }
99 reg |= bit(1); /* set phy RMII override; */
100 if (miiphy_write(devname, phyaddr, 0x16, reg)) {
101 printf("PHY 0x16 write failed\n");
102 return false;
103 }
104 }
105
106 /* PHY-KSZ8081 */
107 if ((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8081) {
108 unsigned short val = 0;
109
110 if (miiphy_read(devname, phyaddr, 0x1F, &val) != 0) {
111 printf("PHY 0x1F read failed\n");
112 return false;
113 };
114 val |= bit(7); /* bit7:set phy RMII 50MHz clk; */
115 if (miiphy_write(devname, phyaddr, 0x1F, val) != 0) {
116 printf("PHY 0x1F write failed\n");
117 return false;
118 }
119 }
120 }
121 return true;
122 }
123
124 static int g_mdio_registered;
125
hieth_mdiobus_driver_init(struct hieth_netdev_local * ld)126 int hieth_mdiobus_driver_init(struct hieth_netdev_local *ld)
127 {
128 memset(ld->mii_name, 0, MAX_PHY_NAME_LEN);
129 snprintf(ld->mii_name, MAX_PHY_NAME_LEN, "mii_hieth");
130 ld->mdio_frqdiv = ETH_MDIO_FRQDIV;
131 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
132 if (!g_mdio_registered) {
133 struct mii_dev *bus = mdio_alloc();
134
135 if (!bus) {
136 printf("Failed to allocate MDIO bus\n");
137 return -ENOMEM;
138 }
139
140 bus->priv = ld;
141 bus->read = hieth_mdiobus_read;
142 bus->write = hieth_mdiobus_write;
143 snprintf(bus->name, sizeof(bus->name), ld->mii_name);
144
145 hieth_mdio_init(ld);
146
147 if (mdio_register(bus)) {
148 mdio_free(bus);
149 return -1;
150 }
151
152 miiphy_set_current_dev(ld->mii_name);
153 g_mdio_registered = 1;
154 }
155 #endif
156 return 0;
157 }
158
hieth_mdiobus_driver_exit(struct hieth_netdev_local * ld)159 void hieth_mdiobus_driver_exit(struct hieth_netdev_local *ld)
160 {
161 /* add this to avoid the first time to use eth will print 'No such device: XXXXX' message. */
162 if (!miiphy_get_current_dev()) {
163 return;
164 }
165
166 hieth_mdio_exit(ld);
167 }
168