diff options
Diffstat (limited to 'drivers/spi/spi-pxa2xx.c')
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 304 |
1 files changed, 264 insertions, 40 deletions
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 9e9e0f971e6..1a1df5092ac 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -63,10 +63,64 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) +#define QUARK_X1000_SSCR1_CHANGE_MASK (QUARK_X1000_SSCR1_STRF \ + | QUARK_X1000_SSCR1_EFWR \ + | QUARK_X1000_SSCR1_RFT \ + | QUARK_X1000_SSCR1_TFT \ + | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) + #define LPSS_RX_THRESH_DFLT 64 #define LPSS_TX_LOTHRESH_DFLT 160 #define LPSS_TX_HITHRESH_DFLT 224 +struct quark_spi_rate { + u32 bitrate; + u32 dds_clk_rate; + u32 clk_div; +}; + +/* + * 'rate', 'dds', 'clk_div' lookup table, which is defined in + * the Quark SPI datasheet. + */ +static const struct quark_spi_rate quark_spi_rate_table[] = { +/* bitrate, dds_clk_rate, clk_div */ + {50000000, 0x800000, 0}, + {40000000, 0x666666, 0}, + {25000000, 0x400000, 0}, + {20000000, 0x666666, 1}, + {16667000, 0x800000, 2}, + {13333000, 0x666666, 2}, + {12500000, 0x200000, 0}, + {10000000, 0x800000, 4}, + {8000000, 0x666666, 4}, + {6250000, 0x400000, 3}, + {5000000, 0x400000, 4}, + {4000000, 0x666666, 9}, + {3125000, 0x80000, 0}, + {2500000, 0x400000, 9}, + {2000000, 0x666666, 19}, + {1563000, 0x40000, 0}, + {1250000, 0x200000, 9}, + {1000000, 0x400000, 24}, + {800000, 0x666666, 49}, + {781250, 0x20000, 0}, + {625000, 0x200000, 19}, + {500000, 0x400000, 49}, + {400000, 0x666666, 99}, + {390625, 0x10000, 0}, + {250000, 0x400000, 99}, + {200000, 0x666666, 199}, + {195313, 0x8000, 0}, + {125000, 0x100000, 49}, + {100000, 0x200000, 124}, + {50000, 0x100000, 124}, + {25000, 0x80000, 124}, + {10016, 0x20000, 77}, + {5040, 0x20000, 154}, + {1002, 0x8000, 194}, +}; + /* Offset from drv_data->lpss_base */ #define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) @@ -80,6 +134,96 @@ static bool is_lpss_ssp(const struct driver_data *drv_data) return drv_data->ssp_type == LPSS_SSP; } +static bool is_quark_x1000_ssp(const struct driver_data *drv_data) +{ + return drv_data->ssp_type == QUARK_X1000_SSP; +} + +static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data) +{ + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + return QUARK_X1000_SSCR1_CHANGE_MASK; + default: + return SSCR1_CHANGE_MASK; + } +} + +static u32 +pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data) +{ + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + return RX_THRESH_QUARK_X1000_DFLT; + default: + return RX_THRESH_DFLT; + } +} + +static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) +{ + void __iomem *reg = drv_data->ioaddr; + u32 mask; + + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + mask = QUARK_X1000_SSSR_TFL_MASK; + break; + default: + mask = SSSR_TFL_MASK; + break; + } + + return (read_SSSR(reg) & mask) == mask; +} + +static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data, + u32 *sccr1_reg) +{ + u32 mask; + + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + mask = QUARK_X1000_SSCR1_RFT; + break; + default: + mask = SSCR1_RFT; + break; + } + *sccr1_reg &= ~mask; +} + +static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data, + u32 *sccr1_reg, u32 threshold) +{ + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + *sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold); + break; + default: + *sccr1_reg |= SSCR1_RxTresh(threshold); + break; + } +} + +static u32 pxa2xx_configure_sscr0(const struct driver_data *drv_data, + u32 clk_div, u8 bits) +{ + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + return clk_div + | QUARK_X1000_SSCR0_Motorola + | QUARK_X1000_SSCR0_DataSize(bits > 32 ? 8 : bits) + | SSCR0_SSE; + default: + return clk_div + | SSCR0_Motorola + | SSCR0_DataSize(bits > 16 ? bits - 16 : bits) + | SSCR0_SSE + | (bits > 16 ? SSCR0_EDSS : 0); + } +} + /* * Read and write LPSS SSP private registers. Caller must first check that * is_lpss_ssp() returns true before these can be called. @@ -234,7 +378,7 @@ static int null_writer(struct driver_data *drv_data) void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; - if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) + if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -262,7 +406,7 @@ static int u8_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) + if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -289,7 +433,7 @@ static int u16_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) + if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -316,7 +460,7 @@ static int u32_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; - if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK) + if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; @@ -508,8 +652,9 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) * remaining RX bytes. */ if (pxa25x_ssp_comp(drv_data)) { + u32 rx_thre; - sccr1_reg &= ~SSCR1_RFT; + pxa2xx_spi_clear_rx_thre(drv_data, &sccr1_reg); bytes_left = drv_data->rx_end - drv_data->rx; switch (drv_data->n_bytes) { @@ -519,10 +664,11 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) bytes_left >>= 1; } - if (bytes_left > RX_THRESH_DFLT) - bytes_left = RX_THRESH_DFLT; + rx_thre = pxa2xx_spi_get_rx_default_thre(drv_data); + if (rx_thre > bytes_left) + rx_thre = bytes_left; - sccr1_reg |= SSCR1_RxTresh(bytes_left); + pxa2xx_spi_set_rx_thre(drv_data, &sccr1_reg, rx_thre); } write_SSCR1(sccr1_reg, reg); } @@ -585,6 +731,28 @@ static irqreturn_t ssp_int(int irq, void *dev_id) return drv_data->transfer_handler(drv_data); } +/* + * The Quark SPI data sheet gives a table, and for the given 'rate', + * the 'dds' and 'clk_div' can be found in the table. + */ +static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) { + if (rate >= quark_spi_rate_table[i].bitrate) { + *dds = quark_spi_rate_table[i].dds_clk_rate; + *clk_div = quark_spi_rate_table[i].clk_div; + return quark_spi_rate_table[i].bitrate; + } + } + + *dds = quark_spi_rate_table[i-1].dds_clk_rate; + *clk_div = quark_spi_rate_table[i-1].clk_div; + + return quark_spi_rate_table[i-1].bitrate; +} + static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) { unsigned long ssp_clk = drv_data->max_clk_rate; @@ -598,6 +766,20 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) return ((ssp_clk / rate - 1) & 0xfff) << 8; } +static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, + struct chip_data *chip, int rate) +{ + u32 clk_div; + + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div); + return clk_div << 8; + default: + return ssp_get_clk_div(drv_data, rate); + } +} + static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; @@ -613,6 +795,7 @@ static void pump_transfers(unsigned long data) u32 cr1; u32 dma_thresh = drv_data->cur_chip->dma_threshold; u32 dma_burst = drv_data->cur_chip->dma_burst_size; + u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data); /* Get current state information */ message = drv_data->cur_msg; @@ -699,7 +882,7 @@ static void pump_transfers(unsigned long data) if (transfer->bits_per_word) bits = transfer->bits_per_word; - clk_div = ssp_get_clk_div(drv_data, speed); + clk_div = pxa2xx_ssp_get_clk_div(drv_data, chip, speed); if (bits <= 8) { drv_data->n_bytes = 1; @@ -731,11 +914,7 @@ static void pump_transfers(unsigned long data) "pump_transfers: DMA burst size reduced to match bits_per_word\n"); } - cr0 = clk_div - | SSCR0_Motorola - | SSCR0_DataSize(bits > 16 ? bits - 16 : bits) - | SSCR0_SSE - | (bits > 16 ? SSCR0_EDSS : 0); + cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); } message->state = RUNNING_STATE; @@ -771,17 +950,20 @@ static void pump_transfers(unsigned long data) write_SSITF(chip->lpss_tx_threshold, reg); } + if (is_quark_x1000_ssp(drv_data) && + (read_DDS_RATE(reg) != chip->dds_rate)) + write_DDS_RATE(chip->dds_rate, reg); + /* see if we need to reload the config registers */ - if ((read_SSCR0(reg) != cr0) - || (read_SSCR1(reg) & SSCR1_CHANGE_MASK) != - (cr1 & SSCR1_CHANGE_MASK)) { + if ((read_SSCR0(reg) != cr0) || + (read_SSCR1(reg) & change_mask) != (cr1 & change_mask)) { /* stop the SSP, and update the other bits */ write_SSCR0(cr0 & ~SSCR0_SSE, reg); 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); + write_SSCR1(cr1 & change_mask, reg); /* restart the SSP */ write_SSCR0(cr0, reg); @@ -875,14 +1057,22 @@ static int setup(struct spi_device *spi) unsigned int clk_div; uint tx_thres, tx_hi_thres, rx_thres; - if (is_lpss_ssp(drv_data)) { + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + tx_thres = TX_THRESH_QUARK_X1000_DFLT; + tx_hi_thres = 0; + rx_thres = RX_THRESH_QUARK_X1000_DFLT; + break; + case LPSS_SSP: tx_thres = LPSS_TX_LOTHRESH_DFLT; tx_hi_thres = LPSS_TX_HITHRESH_DFLT; rx_thres = LPSS_RX_THRESH_DFLT; - } else { + break; + default: tx_thres = TX_THRESH_DFLT; tx_hi_thres = 0; rx_thres = RX_THRESH_DFLT; + break; } /* Only alloc on first setup */ @@ -935,9 +1125,6 @@ static int setup(struct spi_device *spi) chip->enable_dma = drv_data->master_info->enable_dma; } - chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | - (SSCR1_TxTresh(tx_thres) & SSCR1_TFT); - chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres); chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres) | SSITF_TxHiThresh(tx_hi_thres); @@ -956,15 +1143,24 @@ static int setup(struct spi_device *spi) } } - clk_div = ssp_get_clk_div(drv_data, spi->max_speed_hz); + clk_div = pxa2xx_ssp_get_clk_div(drv_data, chip, spi->max_speed_hz); chip->speed_hz = spi->max_speed_hz; - chip->cr0 = clk_div - | SSCR0_Motorola - | SSCR0_DataSize(spi->bits_per_word > 16 ? - spi->bits_per_word - 16 : spi->bits_per_word) - | SSCR0_SSE - | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0); + chip->cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, + spi->bits_per_word); + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + chip->threshold = (QUARK_X1000_SSCR1_RxTresh(rx_thres) + & QUARK_X1000_SSCR1_RFT) + | (QUARK_X1000_SSCR1_TxTresh(tx_thres) + & QUARK_X1000_SSCR1_TFT); + break; + default: + chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | + (SSCR1_TxTresh(tx_thres) & SSCR1_TFT); + break; + } + chip->cr1 &= ~(SSCR1_SPO | SSCR1_SPH); chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); @@ -993,7 +1189,8 @@ static int setup(struct spi_device *spi) chip->read = u16_reader; chip->write = u16_writer; } else if (spi->bits_per_word <= 32) { - chip->cr0 |= SSCR0_EDSS; + if (!is_quark_x1000_ssp(drv_data)) + chip->cr0 |= SSCR0_EDSS; chip->n_bytes = 4; chip->read = u32_reader; chip->write = u32_writer; @@ -1144,7 +1341,15 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; if (pxa25x_ssp_comp(drv_data)) { - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + break; + default: + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + break; + } + drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; @@ -1182,16 +1387,35 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) /* Load default SSP configuration */ write_SSCR0(0, drv_data->ioaddr); - write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | - SSCR1_TxTresh(TX_THRESH_DFLT), - drv_data->ioaddr); - write_SSCR0(SSCR0_SCR(2) - | SSCR0_Motorola - | SSCR0_DataSize(8), - drv_data->ioaddr); + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + write_SSCR1(QUARK_X1000_SSCR1_RxTresh( + RX_THRESH_QUARK_X1000_DFLT) | + QUARK_X1000_SSCR1_TxTresh( + TX_THRESH_QUARK_X1000_DFLT), + drv_data->ioaddr); + + /* using the Motorola SPI protocol and use 8 bit frame */ + write_SSCR0(QUARK_X1000_SSCR0_Motorola + | QUARK_X1000_SSCR0_DataSize(8), + drv_data->ioaddr); + break; + default: + write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | + SSCR1_TxTresh(TX_THRESH_DFLT), + drv_data->ioaddr); + write_SSCR0(SSCR0_SCR(2) + | SSCR0_Motorola + | SSCR0_DataSize(8), + drv_data->ioaddr); + break; + } + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, drv_data->ioaddr); - write_SSPSP(0, drv_data->ioaddr); + + if (!is_quark_x1000_ssp(drv_data)) + write_SSPSP(0, drv_data->ioaddr); lpss_ssp_setup(drv_data); |