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 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 41 static 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 57 static 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 73 static 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 112 static 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 134 static 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 144 static 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 161 static 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 178 static 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 201 static 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 218 static const struct b53_io_ops b53_mmap_ops = { 219 .read8 = b53_mmap_read8, 220 .read16 = b53_mmap_read16, 221 .read32 = b53_mmap_read32, 222 .read48 = b53_mmap_read48, 223 .read64 = b53_mmap_read64, 224 .write8 = b53_mmap_write8, 225 .write16 = b53_mmap_write16, 226 .write32 = b53_mmap_write32, 227 .write48 = b53_mmap_write48, 228 .write64 = b53_mmap_write64, 229 }; 230 231 static int b53_mmap_probe(struct platform_device *pdev) 232 { 233 struct b53_platform_data *pdata = pdev->dev.platform_data; 234 struct b53_mmap_priv *priv; 235 struct b53_device *dev; 236 237 if (!pdata) 238 return -EINVAL; 239 240 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 241 if (!priv) 242 return -ENOMEM; 243 244 priv->regs = pdata->regs; 245 246 dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv); 247 if (!dev) 248 return -ENOMEM; 249 250 dev->pdata = pdata; 251 252 platform_set_drvdata(pdev, dev); 253 254 return b53_switch_register(dev); 255 } 256 257 static int b53_mmap_remove(struct platform_device *pdev) 258 { 259 struct b53_device *dev = platform_get_drvdata(pdev); 260 261 if (dev) 262 b53_switch_remove(dev); 263 264 return 0; 265 } 266 267 static const struct of_device_id b53_mmap_of_table[] = { 268 { .compatible = "brcm,bcm3384-switch" }, 269 { .compatible = "brcm,bcm6328-switch" }, 270 { .compatible = "brcm,bcm6368-switch" }, 271 { .compatible = "brcm,bcm63xx-switch" }, 272 { /* sentinel */ }, 273 }; 274 MODULE_DEVICE_TABLE(of, b53_mmap_of_table); 275 276 static struct platform_driver b53_mmap_driver = { 277 .probe = b53_mmap_probe, 278 .remove = b53_mmap_remove, 279 .driver = { 280 .name = "b53-switch", 281 .of_match_table = b53_mmap_of_table, 282 }, 283 }; 284 285 module_platform_driver(b53_mmap_driver); 286 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 287 MODULE_DESCRIPTION("B53 MMAP access driver"); 288 MODULE_LICENSE("Dual BSD/GPL"); 289