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 27 struct b53_mmap_priv { 28 void __iomem *regs; 29 }; 30 31 static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) 32 { 33 u8 __iomem *regs = dev->priv; 34 35 *val = readb(regs + (page << 8) + reg); 36 37 return 0; 38 } 39 40 static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) 41 { 42 u8 __iomem *regs = dev->priv; 43 44 if (WARN_ON(reg % 2)) 45 return -EINVAL; 46 47 if (dev->pdata && dev->pdata->big_endian) 48 *val = ioread16be(regs + (page << 8) + reg); 49 else 50 *val = readw(regs + (page << 8) + reg); 51 52 return 0; 53 } 54 55 static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) 56 { 57 u8 __iomem *regs = dev->priv; 58 59 if (WARN_ON(reg % 4)) 60 return -EINVAL; 61 62 if (dev->pdata && dev->pdata->big_endian) 63 *val = ioread32be(regs + (page << 8) + reg); 64 else 65 *val = readl(regs + (page << 8) + reg); 66 67 return 0; 68 } 69 70 static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) 71 { 72 u8 __iomem *regs = dev->priv; 73 74 if (WARN_ON(reg % 2)) 75 return -EINVAL; 76 77 if (reg % 4) { 78 u16 lo; 79 u32 hi; 80 81 if (dev->pdata && dev->pdata->big_endian) { 82 lo = ioread16be(regs + (page << 8) + reg); 83 hi = ioread32be(regs + (page << 8) + reg + 2); 84 } else { 85 lo = readw(regs + (page << 8) + reg); 86 hi = readl(regs + (page << 8) + reg + 2); 87 } 88 89 *val = ((u64)hi << 16) | lo; 90 } else { 91 u32 lo; 92 u16 hi; 93 94 if (dev->pdata && dev->pdata->big_endian) { 95 lo = ioread32be(regs + (page << 8) + reg); 96 hi = ioread16be(regs + (page << 8) + reg + 4); 97 } else { 98 lo = readl(regs + (page << 8) + reg); 99 hi = readw(regs + (page << 8) + reg + 4); 100 } 101 102 *val = ((u64)hi << 32) | lo; 103 } 104 105 return 0; 106 } 107 108 static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) 109 { 110 u8 __iomem *regs = dev->priv; 111 u32 hi, lo; 112 113 if (WARN_ON(reg % 4)) 114 return -EINVAL; 115 116 if (dev->pdata && dev->pdata->big_endian) { 117 lo = ioread32be(regs + (page << 8) + reg); 118 hi = ioread32be(regs + (page << 8) + reg + 4); 119 } else { 120 lo = readl(regs + (page << 8) + reg); 121 hi = readl(regs + (page << 8) + reg + 4); 122 } 123 124 *val = ((u64)hi << 32) | lo; 125 126 return 0; 127 } 128 129 static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) 130 { 131 u8 __iomem *regs = dev->priv; 132 133 writeb(value, regs + (page << 8) + reg); 134 135 return 0; 136 } 137 138 static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, 139 u16 value) 140 { 141 u8 __iomem *regs = dev->priv; 142 143 if (WARN_ON(reg % 2)) 144 return -EINVAL; 145 146 if (dev->pdata && dev->pdata->big_endian) 147 iowrite16be(value, regs + (page << 8) + reg); 148 else 149 writew(value, regs + (page << 8) + reg); 150 151 return 0; 152 } 153 154 static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, 155 u32 value) 156 { 157 u8 __iomem *regs = dev->priv; 158 159 if (WARN_ON(reg % 4)) 160 return -EINVAL; 161 162 if (dev->pdata && dev->pdata->big_endian) 163 iowrite32be(value, regs + (page << 8) + reg); 164 else 165 writel(value, regs + (page << 8) + reg); 166 167 return 0; 168 } 169 170 static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, 171 u64 value) 172 { 173 if (WARN_ON(reg % 2)) 174 return -EINVAL; 175 176 if (reg % 4) { 177 u32 hi = (u32)(value >> 16); 178 u16 lo = (u16)value; 179 180 b53_mmap_write16(dev, page, reg, lo); 181 b53_mmap_write32(dev, page, reg + 2, hi); 182 } else { 183 u16 hi = (u16)(value >> 32); 184 u32 lo = (u32)value; 185 186 b53_mmap_write32(dev, page, reg, lo); 187 b53_mmap_write16(dev, page, reg + 4, hi); 188 } 189 190 return 0; 191 } 192 193 static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, 194 u64 value) 195 { 196 u32 hi, lo; 197 198 hi = upper_32_bits(value); 199 lo = lower_32_bits(value); 200 201 if (WARN_ON(reg % 4)) 202 return -EINVAL; 203 204 b53_mmap_write32(dev, page, reg, lo); 205 b53_mmap_write32(dev, page, reg + 4, hi); 206 207 return 0; 208 } 209 210 static const struct b53_io_ops b53_mmap_ops = { 211 .read8 = b53_mmap_read8, 212 .read16 = b53_mmap_read16, 213 .read32 = b53_mmap_read32, 214 .read48 = b53_mmap_read48, 215 .read64 = b53_mmap_read64, 216 .write8 = b53_mmap_write8, 217 .write16 = b53_mmap_write16, 218 .write32 = b53_mmap_write32, 219 .write48 = b53_mmap_write48, 220 .write64 = b53_mmap_write64, 221 }; 222 223 static int b53_mmap_probe(struct platform_device *pdev) 224 { 225 struct b53_platform_data *pdata = pdev->dev.platform_data; 226 struct b53_device *dev; 227 228 if (!pdata) 229 return -EINVAL; 230 231 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); 232 if (!dev) 233 return -ENOMEM; 234 235 dev->pdata = pdata; 236 237 platform_set_drvdata(pdev, dev); 238 239 return b53_switch_register(dev); 240 } 241 242 static int b53_mmap_remove(struct platform_device *pdev) 243 { 244 struct b53_device *dev = platform_get_drvdata(pdev); 245 246 if (dev) 247 b53_switch_remove(dev); 248 249 return 0; 250 } 251 252 static const struct of_device_id b53_mmap_of_table[] = { 253 { .compatible = "brcm,bcm3384-switch" }, 254 { .compatible = "brcm,bcm6328-switch" }, 255 { .compatible = "brcm,bcm6368-switch" }, 256 { .compatible = "brcm,bcm63xx-switch" }, 257 { /* sentinel */ }, 258 }; 259 260 static struct platform_driver b53_mmap_driver = { 261 .probe = b53_mmap_probe, 262 .remove = b53_mmap_remove, 263 .driver = { 264 .name = "b53-switch", 265 .of_match_table = b53_mmap_of_table, 266 }, 267 }; 268 269 module_platform_driver(b53_mmap_driver); 270 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 271 MODULE_DESCRIPTION("B53 MMAP access driver"); 272 MODULE_LICENSE("Dual BSD/GPL"); 273