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