From 1a8d3b777dd71813b87c66c0eb161cc87f042126 Mon Sep 17 00:00:00 2001 From: John Linn Date: Mon, 14 Sep 2009 08:17:05 +0000 Subject: Xilinx: SPI: Fix bits_per_word for transfers The bits_per_word value can be set for each transfer, or can be set to zero in each transfer in which case it should default to the value in the driver. The driver was fixed to properly check the bits_per_word in the transfer that is passed in. Signed-off-by: John Linn Signed-off-by: Grant Likely --- drivers/spi/xilinx_spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi/xilinx_spi.c') diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 46b8c5c2f45..5a143b9f636 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -148,7 +148,8 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, { u8 bits_per_word; - bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; + bits_per_word = (t && t->bits_per_word) + ? t->bits_per_word : spi->bits_per_word; if (bits_per_word != 8) { dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", __func__, bits_per_word); -- cgit v1.2.3-70-g09d2 From d5af91a1faca68e9a8cc493b85aa7b194b6128aa Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Fri, 13 Nov 2009 12:28:39 +0100 Subject: xilinx_spi: Split into of driver and generic part. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch splits the xilinx_spi driver into a generic part and a OF driver part. The reason for this is to later add in a platform driver as well. Tested-by: John Linn Signed-off-by: Richard Röjfors Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 9 ++- drivers/spi/Makefile | 1 + drivers/spi/xilinx_spi.c | 159 ++++++++++------------------------------- drivers/spi/xilinx_spi.h | 32 +++++++++ drivers/spi/xilinx_spi_of.c | 133 ++++++++++++++++++++++++++++++++++ include/linux/spi/xilinx_spi.h | 16 +++++ 6 files changed, 229 insertions(+), 121 deletions(-) create mode 100644 drivers/spi/xilinx_spi.h create mode 100644 drivers/spi/xilinx_spi_of.c create mode 100644 include/linux/spi/xilinx_spi.h (limited to 'drivers/spi/xilinx_spi.c') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2a4ba199308..f34a2d16d18 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -244,14 +244,21 @@ config SPI_TXX9 config SPI_XILINX tristate "Xilinx SPI controller" - depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL + depends on EXPERIMENTAL select SPI_BITBANG + select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) help This exposes the SPI controller IP from the Xilinx EDK. See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" Product Specification document (DS464) for hardware details. +config SPI_XILINX_OF + tristate "Xilinx SPI controller OF device" + depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE) + help + This is the OF driver for the SPI controller IP from the Xilinx EDK. + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3f092a9afa..01c40954804 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o # ... add above this line ... diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 5a143b9f636..69fa26d82ce 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -14,16 +14,14 @@ #include #include #include -#include - -#include -#include -#include #include #include #include +#include "xilinx_spi.h" +#include + #define XILINX_SPI_NAME "xilinx_spi" /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) @@ -78,7 +76,7 @@ struct xilinx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; struct completion done; - + struct resource mem; /* phys mem */ void __iomem *regs; /* virt. address of the control registers */ u32 irq; @@ -284,40 +282,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init xilinx_spi_of_probe(struct of_device *ofdev, - const struct of_device_id *match) +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, + u32 irq, s16 bus_num) { struct spi_master *master; struct xilinx_spi *xspi; - struct resource r_irq_struct; - struct resource r_mem_struct; - - struct resource *r_irq = &r_irq_struct; - struct resource *r_mem = &r_mem_struct; - int rc = 0; - const u32 *prop; - int len; - - /* Get resources(memory, IRQ) associated with the device */ - master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); + struct xspi_platform_data *pdata = dev->platform_data; + int ret; - if (master == NULL) { - return -ENOMEM; - } - - dev_set_drvdata(&ofdev->dev, master); - - rc = of_address_to_resource(ofdev->node, 0, r_mem); - if (rc) { - dev_warn(&ofdev->dev, "invalid address\n"); - goto put_master; + if (!pdata) { + dev_err(dev, "No platform data attached\n"); + return NULL; } - rc = of_irq_to_resource(ofdev->node, 0, r_irq); - if (rc == NO_IRQ) { - dev_warn(&ofdev->dev, "no IRQ found\n"); - goto put_master; - } + master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); + if (!master) + return NULL; /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA; @@ -330,128 +310,67 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev, xspi->bitbang.master->setup = xilinx_spi_setup; init_completion(&xspi->done); - xspi->irq = r_irq->start; - - if (!request_mem_region(r_mem->start, - r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { - rc = -ENXIO; - dev_warn(&ofdev->dev, "memory request failure\n"); + if (!request_mem_region(mem->start, resource_size(mem), + XILINX_SPI_NAME)) goto put_master; - } - xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); + xspi->regs = ioremap(mem->start, resource_size(mem)); if (xspi->regs == NULL) { - rc = -ENOMEM; - dev_warn(&ofdev->dev, "ioremap failure\n"); - goto release_mem; + dev_warn(dev, "ioremap failure\n"); + goto map_failed; } - xspi->irq = r_irq->start; - /* dynamic bus assignment */ - master->bus_num = -1; + master->bus_num = bus_num; + master->num_chipselect = pdata->num_chipselect; - /* number of slave select bits is required */ - prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); - if (!prop || len < sizeof(*prop)) { - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); - goto unmap_io; - } - master->num_chipselect = *prop; + xspi->mem = *mem; + xspi->irq = irq; /* SPI controller initializations */ xspi_init_hw(xspi->regs); /* Register for SPI Interrupt */ - rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); - if (rc != 0) { - dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); + if (ret) goto unmap_io; - } - rc = spi_bitbang_start(&xspi->bitbang); - if (rc != 0) { - dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); + ret = spi_bitbang_start(&xspi->bitbang); + if (ret) { + dev_err(dev, "spi_bitbang_start FAILED\n"); goto free_irq; } - dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", - (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); - - /* Add any subnodes on the SPI bus */ - of_register_spi_devices(master, ofdev->node); - - return rc; + dev_info(dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", + (u32)mem->start, (u32)xspi->regs, xspi->irq); + return master; free_irq: free_irq(xspi->irq, xspi); unmap_io: iounmap(xspi->regs); -release_mem: - release_mem_region(r_mem->start, resource_size(r_mem)); +map_failed: + release_mem_region(mem->start, resource_size(mem)); put_master: spi_master_put(master); - return rc; + return NULL; } +EXPORT_SYMBOL(xilinx_spi_init); -static int __devexit xilinx_spi_remove(struct of_device *ofdev) +void xilinx_spi_deinit(struct spi_master *master) { struct xilinx_spi *xspi; - struct spi_master *master; - struct resource r_mem; - master = platform_get_drvdata(ofdev); xspi = spi_master_get_devdata(master); spi_bitbang_stop(&xspi->bitbang); free_irq(xspi->irq, xspi); iounmap(xspi->regs); - if (!of_address_to_resource(ofdev->node, 0, &r_mem)) - release_mem_region(r_mem.start, resource_size(&r_mem)); - dev_set_drvdata(&ofdev->dev, 0); - spi_master_put(xspi->bitbang.master); - - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:" XILINX_SPI_NAME); - -static int __exit xilinx_spi_of_remove(struct of_device *op) -{ - return xilinx_spi_remove(op); -} -static struct of_device_id xilinx_spi_of_match[] = { - { .compatible = "xlnx,xps-spi-2.00.a", }, - { .compatible = "xlnx,xps-spi-2.00.b", }, - {} -}; - -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); - -static struct of_platform_driver xilinx_spi_of_driver = { - .owner = THIS_MODULE, - .name = "xilinx-xps-spi", - .match_table = xilinx_spi_of_match, - .probe = xilinx_spi_of_probe, - .remove = __exit_p(xilinx_spi_of_remove), - .driver = { - .name = "xilinx-xps-spi", - .owner = THIS_MODULE, - }, -}; - -static int __init xilinx_spi_init(void) -{ - return of_register_platform_driver(&xilinx_spi_of_driver); + release_mem_region(xspi->mem.start, resource_size(&xspi->mem)); + spi_master_put(xspi->bitbang.master); } -module_init(xilinx_spi_init); +EXPORT_SYMBOL(xilinx_spi_deinit); -static void __exit xilinx_spi_exit(void) -{ - of_unregister_platform_driver(&xilinx_spi_of_driver); -} -module_exit(xilinx_spi_exit); MODULE_AUTHOR("MontaVista Software, Inc. "); MODULE_DESCRIPTION("Xilinx SPI driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h new file mode 100644 index 00000000000..d211accf68d --- /dev/null +++ b/drivers/spi/xilinx_spi.h @@ -0,0 +1,32 @@ +/* + * Xilinx SPI device driver API and platform data header file + * + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _XILINX_SPI_H_ +#define _XILINX_SPI_H_ + +#include +#include + +#define XILINX_SPI_NAME "xilinx_spi" + +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, + u32 irq, s16 bus_num); + +void xilinx_spi_deinit(struct spi_master *master); +#endif diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c new file mode 100644 index 00000000000..151aa13494b --- /dev/null +++ b/drivers/spi/xilinx_spi_of.c @@ -0,0 +1,133 @@ +/* + * Xilinx SPI OF device driver + * + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Xilinx SPI devices as OF devices + * + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "xilinx_spi.h" + + +static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct spi_master *master; + struct xspi_platform_data *pdata; + struct resource r_mem; + struct resource r_irq; + int rc = 0; + const u32 *prop; + int len; + + rc = of_address_to_resource(ofdev->node, 0, &r_mem); + if (rc) { + dev_warn(&ofdev->dev, "invalid address\n"); + return rc; + } + + rc = of_irq_to_resource(ofdev->node, 0, &r_irq); + if (rc == NO_IRQ) { + dev_warn(&ofdev->dev, "no IRQ found\n"); + return -ENODEV; + } + + ofdev->dev.platform_data = + kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL); + pdata = ofdev->dev.platform_data; + if (!pdata) + return -ENOMEM; + + /* number of slave select bits is required */ + prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); + if (!prop || len < sizeof(*prop)) { + dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); + return -EINVAL; + } + pdata->num_chipselect = *prop; + master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1); + if (!master) + return -ENODEV; + + dev_set_drvdata(&ofdev->dev, master); + + /* Add any subnodes on the SPI bus */ + of_register_spi_devices(master, ofdev->node); + + return 0; +} + +static int __devexit xilinx_spi_remove(struct of_device *ofdev) +{ + xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); + dev_set_drvdata(&ofdev->dev, 0); + kfree(ofdev->dev.platform_data); + ofdev->dev.platform_data = NULL; + return 0; +} + +static int __exit xilinx_spi_of_remove(struct of_device *op) +{ + return xilinx_spi_remove(op); +} + +static struct of_device_id xilinx_spi_of_match[] = { + { .compatible = "xlnx,xps-spi-2.00.a", }, + { .compatible = "xlnx,xps-spi-2.00.b", }, + {} +}; + +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); + +static struct of_platform_driver xilinx_spi_of_driver = { + .match_table = xilinx_spi_of_match, + .probe = xilinx_spi_of_probe, + .remove = __exit_p(xilinx_spi_of_remove), + .driver = { + .name = "xilinx-xps-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init xilinx_spi_of_init(void) +{ + return of_register_platform_driver(&xilinx_spi_of_driver); +} +module_init(xilinx_spi_of_init); + +static void __exit xilinx_spi_of_exit(void) +{ + of_unregister_platform_driver(&xilinx_spi_of_driver); +} +module_exit(xilinx_spi_of_exit); + +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_DESCRIPTION("Xilinx SPI platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h new file mode 100644 index 00000000000..06df0aba3ae --- /dev/null +++ b/include/linux/spi/xilinx_spi.h @@ -0,0 +1,16 @@ +#ifndef __LINUX_SPI_XILINX_SPI_H +#define __LINUX_SPI_XILINX_SPI_H + +/** + * struct xspi_platform_data - Platform data of the Xilinx SPI driver + * @num_chipselect: Number of chip select by the IP + * @devices: Devices to add when the driver is probed. + * @num_devices: Number of devices in the devices array. + */ +struct xspi_platform_data { + u16 num_chipselect; + struct spi_board_info *devices; + u8 num_devices; +}; + +#endif /* __LINUX_SPI_XILINX_SPI_H */ -- cgit v1.2.3-70-g09d2 From 86fc593599c11b62a11c85b4d7b709089df15c29 Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Fri, 13 Nov 2009 12:28:49 +0100 Subject: xilinx_spi: Switch to iomem functions and support little endian. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch changes the out_(be)(8|16|32) and in_(be)(8|16|32) calls to 32 bits ioread/iowrite. The read and write function are attached to the internal struct as callbacks, callback is selected depending on endianess. This will also build on platforms not supporting the in/out calls for instance x86. Acked-by: Grant Likely Tested-by: John Linn Signed-off-by: Richard Röjfors Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 2 +- drivers/spi/xilinx_spi.c | 96 ++++++++++++++++++++++++------------------ include/linux/spi/xilinx_spi.h | 2 + 3 files changed, 57 insertions(+), 43 deletions(-) (limited to 'drivers/spi/xilinx_spi.c') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f34a2d16d18..b528271c72e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -244,7 +244,7 @@ config SPI_TXX9 config SPI_XILINX tristate "Xilinx SPI controller" - depends on EXPERIMENTAL + depends on HAS_IOMEM && EXPERIMENTAL select SPI_BITBANG select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) help diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 69fa26d82ce..135a3a5931a 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -27,7 +27,7 @@ /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) * Product Specification", DS464 */ -#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ +#define XSPI_CR_OFFSET 0x60 /* 16-bit Control Register */ #define XSPI_CR_ENABLE 0x02 #define XSPI_CR_MASTER_MODE 0x04 @@ -39,7 +39,7 @@ #define XSPI_CR_MANUAL_SSELECT 0x80 #define XSPI_CR_TRANS_INHIBIT 0x100 -#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ +#define XSPI_SR_OFFSET 0x64 /* 8-bit Status Register */ #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ @@ -47,8 +47,8 @@ #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ -#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ -#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ +#define XSPI_TXD_OFFSET 0x68 /* 8-bit Data Transmit Register */ +#define XSPI_RXD_OFFSET 0x6c /* 8-bit Data Receive Register */ #define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ @@ -86,25 +86,29 @@ struct xilinx_spi { u8 *rx_ptr; /* pointer in the Tx buffer */ const u8 *tx_ptr; /* pointer in the Rx buffer */ int remaining_bytes; /* the number of bytes left to transfer */ + unsigned int (*read_fn) (void __iomem *); + void (*write_fn) (u32, void __iomem *); }; -static void xspi_init_hw(void __iomem *regs_base) +static void xspi_init_hw(struct xilinx_spi *xspi) { + void __iomem *regs_base = xspi->regs; + /* Reset the SPI device */ - out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, - XIPIF_V123B_RESET_MASK); + xspi->write_fn(XIPIF_V123B_RESET_MASK, + regs_base + XIPIF_V123B_RESETR_OFFSET); /* Disable all the interrupts just in case */ - out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0); + xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); /* Enable the global IPIF interrupt */ - out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, - XIPIF_V123B_GINTR_ENABLE); + xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, + regs_base + XIPIF_V123B_DGIER_OFFSET); /* Deselect the slave on the SPI bus */ - out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); + xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET); /* Disable the transmitter, enable Manual Slave Select Assertion, * put SPI controller into master mode, and enable it */ - out_be16(regs_base + XSPI_CR_OFFSET, - XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT - | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); + xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | + XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE, + regs_base + XSPI_CR_OFFSET); } static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) @@ -113,16 +117,16 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) if (is_on == BITBANG_CS_INACTIVE) { /* Deselect the slave on the SPI bus */ - out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); + xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET); } else if (is_on == BITBANG_CS_ACTIVE) { /* Set the SPI clock phase and polarity */ - u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) + u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_MODE_MASK; if (spi->mode & SPI_CPHA) cr |= XSPI_CR_CPHA; if (spi->mode & SPI_CPOL) cr |= XSPI_CR_CPOL; - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); /* We do not check spi->max_speed_hz here as the SPI clock * frequency is not software programmable (the IP block design @@ -130,8 +134,8 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) */ /* Activate the chip select */ - out_be32(xspi->regs + XSPI_SSR_OFFSET, - ~(0x0001 << spi->chip_select)); + xspi->write_fn(~(0x0001 << spi->chip_select), + xspi->regs + XSPI_SSR_OFFSET); } } @@ -178,15 +182,15 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) u8 sr; /* Fill the Tx FIFO with as many bytes as possible */ - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { - if (xspi->tx_ptr) { - out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); - } else { - out_8(xspi->regs + XSPI_TXD_OFFSET, 0); - } + if (xspi->tx_ptr) + xspi->write_fn(*xspi->tx_ptr++, + xspi->regs + XSPI_TXD_OFFSET); + else + xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); xspi->remaining_bytes--; - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); } } @@ -208,18 +212,19 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) /* Enable the transmit empty interrupt, which we use to determine * progress on the transmission. */ - ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET); - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, - ipif_ier | XSPI_INTR_TX_EMPTY); + ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET); + xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY, + xspi->regs + XIPIF_V123B_IIER_OFFSET); /* Start the transfer by not inhibiting the transmitter any longer */ - cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & + ~XSPI_CR_TRANS_INHIBIT; + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); wait_for_completion(&xspi->done); /* Disable the transmit empty interrupt */ - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier); + xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET); return t->len - xspi->remaining_bytes; } @@ -236,8 +241,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) u32 ipif_isr; /* Get the IPIF interrupts, and clear them immediately */ - ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET); - out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr); + ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); + xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET); if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ u16 cr; @@ -248,20 +253,20 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) * transmitter while the Isr refills the transmit register/FIFO, * or make sure it is stopped if we're done. */ - cr = in_be16(xspi->regs + XSPI_CR_OFFSET); - out_be16(xspi->regs + XSPI_CR_OFFSET, - cr | XSPI_CR_TRANS_INHIBIT); + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); + xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, + xspi->regs + XSPI_CR_OFFSET); /* Read out all the data from the Rx FIFO */ - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { u8 data; - data = in_8(xspi->regs + XSPI_RXD_OFFSET); + data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); if (xspi->rx_ptr) { *xspi->rx_ptr++ = data; } - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); } /* See if there is more data to send */ @@ -270,7 +275,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) /* Start the transfer by not inhibiting the * transmitter any longer */ - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); } else { /* No more data to send. * Indicate the transfer is completed. @@ -325,9 +330,16 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->mem = *mem; xspi->irq = irq; + if (pdata->little_endian) { + xspi->read_fn = ioread32; + xspi->write_fn = iowrite32; + } else { + xspi->read_fn = ioread32be; + xspi->write_fn = iowrite32be; + } /* SPI controller initializations */ - xspi_init_hw(xspi->regs); + xspi_init_hw(xspi); /* Register for SPI Interrupt */ ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h index 06df0aba3ae..a705ad85eeb 100644 --- a/include/linux/spi/xilinx_spi.h +++ b/include/linux/spi/xilinx_spi.h @@ -4,11 +4,13 @@ /** * struct xspi_platform_data - Platform data of the Xilinx SPI driver * @num_chipselect: Number of chip select by the IP + * @little_endian If registers should be accessed little endian or not * @devices: Devices to add when the driver is probed. * @num_devices: Number of devices in the devices array. */ struct xspi_platform_data { u16 num_chipselect; + bool little_endian; struct spi_board_info *devices; u8 num_devices; }; -- cgit v1.2.3-70-g09d2 From c9da2e125588677d74324df5088149063d578e8f Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Fri, 13 Nov 2009 12:28:55 +0100 Subject: xilinx_spi: add support for the DS570 IP. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds in support for the DS570 IP. It's register compatible with the DS464, but adds support for 8/16/32 SPI. The 8/16/32 support is added by attaching callbacks reading/writing the proper amount of data. To indicate to the driver which amount of bits to use a new field is introduced in the platform data struct. Acked-by: Grant Likely Tested-by: John Linn Signed-off-by: Richard Röjfors Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 +- drivers/spi/xilinx_spi.c | 118 ++++++++++++++++++++++++++++++----------- drivers/spi/xilinx_spi_of.c | 1 + include/linux/spi/xilinx_spi.h | 6 ++- 4 files changed, 95 insertions(+), 34 deletions(-) (limited to 'drivers/spi/xilinx_spi.c') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b528271c72e..4dda097c239 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -243,7 +243,7 @@ config SPI_TXX9 SPI driver for Toshiba TXx9 MIPS SoCs config SPI_XILINX - tristate "Xilinx SPI controller" + tristate "Xilinx SPI controller common module" depends on HAS_IOMEM && EXPERIMENTAL select SPI_BITBANG select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) @@ -253,6 +253,8 @@ config SPI_XILINX See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" Product Specification document (DS464) for hardware details. + Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" + config SPI_XILINX_OF tristate "Xilinx SPI controller OF device" depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE) diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 135a3a5931a..b927812822c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -27,7 +27,7 @@ /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) * Product Specification", DS464 */ -#define XSPI_CR_OFFSET 0x60 /* 16-bit Control Register */ +#define XSPI_CR_OFFSET 0x60 /* Control Register */ #define XSPI_CR_ENABLE 0x02 #define XSPI_CR_MASTER_MODE 0x04 @@ -38,8 +38,9 @@ #define XSPI_CR_RXFIFO_RESET 0x40 #define XSPI_CR_MANUAL_SSELECT 0x80 #define XSPI_CR_TRANS_INHIBIT 0x100 +#define XSPI_CR_LSB_FIRST 0x200 -#define XSPI_SR_OFFSET 0x64 /* 8-bit Status Register */ +#define XSPI_SR_OFFSET 0x64 /* Status Register */ #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ @@ -47,8 +48,8 @@ #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ -#define XSPI_TXD_OFFSET 0x68 /* 8-bit Data Transmit Register */ -#define XSPI_RXD_OFFSET 0x6c /* 8-bit Data Receive Register */ +#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */ +#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */ #define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ @@ -68,6 +69,7 @@ #define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */ #define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */ #define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */ +#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */ #define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ #define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ @@ -81,15 +83,61 @@ struct xilinx_spi { u32 irq; - u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ - u8 *rx_ptr; /* pointer in the Tx buffer */ const u8 *tx_ptr; /* pointer in the Rx buffer */ int remaining_bytes; /* the number of bytes left to transfer */ + u8 bits_per_word; unsigned int (*read_fn) (void __iomem *); void (*write_fn) (u32, void __iomem *); + void (*tx_fn) (struct xilinx_spi *); + void (*rx_fn) (struct xilinx_spi *); }; +static void xspi_tx8(struct xilinx_spi *xspi) +{ + xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET); + xspi->tx_ptr++; +} + +static void xspi_tx16(struct xilinx_spi *xspi) +{ + xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); + xspi->tx_ptr += 2; +} + +static void xspi_tx32(struct xilinx_spi *xspi) +{ + xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); + xspi->tx_ptr += 4; +} + +static void xspi_rx8(struct xilinx_spi *xspi) +{ + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); + if (xspi->rx_ptr) { + *xspi->rx_ptr = data & 0xff; + xspi->rx_ptr++; + } +} + +static void xspi_rx16(struct xilinx_spi *xspi) +{ + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); + if (xspi->rx_ptr) { + *(u16 *)(xspi->rx_ptr) = data & 0xffff; + xspi->rx_ptr += 2; + } +} + +static void xspi_rx32(struct xilinx_spi *xspi) +{ + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); + if (xspi->rx_ptr) { + *(u32 *)(xspi->rx_ptr) = data; + xspi->rx_ptr += 4; + } +} + static void xspi_init_hw(struct xilinx_spi *xspi) { void __iomem *regs_base = xspi->regs; @@ -107,8 +155,8 @@ static void xspi_init_hw(struct xilinx_spi *xspi) /* Disable the transmitter, enable Manual Slave Select Assertion, * put SPI controller into master mode, and enable it */ xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | - XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE, - regs_base + XSPI_CR_OFFSET); + XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | + XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET); } static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) @@ -141,18 +189,20 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) /* spi_bitbang requires custom setup_transfer() to be defined if there is a * custom txrx_bufs(). We have nothing to setup here as the SPI IP block - * supports just 8 bits per word, and SPI clock can't be changed in software. - * Check for 8 bits per word. Chip select delay calculations could be + * supports 8 or 16 bits per word which cannot be changed in software. + * SPI clock can't be changed in software either. + * Check for correct bits per word. Chip select delay calculations could be * added here as soon as bitbang_work() can be made aware of the delay value. */ static int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); u8 bits_per_word; bits_per_word = (t && t->bits_per_word) ? t->bits_per_word : spi->bits_per_word; - if (bits_per_word != 8) { + if (bits_per_word != xspi->bits_per_word) { dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", __func__, bits_per_word); return -EINVAL; @@ -163,17 +213,16 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, static int xilinx_spi_setup(struct spi_device *spi) { - struct spi_bitbang *bitbang; - struct xilinx_spi *xspi; - int retval; - - xspi = spi_master_get_devdata(spi->master); - bitbang = &xspi->bitbang; - - retval = xilinx_spi_setup_transfer(spi, NULL); - if (retval < 0) - return retval; - + /* always return 0, we can not check the number of bits. + * There are cases when SPI setup is called before any driver is + * there, in that case the SPI core defaults to 8 bits, which we + * do not support in some cases. But if we return an error, the + * SPI device would not be registered and no driver can get hold of it + * When the driver is there, it will call SPI setup again with the + * correct number of bits per transfer. + * If a driver setups with the wrong bit number, it will fail when + * it tries to do a transfer + */ return 0; } @@ -185,11 +234,10 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { if (xspi->tx_ptr) - xspi->write_fn(*xspi->tx_ptr++, - xspi->regs + XSPI_TXD_OFFSET); + xspi->tx_fn(xspi); else xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); - xspi->remaining_bytes--; + xspi->remaining_bytes -= xspi->bits_per_word / 8; sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); } } @@ -260,12 +308,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) /* Read out all the data from the Rx FIFO */ sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { - u8 data; - - data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); - if (xspi->rx_ptr) { - *xspi->rx_ptr++ = data; - } + xspi->rx_fn(xspi); sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); } @@ -337,6 +380,19 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->read_fn = ioread32be; xspi->write_fn = iowrite32be; } + xspi->bits_per_word = pdata->bits_per_word; + if (xspi->bits_per_word == 8) { + xspi->tx_fn = xspi_tx8; + xspi->rx_fn = xspi_rx8; + } else if (xspi->bits_per_word == 16) { + xspi->tx_fn = xspi_tx16; + xspi->rx_fn = xspi_rx16; + } else if (xspi->bits_per_word == 32) { + xspi->tx_fn = xspi_tx32; + xspi->rx_fn = xspi_rx32; + } else + goto unmap_io; + /* SPI controller initializations */ xspi_init_hw(xspi); diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c index 151aa13494b..71dc3adc049 100644 --- a/drivers/spi/xilinx_spi_of.c +++ b/drivers/spi/xilinx_spi_of.c @@ -72,6 +72,7 @@ static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, return -EINVAL; } pdata->num_chipselect = *prop; + pdata->bits_per_word = 8; master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1); if (!master) return -ENODEV; diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h index a705ad85eeb..6f17278810b 100644 --- a/include/linux/spi/xilinx_spi.h +++ b/include/linux/spi/xilinx_spi.h @@ -3,14 +3,16 @@ /** * struct xspi_platform_data - Platform data of the Xilinx SPI driver - * @num_chipselect: Number of chip select by the IP - * @little_endian If registers should be accessed little endian or not + * @num_chipselect: Number of chip select by the IP. + * @little_endian: If registers should be accessed little endian or not. + * @bits_per_word: Number of bits per word. * @devices: Devices to add when the driver is probed. * @num_devices: Number of devices in the devices array. */ struct xspi_platform_data { u16 num_chipselect; bool little_endian; + u8 bits_per_word; struct spi_board_info *devices; u8 num_devices; }; -- cgit v1.2.3-70-g09d2 From 920712af498a3081a9ac8256951d1c7eae109c50 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 25 Nov 2009 07:23:35 -0700 Subject: spi/xilinx_spi: fix incorrect casting This patch fixes the error exposed by the following build warning: drivers/spi/xilinx_spi.c: In function 'xilinx_spi_init': drivers/spi/xilinx_spi.c:411: warning: cast from pointer to integer of different size Fixed by change %x to %p in the format string. Signed-off-by: Grant Likely --- drivers/spi/xilinx_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi/xilinx_spi.c') diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index b927812822c..9f386379c16 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -408,8 +408,8 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, goto free_irq; } - dev_info(dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", - (u32)mem->start, (u32)xspi->regs, xspi->irq); + dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", + (unsigned long long)mem->start, xspi->regs, xspi->irq); return master; free_irq: -- cgit v1.2.3-70-g09d2