aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/Kconfig9
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/bcm6348-iudma.c642
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-mscc-bitbang-spi.c122
-rw-r--r--drivers/misc/Kconfig6
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/jz4780_efuse.c103
-rw-r--r--drivers/mmc/Kconfig6
-rw-r--r--drivers/mmc/Makefile1
-rw-r--r--drivers/mmc/jz_mmc.c488
-rw-r--r--drivers/net/Kconfig18
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/bcm6348-eth.c537
-rw-r--r--drivers/net/bcm6368-eth.c625
-rw-r--r--drivers/pinctrl/Kconfig1
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/mscc/Kconfig22
-rw-r--r--drivers/pinctrl/mscc/Makefile5
-rw-r--r--drivers/pinctrl/mscc/mscc-common.c236
-rw-r--r--drivers/pinctrl/mscc/mscc-common.h51
-rw-r--r--drivers/pinctrl/mscc/pinctrl-luton.c172
-rw-r--r--drivers/pinctrl/mscc/pinctrl-ocelot.c188
-rw-r--r--drivers/spi/designware_spi.c8
25 files changed, 3252 insertions, 1 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 8a4162eccd..1820676d7a 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -26,6 +26,15 @@ config SANDBOX_DMA
Enable support for a test DMA uclass implementation. It stimulates
DMA transfer by simple copying data between channels.
+config BCM6348_IUDMA
+ bool "BCM6348 IUDMA driver"
+ depends on ARCH_BMIPS
+ select DMA_CHANNELS
+ help
+ Enable the BCM6348 IUDMA driver.
+ This driver support data transfer from devices to
+ memory and from memory to devices.
+
config TI_EDMA3
bool "TI EDMA3 driver"
help
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index aff31f986a..b5f9147e0a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_DMA) += dma-uclass.o
obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
obj-$(CONFIG_APBH_DMA) += apbh_dma.o
+obj-$(CONFIG_BCM6348_IUDMA) += bcm6348-iudma.o
obj-$(CONFIG_FSL_DMA) += fsl_dma.o
obj-$(CONFIG_SANDBOX_DMA) += sandbox-dma-test.o
obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
diff --git a/drivers/dma/bcm6348-iudma.c b/drivers/dma/bcm6348-iudma.c
new file mode 100644
index 0000000000..1d3c192cfe
--- /dev/null
+++ b/drivers/dma/bcm6348-iudma.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/drivers/dma/bcm63xx-iudma.c:
+ * Copyright (C) 2015 Simon Arlott <simon@fire.lp0.eu>
+ *
+ * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h:
+ * Copyright (C) 2000-2010 Broadcom Corporation
+ *
+ * Derived from bcm963xx_4.12L.06B_consumer/bcmdrivers/opensource/net/enet/impl4/bcmenet.c:
+ * Copyright (C) 2010 Broadcom Corporation
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dma-uclass.h>
+#include <memalign.h>
+#include <reset.h>
+#include <asm/io.h>
+
+#define DMA_RX_DESC 6
+#define DMA_TX_DESC 1
+
+/* DMA Channels */
+#define DMA_CHAN_FLOWC(x) ((x) >> 1)
+#define DMA_CHAN_MAX 16
+#define DMA_CHAN_SIZE 0x10
+#define DMA_CHAN_TOUT 500
+
+/* DMA Global Configuration register */
+#define DMA_CFG_REG 0x00
+#define DMA_CFG_ENABLE_SHIFT 0
+#define DMA_CFG_ENABLE_MASK (1 << DMA_CFG_ENABLE_SHIFT)
+#define DMA_CFG_FLOWC_ENABLE(x) BIT(DMA_CHAN_FLOWC(x) + 1)
+#define DMA_CFG_NCHANS_SHIFT 24
+#define DMA_CFG_NCHANS_MASK (0xf << DMA_CFG_NCHANS_SHIFT)
+
+/* DMA Global Flow Control registers */
+#define DMA_FLOWC_THR_LO_REG(x) (0x04 + DMA_CHAN_FLOWC(x) * 0x0c)
+#define DMA_FLOWC_THR_HI_REG(x) (0x08 + DMA_CHAN_FLOWC(x) * 0x0c)
+#define DMA_FLOWC_ALLOC_REG(x) (0x0c + DMA_CHAN_FLOWC(x) * 0x0c)
+#define DMA_FLOWC_ALLOC_FORCE_SHIFT 31
+#define DMA_FLOWC_ALLOC_FORCE_MASK (1 << DMA_FLOWC_ALLOC_FORCE_SHIFT)
+
+/* DMA Global Reset register */
+#define DMA_RST_REG 0x34
+#define DMA_RST_CHAN_SHIFT 0
+#define DMA_RST_CHAN_MASK(x) (1 << x)
+
+/* DMA Channel Configuration register */
+#define DMAC_CFG_REG(x) (DMA_CHAN_SIZE * (x) + 0x00)
+#define DMAC_CFG_ENABLE_SHIFT 0
+#define DMAC_CFG_ENABLE_MASK (1 << DMAC_CFG_ENABLE_SHIFT)
+#define DMAC_CFG_PKT_HALT_SHIFT 1
+#define DMAC_CFG_PKT_HALT_MASK (1 << DMAC_CFG_PKT_HALT_SHIFT)
+#define DMAC_CFG_BRST_HALT_SHIFT 2
+#define DMAC_CFG_BRST_HALT_MASK (1 << DMAC_CFG_BRST_HALT_SHIFT)
+
+/* DMA Channel Max Burst Length register */
+#define DMAC_BURST_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c)
+
+/* DMA SRAM Descriptor Ring Start register */
+#define DMAS_RSTART_REG(x) (DMA_CHAN_SIZE * (x) + 0x00)
+
+/* DMA SRAM State/Bytes done/ring offset register */
+#define DMAS_STATE_DATA_REG(x) (DMA_CHAN_SIZE * (x) + 0x04)
+
+/* DMA SRAM Buffer Descriptor status and length register */
+#define DMAS_DESC_LEN_STATUS_REG(x) (DMA_CHAN_SIZE * (x) + 0x08)
+
+/* DMA SRAM Buffer Descriptor status and length register */
+#define DMAS_DESC_BASE_BUFPTR_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c)
+
+/* DMA Descriptor Status */
+#define DMAD_ST_CRC_SHIFT 8
+#define DMAD_ST_CRC_MASK (1 << DMAD_ST_CRC_SHIFT)
+#define DMAD_ST_WRAP_SHIFT 12
+#define DMAD_ST_WRAP_MASK (1 << DMAD_ST_WRAP_SHIFT)
+#define DMAD_ST_SOP_SHIFT 13
+#define DMAD_ST_SOP_MASK (1 << DMAD_ST_SOP_SHIFT)
+#define DMAD_ST_EOP_SHIFT 14
+#define DMAD_ST_EOP_MASK (1 << DMAD_ST_EOP_SHIFT)
+#define DMAD_ST_OWN_SHIFT 15
+#define DMAD_ST_OWN_MASK (1 << DMAD_ST_OWN_SHIFT)
+
+#define DMAD6348_ST_OV_ERR_SHIFT 0
+#define DMAD6348_ST_OV_ERR_MASK (1 << DMAD6348_ST_OV_ERR_SHIFT)
+#define DMAD6348_ST_CRC_ERR_SHIFT 1
+#define DMAD6348_ST_CRC_ERR_MASK (1 << DMAD6348_ST_CRC_ERR_SHIFT)
+#define DMAD6348_ST_RX_ERR_SHIFT 2
+#define DMAD6348_ST_RX_ERR_MASK (1 << DMAD6348_ST_RX_ERR_SHIFT)
+#define DMAD6348_ST_OS_ERR_SHIFT 4
+#define DMAD6348_ST_OS_ERR_MASK (1 << DMAD6348_ST_OS_ERR_SHIFT)
+#define DMAD6348_ST_UN_ERR_SHIFT 9
+#define DMAD6348_ST_UN_ERR_MASK (1 << DMAD6348_ST_UN_ERR_SHIFT)
+
+struct bcm6348_dma_desc {
+ uint16_t length;
+ uint16_t status;
+ uint32_t address;
+};
+
+struct bcm6348_chan_priv {
+ void __iomem *dma_ring;
+ uint8_t dma_ring_size;
+ uint8_t desc_id;
+ uint8_t desc_cnt;
+ bool *busy_desc;
+ bool running;
+};
+
+struct bcm6348_iudma_hw {
+ uint16_t err_mask;
+};
+
+struct bcm6348_iudma_priv {
+ const struct bcm6348_iudma_hw *hw;
+ void __iomem *base;
+ void __iomem *chan;
+ void __iomem *sram;
+ struct bcm6348_chan_priv **ch_priv;
+ uint8_t n_channels;
+};
+
+static inline bool bcm6348_iudma_chan_is_rx(uint8_t ch)
+{
+ return !(ch & 1);
+}
+
+static inline void bcm6348_iudma_fdc(void *ptr, ulong size)
+{
+ ulong start = (ulong) ptr;
+
+ flush_dcache_range(start, start + size);
+}
+
+static inline void bcm6348_iudma_idc(void *ptr, ulong size)
+{
+ ulong start = (ulong) ptr;
+
+ invalidate_dcache_range(start, start + size);
+}
+
+static void bcm6348_iudma_chan_stop(struct bcm6348_iudma_priv *priv,
+ uint8_t ch)
+{
+ unsigned int timeout = DMA_CHAN_TOUT;
+
+ do {
+ uint32_t cfg, halt;
+
+ if (timeout > DMA_CHAN_TOUT / 2)
+ halt = DMAC_CFG_PKT_HALT_MASK;
+ else
+ halt = DMAC_CFG_BRST_HALT_MASK;
+
+ /* try to stop dma channel */
+ writel_be(halt, priv->chan + DMAC_CFG_REG(ch));
+ mb();
+
+ /* check if channel was stopped */
+ cfg = readl_be(priv->chan + DMAC_CFG_REG(ch));
+ if (!(cfg & DMAC_CFG_ENABLE_MASK))
+ break;
+
+ udelay(1);
+ } while (--timeout);
+
+ if (!timeout)
+ pr_err("unable to stop channel %u\n", ch);
+
+ /* reset dma channel */
+ setbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch));
+ mb();
+ clrbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch));
+}
+
+static int bcm6348_iudma_disable(struct dma *dma)
+{
+ struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+
+ /* stop dma channel */
+ bcm6348_iudma_chan_stop(priv, dma->id);
+
+ /* dma flow control */
+ if (bcm6348_iudma_chan_is_rx(dma->id))
+ writel_be(DMA_FLOWC_ALLOC_FORCE_MASK,
+ DMA_FLOWC_ALLOC_REG(dma->id));
+
+ /* init channel config */
+ ch_priv->running = false;
+ ch_priv->desc_id = 0;
+ if (bcm6348_iudma_chan_is_rx(dma->id))
+ ch_priv->desc_cnt = 0;
+ else
+ ch_priv->desc_cnt = ch_priv->dma_ring_size;
+
+ return 0;
+}
+
+static int bcm6348_iudma_enable(struct dma *dma)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+ struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
+ uint8_t i;
+
+ /* dma ring init */
+ for (i = 0; i < ch_priv->desc_cnt; i++) {
+ if (bcm6348_iudma_chan_is_rx(dma->id)) {
+ ch_priv->busy_desc[i] = false;
+ dma_desc->status |= DMAD_ST_OWN_MASK;
+ } else {
+ dma_desc->status = 0;
+ dma_desc->length = 0;
+ dma_desc->address = 0;
+ }
+
+ if (i == ch_priv->desc_cnt - 1)
+ dma_desc->status |= DMAD_ST_WRAP_MASK;
+
+ dma_desc++;
+ }
+
+ /* init to first descriptor */
+ ch_priv->desc_id = 0;
+
+ /* force cache writeback */
+ bcm6348_iudma_fdc(ch_priv->dma_ring,
+ sizeof(*dma_desc) * ch_priv->desc_cnt);
+
+ /* clear sram */
+ writel_be(0, priv->sram + DMAS_STATE_DATA_REG(dma->id));
+ writel_be(0, priv->sram + DMAS_DESC_LEN_STATUS_REG(dma->id));
+ writel_be(0, priv->sram + DMAS_DESC_BASE_BUFPTR_REG(dma->id));
+
+ /* set dma ring start */
+ writel_be(virt_to_phys(ch_priv->dma_ring),
+ priv->sram + DMAS_RSTART_REG(dma->id));
+
+ /* set flow control */
+ if (bcm6348_iudma_chan_is_rx(dma->id)) {
+ u32 val;
+
+ setbits_be32(priv->base + DMA_CFG_REG,
+ DMA_CFG_FLOWC_ENABLE(dma->id));
+
+ val = ch_priv->desc_cnt / 3;
+ writel_be(val, priv->base + DMA_FLOWC_THR_LO_REG(dma->id));
+
+ val = (ch_priv->desc_cnt * 2) / 3;
+ writel_be(val, priv->base + DMA_FLOWC_THR_HI_REG(dma->id));
+
+ writel_be(0, priv->base + DMA_FLOWC_ALLOC_REG(dma->id));
+ }
+
+ /* set dma max burst */
+ writel_be(ch_priv->desc_cnt,
+ priv->chan + DMAC_BURST_REG(dma->id));
+
+ /* kick rx dma channel */
+ if (bcm6348_iudma_chan_is_rx(dma->id))
+ setbits_be32(priv->chan + DMAC_CFG_REG(dma->id),
+ DMAC_CFG_ENABLE_MASK);
+
+ /* channel is now enabled */
+ ch_priv->running = true;
+
+ return 0;
+}
+
+static int bcm6348_iudma_request(struct dma *dma)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv;
+
+ /* check if channel is valid */
+ if (dma->id >= priv->n_channels)
+ return -ENODEV;
+
+ /* alloc channel private data */
+ priv->ch_priv[dma->id] = calloc(1, sizeof(struct bcm6348_chan_priv));
+ if (!priv->ch_priv[dma->id])
+ return -ENOMEM;
+ ch_priv = priv->ch_priv[dma->id];
+
+ /* alloc dma ring */
+ if (bcm6348_iudma_chan_is_rx(dma->id))
+ ch_priv->dma_ring_size = DMA_RX_DESC;
+ else
+ ch_priv->dma_ring_size = DMA_TX_DESC;
+
+ ch_priv->dma_ring =
+ malloc_cache_aligned(sizeof(struct bcm6348_dma_desc) *
+ ch_priv->dma_ring_size);
+ if (!ch_priv->dma_ring)
+ return -ENOMEM;
+
+ /* init channel config */
+ ch_priv->running = false;
+ ch_priv->desc_id = 0;
+ if (bcm6348_iudma_chan_is_rx(dma->id)) {
+ ch_priv->desc_cnt = 0;
+ ch_priv->busy_desc = calloc(ch_priv->desc_cnt, sizeof(bool));
+ } else {
+ ch_priv->desc_cnt = ch_priv->dma_ring_size;
+ ch_priv->busy_desc = NULL;
+ }
+
+ return 0;
+}
+
+static int bcm6348_iudma_receive(struct dma *dma, void **dst, void *metadata)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ const struct bcm6348_iudma_hw *hw = priv->hw;
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+ struct bcm6348_dma_desc *dma_desc = dma_desc = ch_priv->dma_ring;
+ int ret;
+
+ /* get dma ring descriptor address */
+ dma_desc += ch_priv->desc_id;
+
+ /* invalidate cache data */
+ bcm6348_iudma_idc(dma_desc, sizeof(*dma_desc));
+
+ /* check dma own */
+ if (dma_desc->status & DMAD_ST_OWN_MASK)
+ return -EAGAIN;
+
+ /* check pkt */
+ if (!(dma_desc->status & DMAD_ST_EOP_MASK) ||
+ !(dma_desc->status & DMAD_ST_SOP_MASK) ||
+ (dma_desc->status & hw->err_mask)) {
+ pr_err("invalid pkt received (ch=%ld desc=%u) (st=%04x)\n",
+ dma->id, ch_priv->desc_id, dma_desc->status);
+ ret = -EAGAIN;
+ } else {
+ /* set dma buffer address */
+ *dst = phys_to_virt(dma_desc->address);
+
+ /* invalidate cache data */
+ bcm6348_iudma_idc(*dst, dma_desc->length);
+
+ /* return packet length */
+ ret = dma_desc->length;
+ }
+
+ /* busy dma descriptor */
+ ch_priv->busy_desc[ch_priv->desc_id] = true;
+
+ /* increment dma descriptor */
+ ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->desc_cnt;
+
+ return ret;
+}
+
+static int bcm6348_iudma_send(struct dma *dma, void *src, size_t len,
+ void *metadata)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+ struct bcm6348_dma_desc *dma_desc;
+ uint16_t status;
+
+ /* flush cache */
+ bcm6348_iudma_fdc(src, len);
+
+ /* get dma ring descriptor address */
+ dma_desc = ch_priv->dma_ring;
+ dma_desc += ch_priv->desc_id;
+
+ /* config dma descriptor */
+ status = (DMAD_ST_OWN_MASK |
+ DMAD_ST_EOP_MASK |
+ DMAD_ST_CRC_MASK |
+ DMAD_ST_SOP_MASK);
+ if (ch_priv->desc_id == ch_priv->desc_cnt - 1)
+ status |= DMAD_ST_WRAP_MASK;
+
+ /* set dma descriptor */
+ dma_desc->address = virt_to_phys(src);
+ dma_desc->length = len;
+ dma_desc->status = status;
+
+ /* flush cache */
+ bcm6348_iudma_fdc(dma_desc, sizeof(*dma_desc));
+
+ /* kick tx dma channel */
+ setbits_be32(priv->chan + DMAC_CFG_REG(dma->id), DMAC_CFG_ENABLE_MASK);
+
+ /* poll dma status */
+ do {
+ /* invalidate cache */
+ bcm6348_iudma_idc(dma_desc, sizeof(*dma_desc));
+
+ if (!(dma_desc->status & DMAD_ST_OWN_MASK))
+ break;
+ } while(1);
+
+ /* increment dma descriptor */
+ ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->desc_cnt;
+
+ return 0;
+}
+
+static int bcm6348_iudma_free_rcv_buf(struct dma *dma, void *dst, size_t size)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+ struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
+ uint16_t status;
+ uint8_t i;
+ u32 cfg;
+
+ /* get dirty dma descriptor */
+ for (i = 0; i < ch_priv->desc_cnt; i++) {
+ if (phys_to_virt(dma_desc->address) == dst)
+ break;
+
+ dma_desc++;
+ }
+
+ /* dma descriptor not found */
+ if (i == ch_priv->desc_cnt) {
+ pr_err("dirty dma descriptor not found\n");
+ return -ENOENT;
+ }
+
+ /* invalidate cache */
+ bcm6348_iudma_idc(ch_priv->dma_ring,
+ sizeof(*dma_desc) * ch_priv->desc_cnt);
+
+ /* free dma descriptor */
+ ch_priv->busy_desc[i] = false;
+
+ status = DMAD_ST_OWN_MASK;
+ if (i == ch_priv->desc_cnt - 1)
+ status |= DMAD_ST_WRAP_MASK;
+
+ dma_desc->status |= status;
+ dma_desc->length = PKTSIZE_ALIGN;
+
+ /* tell dma we allocated one buffer */
+ writel_be(1, DMA_FLOWC_ALLOC_REG(dma->id));
+
+ /* flush cache */
+ bcm6348_iudma_fdc(ch_priv->dma_ring,
+ sizeof(*dma_desc) * ch_priv->desc_cnt);
+
+ /* kick rx dma channel if disabled */
+ cfg = readl_be(priv->chan + DMAC_CFG_REG(dma->id));
+ if (!(cfg & DMAC_CFG_ENABLE_MASK))
+ setbits_be32(priv->chan + DMAC_CFG_REG(dma->id),
+ DMAC_CFG_ENABLE_MASK);
+
+ return 0;
+}
+
+static int bcm6348_iudma_add_rcv_buf(struct dma *dma, void *dst, size_t size)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+ struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
+
+ /* no more dma descriptors available */
+ if (ch_priv->desc_cnt == ch_priv->dma_ring_size) {
+ pr_err("max number of buffers reached\n");
+ return -EINVAL;
+ }
+
+ /* get next dma descriptor */
+ dma_desc += ch_priv->desc_cnt;
+
+ /* init dma descriptor */
+ dma_desc->address = virt_to_phys(dst);
+ dma_desc->length = size;
+ dma_desc->status = 0;
+
+ /* flush cache */
+ bcm6348_iudma_fdc(dma_desc, sizeof(*dma_desc));
+
+ /* increment dma descriptors */
+ ch_priv->desc_cnt++;
+
+ return 0;
+}
+
+static int bcm6348_iudma_prepare_rcv_buf(struct dma *dma, void *dst,
+ size_t size)
+{
+ const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
+ struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
+
+ /* only add new rx buffers if channel isn't running */
+ if (ch_priv->running)
+ return bcm6348_iudma_free_rcv_buf(dma, dst, size);
+ else
+ return bcm6348_iudma_add_rcv_buf(dma, dst, size);
+}
+
+static const struct dma_ops bcm6348_iudma_ops = {
+ .disable = bcm6348_iudma_disable,
+ .enable = bcm6348_iudma_enable,
+ .prepare_rcv_buf = bcm6348_iudma_prepare_rcv_buf,
+ .request = bcm6348_iudma_request,
+ .receive = bcm6348_iudma_receive,
+ .send = bcm6348_iudma_send,
+};
+
+static const struct bcm6348_iudma_hw bcm6348_hw = {
+ .err_mask = (DMAD6348_ST_OV_ERR_MASK |
+ DMAD6348_ST_CRC_ERR_MASK |
+ DMAD6348_ST_RX_ERR_MASK |
+ DMAD6348_ST_OS_ERR_MASK |
+ DMAD6348_ST_UN_ERR_MASK),
+};
+
+static const struct bcm6348_iudma_hw bcm6368_hw = {
+ .err_mask = 0,
+};
+
+static const struct udevice_id bcm6348_iudma_ids[] = {
+ {
+ .compatible = "brcm,bcm6348-iudma",
+ .data = (ulong)&bcm6348_hw,
+ }, {
+ .compatible = "brcm,bcm6368-iudma",
+ .data = (ulong)&bcm6368_hw,
+ }, { /* sentinel */ }
+};
+
+static int bcm6348_iudma_probe(struct udevice *dev)
+{
+ struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct bcm6348_iudma_priv *priv = dev_get_priv(dev);
+ const struct bcm6348_iudma_hw *hw =
+ (const struct bcm6348_iudma_hw *)dev_get_driver_data(dev);
+ uint8_t ch;
+ int i;
+
+ uc_priv->supported = (DMA_SUPPORTS_DEV_TO_MEM |
+ DMA_SUPPORTS_MEM_TO_DEV);
+ priv->hw = hw;
+
+ /* dma global base address */
+ priv->base = dev_remap_addr_name(dev, "dma");
+ if (!priv->base)
+ return -EINVAL;
+
+ /* dma channels base address */
+ priv->chan = dev_remap_addr_name(dev, "dma-channels");
+ if (!priv->chan)
+ return -EINVAL;
+
+ /* dma sram base address */
+ priv->sram = dev_remap_addr_name(dev, "dma-sram");
+ if (!priv->sram)
+ return -EINVAL;
+
+ /* get number of channels */
+ priv->n_channels = dev_read_u32_default(dev, "dma-channels", 8);
+ if (priv->n_channels > DMA_CHAN_MAX)
+ return -EINVAL;
+
+ /* try to enable clocks */
+ for (i = 0; ; i++) {
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_index(dev, i, &clk);
+ if (ret < 0)
+ break;
+
+ ret = clk_enable(&clk);
+ if (ret < 0) {
+ pr_err("error enabling clock %d\n", i);
+ return ret;
+ }
+
+ ret = clk_free(&clk);
+ if (ret < 0) {
+ pr_err("error freeing clock %d\n", i);
+ return ret;
+ }
+ }
+
+ /* try to perform resets */
+ for (i = 0; ; i++) {
+ struct reset_ctl reset;
+ int ret;
+
+ ret = reset_get_by_index(dev, i, &reset);
+ if (ret < 0)
+ break;
+
+ ret = reset_deassert(&reset);
+ if (ret < 0) {
+ pr_err("error deasserting reset %d\n", i);
+ return ret;
+ }
+
+ ret = reset_free(&reset);
+ if (ret < 0) {
+ pr_err("error freeing reset %d\n", i);
+ return ret;
+ }
+ }
+
+ /* disable dma controller */
+ clrbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK);
+
+ /* alloc channel private data pointers */
+ priv->ch_priv = calloc(priv->n_channels,
+ sizeof(struct bcm6348_chan_priv*));
+ if (!priv->ch_priv)
+ return -ENOMEM;
+
+ /* stop dma channels */
+ for (ch = 0; ch < priv->n_channels; ch++)
+ bcm6348_iudma_chan_stop(priv, ch);
+
+ /* enable dma controller */
+ setbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(bcm6348_iudma) = {
+ .name = "bcm6348_iudma",
+ .id = UCLASS_DMA,
+ .of_match = bcm6348_iudma_ids,
+ .ops = &bcm6348_iudma_ops,
+ .priv_auto_alloc_size = sizeof(struct bcm6348_iudma_priv),
+ .probe = bcm6348_iudma_probe,
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 35344e57c6..c8c6c60623 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -99,6 +99,13 @@ config LPC32XX_GPIO
help
Support for the LPC32XX GPIO driver.
+config MSCC_BITBANG_SPI_GPIO
+ bool "Microsemi bitbang spi GPIO driver"
+ depends on DM_GPIO && SOC_VCOREIII
+ help
+ Support controlling the GPIO used for SPI bitbang by software. Can
+ be used by the VCoreIII SoCs, but it was mainly useful for Luton.
+
config MSM_GPIO
bool "Qualcomm GPIO driver"
depends on DM_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 7ed9a4ec42..61feda1537 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o
obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o
obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
+obj-$(CONFIG_MSCC_BITBANG_SPI_GPIO) += gpio-mscc-bitbang-spi.o
diff --git a/drivers/gpio/gpio-mscc-bitbang-spi.c b/drivers/gpio/gpio-mscc-bitbang-spi.c
new file mode 100644
index 0000000000..b675f9052c
--- /dev/null
+++ b/drivers/gpio/gpio-mscc-bitbang-spi.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi SoCs pinctrl driver
+ *
+ * Author: <gregory.clement@bootlin.com>
+ * License: Dual MIT/GPL
+ * Copyright (c) 2018 Microsemi Corporation
+ */
+
+#include <common.h>
+#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <errno.h>
+
+enum {
+ SDI,
+ CS0,
+ CS1,
+ CS2,
+ CS3,
+ SDO,
+ SCK
+};
+
+static const int pinmap[] = { 0, 5, 6, 7, 8, 10, 12 };
+
+#define SW_SPI_CSn_OE 0x1E /* bits 1 to 4 */
+#define SW_SPI_CS0_OE BIT(1)
+#define SW_SPI_SDO_OE BIT(9)
+#define SW_SPI_SCK_OE BIT(11)
+#define SW_PIN_CTRL_MODE BIT(13)
+
+struct mscc_bb_spi_gpio {
+ void __iomem *regs;
+ u32 cache_val;
+};
+
+static int mscc_bb_spi_gpio_set(struct udevice *dev, unsigned oft, int val)
+{
+ struct mscc_bb_spi_gpio *gpio = dev_get_priv(dev);
+
+ if (val)
+ gpio->cache_val |= BIT(pinmap[oft]);
+ else
+ gpio->cache_val &= ~BIT(pinmap[oft]);
+
+ writel(gpio->cache_val, gpio->regs);
+
+ return 0;
+}
+
+static int mscc_bb_spi_gpio_direction_output(struct udevice *dev, unsigned oft,
+ int val)
+{
+ if (oft == 0) {
+ pr_err("SW_SPI_DSI can't be used as output\n");
+ return -ENOTSUPP;
+ }
+
+ mscc_bb_spi_gpio_set(dev, oft, val);
+
+ return 0;
+}
+
+static int mscc_bb_spi_gpio_direction_input(struct udevice *dev, unsigned oft)
+{
+ return 0;
+}
+
+static int mscc_bb_spi_gpio_get(struct udevice *dev, unsigned int oft)
+{
+ struct mscc_bb_spi_gpio *gpio = dev_get_priv(dev);
+ u32 val = readl(gpio->regs);
+
+ return !!(val & BIT(pinmap[oft]));
+}
+
+static const struct dm_gpio_ops mscc_bb_spi_gpio_ops = {
+ .direction_output = mscc_bb_spi_gpio_direction_output,
+ .direction_input = mscc_bb_spi_gpio_direction_input,
+ .set_value = mscc_bb_spi_gpio_set,
+ .get_value = mscc_bb_spi_gpio_get,
+};
+
+static int mscc_bb_spi_gpio_probe(struct udevice *dev)
+{
+ struct mscc_bb_spi_gpio *gpio = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ gpio->regs = dev_remap_addr(dev);
+ if (!gpio->regs)
+ return -EINVAL;
+
+ uc_priv->bank_name = dev->name;
+ uc_priv->gpio_count = ARRAY_SIZE(pinmap);
+ /*
+ * Enable software mode to control the SPI pin, enables the
+ * output mode for most of the pin and initialize the cache
+ * value in the same time
+ */
+
+ gpio->cache_val = SW_PIN_CTRL_MODE | SW_SPI_SCK_OE | SW_SPI_SDO_OE |
+ SW_SPI_CS0_OE;
+ writel(gpio->cache_val, gpio->regs);
+
+ return 0;
+}
+
+static const struct udevice_id mscc_bb_spi_gpio_ids[] = {
+ {.compatible = "mscc,spi-bitbang-gpio"},
+ {}
+};
+
+U_BOOT_DRIVER(gpio_mscc_bb_spi) = {
+ .name = "gpio-mscc-spi-bitbang",
+ .id = UCLASS_GPIO,
+ .ops = &mscc_bb_spi_gpio_ops,
+ .probe = mscc_bb_spi_gpio_probe,
+ .of_match = of_match_ptr(mscc_bb_spi_gpio_ids),
+ .priv_auto_alloc_size = sizeof(struct mscc_bb_spi_gpio),
+};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 48febc47d2..704c8dd195 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -120,6 +120,12 @@ config FSL_SEC_MON
Security Monitor can be transitioned on any security failures,
like software violations or hardware security violations.
+config JZ4780_EFUSE
+ bool "Ingenic JZ4780 eFUSE support"
+ depends on ARCH_JZ47XX
+ help
+ This selects support for the eFUSE on Ingenic JZ4780 SoCs.
+
config MXC_OCOTP
bool "Enable MXC OCOTP Driver"
help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 302d441592..6bdf5054f4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_TEGRA_CAR) += tegra_car.o
obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress_config.o
obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o
+obj-$(CONFIG_JZ4780_EFUSE) += jz4780_efuse.o
diff --git a/drivers/misc/jz4780_efuse.c b/drivers/misc/jz4780_efuse.c
new file mode 100644
index 0000000000..bc3dc93af2
--- /dev/null
+++ b/drivers/misc/jz4780_efuse.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * JZ4780 EFUSE driver
+ *
+ * Copyright (c) 2014 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <errno.h>
+#include <mach/jz4780.h>
+#include <wait_bit.h>
+
+#define EFUSE_EFUCTRL 0xd0
+#define EFUSE_EFUCFG 0xd4
+#define EFUSE_EFUSTATE 0xd8
+#define EFUSE_EFUDATA(n) (0xdc + ((n) * 4))
+
+#define EFUSE_EFUCTRL_RD_EN BIT(0)
+#define EFUSE_EFUCTRL_LEN_BIT 16
+#define EFUSE_EFUCTRL_LEN_MASK 0x1f
+#define EFUSE_EFUCTRL_ADDR_BIT 21
+#define EFUSE_EFUCTRL_ADDR_MASK 0x1ff
+#define EFUSE_EFUCTRL_CS BIT(30)
+
+#define EFUSE_EFUCFG_RD_STROBE_BIT 16
+#define EFUSE_EFUCFG_RD_STROBE_MASK 0xf
+#define EFUSE_EFUCFG_RD_ADJ_BIT 20
+#define EFUSE_EFUCFG_RD_ADJ_MASK 0xf
+
+#define EFUSE_EFUSTATE_RD_DONE BIT(0)
+
+static void jz4780_efuse_read_chunk(size_t addr, size_t count, u8 *buf)
+{
+ void __iomem *regs = (void __iomem *)NEMC_BASE;
+ size_t i;
+ u32 val;
+ int ret;
+
+ val = EFUSE_EFUCTRL_RD_EN |
+ ((count - 1) << EFUSE_EFUCTRL_LEN_BIT) |
+ (addr << EFUSE_EFUCTRL_ADDR_BIT) |
+ ((addr > 0x200) ? EFUSE_EFUCTRL_CS : 0);
+ writel(val, regs + EFUSE_EFUCTRL);
+
+ ret = wait_for_bit_le32(regs + EFUSE_EFUSTATE,
+ EFUSE_EFUSTATE_RD_DONE, true, 10000, false);
+ if (ret)
+ return;
+
+ if ((count % 4) == 0) {
+ for (i = 0; i < count / 4; i++) {
+ val = readl(regs + EFUSE_EFUDATA(i));
+ put_unaligned(val, (u32 *)(buf + (i * 4)));
+ }
+ } else {
+ val = readl(regs + EFUSE_EFUDATA(0));
+ if (count > 2)
+ buf[2] = (val >> 16) & 0xff;
+ if (count > 1)
+ buf[1] = (val >> 8) & 0xff;
+ buf[0] = val & 0xff;
+ }
+}
+
+static inline int jz4780_efuse_chunk_size(size_t count)
+{
+ if (count >= 32)
+ return 32;
+ else if ((count / 4) > 0)
+ return (count / 4) * 4;
+ else
+ return count % 4;
+}
+
+void jz4780_efuse_read(size_t addr, size_t count, u8 *buf)
+{
+ size_t chunk;
+
+ while (count > 0) {
+ chunk = jz4780_efuse_chunk_size(count);
+ jz4780_efuse_read_chunk(addr, chunk, buf);
+ addr += chunk;
+ buf += chunk;
+ count -= chunk;
+ }
+}
+
+void jz4780_efuse_init(u32 ahb2_rate)
+{
+ void __iomem *regs = (void __iomem *)NEMC_BASE;
+ u32 rd_adj, rd_strobe, tmp;
+
+ rd_adj = (((6500 * (ahb2_rate / 1000000)) / 1000000) + 0xf) / 2;
+ tmp = (((35000 * (ahb2_rate / 1000000)) / 1000000) - 4) - rd_adj;
+ rd_strobe = ((tmp + 0xf) / 2 < 7) ? 7 : (tmp + 0xf) / 2;
+
+ tmp = (rd_adj << EFUSE_EFUCFG_RD_ADJ_BIT) |
+ (rd_strobe << EFUSE_EFUCFG_RD_STROBE_BIT);
+ writel(tmp, regs + EFUSE_EFUCFG);
+}
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index fbd13964a0..496b2cba64 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -332,6 +332,12 @@ config MMC_BCM2835
If unsure, say N.
+config JZ47XX_MMC
+ bool "Ingenic JZ47xx SD/MMC Host Controller support"
+ depends on ARCH_JZ47XX
+ help
+ This selects support for the SD Card Controller on Ingenic JZ47xx SoCs.
+
config MMC_SANDBOX
bool "Sandbox MMC support"
depends on SANDBOX
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 801a26d821..7892c468f0 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_MMC_SANDBOX) += sandbox_mmc.o
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o
+obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o
# SDHCI
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
diff --git a/drivers/mmc/jz_mmc.c b/drivers/mmc/jz_mmc.c
new file mode 100644
index 0000000000..3132c3e191
--- /dev/null
+++ b/drivers/mmc/jz_mmc.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ingenic JZ MMC driver
+ *
+ * Copyright (c) 2013 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <errno.h>
+#include <mach/jz4780.h>
+#include <wait_bit.h>
+
+/* Registers */
+#define MSC_STRPCL 0x000
+#define MSC_STAT 0x004
+#define MSC_CLKRT 0x008
+#define MSC_CMDAT 0x00c
+#define MSC_RESTO 0x010
+#define MSC_RDTO 0x014
+#define MSC_BLKLEN 0x018
+#define MSC_NOB 0x01c
+#define MSC_SNOB 0x020
+#define MSC_IMASK 0x024
+#define MSC_IREG 0x028
+#define MSC_CMD 0x02c
+#define MSC_ARG 0x030
+#define MSC_RES 0x034
+#define MSC_RXFIFO 0x038
+#define MSC_TXFIFO 0x03c
+#define MSC_LPM 0x040
+#define MSC_DMAC 0x044
+#define MSC_DMANDA 0x048
+#define MSC_DMADA 0x04c
+#define MSC_DMALEN 0x050
+#define MSC_DMACMD 0x054
+#define MSC_CTRL2 0x058
+#define MSC_RTCNT 0x05c
+#define MSC_DBG 0x0fc
+
+/* MSC Clock and Control Register (MSC_STRPCL) */
+#define MSC_STRPCL_EXIT_MULTIPLE BIT(7)
+#define MSC_STRPCL_EXIT_TRANSFER BIT(6)
+#define MSC_STRPCL_START_READWAIT BIT(5)
+#define MSC_STRPCL_STOP_READWAIT BIT(4)
+#define MSC_STRPCL_RESET BIT(3)
+#define MSC_STRPCL_START_OP BIT(2)
+#define MSC_STRPCL_CLOCK_CONTROL_STOP BIT(0)
+#define MSC_STRPCL_CLOCK_CONTROL_START BIT(1)
+
+/* MSC Status Register (MSC_STAT) */
+#define MSC_STAT_AUTO_CMD_DONE BIT(31)
+#define MSC_STAT_IS_RESETTING BIT(15)
+#define MSC_STAT_SDIO_INT_ACTIVE BIT(14)
+#define MSC_STAT_PRG_DONE BIT(13)
+#define MSC_STAT_DATA_TRAN_DONE BIT(12)
+#define MSC_STAT_END_CMD_RES BIT(11)
+#define MSC_STAT_DATA_FIFO_AFULL BIT(10)
+#define MSC_STAT_IS_READWAIT BIT(9)
+#define MSC_STAT_CLK_EN BIT(8)
+#define MSC_STAT_DATA_FIFO_FULL BIT(7)
+#define MSC_STAT_DATA_FIFO_EMPTY BIT(6)
+#define MSC_STAT_CRC_RES_ERR BIT(5)
+#define MSC_STAT_CRC_READ_ERROR BIT(4)
+#define MSC_STAT_CRC_WRITE_ERROR BIT(2)
+#define MSC_STAT_CRC_WRITE_ERROR_NOSTS BIT(4)
+#define MSC_STAT_TIME_OUT_RES BIT(1)
+#define MSC_STAT_TIME_OUT_READ BIT(0)
+
+/* MSC Bus Clock Control Register (MSC_CLKRT) */
+#define MSC_CLKRT_CLK_RATE_MASK 0x7
+
+/* MSC Command Sequence Control Register (MSC_CMDAT) */
+#define MSC_CMDAT_IO_ABORT BIT(11)
+#define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << 9)
+#define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << 9)
+#define MSC_CMDAT_DMA_EN BIT(8)
+#define MSC_CMDAT_INIT BIT(7)
+#define MSC_CMDAT_BUSY BIT(6)
+#define MSC_CMDAT_STREAM_BLOCK BIT(5)
+#define MSC_CMDAT_WRITE BIT(4)
+#define MSC_CMDAT_DATA_EN BIT(3)
+#define MSC_CMDAT_RESPONSE_MASK (0x7 << 0)
+#define MSC_CMDAT_RESPONSE_NONE (0x0 << 0) /* No response */
+#define MSC_CMDAT_RESPONSE_R1 (0x1 << 0) /* Format R1 and R1b */
+#define MSC_CMDAT_RESPONSE_R2 (0x2 << 0) /* Format R2 */
+#define MSC_CMDAT_RESPONSE_R3 (0x3 << 0) /* Format R3 */
+#define MSC_CMDAT_RESPONSE_R4 (0x4 << 0) /* Format R4 */
+#define MSC_CMDAT_RESPONSE_R5 (0x5 << 0) /* Format R5 */
+#define MSC_CMDAT_RESPONSE_R6 (0x6 << 0) /* Format R6 */
+
+/* MSC Interrupts Mask Register (MSC_IMASK) */
+#define MSC_IMASK_TIME_OUT_RES BIT(9)
+#define MSC_IMASK_TIME_OUT_READ BIT(8)
+#define MSC_IMASK_SDIO BIT(7)
+#define MSC_IMASK_TXFIFO_WR_REQ BIT(6)
+#define MSC_IMASK_RXFIFO_RD_REQ BIT(5)
+#define MSC_IMASK_END_CMD_RES BIT(2)
+#define MSC_IMASK_PRG_DONE BIT(1)
+#define MSC_IMASK_DATA_TRAN_DONE BIT(0)
+
+/* MSC Interrupts Status Register (MSC_IREG) */
+#define MSC_IREG_TIME_OUT_RES BIT(9)
+#define MSC_IREG_TIME_OUT_READ BIT(8)
+#define MSC_IREG_SDIO BIT(7)
+#define MSC_IREG_TXFIFO_WR_REQ BIT(6)
+#define MSC_IREG_RXFIFO_RD_REQ BIT(5)
+#define MSC_IREG_END_CMD_RES BIT(2)
+#define MSC_IREG_PRG_DONE BIT(1)
+#define MSC_IREG_DATA_TRAN_DONE BIT(0)
+
+struct jz_mmc_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+
+struct jz_mmc_priv {
+ void __iomem *regs;
+ u32 flags;
+/* priv flags */
+#define JZ_MMC_BUS_WIDTH_MASK 0x3
+#define JZ_MMC_BUS_WIDTH_1 0x0
+#define JZ_MMC_BUS_WIDTH_4 0x2
+#define JZ_MMC_BUS_WIDTH_8 0x3
+#define JZ_MMC_SENT_INIT BIT(2)
+};
+
+static int jz_mmc_clock_rate(void)
+{
+ return 24000000;
+}
+
+static int jz_mmc_send_cmd(struct mmc *mmc, struct jz_mmc_priv *priv,
+ struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ u32 stat, mask, cmdat = 0;
+ int i, ret;
+
+ /* stop the clock */
+ writel(MSC_STRPCL_CLOCK_CONTROL_STOP, priv->regs + MSC_STRPCL);
+ ret = wait_for_bit_le32(priv->regs + MSC_STAT,
+ MSC_STAT_CLK_EN, false, 10000, false);
+ if (ret)
+ return ret;
+
+ writel(0, priv->regs + MSC_DMAC);
+
+ /* setup command */
+ writel(cmd->cmdidx, priv->regs + MSC_CMD);
+ writel(cmd->cmdarg, priv->regs + MSC_ARG);
+
+ if (data) {
+ /* setup data */
+ cmdat |= MSC_CMDAT_DATA_EN;
+ if (data->flags & MMC_DATA_WRITE)
+ cmdat |= MSC_CMDAT_WRITE;
+
+ writel(data->blocks, priv->regs + MSC_NOB);
+ writel(data->blocksize, priv->regs + MSC_BLKLEN);
+ } else {
+ writel(0, priv->regs + MSC_NOB);
+ writel(0, priv->regs + MSC_BLKLEN);
+ }
+
+ /* setup response */
+ switch (cmd->resp_type) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1b:
+ cmdat |= MSC_CMDAT_RESPONSE_R1;
+ break;
+ case MMC_RSP_R2:
+ cmdat |= MSC_CMDAT_RESPONSE_R2;
+ break;
+ case MMC_RSP_R3:
+ cmdat |= MSC_CMDAT_RESPONSE_R3;
+ break;
+ default:
+ break;
+ }
+
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ cmdat |= MSC_CMDAT_BUSY;
+
+ /* set init for the first command only */
+ if (!(priv->flags & JZ_MMC_SENT_INIT)) {
+ cmdat |= MSC_CMDAT_INIT;
+ priv->flags |= JZ_MMC_SENT_INIT;
+ }
+
+ cmdat |= (priv->flags & JZ_MMC_BUS_WIDTH_MASK) << 9;
+
+ /* write the data setup */
+ writel(cmdat, priv->regs + MSC_CMDAT);
+
+ /* unmask interrupts */
+ mask = 0xffffffff & ~(MSC_IMASK_END_CMD_RES | MSC_IMASK_TIME_OUT_RES);
+ if (data) {
+ mask &= ~MSC_IMASK_DATA_TRAN_DONE;
+ if (data->flags & MMC_DATA_WRITE) {
+ mask &= ~MSC_IMASK_TXFIFO_WR_REQ;
+ } else {
+ mask &= ~(MSC_IMASK_RXFIFO_RD_REQ |
+ MSC_IMASK_TIME_OUT_READ);
+ }
+ }
+ writel(mask, priv->regs + MSC_IMASK);
+
+ /* clear interrupts */
+ writel(0xffffffff, priv->regs + MSC_IREG);
+
+ /* start the command (& the clock) */
+ writel(MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START,
+ priv->regs + MSC_STRPCL);
+
+ /* wait for completion */
+ for (i = 0; i < 100; i++) {
+ stat = readl(priv->regs + MSC_IREG);
+ stat &= MSC_IREG_END_CMD_RES | MSC_IREG_TIME_OUT_RES;
+ if (stat)
+ break;
+ mdelay(1);
+ }
+ writel(stat, priv->regs + MSC_IREG);
+ if (stat & MSC_IREG_TIME_OUT_RES)
+ return -ETIMEDOUT;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ /* read the response */
+ if (cmd->resp_type & MMC_RSP_136) {
+ u16 a, b, c, i;
+
+ a = readw(priv->regs + MSC_RES);
+ for (i = 0; i < 4; i++) {
+ b = readw(priv->regs + MSC_RES);
+ c = readw(priv->regs + MSC_RES);
+ cmd->response[i] =
+ (a << 24) | (b << 8) | (c >> 8);
+ a = c;
+ }
+ } else {
+ cmd->response[0] = readw(priv->regs + MSC_RES) << 24;
+ cmd->response[0] |= readw(priv->regs + MSC_RES) << 8;
+ cmd->response[0] |= readw(priv->regs + MSC_RES) & 0xff;
+ }
+ }
+
+ if (data && (data->flags & MMC_DATA_WRITE)) {
+ /* write the data */
+ int sz = DIV_ROUND_UP(data->blocks * data->blocksize, 4);
+ const void *buf = data->src;
+
+ while (sz--) {
+ u32 val = get_unaligned_le32(buf);
+
+ wait_for_bit_le32(priv->regs + MSC_IREG,
+ MSC_IREG_TXFIFO_WR_REQ,
+ true, 10000, false);
+ writel(val, priv->regs + MSC_TXFIFO);
+ buf += 4;
+ }
+ } else if (data && (data->flags & MMC_DATA_READ)) {
+ /* read the data */
+ int sz = data->blocks * data->blocksize;
+ void *buf = data->dest;
+
+ do {
+ stat = readl(priv->regs + MSC_STAT);
+
+ if (stat & MSC_STAT_TIME_OUT_READ)
+ return -ETIMEDOUT;
+ if (stat & MSC_STAT_CRC_READ_ERROR)
+ return -EINVAL;
+ if (stat & MSC_STAT_DATA_FIFO_EMPTY) {
+ udelay(10);
+ continue;
+ }
+ do {
+ u32 val = readl(priv->regs + MSC_RXFIFO);
+
+ if (sz == 1)
+ *(u8 *)buf = (u8)val;
+ else if (sz == 2)
+ put_unaligned_le16(val, buf);
+ else if (sz >= 4)
+ put_unaligned_le32(val, buf);
+ buf += 4;
+ sz -= 4;
+ stat = readl(priv->regs + MSC_STAT);
+ } while (!(stat & MSC_STAT_DATA_FIFO_EMPTY));
+ } while (!(stat & MSC_STAT_DATA_TRAN_DONE));
+ }
+
+ return 0;
+}
+
+static int jz_mmc_set_ios(struct mmc *mmc, struct jz_mmc_priv *priv)
+{
+ u32 real_rate = jz_mmc_clock_rate();
+ u8 clk_div = 0;
+
+ /* calculate clock divide */
+ while ((real_rate > mmc->clock) && (clk_div < 7)) {
+ real_rate >>= 1;
+ clk_div++;
+ }
+ writel(clk_div & MSC_CLKRT_CLK_RATE_MASK, priv->regs + MSC_CLKRT);
+
+ /* set the bus width for the next command */
+ priv->flags &= ~JZ_MMC_BUS_WIDTH_MASK;
+ if (mmc->bus_width == 8)
+ priv->flags |= JZ_MMC_BUS_WIDTH_8;
+ else if (mmc->bus_width == 4)
+ priv->flags |= JZ_MMC_BUS_WIDTH_4;
+ else
+ priv->flags |= JZ_MMC_BUS_WIDTH_1;
+
+ return 0;
+}
+
+static int jz_mmc_core_init(struct mmc *mmc)
+{
+ struct jz_mmc_priv *priv = mmc->priv;
+ int ret;
+
+ /* Reset */
+ writel(MSC_STRPCL_RESET, priv->regs + MSC_STRPCL);
+ ret = wait_for_bit_le32(priv->regs + MSC_STAT,
+ MSC_STAT_IS_RESETTING, false, 10000, false);
+ if (ret)
+ return ret;
+
+ /* Maximum timeouts */
+ writel(0xffff, priv->regs + MSC_RESTO);
+ writel(0xffffffff, priv->regs + MSC_RDTO);
+
+ /* Enable low power mode */
+ writel(0x1, priv->regs + MSC_LPM);
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_MMC)
+
+static int jz_mmc_legacy_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ struct jz_mmc_priv *priv = mmc->priv;
+
+ return jz_mmc_send_cmd(mmc, priv, cmd, data);
+}
+
+static int jz_mmc_legacy_set_ios(struct mmc *mmc)
+{
+ struct jz_mmc_priv *priv = mmc->priv;
+
+ return jz_mmc_set_ios(mmc, priv);
+};
+
+static const struct mmc_ops jz_msc_ops = {
+ .send_cmd = jz_mmc_legacy_send_cmd,
+ .set_ios = jz_mmc_legacy_set_ios,
+ .init = jz_mmc_core_init,
+};
+
+static struct jz_mmc_priv jz_mmc_priv_static;
+static struct jz_mmc_plat jz_mmc_plat_static = {
+ .cfg = {
+ .name = "MSC",
+ .ops = &jz_msc_ops,
+
+ .voltages = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 |
+ MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36,
+ .host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS,
+
+ .f_min = 375000,
+ .f_max = 48000000,
+ .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
+ },
+};
+
+int jz_mmc_init(void __iomem *base)
+{
+ struct mmc *mmc;
+
+ jz_mmc_priv_static.regs = base;
+
+ mmc = mmc_create(&jz_mmc_plat_static.cfg, &jz_mmc_priv_static);
+
+ return mmc ? 0 : -ENODEV;
+}
+
+#else /* CONFIG_DM_MMC */
+
+#include <dm.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+static int jz_mmc_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ struct jz_mmc_priv *priv = dev_get_priv(dev);
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+
+ return jz_mmc_send_cmd(mmc, priv, cmd, data);
+}
+
+static int jz_mmc_dm_set_ios(struct udevice *dev)
+{
+ struct jz_mmc_priv *priv = dev_get_priv(dev);
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+
+ return jz_mmc_set_ios(mmc, priv);
+};
+
+static const struct dm_mmc_ops jz_msc_ops = {
+ .send_cmd = jz_mmc_dm_send_cmd,
+ .set_ios = jz_mmc_dm_set_ios,
+};
+
+static int jz_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct jz_mmc_priv *priv = dev_get_priv(dev);
+ struct jz_mmc_plat *plat = dev_get_platdata(dev);
+ struct mmc_config *cfg;
+ int ret;
+
+ priv->regs = map_physmem(devfdt_get_addr(dev), 0x100, MAP_NOCACHE);
+ cfg = &plat->cfg;
+
+ cfg->name = "MSC";
+ cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+ ret = mmc_of_parse(dev, cfg);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse host caps\n");
+ return ret;
+ }
+
+ cfg->f_min = 400000;
+ cfg->f_max = 52000000;
+
+ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+ cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+
+ return 0;
+}
+
+static int jz_mmc_bind(struct udevice *dev)
+{
+ struct jz_mmc_plat *plat = dev_get_platdata(dev);
+
+ return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static int jz_mmc_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct jz_mmc_priv *priv = dev_get_priv(dev);
+ struct jz_mmc_plat *plat = dev_get_platdata(dev);
+
+ plat->mmc.priv = priv;
+ upriv->mmc = &plat->mmc;
+ return jz_mmc_core_init(&plat->mmc);
+}
+
+static const struct udevice_id jz_mmc_ids[] = {
+ { .compatible = "ingenic,jz4780-mmc" },
+ { }
+};
+
+U_BOOT_DRIVER(jz_mmc_drv) = {
+ .name = "jz_mmc",
+ .id = UCLASS_MMC,
+ .of_match = jz_mmc_ids,
+ .ofdata_to_platdata = jz_mmc_ofdata_to_platdata,
+ .bind = jz_mmc_bind,
+ .probe = jz_mmc_probe,
+ .priv_auto_alloc_size = sizeof(struct jz_mmc_priv),
+ .platdata_auto_alloc_size = sizeof(struct jz_mmc_plat),
+ .ops = &jz_msc_ops,
+};
+#endif /* CONFIG_DM_MMC */
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8fb365fc5d..7044c6adf3 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -72,6 +72,24 @@ config BCM_SF2_ETH_GMAC
by the BCM_SF2_ETH driver.
Say Y to any bcmcygnus based platforms.
+config BCM6348_ETH
+ bool "BCM6348 EMAC support"
+ depends on DM_ETH && ARCH_BMIPS
+ select DMA
+ select DMA_CHANNELS
+ select MII
+ select PHYLIB
+ help
+ This driver supports the BCM6348 Ethernet MAC.
+
+config BCM6368_ETH
+ bool "BCM6368 EMAC support"
+ depends on DM_ETH && ARCH_BMIPS
+ select DMA
+ select MII
+ help
+ This driver supports the BCM6368 Ethernet MAC.
+
config DWC_ETH_QOS
bool "Synopsys DWC Ethernet QOS device support"
depends on DM_ETH
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 99056aa041..0dbfa03306 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,8 @@
obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
obj-$(CONFIG_AG7XXX) += ag7xxx.o
obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o
+obj-$(CONFIG_BCM6348_ETH) += bcm6348-eth.o
+obj-$(CONFIG_BCM6368_ETH) += bcm6368-eth.o
obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o
obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
diff --git a/drivers/net/bcm6348-eth.c b/drivers/net/bcm6348-eth.c
new file mode 100644
index 0000000000..7100e68bd2
--- /dev/null
+++ b/drivers/net/bcm6348-eth.c
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dma.h>
+#include <miiphy.h>
+#include <net.h>
+#include <phy.h>
+#include <reset.h>
+#include <wait_bit.h>
+#include <asm/io.h>
+
+#define ETH_RX_DESC PKTBUFSRX
+#define ETH_MAX_MTU_SIZE 1518
+#define ETH_TIMEOUT 100
+#define ETH_TX_WATERMARK 32
+
+/* ETH Receiver Configuration register */
+#define ETH_RXCFG_REG 0x00
+#define ETH_RXCFG_ENFLOW_SHIFT 5
+#define ETH_RXCFG_ENFLOW_MASK (1 << ETH_RXCFG_ENFLOW_SHIFT)
+
+/* ETH Receive Maximum Length register */
+#define ETH_RXMAXLEN_REG 0x04
+#define ETH_RXMAXLEN_SHIFT 0
+#define ETH_RXMAXLEN_MASK (0x7ff << ETH_RXMAXLEN_SHIFT)
+
+/* ETH Transmit Maximum Length register */
+#define ETH_TXMAXLEN_REG 0x08
+#define ETH_TXMAXLEN_SHIFT 0
+#define ETH_TXMAXLEN_MASK (0x7ff << ETH_TXMAXLEN_SHIFT)
+
+/* MII Status/Control register */
+#define MII_SC_REG 0x10
+#define MII_SC_MDCFREQDIV_SHIFT 0
+#define MII_SC_MDCFREQDIV_MASK (0x7f << MII_SC_MDCFREQDIV_SHIFT)
+#define MII_SC_PREAMBLE_EN_SHIFT 7
+#define MII_SC_PREAMBLE_EN_MASK (1 << MII_SC_PREAMBLE_EN_SHIFT)
+
+/* MII Data register */
+#define MII_DAT_REG 0x14
+#define MII_DAT_DATA_SHIFT 0
+#define MII_DAT_DATA_MASK (0xffff << MII_DAT_DATA_SHIFT)
+#define MII_DAT_TA_SHIFT 16
+#define MII_DAT_TA_MASK (0x3 << MII_DAT_TA_SHIFT)
+#define MII_DAT_REG_SHIFT 18
+#define MII_DAT_REG_MASK (0x1f << MII_DAT_REG_SHIFT)
+#define MII_DAT_PHY_SHIFT 23
+#define MII_DAT_PHY_MASK (0x1f << MII_DAT_PHY_SHIFT)
+#define MII_DAT_OP_SHIFT 28
+#define MII_DAT_OP_WRITE (0x5 << MII_DAT_OP_SHIFT)
+#define MII_DAT_OP_READ (0x6 << MII_DAT_OP_SHIFT)
+
+/* ETH Interrupts Mask register */
+#define ETH_IRMASK_REG 0x18
+
+/* ETH Interrupts register */
+#define ETH_IR_REG 0x1c
+#define ETH_IR_MII_SHIFT 0
+#define ETH_IR_MII_MASK (1 << ETH_IR_MII_SHIFT)
+
+/* ETH Control register */
+#define ETH_CTL_REG 0x2c
+#define ETH_CTL_ENABLE_SHIFT 0
+#define ETH_CTL_ENABLE_MASK (1 << ETH_CTL_ENABLE_SHIFT)
+#define ETH_CTL_DISABLE_SHIFT 1
+#define ETH_CTL_DISABLE_MASK (1 << ETH_CTL_DISABLE_SHIFT)
+#define ETH_CTL_RESET_SHIFT 2
+#define ETH_CTL_RESET_MASK (1 << ETH_CTL_RESET_SHIFT)
+#define ETH_CTL_EPHY_SHIFT 3
+#define ETH_CTL_EPHY_MASK (1 << ETH_CTL_EPHY_SHIFT)
+
+/* ETH Transmit Control register */
+#define ETH_TXCTL_REG 0x30
+#define ETH_TXCTL_FD_SHIFT 0
+#define ETH_TXCTL_FD_MASK (1 << ETH_TXCTL_FD_SHIFT)
+
+/* ETH Transmit Watermask register */
+#define ETH_TXWMARK_REG 0x34
+#define ETH_TXWMARK_WM_SHIFT 0
+#define ETH_TXWMARK_WM_MASK (0x3f << ETH_TXWMARK_WM_SHIFT)
+
+/* MIB Control register */
+#define MIB_CTL_REG 0x38
+#define MIB_CTL_RDCLEAR_SHIFT 0
+#define MIB_CTL_RDCLEAR_MASK (1 << MIB_CTL_RDCLEAR_SHIFT)
+
+/* ETH Perfect Match registers */
+#define ETH_PM_CNT 4
+#define ETH_PML_REG(x) (0x58 + (x) * 0x8)
+#define ETH_PMH_REG(x) (0x5c + (x) * 0x8)
+#define ETH_PMH_VALID_SHIFT 16
+#define ETH_PMH_VALID_MASK (1 << ETH_PMH_VALID_SHIFT)
+
+/* MIB Counters registers */
+#define MIB_REG_CNT 55
+#define MIB_REG(x) (0x200 + (x) * 4)
+
+/* ETH data */
+struct bcm6348_eth_priv {
+ void __iomem *base;
+ /* DMA */
+ struct dma rx_dma;
+ struct dma tx_dma;
+ /* PHY */
+ int phy_id;
+ struct phy_device *phy_dev;
+};
+
+static void bcm6348_eth_mac_disable(struct bcm6348_eth_priv *priv)
+{
+ /* disable emac */
+ clrsetbits_be32(priv->base + ETH_CTL_REG, ETH_CTL_ENABLE_MASK,
+ ETH_CTL_DISABLE_MASK);
+
+ /* wait until emac is disabled */
+ if (wait_for_bit_be32(priv->base + ETH_CTL_REG,
+ ETH_CTL_DISABLE_MASK, false,
+ ETH_TIMEOUT, false))
+ pr_err("%s: error disabling emac\n", __func__);
+}
+
+static void bcm6348_eth_mac_enable(struct bcm6348_eth_priv *priv)
+{
+ setbits_be32(priv->base + ETH_CTL_REG, ETH_CTL_ENABLE_MASK);
+}
+
+static void bcm6348_eth_mac_reset(struct bcm6348_eth_priv *priv)
+{
+ /* reset emac */
+ writel_be(ETH_CTL_RESET_MASK, priv->base + ETH_CTL_REG);
+ wmb();
+
+ /* wait until emac is reset */
+ if (wait_for_bit_be32(priv->base + ETH_CTL_REG,
+ ETH_CTL_RESET_MASK, false,
+ ETH_TIMEOUT, false))
+ pr_err("%s: error resetting emac\n", __func__);
+}
+
+static int bcm6348_eth_free_pkt(struct udevice *dev, uchar *packet, int len)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+
+ return dma_prepare_rcv_buf(&priv->rx_dma, packet, len);
+}
+
+static int bcm6348_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+
+ return dma_receive(&priv->rx_dma, (void**)packetp, NULL);
+}
+
+static int bcm6348_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+
+ return dma_send(&priv->tx_dma, packet, length, NULL);
+}
+
+static int bcm6348_eth_adjust_link(struct udevice *dev,
+ struct phy_device *phydev)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+
+ /* mac duplex parameters */
+ if (phydev->duplex)
+ setbits_be32(priv->base + ETH_TXCTL_REG, ETH_TXCTL_FD_MASK);
+ else
+ clrbits_be32(priv->base + ETH_TXCTL_REG, ETH_TXCTL_FD_MASK);
+
+ /* rx flow control (pause frame handling) */
+ if (phydev->pause)
+ setbits_be32(priv->base + ETH_RXCFG_REG,
+ ETH_RXCFG_ENFLOW_MASK);
+ else
+ clrbits_be32(priv->base + ETH_RXCFG_REG,
+ ETH_RXCFG_ENFLOW_MASK);
+
+ return 0;
+}
+
+static int bcm6348_eth_start(struct udevice *dev)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+ int ret, i;
+
+ /* prepare rx dma buffers */
+ for (i = 0; i < ETH_RX_DESC; i++) {
+ ret = dma_prepare_rcv_buf(&priv->rx_dma, net_rx_packets[i],
+ PKTSIZE_ALIGN);
+ if (ret < 0)
+ break;
+ }
+
+ /* enable dma rx channel */
+ dma_enable(&priv->rx_dma);
+
+ /* enable dma tx channel */
+ dma_enable(&priv->tx_dma);
+
+ ret = phy_startup(priv->phy_dev);
+ if (ret) {
+ pr_err("%s: could not initialize phy\n", __func__);
+ return ret;
+ }
+
+ if (!priv->phy_dev->link) {
+ pr_err("%s: no phy link\n", __func__);
+ return -EIO;
+ }
+
+ bcm6348_eth_adjust_link(dev, priv->phy_dev);
+
+ /* zero mib counters */
+ for (i = 0; i < MIB_REG_CNT; i++)
+ writel_be(0, MIB_REG(i));
+
+ /* enable rx flow control */
+ setbits_be32(priv->base + ETH_RXCFG_REG, ETH_RXCFG_ENFLOW_MASK);
+
+ /* set max rx/tx length */
+ writel_be((ETH_MAX_MTU_SIZE << ETH_RXMAXLEN_SHIFT) &
+ ETH_RXMAXLEN_MASK, priv->base + ETH_RXMAXLEN_REG);
+ writel_be((ETH_MAX_MTU_SIZE << ETH_TXMAXLEN_SHIFT) &
+ ETH_TXMAXLEN_MASK, priv->base + ETH_TXMAXLEN_REG);
+
+ /* set correct transmit fifo watermark */
+ writel_be((ETH_TX_WATERMARK << ETH_TXWMARK_WM_SHIFT) &
+ ETH_TXWMARK_WM_MASK, priv->base + ETH_TXWMARK_REG);
+
+ /* enable emac */
+ bcm6348_eth_mac_enable(priv);
+
+ /* clear interrupts */
+ writel_be(0, priv->base + ETH_IRMASK_REG);
+
+ return 0;
+}
+
+static void bcm6348_eth_stop(struct udevice *dev)
+{
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+
+ /* disable dma rx channel */
+ dma_disable(&priv->rx_dma);
+
+ /* disable dma tx channel */
+ dma_disable(&priv->tx_dma);
+
+ /* disable emac */
+ bcm6348_eth_mac_disable(priv);
+}
+
+static int bcm6348_eth_write_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+ bool running = false;
+
+ /* check if emac is running */
+ if (readl_be(priv->base + ETH_CTL_REG) & ETH_CTL_ENABLE_MASK)
+ running = true;
+
+ /* disable emac */
+ if (running)
+ bcm6348_eth_mac_disable(priv);
+
+ /* set mac address */
+ writel_be((pdata->enetaddr[2] << 24) | (pdata->enetaddr[3]) << 16 |
+ (pdata->enetaddr[4]) << 8 | (pdata->enetaddr[5]),
+ priv->base + ETH_PML_REG(0));
+ writel_be((pdata->enetaddr[1]) | (pdata->enetaddr[0] << 8) |
+ ETH_PMH_VALID_MASK, priv->base + ETH_PMH_REG(0));
+
+ /* enable emac */
+ if (running)
+ bcm6348_eth_mac_enable(priv);
+
+ return 0;
+}
+
+static const struct eth_ops bcm6348_eth_ops = {
+ .free_pkt = bcm6348_eth_free_pkt,
+ .recv = bcm6348_eth_recv,
+ .send = bcm6348_eth_send,
+ .start = bcm6348_eth_start,
+ .stop = bcm6348_eth_stop,
+ .write_hwaddr = bcm6348_eth_write_hwaddr,
+};
+
+static const struct udevice_id bcm6348_eth_ids[] = {
+ { .compatible = "brcm,bcm6348-enet", },
+ { /* sentinel */ }
+};
+
+static int bcm6348_mdio_op(void __iomem *base, uint32_t data)
+{
+ /* make sure mii interrupt status is cleared */
+ writel_be(ETH_IR_MII_MASK, base + ETH_IR_REG);
+
+ /* issue mii op */
+ writel_be(data, base + MII_DAT_REG);
+
+ /* wait until emac is disabled */
+ return wait_for_bit_be32(base + ETH_IR_REG,
+ ETH_IR_MII_MASK, true,
+ ETH_TIMEOUT, false);
+}
+
+static int bcm6348_mdio_read(struct mii_dev *bus, int addr, int devaddr,
+ int reg)
+{
+ void __iomem *base = bus->priv;
+ uint32_t val;
+
+ val = MII_DAT_OP_READ;
+ val |= (reg << MII_DAT_REG_SHIFT) & MII_DAT_REG_MASK;
+ val |= (0x2 << MII_DAT_TA_SHIFT) & MII_DAT_TA_MASK;
+ val |= (addr << MII_DAT_PHY_SHIFT) & MII_DAT_PHY_MASK;
+
+ if (bcm6348_mdio_op(base, val)) {
+ pr_err("%s: timeout\n", __func__);
+ return -EINVAL;
+ }
+
+ val = readl_be(base + MII_DAT_REG) & MII_DAT_DATA_MASK;
+ val >>= MII_DAT_DATA_SHIFT;
+
+ return val;
+}
+
+static int bcm6348_mdio_write(struct mii_dev *bus, int addr, int dev_addr,
+ int reg, u16 value)
+{
+ void __iomem *base = bus->priv;
+ uint32_t val;
+
+ val = MII_DAT_OP_WRITE;
+ val |= (reg << MII_DAT_REG_SHIFT) & MII_DAT_REG_MASK;
+ val |= (0x2 << MII_DAT_TA_SHIFT) & MII_DAT_TA_MASK;
+ val |= (addr << MII_DAT_PHY_SHIFT) & MII_DAT_PHY_MASK;
+ val |= (value << MII_DAT_DATA_SHIFT) & MII_DAT_DATA_MASK;
+
+ if (bcm6348_mdio_op(base, val)) {
+ pr_err("%s: timeout\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bcm6348_mdio_init(const char *name, void __iomem *base)
+{
+ struct mii_dev *bus;
+
+ bus = mdio_alloc();
+ if (!bus) {
+ pr_err("%s: failed to allocate MDIO bus\n", __func__);
+ return -ENOMEM;
+ }
+
+ bus->read = bcm6348_mdio_read;
+ bus->write = bcm6348_mdio_write;
+ bus->priv = base;
+ snprintf(bus->name, sizeof(bus->name), "%s", name);
+
+ return mdio_register(bus);
+}
+
+static int bcm6348_phy_init(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+ struct mii_dev *bus;
+
+ /* get mii bus */
+ bus = miiphy_get_dev_by_name(dev->name);
+
+ /* phy connect */
+ priv->phy_dev = phy_connect(bus, priv->phy_id, dev,
+ pdata->phy_interface);
+ if (!priv->phy_dev) {
+ pr_err("%s: no phy device\n", __func__);
+ return -ENODEV;
+ }
+
+ priv->phy_dev->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_Pause |
+ SUPPORTED_MII);
+ priv->phy_dev->advertising = priv->phy_dev->supported;
+
+ /* phy config */
+ phy_config(priv->phy_dev);
+
+ return 0;
+}
+
+static int bcm6348_eth_probe(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcm6348_eth_priv *priv = dev_get_priv(dev);
+ struct ofnode_phandle_args phy;
+ const char *phy_mode;
+ int ret, i;
+
+ /* get base address */
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+ pdata->iobase = (phys_addr_t) priv->base;
+
+ /* get phy mode */
+ pdata->phy_interface = PHY_INTERFACE_MODE_NONE;
+ phy_mode = dev_read_string(dev, "phy-mode");
+ if (phy_mode)
+ pdata->phy_interface = phy_get_interface_by_name(phy_mode);
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_NONE)
+ return -ENODEV;
+
+ /* get phy */
+ if (dev_read_phandle_with_args(dev, "phy", NULL, 0, 0, &phy))
+ return -ENOENT;
+ priv->phy_id = ofnode_read_u32_default(phy.node, "reg", -1);
+
+ /* get dma channels */
+ ret = dma_get_by_name(dev, "tx", &priv->tx_dma);
+ if (ret)
+ return -EINVAL;
+
+ ret = dma_get_by_name(dev, "rx", &priv->rx_dma);
+ if (ret)
+ return -EINVAL;
+
+ /* try to enable clocks */
+ for (i = 0; ; i++) {
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_index(dev, i, &clk);
+ if (ret < 0)
+ break;
+
+ ret = clk_enable(&clk);
+ if (ret < 0) {
+ pr_err("%s: error enabling clock %d\n", __func__, i);
+ return ret;
+ }
+
+ ret = clk_free(&clk);
+ if (ret < 0) {
+ pr_err("%s: error freeing clock %d\n", __func__, i);
+ return ret;
+ }
+ }
+
+ /* try to perform resets */
+ for (i = 0; ; i++) {
+ struct reset_ctl reset;
+ int ret;
+
+ ret = reset_get_by_index(dev, i, &reset);
+ if (ret < 0)
+ break;
+
+ ret = reset_deassert(&reset);
+ if (ret < 0) {
+ pr_err("%s: error deasserting reset %d\n", __func__, i);
+ return ret;
+ }
+
+ ret = reset_free(&reset);
+ if (ret < 0) {
+ pr_err("%s: error freeing reset %d\n", __func__, i);
+ return ret;
+ }
+ }
+
+ /* disable emac */
+ bcm6348_eth_mac_disable(priv);
+
+ /* reset emac */
+ bcm6348_eth_mac_reset(priv);
+
+ /* select correct mii interface */
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
+ clrbits_be32(priv->base + ETH_CTL_REG, ETH_CTL_EPHY_MASK);
+ else
+ setbits_be32(priv->base + ETH_CTL_REG, ETH_CTL_EPHY_MASK);
+
+ /* turn on mdc clock */
+ writel_be((0x1f << MII_SC_MDCFREQDIV_SHIFT) |
+ MII_SC_PREAMBLE_EN_MASK, priv->base + MII_SC_REG);
+
+ /* set mib counters to not clear when read */
+ clrbits_be32(priv->base + MIB_CTL_REG, MIB_CTL_RDCLEAR_MASK);
+
+ /* initialize perfect match registers */
+ for (i = 0; i < ETH_PM_CNT; i++) {
+ writel_be(0, priv->base + ETH_PML_REG(i));
+ writel_be(0, priv->base + ETH_PMH_REG(i));
+ }
+
+ /* init mii bus */
+ ret = bcm6348_mdio_init(dev->name, priv->base);
+ if (ret)
+ return ret;
+
+ /* init phy */
+ ret = bcm6348_phy_init(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(bcm6348_eth) = {
+ .name = "bcm6348_eth",
+ .id = UCLASS_ETH,
+ .of_match = bcm6348_eth_ids,
+ .ops = &bcm6348_eth_ops,
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+ .priv_auto_alloc_size = sizeof(struct bcm6348_eth_priv),
+ .probe = bcm6348_eth_probe,
+};
diff --git a/drivers/net/bcm6368-eth.c b/drivers/net/bcm6368-eth.c
new file mode 100644
index 0000000000..a31efba9d1
--- /dev/null
+++ b/drivers/net/bcm6368-eth.c
@@ -0,0 +1,625 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dma.h>
+#include <miiphy.h>
+#include <net.h>
+#include <reset.h>
+#include <wait_bit.h>
+#include <asm/io.h>
+
+#define ETH_PORT_STR "brcm,enetsw-port"
+
+#define ETH_RX_DESC PKTBUFSRX
+#define ETH_ZLEN 60
+#define ETH_TIMEOUT 100
+
+#define ETH_MAX_PORT 8
+#define ETH_RGMII_PORT0 4
+
+/* Port traffic control */
+#define ETH_PTCTRL_REG(x) (0x0 + (x))
+#define ETH_PTCTRL_RXDIS_SHIFT 0
+#define ETH_PTCTRL_RXDIS_MASK (1 << ETH_PTCTRL_RXDIS_SHIFT)
+#define ETH_PTCTRL_TXDIS_SHIFT 1
+#define ETH_PTCTRL_TXDIS_MASK (1 << ETH_PTCTRL_TXDIS_SHIFT)
+
+/* Switch mode register */
+#define ETH_SWMODE_REG 0xb
+#define ETH_SWMODE_FWD_EN_SHIFT 1
+#define ETH_SWMODE_FWD_EN_MASK (1 << ETH_SWMODE_FWD_EN_SHIFT)
+
+/* IMP override Register */
+#define ETH_IMPOV_REG 0xe
+#define ETH_IMPOV_LINKUP_SHIFT 0
+#define ETH_IMPOV_LINKUP_MASK (1 << ETH_IMPOV_LINKUP_SHIFT)
+#define ETH_IMPOV_FDX_SHIFT 1
+#define ETH_IMPOV_FDX_MASK (1 << ETH_IMPOV_FDX_SHIFT)
+#define ETH_IMPOV_100_SHIFT 2
+#define ETH_IMPOV_100_MASK (1 << ETH_IMPOV_100_SHIFT)
+#define ETH_IMPOV_1000_SHIFT 3
+#define ETH_IMPOV_1000_MASK (1 << ETH_IMPOV_1000_SHIFT)
+#define ETH_IMPOV_RXFLOW_SHIFT 4
+#define ETH_IMPOV_RXFLOW_MASK (1 << ETH_IMPOV_RXFLOW_SHIFT)
+#define ETH_IMPOV_TXFLOW_SHIFT 5
+#define ETH_IMPOV_TXFLOW_MASK (1 << ETH_IMPOV_TXFLOW_SHIFT)
+#define ETH_IMPOV_FORCE_SHIFT 7
+#define ETH_IMPOV_FORCE_MASK (1 << ETH_IMPOV_FORCE_SHIFT)
+
+/* Port override Register */
+#define ETH_PORTOV_REG(x) (0x58 + (x))
+#define ETH_PORTOV_LINKUP_SHIFT 0
+#define ETH_PORTOV_LINKUP_MASK (1 << ETH_PORTOV_LINKUP_SHIFT)
+#define ETH_PORTOV_FDX_SHIFT 1
+#define ETH_PORTOV_FDX_MASK (1 << ETH_PORTOV_FDX_SHIFT)
+#define ETH_PORTOV_100_SHIFT 2
+#define ETH_PORTOV_100_MASK (1 << ETH_PORTOV_100_SHIFT)
+#define ETH_PORTOV_1000_SHIFT 3
+#define ETH_PORTOV_1000_MASK (1 << ETH_PORTOV_1000_SHIFT)
+#define ETH_PORTOV_RXFLOW_SHIFT 4
+#define ETH_PORTOV_RXFLOW_MASK (1 << ETH_PORTOV_RXFLOW_SHIFT)
+#define ETH_PORTOV_TXFLOW_SHIFT 5
+#define ETH_PORTOV_TXFLOW_MASK (1 << ETH_PORTOV_TXFLOW_SHIFT)
+#define ETH_PORTOV_ENABLE_SHIFT 6
+#define ETH_PORTOV_ENABLE_MASK (1 << ETH_PORTOV_ENABLE_SHIFT)
+
+/* Port RGMII control register */
+#define ETH_RGMII_CTRL_REG(x) (0x60 + (x))
+#define ETH_RGMII_CTRL_GMII_CLK_EN (1 << 7)
+#define ETH_RGMII_CTRL_MII_OVERRIDE_EN (1 << 6)
+#define ETH_RGMII_CTRL_MII_MODE_MASK (3 << 4)
+#define ETH_RGMII_CTRL_RGMII_MODE (0 << 4)
+#define ETH_RGMII_CTRL_MII_MODE (1 << 4)
+#define ETH_RGMII_CTRL_RVMII_MODE (2 << 4)
+#define ETH_RGMII_CTRL_TIMING_SEL_EN (1 << 0)
+
+/* Port RGMII timing register */
+#define ENETSW_RGMII_TIMING_REG(x) (0x68 + (x))
+
+/* MDIO control register */
+#define MII_SC_REG 0xb0
+#define MII_SC_EXT_SHIFT 16
+#define MII_SC_EXT_MASK (1 << MII_SC_EXT_SHIFT)
+#define MII_SC_REG_SHIFT 20
+#define MII_SC_PHYID_SHIFT 25
+#define MII_SC_RD_SHIFT 30
+#define MII_SC_RD_MASK (1 << MII_SC_RD_SHIFT)
+#define MII_SC_WR_SHIFT 31
+#define MII_SC_WR_MASK (1 << MII_SC_WR_SHIFT)
+
+/* MDIO data register */
+#define MII_DAT_REG 0xb4
+
+/* Global Management Configuration Register */
+#define ETH_GMCR_REG 0x200
+#define ETH_GMCR_RST_MIB_SHIFT 0
+#define ETH_GMCR_RST_MIB_MASK (1 << ETH_GMCR_RST_MIB_SHIFT)
+
+/* Jumbo control register port mask register */
+#define ETH_JMBCTL_PORT_REG 0x4004
+
+/* Jumbo control mib good frame register */
+#define ETH_JMBCTL_MAXSIZE_REG 0x4008
+
+/* ETH port data */
+struct bcm_enetsw_port {
+ bool used;
+ const char *name;
+ /* Config */
+ bool bypass_link;
+ int force_speed;
+ bool force_duplex_full;
+ /* PHY */
+ int phy_id;
+};
+
+/* ETH data */
+struct bcm6368_eth_priv {
+ void __iomem *base;
+ /* DMA */
+ struct dma rx_dma;
+ struct dma tx_dma;
+ /* Ports */
+ uint8_t num_ports;
+ struct bcm_enetsw_port used_ports[ETH_MAX_PORT];
+ int sw_port_link[ETH_MAX_PORT];
+ bool rgmii_override;
+ bool rgmii_timing;
+ /* PHY */
+ int phy_id;
+};
+
+static inline bool bcm_enet_port_is_rgmii(int portid)
+{
+ return portid >= ETH_RGMII_PORT0;
+}
+
+static int bcm6368_mdio_read(struct bcm6368_eth_priv *priv, uint8_t ext,
+ int phy_id, int reg)
+{
+ uint32_t val;
+
+ writel_be(0, priv->base + MII_SC_REG);
+
+ val = MII_SC_RD_MASK |
+ (phy_id << MII_SC_PHYID_SHIFT) |
+ (reg << MII_SC_REG_SHIFT);
+
+ if (ext)
+ val |= MII_SC_EXT_MASK;
+
+ writel_be(val, priv->base + MII_SC_REG);
+ udelay(50);
+
+ return readw_be(priv->base + MII_DAT_REG);
+}
+
+static int bcm6368_mdio_write(struct bcm6368_eth_priv *priv, uint8_t ext,
+ int phy_id, int reg, u16 data)
+{
+ uint32_t val;
+
+ writel_be(0, priv->base + MII_SC_REG);
+
+ val = MII_SC_WR_MASK |
+ (phy_id << MII_SC_PHYID_SHIFT) |
+ (reg << MII_SC_REG_SHIFT);
+
+ if (ext)
+ val |= MII_SC_EXT_MASK;
+
+ val |= data;
+
+ writel_be(val, priv->base + MII_SC_REG);
+ udelay(50);
+
+ return 0;
+}
+
+static int bcm6368_eth_free_pkt(struct udevice *dev, uchar *packet, int len)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+
+ return dma_prepare_rcv_buf(&priv->rx_dma, packet, len);
+}
+
+static int bcm6368_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+
+ return dma_receive(&priv->rx_dma, (void**)packetp, NULL);
+}
+
+static int bcm6368_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+
+ /* pad packets smaller than ETH_ZLEN */
+ if (length < ETH_ZLEN) {
+ memset(packet + length, 0, ETH_ZLEN - length);
+ length = ETH_ZLEN;
+ }
+
+ return dma_send(&priv->tx_dma, packet, length, NULL);
+}
+
+static int bcm6368_eth_adjust_link(struct udevice *dev)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+ unsigned int i;
+
+ for (i = 0; i < priv->num_ports; i++) {
+ struct bcm_enetsw_port *port;
+ int val, j, up, adv, lpa, speed, duplex, media;
+ int external_phy = bcm_enet_port_is_rgmii(i);
+ u8 override;
+
+ port = &priv->used_ports[i];
+ if (!port->used)
+ continue;
+
+ if (port->bypass_link)
+ continue;
+
+ /* dummy read to clear */
+ for (j = 0; j < 2; j++)
+ val = bcm6368_mdio_read(priv, external_phy,
+ port->phy_id, MII_BMSR);
+
+ if (val == 0xffff)
+ continue;
+
+ up = (val & BMSR_LSTATUS) ? 1 : 0;
+ if (!(up ^ priv->sw_port_link[i]))
+ continue;
+
+ priv->sw_port_link[i] = up;
+
+ /* link changed */
+ if (!up) {
+ dev_info(&priv->pdev->dev, "link DOWN on %s\n",
+ port->name);
+ writeb_be(ETH_PORTOV_ENABLE_MASK,
+ priv->base + ETH_PORTOV_REG(i));
+ writeb_be(ETH_PTCTRL_RXDIS_MASK |
+ ETH_PTCTRL_TXDIS_MASK,
+ priv->base + ETH_PTCTRL_REG(i));
+ continue;
+ }
+
+ adv = bcm6368_mdio_read(priv, external_phy,
+ port->phy_id, MII_ADVERTISE);
+
+ lpa = bcm6368_mdio_read(priv, external_phy, port->phy_id,
+ MII_LPA);
+
+ /* figure out media and duplex from advertise and LPA values */
+ media = mii_nway_result(lpa & adv);
+ duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+
+ if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF))
+ speed = 100;
+ else
+ speed = 10;
+
+ if (val & BMSR_ESTATEN) {
+ adv = bcm6368_mdio_read(priv, external_phy,
+ port->phy_id, MII_CTRL1000);
+
+ lpa = bcm6368_mdio_read(priv, external_phy,
+ port->phy_id, MII_STAT1000);
+
+ if ((adv & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) &&
+ (lpa & (LPA_1000FULL | LPA_1000HALF))) {
+ speed = 1000;
+ duplex = (lpa & LPA_1000FULL);
+ }
+ }
+
+ pr_alert("link UP on %s, %dMbps, %s-duplex\n",
+ port->name, speed, duplex ? "full" : "half");
+
+ override = ETH_PORTOV_ENABLE_MASK |
+ ETH_PORTOV_LINKUP_MASK;
+
+ if (speed == 1000)
+ override |= ETH_PORTOV_1000_MASK;
+ else if (speed == 100)
+ override |= ETH_PORTOV_100_MASK;
+ if (duplex)
+ override |= ETH_PORTOV_FDX_MASK;
+
+ writeb_be(override, priv->base + ETH_PORTOV_REG(i));
+ writeb_be(0, priv->base + ETH_PTCTRL_REG(i));
+ }
+
+ return 0;
+}
+
+static int bcm6368_eth_start(struct udevice *dev)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+ uint8_t i;
+
+ /* prepare rx dma buffers */
+ for (i = 0; i < ETH_RX_DESC; i++) {
+ int ret = dma_prepare_rcv_buf(&priv->rx_dma, net_rx_packets[i],
+ PKTSIZE_ALIGN);
+ if (ret < 0)
+ break;
+ }
+
+ /* enable dma rx channel */
+ dma_enable(&priv->rx_dma);
+
+ /* enable dma tx channel */
+ dma_enable(&priv->tx_dma);
+
+ /* apply override config for bypass_link ports here. */
+ for (i = 0; i < priv->num_ports; i++) {
+ struct bcm_enetsw_port *port;
+ u8 override;
+
+ port = &priv->used_ports[i];
+ if (!port->used)
+ continue;
+
+ if (!port->bypass_link)
+ continue;
+
+ override = ETH_PORTOV_ENABLE_MASK |
+ ETH_PORTOV_LINKUP_MASK;
+
+ switch (port->force_speed) {
+ case 1000:
+ override |= ETH_PORTOV_1000_MASK;
+ break;
+ case 100:
+ override |= ETH_PORTOV_100_MASK;
+ break;
+ case 10:
+ break;
+ default:
+ pr_warn("%s: invalid forced speed on port %s\n",
+ __func__, port->name);
+ break;
+ }
+
+ if (port->force_duplex_full)
+ override |= ETH_PORTOV_FDX_MASK;
+
+ writeb_be(override, priv->base + ETH_PORTOV_REG(i));
+ writeb_be(0, priv->base + ETH_PTCTRL_REG(i));
+ }
+
+ bcm6368_eth_adjust_link(dev);
+
+ return 0;
+}
+
+static void bcm6368_eth_stop(struct udevice *dev)
+{
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+
+ /* disable dma rx channel */
+ dma_disable(&priv->rx_dma);
+
+ /* disable dma tx channel */
+ dma_disable(&priv->tx_dma);
+}
+
+static const struct eth_ops bcm6368_eth_ops = {
+ .free_pkt = bcm6368_eth_free_pkt,
+ .recv = bcm6368_eth_recv,
+ .send = bcm6368_eth_send,
+ .start = bcm6368_eth_start,
+ .stop = bcm6368_eth_stop,
+};
+
+static const struct udevice_id bcm6368_eth_ids[] = {
+ { .compatible = "brcm,bcm6368-enet", },
+ { /* sentinel */ }
+};
+
+static bool bcm6368_phy_is_external(struct bcm6368_eth_priv *priv, int phy_id)
+{
+ uint8_t i;
+
+ for (i = 0; i < priv->num_ports; ++i) {
+ if (!priv->used_ports[i].used)
+ continue;
+ if (priv->used_ports[i].phy_id == phy_id)
+ return bcm_enet_port_is_rgmii(i);
+ }
+
+ return true;
+}
+
+static int bcm6368_mii_mdio_read(struct mii_dev *bus, int addr, int devaddr,
+ int reg)
+{
+ struct bcm6368_eth_priv *priv = bus->priv;
+ bool ext = bcm6368_phy_is_external(priv, addr);
+
+ return bcm6368_mdio_read(priv, ext, addr, reg);
+}
+
+static int bcm6368_mii_mdio_write(struct mii_dev *bus, int addr, int devaddr,
+ int reg, u16 data)
+{
+ struct bcm6368_eth_priv *priv = bus->priv;
+ bool ext = bcm6368_phy_is_external(priv, addr);
+
+ return bcm6368_mdio_write(priv, ext, addr, reg, data);
+}
+
+static int bcm6368_mdio_init(const char *name, struct bcm6368_eth_priv *priv)
+{
+ struct mii_dev *bus;
+
+ bus = mdio_alloc();
+ if (!bus) {
+ pr_err("%s: failed to allocate MDIO bus\n", __func__);
+ return -ENOMEM;
+ }
+
+ bus->read = bcm6368_mii_mdio_read;
+ bus->write = bcm6368_mii_mdio_write;
+ bus->priv = priv;
+ snprintf(bus->name, sizeof(bus->name), "%s", name);
+
+ return mdio_register(bus);
+}
+
+static int bcm6368_eth_probe(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct bcm6368_eth_priv *priv = dev_get_priv(dev);
+ int num_ports, ret, i;
+ uint32_t val;
+ ofnode node;
+
+ /* get base address */
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+ pdata->iobase = (phys_addr_t) priv->base;
+
+ /* get number of ports */
+ num_ports = dev_read_u32_default(dev, "brcm,num-ports", ETH_MAX_PORT);
+ if (!num_ports || num_ports > ETH_MAX_PORT)
+ return -EINVAL;
+
+ /* get dma channels */
+ ret = dma_get_by_name(dev, "tx", &priv->tx_dma);
+ if (ret)
+ return -EINVAL;
+
+ ret = dma_get_by_name(dev, "rx", &priv->rx_dma);
+ if (ret)
+ return -EINVAL;
+
+ /* try to enable clocks */
+ for (i = 0; ; i++) {
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_index(dev, i, &clk);
+ if (ret < 0)
+ break;
+
+ ret = clk_enable(&clk);
+ if (ret < 0) {
+ pr_err("%s: error enabling clock %d\n", __func__, i);
+ return ret;
+ }
+
+ ret = clk_free(&clk);
+ if (ret < 0) {
+ pr_err("%s: error freeing clock %d\n", __func__, i);
+ return ret;
+ }
+ }
+
+ /* try to perform resets */
+ for (i = 0; ; i++) {
+ struct reset_ctl reset;
+ int ret;
+
+ ret = reset_get_by_index(dev, i, &reset);
+ if (ret < 0)
+ break;
+
+ ret = reset_deassert(&reset);
+ if (ret < 0) {
+ pr_err("%s: error deasserting reset %d\n", __func__, i);
+ return ret;
+ }
+
+ ret = reset_free(&reset);
+ if (ret < 0) {
+ pr_err("%s: error freeing reset %d\n", __func__, i);
+ return ret;
+ }
+ }
+
+ /* set priv data */
+ priv->num_ports = num_ports;
+ if (dev_read_bool(dev, "brcm,rgmii-override"))
+ priv->rgmii_override = true;
+ if (dev_read_bool(dev, "brcm,rgmii-timing"))
+ priv->rgmii_timing = true;
+
+ /* get ports */
+ dev_for_each_subnode(node, dev) {
+ const char *comp;
+ const char *label;
+ unsigned int p;
+ int phy_id;
+ int speed;
+
+ comp = ofnode_read_string(node, "compatible");
+ if (!comp || memcmp(comp, ETH_PORT_STR, sizeof(ETH_PORT_STR)))
+ continue;
+
+ p = ofnode_read_u32_default(node, "reg", ETH_MAX_PORT);
+ if (p >= num_ports)
+ return -EINVAL;
+
+ label = ofnode_read_string(node, "label");
+ if (!label) {
+ debug("%s: node %s has no label\n", __func__,
+ ofnode_get_name(node));
+ return -EINVAL;
+ }
+
+ phy_id = ofnode_read_u32_default(node, "brcm,phy-id", -1);
+
+ priv->used_ports[p].used = true;
+ priv->used_ports[p].name = label;
+ priv->used_ports[p].phy_id = phy_id;
+
+ if (ofnode_read_bool(node, "full-duplex"))
+ priv->used_ports[p].force_duplex_full = true;
+ if (ofnode_read_bool(node, "bypass-link"))
+ priv->used_ports[p].bypass_link = true;
+ speed = ofnode_read_u32_default(node, "speed", 0);
+ if (speed)
+ priv->used_ports[p].force_speed = speed;
+ }
+
+ /* init mii bus */
+ ret = bcm6368_mdio_init(dev->name, priv);
+ if (ret)
+ return ret;
+
+ /* disable all ports */
+ for (i = 0; i < priv->num_ports; i++) {
+ writeb_be(ETH_PORTOV_ENABLE_MASK,
+ priv->base + ETH_PORTOV_REG(i));
+ writeb_be(ETH_PTCTRL_RXDIS_MASK |
+ ETH_PTCTRL_TXDIS_MASK,
+ priv->base + ETH_PTCTRL_REG(i));
+
+ priv->sw_port_link[i] = 0;
+ }
+
+ /* enable external ports */
+ for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) {
+ u8 rgmii_ctrl;
+
+ if (!priv->used_ports[i].used)
+ continue;
+
+ rgmii_ctrl = readb_be(priv->base + ETH_RGMII_CTRL_REG(i));
+ rgmii_ctrl |= ETH_RGMII_CTRL_GMII_CLK_EN;
+ if (priv->rgmii_override)
+ rgmii_ctrl |= ETH_RGMII_CTRL_MII_OVERRIDE_EN;
+ if (priv->rgmii_timing)
+ rgmii_ctrl |= ETH_RGMII_CTRL_TIMING_SEL_EN;
+ writeb_be(rgmii_ctrl, priv->base + ETH_RGMII_CTRL_REG(i));
+ }
+
+ /* reset mib */
+ val = readb_be(priv->base + ETH_GMCR_REG);
+ val |= ETH_GMCR_RST_MIB_MASK;
+ writeb_be(val, priv->base + ETH_GMCR_REG);
+ mdelay(1);
+ val &= ~ETH_GMCR_RST_MIB_MASK;
+ writeb_be(val, priv->base + ETH_GMCR_REG);
+ mdelay(1);
+
+ /* force CPU port state */
+ val = readb_be(priv->base + ETH_IMPOV_REG);
+ val |= ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK;
+ writeb_be(val, priv->base + ETH_IMPOV_REG);
+
+ /* enable switch forward engine */
+ val = readb_be(priv->base + ETH_SWMODE_REG);
+ val |= ETH_SWMODE_FWD_EN_MASK;
+ writeb_be(val, priv->base + ETH_SWMODE_REG);
+
+ /* enable jumbo on all ports */
+ writel_be(0x1ff, priv->base + ETH_JMBCTL_PORT_REG);
+ writew_be(9728, priv->base + ETH_JMBCTL_MAXSIZE_REG);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(bcm6368_eth) = {
+ .name = "bcm6368_eth",
+ .id = UCLASS_ETH,
+ .of_match = bcm6368_eth_ids,
+ .ops = &bcm6368_eth_ops,
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+ .priv_auto_alloc_size = sizeof(struct bcm6368_eth_priv),
+ .probe = bcm6368_eth_probe,
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7e6fad305a..1dbe2b104b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -306,6 +306,7 @@ source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/renesas/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
source "drivers/pinctrl/exynos/Kconfig"
+source "drivers/pinctrl/mscc/Kconfig"
source "drivers/pinctrl/mvebu/Kconfig"
source "drivers/pinctrl/broadcom/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 293bad3a95..66d36b99d1 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl_pic32.o
obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/
obj-$(CONFIG_PINCTRL_MESON) += meson/
obj-$(CONFIG_PINCTRL_MTK) += mediatek/
+obj-$(CONFIG_PINCTRL_MSCC) += mscc/
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o
diff --git a/drivers/pinctrl/mscc/Kconfig b/drivers/pinctrl/mscc/Kconfig
new file mode 100644
index 0000000000..cfc6c06076
--- /dev/null
+++ b/drivers/pinctrl/mscc/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+config PINCTRL_MSCC
+ bool
+
+config PINCTRL_MSCC_OCELOT
+ depends on SOC_OCELOT && PINCTRL_FULL && OF_CONTROL
+ select PINCTRL_MSCC
+ default y
+ bool "Microsemi ocelot family pin control driver"
+ help
+ Support pin multiplexing and pin configuration control on
+ Microsemi ocelot SoCs.
+
+config PINCTRL_MSCC_LUTON
+ depends on SOC_LUTON && PINCTRL_FULL && OF_CONTROL
+ select PINCTRL_MSCC
+ default y
+ bool "Microsemi luton family pin control driver"
+ help
+ Support pin multiplexing and pin configuration control on
+ Microsemi luton SoCs.
diff --git a/drivers/pinctrl/mscc/Makefile b/drivers/pinctrl/mscc/Makefile
new file mode 100644
index 0000000000..6910671728
--- /dev/null
+++ b/drivers/pinctrl/mscc/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+obj-y += mscc-common.o
+obj-$(CONFIG_PINCTRL_MSCC_OCELOT) += pinctrl-ocelot.o
+obj-$(CONFIG_PINCTRL_MSCC_LUTON) += pinctrl-luton.o
diff --git a/drivers/pinctrl/mscc/mscc-common.c b/drivers/pinctrl/mscc/mscc-common.c
new file mode 100644
index 0000000000..d74b8a6649
--- /dev/null
+++ b/drivers/pinctrl/mscc/mscc-common.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi SoCs pinctrl driver
+ *
+ * Author: <alexandre.belloni@free-electrons.com>
+ * Author: <gregory.clement@bootlin.com>
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#include <asm/gpio.h>
+#include <asm/system.h>
+#include <common.h>
+#include <config.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/io.h>
+#include "mscc-common.h"
+
+#define MSCC_GPIO_OUT_SET 0x0
+#define MSCC_GPIO_OUT_CLR 0x4
+#define MSCC_GPIO_OUT 0x8
+#define MSCC_GPIO_IN 0xc
+#define MSCC_GPIO_OE 0x10
+#define MSCC_GPIO_INTR 0x14
+#define MSCC_GPIO_INTR_ENA 0x18
+#define MSCC_GPIO_INTR_IDENT 0x1c
+#define MSCC_GPIO_ALT0 0x20
+#define MSCC_GPIO_ALT1 0x24
+
+static int mscc_get_functions_count(struct udevice *dev)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev);
+
+ return info->num_func;
+}
+
+static const char *mscc_get_function_name(struct udevice *dev,
+ unsigned int function)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev);
+
+ return info->function_names[function];
+}
+
+static int mscc_pin_function_idx(unsigned int pin, unsigned int function,
+ const struct mscc_pin_data *mscc_pins)
+{
+ struct mscc_pin_caps *p = mscc_pins[pin].drv_data;
+ int i;
+
+ for (i = 0; i < MSCC_FUNC_PER_PIN; i++) {
+ if (function == p->functions[i])
+ return i;
+ }
+
+ return -1;
+}
+
+static int mscc_pinmux_set_mux(struct udevice *dev,
+ unsigned int pin_selector, unsigned int selector)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev);
+ struct mscc_pin_caps *pin = info->mscc_pins[pin_selector].drv_data;
+ int f;
+
+ f = mscc_pin_function_idx(pin_selector, selector, info->mscc_pins);
+ if (f < 0)
+ return -EINVAL;
+ /*
+ * f is encoded on two bits.
+ * bit 0 of f goes in BIT(pin) of ALT0, bit 1 of f goes in BIT(pin) of
+ * ALT1
+ * This is racy because both registers can't be updated at the same time
+ * but it doesn't matter much for now.
+ */
+ if (f & BIT(0))
+ setbits_le32(info->regs + MSCC_GPIO_ALT0, BIT(pin->pin));
+ else
+ clrbits_le32(info->regs + MSCC_GPIO_ALT0, BIT(pin->pin));
+
+ if (f & BIT(1))
+ setbits_le32(info->regs + MSCC_GPIO_ALT1, BIT(pin->pin - 1));
+ else
+ clrbits_le32(info->regs + MSCC_GPIO_ALT1, BIT(pin->pin - 1));
+
+ return 0;
+}
+
+static int mscc_pctl_get_groups_count(struct udevice *dev)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev);
+
+ return info->num_pins;
+}
+
+static const char *mscc_pctl_get_group_name(struct udevice *dev,
+ unsigned int group)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev);
+
+ return info->mscc_pins[group].name;
+}
+
+static int mscc_create_group_func_map(struct udevice *dev,
+ struct mscc_pinctrl *info)
+{
+ u16 pins[info->num_pins];
+ int f, npins, i;
+
+ for (f = 0; f < info->num_func; f++) {
+ for (npins = 0, i = 0; i < info->num_pins; i++) {
+ if (mscc_pin_function_idx(i, f, info->mscc_pins) >= 0)
+ pins[npins++] = i;
+ }
+
+ info->func[f].ngroups = npins;
+ info->func[f].groups = devm_kzalloc(dev, npins *
+ sizeof(char *), GFP_KERNEL);
+ if (!info->func[f].groups)
+ return -ENOMEM;
+
+ for (i = 0; i < npins; i++)
+ info->func[f].groups[i] = info->mscc_pins[pins[i]].name;
+ }
+
+ return 0;
+}
+
+static int mscc_pinctrl_register(struct udevice *dev, struct mscc_pinctrl *info)
+{
+ int ret;
+
+ ret = mscc_create_group_func_map(dev, info);
+ if (ret) {
+ dev_err(dev, "Unable to create group func map.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mscc_gpio_get(struct udevice *dev, unsigned int offset)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev->parent);
+ unsigned int val;
+
+ val = readl(info->regs + MSCC_GPIO_IN);
+
+ return !!(val & BIT(offset));
+}
+
+static int mscc_gpio_set(struct udevice *dev, unsigned int offset, int value)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev->parent);
+
+ if (value)
+ writel(BIT(offset), info->regs + MSCC_GPIO_OUT_SET);
+ else
+ writel(BIT(offset), info->regs + MSCC_GPIO_OUT_CLR);
+
+ return 0;
+}
+
+static int mscc_gpio_get_direction(struct udevice *dev, unsigned int offset)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev->parent);
+ unsigned int val;
+
+ val = readl(info->regs + MSCC_GPIO_OE);
+
+ return (val & BIT(offset)) ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static int mscc_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev->parent);
+
+ clrbits_le32(info->regs + MSCC_GPIO_OE, BIT(offset));
+
+ return 0;
+}
+
+static int mscc_gpio_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct mscc_pinctrl *info = dev_get_priv(dev->parent);
+
+ setbits_le32(info->regs + MSCC_GPIO_OE, BIT(offset));
+
+ return mscc_gpio_set(dev, offset, value);
+}
+
+const struct dm_gpio_ops mscc_gpio_ops = {
+ .set_value = mscc_gpio_set,
+ .get_value = mscc_gpio_get,
+ .get_function = mscc_gpio_get_direction,
+ .direction_input = mscc_gpio_direction_input,
+ .direction_output = mscc_gpio_direction_output,
+};
+
+const struct pinctrl_ops mscc_pinctrl_ops = {
+ .get_pins_count = mscc_pctl_get_groups_count,
+ .get_pin_name = mscc_pctl_get_group_name,
+ .get_functions_count = mscc_get_functions_count,
+ .get_function_name = mscc_get_function_name,
+ .pinmux_set = mscc_pinmux_set_mux,
+ .set_state = pinctrl_generic_set_state,
+};
+
+int mscc_pinctrl_probe(struct udevice *dev, int num_func,
+ const struct mscc_pin_data *mscc_pins, int num_pins,
+ char *const *function_names)
+{
+ struct mscc_pinctrl *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->regs = dev_remap_addr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+
+ priv->func = devm_kzalloc(dev, num_func * sizeof(struct mscc_pmx_func),
+ GFP_KERNEL);
+ priv->num_func = num_func;
+ priv->mscc_pins = mscc_pins;
+ priv->num_pins = num_pins;
+ priv->function_names = function_names;
+ ret = mscc_pinctrl_register(dev, priv);
+
+ return ret;
+}
diff --git a/drivers/pinctrl/mscc/mscc-common.h b/drivers/pinctrl/mscc/mscc-common.h
new file mode 100644
index 0000000000..b0001db44c
--- /dev/null
+++ b/drivers/pinctrl/mscc/mscc-common.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi SoCs pinctrl driver
+ *
+ * Author: <alexandre.belloni@free-electrons.com>
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#define MSCC_FUNC_PER_PIN 4
+
+struct mscc_pin_caps {
+ unsigned int pin;
+ unsigned char functions[MSCC_FUNC_PER_PIN];
+};
+
+struct mscc_pin_data {
+ const char *name;
+ struct mscc_pin_caps *drv_data;
+};
+
+#define MSCC_P(p, f0, f1, f2) \
+static struct mscc_pin_caps mscc_pin_##p = { \
+ .pin = p, \
+ .functions = { \
+ FUNC_GPIO, FUNC_##f0, FUNC_##f1, FUNC_##f2, \
+ }, \
+}
+
+struct mscc_pmx_func {
+ const char **groups;
+ unsigned int ngroups;
+};
+
+struct mscc_pinctrl {
+ struct udevice *dev;
+ struct pinctrl_dev *pctl;
+ void __iomem *regs;
+ struct mscc_pmx_func *func;
+ int num_func;
+ const struct mscc_pin_data *mscc_pins;
+ int num_pins;
+ char * const *function_names;
+};
+
+int mscc_pinctrl_probe(struct udevice *dev, int num_func,
+ const struct mscc_pin_data *mscc_pins, int num_pins,
+ char * const *function_names);
+const struct pinctrl_ops mscc_pinctrl_ops;
+
+const struct dm_gpio_ops mscc_gpio_ops;
diff --git a/drivers/pinctrl/mscc/pinctrl-luton.c b/drivers/pinctrl/mscc/pinctrl-luton.c
new file mode 100644
index 0000000000..7166588e3c
--- /dev/null
+++ b/drivers/pinctrl/mscc/pinctrl-luton.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi SoCs pinctrl driver
+ *
+ * Author: <gregory.clement@bootlin.com>
+ * License: Dual MIT/GPL
+ * Copyright (c) 2018 Microsemi Corporation
+ */
+
+#include <common.h>
+#include <config.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/io.h>
+#include <asm/gpio.h>
+#include <asm/system.h>
+#include "mscc-common.h"
+
+enum {
+ FUNC_NONE,
+ FUNC_GPIO,
+ FUNC_SIO,
+ FUNC_TACHO,
+ FUNC_TWI,
+ FUNC_PHY_LED,
+ FUNC_EXT_IRQ,
+ FUNC_SFP,
+ FUNC_SI,
+ FUNC_PWM,
+ FUNC_UART,
+ FUNC_MAX
+};
+
+static char * const luton_function_names[] = {
+ [FUNC_NONE] = "none",
+ [FUNC_GPIO] = "gpio",
+ [FUNC_SIO] = "sio",
+ [FUNC_TACHO] = "tacho",
+ [FUNC_TWI] = "twi",
+ [FUNC_PHY_LED] = "phy_led",
+ [FUNC_EXT_IRQ] = "ext_irq",
+ [FUNC_SFP] = "sfp",
+ [FUNC_SI] = "si",
+ [FUNC_PWM] = "pwm",
+ [FUNC_UART] = "uart",
+};
+
+MSCC_P(0, SIO, NONE, NONE);
+MSCC_P(1, SIO, NONE, NONE);
+MSCC_P(2, SIO, NONE, NONE);
+MSCC_P(3, SIO, NONE, NONE);
+MSCC_P(4, TACHO, NONE, NONE);
+MSCC_P(5, TWI, PHY_LED, NONE);
+MSCC_P(6, TWI, PHY_LED, NONE);
+MSCC_P(7, NONE, PHY_LED, NONE);
+MSCC_P(8, EXT_IRQ, PHY_LED, NONE);
+MSCC_P(9, EXT_IRQ, PHY_LED, NONE);
+MSCC_P(10, SFP, PHY_LED, NONE);
+MSCC_P(11, SFP, PHY_LED, NONE);
+MSCC_P(12, SFP, PHY_LED, NONE);
+MSCC_P(13, SFP, PHY_LED, NONE);
+MSCC_P(14, SI, PHY_LED, NONE);
+MSCC_P(15, SI, PHY_LED, NONE);
+MSCC_P(16, SI, PHY_LED, NONE);
+MSCC_P(17, SFP, PHY_LED, NONE);
+MSCC_P(18, SFP, PHY_LED, NONE);
+MSCC_P(19, SFP, PHY_LED, NONE);
+MSCC_P(20, SFP, PHY_LED, NONE);
+MSCC_P(21, SFP, PHY_LED, NONE);
+MSCC_P(22, SFP, PHY_LED, NONE);
+MSCC_P(23, SFP, PHY_LED, NONE);
+MSCC_P(24, SFP, PHY_LED, NONE);
+MSCC_P(25, SFP, PHY_LED, NONE);
+MSCC_P(26, SFP, PHY_LED, NONE);
+MSCC_P(27, SFP, PHY_LED, NONE);
+MSCC_P(28, SFP, PHY_LED, NONE);
+MSCC_P(29, PWM, NONE, NONE);
+MSCC_P(30, UART, NONE, NONE);
+MSCC_P(31, UART, NONE, NONE);
+
+#define LUTON_PIN(n) { \
+ .name = "GPIO_"#n, \
+ .drv_data = &mscc_pin_##n \
+}
+
+static const struct mscc_pin_data luton_pins[] = {
+ LUTON_PIN(0),
+ LUTON_PIN(1),
+ LUTON_PIN(2),
+ LUTON_PIN(3),
+ LUTON_PIN(4),
+ LUTON_PIN(5),
+ LUTON_PIN(6),
+ LUTON_PIN(7),
+ LUTON_PIN(8),
+ LUTON_PIN(9),
+ LUTON_PIN(10),
+ LUTON_PIN(11),
+ LUTON_PIN(12),
+ LUTON_PIN(13),
+ LUTON_PIN(14),
+ LUTON_PIN(15),
+ LUTON_PIN(16),
+ LUTON_PIN(17),
+ LUTON_PIN(18),
+ LUTON_PIN(19),
+ LUTON_PIN(20),
+ LUTON_PIN(21),
+ LUTON_PIN(22),
+ LUTON_PIN(23),
+ LUTON_PIN(24),
+ LUTON_PIN(25),
+ LUTON_PIN(26),
+ LUTON_PIN(27),
+ LUTON_PIN(28),
+ LUTON_PIN(29),
+ LUTON_PIN(30),
+ LUTON_PIN(31),
+};
+
+static int luton_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ uc_priv->bank_name = "luton-gpio";
+ uc_priv->gpio_count = ARRAY_SIZE(luton_pins);
+
+ return 0;
+}
+
+static struct driver luton_gpio_driver = {
+ .name = "luton-gpio",
+ .id = UCLASS_GPIO,
+ .probe = luton_gpio_probe,
+ .ops = &mscc_gpio_ops,
+};
+
+int luton_pinctrl_probe(struct udevice *dev)
+{
+ int ret;
+
+ ret = mscc_pinctrl_probe(dev, FUNC_MAX, luton_pins,
+ ARRAY_SIZE(luton_pins), luton_function_names);
+
+ if (ret)
+ return ret;
+
+ ret = device_bind(dev, &luton_gpio_driver, "luton-gpio", NULL,
+ dev_of_offset(dev), NULL);
+
+ return 0;
+}
+
+static const struct udevice_id luton_pinctrl_of_match[] = {
+ {.compatible = "mscc,luton-pinctrl"},
+ {},
+};
+
+U_BOOT_DRIVER(luton_pinctrl) = {
+ .name = "luton-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = of_match_ptr(luton_pinctrl_of_match),
+ .probe = luton_pinctrl_probe,
+ .priv_auto_alloc_size = sizeof(struct mscc_pinctrl),
+ .ops = &mscc_pinctrl_ops,
+};
diff --git a/drivers/pinctrl/mscc/pinctrl-ocelot.c b/drivers/pinctrl/mscc/pinctrl-ocelot.c
new file mode 100644
index 0000000000..10f9b90aad
--- /dev/null
+++ b/drivers/pinctrl/mscc/pinctrl-ocelot.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi SoCs pinctrl driver
+ *
+ * Author: <alexandre.belloni@free-electrons.com>
+ * Author: <gregory.clement@bootlin.com>
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#include <asm/gpio.h>
+#include <asm/system.h>
+#include <common.h>
+#include <config.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/io.h>
+#include "mscc-common.h"
+
+enum {
+ FUNC_NONE,
+ FUNC_GPIO,
+ FUNC_IRQ0_IN,
+ FUNC_IRQ0_OUT,
+ FUNC_IRQ1_IN,
+ FUNC_IRQ1_OUT,
+ FUNC_MIIM1,
+ FUNC_PCI_WAKE,
+ FUNC_PTP0,
+ FUNC_PTP1,
+ FUNC_PTP2,
+ FUNC_PTP3,
+ FUNC_PWM,
+ FUNC_RECO_CLK0,
+ FUNC_RECO_CLK1,
+ FUNC_SFP0,
+ FUNC_SFP1,
+ FUNC_SFP2,
+ FUNC_SFP3,
+ FUNC_SFP4,
+ FUNC_SFP5,
+ FUNC_SG0,
+ FUNC_SI,
+ FUNC_TACHO,
+ FUNC_TWI,
+ FUNC_TWI_SCL_M,
+ FUNC_UART,
+ FUNC_UART2,
+ FUNC_MAX
+};
+
+static char * const ocelot_function_names[] = {
+ [FUNC_NONE] = "none",
+ [FUNC_GPIO] = "gpio",
+ [FUNC_IRQ0_IN] = "irq0_in",
+ [FUNC_IRQ0_OUT] = "irq0_out",
+ [FUNC_IRQ1_IN] = "irq1_in",
+ [FUNC_IRQ1_OUT] = "irq1_out",
+ [FUNC_MIIM1] = "miim1",
+ [FUNC_PCI_WAKE] = "pci_wake",
+ [FUNC_PTP0] = "ptp0",
+ [FUNC_PTP1] = "ptp1",
+ [FUNC_PTP2] = "ptp2",
+ [FUNC_PTP3] = "ptp3",
+ [FUNC_PWM] = "pwm",
+ [FUNC_RECO_CLK0] = "reco_clk0",
+ [FUNC_RECO_CLK1] = "reco_clk1",
+ [FUNC_SFP0] = "sfp0",
+ [FUNC_SFP1] = "sfp1",
+ [FUNC_SFP2] = "sfp2",
+ [FUNC_SFP3] = "sfp3",
+ [FUNC_SFP4] = "sfp4",
+ [FUNC_SFP5] = "sfp5",
+ [FUNC_SG0] = "sg0",
+ [FUNC_SI] = "si",
+ [FUNC_TACHO] = "tacho",
+ [FUNC_TWI] = "twi",
+ [FUNC_TWI_SCL_M] = "twi_scl_m",
+ [FUNC_UART] = "uart",
+ [FUNC_UART2] = "uart2",
+};
+
+MSCC_P(0, SG0, NONE, NONE);
+MSCC_P(1, SG0, NONE, NONE);
+MSCC_P(2, SG0, NONE, NONE);
+MSCC_P(3, SG0, NONE, NONE);
+MSCC_P(4, IRQ0_IN, IRQ0_OUT, TWI);
+MSCC_P(5, IRQ1_IN, IRQ1_OUT, PCI_WAKE);
+MSCC_P(6, UART, TWI_SCL_M, NONE);
+MSCC_P(7, UART, TWI_SCL_M, NONE);
+MSCC_P(8, SI, TWI_SCL_M, IRQ0_OUT);
+MSCC_P(9, SI, TWI_SCL_M, IRQ1_OUT);
+MSCC_P(10, PTP2, TWI_SCL_M, SFP0);
+MSCC_P(11, PTP3, TWI_SCL_M, SFP1);
+MSCC_P(12, UART2, TWI_SCL_M, SFP2);
+MSCC_P(13, UART2, TWI_SCL_M, SFP3);
+MSCC_P(14, MIIM1, TWI_SCL_M, SFP4);
+MSCC_P(15, MIIM1, TWI_SCL_M, SFP5);
+MSCC_P(16, TWI, NONE, SI);
+MSCC_P(17, TWI, TWI_SCL_M, SI);
+MSCC_P(18, PTP0, TWI_SCL_M, NONE);
+MSCC_P(19, PTP1, TWI_SCL_M, NONE);
+MSCC_P(20, RECO_CLK0, TACHO, NONE);
+MSCC_P(21, RECO_CLK1, PWM, NONE);
+
+#define OCELOT_PIN(n) { \
+ .name = "GPIO_"#n, \
+ .drv_data = &mscc_pin_##n \
+}
+
+static const struct mscc_pin_data ocelot_pins[] = {
+ OCELOT_PIN(0),
+ OCELOT_PIN(1),
+ OCELOT_PIN(2),
+ OCELOT_PIN(3),
+ OCELOT_PIN(4),
+ OCELOT_PIN(5),
+ OCELOT_PIN(6),
+ OCELOT_PIN(7),
+ OCELOT_PIN(8),
+ OCELOT_PIN(9),
+ OCELOT_PIN(10),
+ OCELOT_PIN(11),
+ OCELOT_PIN(12),
+ OCELOT_PIN(13),
+ OCELOT_PIN(14),
+ OCELOT_PIN(15),
+ OCELOT_PIN(16),
+ OCELOT_PIN(17),
+ OCELOT_PIN(18),
+ OCELOT_PIN(19),
+ OCELOT_PIN(20),
+ OCELOT_PIN(21),
+};
+
+static int ocelot_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ uc_priv->bank_name = "ocelot-gpio";
+ uc_priv->gpio_count = ARRAY_SIZE(ocelot_pins);
+
+ return 0;
+}
+
+static struct driver ocelot_gpio_driver = {
+ .name = "ocelot-gpio",
+ .id = UCLASS_GPIO,
+ .probe = ocelot_gpio_probe,
+ .ops = &mscc_gpio_ops,
+};
+
+int ocelot_pinctrl_probe(struct udevice *dev)
+{
+ int ret;
+
+ ret = mscc_pinctrl_probe(dev, FUNC_MAX, ocelot_pins,
+ ARRAY_SIZE(ocelot_pins),
+ ocelot_function_names);
+
+ if (ret)
+ return ret;
+
+ ret = device_bind(dev, &ocelot_gpio_driver, "ocelot-gpio", NULL,
+ dev_of_offset(dev), NULL);
+
+ return ret;
+}
+
+static const struct udevice_id ocelot_pinctrl_of_match[] = {
+ {.compatible = "mscc,ocelot-pinctrl"},
+ {},
+};
+
+U_BOOT_DRIVER(ocelot_pinctrl) = {
+ .name = "ocelot-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = of_match_ptr(ocelot_pinctrl_of_match),
+ .probe = ocelot_pinctrl_probe,
+ .priv_auto_alloc_size = sizeof(struct mscc_pinctrl),
+ .ops = &mscc_pinctrl_ops,
+};
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 5cca414486..02d93763d4 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -369,7 +369,13 @@ static int poll_transfer(struct dw_spi_priv *priv)
return 0;
}
-static void external_cs_manage(struct udevice *dev, bool on)
+/*
+ * We define external_cs_manage function as 'weak' as some targets
+ * (like MSCC Ocelot) don't control the external CS pin using a GPIO
+ * controller. These SoCs use specific registers to control by
+ * software the SPI pins (and especially the CS).
+ */
+__weak void external_cs_manage(struct udevice *dev, bool on)
{
#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_SPL_BUILD)
struct dw_spi_priv *priv = dev_get_priv(dev->parent);