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