Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 164794
b: refs/heads/master
c: 570327d
h: refs/heads/master
v: v3
  • Loading branch information
Ben Dooks authored and Linus Torvalds committed Sep 23, 2009
1 parent 31fa4c3 commit 4190ba1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 38 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: 6d61320707ac2960bc820616bd5921885b874780
refs/heads/master: 570327d9f48b0be5ca626bcfd80266b7ee2001aa
126 changes: 89 additions & 37 deletions trunk/drivers/spi/spi_s3c24xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@
#include <plat/regs-spi.h>
#include <mach/spi.h>

/**
* s3c24xx_spi_devstate - per device data
* @hz: Last frequency calculated for @sppre field.
* @mode: Last mode setting for the @spcon field.
* @spcon: Value to write to the SPCON register.
* @sppre: Value to write to the SPPRE register.
*/
struct s3c24xx_spi_devstate {
unsigned int hz;
unsigned int mode;
u8 spcon;
u8 sppre;
};

struct s3c24xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
Expand Down Expand Up @@ -68,43 +82,31 @@ static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)

static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned int spcon;

/* change the chipselect state and the state of the spi engine clock */

switch (value) {
case BITBANG_CS_INACTIVE:
hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
writeb(cs->spcon, hw->regs + S3C2410_SPCON);
break;

case BITBANG_CS_ACTIVE:
spcon = readb(hw->regs + S3C2410_SPCON);

if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;
else
spcon &= ~S3C2410_SPCON_CPHA_FMTB;

if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;
else
spcon &= ~S3C2410_SPCON_CPOL_HIGH;

spcon |= S3C2410_SPCON_ENSCK;

/* write new configration */

writeb(spcon, hw->regs + S3C2410_SPCON);
writeb(cs->spcon | S3C2410_SPCON_ENSCK,
hw->regs + S3C2410_SPCON);
hw->set_cs(hw->pdata, spi->chip_select, cspol);

break;
}
}

static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
static int s3c24xx_spi_update_state(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
struct s3c24xx_spi_devstate *cs = spi->controller_state;
unsigned int bpw;
unsigned int hz;
unsigned int div;
Expand All @@ -124,41 +126,89 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi,
return -EINVAL;
}

clk = clk_get_rate(hw->clk);
div = DIV_ROUND_UP(clk, hz * 2) - 1;
if (spi->mode != cs->mode) {
u8 spcon = SPCON_DEFAULT;

if (div > 255)
div = 255;
if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;

dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n",
div, hz, clk / (2 * (div + 1)));
if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;

cs->mode = spi->mode;
cs->spcon = spcon;
}

writeb(div, hw->regs + S3C2410_SPPRE);
if (cs->hz != hz) {
clk = clk_get_rate(hw->clk);
div = DIV_ROUND_UP(clk, hz * 2) - 1;

spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy) {
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
/* need to ndelay for 0.5 clocktick ? */
if (div > 255)
div = 255;

dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",
div, hz, clk / (2 * (div + 1)));

cs->hz = hz;
cs->sppre = div;
}
spin_unlock(&hw->bitbang.lock);

return 0;
}

static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
int ret;

ret = s3c24xx_spi_update_state(spi, t);
if (!ret)
writeb(cs->sppre, hw->regs + S3C2410_SPPRE);

return ret;
}

static int s3c24xx_spi_setup(struct spi_device *spi)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
int ret;

ret = s3c24xx_spi_setupxfer(spi, NULL);
if (ret < 0) {
dev_err(&spi->dev, "setupxfer returned %d\n", ret);
/* allocate settings on the first call */
if (!cs) {
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
if (!cs) {
dev_err(&spi->dev, "no memory for controller state\n");
return -ENOMEM;
}

cs->spcon = SPCON_DEFAULT;
cs->hz = -1;
spi->controller_state = cs;
}

/* initialise the state from the device */
ret = s3c24xx_spi_update_state(spi, NULL);
if (ret)
return ret;

spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy) {
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
/* need to ndelay for 0.5 clocktick ? */
}
spin_unlock(&hw->bitbang.lock);

return 0;
}

static void s3c24xx_spi_cleanup(struct spi_device *spi)
{
kfree(spi->controller_state);
}

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{
return hw->tx ? hw->tx[count] : 0;
Expand Down Expand Up @@ -286,7 +336,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;

hw->master->setup = s3c24xx_spi_setup;
hw->master->cleanup = s3c24xx_spi_cleanup;

dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);

Expand Down

0 comments on commit 4190ba1

Please sign in to comment.