|
@@ -83,11 +83,6 @@
|
|
|
#define SPI_SHUTDOWN 1
|
|
|
|
|
|
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 clk *ick;
|
|
|
struct clk *fck;
|
|
@@ -104,8 +99,6 @@ struct omap1_spi100k_cs {
|
|
|
int word_len;
|
|
|
};
|
|
|
|
|
|
-static struct workqueue_struct *omap1_spi100k_wq;
|
|
|
-
|
|
|
#define MOD_REG_BIT(val, mask, set) do { \
|
|
|
if (set) \
|
|
|
val |= mask; \
|
|
@@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi)
|
|
|
|
|
|
spi100k_open(spi->master);
|
|
|
|
|
|
- clk_enable(spi100k->ick);
|
|
|
- clk_enable(spi100k->fck);
|
|
|
+ clk_prepare_enable(spi100k->ick);
|
|
|
+ clk_prepare_enable(spi100k->fck);
|
|
|
|
|
|
ret = omap1_spi100k_setup_transfer(spi, NULL);
|
|
|
|
|
|
- clk_disable(spi100k->ick);
|
|
|
- clk_disable(spi100k->fck);
|
|
|
+ clk_disable_unprepare(spi100k->ick);
|
|
|
+ clk_disable_unprepare(spi100k->fck);
|
|
|
|
|
|
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;
|
|
|
- int status = 0;
|
|
|
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
|
|
|
|
|
- spi100k = container_of(work, struct omap1_spi100k, work);
|
|
|
- spin_lock_irq(&spi100k->lock);
|
|
|
+ clk_prepare_enable(spi100k->ick);
|
|
|
+ clk_prepare_enable(spi100k->fck);
|
|
|
|
|
|
- clk_enable(spi100k->ick);
|
|
|
- clk_enable(spi100k->fck);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- /* We only enable one channel at a time -- the one whose message is
|
|
|
- * at the head of the queue -- although this controller would gladly
|
|
|
- * arbitrate among multiple channels. This corresponds to "single
|
|
|
- * channel" master mode. As a side effect, we need to manage the
|
|
|
- * chipselect with the FORCE bit ... CS != channel enable.
|
|
|
- */
|
|
|
- while (!list_empty(&spi100k->msg_queue)) {
|
|
|
- struct spi_message *m;
|
|
|
- struct spi_device *spi;
|
|
|
- struct spi_transfer *t = NULL;
|
|
|
- int cs_active = 0;
|
|
|
- struct omap1_spi100k_cs *cs;
|
|
|
- int par_override = 0;
|
|
|
-
|
|
|
- m = container_of(spi100k->msg_queue.next, struct spi_message,
|
|
|
- queue);
|
|
|
-
|
|
|
- list_del_init(&m->queue);
|
|
|
- spin_unlock_irq(&spi100k->lock);
|
|
|
-
|
|
|
- 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;
|
|
|
+static int omap1_spi100k_transfer_one_message(struct spi_master *master,
|
|
|
+ struct spi_message *m)
|
|
|
+{
|
|
|
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
|
|
+ struct spi_device *spi = m->spi;
|
|
|
+ struct spi_transfer *t = NULL;
|
|
|
+ int cs_active = 0;
|
|
|
+ int par_override = 0;
|
|
|
+ int status = 0;
|
|
|
+
|
|
|
+ list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
|
+ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
|
|
+ status = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (par_override || t->speed_hz || t->bits_per_word) {
|
|
|
+ par_override = 1;
|
|
|
+ status = omap1_spi100k_setup_transfer(spi, t);
|
|
|
+ if (status < 0)
|
|
|
break;
|
|
|
- }
|
|
|
- if (par_override || t->speed_hz || t->bits_per_word) {
|
|
|
- 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 (!t->speed_hz && !t->bits_per_word)
|
|
|
+ par_override = 0;
|
|
|
+ }
|
|
|
|
|
|
- if (!cs_active) {
|
|
|
- omap1_spi100k_force_cs(spi100k, 1);
|
|
|
- cs_active = 1;
|
|
|
- }
|
|
|
+ if (!cs_active) {
|
|
|
+ omap1_spi100k_force_cs(spi100k, 1);
|
|
|
+ cs_active = 1;
|
|
|
+ }
|
|
|
|
|
|
- if (t->len) {
|
|
|
- unsigned count;
|
|
|
+ if (t->len) {
|
|
|
+ unsigned count;
|
|
|
|
|
|
- count = omap1_spi100k_txrx_pio(spi, t);
|
|
|
- m->actual_length += count;
|
|
|
+ count = omap1_spi100k_txrx_pio(spi, t);
|
|
|
+ m->actual_length += count;
|
|
|
|
|
|
- if (count != t->len) {
|
|
|
- status = -EIO;
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (count != t->len) {
|
|
|
+ status = -EIO;
|
|
|
+ break;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (t->delay_usecs)
|
|
|
- udelay(t->delay_usecs);
|
|
|
+ if (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) {
|
|
|
- omap1_spi100k_force_cs(spi100k, 0);
|
|
|
- cs_active = 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Restore defaults if they were overriden */
|
|
|
- if (par_override) {
|
|
|
- par_override = 0;
|
|
|
- status = omap1_spi100k_setup_transfer(spi, NULL);
|
|
|
+ if (t->cs_change) {
|
|
|
+ omap1_spi100k_force_cs(spi100k, 0);
|
|
|
+ cs_active = 0;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (cs_active)
|
|
|
- omap1_spi100k_force_cs(spi100k, 0);
|
|
|
+ /* Restore defaults if they were overriden */
|
|
|
+ if (par_override) {
|
|
|
+ par_override = 0;
|
|
|
+ status = omap1_spi100k_setup_transfer(spi, NULL);
|
|
|
+ }
|
|
|
|
|
|
- m->status = status;
|
|
|
- m->complete(m->context);
|
|
|
+ if (cs_active)
|
|
|
+ omap1_spi100k_force_cs(spi100k, 0);
|
|
|
|
|
|
- spin_lock_irq(&spi100k->lock);
|
|
|
- }
|
|
|
+ m->status = status;
|
|
|
|
|
|
- clk_disable(spi100k->ick);
|
|
|
- clk_disable(spi100k->fck);
|
|
|
- spin_unlock_irq(&spi100k->lock);
|
|
|
+ spi_finalize_current_message(master);
|
|
|
|
|
|
- if (status < 0)
|
|
|
- printk(KERN_WARNING "spi transfer failed with %d\n", status);
|
|
|
+ return 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;
|
|
|
- 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);
|
|
|
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ clk_disable_unprepare(spi100k->ick);
|
|
|
+ clk_disable_unprepare(spi100k->fck);
|
|
|
|
|
|
-static int omap1_spi100k_reset(struct omap1_spi100k *spi100k)
|
|
|
-{
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|
|
master->bus_num = pdev->id;
|
|
|
|
|
|
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->num_chipselect = 2;
|
|
|
master->mode_bits = MODEBITS;
|
|
|
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);
|
|
|
|
|
@@ -514,40 +443,29 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|
|
*/
|
|
|
spi100k->base = (void __iomem *) pdev->dev.platform_data;
|
|
|
|
|
|
- INIT_WORK(&spi100k->work, omap1_spi100k_work);
|
|
|
-
|
|
|
- spin_lock_init(&spi100k->lock);
|
|
|
- INIT_LIST_HEAD(&spi100k->msg_queue);
|
|
|
- spi100k->ick = clk_get(&pdev->dev, "ick");
|
|
|
+ spi100k->ick = devm_clk_get(&pdev->dev, "ick");
|
|
|
if (IS_ERR(spi100k->ick)) {
|
|
|
dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
|
|
|
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)) {
|
|
|
dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
|
|
|
status = PTR_ERR(spi100k->fck);
|
|
|
- goto err2;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
- if (omap1_spi100k_reset(spi100k) < 0)
|
|
|
- goto err3;
|
|
|
-
|
|
|
status = spi_register_master(master);
|
|
|
if (status < 0)
|
|
|
- goto err3;
|
|
|
+ goto err;
|
|
|
|
|
|
spi100k->state = SPI_RUNNING;
|
|
|
|
|
|
return status;
|
|
|
|
|
|
-err3:
|
|
|
- clk_put(spi100k->fck);
|
|
|
-err2:
|
|
|
- clk_put(spi100k->ick);
|
|
|
-err1:
|
|
|
+err:
|
|
|
spi_master_put(master);
|
|
|
return status;
|
|
|
}
|
|
@@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev)
|
|
|
struct spi_master *master;
|
|
|
struct omap1_spi100k *spi100k;
|
|
|
struct resource *r;
|
|
|
- unsigned limit = 500;
|
|
|
- unsigned long flags;
|
|
|
int status = 0;
|
|
|
|
|
|
master = platform_get_drvdata(pdev);
|
|
|
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)
|
|
|
return status;
|
|
|
|
|
|
- clk_put(spi100k->fck);
|
|
|
- clk_put(spi100k->ick);
|
|
|
-
|
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
|
|
spi_unregister_master(master);
|
|
@@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = {
|
|
|
.name = "omap1_spi100k",
|
|
|
.owner = THIS_MODULE,
|
|
|
},
|
|
|
+ .probe = omap1_spi100k_probe,
|
|
|
.remove = omap1_spi100k_remove,
|
|
|
};
|
|
|
|
|
|
-
|
|
|
-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_platform_driver(omap1_spi100k_driver);
|
|
|
|
|
|
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
|
|
|
MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
|