#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/gpio.h>
+#include <linux/dma-mapping.h>
/*
* The Meson SPICC controller could support DMA based transfers, but is not
* to have a CS go down over the full transfer
*/
-#define SPICC_MAX_BURST 128
-
/* Register Map */
#define SPICC_RXDATA 0x00
#define SPICC_DRCTL_LOWLEVEL 2
#define SPICC_CS_MASK GENMASK(13, 12)
#define SPICC_DATARATE_MASK GENMASK(18, 16)
-#define SPICC_DATARATE_DIV4 0
-#define SPICC_DATARATE_DIV8 1
-#define SPICC_DATARATE_DIV16 2
-#define SPICC_DATARATE_DIV32 3
+#define SPICC_FIX_FACTOR_MULT 1
+#define SPICC_FIX_FACTOR_DIV 4
#define SPICC_BITLENGTH_MASK GENMASK(24, 19)
#define SPICC_BURSTLENGTH_MASK GENMASK(31, 25)
#define SPICC_DMAREG 0x10
#define SPICC_DMA_ENABLE BIT(0)
+/* When txfifo_count<threshold, request a read(dma->txfifo) burst */
#define SPICC_TXFIFO_THRESHOLD_MASK GENMASK(5, 1)
+#define SPICC_TXFIFO_THRESHOLD_DEFAULT 4
+/* When rxfifo count>threshold, request a write(rxfifo->dma) burst */
#define SPICC_RXFIFO_THRESHOLD_MASK GENMASK(10, 6)
#define SPICC_READ_BURST_MASK GENMASK(14, 11)
#define SPICC_WRITE_BURST_MASK GENMASK(18, 15)
#define SPICC_TXCNT_MASK GENMASK(4, 0) /* TX FIFO Counter */
#define SPICC_RXCNT_MASK GENMASK(9, 5) /* RX FIFO Counter */
#define SPICC_SMSTATUS_MASK GENMASK(12, 10) /* State Machine Status */
-#define SPICC_LBC_RO BIT(13) /* Loop Back Control Read-Only */
-#define SPICC_LBC_W1 BIT(14) /* Loop Back Control Write-Only */
-#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */
-#define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */
-#define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */
-#define SPICC_DLYCTL_W1_MASK GENMASK(21, 16) /* Delay Control Write-Only */
-#define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */
-#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */
+#define SPICC_LBC BIT(14) /* Loop Back Control */
+#define SPICC_SWAP BIT(15) /* RX FIFO Data Swap */
+#define SPICC_MO_DELAY_MASK GENMASK(17, 16) /* Master Output Delay */
+#define SPICC_MO_NO_DELAY 0
+#define SPICC_MO_DELAY_1_CYCLE 1
+#define SPICC_MO_DELAY_2_CYCLE 2
+#define SPICC_MO_DELAY_3_CYCLE 3
+#define SPICC_MI_DELAY_MASK GENMASK(19, 18) /* Master Input Delay */
+#define SPICC_MI_NO_DELAY 0
+#define SPICC_MI_DELAY_1_CYCLE 1
+#define SPICC_MI_DELAY_2_CYCLE 2
+#define SPICC_MI_DELAY_3_CYCLE 3
+#define SPICC_MI_CAP_DELAY_MASK GENMASK(21, 20) /* Master Capture Delay */
+#define SPICC_CAP_AHEAD_2_CYCLE 0
+#define SPICC_CAP_AHEAD_1_CYCLE 1
+#define SPICC_CAP_NO_DELAY 2
+#define SPICC_CAP_DELAY_1_CYCLE 3
+#define SPICC_FIFORST_MASK GENMASK(23, 22) /* FIFO Softreset */
#define SPICC_DRADDR 0x20 /* Read Address of DMA */
#define SPICC_DWADDR 0x24 /* Write Address of DMA */
-#define SPICC_ENH_CTL0 0x38 /* Enhanced Feature */
-#define SPICC_ENH_CLK_CS_DELAY_MASK GENMASK(15, 0)
+#define SPICC_ENH_CTL0 0x38 /* Enhanced Feature 0 */
+#define SPICC_ENH_CS_PRE_DELAY_MASK GENMASK(15, 0)
#define SPICC_ENH_DATARATE_MASK GENMASK(23, 16)
+#define SPICC_ENH_FIX_FACTOR_MULT 1
+#define SPICC_ENH_FIX_FACTOR_DIV 2
#define SPICC_ENH_DATARATE_EN BIT(24)
#define SPICC_ENH_MOSI_OEN BIT(25)
#define SPICC_ENH_CLK_OEN BIT(26)
#define SPICC_ENH_CS_OEN BIT(27)
-#define SPICC_ENH_CLK_CS_DELAY_EN BIT(28)
+#define SPICC_ENH_CS_PRE_DELAY_EN BIT(28)
#define SPICC_ENH_MAIN_CLK_AO BIT(29)
+#define SPICC_ENH_CTL1 0x3c /* Enhanced Feature 1 */
+#define SPICC_ENH_MI_CAP_DELAY_EN BIT(0)
+#define SPICC_ENH_MI_CAP_DELAY_MASK GENMASK(9, 1)
+#define SPICC_ENH_SI_CAP_DELAY_EN BIT(14) /* slave mode */
+#define SPICC_ENH_DELAY_EN BIT(15)
+#define SPICC_ENH_SI_DELAY_EN BIT(16) /* slave mode */
+#define SPICC_ENH_SI_DELAY_MASK GENMASK(19, 17) /* slave mode */
+#define SPICC_ENH_MI_DELAY_EN BIT(20)
+#define SPICC_ENH_MI_DELAY_MASK GENMASK(23, 21)
+#define SPICC_ENH_MO_DELAY_EN BIT(24)
+#define SPICC_ENH_MO_DELAY_MASK GENMASK(27, 25)
+#define SPICC_ENH_MO_OEN_DELAY_EN BIT(28)
+#define SPICC_ENH_MO_OEN_DELAY_MASK GENMASK(31, 29)
+
+#define SPICC_ENH_CTL2 0x40 /* Enhanced Feature */
+#define SPICC_ENH_TI_DELAY_MASK GENMASK(14, 0)
+#define SPICC_ENH_TI_DELAY_EN BIT(15)
+#define SPICC_ENH_TT_DELAY_MASK GENMASK(30, 16)
+#define SPICC_ENH_TT_DELAY_EN BIT(31)
+
#define writel_bits_relaxed(mask, val, addr) \
writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
-
-#define SPICC_BURST_MAX 16
-#define SPICC_FIFO_HALF 10
+#define readl_bits_relaxed(mask, addr) \
+ ((readl_relaxed(addr) & (mask)) >> (ffs(mask) - 1))
+#define mask_width(mask) (fls(mask) + 1 - ffs(mask))
/* only for local test, must remove it before upstream pushing */
#define MESON_SPICC_TEST_ENTRY
+/*
+ * cs_pre_delay: delay time from SS falling edge to the first CLK edge.
+ * mo_delay: MOSI output delay time.
+ * mi_delay: MISO input delay time.
+ * mi_capture_delay: MISO capture delay time.
+ * tt_delay: trailing time from the last CLK edge to the SS rising edge.
+ * ti_delay: idling time between transfers.
+ */
+struct meson_spicc_controller_data {
+ unsigned int cs_pre_delay;
+ unsigned int tt_delay;
+ unsigned int ti_delay;
+};
+
struct meson_spicc_data {
+ unsigned int min_speed_hz;
unsigned int max_speed_hz;
+ unsigned int fifo_size;
+ bool dma_burst_triggered_by_ssctl;
bool has_oen;
bool has_enhance_clk_div;
+ bool has_cs_pre_delay;
+ bool has_enhance_io_delay;
+ bool has_comp_clk;
+ bool is_div_parent_comp_clk;
+ bool has_enhance_tt_ti_delay;
};
struct meson_spicc_device {
struct platform_device *pdev;
void __iomem *base;
struct clk *core;
+ struct clk *comp;
struct clk *clk;
struct spi_message *message;
struct spi_transfer *xfer;
unsigned long rx_remain;
unsigned long rxb_remain;
unsigned long xfer_remain;
- bool is_burst_end;
- bool is_last_burst;
+ bool using_dma;
#ifdef MESON_SPICC_TEST_ENTRY
- struct class cls;
- u8 test_data;
-#endif /* end MESON_SPICC_TEST_ENTRY */
+ struct class cls;
+ u8 test_data;
+#endif
};
static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
}
+static void meson_spicc_setup_controller_data(
+ struct meson_spicc_device *spicc,
+ struct spi_device *spi)
+{
+ struct meson_spicc_controller_data *cd;
+ u32 conf;
+
+ cd = (struct meson_spicc_controller_data *)spi->controller_data;
+ if (!cd)
+ return;
+
+ /* setup cs preload delay for ss chip-select */
+ if (spicc->data->has_cs_pre_delay && !gpio_is_valid(spi->cs_gpio)) {
+ conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0);
+ conf &= ~(SPICC_ENH_CS_PRE_DELAY_MASK |
+ SPICC_ENH_CS_PRE_DELAY_EN);
+ if (cd->cs_pre_delay) {
+ conf |= SPICC_ENH_CS_PRE_DELAY_EN;
+ conf |= FIELD_PREP(SPICC_ENH_CS_PRE_DELAY_MASK,
+ cd->cs_pre_delay);
+ }
+ writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
+ }
+
+ if (spicc->data->has_enhance_tt_ti_delay) {
+ conf = 0;
+ if (cd->tt_delay) {
+ conf |= SPICC_ENH_TT_DELAY_EN;
+ conf |= FIELD_PREP(SPICC_ENH_TT_DELAY_MASK,
+ cd->tt_delay);
+ }
+ if (cd->ti_delay) {
+ conf |= SPICC_ENH_TI_DELAY_EN;
+ conf |= FIELD_PREP(SPICC_ENH_TI_DELAY_MASK,
+ cd->ti_delay);
+ }
+ writel_relaxed(conf, spicc->base + SPICC_ENH_CTL2);
+ }
+}
+
+static void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc)
+{
+ u32 div, hz;
+ u32 mi_delay, cap_delay;
+ u32 conf;
+
+ if (spicc->data->has_enhance_clk_div) {
+ div = readl_bits_relaxed(SPICC_ENH_DATARATE_MASK,
+ spicc->base + SPICC_ENH_CTL0);
+ div++;
+ div <<= 1;
+ } else {
+ div = readl_bits_relaxed(SPICC_DATARATE_MASK,
+ spicc->base + SPICC_CONREG);
+ div += 2;
+ div = 1 << div;
+ }
+
+ mi_delay = SPICC_MI_NO_DELAY;
+ cap_delay = SPICC_CAP_AHEAD_2_CYCLE;
+ hz = clk_get_rate(spicc->clk);
+
+ if (hz >= 100000000)
+ cap_delay = SPICC_CAP_DELAY_1_CYCLE;
+ else if (hz >= 80000000)
+ cap_delay = SPICC_CAP_NO_DELAY;
+ else if (hz >= 40000000)
+ cap_delay = SPICC_CAP_AHEAD_1_CYCLE;
+ else if (div >= 16)
+ mi_delay = SPICC_MI_DELAY_3_CYCLE;
+ else if (div >= 8)
+ mi_delay = SPICC_MI_DELAY_2_CYCLE;
+ else if (div >= 6)
+ mi_delay = SPICC_MI_DELAY_1_CYCLE;
+
+ conf = readl_relaxed(spicc->base + SPICC_TESTREG);
+ conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK
+ | SPICC_MI_CAP_DELAY_MASK);
+ conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay);
+ conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay);
+ writel_relaxed(conf, spicc->base + SPICC_TESTREG);
+}
+
+static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
+ struct spi_transfer *t)
+{
+ struct device *dev = spicc->master->dev.parent;
+
+ t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, t->tx_dma)) {
+ dev_err(dev, "tx_dma map failed\n");
+ return -ENOMEM;
+ }
+
+ t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, t->rx_dma)) {
+ dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
+ dev_err(dev, "rx_dma map failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
+ struct spi_transfer *t)
+{
+ struct device *dev = spicc->master->dev.parent;
+
+ dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
+ dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
+}
+
+static void meson_spicc_setup_dma_burst(struct meson_spicc_device *spicc)
+{
+ unsigned int burst_len, thres;
+ unsigned int fifo_size = spicc->data->fifo_size;
+
+ burst_len = min_t(unsigned int,
+ spicc->xfer_remain / spicc->bytes_per_word,
+ fifo_size << 3);
+
+ thres = burst_len % fifo_size;
+ if (thres == 0)
+ thres = fifo_size;
+ else if (burst_len != thres) {
+ burst_len -= thres;
+ thres = fifo_size;
+ }
+
+ /* Setup Xfer variables */
+ spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
+
+ /* Setup burst length */
+ writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
+ FIELD_PREP(SPICC_BURSTLENGTH_MASK,
+ burst_len - 1),
+ spicc->base + SPICC_CONREG);
+
+ writel_relaxed(SPICC_DMA_ENABLE
+ | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK,
+ SPICC_TXFIFO_THRESHOLD_DEFAULT)
+ | FIELD_PREP(SPICC_READ_BURST_MASK,
+ fifo_size - SPICC_TXFIFO_THRESHOLD_DEFAULT)
+ | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, thres - 1)
+ | FIELD_PREP(SPICC_WRITE_BURST_MASK, thres - 1),
+ spicc->base + SPICC_DMAREG);
+}
+
static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
{
return !!FIELD_GET(SPICC_TF,
static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc)
{
- return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN,
+ return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF,
readl_relaxed(spicc->base + SPICC_STATREG));
}
+static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
+{
+ u32 data;
+
+ if (spicc->data->has_oen)
+ writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
+ SPICC_ENH_MAIN_CLK_AO,
+ spicc->base + SPICC_ENH_CTL0);
+
+ writel_bits_relaxed(SPICC_FIFORST_MASK,
+ FIELD_PREP(SPICC_FIFORST_MASK, 3),
+ spicc->base + SPICC_TESTREG);
+
+ while (meson_spicc_rxready(spicc))
+ data = readl_relaxed(spicc->base + SPICC_RXDATA);
+
+ if (spicc->data->has_oen)
+ writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
+ spicc->base + SPICC_ENH_CTL0);
+}
+
static inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc)
{
unsigned int bytes = spicc->bytes_per_word;
spicc->base + SPICC_TXDATA);
}
-static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc,
- u32 irq_ctrl)
+static void meson_spicc_setup_pio_burst(struct meson_spicc_device *spicc)
{
- if (spicc->rx_remain > SPICC_FIFO_HALF)
- irq_ctrl |= SPICC_RH_EN;
- else
- irq_ctrl |= SPICC_RR_EN;
+ unsigned int burst_len;
- return irq_ctrl;
-}
+ burst_len = min_t(unsigned int,
+ spicc->xfer_remain / spicc->bytes_per_word,
+ spicc->data->fifo_size);
-static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
- unsigned int burst_len)
-{
/* Setup Xfer variables */
spicc->tx_remain = burst_len;
spicc->rx_remain = burst_len;
spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
- spicc->is_burst_end = false;
- if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain)
- spicc->is_last_burst = true;
- else
- spicc->is_last_burst = false;
/* Setup burst length */
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
FIELD_PREP(SPICC_BURSTLENGTH_MASK,
- burst_len),
+ burst_len - 1),
spicc->base + SPICC_CONREG);
/* Fill TX FIFO */
static irqreturn_t meson_spicc_irq(int irq, void *data)
{
struct meson_spicc_device *spicc = (void *) data;
- u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG);
- u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
-
- ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN);
-
- /* Empty RX FIFO */
- meson_spicc_rx(spicc);
-
- /* Enable TC interrupt since we transferred everything */
- if (!spicc->tx_remain && !spicc->rx_remain) {
- spicc->is_burst_end = true;
-
- /* Enable TC interrupt */
- ctrl |= SPICC_TC_EN;
-
- /* Reload IRQ status */
- stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
- }
-
- /* Check transfer complete */
- if ((stat & SPICC_TC) && spicc->is_burst_end) {
- unsigned int burst_len;
-
- /* Clear TC bit */
- writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG);
-
- /* Disable TC interrupt */
- ctrl &= ~SPICC_TC_EN;
-
- if (spicc->is_last_burst) {
- /* Disable all IRQs */
- writel(0, spicc->base + SPICC_INTREG);
-
- spi_finalize_current_transfer(spicc->master);
- return IRQ_HANDLED;
+ writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
+
+ if (!spicc->using_dma)
+ /* Empty RX FIFO */
+ meson_spicc_rx(spicc);
+
+ if (!spicc->xfer_remain) {
+ /* Disable all IRQs */
+ writel(0, spicc->base + SPICC_INTREG);
+ if (spicc->using_dma) {
+ spicc->using_dma = 0;
+ writel_bits_relaxed(SPICC_DMA_ENABLE, 0,
+ spicc->base + SPICC_DMAREG);
+ if (!spicc->message->is_dma_mapped)
+ meson_spicc_dma_unmap(spicc, spicc->xfer);
}
+ spi_finalize_current_transfer(spicc->master);
- burst_len = min_t(unsigned int,
- spicc->xfer_remain / spicc->bytes_per_word,
- SPICC_BURST_MAX);
-
- /* Setup burst */
- meson_spicc_setup_burst(spicc, burst_len);
-
- /* Restart burst */
- writel_bits_relaxed(SPICC_XCH, SPICC_XCH,
- spicc->base + SPICC_CONREG);
+ return IRQ_HANDLED;
}
- /* Setup RX interrupt trigger */
- ctrl = meson_spicc_setup_rx_irq(spicc, ctrl);
+ /* Setup burst */
+ if (spicc->using_dma)
+ meson_spicc_setup_dma_burst(spicc);
+ else
+ meson_spicc_setup_pio_burst(spicc);
- /* Reconfigure interrupts */
- writel(ctrl, spicc->base + SPICC_INTREG);
+ /* Start burst */
+ writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
return IRQ_HANDLED;
}
{
u32 conf, conf_orig;
+ meson_spicc_reset_fifo(spicc);
+
/* Read original configuration */
conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
writel_relaxed(conf, spicc->base + SPICC_CONREG);
clk_set_rate(spicc->clk, xfer->speed_hz);
+ meson_spicc_auto_io_delay(spicc);
+
+ spicc->using_dma = 0;
+ if ((xfer->bits_per_word == 64)
+ && (spicc->message->is_dma_mapped
+ || !meson_spicc_dma_map(spicc, xfer))) {
+ spicc->using_dma = 1;
+ writel_relaxed(xfer->tx_dma, spicc->base + SPICC_DRADDR);
+ writel_relaxed(xfer->rx_dma, spicc->base + SPICC_DWADDR);
+ writel_relaxed(xfer->speed_hz >> 25,
+ spicc->base + SPICC_PERIODREG);
+ }
+ writel_relaxed(0, spicc->base + SPICC_DMAREG);
}
static int meson_spicc_transfer_one(struct spi_master *master,
struct spi_transfer *xfer)
{
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
- unsigned int burst_len;
- u32 irq = 0;
/* Store current transfer */
spicc->xfer = xfer;
spicc->bytes_per_word =
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
+ if (xfer->len % spicc->bytes_per_word)
+ return -EINVAL;
+
/* Setup transfer parameters */
meson_spicc_setup_xfer(spicc, xfer);
- burst_len = min_t(unsigned int,
- spicc->xfer_remain / spicc->bytes_per_word,
- SPICC_BURST_MAX);
-
- meson_spicc_setup_burst(spicc, burst_len);
-
- irq = meson_spicc_setup_rx_irq(spicc, irq);
+ /* Setup burst */
+ if (spicc->using_dma)
+ meson_spicc_setup_dma_burst(spicc);
+ else
+ meson_spicc_setup_pio_burst(spicc);
/* Start burst */
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
/* Enable interrupts */
- writel_relaxed(irq, spicc->base + SPICC_INTREG);
+ writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
return 1;
}
conf |= SPICC_ENABLE;
conf |= SPICC_MODE_MASTER;
+ if (spicc->data->dma_burst_triggered_by_ssctl)
+ conf |= SPICC_SSCTL;
+
/* SMC = 0 */
/* Setup transfer mode */
/* Select CS */
conf |= FIELD_PREP(SPICC_CS_MASK, spi->chip_select);
- /* Default Clock rate core/4 */
-
/* Default 8bit word */
conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1);
/* Setup no wait cycles by default */
writel_relaxed(0, spicc->base + SPICC_PERIODREG);
- writel_bits_relaxed(BIT(24), BIT(24), spicc->base + SPICC_TESTREG);
-
meson_spicc_oen_enable(spicc);
+ conf = readl_relaxed(spicc->base + SPICC_TESTREG);
+ conf &= ~SPICC_LBC;
+ if (spi->mode & SPI_LOOP)
+ conf |= SPICC_LBC;
+ writel_relaxed(conf, spicc->base + SPICC_TESTREG);
+
+ /* setup cs-preload, input/output/capture delay */
+ meson_spicc_setup_controller_data(spicc, spi);
+
return 0;
}
{
int ret = 0;
- if (!spi->controller_state)
- spi->controller_state = spi_master_get_devdata(spi->master);
- else if (gpio_is_valid(spi->cs_gpio))
- goto out_gpio;
- else if (spi->cs_gpio == -ENOENT)
+ if (!gpio_is_valid(spi->cs_gpio))
return 0;
- if (gpio_is_valid(spi->cs_gpio)) {
+ if (!spi->controller_state) {
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
if (ret) {
dev_err(&spi->dev, "failed to request cs gpio\n");
return ret;
}
+ spi->controller_state = spi_master_get_devdata(spi->master);
}
-out_gpio:
ret = gpio_direction_output(spi->cs_gpio,
!(spi->mode & SPI_CS_HIGH));
}
#ifdef MESON_SPICC_TEST_ENTRY
-static ssize_t test_store(
+static struct meson_spicc_controller_data cd = {0};
+static ssize_t store_setting(
struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
- struct meson_spicc_device *spicc;
- struct device *dev;
- unsigned int cs, speed, mode, bits_per_word, num;
+ if (!strcmp(attr->attr.name, "controller_data"))
+ sscanf(buf, "%d%d%d", &cd.cs_pre_delay,
+ &cd.tt_delay, &cd.ti_delay);
+
+ return count;
+}
+
+static ssize_t show_setting(
+ struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ if (!strcmp(attr->attr.name, "controller_data")) {
+ pr_info("cs_pre_delay = %d\n", cd.cs_pre_delay);
+ pr_info("tt_delay = %d\n", cd.tt_delay);
+ pr_info("ti_delay = %d\n", cd.ti_delay);
+ }
+
+ return 0;
+}
+
+#define TEST_PARAM_NUM 5
+static ssize_t store_test(
+ struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct meson_spicc_device *spicc = container_of(
+ class, struct meson_spicc_device, cls);
+ struct device *dev = spicc->master->dev.parent;
+ unsigned int cs_gpio, speed, mode, bits_per_word, num;
u8 *tx_buf, *rx_buf;
unsigned long value;
char *kstr, *str_temp, *token;
- int i;
+ int i, ret;
struct spi_transfer t;
struct spi_message m;
- spicc = container_of(class, struct meson_spicc_device, cls);
- dev = spicc->master->dev.parent;
- if (sscanf(buf, "%d%d%d%d%d", &cs, &speed,
- &mode, &bits_per_word, &num) != 5) {
- dev_err(dev, "error test data\n");
+ if (sscanf(buf, "%d%d%x%d%d", &cs_gpio, &speed,
+ &mode, &bits_per_word, &num) != TEST_PARAM_NUM) {
+ dev_err(dev, "error format\n");
return count;
}
+
kstr = kstrdup(buf, GFP_KERNEL);
tx_buf = kzalloc(num, GFP_KERNEL | GFP_DMA);
rx_buf = kzalloc(num, GFP_KERNEL | GFP_DMA);
- if (IS_ERR_OR_NULL(kstr) ||
- IS_ERR_OR_NULL(tx_buf) ||
- IS_ERR_OR_NULL(rx_buf)) {
+ if (IS_ERR(kstr) ||
+ IS_ERR(tx_buf) ||
+ IS_ERR(rx_buf)) {
dev_err(dev, "failed to alloc tx rx buffer\n");
goto test_end;
}
str_temp = kstr;
- /* skip pass over "cs speed mode bits_per_word num" */
- for (i = 0; i < 5; i++)
+ /* skip pass over "cs_gpio speed mode bits_per_word num" */
+ for (i = 0; i < TEST_PARAM_NUM; i++)
strsep(&str_temp, ", ");
for (i = 0; i < num; i++) {
token = strsep(&str_temp, ", ");
- if (!token || kstrtoul(token, 16, &value))
+ if ((token == 0) || kstrtoul(token, 16, &value))
break;
tx_buf[i] = (u8)(value & 0xff);
}
spi_message_init(&m);
m.spi = spi_alloc_device(spicc->master);
-
- m.spi->cs_gpio = -ENOENT;
- m.spi->chip_select = 0;
- if (cs < 4)
- m.spi->chip_select = cs;
- else if (((cs-4) < spicc->master->num_chipselect)
- && spicc->master->cs_gpios)
- m.spi->cs_gpio = spicc->master->cs_gpios[cs - 4];
-
+ m.spi->cs_gpio = (cs_gpio > 0) ? cs_gpio : -ENOENT;
m.spi->max_speed_hz = speed;
- m.spi->mode = mode;
+ m.spi->mode = mode & 0xffff;
m.spi->bits_per_word = bits_per_word;
- spi_setup(m.spi);
+ m.spi->controller_data = &cd;
+ if (spi_setup(m.spi))
+ goto test_end;
memset(&t, 0, sizeof(t));
t.tx_buf = (void *)tx_buf;
t.rx_buf = (void *)rx_buf;
t.len = num;
spi_message_add_tail(&t, &m);
- i = spi_sync(m.spi, &m);
+ ret = spi_sync(m.spi, &m);
spi_dev_put(m.spi);
+ if (!ret && (mode & (SPI_LOOP | (1<<16)))) {
+ ret = 0;
+ for (i = 0; i < num; i++) {
+ if (tx_buf[i] != rx_buf[i]) {
+ ret++;
+ pr_info("[%d]: 0x%x, 0x%x\n",
+ i, tx_buf[i], rx_buf[i]);
+ }
+ }
+ dev_info(dev, "total %d, failed %d\n", num, ret);
+ }
- dev_info(dev, "read back data ok\n");
- for (i = 0; i < min_t(size_t, 32, num); i++)
- dev_info(dev, "[%d]: 0x%2x, 0x%2x\n", i, tx_buf[i], rx_buf[i]);
test_end:
+ dev_info(dev, "test end @%d\n", (u32)clk_get_rate(spicc->clk));
kfree(kstr);
kfree(tx_buf);
kfree(rx_buf);
}
static struct class_attribute spicc_class_attrs[] = {
- __ATTR(test, 0200, NULL, test_store),
+ __ATTR(test, 0200, NULL, store_test),
+ __ATTR(controller_data, 0644, show_setting, store_setting),
__ATTR_NULL
};
#endif /* end MESON_SPICC_TEST_ENTRY */
/*
+ * There are three function blocks driven by core/comp clk in spicc,
+ * x-------------------------------------------------------x
+ * | | apb bus | delay control | rate divider |
+ * x-------------------------------------------------------x
+ * |gxl/txl/txlx | core | core | core |
+ * x-------------------------------------------------------x
+ * |axg | core | comp | core |
+ * x-------------------------------------------------------x
+ * |txhd/g12 | core | comp | comp |
+ * x-------------------------------------------------------x
+ *
* The Clock Mux
* x-----------------x x------------x x------\
* |---| 0) fixed factor |---| 1) old div |----| |
* src -> 2 -> 3 -> 5 -> out
*/
-/* algorithm for div0 + div1: rate = freq / 4 / (2 ^ N) */
-static struct clk_fixed_factor meson_spicc_div0 = {
- .mult = 1,
- .div = 4,
-};
-
-static struct clk_divider meson_spicc_div1 = {
- .reg = (void *) SPICC_CONREG,
- .shift = 16,
- .width = 3,
- .flags = CLK_DIVIDER_POWER_OF_TWO,
-};
-
-/* algorithm for div2 + div3: rate = freq / 2 / (N + 1) */
-static struct clk_fixed_factor meson_spicc_div2 = {
- .mult = 1,
- .div = 2,
-};
-
-static struct clk_divider meson_spicc_div3 = {
- .reg = (void *) SPICC_ENH_CTL0,
- .shift = 16,
- .width = 8,
-};
-
-static struct clk_mux meson_spicc_sel = {
- .reg = (void *) SPICC_ENH_CTL0,
- .mask = 0x1,
- .shift = 24,
-};
-
-static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
+/*
+ * Register a group of clk, one is a clk-fixed-factor and the other
+ * is a clk-divider whose parent is the clk-fixed-factor.
+ */
+static struct clk *meson_spicc_clk_register_ff_divider(
+ struct meson_spicc_device *spicc, bool is_enhance)
{
struct device *dev = &spicc->pdev->dev;
- struct clk_fixed_factor *div0;
- struct clk_divider *div1;
- struct clk_mux *mux;
struct clk_init_data init;
struct clk *clk;
+ struct clk_fixed_factor *ff;
+ struct clk_divider *div;
const char *parent_names[1];
- const char *mux_parent_names[2];
char name[32];
+ char *which;
+
+ ff = devm_kzalloc(dev, sizeof(*ff), GFP_KERNEL);
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+ if (!ff || !div)
+ return ERR_PTR(-ENOMEM);
+
+ if (is_enhance) {
+ which = "enh";
+ ff->mult = SPICC_ENH_FIX_FACTOR_MULT;
+ ff->div = SPICC_ENH_FIX_FACTOR_DIV;
+ div->reg = spicc->base + SPICC_ENH_CTL0;
+ div->shift = ffs(SPICC_ENH_DATARATE_MASK) - 1;
+ div->width = mask_width(SPICC_ENH_DATARATE_MASK);
+ div->flags = CLK_DIVIDER_ROUND_CLOSEST;
+#ifdef CONFIG_AMLOGIC_MODIFY
+ div->flags |= CLK_DIVIDER_PROHIBIT_ZERO;
+#endif
+ } else {
+ which = "old";
+ ff->mult = SPICC_FIX_FACTOR_MULT;
+ ff->div = SPICC_FIX_FACTOR_DIV;
+ div->reg = spicc->base + SPICC_CONREG;
+ div->shift = ffs(SPICC_DATARATE_MASK) - 1;
+ div->width = mask_width(SPICC_DATARATE_MASK);
+ div->flags = CLK_DIVIDER_POWER_OF_TWO;
+ }
+
+ /* Get parent clk of the group */
+ clk = spicc->data->is_div_parent_comp_clk ? spicc->comp : spicc->core;
- div0 = &meson_spicc_div0;
- snprintf(name, sizeof(name), "%s#_div0", dev_name(dev));
+ /* Register clk-fixed-factor */
+ parent_names[0] = __clk_get_name(clk);
+ snprintf(name, sizeof(name), "%s#_%sff", dev_name(dev), which);
init.name = name;
init.ops = &clk_fixed_factor_ops;
- init.flags = 0;
- parent_names[0] = __clk_get_name(spicc->core);
+ init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
init.num_parents = 1;
+ ff->hw.init = &init;
+ clk = devm_clk_register(dev, &ff->hw);
+ if (IS_ERR(clk))
+ return clk;
- div0->hw.init = &init;
-
- clk = devm_clk_register(dev, &div0->hw);
- if (WARN_ON(IS_ERR(clk)))
- return PTR_ERR(clk);
-
- div1 = &meson_spicc_div1;
- snprintf(name, sizeof(name), "%s#_div1", dev_name(dev));
+ /* Register clk-divider, which parent the clk-fixed-factor */
+ parent_names[0] = __clk_get_name(clk);
+ snprintf(name, sizeof(name), "%s#_%sdiv", dev_name(dev), which);
init.name = name;
init.ops = &clk_divider_ops;
init.flags = CLK_SET_RATE_PARENT;
- parent_names[0] = __clk_get_name(clk);
init.parent_names = parent_names;
init.num_parents = 1;
+ div->hw.init = &init;
+ clk = devm_clk_register(dev, &div->hw);
- div1->reg = spicc->base + (u64) div1->reg;
- div1->hw.init = &init;
-
- clk = devm_clk_register(dev, &div1->hw);
- if (WARN_ON(IS_ERR(clk)))
- return PTR_ERR(clk);
+ return clk;
+}
- if (spicc->data->has_enhance_clk_div == false) {
- spicc->clk = clk;
- return 0;
- }
+static struct clk *meson_spicc_clk_register_mux(
+ struct meson_spicc_device *spicc,
+ const char * const *parent_names)
+{
+ struct device *dev = &spicc->pdev->dev;
+ struct clk_init_data init;
+ struct clk_mux *mux;
+ struct clk *clk;
+ char name[32];
- mux_parent_names[0] = __clk_get_name(clk);
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
- div0 = &meson_spicc_div2;
- snprintf(name, sizeof(name), "%s#_div2", dev_name(dev));
+ snprintf(name, sizeof(name), "%s#_sel", dev_name(dev));
init.name = name;
- init.ops = &clk_fixed_factor_ops;
- init.flags = 0;
- parent_names[0] = __clk_get_name(spicc->core);
+ init.ops = &clk_mux_ops;
init.parent_names = parent_names;
- init.num_parents = 1;
-
- div0->hw.init = &init;
+ init.num_parents = 2;
+ init.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT;
- clk = devm_clk_register(dev, &div0->hw);
- if (WARN_ON(IS_ERR(clk)))
- return PTR_ERR(clk);
+ mux->mask = mask_width(SPICC_ENH_DATARATE_EN);
+ mux->shift = ffs(SPICC_ENH_DATARATE_EN) - 1;
+ mux->reg = spicc->base + SPICC_ENH_CTL0;
+ mux->hw.init = &init;
+ clk = devm_clk_register(dev, &mux->hw);
- div1 = &meson_spicc_div3;
- snprintf(name, sizeof(name), "%s#_div3", dev_name(dev));
- init.name = name;
- init.ops = &clk_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
- parent_names[0] = __clk_get_name(clk);
- init.parent_names = parent_names;
- init.num_parents = 1;
+ return clk;
+}
- div1->reg = spicc->base + (u64) div1->reg;
- div1->hw.init = &init;
+static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
+{
+ struct device *dev = &spicc->pdev->dev;
+ struct clk *clk;
+ const char *parent_names[2];
- clk = devm_clk_register(dev, &div1->hw);
+ /* Get core clk */
+ clk = devm_clk_get(dev, "core");
if (WARN_ON(IS_ERR(clk)))
return PTR_ERR(clk);
+ clk_prepare_enable(clk);
+ spicc->core = clk;
+
+ /* Get composite clk */
+ if (spicc->data->has_comp_clk) {
+ clk = devm_clk_get(dev, "comp");
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+ clk_prepare_enable(clk);
+ spicc->comp = clk;
+ }
- mux_parent_names[1] = __clk_get_name(clk);
+ /* Register old divider */
+ clk = meson_spicc_clk_register_ff_divider(spicc, 0);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Register old divider failed\n");
+ return PTR_ERR(clk);
+ }
- mux = &meson_spicc_sel;
- snprintf(name, sizeof(name), "%s#_sel", dev_name(dev));
- init.name = name;
- init.ops = &clk_mux_ops;
- init.parent_names = mux_parent_names;
- init.num_parents = 2;
- init.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT;
+ /* use old divider as spi clk if there isn't enhance divider */
+ if (spicc->data->has_enhance_clk_div == false) {
+ spicc->clk = clk;
+ clk_prepare_enable(spicc->clk);
+ return 0;
+ }
- mux->reg = spicc->base + (u64) mux->reg;
- mux->hw.init = &init;
+ /* Set old divider as the first parent of mux */
+ parent_names[0] = __clk_get_name(clk);
+
+ /* Register enhance divider */
+ clk = meson_spicc_clk_register_ff_divider(spicc, 1);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Register enh divider failed\n");
+ return PTR_ERR(clk);
+ }
+
+ /* Set enhance divider as the second parent of mux */
+ parent_names[1] = __clk_get_name(clk);
- spicc->clk = devm_clk_register(dev, &mux->hw);
- if (WARN_ON(IS_ERR(spicc->clk)))
+ /* Register mux and use it as spi clk */
+ spicc->clk = meson_spicc_clk_register_mux(spicc, parent_names);
+ if (IS_ERR(spicc->clk)) {
+ dev_err(dev, "Register mux failed\n");
return PTR_ERR(spicc->clk);
+ }
+ /* Fix ehance divider as the parent of mux */
clk_set_parent(spicc->clk, clk);
+ clk_prepare_enable(spicc->clk);
return 0;
}
struct spi_master *master;
struct meson_spicc_device *spicc;
struct resource *res;
- int ret, irq, rate;
+ int ret, irq;
master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
if (!master) {
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq,
- 0, NULL, spicc);
+ 0, dev_name(&pdev->dev), spicc);
if (ret) {
dev_err(&pdev->dev, "irq request failed\n");
goto out_master;
}
- spicc->core = devm_clk_get(&pdev->dev, "core");
- if (IS_ERR(spicc->core)) {
- dev_err(&pdev->dev, "core clock request failed\n");
- ret = PTR_ERR(spicc->core);
- goto out_master;
- }
-
- ret = clk_prepare_enable(spicc->core);
+ ret = meson_spicc_clk_init(spicc);
if (ret) {
- dev_err(&pdev->dev, "core clock enable failed\n");
+ dev_err(&pdev->dev, "clock registration failed\n");
goto out_master;
}
- rate = clk_get_rate(spicc->core);
device_reset_optional(&pdev->dev);
master->num_chipselect = 4;
master->dev.of_node = pdev->dev.of_node;
- master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH;
- master->bits_per_word_mask = SPI_BPW_MASK(32) |
- SPI_BPW_MASK(24) |
- SPI_BPW_MASK(16) |
- SPI_BPW_MASK(8);
+ master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX);
- master->min_speed_hz = rate >> 9;
+ /* Setup max/min rate according to the Meson datasheet */
+ master->min_speed_hz = spicc->data->min_speed_hz;
+ master->max_speed_hz = spicc->data->max_speed_hz;
master->setup = meson_spicc_setup;
master->cleanup = meson_spicc_cleanup;
master->prepare_message = meson_spicc_prepare_message;
master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer;
master->transfer_one = meson_spicc_transfer_one;
- /* Setup max rate according to the Meson datasheet */
- master->max_speed_hz = min_t(unsigned int, rate >> 1,
- spicc->data->max_speed_hz);
-
- ret = meson_spicc_clk_init(spicc);
- if (ret) {
- dev_err(&pdev->dev, "clock registration failed\n");
- goto out_master;
- }
-
ret = devm_spi_register_master(&pdev->dev, master);
if (!ret) {
#ifdef MESON_SPICC_TEST_ENTRY
- spicc->cls.name = kzalloc(10, GFP_KERNEL);
- sprintf((char *)spicc->cls.name, "spicc%d", master->bus_num);
+ spicc->cls.name = dev_name(&pdev->dev);
spicc->cls.class_attrs = spicc_class_attrs;
ret = class_register(&spicc->cls);
#endif /* end MESON_SPICC_TEST_ENTRY */
}
static const struct meson_spicc_data meson_spicc_gx_data = {
- .max_speed_hz = 30000000,
+ .min_speed_hz = 325000,
+ .max_speed_hz = 4166667,
+ .dma_burst_triggered_by_ssctl = true,
+ .fifo_size = 16,
};
static const struct meson_spicc_data meson_spicc_txlx_data = {
- .max_speed_hz = 80000000,
+ .min_speed_hz = 325000,
+ .max_speed_hz = 83333333,
+ .dma_burst_triggered_by_ssctl = true,
+ .fifo_size = 16,
.has_oen = true,
.has_enhance_clk_div = true,
+ .has_cs_pre_delay = true,
};
static const struct meson_spicc_data meson_spicc_axg_data = {
- .max_speed_hz = 80000000,
+ .min_speed_hz = 325000,
+ .max_speed_hz = 83333333,
+ .fifo_size = 16,
.has_oen = true,
.has_enhance_clk_div = true,
+ .has_cs_pre_delay = true,
+ .has_enhance_io_delay = true,
+ .has_comp_clk = true,
+};
+
+static const struct meson_spicc_data meson_spicc_g12a_data = {
+ .min_speed_hz = 50000,
+ .max_speed_hz = 166666667,
+ .fifo_size = 15,
+ .has_oen = true,
+ .has_enhance_clk_div = true,
+ .has_cs_pre_delay = true,
+ .has_enhance_io_delay = true,
+ .has_comp_clk = true,
+ .is_div_parent_comp_clk = true,
+ .has_enhance_tt_ti_delay = true,
};
static const struct of_device_id meson_spicc_of_match[] = {
.compatible = "amlogic,meson-axg-spicc",
.data = &meson_spicc_axg_data,
},
+ {
+ .compatible = "amlogic,meson-g12a-spicc",
+ .data = &meson_spicc_g12a_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, meson_spicc_of_match);