You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branches 'spi-drivers' and 'spi-mxs' into spi-next
This commit is contained in:
@@ -325,6 +325,12 @@ config SPI_S3C64XX
|
||||
help
|
||||
SPI driver for Samsung S3C64XX and newer SoCs.
|
||||
|
||||
config SPI_SC18IS602
|
||||
tristate "NXP SC18IS602/602B/603 I2C to SPI bridge"
|
||||
depends on I2C
|
||||
help
|
||||
SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge.
|
||||
|
||||
config SPI_SH_MSIOF
|
||||
tristate "SuperH MSIOF SPI controller"
|
||||
depends on SUPERH && HAVE_CLK
|
||||
@@ -364,6 +370,13 @@ config SPI_STMP3XXX
|
||||
help
|
||||
SPI driver for Freescale STMP37xx/378x SoC SSP interface
|
||||
|
||||
config SPI_MXS
|
||||
tristate "Freescale MXS SPI controller"
|
||||
depends on ARCH_MXS
|
||||
select STMP_DEVICE
|
||||
help
|
||||
SPI driver for Freescale MXS devices.
|
||||
|
||||
config SPI_TEGRA
|
||||
tristate "Nvidia Tegra SPI controller"
|
||||
depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
|
||||
|
||||
@@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
|
||||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
||||
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
|
||||
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
|
||||
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
|
||||
@@ -51,6 +52,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
|
||||
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
|
||||
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
|
||||
obj-$(CONFIG_SPI_SH) += spi-sh.o
|
||||
obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
||||
|
||||
@@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
#else /* CONFIG_OF */
|
||||
#define altera_spi_match NULL
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
@@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = NULL,
|
||||
.of_match_table = altera_spi_match,
|
||||
.of_match_table = of_match_ptr(altera_spi_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(altera_spi_driver);
|
||||
|
||||
+119
-12
@@ -22,6 +22,8 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
@@ -46,6 +48,7 @@ struct spi_gpio {
|
||||
struct spi_bitbang bitbang;
|
||||
struct spi_gpio_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int cs_gpios[0];
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -89,15 +92,21 @@ struct spi_gpio {
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static inline const struct spi_gpio_platform_data * __pure
|
||||
spi_to_pdata(const struct spi_device *spi)
|
||||
static inline struct spi_gpio * __pure
|
||||
spi_to_spi_gpio(const struct spi_device *spi)
|
||||
{
|
||||
const struct spi_bitbang *bang;
|
||||
const struct spi_gpio *spi_gpio;
|
||||
struct spi_gpio *spi_gpio;
|
||||
|
||||
bang = spi_master_get_devdata(spi->master);
|
||||
spi_gpio = container_of(bang, struct spi_gpio, bitbang);
|
||||
return &spi_gpio->pdata;
|
||||
return spi_gpio;
|
||||
}
|
||||
|
||||
static inline struct spi_gpio_platform_data * __pure
|
||||
spi_to_pdata(const struct spi_device *spi)
|
||||
{
|
||||
return &spi_to_spi_gpio(spi)->pdata;
|
||||
}
|
||||
|
||||
/* this is #defined to avoid unused-variable warnings when inlining */
|
||||
@@ -210,7 +219,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
|
||||
|
||||
static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||
{
|
||||
unsigned long cs = (unsigned long) spi->controller_data;
|
||||
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||
|
||||
/* set initial clock polarity */
|
||||
if (is_active)
|
||||
@@ -224,12 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||
|
||||
static int spi_gpio_setup(struct spi_device *spi)
|
||||
{
|
||||
unsigned long cs = (unsigned long) spi->controller_data;
|
||||
int status = 0;
|
||||
unsigned int cs;
|
||||
int status = 0;
|
||||
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||
struct device_node *np = spi->master->dev.of_node;
|
||||
|
||||
if (spi->bits_per_word > 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (np) {
|
||||
/*
|
||||
* In DT environments, the CS GPIOs have already been
|
||||
* initialized from the "cs-gpios" property of the node.
|
||||
*/
|
||||
cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||
} else {
|
||||
/*
|
||||
* ... otherwise, take it from spi->controller_data
|
||||
*/
|
||||
cs = (unsigned int) spi->controller_data;
|
||||
}
|
||||
|
||||
if (!spi->controller_state) {
|
||||
if (cs != SPI_GPIO_NO_CHIPSELECT) {
|
||||
status = gpio_request(cs, dev_name(&spi->dev));
|
||||
@@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||
!(spi->mode & SPI_CS_HIGH));
|
||||
}
|
||||
}
|
||||
if (!status)
|
||||
if (!status) {
|
||||
status = spi_bitbang_setup(spi);
|
||||
/* in case it was initialized from static board data */
|
||||
spi_gpio->cs_gpios[spi->chip_select] = cs;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
|
||||
gpio_free(cs);
|
||||
@@ -250,7 +279,8 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||
|
||||
static void spi_gpio_cleanup(struct spi_device *spi)
|
||||
{
|
||||
unsigned long cs = (unsigned long) spi->controller_data;
|
||||
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||
|
||||
if (cs != SPI_GPIO_NO_CHIPSELECT)
|
||||
gpio_free(cs);
|
||||
@@ -313,6 +343,55 @@ done:
|
||||
return value;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id spi_gpio_dt_ids[] = {
|
||||
{ .compatible = "spi-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
|
||||
|
||||
static int spi_gpio_probe_dt(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 tmp;
|
||||
struct spi_gpio_platform_data *pdata;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(spi_gpio_dt_ids, &pdev->dev);
|
||||
|
||||
if (!of_id)
|
||||
return 0;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
|
||||
pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
|
||||
pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
|
||||
|
||||
ret = of_property_read_u32(np, "num-chipselects", &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "num-chipselects property not found\n");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
pdata->num_chipselect = tmp;
|
||||
pdev->dev.platform_data = pdata;
|
||||
|
||||
return 1;
|
||||
|
||||
error_free:
|
||||
devm_kfree(&pdev->dev, pdata);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline int spi_gpio_probe_dt(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int status;
|
||||
@@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
struct spi_gpio *spi_gpio;
|
||||
struct spi_gpio_platform_data *pdata;
|
||||
u16 master_flags = 0;
|
||||
bool use_of = 0;
|
||||
|
||||
status = spi_gpio_probe_dt(pdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (status > 0)
|
||||
use_of = 1;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
#ifdef GENERIC_BITBANG
|
||||
@@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
|
||||
(sizeof(int) * SPI_N_CHIPSEL));
|
||||
if (!master) {
|
||||
status = -ENOMEM;
|
||||
goto gpio_free;
|
||||
@@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
master->num_chipselect = SPI_N_CHIPSEL;
|
||||
master->setup = spi_gpio_setup;
|
||||
master->cleanup = spi_gpio_cleanup;
|
||||
#ifdef CONFIG_OF
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
if (use_of) {
|
||||
int i;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
/*
|
||||
* In DT environments, take the CS GPIO from the "cs-gpios"
|
||||
* property of the node.
|
||||
*/
|
||||
|
||||
for (i = 0; i < SPI_N_CHIPSEL; i++)
|
||||
spi_gpio->cs_gpios[i] =
|
||||
of_get_named_gpio(np, "cs-gpios", i);
|
||||
}
|
||||
#endif
|
||||
|
||||
spi_gpio->bitbang.master = spi_master_get(master);
|
||||
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
|
||||
@@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev)
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
||||
static struct platform_driver spi_gpio_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
|
||||
},
|
||||
.probe = spi_gpio_probe,
|
||||
.remove = __devexit_p(spi_gpio_remove),
|
||||
};
|
||||
|
||||
@@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
||||
#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
|
||||
#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
|
||||
#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
|
||||
#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20))
|
||||
|
||||
#define MX51_ECSPI_INT 0x10
|
||||
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
||||
@@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||
if (config->mode & SPI_CPHA)
|
||||
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
|
||||
|
||||
if (config->mode & SPI_CPOL)
|
||||
if (config->mode & SPI_CPOL) {
|
||||
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
|
||||
|
||||
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs);
|
||||
}
|
||||
if (config->mode & SPI_CS_HIGH)
|
||||
cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@ free_master:
|
||||
|
||||
static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
||||
|
||||
flush_workqueue(mps->workqueue);
|
||||
@@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
iounmap(mps->psc);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
|
||||
|
||||
static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(&op->dev);
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
|
||||
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
|
||||
|
||||
flush_workqueue(mps->workqueue);
|
||||
@@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
iounmap(mps->psc);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||
GFP_KERNEL);
|
||||
if (!ms->gpio_cs) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
goto err_alloc_gpio;
|
||||
}
|
||||
|
||||
for (i = 0; i < ms->gpio_cs_count; i++) {
|
||||
@@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||
|
||||
err_register:
|
||||
dev_err(&ms->master->dev, "initialization failed\n");
|
||||
spi_master_put(master);
|
||||
err_gpio:
|
||||
while (i-- > 0)
|
||||
gpio_free(ms->gpio_cs[i]);
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
err_alloc_gpio:
|
||||
spi_master_put(master);
|
||||
err_alloc:
|
||||
err_init:
|
||||
iounmap(regs);
|
||||
@@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||
|
||||
static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(&op->dev);
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
|
||||
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
|
||||
int i;
|
||||
|
||||
@@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
spi_unregister_master(master);
|
||||
spi_master_put(master);
|
||||
iounmap(ms->regs);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+202
-171
File diff suppressed because it is too large
Load Diff
+64
-151
@@ -36,12 +36,6 @@
|
||||
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
||||
|
||||
struct orion_spi {
|
||||
struct work_struct work;
|
||||
|
||||
/* Lock access to transfer list. */
|
||||
spinlock_t lock;
|
||||
|
||||
struct list_head msg_queue;
|
||||
struct spi_master *master;
|
||||
void __iomem *base;
|
||||
unsigned int max_speed;
|
||||
@@ -49,8 +43,6 @@ struct orion_spi {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *orion_spi_wq;
|
||||
|
||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||
{
|
||||
return orion_spi->base + reg;
|
||||
@@ -277,73 +269,78 @@ out:
|
||||
}
|
||||
|
||||
|
||||
static void orion_spi_work(struct work_struct *work)
|
||||
static int orion_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct orion_spi *orion_spi =
|
||||
container_of(work, struct orion_spi, work);
|
||||
struct orion_spi *orion_spi = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int par_override = 0;
|
||||
int status = 0;
|
||||
int cs_active = 0;
|
||||
|
||||
spin_lock_irq(&orion_spi->lock);
|
||||
while (!list_empty(&orion_spi->msg_queue)) {
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int par_override = 0;
|
||||
int status = 0;
|
||||
int cs_active = 0;
|
||||
/* Load defaults */
|
||||
status = orion_spi_setup_transfer(spi, NULL);
|
||||
|
||||
m = container_of(orion_spi->msg_queue.next, struct spi_message,
|
||||
queue);
|
||||
if (status < 0)
|
||||
goto msg_done;
|
||||
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock_irq(&orion_spi->lock);
|
||||
|
||||
spi = m->spi;
|
||||
|
||||
/* Load defaults */
|
||||
status = orion_spi_setup_transfer(spi, NULL);
|
||||
|
||||
if (status < 0)
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
/* make sure buffer length is even when working in 16
|
||||
* bit mode*/
|
||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"odd data length %d while in 16 bit mode\n",
|
||||
t->len);
|
||||
status = -EIO;
|
||||
goto msg_done;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = orion_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
par_override = 0;
|
||||
}
|
||||
|
||||
if (!cs_active) {
|
||||
orion_spi_set_cs(orion_spi, 1);
|
||||
cs_active = 1;
|
||||
}
|
||||
|
||||
if (t->len)
|
||||
m->actual_length +=
|
||||
orion_spi_write_read(spi, t);
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
if (t->cs_change) {
|
||||
orion_spi_set_cs(orion_spi, 0);
|
||||
cs_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
msg_done:
|
||||
if (cs_active)
|
||||
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"device min speed (%d Hz) exceeds "
|
||||
"required transfer speed (%d Hz)\n",
|
||||
orion_spi->min_speed, t->speed_hz);
|
||||
status = -EIO;
|
||||
goto msg_done;
|
||||
}
|
||||
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = orion_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
par_override = 0;
|
||||
}
|
||||
|
||||
if (!cs_active) {
|
||||
orion_spi_set_cs(orion_spi, 1);
|
||||
cs_active = 1;
|
||||
}
|
||||
|
||||
if (t->len)
|
||||
m->actual_length += orion_spi_write_read(spi, t);
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
if (t->cs_change) {
|
||||
orion_spi_set_cs(orion_spi, 0);
|
||||
|
||||
m->status = status;
|
||||
m->complete(m->context);
|
||||
|
||||
spin_lock_irq(&orion_spi->lock);
|
||||
cs_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&orion_spi->lock);
|
||||
msg_done:
|
||||
if (cs_active)
|
||||
orion_spi_set_cs(orion_spi, 0);
|
||||
|
||||
m->status = status;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init orion_spi_reset(struct orion_spi *orion_spi)
|
||||
@@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct orion_spi *orion_spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = 0;
|
||||
|
||||
/* reject invalid messages and transfers */
|
||||
if (list_empty(&m->transfers) || !m->complete)
|
||||
return -EINVAL;
|
||||
|
||||
orion_spi = spi_master_get_devdata(spi->master);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
unsigned int bits_per_word = spi->bits_per_word;
|
||||
|
||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"invalid transfer data buffers\n");
|
||||
goto msg_rejected;
|
||||
}
|
||||
|
||||
if (t->bits_per_word)
|
||||
bits_per_word = t->bits_per_word;
|
||||
|
||||
if ((bits_per_word != 8) && (bits_per_word != 16)) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"invalid transfer bits_per_word (%d bits)\n",
|
||||
bits_per_word);
|
||||
goto msg_rejected;
|
||||
}
|
||||
/*make sure buffer length is even when working in 16 bit mode*/
|
||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"odd data length (%d) while in 16 bit mode\n",
|
||||
t->len);
|
||||
goto msg_rejected;
|
||||
}
|
||||
|
||||
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"device min speed (%d Hz) exceeds "
|
||||
"required transfer speed (%d Hz)\n",
|
||||
orion_spi->min_speed, t->speed_hz);
|
||||
goto msg_rejected;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
spin_lock_irqsave(&orion_spi->lock, flags);
|
||||
list_add_tail(&m->queue, &orion_spi->msg_queue);
|
||||
queue_work(orion_spi_wq, &orion_spi->work);
|
||||
spin_unlock_irqrestore(&orion_spi->lock, flags);
|
||||
|
||||
return 0;
|
||||
msg_rejected:
|
||||
/* Message rejected and not queued */
|
||||
m->status = -EINVAL;
|
||||
if (m->complete)
|
||||
m->complete(m->context);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init orion_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
@@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||
master->mode_bits = 0;
|
||||
|
||||
master->setup = orion_spi_setup;
|
||||
master->transfer = orion_spi_transfer;
|
||||
master->transfer_one_message = orion_spi_transfer_one_message;
|
||||
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, master);
|
||||
@@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
spi->base = ioremap(r->start, SZ_1K);
|
||||
|
||||
INIT_WORK(&spi->work, orion_spi_work);
|
||||
|
||||
spin_lock_init(&spi->lock);
|
||||
INIT_LIST_HEAD(&spi->msg_queue);
|
||||
|
||||
if (orion_spi_reset(spi) < 0)
|
||||
goto out_rel_mem;
|
||||
|
||||
@@ -536,14 +459,12 @@ out:
|
||||
static int __exit orion_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
struct resource *r;
|
||||
struct orion_spi *spi;
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
spi = spi_master_get_devdata(master);
|
||||
|
||||
cancel_work_sync(&spi->work);
|
||||
|
||||
clk_disable_unprepare(spi->clk);
|
||||
clk_put(spi->clk);
|
||||
|
||||
@@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = {
|
||||
|
||||
static int __init orion_spi_init(void)
|
||||
{
|
||||
orion_spi_wq = create_singlethread_workqueue(
|
||||
orion_spi_driver.driver.name);
|
||||
if (orion_spi_wq == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return platform_driver_probe(&orion_spi_driver, orion_spi_probe);
|
||||
}
|
||||
module_init(orion_spi_init);
|
||||
|
||||
static void __exit orion_spi_exit(void)
|
||||
{
|
||||
flush_workqueue(orion_spi_wq);
|
||||
platform_driver_unregister(&orion_spi_driver);
|
||||
|
||||
destroy_workqueue(orion_spi_wq);
|
||||
}
|
||||
module_exit(orion_spi_exit);
|
||||
|
||||
|
||||
+208
-43
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
|
||||
*
|
||||
* Copyright (C) 2008-2009 ST-Ericsson AB
|
||||
* Copyright (C) 2008-2012 ST-Ericsson AB
|
||||
* Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
@@ -40,6 +40,9 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/*
|
||||
* This macro is used to define some register default values.
|
||||
@@ -356,6 +359,8 @@ struct vendor_data {
|
||||
* @sgt_rx: scattertable for the RX transfer
|
||||
* @sgt_tx: scattertable for the TX transfer
|
||||
* @dummypage: a dummy page used for driving data on the bus with DMA
|
||||
* @cur_cs: current chip select (gpio)
|
||||
* @chipselects: list of chipselects (gpios)
|
||||
*/
|
||||
struct pl022 {
|
||||
struct amba_device *adev;
|
||||
@@ -363,6 +368,10 @@ struct pl022 {
|
||||
resource_size_t phybase;
|
||||
void __iomem *virtbase;
|
||||
struct clk *clk;
|
||||
/* Two optional pin states - default & sleep */
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_sleep;
|
||||
struct spi_master *master;
|
||||
struct pl022_ssp_controller *master_info;
|
||||
/* Message per-transfer pump */
|
||||
@@ -389,6 +398,8 @@ struct pl022 {
|
||||
char *dummypage;
|
||||
bool dma_running;
|
||||
#endif
|
||||
int cur_cs;
|
||||
int *chipselects;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -433,6 +444,14 @@ static void null_cs_control(u32 command)
|
||||
pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
|
||||
}
|
||||
|
||||
static void pl022_cs_control(struct pl022 *pl022, u32 command)
|
||||
{
|
||||
if (gpio_is_valid(pl022->cur_cs))
|
||||
gpio_set_value(pl022->cur_cs, command);
|
||||
else
|
||||
pl022->cur_chip->cs_control(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* giveback - current spi_message is over, schedule next message and call
|
||||
* callback of this message. Assumes that caller already
|
||||
@@ -479,7 +498,7 @@ static void giveback(struct pl022 *pl022)
|
||||
if (next_msg && next_msg->spi != pl022->cur_msg->spi)
|
||||
next_msg = NULL;
|
||||
if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
else
|
||||
pl022->next_msg_cs_active = true;
|
||||
|
||||
@@ -818,8 +837,7 @@ static void dma_callback(void *data)
|
||||
/* Update total bytes transferred */
|
||||
msg->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->
|
||||
cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
|
||||
/* Move to next transfer */
|
||||
msg->state = next_transfer(pl022);
|
||||
@@ -1252,8 +1270,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||
/* Update total bytes transferred */
|
||||
msg->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->
|
||||
cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
/* Move to next transfer */
|
||||
msg->state = next_transfer(pl022);
|
||||
tasklet_schedule(&pl022->pump_transfers);
|
||||
@@ -1338,7 +1355,7 @@ static void pump_transfers(unsigned long data)
|
||||
|
||||
/* Reselect chip select only if cs_change was requested */
|
||||
if (previous->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
} else {
|
||||
/* STATE_START */
|
||||
message->state = STATE_RUNNING;
|
||||
@@ -1377,7 +1394,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
|
||||
|
||||
/* Enable target chip, if not already active */
|
||||
if (!pl022->next_msg_cs_active)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
|
||||
if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
|
||||
/* Error path */
|
||||
@@ -1429,12 +1446,12 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||
if (previous->delay_usecs)
|
||||
udelay(previous->delay_usecs);
|
||||
if (previous->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
} else {
|
||||
/* STATE_START */
|
||||
message->state = STATE_RUNNING;
|
||||
if (!pl022->next_msg_cs_active)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
}
|
||||
|
||||
/* Configuration Changing Per Transfer */
|
||||
@@ -1466,7 +1483,7 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||
/* Update total byte transferred */
|
||||
message->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
/* Move to next transfer */
|
||||
message->state = next_transfer(pl022);
|
||||
}
|
||||
@@ -1495,6 +1512,7 @@ static int pl022_transfer_one_message(struct spi_master *master,
|
||||
|
||||
/* Setup the SPI using the per chip configuration */
|
||||
pl022->cur_chip = spi_get_ctldata(msg->spi);
|
||||
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
|
||||
|
||||
restore_state(pl022);
|
||||
flush(pl022);
|
||||
@@ -1766,12 +1784,14 @@ static const struct pl022_config_chip pl022_default_chip_info = {
|
||||
static int pl022_setup(struct spi_device *spi)
|
||||
{
|
||||
struct pl022_config_chip const *chip_info;
|
||||
struct pl022_config_chip chip_info_dt;
|
||||
struct chip_data *chip;
|
||||
struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
|
||||
int status = 0;
|
||||
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
|
||||
unsigned int bits = spi->bits_per_word;
|
||||
u32 tmp;
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
@@ -1794,10 +1814,32 @@ static int pl022_setup(struct spi_device *spi)
|
||||
chip_info = spi->controller_data;
|
||||
|
||||
if (chip_info == NULL) {
|
||||
chip_info = &pl022_default_chip_info;
|
||||
/* spi_board_info.controller_data not is supplied */
|
||||
dev_dbg(&spi->dev,
|
||||
"using default controller_data settings\n");
|
||||
if (np) {
|
||||
chip_info_dt = pl022_default_chip_info;
|
||||
|
||||
chip_info_dt.hierarchy = SSP_MASTER;
|
||||
of_property_read_u32(np, "pl022,interface",
|
||||
&chip_info_dt.iface);
|
||||
of_property_read_u32(np, "pl022,com-mode",
|
||||
&chip_info_dt.com_mode);
|
||||
of_property_read_u32(np, "pl022,rx-level-trig",
|
||||
&chip_info_dt.rx_lev_trig);
|
||||
of_property_read_u32(np, "pl022,tx-level-trig",
|
||||
&chip_info_dt.tx_lev_trig);
|
||||
of_property_read_u32(np, "pl022,ctrl-len",
|
||||
&chip_info_dt.ctrl_len);
|
||||
of_property_read_u32(np, "pl022,wait-state",
|
||||
&chip_info_dt.wait_state);
|
||||
of_property_read_u32(np, "pl022,duplex",
|
||||
&chip_info_dt.duplex);
|
||||
|
||||
chip_info = &chip_info_dt;
|
||||
} else {
|
||||
chip_info = &pl022_default_chip_info;
|
||||
/* spi_board_info.controller_data not is supplied */
|
||||
dev_dbg(&spi->dev,
|
||||
"using default controller_data settings\n");
|
||||
}
|
||||
} else
|
||||
dev_dbg(&spi->dev,
|
||||
"using user supplied controller_data settings\n");
|
||||
@@ -1840,8 +1882,9 @@ static int pl022_setup(struct spi_device *spi)
|
||||
chip->xfer_type = chip_info->com_mode;
|
||||
if (!chip_info->cs_control) {
|
||||
chip->cs_control = null_cs_control;
|
||||
dev_warn(&spi->dev,
|
||||
"chip select function is NULL for this chip\n");
|
||||
if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
|
||||
dev_warn(&spi->dev,
|
||||
"invalid chip select\n");
|
||||
} else
|
||||
chip->cs_control = chip_info->cs_control;
|
||||
|
||||
@@ -1986,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi)
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static struct pl022_ssp_controller *
|
||||
pl022_platform_data_dt_get(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pl022_ssp_controller *pd;
|
||||
u32 tmp;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "no dt node defined\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
|
||||
if (!pd) {
|
||||
dev_err(dev, "cannot allocate platform data memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pd->bus_id = -1;
|
||||
of_property_read_u32(np, "num-cs", &tmp);
|
||||
pd->num_chipselect = tmp;
|
||||
of_property_read_u32(np, "pl022,autosuspend-delay",
|
||||
&pd->autosuspend_delay);
|
||||
pd->rt = of_property_read_bool(np, "pl022,rt");
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
@@ -1993,22 +2064,31 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
|
||||
struct spi_master *master;
|
||||
struct pl022 *pl022 = NULL; /*Data for this driver */
|
||||
int status = 0;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
int status = 0, i, num_cs;
|
||||
|
||||
dev_info(&adev->dev,
|
||||
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
|
||||
if (platform_info == NULL) {
|
||||
dev_err(&adev->dev, "probe - no platform data supplied\n");
|
||||
status = -ENODEV;
|
||||
goto err_no_pdata;
|
||||
if (!platform_info && IS_ENABLED(CONFIG_OF))
|
||||
platform_info = pl022_platform_data_dt_get(dev);
|
||||
|
||||
if (!platform_info) {
|
||||
dev_err(dev, "probe: no platform data defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (platform_info->num_chipselect) {
|
||||
num_cs = platform_info->num_chipselect;
|
||||
} else {
|
||||
dev_err(dev, "probe: no chip select defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate master with space for data */
|
||||
master = spi_alloc_master(dev, sizeof(struct pl022));
|
||||
if (master == NULL) {
|
||||
dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
|
||||
status = -ENOMEM;
|
||||
goto err_no_master;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pl022 = spi_master_get_devdata(master);
|
||||
@@ -2016,19 +2096,71 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
pl022->master_info = platform_info;
|
||||
pl022->adev = adev;
|
||||
pl022->vendor = id->data;
|
||||
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
|
||||
pl022->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR(pl022->pinctrl)) {
|
||||
status = PTR_ERR(pl022->pinctrl);
|
||||
goto err_no_pinctrl;
|
||||
}
|
||||
|
||||
pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
/* enable pins to be muxed in and configured */
|
||||
if (!IS_ERR(pl022->pins_default)) {
|
||||
status = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_default);
|
||||
if (status)
|
||||
dev_err(dev, "could not set default pins\n");
|
||||
} else
|
||||
dev_err(dev, "could not get default pinstate\n");
|
||||
|
||||
pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl,
|
||||
PINCTRL_STATE_SLEEP);
|
||||
if (IS_ERR(pl022->pins_sleep))
|
||||
dev_dbg(dev, "could not get sleep pinstate\n");
|
||||
|
||||
/*
|
||||
* Bus Number Which has been Assigned to this SSP controller
|
||||
* on this board
|
||||
*/
|
||||
master->bus_num = platform_info->bus_id;
|
||||
master->num_chipselect = platform_info->num_chipselect;
|
||||
master->num_chipselect = num_cs;
|
||||
master->cleanup = pl022_cleanup;
|
||||
master->setup = pl022_setup;
|
||||
master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
|
||||
master->transfer_one_message = pl022_transfer_one_message;
|
||||
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
||||
master->rt = platform_info->rt;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
if (platform_info->num_chipselect && platform_info->chipselects) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = platform_info->chipselects[i];
|
||||
} else if (IS_ENABLED(CONFIG_OF)) {
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
|
||||
if (cs_gpio == -EPROBE_DEFER) {
|
||||
status = -EPROBE_DEFER;
|
||||
goto err_no_gpio;
|
||||
}
|
||||
|
||||
pl022->chipselects[i] = cs_gpio;
|
||||
|
||||
if (gpio_is_valid(cs_gpio)) {
|
||||
if (devm_gpio_request(dev, cs_gpio, "ssp-pl022"))
|
||||
dev_err(&adev->dev,
|
||||
"could not request %d gpio\n",
|
||||
cs_gpio);
|
||||
else if (gpio_direction_output(cs_gpio, 1))
|
||||
dev_err(&adev->dev,
|
||||
"could set gpio %d as output\n",
|
||||
cs_gpio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Supports mode 0-3, loopback, and active low CS. Transfers are
|
||||
@@ -2045,7 +2177,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
goto err_no_ioregion;
|
||||
|
||||
pl022->phybase = adev->res.start;
|
||||
pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
|
||||
pl022->virtbase = devm_ioremap(dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (pl022->virtbase == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto err_no_ioremap;
|
||||
@@ -2055,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
pl022->clk = clk_get(&adev->dev, NULL);
|
||||
pl022->clk = devm_clk_get(&adev->dev, NULL);
|
||||
if (IS_ERR(pl022->clk)) {
|
||||
status = PTR_ERR(pl022->clk);
|
||||
dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
|
||||
@@ -2083,8 +2216,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
SSP_CR1(pl022->virtbase));
|
||||
load_ssp_default_config(pl022);
|
||||
|
||||
status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
|
||||
pl022);
|
||||
status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
|
||||
0, "pl022", pl022);
|
||||
if (status < 0) {
|
||||
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
|
||||
goto err_no_irq;
|
||||
@@ -2124,22 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
err_spi_register:
|
||||
if (platform_info->enable_dma)
|
||||
pl022_dma_remove(pl022);
|
||||
|
||||
free_irq(adev->irq[0], pl022);
|
||||
err_no_irq:
|
||||
clk_disable(pl022->clk);
|
||||
err_no_clk_en:
|
||||
clk_unprepare(pl022->clk);
|
||||
err_clk_prep:
|
||||
clk_put(pl022->clk);
|
||||
err_no_clk:
|
||||
iounmap(pl022->virtbase);
|
||||
err_no_ioremap:
|
||||
amba_release_regions(adev);
|
||||
err_no_ioregion:
|
||||
err_no_gpio:
|
||||
err_no_pinctrl:
|
||||
spi_master_put(master);
|
||||
err_no_master:
|
||||
err_no_pdata:
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -2161,20 +2290,55 @@ pl022_remove(struct amba_device *adev)
|
||||
if (pl022->master_info->enable_dma)
|
||||
pl022_dma_remove(pl022);
|
||||
|
||||
free_irq(adev->irq[0], pl022);
|
||||
clk_disable(pl022->clk);
|
||||
clk_unprepare(pl022->clk);
|
||||
clk_put(pl022->clk);
|
||||
pm_runtime_disable(&adev->dev);
|
||||
iounmap(pl022->virtbase);
|
||||
amba_release_regions(adev);
|
||||
tasklet_disable(&pl022->pump_transfers);
|
||||
spi_unregister_master(pl022->master);
|
||||
spi_master_put(pl022->master);
|
||||
amba_set_drvdata(adev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
|
||||
/*
|
||||
* These two functions are used from both suspend/resume and
|
||||
* the runtime counterparts to handle external resources like
|
||||
* clocks, pins and regulators when going to sleep.
|
||||
*/
|
||||
static void pl022_suspend_resources(struct pl022 *pl022)
|
||||
{
|
||||
int ret;
|
||||
|
||||
clk_disable(pl022->clk);
|
||||
|
||||
/* Optionally let pins go into sleep states */
|
||||
if (!IS_ERR(pl022->pins_sleep)) {
|
||||
ret = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_sleep);
|
||||
if (ret)
|
||||
dev_err(&pl022->adev->dev,
|
||||
"could not set pins to sleep state\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pl022_resume_resources(struct pl022 *pl022)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Optionaly enable pins to be muxed in and configured */
|
||||
if (!IS_ERR(pl022->pins_default)) {
|
||||
ret = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_default);
|
||||
if (ret)
|
||||
dev_err(&pl022->adev->dev,
|
||||
"could not set default pins\n");
|
||||
}
|
||||
|
||||
clk_enable(pl022->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static int pl022_suspend(struct device *dev)
|
||||
{
|
||||
@@ -2186,6 +2350,7 @@ static int pl022_suspend(struct device *dev)
|
||||
dev_warn(dev, "cannot suspend master\n");
|
||||
return ret;
|
||||
}
|
||||
pl022_suspend_resources(pl022);
|
||||
|
||||
dev_dbg(dev, "suspended\n");
|
||||
return 0;
|
||||
@@ -2196,6 +2361,8 @@ static int pl022_resume(struct device *dev)
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pl022_resume_resources(pl022);
|
||||
|
||||
/* Start the queue running */
|
||||
ret = spi_master_resume(pl022->master);
|
||||
if (ret)
|
||||
@@ -2212,8 +2379,7 @@ static int pl022_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(pl022->clk);
|
||||
|
||||
pl022_suspend_resources(pl022);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2221,8 +2387,7 @@ static int pl022_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
|
||||
clk_enable(pl022->clk);
|
||||
|
||||
pl022_resume_resources(pl022);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
|
||||
if (!pdata->set_cs) {
|
||||
if (pdata->pin_cs < 0) {
|
||||
dev_err(&pdev->dev, "No chipselect pin\n");
|
||||
err = -EINVAL;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
|
||||
@@ -976,7 +976,8 @@ err_msgq:
|
||||
spi_set_ctldata(spi, NULL);
|
||||
|
||||
err_gpio_req:
|
||||
kfree(cs);
|
||||
if (spi->dev.of_node)
|
||||
kfree(cs);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c64xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
|
||||
spi_master_suspend(master);
|
||||
@@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(struct device *dev)
|
||||
|
||||
static int s3c64xx_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||
|
||||
@@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(sdd->clk);
|
||||
@@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||
|
||||
static int s3c64xx_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
|
||||
clk_enable(sdd->src_clk);
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* NXP SC18IS602/603 SPI driver
|
||||
*
|
||||
* Copyright (C) Guenter Roeck <linux@roeck-us.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/sc18is602.h>
|
||||
|
||||
enum chips { sc18is602, sc18is602b, sc18is603 };
|
||||
|
||||
#define SC18IS602_BUFSIZ 200
|
||||
#define SC18IS602_CLOCK 7372000
|
||||
|
||||
#define SC18IS602_MODE_CPHA BIT(2)
|
||||
#define SC18IS602_MODE_CPOL BIT(3)
|
||||
#define SC18IS602_MODE_LSB_FIRST BIT(5)
|
||||
#define SC18IS602_MODE_CLOCK_DIV_4 0x0
|
||||
#define SC18IS602_MODE_CLOCK_DIV_16 0x1
|
||||
#define SC18IS602_MODE_CLOCK_DIV_64 0x2
|
||||
#define SC18IS602_MODE_CLOCK_DIV_128 0x3
|
||||
|
||||
struct sc18is602 {
|
||||
struct spi_master *master;
|
||||
struct device *dev;
|
||||
u8 ctrl;
|
||||
u32 freq;
|
||||
u32 speed;
|
||||
|
||||
/* I2C data */
|
||||
struct i2c_client *client;
|
||||
enum chips id;
|
||||
u8 buffer[SC18IS602_BUFSIZ + 1];
|
||||
int tlen; /* Data queued for tx in buffer */
|
||||
int rindex; /* Receive data index in buffer */
|
||||
};
|
||||
|
||||
static int sc18is602_wait_ready(struct sc18is602 *hw, int len)
|
||||
{
|
||||
int i, err;
|
||||
int usecs = 1000000 * len / hw->speed + 1;
|
||||
u8 dummy[1];
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
err = i2c_master_recv(hw->client, dummy, 1);
|
||||
if (err >= 0)
|
||||
return 0;
|
||||
usleep_range(usecs, usecs * 2);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg,
|
||||
struct spi_transfer *t, bool do_transfer)
|
||||
{
|
||||
unsigned int len = t->len;
|
||||
int ret;
|
||||
|
||||
if (hw->tlen == 0) {
|
||||
/* First byte (I2C command) is chip select */
|
||||
hw->buffer[0] = 1 << msg->spi->chip_select;
|
||||
hw->tlen = 1;
|
||||
hw->rindex = 0;
|
||||
}
|
||||
/*
|
||||
* We can not immediately send data to the chip, since each I2C message
|
||||
* resembles a full SPI message (from CS active to CS inactive).
|
||||
* Enqueue messages up to the first read or until do_transfer is true.
|
||||
*/
|
||||
if (t->tx_buf) {
|
||||
memcpy(&hw->buffer[hw->tlen], t->tx_buf, len);
|
||||
hw->tlen += len;
|
||||
if (t->rx_buf)
|
||||
do_transfer = true;
|
||||
else
|
||||
hw->rindex = hw->tlen - 1;
|
||||
} else if (t->rx_buf) {
|
||||
/*
|
||||
* For receive-only transfers we still need to perform a dummy
|
||||
* write to receive data from the SPI chip.
|
||||
* Read data starts at the end of transmit data (minus 1 to
|
||||
* account for CS).
|
||||
*/
|
||||
hw->rindex = hw->tlen - 1;
|
||||
memset(&hw->buffer[hw->tlen], 0, len);
|
||||
hw->tlen += len;
|
||||
do_transfer = true;
|
||||
}
|
||||
|
||||
if (do_transfer && hw->tlen > 1) {
|
||||
ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_master_send(hw->client, hw->buffer, hw->tlen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != hw->tlen)
|
||||
return -EIO;
|
||||
|
||||
if (t->rx_buf) {
|
||||
int rlen = hw->rindex + len;
|
||||
|
||||
ret = sc18is602_wait_ready(hw, hw->tlen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_master_recv(hw->client, hw->buffer, rlen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != rlen)
|
||||
return -EIO;
|
||||
memcpy(t->rx_buf, &hw->buffer[hw->rindex], len);
|
||||
}
|
||||
hw->tlen = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
|
||||
{
|
||||
u8 ctrl = 0;
|
||||
int ret;
|
||||
|
||||
if (mode & SPI_CPHA)
|
||||
ctrl |= SC18IS602_MODE_CPHA;
|
||||
if (mode & SPI_CPOL)
|
||||
ctrl |= SC18IS602_MODE_CPOL;
|
||||
if (mode & SPI_LSB_FIRST)
|
||||
ctrl |= SC18IS602_MODE_LSB_FIRST;
|
||||
|
||||
/* Find the closest clock speed */
|
||||
if (hz >= hw->freq / 4) {
|
||||
ctrl |= SC18IS602_MODE_CLOCK_DIV_4;
|
||||
hw->speed = hw->freq / 4;
|
||||
} else if (hz >= hw->freq / 16) {
|
||||
ctrl |= SC18IS602_MODE_CLOCK_DIV_16;
|
||||
hw->speed = hw->freq / 16;
|
||||
} else if (hz >= hw->freq / 64) {
|
||||
ctrl |= SC18IS602_MODE_CLOCK_DIV_64;
|
||||
hw->speed = hw->freq / 64;
|
||||
} else {
|
||||
ctrl |= SC18IS602_MODE_CLOCK_DIV_128;
|
||||
hw->speed = hw->freq / 128;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't do anything if the control value did not change. The initial
|
||||
* value of 0xff for hw->ctrl ensures that the correct mode will be set
|
||||
* with the first call to this function.
|
||||
*/
|
||||
if (ctrl == hw->ctrl)
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hw->ctrl = ctrl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc18is602_check_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t, int tlen)
|
||||
{
|
||||
int bpw;
|
||||
uint32_t hz;
|
||||
|
||||
if (t && t->len + tlen > SC18IS602_BUFSIZ)
|
||||
return -EINVAL;
|
||||
|
||||
bpw = spi->bits_per_word;
|
||||
if (t && t->bits_per_word)
|
||||
bpw = t->bits_per_word;
|
||||
if (bpw != 8)
|
||||
return -EINVAL;
|
||||
|
||||
hz = spi->max_speed_hz;
|
||||
if (t && t->speed_hz)
|
||||
hz = t->speed_hz;
|
||||
if (hz == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc18is602_transfer_one(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct sc18is602 *hw = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t;
|
||||
int status = 0;
|
||||
|
||||
/* SC18IS602 does not support CS2 */
|
||||
if (hw->id == sc18is602 && spi->chip_select == 2) {
|
||||
status = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
hw->tlen = 0;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
u32 hz = t->speed_hz ? : spi->max_speed_hz;
|
||||
bool do_transfer;
|
||||
|
||||
status = sc18is602_check_transfer(spi, t, hw->tlen);
|
||||
if (status < 0)
|
||||
break;
|
||||
|
||||
status = sc18is602_setup_transfer(hw, hz, spi->mode);
|
||||
if (status < 0)
|
||||
break;
|
||||
|
||||
do_transfer = t->cs_change || list_is_last(&t->transfer_list,
|
||||
&m->transfers);
|
||||
|
||||
if (t->len) {
|
||||
status = sc18is602_txrx(hw, m, t, do_transfer);
|
||||
if (status < 0)
|
||||
break;
|
||||
m->actual_length += status;
|
||||
}
|
||||
status = 0;
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
}
|
||||
error:
|
||||
m->status = status;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int sc18is602_setup(struct spi_device *spi)
|
||||
{
|
||||
if (!spi->bits_per_word)
|
||||
spi->bits_per_word = 8;
|
||||
|
||||
if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
|
||||
return -EINVAL;
|
||||
|
||||
return sc18is602_check_transfer(spi, NULL, 0);
|
||||
}
|
||||
|
||||
static int sc18is602_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct sc18is602 *hw;
|
||||
struct spi_master *master;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct sc18is602));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
i2c_set_clientdata(client, hw);
|
||||
|
||||
hw->master = master;
|
||||
hw->client = client;
|
||||
hw->dev = dev;
|
||||
hw->ctrl = 0xff;
|
||||
|
||||
hw->id = id->driver_data;
|
||||
|
||||
switch (hw->id) {
|
||||
case sc18is602:
|
||||
case sc18is602b:
|
||||
master->num_chipselect = 4;
|
||||
hw->freq = SC18IS602_CLOCK;
|
||||
break;
|
||||
case sc18is603:
|
||||
master->num_chipselect = 2;
|
||||
if (pdata) {
|
||||
hw->freq = pdata->clock_frequency;
|
||||
} else {
|
||||
const __be32 *val;
|
||||
int len;
|
||||
|
||||
val = of_get_property(np, "clock-frequency", &len);
|
||||
if (val && len >= sizeof(__be32))
|
||||
hw->freq = be32_to_cpup(val);
|
||||
}
|
||||
if (!hw->freq)
|
||||
hw->freq = SC18IS602_CLOCK;
|
||||
break;
|
||||
}
|
||||
master->bus_num = client->adapter->nr;
|
||||
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
|
||||
master->setup = sc18is602_setup;
|
||||
master->transfer_one_message = sc18is602_transfer_one;
|
||||
master->dev.of_node = np;
|
||||
|
||||
error = spi_register_master(master);
|
||||
if (error)
|
||||
goto error_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_reg:
|
||||
spi_master_put(master);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sc18is602_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sc18is602 *hw = i2c_get_clientdata(client);
|
||||
struct spi_master *master = hw->master;
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sc18is602_id[] = {
|
||||
{ "sc18is602", sc18is602 },
|
||||
{ "sc18is602b", sc18is602b },
|
||||
{ "sc18is603", sc18is603 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sc18is602_id);
|
||||
|
||||
static struct i2c_driver sc18is602_driver = {
|
||||
.driver = {
|
||||
.name = "sc18is602",
|
||||
},
|
||||
.probe = sc18is602_probe,
|
||||
.remove = sc18is602_remove,
|
||||
.id_table = sc18is602_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(sc18is602_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver");
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev)
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||
goto error2;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
@@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
devm_iounmap(hspi->dev, hspi->addr);
|
||||
error1:
|
||||
clk_put(clk);
|
||||
error0:
|
||||
@@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev)
|
||||
|
||||
clk_put(hspi->clk);
|
||||
spi_unregister_master(hspi->master);
|
||||
devm_iounmap(hspi->dev, hspi->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||
struct stmp_spi *ss;
|
||||
struct spi_master *master;
|
||||
|
||||
master = platform_get_drvdata(dev);
|
||||
if (master == NULL)
|
||||
goto out0;
|
||||
master = spi_master_get(platform_get_drvdata(dev));
|
||||
ss = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
@@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||
destroy_workqueue(ss->workqueue);
|
||||
iounmap(ss->regs);
|
||||
spi_master_put(master);
|
||||
platform_set_drvdata(dev, NULL);
|
||||
out0:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -652,7 +652,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
||||
struct spi_tegra_data *tspi;
|
||||
struct resource *r;
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
master = spi_master_get(dev_get_drvdata(&pdev->dev));
|
||||
tspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
@@ -668,6 +668,8 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
||||
clk_put(tspi->clk);
|
||||
iounmap(tspi->base);
|
||||
|
||||
spi_master_put(master);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
|
||||
|
||||
@@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = {
|
||||
.remove = __devexit_p(tle62x0_remove),
|
||||
};
|
||||
|
||||
static __init int tle62x0_init(void)
|
||||
{
|
||||
return spi_register_driver(&tle62x0_driver);
|
||||
}
|
||||
|
||||
static __exit void tle62x0_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&tle62x0_driver);
|
||||
}
|
||||
|
||||
module_init(tle62x0_init);
|
||||
module_exit(tle62x0_exit);
|
||||
module_spi_driver(tle62x0_driver);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("TLE62x0 SPI driver");
|
||||
|
||||
@@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
|
||||
|
||||
pci_iounmap(board_dat->pdev, data->io_remap_addr);
|
||||
spi_unregister_master(data->master);
|
||||
spi_master_put(data->master);
|
||||
platform_set_drvdata(plat_dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user