Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 307648
b: refs/heads/master
c: 5fda88f
h: refs/heads/master
v: v3
  • Loading branch information
Shubhrajyoti D authored and Grant Likely committed May 20, 2012
1 parent 9697318 commit 55b3d11
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 134 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a3633fe7aa7022e2e9b2b799a0cbf3d6944d8ba5
refs/heads/master: 5fda88f5e17ea0e767360a3f09eac80f83296ea9
243 changes: 110 additions & 133 deletions trunk/drivers/spi/spi-omap2-mcspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,13 @@ struct omap2_mcspi_regs {
};

struct omap2_mcspi {
struct work_struct work;
/* lock protects queue and registers */
spinlock_t lock;
struct list_head msg_queue;
struct spi_master *master;
/* Virtual base address of the controller */
void __iomem *base;
unsigned long phys;
/* SPI1 has 4 channels, while SPI2 has 2 */
struct omap2_mcspi_dma *dma_channels;
struct device *dev;
struct workqueue_struct *wq;
struct omap2_mcspi_regs ctx;
};

Expand Down Expand Up @@ -275,6 +270,23 @@ static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
return pm_runtime_get_sync(mcspi->dev);
}

static int omap2_prepare_transfer(struct spi_master *master)
{
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);

pm_runtime_get_sync(mcspi->dev);
return 0;
}

static int omap2_unprepare_transfer(struct spi_master *master)
{
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);

pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
return 0;
}

static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
{
unsigned long timeout;
Expand Down Expand Up @@ -846,144 +858,122 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
}
}

static void omap2_mcspi_work(struct work_struct *work)
static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
{
struct omap2_mcspi *mcspi;

mcspi = container_of(work, struct omap2_mcspi, work);

if (omap2_mcspi_enable_clocks(mcspi) < 0)
return;

spin_lock_irq(&mcspi->lock);

/* We only enable one channel at a time -- the one whose message is
* at the head of the queue -- although this controller would gladly
* -- 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(&mcspi->msg_queue)) {
struct spi_message *m;
struct spi_device *spi;
struct spi_transfer *t = NULL;
int cs_active = 0;
struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd;
int par_override = 0;
int status = 0;
u32 chconf;

m = container_of(mcspi->msg_queue.next, struct spi_message,
queue);

list_del_init(&m->queue);
spin_unlock_irq(&mcspi->lock);

spi = m->spi;
cs = spi->controller_state;
cd = spi->controller_data;

omap2_mcspi_set_enable(spi, 1);
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 = omap2_mcspi_setup_transfer(spi, t);
if (status < 0)
break;
if (!t->speed_hz && !t->bits_per_word)
par_override = 0;
}
struct spi_device *spi;
struct spi_transfer *t = NULL;
int cs_active = 0;
struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd;
int par_override = 0;
int status = 0;
u32 chconf;

if (!cs_active) {
omap2_mcspi_force_cs(spi, 1);
cs_active = 1;
}
spi = m->spi;
cs = spi->controller_state;
cd = spi->controller_data;

chconf = mcspi_cached_chconf0(spi);
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
omap2_mcspi_set_enable(spi, 1);
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 = omap2_mcspi_setup_transfer(spi, t);
if (status < 0)
break;
if (!t->speed_hz && !t->bits_per_word)
par_override = 0;
}

if (t->tx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
else if (t->rx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;

if (cd && cd->turbo_mode && t->tx_buf == NULL) {
/* Turbo mode is for more than one word */
if (t->len > ((cs->word_len + 7) >> 3))
chconf |= OMAP2_MCSPI_CHCONF_TURBO;
}
if (!cs_active) {
omap2_mcspi_force_cs(spi, 1);
cs_active = 1;
}

mcspi_write_chconf0(spi, chconf);
chconf = mcspi_cached_chconf0(spi);
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;

if (t->len) {
unsigned count;
if (t->tx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
else if (t->rx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;

/* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL)
__raw_writel(0, cs->base
+ OMAP2_MCSPI_TX0);
if (cd && cd->turbo_mode && t->tx_buf == NULL) {
/* Turbo mode is for more than one word */
if (t->len > ((cs->word_len + 7) >> 3))
chconf |= OMAP2_MCSPI_CHCONF_TURBO;
}

if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
count = omap2_mcspi_txrx_dma(spi, t);
else
count = omap2_mcspi_txrx_pio(spi, t);
m->actual_length += count;
mcspi_write_chconf0(spi, chconf);

if (count != t->len) {
status = -EIO;
break;
}
}
if (t->len) {
unsigned count;

/* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL)
__raw_writel(0, cs->base
+ OMAP2_MCSPI_TX0);

if (t->delay_usecs)
udelay(t->delay_usecs);
if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
count = omap2_mcspi_txrx_dma(spi, t);
else
count = omap2_mcspi_txrx_pio(spi, t);
m->actual_length += count;

/* ignore the "leave it on after last xfer" hint */
if (t->cs_change) {
omap2_mcspi_force_cs(spi, 0);
cs_active = 0;
if (count != t->len) {
status = -EIO;
break;
}
}

/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL);
}
if (t->delay_usecs)
udelay(t->delay_usecs);

if (cs_active)
/* ignore the "leave it on after last xfer" hint */
if (t->cs_change) {
omap2_mcspi_force_cs(spi, 0);
cs_active = 0;
}
}
/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL);
}

omap2_mcspi_set_enable(spi, 0);

m->status = status;
m->complete(m->context);
if (cs_active)
omap2_mcspi_force_cs(spi, 0);

spin_lock_irq(&mcspi->lock);
}
omap2_mcspi_set_enable(spi, 0);

spin_unlock_irq(&mcspi->lock);
m->status = status;

omap2_mcspi_disable_clocks(mcspi);
}

static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
static int omap2_mcspi_transfer_one_message(struct spi_master *master,
struct spi_message *m)
{
struct omap2_mcspi *mcspi;
unsigned long flags;
struct spi_transfer *t;

mcspi = spi_master_get_devdata(master);
m->actual_length = 0;
m->status = 0;

/* reject invalid messages and transfers */
if (list_empty(&m->transfers) || !m->complete)
if (list_empty(&m->transfers))
return -EINVAL;
list_for_each_entry(t, &m->transfers, transfer_list) {
const void *tx_buf = t->tx_buf;
Expand All @@ -995,7 +985,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
|| (t->bits_per_word &&
( t->bits_per_word < 4
|| t->bits_per_word > 32))) {
dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
t->speed_hz,
len,
tx_buf ? "tx" : "",
Expand All @@ -1004,7 +994,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
return -EINVAL;
}
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n",
dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n",
t->speed_hz,
OMAP2_MCSPI_MAX_FREQ >> 15);
return -EINVAL;
Expand All @@ -1014,35 +1004,30 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
continue;

if (tx_buf != NULL) {
t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf,
t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
len, DMA_TO_DEVICE);
if (dma_mapping_error(&spi->dev, t->tx_dma)) {
dev_dbg(&spi->dev, "dma %cX %d bytes error\n",
if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
'T', len);
return -EINVAL;
}
}
if (rx_buf != NULL) {
t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len,
t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&spi->dev, t->rx_dma)) {
dev_dbg(&spi->dev, "dma %cX %d bytes error\n",
if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
'R', len);
if (tx_buf != NULL)
dma_unmap_single(&spi->dev, t->tx_dma,
dma_unmap_single(mcspi->dev, t->tx_dma,
len, DMA_TO_DEVICE);
return -EINVAL;
}
}
}

mcspi = spi_master_get_devdata(spi->master);

spin_lock_irqsave(&mcspi->lock, flags);
list_add_tail(&m->queue, &mcspi->msg_queue);
queue_work(mcspi->wq, &mcspi->work);
spin_unlock_irqrestore(&mcspi->lock, flags);

omap2_mcspi_work(mcspi, m);
spi_finalize_current_message(master);
return 0;
}

Expand Down Expand Up @@ -1120,7 +1105,9 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

master->setup = omap2_mcspi_setup;
master->transfer = omap2_mcspi_transfer;
master->prepare_transfer_hardware = omap2_prepare_transfer;
master->unprepare_transfer_hardware = omap2_unprepare_transfer;
master->transfer_one_message = omap2_mcspi_transfer_one_message;
master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;

Expand All @@ -1145,12 +1132,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
mcspi = spi_master_get_devdata(master);
mcspi->master = master;

mcspi->wq = alloc_workqueue(dev_name(&pdev->dev), WQ_MEM_RECLAIM, 1);
if (mcspi->wq == NULL) {
status = -ENOMEM;
goto free_master;
}

r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
status = -ENODEV;
Expand All @@ -1169,10 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
}

mcspi->dev = &pdev->dev;
INIT_WORK(&mcspi->work, omap2_mcspi_work);

spin_lock_init(&mcspi->lock);
INIT_LIST_HEAD(&mcspi->msg_queue);
INIT_LIST_HEAD(&mcspi->ctx.cs);

mcspi->dma_channels = kcalloc(master->num_chipselect,
Expand Down Expand Up @@ -1253,7 +1231,6 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev)

spi_unregister_master(master);
kfree(dma_channels);
destroy_workqueue(mcspi->wq);
platform_set_drvdata(pdev, NULL);

return 0;
Expand Down

0 comments on commit 55b3d11

Please sign in to comment.