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/of.h> 23 #include <linux/io.h> 24 #include <linux/mfd/syscon.h> 25 #include <linux/platform_device.h> 26 #include <linux/platform_data/b53.h> 27 #include <linux/regmap.h> 28 29 #include "b53_priv.h" 30 31 #define BCM63XX_EPHY_REG 0x3C 32 33 struct b53_phy_info { 34 u32 ephy_enable_mask; 35 u32 ephy_port_mask; 36 u32 ephy_bias_bit; 37 const u32 *ephy_offset; 38 }; 39 40 struct b53_mmap_priv { 41 void __iomem *regs; 42 struct regmap *gpio_ctrl; 43 const struct b53_phy_info *phy_info; 44 u32 phys_enabled; 45 }; 46 47 static const u32 bcm6318_ephy_offsets[] = {4, 5, 6, 7}; 48 49 static const struct b53_phy_info bcm6318_ephy_info = { 50 .ephy_enable_mask = BIT(0) | BIT(4) | BIT(8) | BIT(12) | BIT(16), 51 .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6318_ephy_offsets) - 1), 0), 52 .ephy_bias_bit = 24, 53 .ephy_offset = bcm6318_ephy_offsets, 54 }; 55 56 static const u32 bcm6368_ephy_offsets[] = {2, 3, 4, 5}; 57 58 static const struct b53_phy_info bcm6368_ephy_info = { 59 .ephy_enable_mask = BIT(0), 60 .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6368_ephy_offsets) - 1), 0), 61 .ephy_bias_bit = 0, 62 .ephy_offset = bcm6368_ephy_offsets, 63 }; 64 65 static const u32 bcm63268_ephy_offsets[] = {4, 9, 14}; 66 67 static const struct b53_phy_info bcm63268_ephy_info = { 68 .ephy_enable_mask = GENMASK(4, 0), 69 .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm63268_ephy_offsets) - 1), 0), 70 .ephy_bias_bit = 24, 71 .ephy_offset = bcm63268_ephy_offsets, 72 }; 73 74 static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) 75 { 76 struct b53_mmap_priv *priv = dev->priv; 77 void __iomem *regs = priv->regs; 78 79 *val = readb(regs + (page << 8) + reg); 80 81 return 0; 82 } 83 84 static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) 85 { 86 struct b53_mmap_priv *priv = dev->priv; 87 void __iomem *regs = priv->regs; 88 89 if (WARN_ON(reg % 2)) 90 return -EINVAL; 91 92 if (dev->pdata && dev->pdata->big_endian) 93 *val = ioread16be(regs + (page << 8) + reg); 94 else 95 *val = readw(regs + (page << 8) + reg); 96 97 return 0; 98 } 99 100 static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) 101 { 102 struct b53_mmap_priv *priv = dev->priv; 103 void __iomem *regs = priv->regs; 104 105 if (WARN_ON(reg % 4)) 106 return -EINVAL; 107 108 if (dev->pdata && dev->pdata->big_endian) 109 *val = ioread32be(regs + (page << 8) + reg); 110 else 111 *val = readl(regs + (page << 8) + reg); 112 113 return 0; 114 } 115 116 static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) 117 { 118 struct b53_mmap_priv *priv = dev->priv; 119 void __iomem *regs = priv->regs; 120 121 if (WARN_ON(reg % 2)) 122 return -EINVAL; 123 124 if (reg % 4) { 125 u16 lo; 126 u32 hi; 127 128 if (dev->pdata && dev->pdata->big_endian) { 129 lo = ioread16be(regs + (page << 8) + reg); 130 hi = ioread32be(regs + (page << 8) + reg + 2); 131 } else { 132 lo = readw(regs + (page << 8) + reg); 133 hi = readl(regs + (page << 8) + reg + 2); 134 } 135 136 *val = ((u64)hi << 16) | lo; 137 } else { 138 u32 lo; 139 u16 hi; 140 141 if (dev->pdata && dev->pdata->big_endian) { 142 lo = ioread32be(regs + (page << 8) + reg); 143 hi = ioread16be(regs + (page << 8) + reg + 4); 144 } else { 145 lo = readl(regs + (page << 8) + reg); 146 hi = readw(regs + (page << 8) + reg + 4); 147 } 148 149 *val = ((u64)hi << 32) | lo; 150 } 151 152 return 0; 153 } 154 155 static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) 156 { 157 struct b53_mmap_priv *priv = dev->priv; 158 void __iomem *regs = priv->regs; 159 u32 hi, lo; 160 161 if (WARN_ON(reg % 4)) 162 return -EINVAL; 163 164 if (dev->pdata && dev->pdata->big_endian) { 165 lo = ioread32be(regs + (page << 8) + reg); 166 hi = ioread32be(regs + (page << 8) + reg + 4); 167 } else { 168 lo = readl(regs + (page << 8) + reg); 169 hi = readl(regs + (page << 8) + reg + 4); 170 } 171 172 *val = ((u64)hi << 32) | lo; 173 174 return 0; 175 } 176 177 static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) 178 { 179 struct b53_mmap_priv *priv = dev->priv; 180 void __iomem *regs = priv->regs; 181 182 writeb(value, regs + (page << 8) + reg); 183 184 return 0; 185 } 186 187 static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, 188 u16 value) 189 { 190 struct b53_mmap_priv *priv = dev->priv; 191 void __iomem *regs = priv->regs; 192 193 if (WARN_ON(reg % 2)) 194 return -EINVAL; 195 196 if (dev->pdata && dev->pdata->big_endian) 197 iowrite16be(value, regs + (page << 8) + reg); 198 else 199 writew(value, regs + (page << 8) + reg); 200 201 return 0; 202 } 203 204 static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, 205 u32 value) 206 { 207 struct b53_mmap_priv *priv = dev->priv; 208 void __iomem *regs = priv->regs; 209 210 if (WARN_ON(reg % 4)) 211 return -EINVAL; 212 213 if (dev->pdata && dev->pdata->big_endian) 214 iowrite32be(value, regs + (page << 8) + reg); 215 else 216 writel(value, regs + (page << 8) + reg); 217 218 return 0; 219 } 220 221 static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, 222 u64 value) 223 { 224 if (WARN_ON(reg % 2)) 225 return -EINVAL; 226 227 if (reg % 4) { 228 u32 hi = (u32)(value >> 16); 229 u16 lo = (u16)value; 230 231 b53_mmap_write16(dev, page, reg, lo); 232 b53_mmap_write32(dev, page, reg + 2, hi); 233 } else { 234 u16 hi = (u16)(value >> 32); 235 u32 lo = (u32)value; 236 237 b53_mmap_write32(dev, page, reg, lo); 238 b53_mmap_write16(dev, page, reg + 4, hi); 239 } 240 241 return 0; 242 } 243 244 static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, 245 u64 value) 246 { 247 u32 hi, lo; 248 249 hi = upper_32_bits(value); 250 lo = lower_32_bits(value); 251 252 if (WARN_ON(reg % 4)) 253 return -EINVAL; 254 255 b53_mmap_write32(dev, page, reg, lo); 256 b53_mmap_write32(dev, page, reg + 4, hi); 257 258 return 0; 259 } 260 261 static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg, 262 u16 *value) 263 { 264 return -EIO; 265 } 266 267 static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg, 268 u16 value) 269 { 270 return -EIO; 271 } 272 273 static int bcm63xx_ephy_set(struct b53_device *dev, int port, bool enable) 274 { 275 struct b53_mmap_priv *priv = dev->priv; 276 const struct b53_phy_info *info = priv->phy_info; 277 struct regmap *gpio_ctrl = priv->gpio_ctrl; 278 u32 mask, val; 279 280 if (enable) { 281 mask = (info->ephy_enable_mask << info->ephy_offset[port]) 282 | BIT(info->ephy_bias_bit); 283 val = 0; 284 } else { 285 mask = (info->ephy_enable_mask << info->ephy_offset[port]); 286 if (!((priv->phys_enabled & ~BIT(port)) & info->ephy_port_mask)) 287 mask |= BIT(info->ephy_bias_bit); 288 val = mask; 289 } 290 return regmap_update_bits(gpio_ctrl, BCM63XX_EPHY_REG, mask, val); 291 } 292 293 static void b53_mmap_phy_enable(struct b53_device *dev, int port) 294 { 295 struct b53_mmap_priv *priv = dev->priv; 296 int ret = 0; 297 298 if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) 299 ret = bcm63xx_ephy_set(dev, port, true); 300 301 if (!ret) 302 priv->phys_enabled |= BIT(port); 303 } 304 305 static void b53_mmap_phy_disable(struct b53_device *dev, int port) 306 { 307 struct b53_mmap_priv *priv = dev->priv; 308 int ret = 0; 309 310 if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) 311 ret = bcm63xx_ephy_set(dev, port, false); 312 313 if (!ret) 314 priv->phys_enabled &= ~BIT(port); 315 } 316 317 static const struct b53_io_ops b53_mmap_ops = { 318 .read8 = b53_mmap_read8, 319 .read16 = b53_mmap_read16, 320 .read32 = b53_mmap_read32, 321 .read48 = b53_mmap_read48, 322 .read64 = b53_mmap_read64, 323 .write8 = b53_mmap_write8, 324 .write16 = b53_mmap_write16, 325 .write32 = b53_mmap_write32, 326 .write48 = b53_mmap_write48, 327 .write64 = b53_mmap_write64, 328 .phy_read16 = b53_mmap_phy_read16, 329 .phy_write16 = b53_mmap_phy_write16, 330 .phy_enable = b53_mmap_phy_enable, 331 .phy_disable = b53_mmap_phy_disable, 332 }; 333 334 static int b53_mmap_probe_of(struct platform_device *pdev, 335 struct b53_platform_data **ppdata) 336 { 337 struct device_node *np = pdev->dev.of_node; 338 struct device_node *of_ports, *of_port; 339 struct device *dev = &pdev->dev; 340 struct b53_platform_data *pdata; 341 void __iomem *mem; 342 343 mem = devm_platform_ioremap_resource(pdev, 0); 344 if (IS_ERR(mem)) 345 return PTR_ERR(mem); 346 347 pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data), 348 GFP_KERNEL); 349 if (!pdata) 350 return -ENOMEM; 351 352 pdata->regs = mem; 353 pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev); 354 pdata->big_endian = of_property_read_bool(np, "big-endian"); 355 356 of_ports = of_get_child_by_name(np, "ports"); 357 if (!of_ports) { 358 dev_err(dev, "no ports child node found\n"); 359 return -EINVAL; 360 } 361 362 for_each_available_child_of_node(of_ports, of_port) { 363 u32 reg; 364 365 if (of_property_read_u32(of_port, "reg", ®)) 366 continue; 367 368 if (reg < B53_N_PORTS) 369 pdata->enabled_ports |= BIT(reg); 370 } 371 372 of_node_put(of_ports); 373 *ppdata = pdata; 374 375 return 0; 376 } 377 378 static int b53_mmap_probe(struct platform_device *pdev) 379 { 380 struct device_node *np = pdev->dev.of_node; 381 struct b53_platform_data *pdata = pdev->dev.platform_data; 382 struct b53_mmap_priv *priv; 383 struct b53_device *dev; 384 int ret; 385 386 if (!pdata && np) { 387 ret = b53_mmap_probe_of(pdev, &pdata); 388 if (ret) { 389 dev_err(&pdev->dev, "OF probe error\n"); 390 return ret; 391 } 392 } 393 394 if (!pdata) 395 return -EINVAL; 396 397 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 398 if (!priv) 399 return -ENOMEM; 400 401 priv->regs = pdata->regs; 402 403 priv->gpio_ctrl = syscon_regmap_lookup_by_phandle(np, "brcm,gpio-ctrl"); 404 if (!IS_ERR(priv->gpio_ctrl)) { 405 if (pdata->chip_id == BCM6318_DEVICE_ID || 406 pdata->chip_id == BCM6328_DEVICE_ID || 407 pdata->chip_id == BCM6362_DEVICE_ID) 408 priv->phy_info = &bcm6318_ephy_info; 409 else if (pdata->chip_id == BCM6368_DEVICE_ID) 410 priv->phy_info = &bcm6368_ephy_info; 411 else if (pdata->chip_id == BCM63268_DEVICE_ID) 412 priv->phy_info = &bcm63268_ephy_info; 413 } 414 415 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv); 416 if (!dev) 417 return -ENOMEM; 418 419 dev->pdata = pdata; 420 421 platform_set_drvdata(pdev, dev); 422 423 return b53_switch_register(dev); 424 } 425 426 static void b53_mmap_remove(struct platform_device *pdev) 427 { 428 struct b53_device *dev = platform_get_drvdata(pdev); 429 430 if (dev) 431 b53_switch_remove(dev); 432 } 433 434 static void b53_mmap_shutdown(struct platform_device *pdev) 435 { 436 struct b53_device *dev = platform_get_drvdata(pdev); 437 438 if (dev) 439 b53_switch_shutdown(dev); 440 441 platform_set_drvdata(pdev, NULL); 442 } 443 444 static const struct of_device_id b53_mmap_of_table[] = { 445 { 446 .compatible = "brcm,bcm3384-switch", 447 .data = (void *)BCM63XX_DEVICE_ID, 448 }, { 449 .compatible = "brcm,bcm6318-switch", 450 .data = (void *)BCM6318_DEVICE_ID, 451 }, { 452 .compatible = "brcm,bcm6328-switch", 453 .data = (void *)BCM6328_DEVICE_ID, 454 }, { 455 .compatible = "brcm,bcm6362-switch", 456 .data = (void *)BCM6362_DEVICE_ID, 457 }, { 458 .compatible = "brcm,bcm6368-switch", 459 .data = (void *)BCM6368_DEVICE_ID, 460 }, { 461 .compatible = "brcm,bcm63268-switch", 462 .data = (void *)BCM63268_DEVICE_ID, 463 }, { 464 .compatible = "brcm,bcm63xx-switch", 465 .data = (void *)BCM63XX_DEVICE_ID, 466 }, { /* sentinel */ } 467 }; 468 MODULE_DEVICE_TABLE(of, b53_mmap_of_table); 469 470 static struct platform_driver b53_mmap_driver = { 471 .probe = b53_mmap_probe, 472 .remove = b53_mmap_remove, 473 .shutdown = b53_mmap_shutdown, 474 .driver = { 475 .name = "b53-switch", 476 .of_match_table = b53_mmap_of_table, 477 }, 478 }; 479 480 module_platform_driver(b53_mmap_driver); 481 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 482 MODULE_DESCRIPTION("B53 MMAP access driver"); 483 MODULE_LICENSE("Dual BSD/GPL"); 484