Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | // SPDX-License-Identifier: GPL-2.0+ /* * Qualcomm IPQ4019 MDIO driver * * Copyright (c) 2020 Sartura Ltd. * * Author: Luka Kovacic <luka.kovacic@sartura.hr> * Author: Robert Marko <robert.marko@sartura.hr> * * Based on Linux driver */ #include <asm/io.h> #include <dm.h> #include <errno.h> #include <linux/bitops.h> #include <linux/iopoll.h> #include <miiphy.h> #include <phy.h> #define MDIO_MODE_REG 0x40 #define MDIO_ADDR_REG 0x44 #define MDIO_DATA_WRITE_REG 0x48 #define MDIO_DATA_READ_REG 0x4c #define MDIO_CMD_REG 0x50 #define MDIO_CMD_ACCESS_BUSY BIT(16) #define MDIO_CMD_ACCESS_START BIT(8) #define MDIO_CMD_ACCESS_CODE_READ 0 #define MDIO_CMD_ACCESS_CODE_WRITE 1 /* 0 = Clause 22, 1 = Clause 45 */ #define MDIO_MODE_BIT BIT(8) #define IPQ4019_MDIO_TIMEOUT 10000 #define IPQ4019_MDIO_SLEEP 10 struct ipq4019_mdio_priv { phys_addr_t mdio_base; }; static int ipq4019_mdio_wait_busy(struct ipq4019_mdio_priv *priv) { unsigned int busy; return readl_poll_sleep_timeout(priv->mdio_base + MDIO_CMD_REG, busy, (busy & MDIO_CMD_ACCESS_BUSY) == 0, IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT); } int ipq4019_mdio_read(struct udevice *dev, int addr, int devad, int reg) { struct ipq4019_mdio_priv *priv = dev_get_priv(dev); unsigned int cmd; if (ipq4019_mdio_wait_busy(priv)) return -ETIMEDOUT; /* Issue the phy address and reg */ writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG); cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ; /* Issue read command */ writel(cmd, priv->mdio_base + MDIO_CMD_REG); /* Wait read complete */ if (ipq4019_mdio_wait_busy(priv)) return -ETIMEDOUT; /* Read and return data */ return readl(priv->mdio_base + MDIO_DATA_READ_REG); } int ipq4019_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) { struct ipq4019_mdio_priv *priv = dev_get_priv(dev); unsigned int cmd; if (ipq4019_mdio_wait_busy(priv)) return -ETIMEDOUT; /* Issue the phy addreass and reg */ writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG); /* Issue write data */ writel(val, priv->mdio_base + MDIO_DATA_WRITE_REG); cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE; /* Issue write command */ writel(cmd, priv->mdio_base + MDIO_CMD_REG); /* Wait for write complete */ if (ipq4019_mdio_wait_busy(priv)) return -ETIMEDOUT; return 0; } static const struct mdio_ops ipq4019_mdio_ops = { .read = ipq4019_mdio_read, .write = ipq4019_mdio_write, }; static int ipq4019_mdio_bind(struct udevice *dev) { if (ofnode_valid(dev_ofnode(dev))) device_set_name(dev, ofnode_get_name(dev_ofnode(dev))); return 0; } static int ipq4019_mdio_probe(struct udevice *dev) { struct ipq4019_mdio_priv *priv = dev_get_priv(dev); unsigned int data; priv->mdio_base = dev_read_addr(dev); if (priv->mdio_base == FDT_ADDR_T_NONE) return -EINVAL; /* Enter Clause 22 mode */ data = readl(priv->mdio_base + MDIO_MODE_REG); data &= ~MDIO_MODE_BIT; writel(data, priv->mdio_base + MDIO_MODE_REG); return 0; } static const struct udevice_id ipq4019_mdio_ids[] = { { .compatible = "qcom,ipq4019-mdio", }, { } }; U_BOOT_DRIVER(ipq4019_mdio) = { .name = "ipq4019_mdio", .id = UCLASS_MDIO, .of_match = ipq4019_mdio_ids, .bind = ipq4019_mdio_bind, .probe = ipq4019_mdio_probe, .ops = &ipq4019_mdio_ops, .priv_auto = sizeof(struct ipq4019_mdio_priv), }; |