if (!use_dma) { /* * Setup FIFO interrupt trigger level * Here we choose 3/4 of the full fifo depth, as it's * the hardcoded value used in old generation of Allwinner * SPI controller. (See spi-sun4i.c)
*/
trig_level = sspi->cfg->fifo_depth / 4 * 3;
} else { /* * Setup FIFO DMA request trigger level * We choose 1/2 of the full fifo depth, that value will * be used as DMA burst length.
*/
trig_level = sspi->cfg->fifo_depth / 2;
if (tfr->tx_buf)
reg |= SUN6I_FIFO_CTL_TF_DRQ_EN; if (tfr->rx_buf)
reg |= SUN6I_FIFO_CTL_RF_DRQ_EN;
}
/* * If it's a TX only transfer, we don't want to fill the RX * FIFO with bogus data
*/ if (sspi->rx_buf) {
reg &= ~SUN6I_TFR_CTL_DHB;
rx_len = tfr->len;
} else {
reg |= SUN6I_TFR_CTL_DHB;
}
/* We want to control the chip select manually */
reg |= SUN6I_TFR_CTL_CS_MANUAL;
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
if (sspi->cfg->has_clk_ctl) { unsignedint mclk_rate = clk_get_rate(sspi->mclk);
/* Ensure that we have a parent clock fast enough */ if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
/* * Setup clock divider. * * We have two choices there. Either we can use the clock * divide rate 1, which is calculated thanks to this formula: * SPI_CLK = MOD_CLK / (2 ^ cdr) * Or we can use CDR2, which is calculated with the formula: * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) * Wether we use the former or the latter is set through the * DRS bit. * * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1.
*/
div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
} else {
div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
reg = SUN6I_CLK_CTL_CDR1(div);
tfr->effective_speed_hz = mclk_rate / (1 << div);
}
/* Finally enable the bus - doing so before might raise SCK to HIGH */
reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
reg |= SUN6I_GBL_CTL_BUS_ENABLE;
sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg);
/* Setup the transfer now... */ if (sspi->tx_buf) {
tx_len = tfr->len;
nbits = tfr->tx_nbits;
} elseif (tfr->rx_buf) {
nbits = tfr->rx_nbits;
}
switch (nbits) { case SPI_NBITS_DUAL:
reg = SUN6I_BURST_CTL_CNT_DRM; break; case SPI_NBITS_QUAD:
reg = SUN6I_BURST_CTL_CNT_QUAD_EN; break; case SPI_NBITS_SINGLE: default:
reg = FIELD_PREP(SUN6I_BURST_CTL_CNT_STC_MASK, tx_len);
}
if (!use_dma) {
sun6i_spi_drain_fifo(sspi);
} else { if (time_left && rx_len) { /* * Even though RX on the peripheral side has finished * RX DMA might still be in flight
*/
time_left = wait_for_completion_timeout(&sspi->dma_rx_done,
time_left); if (!time_left)
dev_warn(&host->dev, "RX DMA timeout\n");
}
}
end = jiffies; if (!time_left) {
dev_warn(&host->dev, "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
jiffies_to_msecs(end - start), tx_time);
ret = -ETIMEDOUT;
}
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
if (ret && use_dma) {
dmaengine_terminate_sync(host->dma_rx);
dmaengine_terminate_sync(host->dma_tx);
}
/* * If the number of spi words to transfer is less or equal than * the fifo length we can just fill the fifo and wait for a single * irq, so don't bother setting up dma
*/ return xfer->len > sspi->cfg->fifo_depth;
}
staticint sun6i_spi_probe(struct platform_device *pdev)
{ struct spi_controller *host; struct sun6i_spi *sspi; struct resource *mem; int ret = 0, irq;
host = spi_alloc_host(&pdev->dev, sizeof(struct sun6i_spi)); if (!host) {
dev_err(&pdev->dev, "Unable to allocate SPI Host\n"); return -ENOMEM;
}
sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) {
dev_err(&pdev->dev, "Couldn't get reset controller\n");
ret = PTR_ERR(sspi->rstc); goto err_free_host;
}
host->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR(host->dma_tx)) { /* Check tx to see if we need defer probing driver */ if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; goto err_free_host;
}
dev_warn(&pdev->dev, "Failed to request TX DMA channel\n");
host->dma_tx = NULL;
}
host->dma_rx = dma_request_chan(&pdev->dev, "rx"); if (IS_ERR(host->dma_rx)) { if (PTR_ERR(host->dma_rx) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; goto err_free_dma_tx;
}
dev_warn(&pdev->dev, "Failed to request RX DMA channel\n");
host->dma_rx = NULL;
}
/* * This wake-up/shutdown pattern is to be able to have the * device woken up, even if runtime_pm is disabled
*/
ret = sun6i_spi_runtime_resume(&pdev->dev); if (ret) {
dev_err(&pdev->dev, "Couldn't resume the device\n"); goto err_free_dma_rx;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.18Bemerkung:
(vorverarbeitet am 2026-06-07)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.