spicc: add dma and enhance features of axg/txhd/g12a/g12b.
authorSunny Luo <sunny.luo@amlogic.com>
Sat, 28 Apr 2018 02:15:15 +0000 (10:15 +0800)
committerSunny Luo <sunny.luo@amlogic.com>
Fri, 13 Jul 2018 06:40:37 +0000 (23:40 -0700)
PD#164751: spicc: add dma and enhance features of axg/txhd/g12a/g12b.

1. fix TESTREG bits defination error;
2. fix cs-gpio error in slave setup;
3. reset fifo to avoid successive fifo error before tansfer;
4. add enhance feature of cs-preload delay control;
5. add auto io delay control;
6. add enhance feature of tt/ti delay control;
7. fix clk error which occurred certainly when sencond spicc added;
8. surpport both auto core clk and  fixed core clk rate setting:
   set dts "fixed_core_rate = <200000000(example)>" to use fixed
   core rate. otherwise, to use auto core clk.
9. support loop-back mode;
10. support DMA transfer;
11. must delete 1000M from parent clk.
12. It will be wrong when enhance divider=2. we have to add a flag
   CLK_DIVIDER_PROHIBIT_ZERO to shield against it.

Change-Id: I820a52e6d31d3fe2e3615a0f4c5a07c17d11914d
Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
Documentation/devicetree/bindings/spi/spi-meson.txt
drivers/spi/spi-meson-spicc.c

index 0986d84..7aeaa45 100644 (file)
@@ -31,6 +31,9 @@ Required properties:
        "amlogic,meson-gx-spicc" on Amlogic GX and compatible SoCs.
        "amlogic,meson-txlx-spicc" on Amlogic TXLX and compatible SoCs
        "amlogic,meson-axg-spicc" on Amlogic AXG and compatible SoCs
+       "amlogic,meson-g12a-spicc" on Amlogic G12A and compatible SoCs
+       "amlogic,meson-g12b-spicc", "amlogic,meson-g12a-spicc"
+                                  on Amlogic G12B and compatible SoCs
  - reg: physical base address and length of the controller registers
  - interrupts: The interrupt specifier
  - clock-names: Must contain "core"
@@ -55,7 +58,7 @@ Example :
                #size-cells = <0>;
        };
 
-       spicc_a: spi@13000 {
+       spicc0: spi@13000 {
                compatible = "amlogic,meson-txlx-spicc";
                reg = <0x0 0x13000 0x0 0x3c>;
                interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
@@ -63,16 +66,56 @@ Example :
                clock-names = "core";
                #address-cells = <1>;
                #size-cells = <0>;
-               status = "disabled";
        };
 
-       spicc_b: spi@15000 {
+       spicc1: spi@15000 {
                compatible = "amlogic,meson-txlx-spicc";
                reg = <0x0 0x15000 0x0 0x3c>;
-               interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+               interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&clkc CLKID_SPICC1>;
                clock-names = "core";
                #address-cells = <1>;
                #size-cells = <0>;
-               status = "disabled";
+       };
+
+       spicc0: spi@13000 {
+               compatible = "amlogic,meson-axg-spicc";
+               reg = <0x0 0x13000 0x0 0x40>;
+               interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clkc CLKID_SPICC0>,
+                        <&clkc CLKID_SPICC_COMP>;
+               clock-names = "core", "delay";
+               #address-cells = <1>;
+               #size-cells = <0>;
+       };
+
+       spicc1: spi@15000 {
+               compatible = "amlogic,meson-axg-spicc";
+               reg = <0x0 0x15000 0x0 0x40>;
+               interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clkc CLKID_SPICC1>,
+                        <&clkc CLKID_SPICC_COMP>;
+               clock-names = "core", "delay";
+               #address-cells = <1>;
+               #size-cells = <0>;
+       };
+
+       spicc0: spi@13000 {
+               compatible = "amlogic,meson-g12a-spicc";
+               reg = <0x0 0x13000 0x0 0x40>;
+               interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clkc CLKID_SPICC0_COMP>;
+               clock-names = "core";
+               #address-cells = <1>;
+               #size-cells = <0>;
+       };
+
+       spicc1: spi@15000 {
+               compatible = "amlogic,meson-g12a-spicc";
+               reg = <0x0 0x15000 0x0 0x40>;
+               interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clkc CLKID_SPICC1_COMP>;
+               clock-names = "core";
+               #address-cells = <1>;
+               #size-cells = <0>;
        };
\ No newline at end of file
index 49f1ef6..d5d8589 100644 (file)
@@ -22,6 +22,7 @@
 #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
@@ -36,8 +37,6 @@
  *   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 */
+#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 {
@@ -145,6 +199,7 @@ 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;
@@ -157,12 +212,11 @@ struct meson_spicc_device {
        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)
@@ -178,6 +232,156 @@ 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,
@@ -186,10 +390,31 @@ static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
 
 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;
@@ -241,34 +466,23 @@ static inline void meson_spicc_tx(struct meson_spicc_device *spicc)
                               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 */
@@ -278,61 +492,36 @@ static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
 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;
 }
@@ -342,6 +531,8 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
 {
        u32 conf, conf_orig;
 
+       meson_spicc_reset_fifo(spicc);
+
        /* Read original configuration */
        conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
 
@@ -355,6 +546,19 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
                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,
@@ -362,8 +566,6 @@ 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;
@@ -377,22 +579,23 @@ static int meson_spicc_transfer_one(struct spi_master *master,
        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;
 }
@@ -411,6 +614,9 @@ static int meson_spicc_prepare_message(struct spi_master *master,
        conf |= SPICC_ENABLE;
        conf |= SPICC_MODE_MASTER;
 
+       if (spicc->data->dma_burst_triggered_by_ssctl)
+               conf |= SPICC_SSCTL;
+
        /* SMC = 0 */
 
        /* Setup transfer mode */
@@ -439,8 +645,6 @@ static int meson_spicc_prepare_message(struct spi_master *master,
        /* 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);
 
@@ -449,10 +653,17 @@ static int meson_spicc_prepare_message(struct spi_master *master,
        /* 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;
 }
 
@@ -472,22 +683,18 @@ static int meson_spicc_setup(struct spi_device *spi)
 {
        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));
 
@@ -503,45 +710,73 @@ static void meson_spicc_cleanup(struct spi_device *spi)
 }
 
 #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);
        }
@@ -550,32 +785,35 @@ static ssize_t test_store(
 
        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);
@@ -583,12 +821,24 @@ test_end:
 }
 
 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 |----|      |
@@ -606,136 +856,167 @@ static struct class_attribute spicc_class_attrs[] = {
  *    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;
 }
 
@@ -744,7 +1025,7 @@ static int meson_spicc_probe(struct platform_device *pdev)
        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) {
@@ -777,58 +1058,37 @@ static int meson_spicc_probe(struct platform_device *pdev)
 
        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 */
@@ -855,19 +1115,44 @@ static int meson_spicc_remove(struct platform_device *pdev)
 }
 
 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[] = {
@@ -883,6 +1168,10 @@ 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);