Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * B53 register access through memory mapped registers
0003  *
0004  * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
0005  *
0006  * Permission to use, copy, modify, and/or distribute this software for any
0007  * purpose with or without fee is hereby granted, provided that the above
0008  * copyright notice and this permission notice appear in all copies.
0009  *
0010  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0011  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0012  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0013  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0014  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0015  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0016  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0017  */
0018 
0019 #include <linux/bits.h>
0020 #include <linux/kernel.h>
0021 #include <linux/module.h>
0022 #include <linux/io.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/platform_data/b53.h>
0025 
0026 #include "b53_priv.h"
0027 
0028 struct b53_mmap_priv {
0029     void __iomem *regs;
0030 };
0031 
0032 static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
0033 {
0034     struct b53_mmap_priv *priv = dev->priv;
0035     void __iomem *regs = priv->regs;
0036 
0037     *val = readb(regs + (page << 8) + reg);
0038 
0039     return 0;
0040 }
0041 
0042 static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
0043 {
0044     struct b53_mmap_priv *priv = dev->priv;
0045     void __iomem *regs = priv->regs;
0046 
0047     if (WARN_ON(reg % 2))
0048         return -EINVAL;
0049 
0050     if (dev->pdata && dev->pdata->big_endian)
0051         *val = ioread16be(regs + (page << 8) + reg);
0052     else
0053         *val = readw(regs + (page << 8) + reg);
0054 
0055     return 0;
0056 }
0057 
0058 static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
0059 {
0060     struct b53_mmap_priv *priv = dev->priv;
0061     void __iomem *regs = priv->regs;
0062 
0063     if (WARN_ON(reg % 4))
0064         return -EINVAL;
0065 
0066     if (dev->pdata && dev->pdata->big_endian)
0067         *val = ioread32be(regs + (page << 8) + reg);
0068     else
0069         *val = readl(regs + (page << 8) + reg);
0070 
0071     return 0;
0072 }
0073 
0074 static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
0075 {
0076     struct b53_mmap_priv *priv = dev->priv;
0077     void __iomem *regs = priv->regs;
0078 
0079     if (WARN_ON(reg % 2))
0080         return -EINVAL;
0081 
0082     if (reg % 4) {
0083         u16 lo;
0084         u32 hi;
0085 
0086         if (dev->pdata && dev->pdata->big_endian) {
0087             lo = ioread16be(regs + (page << 8) + reg);
0088             hi = ioread32be(regs + (page << 8) + reg + 2);
0089         } else {
0090             lo = readw(regs + (page << 8) + reg);
0091             hi = readl(regs + (page << 8) + reg + 2);
0092         }
0093 
0094         *val = ((u64)hi << 16) | lo;
0095     } else {
0096         u32 lo;
0097         u16 hi;
0098 
0099         if (dev->pdata && dev->pdata->big_endian) {
0100             lo = ioread32be(regs + (page << 8) + reg);
0101             hi = ioread16be(regs + (page << 8) + reg + 4);
0102         } else {
0103             lo = readl(regs + (page << 8) + reg);
0104             hi = readw(regs + (page << 8) + reg + 4);
0105         }
0106 
0107         *val = ((u64)hi << 32) | lo;
0108     }
0109 
0110     return 0;
0111 }
0112 
0113 static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
0114 {
0115     struct b53_mmap_priv *priv = dev->priv;
0116     void __iomem *regs = priv->regs;
0117     u32 hi, lo;
0118 
0119     if (WARN_ON(reg % 4))
0120         return -EINVAL;
0121 
0122     if (dev->pdata && dev->pdata->big_endian) {
0123         lo = ioread32be(regs + (page << 8) + reg);
0124         hi = ioread32be(regs + (page << 8) + reg + 4);
0125     } else {
0126         lo = readl(regs + (page << 8) + reg);
0127         hi = readl(regs + (page << 8) + reg + 4);
0128     }
0129 
0130     *val = ((u64)hi << 32) | lo;
0131 
0132     return 0;
0133 }
0134 
0135 static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
0136 {
0137     struct b53_mmap_priv *priv = dev->priv;
0138     void __iomem *regs = priv->regs;
0139 
0140     writeb(value, regs + (page << 8) + reg);
0141 
0142     return 0;
0143 }
0144 
0145 static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
0146                 u16 value)
0147 {
0148     struct b53_mmap_priv *priv = dev->priv;
0149     void __iomem *regs = priv->regs;
0150 
0151     if (WARN_ON(reg % 2))
0152         return -EINVAL;
0153 
0154     if (dev->pdata && dev->pdata->big_endian)
0155         iowrite16be(value, regs + (page << 8) + reg);
0156     else
0157         writew(value, regs + (page << 8) + reg);
0158 
0159     return 0;
0160 }
0161 
0162 static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
0163                 u32 value)
0164 {
0165     struct b53_mmap_priv *priv = dev->priv;
0166     void __iomem *regs = priv->regs;
0167 
0168     if (WARN_ON(reg % 4))
0169         return -EINVAL;
0170 
0171     if (dev->pdata && dev->pdata->big_endian)
0172         iowrite32be(value, regs + (page << 8) + reg);
0173     else
0174         writel(value, regs + (page << 8) + reg);
0175 
0176     return 0;
0177 }
0178 
0179 static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
0180                 u64 value)
0181 {
0182     if (WARN_ON(reg % 2))
0183         return -EINVAL;
0184 
0185     if (reg % 4) {
0186         u32 hi = (u32)(value >> 16);
0187         u16 lo = (u16)value;
0188 
0189         b53_mmap_write16(dev, page, reg, lo);
0190         b53_mmap_write32(dev, page, reg + 2, hi);
0191     } else {
0192         u16 hi = (u16)(value >> 32);
0193         u32 lo = (u32)value;
0194 
0195         b53_mmap_write32(dev, page, reg, lo);
0196         b53_mmap_write16(dev, page, reg + 4, hi);
0197     }
0198 
0199     return 0;
0200 }
0201 
0202 static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
0203                 u64 value)
0204 {
0205     u32 hi, lo;
0206 
0207     hi = upper_32_bits(value);
0208     lo = lower_32_bits(value);
0209 
0210     if (WARN_ON(reg % 4))
0211         return -EINVAL;
0212 
0213     b53_mmap_write32(dev, page, reg, lo);
0214     b53_mmap_write32(dev, page, reg + 4, hi);
0215 
0216     return 0;
0217 }
0218 
0219 static const struct b53_io_ops b53_mmap_ops = {
0220     .read8 = b53_mmap_read8,
0221     .read16 = b53_mmap_read16,
0222     .read32 = b53_mmap_read32,
0223     .read48 = b53_mmap_read48,
0224     .read64 = b53_mmap_read64,
0225     .write8 = b53_mmap_write8,
0226     .write16 = b53_mmap_write16,
0227     .write32 = b53_mmap_write32,
0228     .write48 = b53_mmap_write48,
0229     .write64 = b53_mmap_write64,
0230 };
0231 
0232 static int b53_mmap_probe_of(struct platform_device *pdev,
0233                  struct b53_platform_data **ppdata)
0234 {
0235     struct device_node *np = pdev->dev.of_node;
0236     struct device_node *of_ports, *of_port;
0237     struct device *dev = &pdev->dev;
0238     struct b53_platform_data *pdata;
0239     void __iomem *mem;
0240 
0241     mem = devm_platform_ioremap_resource(pdev, 0);
0242     if (IS_ERR(mem))
0243         return PTR_ERR(mem);
0244 
0245     pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data),
0246                  GFP_KERNEL);
0247     if (!pdata)
0248         return -ENOMEM;
0249 
0250     pdata->regs = mem;
0251     pdata->chip_id = BCM63XX_DEVICE_ID;
0252     pdata->big_endian = of_property_read_bool(np, "big-endian");
0253 
0254     of_ports = of_get_child_by_name(np, "ports");
0255     if (!of_ports) {
0256         dev_err(dev, "no ports child node found\n");
0257         return -EINVAL;
0258     }
0259 
0260     for_each_available_child_of_node(of_ports, of_port) {
0261         u32 reg;
0262 
0263         if (of_property_read_u32(of_port, "reg", &reg))
0264             continue;
0265 
0266         if (reg < B53_CPU_PORT)
0267             pdata->enabled_ports |= BIT(reg);
0268     }
0269 
0270     of_node_put(of_ports);
0271     *ppdata = pdata;
0272 
0273     return 0;
0274 }
0275 
0276 static int b53_mmap_probe(struct platform_device *pdev)
0277 {
0278     struct device_node *np = pdev->dev.of_node;
0279     struct b53_platform_data *pdata = pdev->dev.platform_data;
0280     struct b53_mmap_priv *priv;
0281     struct b53_device *dev;
0282     int ret;
0283 
0284     if (!pdata && np) {
0285         ret = b53_mmap_probe_of(pdev, &pdata);
0286         if (ret) {
0287             dev_err(&pdev->dev, "OF probe error\n");
0288             return ret;
0289         }
0290     }
0291 
0292     if (!pdata)
0293         return -EINVAL;
0294 
0295     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0296     if (!priv)
0297         return -ENOMEM;
0298 
0299     priv->regs = pdata->regs;
0300 
0301     dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);
0302     if (!dev)
0303         return -ENOMEM;
0304 
0305     dev->pdata = pdata;
0306 
0307     platform_set_drvdata(pdev, dev);
0308 
0309     return b53_switch_register(dev);
0310 }
0311 
0312 static int b53_mmap_remove(struct platform_device *pdev)
0313 {
0314     struct b53_device *dev = platform_get_drvdata(pdev);
0315 
0316     if (dev)
0317         b53_switch_remove(dev);
0318 
0319     platform_set_drvdata(pdev, NULL);
0320 
0321     return 0;
0322 }
0323 
0324 static void b53_mmap_shutdown(struct platform_device *pdev)
0325 {
0326     struct b53_device *dev = platform_get_drvdata(pdev);
0327 
0328     if (dev)
0329         b53_switch_shutdown(dev);
0330 
0331     platform_set_drvdata(pdev, NULL);
0332 }
0333 
0334 static const struct of_device_id b53_mmap_of_table[] = {
0335     { .compatible = "brcm,bcm3384-switch" },
0336     { .compatible = "brcm,bcm6328-switch" },
0337     { .compatible = "brcm,bcm6368-switch" },
0338     { .compatible = "brcm,bcm63xx-switch" },
0339     { /* sentinel */ },
0340 };
0341 MODULE_DEVICE_TABLE(of, b53_mmap_of_table);
0342 
0343 static struct platform_driver b53_mmap_driver = {
0344     .probe = b53_mmap_probe,
0345     .remove = b53_mmap_remove,
0346     .shutdown = b53_mmap_shutdown,
0347     .driver = {
0348         .name = "b53-switch",
0349         .of_match_table = b53_mmap_of_table,
0350     },
0351 };
0352 
0353 module_platform_driver(b53_mmap_driver);
0354 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
0355 MODULE_DESCRIPTION("B53 MMAP access driver");
0356 MODULE_LICENSE("Dual BSD/GPL");