Skip to content

Commit

Permalink
spi/pxa2xx: Add chipselect support for Sodaville
Browse files Browse the repository at this point in the history
The SPI core on Sodaville supports chip selects. Its configuration
moved into the SSSR register at bit 0 and 1. Thus Sodaville can be hooked
up with up to 4 devices.
This patch ensures that the bits which are otherwiese reserved are only
touched on Sodaville and not on any other PXAs. Also it makes sure that
the status register does not lose the CS information while clearing the
ROR bit.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
  • Loading branch information
Sebastian Andrzej Siewior committed Dec 2, 2010
1 parent d0777f2 commit 2a8626a
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 25 deletions.
93 changes: 68 additions & 25 deletions drivers/spi/pxa2xx_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@ struct chip_data {
u8 enable_dma;
u8 bits_per_word;
u32 speed_hz;
int gpio_cs;
union {
int gpio_cs;
unsigned int frm;
};
int gpio_cs_inverted;
int (*write)(struct driver_data *drv_data);
int (*read)(struct driver_data *drv_data);
Expand All @@ -176,6 +179,11 @@ static void cs_assert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;

if (drv_data->ssp_type == CE4100_SSP) {
write_SSSR(drv_data->cur_chip->frm, drv_data->ioaddr);
return;
}

if (chip->cs_control) {
chip->cs_control(PXA2XX_CS_ASSERT);
return;
Expand All @@ -189,6 +197,9 @@ static void cs_deassert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;

if (drv_data->ssp_type == CE4100_SSP)
return;

if (chip->cs_control) {
chip->cs_control(PXA2XX_CS_DEASSERT);
return;
Expand All @@ -198,6 +209,25 @@ static void cs_deassert(struct driver_data *drv_data)
gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted);
}

static void write_SSSR_CS(struct driver_data *drv_data, u32 val)
{
void __iomem *reg = drv_data->ioaddr;

if (drv_data->ssp_type == CE4100_SSP)
val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK;

write_SSSR(val, reg);
}

static int pxa25x_ssp_comp(struct driver_data *drv_data)
{
if (drv_data->ssp_type == PXA25x_SSP)
return 1;
if (drv_data->ssp_type == CE4100_SSP)
return 1;
return 0;
}

static int flush(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
Expand All @@ -209,7 +239,7 @@ static int flush(struct driver_data *drv_data)
read_SSDR(reg);
}
} while ((read_SSSR(reg) & SSSR_BSY) && --limit);
write_SSSR(SSSR_ROR, reg);
write_SSSR_CS(drv_data, SSSR_ROR);

return limit;
}
Expand Down Expand Up @@ -502,9 +532,9 @@ static void dma_error_stop(struct driver_data *drv_data, const char *msg)
/* Stop and reset */
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, reg);
flush(drv_data);
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
Expand All @@ -524,7 +554,7 @@ static void dma_transfer_complete(struct driver_data *drv_data)

/* Clear and disable interrupts on SSP and DMA channels*/
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;

Expand Down Expand Up @@ -617,7 +647,7 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)

/* Clear and disable timeout interrupt, do the rest in
* dma_transfer_complete */
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, reg);

/* finish this transfer, start the next */
Expand All @@ -635,9 +665,9 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)
void __iomem *reg = drv_data->ioaddr;

/* Stop and reset SSP */
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, reg);
flush(drv_data);
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
Expand All @@ -653,9 +683,9 @@ static void int_transfer_complete(struct driver_data *drv_data)
void __iomem *reg = drv_data->ioaddr;

/* Stop SSP */
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, reg);

/* Update total byte transfered return count actual bytes read */
Expand Down Expand Up @@ -711,7 +741,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
if (drv_data->tx == drv_data->tx_end) {
write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
/* PXA25x_SSP has no timeout, read trailing bytes */
if (drv_data->ssp_type == PXA25x_SSP) {
if (pxa25x_ssp_comp(drv_data)) {
if (!wait_ssp_rx_stall(reg))
{
int_error_stop(drv_data, "interrupt_transfer: "
Expand Down Expand Up @@ -754,9 +784,9 @@ static irqreturn_t ssp_int(int irq, void *dev_id)

write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);

dev_err(&drv_data->pdev->dev, "bad message state "
"in interrupt handler\n");
Expand Down Expand Up @@ -869,7 +899,7 @@ static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate)
{
unsigned long ssp_clk = clk_get_rate(ssp->clk);

if (ssp->type == PXA25x_SSP)
if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP)
return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8;
else
return ((ssp_clk / rate - 1) & 0xfff) << 8;
Expand Down Expand Up @@ -1095,7 +1125,7 @@ static void pump_transfers(unsigned long data)

/* Clear status */
cr1 = chip->cr1 | chip->threshold | drv_data->int_cr1;
write_SSSR(drv_data->clear_sr, reg);
write_SSSR_CS(drv_data, drv_data->clear_sr);
}

/* see if we need to reload the config registers */
Expand All @@ -1105,15 +1135,15 @@ static void pump_transfers(unsigned long data)

/* stop the SSP, and update the other bits */
write_SSCR0(cr0 & ~SSCR0_SSE, reg);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(chip->timeout, reg);
/* first set CR1 without interrupt and service enables */
write_SSCR1(cr1 & SSCR1_CHANGE_MASK, reg);
/* restart the SSP */
write_SSCR0(cr0, reg);

} else {
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(chip->timeout, reg);
}

Expand Down Expand Up @@ -1240,14 +1270,13 @@ static int setup(struct spi_device *spi)
uint tx_thres = TX_THRESH_DFLT;
uint rx_thres = RX_THRESH_DFLT;

if (drv_data->ssp_type != PXA25x_SSP
if (!pxa25x_ssp_comp(drv_data)
&& (spi->bits_per_word < 4 || spi->bits_per_word > 32)) {
dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d "
"b/w not 4-32 for type non-PXA25x_SSP\n",
drv_data->ssp_type, spi->bits_per_word);
return -EINVAL;
}
else if (drv_data->ssp_type == PXA25x_SSP
} else if (pxa25x_ssp_comp(drv_data)
&& (spi->bits_per_word < 4
|| spi->bits_per_word > 16)) {
dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d "
Expand All @@ -1266,7 +1295,17 @@ static int setup(struct spi_device *spi)
return -ENOMEM;
}

chip->gpio_cs = -1;
if (drv_data->ssp_type == CE4100_SSP) {
if (spi->chip_select > 4) {
dev_err(&spi->dev, "failed setup: "
"cs number must not be > 4.\n");
kfree(chip);
return -EINVAL;
}

chip->frm = spi->chip_select;
} else
chip->gpio_cs = -1;
chip->enable_dma = 0;
chip->timeout = TIMOUT_DFLT;
chip->dma_burst_size = drv_data->master_info->enable_dma ?
Expand Down Expand Up @@ -1322,7 +1361,7 @@ static int setup(struct spi_device *spi)
| (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0);

/* NOTE: PXA25x_SSP _could_ use external clocking ... */
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
dev_dbg(&spi->dev, "%ld Hz actual, %s\n",
clk_get_rate(ssp->clk)
/ (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)),
Expand Down Expand Up @@ -1357,17 +1396,21 @@ static int setup(struct spi_device *spi)

spi_set_ctldata(spi, chip);

if (drv_data->ssp_type == CE4100_SSP)
return 0;

return setup_cs(spi, chip, chip_info);
}

static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
struct driver_data *drv_data = spi_master_get_devdata(spi->master);

if (!chip)
return;

if (gpio_is_valid(chip->gpio_cs))
if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs))
gpio_free(chip->gpio_cs);

kfree(chip);
Expand Down Expand Up @@ -1507,7 +1550,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev)

drv_data->ioaddr = ssp->mmio_base;
drv_data->ssdr_physical = ssp->phys_base + SSDR;
if (ssp->type == PXA25x_SSP) {
if (pxa25x_ssp_comp(drv_data)) {
drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE;
drv_data->dma_cr1 = 0;
drv_data->clear_sr = SSSR_ROR;
Expand Down Expand Up @@ -1569,7 +1612,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev)
| SSCR0_Motorola
| SSCR0_DataSize(8),
drv_data->ioaddr);
if (drv_data->ssp_type != PXA25x_SSP)
if (!pxa25x_ssp_comp(drv_data))
write_SSTO(0, drv_data->ioaddr);
write_SSPSP(0, drv_data->ioaddr);

Expand Down
2 changes: 2 additions & 0 deletions include/linux/pxa2xx_ssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
#define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */
#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */

#define SSSR_ALT_FRM_MASK 3 /* Masks the SFRM signal number */
#define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */
#define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */
#define SSSR_BSY (1 << 4) /* SSP Busy */
Expand Down Expand Up @@ -160,6 +161,7 @@ enum pxa_ssp_type {
PXA25x_NSSP, /* pxa 255, 26x (including ASSP) */
PXA27x_SSP,
PXA168_SSP,
CE4100_SSP,
};

struct ssp_device {
Expand Down

0 comments on commit 2a8626a

Please sign in to comment.