1/* 2 * B53 register access through memory mapped registers 3 * 4 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/io.h> 22#include <linux/platform_device.h> 23#include <linux/platform_data/b53.h> 24 25#include "b53_priv.h" 26 27struct b53_mmap_priv { 28 void __iomem *regs; 29}; 30 31static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) 32{ 33 struct b53_mmap_priv *priv = dev->priv; 34 void __iomem *regs = priv->regs; 35 36 *val = readb(regs + (page << 8) + reg); 37 38 return 0; 39} 40 41static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) 42{ 43 struct b53_mmap_priv *priv = dev->priv; 44 void __iomem *regs = priv->regs; 45 46 if (WARN_ON(reg % 2)) 47 return -EINVAL; 48 49 if (dev->pdata && dev->pdata->big_endian) 50 *val = ioread16be(regs + (page << 8) + reg); 51 else 52 *val = readw(regs + (page << 8) + reg); 53 54 return 0; 55} 56 57static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) 58{ 59 struct b53_mmap_priv *priv = dev->priv; 60 void __iomem *regs = priv->regs; 61 62 if (WARN_ON(reg % 4)) 63 return -EINVAL; 64 65 if (dev->pdata && dev->pdata->big_endian) 66 *val = ioread32be(regs + (page << 8) + reg); 67 else 68 *val = readl(regs + (page << 8) + reg); 69 70 return 0; 71} 72 73static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) 74{ 75 struct b53_mmap_priv *priv = dev->priv; 76 void __iomem *regs = priv->regs; 77 78 if (WARN_ON(reg % 2)) 79 return -EINVAL; 80 81 if (reg % 4) { 82 u16 lo; 83 u32 hi; 84 85 if (dev->pdata && dev->pdata->big_endian) { 86 lo = ioread16be(regs + (page << 8) + reg); 87 hi = ioread32be(regs + (page << 8) + reg + 2); 88 } else { 89 lo = readw(regs + (page << 8) + reg); 90 hi = readl(regs + (page << 8) + reg + 2); 91 } 92 93 *val = ((u64)hi << 16) | lo; 94 } else { 95 u32 lo; 96 u16 hi; 97 98 if (dev->pdata && dev->pdata->big_endian) { 99 lo = ioread32be(regs + (page << 8) + reg); 100 hi = ioread16be(regs + (page << 8) + reg + 4); 101 } else { 102 lo = readl(regs + (page << 8) + reg); 103 hi = readw(regs + (page << 8) + reg + 4); 104 } 105 106 *val = ((u64)hi << 32) | lo; 107 } 108 109 return 0; 110} 111 112static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) 113{ 114 struct b53_mmap_priv *priv = dev->priv; 115 void __iomem *regs = priv->regs; 116 u32 hi, lo; 117 118 if (WARN_ON(reg % 4)) 119 return -EINVAL; 120 121 if (dev->pdata && dev->pdata->big_endian) { 122 lo = ioread32be(regs + (page << 8) + reg); 123 hi = ioread32be(regs + (page << 8) + reg + 4); 124 } else { 125 lo = readl(regs + (page << 8) + reg); 126 hi = readl(regs + (page << 8) + reg + 4); 127 } 128 129 *val = ((u64)hi << 32) | lo; 130 131 return 0; 132} 133 134static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) 135{ 136 struct b53_mmap_priv *priv = dev->priv; 137 void __iomem *regs = priv->regs; 138 139 writeb(value, regs + (page << 8) + reg); 140 141 return 0; 142} 143 144static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, 145 u16 value) 146{ 147 struct b53_mmap_priv *priv = dev->priv; 148 void __iomem *regs = priv->regs; 149 150 if (WARN_ON(reg % 2)) 151 return -EINVAL; 152 153 if (dev->pdata && dev->pdata->big_endian) 154 iowrite16be(value, regs + (page << 8) + reg); 155 else 156 writew(value, regs + (page << 8) + reg); 157 158 return 0; 159} 160 161static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, 162 u32 value) 163{ 164 struct b53_mmap_priv *priv = dev->priv; 165 void __iomem *regs = priv->regs; 166 167 if (WARN_ON(reg % 4)) 168 return -EINVAL; 169 170 if (dev->pdata && dev->pdata->big_endian) 171 iowrite32be(value, regs + (page << 8) + reg); 172 else 173 writel(value, regs + (page << 8) + reg); 174 175 return 0; 176} 177 178static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, 179 u64 value) 180{ 181 if (WARN_ON(reg % 2)) 182 return -EINVAL; 183 184 if (reg % 4) { 185 u32 hi = (u32)(value >> 16); 186 u16 lo = (u16)value; 187 188 b53_mmap_write16(dev, page, reg, lo); 189 b53_mmap_write32(dev, page, reg + 2, hi); 190 } else { 191 u16 hi = (u16)(value >> 32); 192 u32 lo = (u32)value; 193 194 b53_mmap_write32(dev, page, reg, lo); 195 b53_mmap_write16(dev, page, reg + 4, hi); 196 } 197 198 return 0; 199} 200 201static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, 202 u64 value) 203{ 204 u32 hi, lo; 205 206 hi = upper_32_bits(value); 207 lo = lower_32_bits(value); 208 209 if (WARN_ON(reg % 4)) 210 return -EINVAL; 211 212 b53_mmap_write32(dev, page, reg, lo); 213 b53_mmap_write32(dev, page, reg + 4, hi); 214 215 return 0; 216} 217 218static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg, 219 u16 *value) 220{ 221 return -EIO; 222} 223 224static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg, 225 u16 value) 226{ 227 return -EIO; 228} 229 230static const struct b53_io_ops b53_mmap_ops = { 231 .read8 = b53_mmap_read8, 232 .read16 = b53_mmap_read16, 233 .read32 = b53_mmap_read32, 234 .read48 = b53_mmap_read48, 235 .read64 = b53_mmap_read64, 236 .write8 = b53_mmap_write8, 237 .write16 = b53_mmap_write16, 238 .write32 = b53_mmap_write32, 239 .write48 = b53_mmap_write48, 240 .write64 = b53_mmap_write64, 241 .phy_read16 = b53_mmap_phy_read16, 242 .phy_write16 = b53_mmap_phy_write16, 243}; 244 245static int b53_mmap_probe(struct platform_device *pdev) 246{ 247 struct b53_platform_data *pdata = pdev->dev.platform_data; 248 struct b53_mmap_priv *priv; 249 struct b53_device *dev; 250 251 if (!pdata) 252 return -EINVAL; 253 254 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 255 if (!priv) 256 return -ENOMEM; 257 258 priv->regs = pdata->regs; 259 260 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv); 261 if (!dev) 262 return -ENOMEM; 263 264 dev->pdata = pdata; 265 266 platform_set_drvdata(pdev, dev); 267 268 return b53_switch_register(dev); 269} 270 271static int b53_mmap_remove(struct platform_device *pdev) 272{ 273 struct b53_device *dev = platform_get_drvdata(pdev); 274 275 if (dev) 276 b53_switch_remove(dev); 277 278 return 0; 279} 280 281static const struct of_device_id b53_mmap_of_table[] = { 282 { .compatible = "brcm,bcm3384-switch" }, 283 { .compatible = "brcm,bcm6328-switch" }, 284 { .compatible = "brcm,bcm6368-switch" }, 285 { .compatible = "brcm,bcm63xx-switch" }, 286 { /* sentinel */ }, 287}; 288MODULE_DEVICE_TABLE(of, b53_mmap_of_table); 289 290static struct platform_driver b53_mmap_driver = { 291 .probe = b53_mmap_probe, 292 .remove = b53_mmap_remove, 293 .driver = { 294 .name = "b53-switch", 295 .of_match_table = b53_mmap_of_table, 296 }, 297}; 298 299module_platform_driver(b53_mmap_driver); 300MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 301MODULE_DESCRIPTION("B53 MMAP access driver"); 302MODULE_LICENSE("Dual BSD/GPL"); 303