Commit 4374f332 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'spi/topic/omap-100k' into spi-next

parents 45bb5065 2d0c6148
...@@ -83,11 +83,6 @@ ...@@ -83,11 +83,6 @@
#define SPI_SHUTDOWN 1 #define SPI_SHUTDOWN 1
struct omap1_spi100k { struct omap1_spi100k {
struct work_struct work;
/* lock protects queue and registers */
spinlock_t lock;
struct list_head msg_queue;
struct spi_master *master; struct spi_master *master;
struct clk *ick; struct clk *ick;
struct clk *fck; struct clk *fck;
...@@ -104,8 +99,6 @@ struct omap1_spi100k_cs { ...@@ -104,8 +99,6 @@ struct omap1_spi100k_cs {
int word_len; int word_len;
}; };
static struct workqueue_struct *omap1_spi100k_wq;
#define MOD_REG_BIT(val, mask, set) do { \ #define MOD_REG_BIT(val, mask, set) do { \
if (set) \ if (set) \
val |= mask; \ val |= mask; \
...@@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi) ...@@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi)
spi100k_open(spi->master); spi100k_open(spi->master);
clk_enable(spi100k->ick); clk_prepare_enable(spi100k->ick);
clk_enable(spi100k->fck); clk_prepare_enable(spi100k->fck);
ret = omap1_spi100k_setup_transfer(spi, NULL); ret = omap1_spi100k_setup_transfer(spi, NULL);
clk_disable(spi100k->ick); clk_disable_unprepare(spi100k->ick);
clk_disable(spi100k->fck); clk_disable_unprepare(spi100k->fck);
return ret; return ret;
} }
static void omap1_spi100k_work(struct work_struct *work) static int omap1_spi100k_prepare_hardware(struct spi_master *master)
{ {
struct omap1_spi100k *spi100k; struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
int status = 0;
spi100k = container_of(work, struct omap1_spi100k, work); clk_prepare_enable(spi100k->ick);
spin_lock_irq(&spi100k->lock); clk_prepare_enable(spi100k->fck);
clk_enable(spi100k->ick); return 0;
clk_enable(spi100k->fck); }
/* We only enable one channel at a time -- the one whose message is static int omap1_spi100k_transfer_one_message(struct spi_master *master,
* at the head of the queue -- although this controller would gladly struct spi_message *m)
* arbitrate among multiple channels. This corresponds to "single {
* channel" master mode. As a side effect, we need to manage the struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
* chipselect with the FORCE bit ... CS != channel enable. struct spi_device *spi = m->spi;
*/ struct spi_transfer *t = NULL;
while (!list_empty(&spi100k->msg_queue)) { int cs_active = 0;
struct spi_message *m; int par_override = 0;
struct spi_device *spi; int status = 0;
struct spi_transfer *t = NULL;
int cs_active = 0; list_for_each_entry(t, &m->transfers, transfer_list) {
struct omap1_spi100k_cs *cs; if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
int par_override = 0; status = -EINVAL;
break;
m = container_of(spi100k->msg_queue.next, struct spi_message, }
queue); if (par_override || t->speed_hz || t->bits_per_word) {
par_override = 1;
list_del_init(&m->queue); status = omap1_spi100k_setup_transfer(spi, t);
spin_unlock_irq(&spi100k->lock); if (status < 0)
spi = m->spi;
cs = spi->controller_state;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
status = -EINVAL;
break; break;
} if (!t->speed_hz && !t->bits_per_word)
if (par_override || t->speed_hz || t->bits_per_word) { par_override = 0;
par_override = 1; }
status = omap1_spi100k_setup_transfer(spi, t);
if (status < 0)
break;
if (!t->speed_hz && !t->bits_per_word)
par_override = 0;
}
if (!cs_active) { if (!cs_active) {
omap1_spi100k_force_cs(spi100k, 1); omap1_spi100k_force_cs(spi100k, 1);
cs_active = 1; cs_active = 1;
} }
if (t->len) { if (t->len) {
unsigned count; unsigned count;
count = omap1_spi100k_txrx_pio(spi, t); count = omap1_spi100k_txrx_pio(spi, t);
m->actual_length += count; m->actual_length += count;
if (count != t->len) { if (count != t->len) {
status = -EIO; status = -EIO;
break; break;
}
} }
}
if (t->delay_usecs) if (t->delay_usecs)
udelay(t->delay_usecs); udelay(t->delay_usecs);
/* ignore the "leave it on after last xfer" hint */ /* ignore the "leave it on after last xfer" hint */
if (t->cs_change) { if (t->cs_change) {
omap1_spi100k_force_cs(spi100k, 0); omap1_spi100k_force_cs(spi100k, 0);
cs_active = 0; cs_active = 0;
}
}
/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap1_spi100k_setup_transfer(spi, NULL);
} }
}
if (cs_active) /* Restore defaults if they were overriden */
omap1_spi100k_force_cs(spi100k, 0); if (par_override) {
par_override = 0;
status = omap1_spi100k_setup_transfer(spi, NULL);
}
m->status = status; if (cs_active)
m->complete(m->context); omap1_spi100k_force_cs(spi100k, 0);
spin_lock_irq(&spi100k->lock); m->status = status;
}
clk_disable(spi100k->ick); spi_finalize_current_message(master);
clk_disable(spi100k->fck);
spin_unlock_irq(&spi100k->lock);
if (status < 0) return status;
printk(KERN_WARNING "spi transfer failed with %d\n", status);
} }
static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) static int omap1_spi100k_unprepare_hardware(struct spi_master *master)
{ {
struct omap1_spi100k *spi100k; struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
unsigned long flags;
struct spi_transfer *t;
m->actual_length = 0;
m->status = -EINPROGRESS;
spi100k = spi_master_get_devdata(spi->master);
/* Don't accept new work if we're shutting down */
if (spi100k->state == SPI_SHUTDOWN)
return -ESHUTDOWN;
/* reject invalid messages and transfers */
if (list_empty(&m->transfers) || !m->complete)
return -EINVAL;
list_for_each_entry(t, &m->transfers, transfer_list) {
const void *tx_buf = t->tx_buf;
void *rx_buf = t->rx_buf;
unsigned len = t->len;
if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
|| (len && !(rx_buf || tx_buf))) {
dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
t->speed_hz,
len,
tx_buf ? "tx" : "",
rx_buf ? "rx" : "",
t->bits_per_word);
return -EINVAL;
}
if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
t->speed_hz,
OMAP1_SPI100K_MAX_FREQ/(1<<16));
return -EINVAL;
}
}
spin_lock_irqsave(&spi100k->lock, flags);
list_add_tail(&m->queue, &spi100k->msg_queue);
queue_work(omap1_spi100k_wq, &spi100k->work);
spin_unlock_irqrestore(&spi100k->lock, flags);
return 0; clk_disable_unprepare(spi100k->ick);
} clk_disable_unprepare(spi100k->fck);
static int omap1_spi100k_reset(struct omap1_spi100k *spi100k)
{
return 0; return 0;
} }
...@@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev) ...@@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
master->bus_num = pdev->id; master->bus_num = pdev->id;
master->setup = omap1_spi100k_setup; master->setup = omap1_spi100k_setup;
master->transfer = omap1_spi100k_transfer; master->transfer_one_message = omap1_spi100k_transfer_one_message;
master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware;
master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware;
master->cleanup = NULL; master->cleanup = NULL;
master->num_chipselect = 2; master->num_chipselect = 2;
master->mode_bits = MODEBITS; master->mode_bits = MODEBITS;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
platform_set_drvdata(pdev, master); platform_set_drvdata(pdev, master);
...@@ -514,40 +443,29 @@ static int omap1_spi100k_probe(struct platform_device *pdev) ...@@ -514,40 +443,29 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
*/ */
spi100k->base = (void __iomem *) pdev->dev.platform_data; spi100k->base = (void __iomem *) pdev->dev.platform_data;
INIT_WORK(&spi100k->work, omap1_spi100k_work); spi100k->ick = devm_clk_get(&pdev->dev, "ick");
spin_lock_init(&spi100k->lock);
INIT_LIST_HEAD(&spi100k->msg_queue);
spi100k->ick = clk_get(&pdev->dev, "ick");
if (IS_ERR(spi100k->ick)) { if (IS_ERR(spi100k->ick)) {
dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
status = PTR_ERR(spi100k->ick); status = PTR_ERR(spi100k->ick);
goto err1; goto err;
} }
spi100k->fck = clk_get(&pdev->dev, "fck"); spi100k->fck = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(spi100k->fck)) { if (IS_ERR(spi100k->fck)) {
dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
status = PTR_ERR(spi100k->fck); status = PTR_ERR(spi100k->fck);
goto err2; goto err;
} }
if (omap1_spi100k_reset(spi100k) < 0)
goto err3;
status = spi_register_master(master); status = spi_register_master(master);
if (status < 0) if (status < 0)
goto err3; goto err;
spi100k->state = SPI_RUNNING; spi100k->state = SPI_RUNNING;
return status; return status;
err3: err:
clk_put(spi100k->fck);
err2:
clk_put(spi100k->ick);
err1:
spi_master_put(master); spi_master_put(master);
return status; return status;
} }
...@@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev) ...@@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev)
struct spi_master *master; struct spi_master *master;
struct omap1_spi100k *spi100k; struct omap1_spi100k *spi100k;
struct resource *r; struct resource *r;
unsigned limit = 500;
unsigned long flags;
int status = 0; int status = 0;
master = platform_get_drvdata(pdev); master = platform_get_drvdata(pdev);
spi100k = spi_master_get_devdata(master); spi100k = spi_master_get_devdata(master);
spin_lock_irqsave(&spi100k->lock, flags);
spi100k->state = SPI_SHUTDOWN;
while (!list_empty(&spi100k->msg_queue) && limit--) {
spin_unlock_irqrestore(&spi100k->lock, flags);
msleep(10);
spin_lock_irqsave(&spi100k->lock, flags);
}
if (!list_empty(&spi100k->msg_queue))
status = -EBUSY;
spin_unlock_irqrestore(&spi100k->lock, flags);
if (status != 0) if (status != 0)
return status; return status;
clk_put(spi100k->fck);
clk_put(spi100k->ick);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_unregister_master(master); spi_unregister_master(master);
...@@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = { ...@@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = {
.name = "omap1_spi100k", .name = "omap1_spi100k",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = omap1_spi100k_probe,
.remove = omap1_spi100k_remove, .remove = omap1_spi100k_remove,
}; };
module_platform_driver(omap1_spi100k_driver);
static int __init omap1_spi100k_init(void)
{
omap1_spi100k_wq = create_singlethread_workqueue(
omap1_spi100k_driver.driver.name);
if (omap1_spi100k_wq == NULL)
return -1;
return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
}
static void __exit omap1_spi100k_exit(void)
{
platform_driver_unregister(&omap1_spi100k_driver);
destroy_workqueue(omap1_spi100k_wq);
}
module_init(omap1_spi100k_init);
module_exit(omap1_spi100k_exit);
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
......
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