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
22enum {
23	REG_MDIO_CONTROL = 0x0,
24	REG_MDIO_ADDR_DATA = 0x2,
25	REG_MDIO_RX_DATA = 0x4,
26};
27
28static 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
39static 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
52static 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
63static 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
72static 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
81static 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
92static 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
108static int ihs_mdio_reset(struct mii_dev *bus)
109{
110	ihs_mdio_idle(bus);
111
112	return 0;
113}
114
115static 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
134static 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
147int 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