1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Aspeed MDIO driver 4 * 5 * (C) Copyright 2021 Aspeed Technology Inc. 6 * 7 * This file is inspired from the Linux kernel driver drivers/net/phy/mdio-aspeed.c 8 */ 9 10#include <dm.h> 11#include <log.h> 12#include <miiphy.h> 13#include <net.h> 14#include <reset.h> 15#include <linux/bitops.h> 16#include <linux/bitfield.h> 17#include <linux/io.h> 18#include <linux/iopoll.h> 19 20#define ASPEED_MDIO_CTRL 0x0 21#define ASPEED_MDIO_CTRL_FIRE BIT(31) 22#define ASPEED_MDIO_CTRL_ST BIT(28) 23#define ASPEED_MDIO_CTRL_ST_C45 0 24#define ASPEED_MDIO_CTRL_ST_C22 1 25#define ASPEED_MDIO_CTRL_OP GENMASK(27, 26) 26#define MDIO_C22_OP_WRITE 0b01 27#define MDIO_C22_OP_READ 0b10 28#define ASPEED_MDIO_CTRL_PHYAD GENMASK(25, 21) 29#define ASPEED_MDIO_CTRL_REGAD GENMASK(20, 16) 30#define ASPEED_MDIO_CTRL_MIIWDATA GENMASK(15, 0) 31 32#define ASPEED_MDIO_DATA 0x4 33#define ASPEED_MDIO_DATA_MDC_THRES GENMASK(31, 24) 34#define ASPEED_MDIO_DATA_MDIO_EDGE BIT(23) 35#define ASPEED_MDIO_DATA_MDIO_LATCH GENMASK(22, 20) 36#define ASPEED_MDIO_DATA_IDLE BIT(16) 37#define ASPEED_MDIO_DATA_MIIRDATA GENMASK(15, 0) 38 39#define ASPEED_MDIO_TIMEOUT_US 1000 40 41struct aspeed_mdio_priv { 42 void *base; 43}; 44 45static int aspeed_mdio_read(struct udevice *mdio_dev, int addr, int devad, int reg) 46{ 47 struct aspeed_mdio_priv *priv = dev_get_priv(mdio_dev); 48 u32 ctrl; 49 u32 data; 50 int rc; 51 52 if (devad != MDIO_DEVAD_NONE) 53 return -EOPNOTSUPP; 54 55 ctrl = ASPEED_MDIO_CTRL_FIRE 56 | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22) 57 | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_READ) 58 | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr) 59 | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, reg); 60 61 writel(ctrl, priv->base + ASPEED_MDIO_CTRL); 62 63 rc = readl_poll_timeout(priv->base + ASPEED_MDIO_DATA, data, 64 data & ASPEED_MDIO_DATA_IDLE, 65 ASPEED_MDIO_TIMEOUT_US); 66 67 if (rc < 0) 68 return rc; 69 70 return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data); 71} 72 73static int aspeed_mdio_write(struct udevice *mdio_dev, int addr, int devad, int reg, u16 val) 74{ 75 struct aspeed_mdio_priv *priv = dev_get_priv(mdio_dev); 76 u32 ctrl; 77 78 if (devad != MDIO_DEVAD_NONE) 79 return -EOPNOTSUPP; 80 81 ctrl = ASPEED_MDIO_CTRL_FIRE 82 | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22) 83 | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_WRITE) 84 | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr) 85 | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, reg) 86 | FIELD_PREP(ASPEED_MDIO_CTRL_MIIWDATA, val); 87 88 writel(ctrl, priv->base + ASPEED_MDIO_CTRL); 89 90 return readl_poll_timeout(priv->base + ASPEED_MDIO_CTRL, ctrl, 91 !(ctrl & ASPEED_MDIO_CTRL_FIRE), 92 ASPEED_MDIO_TIMEOUT_US); 93} 94 95static const struct mdio_ops aspeed_mdio_ops = { 96 .read = aspeed_mdio_read, 97 .write = aspeed_mdio_write, 98}; 99 100static int aspeed_mdio_probe(struct udevice *dev) 101{ 102 struct aspeed_mdio_priv *priv = dev_get_priv(dev); 103 struct reset_ctl reset_ctl; 104 int ret = 0; 105 106 priv->base = dev_read_addr_ptr(dev); 107 108 ret = reset_get_by_index(dev, 0, &reset_ctl); 109 reset_deassert(&reset_ctl); 110 111 return 0; 112} 113 114static const struct udevice_id aspeed_mdio_ids[] = { 115 { .compatible = "aspeed,ast2600-mdio" }, 116 { } 117}; 118 119U_BOOT_DRIVER(aspeed_mdio) = { 120 .name = "aspeed_mdio", 121 .id = UCLASS_MDIO, 122 .of_match = aspeed_mdio_ids, 123 .probe = aspeed_mdio_probe, 124 .ops = &aspeed_mdio_ops, 125 .plat_auto = sizeof(struct mdio_perdev_priv), 126 .priv_auto = sizeof(struct aspeed_mdio_priv), 127}; 128