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 const struct b53_io_ops b53_mmap_ops = { 220 .read8 = b53_mmap_read8, 221 .read16 = b53_mmap_read16, 222 .read32 = b53_mmap_read32, 223 .read48 = b53_mmap_read48, 224 .read64 = b53_mmap_read64, 225 .write8 = b53_mmap_write8, 226 .write16 = b53_mmap_write16, 227 .write32 = b53_mmap_write32, 228 .write48 = b53_mmap_write48, 229 .write64 = b53_mmap_write64, 230 }; 231 232 static int b53_mmap_probe_of(struct platform_device *pdev, 233 struct b53_platform_data **ppdata) 234 { 235 struct device_node *np = pdev->dev.of_node; 236 struct device_node *of_ports, *of_port; 237 struct device *dev = &pdev->dev; 238 struct b53_platform_data *pdata; 239 void __iomem *mem; 240 241 mem = devm_platform_ioremap_resource(pdev, 0); 242 if (IS_ERR(mem)) 243 return PTR_ERR(mem); 244 245 pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data), 246 GFP_KERNEL); 247 if (!pdata) 248 return -ENOMEM; 249 250 pdata->regs = mem; 251 pdata->chip_id = BCM63XX_DEVICE_ID; 252 pdata->big_endian = of_property_read_bool(np, "big-endian"); 253 254 of_ports = of_get_child_by_name(np, "ports"); 255 if (!of_ports) { 256 dev_err(dev, "no ports child node found\n"); 257 return -EINVAL; 258 } 259 260 for_each_available_child_of_node(of_ports, of_port) { 261 u32 reg; 262 263 if (of_property_read_u32(of_port, "reg", ®)) 264 continue; 265 266 if (reg < B53_CPU_PORT) 267 pdata->enabled_ports |= BIT(reg); 268 } 269 270 of_node_put(of_ports); 271 *ppdata = pdata; 272 273 return 0; 274 } 275 276 static int b53_mmap_probe(struct platform_device *pdev) 277 { 278 struct device_node *np = pdev->dev.of_node; 279 struct b53_platform_data *pdata = pdev->dev.platform_data; 280 struct b53_mmap_priv *priv; 281 struct b53_device *dev; 282 int ret; 283 284 if (!pdata && np) { 285 ret = b53_mmap_probe_of(pdev, &pdata); 286 if (ret) { 287 dev_err(&pdev->dev, "OF probe error\n"); 288 return ret; 289 } 290 } 291 292 if (!pdata) 293 return -EINVAL; 294 295 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 296 if (!priv) 297 return -ENOMEM; 298 299 priv->regs = pdata->regs; 300 301 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv); 302 if (!dev) 303 return -ENOMEM; 304 305 dev->pdata = pdata; 306 307 platform_set_drvdata(pdev, dev); 308 309 return b53_switch_register(dev); 310 } 311 312 static int b53_mmap_remove(struct platform_device *pdev) 313 { 314 struct b53_device *dev = platform_get_drvdata(pdev); 315 316 if (dev) 317 b53_switch_remove(dev); 318 319 return 0; 320 } 321 322 static const struct of_device_id b53_mmap_of_table[] = { 323 { .compatible = "brcm,bcm3384-switch" }, 324 { .compatible = "brcm,bcm6328-switch" }, 325 { .compatible = "brcm,bcm6368-switch" }, 326 { .compatible = "brcm,bcm63xx-switch" }, 327 { /* sentinel */ }, 328 }; 329 MODULE_DEVICE_TABLE(of, b53_mmap_of_table); 330 331 static struct platform_driver b53_mmap_driver = { 332 .probe = b53_mmap_probe, 333 .remove = b53_mmap_remove, 334 .driver = { 335 .name = "b53-switch", 336 .of_match_table = b53_mmap_of_table, 337 }, 338 }; 339 340 module_platform_driver(b53_mmap_driver); 341 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 342 MODULE_DESCRIPTION("B53 MMAP access driver"); 343 MODULE_LICENSE("Dual BSD/GPL"); 344