Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.14-rc6 211 lines 5.0 kB view raw
1/* 2 * Copyright (C) 2015 Broadcom Corporation 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation version 2. 7 * 8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 9 * kind, whether express or implied; without even the implied warranty 10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14#include <linux/delay.h> 15#include <linux/io.h> 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/of.h> 19#include <linux/of_platform.h> 20#include <linux/of_mdio.h> 21#include <linux/phy.h> 22#include <linux/platform_device.h> 23#include <linux/sched.h> 24 25#define IPROC_GPHY_MDCDIV 0x1a 26 27#define MII_CTRL_OFFSET 0x000 28 29#define MII_CTRL_DIV_SHIFT 0 30#define MII_CTRL_PRE_SHIFT 7 31#define MII_CTRL_BUSY_SHIFT 8 32 33#define MII_DATA_OFFSET 0x004 34#define MII_DATA_MASK 0xffff 35#define MII_DATA_TA_SHIFT 16 36#define MII_DATA_TA_VAL 2 37#define MII_DATA_RA_SHIFT 18 38#define MII_DATA_PA_SHIFT 23 39#define MII_DATA_OP_SHIFT 28 40#define MII_DATA_OP_WRITE 1 41#define MII_DATA_OP_READ 2 42#define MII_DATA_SB_SHIFT 30 43 44struct iproc_mdio_priv { 45 struct mii_bus *mii_bus; 46 void __iomem *base; 47}; 48 49static inline int iproc_mdio_wait_for_idle(void __iomem *base) 50{ 51 u32 val; 52 unsigned int timeout = 1000; /* loop for 1s */ 53 54 do { 55 val = readl(base + MII_CTRL_OFFSET); 56 if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0) 57 return 0; 58 59 usleep_range(1000, 2000); 60 } while (timeout--); 61 62 return -ETIMEDOUT; 63} 64 65static inline void iproc_mdio_config_clk(void __iomem *base) 66{ 67 u32 val; 68 69 val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) | 70 BIT(MII_CTRL_PRE_SHIFT); 71 writel(val, base + MII_CTRL_OFFSET); 72} 73 74static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg) 75{ 76 struct iproc_mdio_priv *priv = bus->priv; 77 u32 cmd; 78 int rc; 79 80 rc = iproc_mdio_wait_for_idle(priv->base); 81 if (rc) 82 return rc; 83 84 /* Prepare the read operation */ 85 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | 86 (reg << MII_DATA_RA_SHIFT) | 87 (phy_id << MII_DATA_PA_SHIFT) | 88 BIT(MII_DATA_SB_SHIFT) | 89 (MII_DATA_OP_READ << MII_DATA_OP_SHIFT); 90 91 writel(cmd, priv->base + MII_DATA_OFFSET); 92 93 rc = iproc_mdio_wait_for_idle(priv->base); 94 if (rc) 95 return rc; 96 97 cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK; 98 99 return cmd; 100} 101 102static int iproc_mdio_write(struct mii_bus *bus, int phy_id, 103 int reg, u16 val) 104{ 105 struct iproc_mdio_priv *priv = bus->priv; 106 u32 cmd; 107 int rc; 108 109 rc = iproc_mdio_wait_for_idle(priv->base); 110 if (rc) 111 return rc; 112 113 /* Prepare the write operation */ 114 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | 115 (reg << MII_DATA_RA_SHIFT) | 116 (phy_id << MII_DATA_PA_SHIFT) | 117 BIT(MII_DATA_SB_SHIFT) | 118 (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) | 119 ((u32)(val) & MII_DATA_MASK); 120 121 writel(cmd, priv->base + MII_DATA_OFFSET); 122 123 rc = iproc_mdio_wait_for_idle(priv->base); 124 if (rc) 125 return rc; 126 127 return 0; 128} 129 130static int iproc_mdio_probe(struct platform_device *pdev) 131{ 132 struct iproc_mdio_priv *priv; 133 struct mii_bus *bus; 134 struct resource *res; 135 int rc; 136 137 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 138 if (!priv) 139 return -ENOMEM; 140 141 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 142 priv->base = devm_ioremap_resource(&pdev->dev, res); 143 if (IS_ERR(priv->base)) { 144 dev_err(&pdev->dev, "failed to ioremap register\n"); 145 return PTR_ERR(priv->base); 146 } 147 148 priv->mii_bus = mdiobus_alloc(); 149 if (!priv->mii_bus) { 150 dev_err(&pdev->dev, "MDIO bus alloc failed\n"); 151 return -ENOMEM; 152 } 153 154 bus = priv->mii_bus; 155 bus->priv = priv; 156 bus->name = "iProc MDIO bus"; 157 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); 158 bus->parent = &pdev->dev; 159 bus->read = iproc_mdio_read; 160 bus->write = iproc_mdio_write; 161 162 iproc_mdio_config_clk(priv->base); 163 164 rc = of_mdiobus_register(bus, pdev->dev.of_node); 165 if (rc) { 166 dev_err(&pdev->dev, "MDIO bus registration failed\n"); 167 goto err_iproc_mdio; 168 } 169 170 platform_set_drvdata(pdev, priv); 171 172 dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base); 173 174 return 0; 175 176err_iproc_mdio: 177 mdiobus_free(bus); 178 return rc; 179} 180 181static int iproc_mdio_remove(struct platform_device *pdev) 182{ 183 struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); 184 185 mdiobus_unregister(priv->mii_bus); 186 mdiobus_free(priv->mii_bus); 187 188 return 0; 189} 190 191static const struct of_device_id iproc_mdio_of_match[] = { 192 { .compatible = "brcm,iproc-mdio", }, 193 { /* sentinel */ }, 194}; 195MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); 196 197static struct platform_driver iproc_mdio_driver = { 198 .driver = { 199 .name = "iproc-mdio", 200 .of_match_table = iproc_mdio_of_match, 201 }, 202 .probe = iproc_mdio_probe, 203 .remove = iproc_mdio_remove, 204}; 205 206module_platform_driver(iproc_mdio_driver); 207 208MODULE_AUTHOR("Broadcom Corporation"); 209MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller"); 210MODULE_LICENSE("GPL v2"); 211MODULE_ALIAS("platform:iproc-mdio");