1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2014
4  * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
5  */
6 
7 #include <linux/delay.h>
8 
9 #include <miiphy.h>
10 #ifdef CONFIG_GDSYS_LEGACY_DRIVERS
11 #include <gdsys_fpga.h>
12 #else
13 #include <fdtdec.h>
14 #include <dm.h>
15 #include <regmap.h>
16 #endif
17 
18 #include "ihs_mdio.h"
19 
20 #ifndef CONFIG_GDSYS_LEGACY_DRIVERS
21 enum {
22 	REG_MDIO_CONTROL = 0x0,
23 	REG_MDIO_ADDR_DATA = 0x2,
24 	REG_MDIO_RX_DATA = 0x4,
25 };
26 
read_reg(struct udevice * fpga,uint base,uint addr)27 static inline u16 read_reg(struct udevice *fpga, uint base, uint addr)
28 {
29 	struct regmap *map;
30 	u8 *ptr;
31 
32 	regmap_init_mem(dev_ofnode(fpga), &map);
33 	ptr = regmap_get_range(map, 0);
34 
35 	return in_le16((u16 *)(ptr + base + addr));
36 }
37 
write_reg(struct udevice * fpga,uint base,uint addr,u16 val)38 static inline void write_reg(struct udevice *fpga, uint base, uint addr,
39 			     u16 val)
40 {
41 	struct regmap *map;
42 	u8 *ptr;
43 
44 	regmap_init_mem(dev_ofnode(fpga), &map);
45 	ptr = regmap_get_range(map, 0);
46 
47 	out_le16((u16 *)(ptr + base + addr), val);
48 }
49 #endif
50 
read_control(struct ihs_mdio_info * info)51 static inline u16 read_control(struct ihs_mdio_info *info)
52 {
53 	u16 val;
54 #ifdef CONFIG_GDSYS_LEGACY_DRIVERS
55 	FPGA_GET_REG(info->fpga, mdio.control, &val);
56 #else
57 	val = read_reg(info->fpga, info->base, REG_MDIO_CONTROL);
58 #endif
59 	return val;
60 }
61 
write_control(struct ihs_mdio_info * info,u16 val)62 static inline void write_control(struct ihs_mdio_info *info, u16 val)
63 {
64 #ifdef CONFIG_GDSYS_LEGACY_DRIVERS
65 	FPGA_SET_REG(info->fpga, mdio.control, val);
66 #else
67 	write_reg(info->fpga, info->base, REG_MDIO_CONTROL, val);
68 #endif
69 }
70 
write_addr_data(struct ihs_mdio_info * info,u16 val)71 static inline void write_addr_data(struct ihs_mdio_info *info, u16 val)
72 {
73 #ifdef CONFIG_GDSYS_LEGACY_DRIVERS
74 	FPGA_SET_REG(info->fpga, mdio.address_data, val);
75 #else
76 	write_reg(info->fpga, info->base, REG_MDIO_ADDR_DATA, val);
77 #endif
78 }
79 
read_rx_data(struct ihs_mdio_info * info)80 static inline u16 read_rx_data(struct ihs_mdio_info *info)
81 {
82 	u16 val;
83 #ifdef CONFIG_GDSYS_LEGACY_DRIVERS
84 	FPGA_GET_REG(info->fpga, mdio.rx_data, &val);
85 #else
86 	val = read_reg(info->fpga, info->base, REG_MDIO_RX_DATA);
87 #endif
88 	return val;
89 }
90 
ihs_mdio_idle(struct mii_dev * bus)91 static int ihs_mdio_idle(struct mii_dev *bus)
92 {
93 	struct ihs_mdio_info *info = bus->priv;
94 	u16 val;
95 	unsigned int ctr = 0;
96 
97 	do {
98 		val = read_control(info);
99 		udelay(100);
100 		if (ctr++ > 10)
101 			return -1;
102 	} while (!(val & (1 << 12)));
103 
104 	return 0;
105 }
106 
ihs_mdio_reset(struct mii_dev * bus)107 static int ihs_mdio_reset(struct mii_dev *bus)
108 {
109 	ihs_mdio_idle(bus);
110 
111 	return 0;
112 }
113 
ihs_mdio_read(struct mii_dev * bus,int addr,int dev_addr,int regnum)114 static int ihs_mdio_read(struct mii_dev *bus, int addr, int dev_addr,
115 			 int regnum)
116 {
117 	struct ihs_mdio_info *info = bus->priv;
118 	u16 val;
119 
120 	ihs_mdio_idle(bus);
121 
122 	write_control(info,
123 		      ((addr & 0x1f) << 5) | (regnum & 0x1f) | (2 << 10));
124 
125 	/* wait for rx data available */
126 	udelay(100);
127 
128 	val = read_rx_data(info);
129 
130 	return val;
131 }
132 
ihs_mdio_write(struct mii_dev * bus,int addr,int dev_addr,int regnum,u16 value)133 static int ihs_mdio_write(struct mii_dev *bus, int addr, int dev_addr,
134 			  int regnum, u16 value)
135 {
136 	struct ihs_mdio_info *info = bus->priv;
137 
138 	ihs_mdio_idle(bus);
139 
140 	write_addr_data(info, value);
141 	write_control(info, ((addr & 0x1f) << 5) | (regnum & 0x1f) | (1 << 10));
142 
143 	return 0;
144 }
145 
ihs_mdio_init(struct ihs_mdio_info * info)146 int ihs_mdio_init(struct ihs_mdio_info *info)
147 {
148 	struct mii_dev *bus = mdio_alloc();
149 
150 	if (!bus) {
151 		printf("Failed to allocate FSL MDIO bus\n");
152 		return -1;
153 	}
154 
155 	bus->read = ihs_mdio_read;
156 	bus->write = ihs_mdio_write;
157 	bus->reset = ihs_mdio_reset;
158 	strcpy(bus->name, info->name);
159 
160 	bus->priv = info;
161 
162 	return mdio_register(bus);
163 }
164