brintos

brintos / linux-shallow public Read only

0
0
Text · 7.8 KiB · 467da05 Raw
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