377 lines · c
1/*2 * B53 register access through SPI3 *4 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>5 *6 * Permission to use, copy, modify, and/or distribute this software for any7 * purpose with or without fee is hereby granted, provided that the above8 * copyright notice and this permission notice appear in all copies.9 *10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 */18 19#include <linux/unaligned.h>20 21#include <linux/delay.h>22#include <linux/kernel.h>23#include <linux/module.h>24#include <linux/spi/spi.h>25#include <linux/platform_data/b53.h>26 27#include "b53_priv.h"28 29#define B53_SPI_DATA 0xf030 31#define B53_SPI_STATUS 0xfe32#define B53_SPI_CMD_SPIF BIT(7)33#define B53_SPI_CMD_RACK BIT(5)34 35#define B53_SPI_CMD_READ 0x0036#define B53_SPI_CMD_WRITE 0x0137#define B53_SPI_CMD_NORMAL 0x6038#define B53_SPI_CMD_FAST 0x1039 40#define B53_SPI_PAGE_SELECT 0xff41 42static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,43 unsigned int len)44{45 u8 txbuf[2];46 47 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;48 txbuf[1] = reg;49 50 return spi_write_then_read(spi, txbuf, 2, val, len);51}52 53static inline int b53_spi_clear_status(struct spi_device *spi)54{55 unsigned int i;56 u8 rxbuf;57 int ret;58 59 for (i = 0; i < 10; i++) {60 ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);61 if (ret)62 return ret;63 64 if (!(rxbuf & B53_SPI_CMD_SPIF))65 break;66 67 mdelay(1);68 }69 70 if (i == 10)71 return -EIO;72 73 return 0;74}75 76static inline int b53_spi_set_page(struct spi_device *spi, u8 page)77{78 u8 txbuf[3];79 80 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;81 txbuf[1] = B53_SPI_PAGE_SELECT;82 txbuf[2] = page;83 84 return spi_write(spi, txbuf, sizeof(txbuf));85}86 87static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)88{89 int ret = b53_spi_clear_status(spi);90 91 if (ret)92 return ret;93 94 return b53_spi_set_page(spi, page);95}96 97static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)98{99 u8 rxbuf;100 int retry_count;101 int ret;102 103 ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);104 if (ret)105 return ret;106 107 for (retry_count = 0; retry_count < 10; retry_count++) {108 ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);109 if (ret)110 return ret;111 112 if (rxbuf & B53_SPI_CMD_RACK)113 break;114 115 mdelay(1);116 }117 118 if (retry_count == 10)119 return -EIO;120 121 return 0;122}123 124static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,125 unsigned int len)126{127 struct spi_device *spi = dev->priv;128 int ret;129 130 ret = b53_prepare_reg_access(spi, page);131 if (ret)132 return ret;133 134 ret = b53_spi_prepare_reg_read(spi, reg);135 if (ret)136 return ret;137 138 return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);139}140 141static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)142{143 return b53_spi_read(dev, page, reg, val, 1);144}145 146static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)147{148 __le16 value;149 int ret;150 151 ret = b53_spi_read(dev, page, reg, (u8 *)&value, 2);152 153 if (!ret)154 *val = le16_to_cpu(value);155 156 return ret;157}158 159static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)160{161 __le32 value;162 int ret;163 164 ret = b53_spi_read(dev, page, reg, (u8 *)&value, 4);165 166 if (!ret)167 *val = le32_to_cpu(value);168 169 return ret;170}171 172static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)173{174 __le64 value;175 int ret;176 177 *val = 0;178 ret = b53_spi_read(dev, page, reg, (u8 *)&value, 6);179 if (!ret)180 *val = le64_to_cpu(value);181 182 return ret;183}184 185static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)186{187 __le64 value;188 int ret;189 190 ret = b53_spi_read(dev, page, reg, (u8 *)&value, 8);191 192 if (!ret)193 *val = le64_to_cpu(value);194 195 return ret;196}197 198static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)199{200 struct spi_device *spi = dev->priv;201 int ret;202 u8 txbuf[3];203 204 ret = b53_prepare_reg_access(spi, page);205 if (ret)206 return ret;207 208 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;209 txbuf[1] = reg;210 txbuf[2] = value;211 212 return spi_write(spi, txbuf, sizeof(txbuf));213}214 215static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)216{217 struct spi_device *spi = dev->priv;218 int ret;219 u8 txbuf[4];220 221 ret = b53_prepare_reg_access(spi, page);222 if (ret)223 return ret;224 225 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;226 txbuf[1] = reg;227 put_unaligned_le16(value, &txbuf[2]);228 229 return spi_write(spi, txbuf, sizeof(txbuf));230}231 232static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)233{234 struct spi_device *spi = dev->priv;235 int ret;236 u8 txbuf[6];237 238 ret = b53_prepare_reg_access(spi, page);239 if (ret)240 return ret;241 242 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;243 txbuf[1] = reg;244 put_unaligned_le32(value, &txbuf[2]);245 246 return spi_write(spi, txbuf, sizeof(txbuf));247}248 249static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)250{251 struct spi_device *spi = dev->priv;252 int ret;253 u8 txbuf[10];254 255 ret = b53_prepare_reg_access(spi, page);256 if (ret)257 return ret;258 259 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;260 txbuf[1] = reg;261 put_unaligned_le64(value, &txbuf[2]);262 263 return spi_write(spi, txbuf, sizeof(txbuf) - 2);264}265 266static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)267{268 struct spi_device *spi = dev->priv;269 int ret;270 u8 txbuf[10];271 272 ret = b53_prepare_reg_access(spi, page);273 if (ret)274 return ret;275 276 txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;277 txbuf[1] = reg;278 put_unaligned_le64(value, &txbuf[2]);279 280 return spi_write(spi, txbuf, sizeof(txbuf));281}282 283static const struct b53_io_ops b53_spi_ops = {284 .read8 = b53_spi_read8,285 .read16 = b53_spi_read16,286 .read32 = b53_spi_read32,287 .read48 = b53_spi_read48,288 .read64 = b53_spi_read64,289 .write8 = b53_spi_write8,290 .write16 = b53_spi_write16,291 .write32 = b53_spi_write32,292 .write48 = b53_spi_write48,293 .write64 = b53_spi_write64,294};295 296static int b53_spi_probe(struct spi_device *spi)297{298 struct b53_device *dev;299 int ret;300 301 dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);302 if (!dev)303 return -ENOMEM;304 305 if (spi->dev.platform_data)306 dev->pdata = spi->dev.platform_data;307 308 ret = b53_switch_register(dev);309 if (ret)310 return ret;311 312 spi_set_drvdata(spi, dev);313 314 return 0;315}316 317static void b53_spi_remove(struct spi_device *spi)318{319 struct b53_device *dev = spi_get_drvdata(spi);320 321 if (dev)322 b53_switch_remove(dev);323}324 325static void b53_spi_shutdown(struct spi_device *spi)326{327 struct b53_device *dev = spi_get_drvdata(spi);328 329 if (dev)330 b53_switch_shutdown(dev);331 332 spi_set_drvdata(spi, NULL);333}334 335static const struct of_device_id b53_spi_of_match[] = {336 { .compatible = "brcm,bcm5325" },337 { .compatible = "brcm,bcm5365" },338 { .compatible = "brcm,bcm5395" },339 { .compatible = "brcm,bcm5397" },340 { .compatible = "brcm,bcm5398" },341 { .compatible = "brcm,bcm53115" },342 { .compatible = "brcm,bcm53125" },343 { .compatible = "brcm,bcm53128" },344 { /* sentinel */ }345};346MODULE_DEVICE_TABLE(of, b53_spi_of_match);347 348static const struct spi_device_id b53_spi_ids[] = {349 { .name = "bcm5325" },350 { .name = "bcm5365" },351 { .name = "bcm5395" },352 { .name = "bcm5397" },353 { .name = "bcm5398" },354 { .name = "bcm53115" },355 { .name = "bcm53125" },356 { .name = "bcm53128" },357 { /* sentinel */ }358};359MODULE_DEVICE_TABLE(spi, b53_spi_ids);360 361static struct spi_driver b53_spi_driver = {362 .driver = {363 .name = "b53-switch",364 .of_match_table = b53_spi_of_match,365 },366 .probe = b53_spi_probe,367 .remove = b53_spi_remove,368 .shutdown = b53_spi_shutdown,369 .id_table = b53_spi_ids,370};371 372module_spi_driver(b53_spi_driver);373 374MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");375MODULE_DESCRIPTION("B53 SPI access driver");376MODULE_LICENSE("Dual BSD/GPL");377