385 lines · c
1/*2 * B53 register access through memory mapped registers3 *4 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>5 *6 * Permission to use, copy, modify, and/or distribute this software for any7 * purpose with or without fee is hereby granted, provided that the above8 * copyright notice and this permission notice appear in all copies.9 *10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 */18 19#include <linux/bits.h>20#include <linux/kernel.h>21#include <linux/module.h>22#include <linux/of.h>23#include <linux/io.h>24#include <linux/platform_device.h>25#include <linux/platform_data/b53.h>26 27#include "b53_priv.h"28 29struct b53_mmap_priv {30 void __iomem *regs;31};32 33static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)34{35 struct b53_mmap_priv *priv = dev->priv;36 void __iomem *regs = priv->regs;37 38 *val = readb(regs + (page << 8) + reg);39 40 return 0;41}42 43static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)44{45 struct b53_mmap_priv *priv = dev->priv;46 void __iomem *regs = priv->regs;47 48 if (WARN_ON(reg % 2))49 return -EINVAL;50 51 if (dev->pdata && dev->pdata->big_endian)52 *val = ioread16be(regs + (page << 8) + reg);53 else54 *val = readw(regs + (page << 8) + reg);55 56 return 0;57}58 59static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)60{61 struct b53_mmap_priv *priv = dev->priv;62 void __iomem *regs = priv->regs;63 64 if (WARN_ON(reg % 4))65 return -EINVAL;66 67 if (dev->pdata && dev->pdata->big_endian)68 *val = ioread32be(regs + (page << 8) + reg);69 else70 *val = readl(regs + (page << 8) + reg);71 72 return 0;73}74 75static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)76{77 struct b53_mmap_priv *priv = dev->priv;78 void __iomem *regs = priv->regs;79 80 if (WARN_ON(reg % 2))81 return -EINVAL;82 83 if (reg % 4) {84 u16 lo;85 u32 hi;86 87 if (dev->pdata && dev->pdata->big_endian) {88 lo = ioread16be(regs + (page << 8) + reg);89 hi = ioread32be(regs + (page << 8) + reg + 2);90 } else {91 lo = readw(regs + (page << 8) + reg);92 hi = readl(regs + (page << 8) + reg + 2);93 }94 95 *val = ((u64)hi << 16) | lo;96 } else {97 u32 lo;98 u16 hi;99 100 if (dev->pdata && dev->pdata->big_endian) {101 lo = ioread32be(regs + (page << 8) + reg);102 hi = ioread16be(regs + (page << 8) + reg + 4);103 } else {104 lo = readl(regs + (page << 8) + reg);105 hi = readw(regs + (page << 8) + reg + 4);106 }107 108 *val = ((u64)hi << 32) | lo;109 }110 111 return 0;112}113 114static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)115{116 struct b53_mmap_priv *priv = dev->priv;117 void __iomem *regs = priv->regs;118 u32 hi, lo;119 120 if (WARN_ON(reg % 4))121 return -EINVAL;122 123 if (dev->pdata && dev->pdata->big_endian) {124 lo = ioread32be(regs + (page << 8) + reg);125 hi = ioread32be(regs + (page << 8) + reg + 4);126 } else {127 lo = readl(regs + (page << 8) + reg);128 hi = readl(regs + (page << 8) + reg + 4);129 }130 131 *val = ((u64)hi << 32) | lo;132 133 return 0;134}135 136static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)137{138 struct b53_mmap_priv *priv = dev->priv;139 void __iomem *regs = priv->regs;140 141 writeb(value, regs + (page << 8) + reg);142 143 return 0;144}145 146static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,147 u16 value)148{149 struct b53_mmap_priv *priv = dev->priv;150 void __iomem *regs = priv->regs;151 152 if (WARN_ON(reg % 2))153 return -EINVAL;154 155 if (dev->pdata && dev->pdata->big_endian)156 iowrite16be(value, regs + (page << 8) + reg);157 else158 writew(value, regs + (page << 8) + reg);159 160 return 0;161}162 163static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,164 u32 value)165{166 struct b53_mmap_priv *priv = dev->priv;167 void __iomem *regs = priv->regs;168 169 if (WARN_ON(reg % 4))170 return -EINVAL;171 172 if (dev->pdata && dev->pdata->big_endian)173 iowrite32be(value, regs + (page << 8) + reg);174 else175 writel(value, regs + (page << 8) + reg);176 177 return 0;178}179 180static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,181 u64 value)182{183 if (WARN_ON(reg % 2))184 return -EINVAL;185 186 if (reg % 4) {187 u32 hi = (u32)(value >> 16);188 u16 lo = (u16)value;189 190 b53_mmap_write16(dev, page, reg, lo);191 b53_mmap_write32(dev, page, reg + 2, hi);192 } else {193 u16 hi = (u16)(value >> 32);194 u32 lo = (u32)value;195 196 b53_mmap_write32(dev, page, reg, lo);197 b53_mmap_write16(dev, page, reg + 4, hi);198 }199 200 return 0;201}202 203static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,204 u64 value)205{206 u32 hi, lo;207 208 hi = upper_32_bits(value);209 lo = lower_32_bits(value);210 211 if (WARN_ON(reg % 4))212 return -EINVAL;213 214 b53_mmap_write32(dev, page, reg, lo);215 b53_mmap_write32(dev, page, reg + 4, hi);216 217 return 0;218}219 220static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg,221 u16 *value)222{223 return -EIO;224}225 226static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg,227 u16 value)228{229 return -EIO;230}231 232static const struct b53_io_ops b53_mmap_ops = {233 .read8 = b53_mmap_read8,234 .read16 = b53_mmap_read16,235 .read32 = b53_mmap_read32,236 .read48 = b53_mmap_read48,237 .read64 = b53_mmap_read64,238 .write8 = b53_mmap_write8,239 .write16 = b53_mmap_write16,240 .write32 = b53_mmap_write32,241 .write48 = b53_mmap_write48,242 .write64 = b53_mmap_write64,243 .phy_read16 = b53_mmap_phy_read16,244 .phy_write16 = b53_mmap_phy_write16,245};246 247static int b53_mmap_probe_of(struct platform_device *pdev,248 struct b53_platform_data **ppdata)249{250 struct device_node *np = pdev->dev.of_node;251 struct device_node *of_ports, *of_port;252 struct device *dev = &pdev->dev;253 struct b53_platform_data *pdata;254 void __iomem *mem;255 256 mem = devm_platform_ioremap_resource(pdev, 0);257 if (IS_ERR(mem))258 return PTR_ERR(mem);259 260 pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data),261 GFP_KERNEL);262 if (!pdata)263 return -ENOMEM;264 265 pdata->regs = mem;266 pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev);267 pdata->big_endian = of_property_read_bool(np, "big-endian");268 269 of_ports = of_get_child_by_name(np, "ports");270 if (!of_ports) {271 dev_err(dev, "no ports child node found\n");272 return -EINVAL;273 }274 275 for_each_available_child_of_node(of_ports, of_port) {276 u32 reg;277 278 if (of_property_read_u32(of_port, "reg", ®))279 continue;280 281 if (reg < B53_N_PORTS)282 pdata->enabled_ports |= BIT(reg);283 }284 285 of_node_put(of_ports);286 *ppdata = pdata;287 288 return 0;289}290 291static int b53_mmap_probe(struct platform_device *pdev)292{293 struct device_node *np = pdev->dev.of_node;294 struct b53_platform_data *pdata = pdev->dev.platform_data;295 struct b53_mmap_priv *priv;296 struct b53_device *dev;297 int ret;298 299 if (!pdata && np) {300 ret = b53_mmap_probe_of(pdev, &pdata);301 if (ret) {302 dev_err(&pdev->dev, "OF probe error\n");303 return ret;304 }305 }306 307 if (!pdata)308 return -EINVAL;309 310 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);311 if (!priv)312 return -ENOMEM;313 314 priv->regs = pdata->regs;315 316 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);317 if (!dev)318 return -ENOMEM;319 320 dev->pdata = pdata;321 322 platform_set_drvdata(pdev, dev);323 324 return b53_switch_register(dev);325}326 327static void b53_mmap_remove(struct platform_device *pdev)328{329 struct b53_device *dev = platform_get_drvdata(pdev);330 331 if (dev)332 b53_switch_remove(dev);333}334 335static void b53_mmap_shutdown(struct platform_device *pdev)336{337 struct b53_device *dev = platform_get_drvdata(pdev);338 339 if (dev)340 b53_switch_shutdown(dev);341 342 platform_set_drvdata(pdev, NULL);343}344 345static const struct of_device_id b53_mmap_of_table[] = {346 {347 .compatible = "brcm,bcm3384-switch",348 .data = (void *)BCM63XX_DEVICE_ID,349 }, {350 .compatible = "brcm,bcm6318-switch",351 .data = (void *)BCM63268_DEVICE_ID,352 }, {353 .compatible = "brcm,bcm6328-switch",354 .data = (void *)BCM63XX_DEVICE_ID,355 }, {356 .compatible = "brcm,bcm6362-switch",357 .data = (void *)BCM63XX_DEVICE_ID,358 }, {359 .compatible = "brcm,bcm6368-switch",360 .data = (void *)BCM63XX_DEVICE_ID,361 }, {362 .compatible = "brcm,bcm63268-switch",363 .data = (void *)BCM63268_DEVICE_ID,364 }, {365 .compatible = "brcm,bcm63xx-switch",366 .data = (void *)BCM63XX_DEVICE_ID,367 }, { /* sentinel */ }368};369MODULE_DEVICE_TABLE(of, b53_mmap_of_table);370 371static struct platform_driver b53_mmap_driver = {372 .probe = b53_mmap_probe,373 .remove_new = b53_mmap_remove,374 .shutdown = b53_mmap_shutdown,375 .driver = {376 .name = "b53-switch",377 .of_match_table = b53_mmap_of_table,378 },379};380 381module_platform_driver(b53_mmap_driver);382MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");383MODULE_DESCRIPTION("B53 MMAP access driver");384MODULE_LICENSE("Dual BSD/GPL");385