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.7-rc1 213 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 iproc_mdio_config_clk(priv->base); 85 86 /* Prepare the read operation */ 87 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | 88 (reg << MII_DATA_RA_SHIFT) | 89 (phy_id << MII_DATA_PA_SHIFT) | 90 BIT(MII_DATA_SB_SHIFT) | 91 (MII_DATA_OP_READ << MII_DATA_OP_SHIFT); 92 93 writel(cmd, priv->base + MII_DATA_OFFSET); 94 95 rc = iproc_mdio_wait_for_idle(priv->base); 96 if (rc) 97 return rc; 98 99 cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK; 100 101 return cmd; 102} 103 104static int iproc_mdio_write(struct mii_bus *bus, int phy_id, 105 int reg, u16 val) 106{ 107 struct iproc_mdio_priv *priv = bus->priv; 108 u32 cmd; 109 int rc; 110 111 rc = iproc_mdio_wait_for_idle(priv->base); 112 if (rc) 113 return rc; 114 115 iproc_mdio_config_clk(priv->base); 116 117 /* Prepare the write operation */ 118 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | 119 (reg << MII_DATA_RA_SHIFT) | 120 (phy_id << MII_DATA_PA_SHIFT) | 121 BIT(MII_DATA_SB_SHIFT) | 122 (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) | 123 ((u32)(val) & MII_DATA_MASK); 124 125 writel(cmd, priv->base + MII_DATA_OFFSET); 126 127 rc = iproc_mdio_wait_for_idle(priv->base); 128 if (rc) 129 return rc; 130 131 return 0; 132} 133 134static int iproc_mdio_probe(struct platform_device *pdev) 135{ 136 struct iproc_mdio_priv *priv; 137 struct mii_bus *bus; 138 struct resource *res; 139 int rc; 140 141 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 142 if (!priv) 143 return -ENOMEM; 144 145 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 146 priv->base = devm_ioremap_resource(&pdev->dev, res); 147 if (IS_ERR(priv->base)) { 148 dev_err(&pdev->dev, "failed to ioremap register\n"); 149 return PTR_ERR(priv->base); 150 } 151 152 priv->mii_bus = mdiobus_alloc(); 153 if (!priv->mii_bus) { 154 dev_err(&pdev->dev, "MDIO bus alloc failed\n"); 155 return -ENOMEM; 156 } 157 158 bus = priv->mii_bus; 159 bus->priv = priv; 160 bus->name = "iProc MDIO bus"; 161 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); 162 bus->parent = &pdev->dev; 163 bus->read = iproc_mdio_read; 164 bus->write = iproc_mdio_write; 165 166 rc = of_mdiobus_register(bus, pdev->dev.of_node); 167 if (rc) { 168 dev_err(&pdev->dev, "MDIO bus registration failed\n"); 169 goto err_iproc_mdio; 170 } 171 172 platform_set_drvdata(pdev, priv); 173 174 dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base); 175 176 return 0; 177 178err_iproc_mdio: 179 mdiobus_free(bus); 180 return rc; 181} 182 183static int iproc_mdio_remove(struct platform_device *pdev) 184{ 185 struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); 186 187 mdiobus_unregister(priv->mii_bus); 188 mdiobus_free(priv->mii_bus); 189 190 return 0; 191} 192 193static const struct of_device_id iproc_mdio_of_match[] = { 194 { .compatible = "brcm,iproc-mdio", }, 195 { /* sentinel */ }, 196}; 197MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); 198 199static struct platform_driver iproc_mdio_driver = { 200 .driver = { 201 .name = "iproc-mdio", 202 .of_match_table = iproc_mdio_of_match, 203 }, 204 .probe = iproc_mdio_probe, 205 .remove = iproc_mdio_remove, 206}; 207 208module_platform_driver(iproc_mdio_driver); 209 210MODULE_AUTHOR("Broadcom Corporation"); 211MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller"); 212MODULE_LICENSE("GPL v2"); 213MODULE_ALIAS("platform:iproc-mdio");