diff options
Diffstat (limited to 'drivers/spi/spi-geni-qcom.c')
-rw-r--r-- | drivers/spi/spi-geni-qcom.c | 194 |
1 files changed, 99 insertions, 95 deletions
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 80cea5cd3612..25810a7eef10 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -290,6 +290,7 @@ static int spi_geni_init(struct spi_geni_master *mas) { struct geni_se *se = &mas->se; unsigned int proto, major, minor, ver; + u32 spi_tx_cfg; pm_runtime_get_sync(mas->dev); @@ -308,7 +309,7 @@ static int spi_geni_init(struct spi_geni_master *mas) * Hardware programming guide suggests to configure * RX FIFO RFR level to fifo_depth-2. */ - geni_se_init(se, mas->tx_fifo_depth / 2, mas->tx_fifo_depth - 2); + geni_se_init(se, mas->tx_fifo_depth - 3, mas->tx_fifo_depth - 2); /* Transmit an entire FIFO worth of data per IRQ */ mas->tx_wm = 1; ver = geni_se_get_qup_hw_version(se); @@ -322,16 +323,103 @@ static int spi_geni_init(struct spi_geni_master *mas) geni_se_select_mode(se, GENI_SE_FIFO); + /* We always control CS manually */ + spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); + spi_tx_cfg &= ~CS_TOGGLE; + writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); + pm_runtime_put(mas->dev); return 0; } +static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) +{ + /* + * Calculate how many bytes we'll put in each FIFO word. If the + * transfer words don't pack cleanly into a FIFO word we'll just put + * one transfer word in each FIFO word. If they do pack we'll pack 'em. + */ + if (mas->fifo_width_bits % mas->cur_bits_per_word) + return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word, + BITS_PER_BYTE)); + + return mas->fifo_width_bits / BITS_PER_BYTE; +} + +static bool geni_spi_handle_tx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + unsigned int max_bytes; + const u8 *tx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word; + if (mas->tx_rem_bytes < max_bytes) + max_bytes = mas->tx_rem_bytes; + + tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes; + while (i < max_bytes) { + unsigned int j; + unsigned int bytes_to_write; + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + + bytes_to_write = min(bytes_per_fifo_word, max_bytes - i); + for (j = 0; j < bytes_to_write; j++) + fifo_byte[j] = tx_buf[i++]; + iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1); + } + mas->tx_rem_bytes -= max_bytes; + if (!mas->tx_rem_bytes) { + writel(0, se->base + SE_GENI_TX_WATERMARK_REG); + return false; + } + return true; +} + +static void geni_spi_handle_rx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + u32 rx_fifo_status; + unsigned int rx_bytes; + unsigned int rx_last_byte_valid; + u8 *rx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); + rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word; + if (rx_fifo_status & RX_LAST) { + rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK; + rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT; + if (rx_last_byte_valid && rx_last_byte_valid < 4) + rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid; + } + if (mas->rx_rem_bytes < rx_bytes) + rx_bytes = mas->rx_rem_bytes; + + rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes; + while (i < rx_bytes) { + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + unsigned int bytes_to_read; + unsigned int j; + + bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i); + ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1); + for (j = 0; j < bytes_to_read; j++) + rx_buf[i++] = fifo_byte[j]; + } + mas->rx_rem_bytes -= rx_bytes; +} + static void setup_fifo_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas, u16 mode, struct spi_master *spi) { u32 m_cmd = 0; - u32 spi_tx_cfg, len; + u32 len; struct geni_se *se = &mas->se; int ret; @@ -350,7 +438,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, spin_lock_irq(&mas->lock); spin_unlock_irq(&mas->lock); - spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); if (xfer->bits_per_word != mas->cur_bits_per_word) { spi_setup_word_len(mas, mode, xfer->bits_per_word); mas->cur_bits_per_word = xfer->bits_per_word; @@ -364,8 +451,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, mas->tx_rem_bytes = 0; mas->rx_rem_bytes = 0; - spi_tx_cfg &= ~CS_TOGGLE; - if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word; else @@ -384,7 +469,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, writel(len, se->base + SE_SPI_RX_TRANS_LEN); mas->rx_rem_bytes = xfer->len; } - writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); /* * Lock around right before we start the transfer since our @@ -398,8 +482,10 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, * setting up GENI SE engine, as driver starts data transfer * for the watermark interrupt. */ - if (m_cmd & SPI_TX_ONLY) - writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); + if (m_cmd & SPI_TX_ONLY) { + if (geni_spi_handle_tx(mas)) + writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); + } spin_unlock_irq(&mas->lock); } @@ -417,85 +503,6 @@ static int spi_geni_transfer_one(struct spi_master *spi, return 1; } -static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) -{ - /* - * Calculate how many bytes we'll put in each FIFO word. If the - * transfer words don't pack cleanly into a FIFO word we'll just put - * one transfer word in each FIFO word. If they do pack we'll pack 'em. - */ - if (mas->fifo_width_bits % mas->cur_bits_per_word) - return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word, - BITS_PER_BYTE)); - - return mas->fifo_width_bits / BITS_PER_BYTE; -} - -static void geni_spi_handle_tx(struct spi_geni_master *mas) -{ - struct geni_se *se = &mas->se; - unsigned int max_bytes; - const u8 *tx_buf; - unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); - unsigned int i = 0; - - max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word; - if (mas->tx_rem_bytes < max_bytes) - max_bytes = mas->tx_rem_bytes; - - tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes; - while (i < max_bytes) { - unsigned int j; - unsigned int bytes_to_write; - u32 fifo_word = 0; - u8 *fifo_byte = (u8 *)&fifo_word; - - bytes_to_write = min(bytes_per_fifo_word, max_bytes - i); - for (j = 0; j < bytes_to_write; j++) - fifo_byte[j] = tx_buf[i++]; - iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1); - } - mas->tx_rem_bytes -= max_bytes; - if (!mas->tx_rem_bytes) - writel(0, se->base + SE_GENI_TX_WATERMARK_REG); -} - -static void geni_spi_handle_rx(struct spi_geni_master *mas) -{ - struct geni_se *se = &mas->se; - u32 rx_fifo_status; - unsigned int rx_bytes; - unsigned int rx_last_byte_valid; - u8 *rx_buf; - unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); - unsigned int i = 0; - - rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); - rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word; - if (rx_fifo_status & RX_LAST) { - rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK; - rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT; - if (rx_last_byte_valid && rx_last_byte_valid < 4) - rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid; - } - if (mas->rx_rem_bytes < rx_bytes) - rx_bytes = mas->rx_rem_bytes; - - rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes; - while (i < rx_bytes) { - u32 fifo_word = 0; - u8 *fifo_byte = (u8 *)&fifo_word; - unsigned int bytes_to_read; - unsigned int j; - - bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i); - ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1); - for (j = 0; j < bytes_to_read; j++) - rx_buf[i++] = fifo_byte[j]; - } - mas->rx_rem_bytes -= rx_bytes; -} - static irqreturn_t geni_spi_isr(int irq, void *data) { struct spi_master *spi = data; @@ -613,11 +620,9 @@ static int spi_geni_probe(struct platform_device *pdev) return PTR_ERR(mas->se.opp_table); /* OPP table is optional */ ret = dev_pm_opp_of_add_table(&pdev->dev); - if (!ret) { - mas->se.has_opp_table = true; - } else if (ret != -ENODEV) { + if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - return ret; + goto put_clkname; } spi->bus_num = -1; @@ -669,8 +674,8 @@ spi_geni_probe_free_irq: spi_geni_probe_runtime_disable: pm_runtime_disable(dev); spi_master_put(spi); - if (mas->se.has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); +put_clkname: dev_pm_opp_put_clkname(mas->se.opp_table); return ret; } @@ -685,8 +690,7 @@ static int spi_geni_remove(struct platform_device *pdev) free_irq(mas->irq, spi); pm_runtime_disable(&pdev->dev); - if (mas->se.has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); dev_pm_opp_put_clkname(mas->se.opp_table); return 0; } |