Commit d5af91a1 authored by Richard Röjfors's avatar Richard Röjfors Committed by Grant Likely

xilinx_spi: Split into of driver and generic part.

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: default avatarJohn Linn <John.Linn@xilinx.com>
Signed-off-by: default avatarRichard Röjfors <richard.rojfors@mocean-labs.com>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent b8d4e2ce
...@@ -244,14 +244,21 @@ config SPI_TXX9 ...@@ -244,14 +244,21 @@ config SPI_TXX9
config SPI_XILINX config SPI_XILINX
tristate "Xilinx SPI controller" tristate "Xilinx SPI controller"
depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL depends on EXPERIMENTAL
select SPI_BITBANG select SPI_BITBANG
select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE)
help help
This exposes the SPI controller IP from the Xilinx EDK. This exposes the SPI controller IP from the Xilinx EDK.
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details. 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 # Add new SPI master controllers in alphabetical order above this line
# #
......
...@@ -32,6 +32,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o ...@@ -32,6 +32,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.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_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
# ... add above this line ... # ... add above this line ...
......
...@@ -14,16 +14,14 @@ ...@@ -14,16 +14,14 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_spi.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h> #include <linux/spi/spi_bitbang.h>
#include <linux/io.h> #include <linux/io.h>
#include "xilinx_spi.h"
#include <linux/spi/xilinx_spi.h>
#define XILINX_SPI_NAME "xilinx_spi" #define XILINX_SPI_NAME "xilinx_spi"
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
...@@ -78,7 +76,7 @@ struct xilinx_spi { ...@@ -78,7 +76,7 @@ struct xilinx_spi {
/* bitbang has to be first */ /* bitbang has to be first */
struct spi_bitbang bitbang; struct spi_bitbang bitbang;
struct completion done; struct completion done;
struct resource mem; /* phys mem */
void __iomem *regs; /* virt. address of the control registers */ void __iomem *regs; /* virt. address of the control registers */
u32 irq; u32 irq;
...@@ -284,40 +282,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) ...@@ -284,40 +282,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int __init xilinx_spi_of_probe(struct of_device *ofdev, struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
const struct of_device_id *match) u32 irq, s16 bus_num)
{ {
struct spi_master *master; struct spi_master *master;
struct xilinx_spi *xspi; struct xilinx_spi *xspi;
struct resource r_irq_struct; struct xspi_platform_data *pdata = dev->platform_data;
struct resource r_mem_struct; int ret;
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));
if (master == NULL) { if (!pdata) {
return -ENOMEM; dev_err(dev, "No platform data attached\n");
} return NULL;
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;
} }
rc = of_irq_to_resource(ofdev->node, 0, r_irq); master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
if (rc == NO_IRQ) { if (!master)
dev_warn(&ofdev->dev, "no IRQ found\n"); return NULL;
goto put_master;
}
/* the spi->mode bits understood by this driver: */ /* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA; master->mode_bits = SPI_CPOL | SPI_CPHA;
...@@ -330,128 +310,67 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev, ...@@ -330,128 +310,67 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
xspi->bitbang.master->setup = xilinx_spi_setup; xspi->bitbang.master->setup = xilinx_spi_setup;
init_completion(&xspi->done); init_completion(&xspi->done);
xspi->irq = r_irq->start; if (!request_mem_region(mem->start, resource_size(mem),
XILINX_SPI_NAME))
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");
goto put_master; 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) { if (xspi->regs == NULL) {
rc = -ENOMEM; dev_warn(dev, "ioremap failure\n");
dev_warn(&ofdev->dev, "ioremap failure\n"); goto map_failed;
goto release_mem;
} }
xspi->irq = r_irq->start;
/* dynamic bus assignment */ master->bus_num = bus_num;
master->bus_num = -1; master->num_chipselect = pdata->num_chipselect;
/* number of slave select bits is required */ xspi->mem = *mem;
prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); xspi->irq = irq;
if (!prop || len < sizeof(*prop)) {
dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
goto unmap_io;
}
master->num_chipselect = *prop;
/* SPI controller initializations */ /* SPI controller initializations */
xspi_init_hw(xspi->regs); xspi_init_hw(xspi->regs);
/* Register for SPI Interrupt */ /* Register for SPI Interrupt */
rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
if (rc != 0) { if (ret)
dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
goto unmap_io; goto unmap_io;
}
rc = spi_bitbang_start(&xspi->bitbang); ret = spi_bitbang_start(&xspi->bitbang);
if (rc != 0) { if (ret) {
dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); dev_err(dev, "spi_bitbang_start FAILED\n");
goto free_irq; goto free_irq;
} }
dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", dev_info(dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
(unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); (u32)mem->start, (u32)xspi->regs, xspi->irq);
return master;
/* Add any subnodes on the SPI bus */
of_register_spi_devices(master, ofdev->node);
return rc;
free_irq: free_irq:
free_irq(xspi->irq, xspi); free_irq(xspi->irq, xspi);
unmap_io: unmap_io:
iounmap(xspi->regs); iounmap(xspi->regs);
release_mem: map_failed:
release_mem_region(r_mem->start, resource_size(r_mem)); release_mem_region(mem->start, resource_size(mem));
put_master: put_master:
spi_master_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 xilinx_spi *xspi;
struct spi_master *master;
struct resource r_mem;
master = platform_get_drvdata(ofdev);
xspi = spi_master_get_devdata(master); xspi = spi_master_get_devdata(master);
spi_bitbang_stop(&xspi->bitbang); spi_bitbang_stop(&xspi->bitbang);
free_irq(xspi->irq, xspi); free_irq(xspi->irq, xspi);
iounmap(xspi->regs); 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[] = { release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
{ .compatible = "xlnx,xps-spi-2.00.a", }, spi_master_put(xspi->bitbang.master);
{ .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);
} }
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. <source@mvista.com>"); MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("Xilinx SPI driver"); MODULE_DESCRIPTION("Xilinx SPI driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/*
* 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 <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#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
/*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_spi.h>
#include <linux/spi/xilinx_spi.h>
#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 <info@mocean-labs.com>");
MODULE_DESCRIPTION("Xilinx SPI platform driver");
MODULE_LICENSE("GPL v2");
#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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment