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