| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2014 |
| * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc |
| */ |
| |
| #include <common.h> |
| #include <linux/delay.h> |
| |
| #include <miiphy.h> |
| #ifdef CONFIG_GDSYS_LEGACY_DRIVERS |
| #include <gdsys_fpga.h> |
| #else |
| #include <fdtdec.h> |
| #include <dm.h> |
| #include <regmap.h> |
| #endif |
| |
| #include "ihs_mdio.h" |
| |
| #ifndef CONFIG_GDSYS_LEGACY_DRIVERS |
| enum { |
| REG_MDIO_CONTROL = 0x0, |
| REG_MDIO_ADDR_DATA = 0x2, |
| REG_MDIO_RX_DATA = 0x4, |
| }; |
| |
| static inline u16 read_reg(struct udevice *fpga, uint base, uint addr) |
| { |
| struct regmap *map; |
| u8 *ptr; |
| |
| regmap_init_mem(dev_ofnode(fpga), &map); |
| ptr = regmap_get_range(map, 0); |
| |
| return in_le16((u16 *)(ptr + base + addr)); |
| } |
| |
| static inline void write_reg(struct udevice *fpga, uint base, uint addr, |
| u16 val) |
| { |
| struct regmap *map; |
| u8 *ptr; |
| |
| regmap_init_mem(dev_ofnode(fpga), &map); |
| ptr = regmap_get_range(map, 0); |
| |
| out_le16((u16 *)(ptr + base + addr), val); |
| } |
| #endif |
| |
| static inline u16 read_control(struct ihs_mdio_info *info) |
| { |
| u16 val; |
| #ifdef CONFIG_GDSYS_LEGACY_DRIVERS |
| FPGA_GET_REG(info->fpga, mdio.control, &val); |
| #else |
| val = read_reg(info->fpga, info->base, REG_MDIO_CONTROL); |
| #endif |
| return val; |
| } |
| |
| static inline void write_control(struct ihs_mdio_info *info, u16 val) |
| { |
| #ifdef CONFIG_GDSYS_LEGACY_DRIVERS |
| FPGA_SET_REG(info->fpga, mdio.control, val); |
| #else |
| write_reg(info->fpga, info->base, REG_MDIO_CONTROL, val); |
| #endif |
| } |
| |
| static inline void write_addr_data(struct ihs_mdio_info *info, u16 val) |
| { |
| #ifdef CONFIG_GDSYS_LEGACY_DRIVERS |
| FPGA_SET_REG(info->fpga, mdio.address_data, val); |
| #else |
| write_reg(info->fpga, info->base, REG_MDIO_ADDR_DATA, val); |
| #endif |
| } |
| |
| static inline u16 read_rx_data(struct ihs_mdio_info *info) |
| { |
| u16 val; |
| #ifdef CONFIG_GDSYS_LEGACY_DRIVERS |
| FPGA_GET_REG(info->fpga, mdio.rx_data, &val); |
| #else |
| val = read_reg(info->fpga, info->base, REG_MDIO_RX_DATA); |
| #endif |
| return val; |
| } |
| |
| static int ihs_mdio_idle(struct mii_dev *bus) |
| { |
| struct ihs_mdio_info *info = bus->priv; |
| u16 val; |
| unsigned int ctr = 0; |
| |
| do { |
| val = read_control(info); |
| udelay(100); |
| if (ctr++ > 10) |
| return -1; |
| } while (!(val & (1 << 12))); |
| |
| return 0; |
| } |
| |
| static int ihs_mdio_reset(struct mii_dev *bus) |
| { |
| ihs_mdio_idle(bus); |
| |
| return 0; |
| } |
| |
| static int ihs_mdio_read(struct mii_dev *bus, int addr, int dev_addr, |
| int regnum) |
| { |
| struct ihs_mdio_info *info = bus->priv; |
| u16 val; |
| |
| ihs_mdio_idle(bus); |
| |
| write_control(info, |
| ((addr & 0x1f) << 5) | (regnum & 0x1f) | (2 << 10)); |
| |
| /* wait for rx data available */ |
| udelay(100); |
| |
| val = read_rx_data(info); |
| |
| return val; |
| } |
| |
| static int ihs_mdio_write(struct mii_dev *bus, int addr, int dev_addr, |
| int regnum, u16 value) |
| { |
| struct ihs_mdio_info *info = bus->priv; |
| |
| ihs_mdio_idle(bus); |
| |
| write_addr_data(info, value); |
| write_control(info, ((addr & 0x1f) << 5) | (regnum & 0x1f) | (1 << 10)); |
| |
| return 0; |
| } |
| |
| int ihs_mdio_init(struct ihs_mdio_info *info) |
| { |
| struct mii_dev *bus = mdio_alloc(); |
| |
| if (!bus) { |
| printf("Failed to allocate FSL MDIO bus\n"); |
| return -1; |
| } |
| |
| bus->read = ihs_mdio_read; |
| bus->write = ihs_mdio_write; |
| bus->reset = ihs_mdio_reset; |
| strcpy(bus->name, info->name); |
| |
| bus->priv = info; |
| |
| return mdio_register(bus); |
| } |