summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2016-02-01 16:58:46 -0500
committerTom Rini <trini@konsulko.com>2016-02-01 16:58:46 -0500
commitd2427caf54a08ec80e8bfa5ef2c0b4377da55bab (patch)
treecccf177a353a50be781364a69f957e2a116cac45 /drivers
parent735b1a2e5a61401868bb35702b6e5e18bce6eb97 (diff)
parent7d514a7407756d96996960c2ae539a4b464c65bc (diff)
downloadblackbird-obmc-uboot-d2427caf54a08ec80e8bfa5ef2c0b4377da55bab.tar.gz
blackbird-obmc-uboot-d2427caf54a08ec80e8bfa5ef2c0b4377da55bab.zip
Merge branch 'master' of git://git.denx.de/u-boot-mips
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk_pic32.c433
-rw-r--r--drivers/ddr/microchip/Makefile6
-rw-r--r--drivers/ddr/microchip/ddr2.c278
-rw-r--r--drivers/ddr/microchip/ddr2_regs.h148
-rw-r--r--drivers/ddr/microchip/ddr2_timing.h65
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/pic32_gpio.c174
-rw-r--r--drivers/mmc/Kconfig6
-rw-r--r--drivers/mmc/Makefile2
-rw-r--r--drivers/mmc/pic32_sdhci.c58
-rw-r--r--drivers/mmc/sdhci.c7
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/phy/smsc.c10
-rw-r--r--drivers/net/pic32_eth.c605
-rw-r--r--drivers/net/pic32_eth.h164
-rw-r--r--drivers/net/pic32_mdio.c121
-rw-r--r--drivers/pinctrl/Kconfig10
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl_pic32.c363
-rw-r--r--drivers/serial/Kconfig15
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_pic32.c198
26 files changed, 2683 insertions, 2 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 6294048f26..e7eab6603e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -69,4 +69,5 @@ obj-y += soc/
obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-y += thermal/
+obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
endif
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 8aa81f49f4..c9144e3e1d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o
obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
+obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c
new file mode 100644
index 0000000000..5d883544d5
--- /dev/null
+++ b/drivers/clk/clk_pic32.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <div64.h>
+#include <wait_bit.h>
+#include <dm/lists.h>
+#include <asm/io.h>
+#include <mach/pic32.h>
+#include <dt-bindings/clock/microchip,clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Primary oscillator */
+#define SYS_POSC_CLK_HZ 24000000
+
+/* FRC clk rate */
+#define SYS_FRC_CLK_HZ 8000000
+
+/* Clock Registers */
+#define OSCCON 0x0000
+#define OSCTUNE 0x0010
+#define SPLLCON 0x0020
+#define REFO1CON 0x0080
+#define REFO1TRIM 0x0090
+#define PB1DIV 0x0140
+
+/* SPLL */
+#define ICLK_MASK 0x00000080
+#define PLLIDIV_MASK 0x00000007
+#define PLLODIV_MASK 0x00000007
+#define CUROSC_MASK 0x00000007
+#define PLLMUL_MASK 0x0000007F
+#define FRCDIV_MASK 0x00000007
+
+/* PBCLK */
+#define PBDIV_MASK 0x00000007
+
+/* SYSCLK MUX */
+#define SCLK_SRC_FRC1 0
+#define SCLK_SRC_SPLL 1
+#define SCLK_SRC_POSC 2
+#define SCLK_SRC_FRC2 7
+
+/* Reference Oscillator Control Reg fields */
+#define REFO_SEL_MASK 0x0f
+#define REFO_SEL_SHIFT 0
+#define REFO_ACTIVE BIT(8)
+#define REFO_DIVSW_EN BIT(9)
+#define REFO_OE BIT(12)
+#define REFO_ON BIT(15)
+#define REFO_DIV_SHIFT 16
+#define REFO_DIV_MASK 0x7fff
+
+/* Reference Oscillator Trim Register Fields */
+#define REFO_TRIM_REG 0x10
+#define REFO_TRIM_MASK 0x1ff
+#define REFO_TRIM_SHIFT 23
+#define REFO_TRIM_MAX 511
+
+#define ROCLK_SRC_SCLK 0x0
+#define ROCLK_SRC_SPLL 0x7
+#define ROCLK_SRC_ROCLKI 0x8
+
+/* Memory PLL */
+#define MPLL_IDIV 0x3f
+#define MPLL_MULT 0xff
+#define MPLL_ODIV1 0x7
+#define MPLL_ODIV2 0x7
+#define MPLL_VREG_RDY BIT(23)
+#define MPLL_RDY BIT(31)
+#define MPLL_IDIV_SHIFT 0
+#define MPLL_MULT_SHIFT 8
+#define MPLL_ODIV1_SHIFT 24
+#define MPLL_ODIV2_SHIFT 27
+#define MPLL_IDIV_INIT 0x03
+#define MPLL_MULT_INIT 0x32
+#define MPLL_ODIV1_INIT 0x02
+#define MPLL_ODIV2_INIT 0x01
+
+struct pic32_clk_priv {
+ void __iomem *iobase;
+ void __iomem *syscfg_base;
+};
+
+static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
+{
+ u32 iclk, idiv, odiv, mult;
+ ulong plliclk, v;
+
+ v = readl(priv->iobase + SPLLCON);
+ iclk = (v & ICLK_MASK);
+ idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
+ odiv = ((v >> 24) & PLLODIV_MASK);
+ mult = ((v >> 16) & PLLMUL_MASK) + 1;
+
+ plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
+
+ if (odiv < 2)
+ odiv = 2;
+ else if (odiv < 5)
+ odiv = (1 << odiv);
+ else
+ odiv = 32;
+
+ return ((plliclk / idiv) * mult) / odiv;
+}
+
+static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
+{
+ ulong v;
+ ulong hz;
+ ulong div, frcdiv;
+ ulong curr_osc;
+
+ /* get clk source */
+ v = readl(priv->iobase + OSCCON);
+ curr_osc = (v >> 12) & CUROSC_MASK;
+ switch (curr_osc) {
+ case SCLK_SRC_FRC1:
+ case SCLK_SRC_FRC2:
+ frcdiv = ((v >> 24) & FRCDIV_MASK);
+ div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
+ hz = SYS_FRC_CLK_HZ / div;
+ break;
+
+ case SCLK_SRC_SPLL:
+ hz = pic32_get_pll_rate(priv);
+ break;
+
+ case SCLK_SRC_POSC:
+ hz = SYS_POSC_CLK_HZ;
+ break;
+
+ default:
+ hz = 0;
+ printf("clk: unknown sclk_src.\n");
+ break;
+ }
+
+ return hz;
+}
+
+static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
+{
+ void __iomem *reg;
+ ulong div, clk_freq;
+
+ WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
+
+ clk_freq = pic32_get_sysclk(priv);
+
+ reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
+ div = (readl(reg) & PBDIV_MASK) + 1;
+
+ return clk_freq / div;
+}
+
+static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
+{
+ return pic32_get_pbclk(priv, PB7CLK);
+}
+
+static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
+ int parent_rate, int rate, int parent_id)
+{
+ void __iomem *reg;
+ u32 div, trim, v;
+ u64 frac;
+
+ WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
+
+ /* calculate dividers,
+ * rate = parent_rate / [2 * (div + (trim / 512))]
+ */
+ if (parent_rate <= rate) {
+ div = 0;
+ trim = 0;
+ } else {
+ div = parent_rate / (rate << 1);
+ frac = parent_rate;
+ frac <<= 8;
+ do_div(frac, rate);
+ frac -= (u64)(div << 9);
+ trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
+ }
+
+ reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
+
+ /* disable clk */
+ writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
+
+ /* wait till previous src change is active */
+ wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE,
+ false, CONFIG_SYS_HZ, false);
+
+ /* parent_id */
+ v = readl(reg);
+ v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
+ v |= (parent_id << REFO_SEL_SHIFT);
+
+ /* apply rodiv */
+ v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
+ v |= (div << REFO_DIV_SHIFT);
+ writel(v, reg);
+
+ /* apply trim */
+ v = readl(reg + REFO_TRIM_REG);
+ v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
+ v |= (trim << REFO_TRIM_SHIFT);
+ writel(v, reg + REFO_TRIM_REG);
+
+ /* enable clk */
+ writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
+
+ /* switch divider */
+ writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
+
+ /* wait for divider switching to complete */
+ return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false,
+ CONFIG_SYS_HZ, false);
+}
+
+static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
+{
+ u32 rodiv, rotrim, rosel, v, parent_rate;
+ void __iomem *reg;
+ u64 rate64;
+
+ WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
+
+ reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
+ v = readl(reg);
+ /* get rosel */
+ rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
+ /* get div */
+ rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
+
+ /* get trim */
+ v = readl(reg + REFO_TRIM_REG);
+ rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
+
+ if (!rodiv)
+ return 0;
+
+ /* get parent rate */
+ switch (rosel) {
+ case ROCLK_SRC_SCLK:
+ parent_rate = pic32_get_cpuclk(priv);
+ break;
+ case ROCLK_SRC_SPLL:
+ parent_rate = pic32_get_pll_rate(priv);
+ break;
+ default:
+ parent_rate = 0;
+ break;
+ }
+
+ /* Calculation
+ * rate = parent_rate / [2 * (div + (trim / 512))]
+ */
+ if (rotrim) {
+ rodiv <<= 9;
+ rodiv += rotrim;
+ rate64 = parent_rate;
+ rate64 <<= 8;
+ do_div(rate64, rodiv);
+ v = (u32)rate64;
+ } else {
+ v = parent_rate / (rodiv << 1);
+ }
+ return v;
+}
+
+static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
+{
+ u32 v, idiv, mul;
+ u32 odiv1, odiv2;
+ u64 rate;
+
+ v = readl(priv->syscfg_base + CFGMPLL);
+ idiv = v & MPLL_IDIV;
+ mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
+ odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
+ odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
+
+ rate = (SYS_POSC_CLK_HZ / idiv) * mul;
+ do_div(rate, odiv1);
+ do_div(rate, odiv2);
+
+ return (ulong)rate;
+}
+
+static int pic32_mpll_init(struct pic32_clk_priv *priv)
+{
+ u32 v, mask;
+
+ /* initialize */
+ v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
+ (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
+ (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
+ (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
+
+ writel(v, priv->syscfg_base + CFGMPLL);
+
+ /* Wait for ready */
+ mask = MPLL_RDY | MPLL_VREG_RDY;
+ return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask,
+ true, get_tbclk(), false);
+}
+
+static void pic32_clk_init(struct udevice *dev)
+{
+ const void *blob = gd->fdt_blob;
+ struct pic32_clk_priv *priv;
+ ulong rate, pll_hz;
+ char propname[50];
+ int i;
+
+ priv = dev_get_priv(dev);
+ pll_hz = pic32_get_pll_rate(priv);
+
+ /* Initialize REFOs as not initialized and enabled on reset. */
+ for (i = REF1CLK; i <= REF5CLK; i++) {
+ snprintf(propname, sizeof(propname),
+ "microchip,refo%d-frequency", i - REF1CLK + 1);
+ rate = fdtdec_get_int(blob, dev->of_offset, propname, 0);
+ if (rate)
+ pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
+ }
+
+ /* Memory PLL */
+ pic32_mpll_init(priv);
+}
+
+static ulong pic32_clk_get_rate(struct udevice *dev)
+{
+ struct pic32_clk_priv *priv = dev_get_priv(dev);
+
+ return pic32_get_cpuclk(priv);
+}
+
+static ulong pic32_get_periph_rate(struct udevice *dev, int periph)
+{
+ struct pic32_clk_priv *priv = dev_get_priv(dev);
+ ulong rate;
+
+ switch (periph) {
+ case PB1CLK ... PB7CLK:
+ rate = pic32_get_pbclk(priv, periph);
+ break;
+ case REF1CLK ... REF5CLK:
+ rate = pic32_get_refclk(priv, periph);
+ break;
+ case PLLCLK:
+ rate = pic32_get_pll_rate(priv);
+ break;
+ case MPLL:
+ rate = pic32_get_mpll_rate(priv);
+ break;
+ default:
+ rate = 0;
+ break;
+ }
+
+ return rate;
+}
+
+static ulong pic32_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+{
+ struct pic32_clk_priv *priv = dev_get_priv(dev);
+ ulong pll_hz;
+
+ switch (periph) {
+ case REF1CLK ... REF5CLK:
+ pll_hz = pic32_get_pll_rate(priv);
+ pic32_set_refclk(priv, periph, pll_hz, rate, ROCLK_SRC_SPLL);
+ break;
+ default:
+ break;
+ }
+
+ return rate;
+}
+
+static struct clk_ops pic32_pic32_clk_ops = {
+ .get_rate = pic32_clk_get_rate,
+ .set_periph_rate = pic32_set_periph_rate,
+ .get_periph_rate = pic32_get_periph_rate,
+};
+
+static int pic32_clk_probe(struct udevice *dev)
+{
+ struct pic32_clk_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+ fdt_size_t size;
+
+ addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->iobase = ioremap(addr, size);
+ if (!priv->iobase)
+ return -EINVAL;
+
+ priv->syscfg_base = pic32_get_syscfg_base();
+
+ /* initialize clocks */
+ pic32_clk_init(dev);
+
+ return 0;
+}
+
+static const struct udevice_id pic32_clk_ids[] = {
+ { .compatible = "microchip,pic32mzda-clk"},
+ {}
+};
+
+U_BOOT_DRIVER(pic32_clk) = {
+ .name = "pic32_clk",
+ .id = UCLASS_CLK,
+ .of_match = pic32_clk_ids,
+ .flags = DM_FLAG_PRE_RELOC,
+ .ops = &pic32_pic32_clk_ops,
+ .probe = pic32_clk_probe,
+ .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
+};
diff --git a/drivers/ddr/microchip/Makefile b/drivers/ddr/microchip/Makefile
new file mode 100644
index 0000000000..305c48b164
--- /dev/null
+++ b/drivers/ddr/microchip/Makefile
@@ -0,0 +1,6 @@
+#
+# Copyright (C) 2015 Microchip Technology Inc.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+obj-$(CONFIG_MACH_PIC32) += ddr2.o
diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c
new file mode 100644
index 0000000000..6056418588
--- /dev/null
+++ b/drivers/ddr/microchip/ddr2.c
@@ -0,0 +1,278 @@
+/*
+ * (c) 2015 Paul Thacker <paul.thacker@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+#include <common.h>
+#include <wait_bit.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <mach/pic32.h>
+#include <mach/ddr.h>
+
+#include "ddr2_regs.h"
+#include "ddr2_timing.h"
+
+/* init DDR2 Phy */
+void ddr2_phy_init(void)
+{
+ struct ddr2_phy_regs *ddr2_phy;
+ u32 pad_ctl;
+
+ ddr2_phy = ioremap(PIC32_DDR2P_BASE, sizeof(*ddr2_phy));
+
+ /* PHY_DLL_RECALIB */
+ writel(DELAY_START_VAL(3) | DISABLE_RECALIB(0) |
+ RECALIB_CNT(0x10), &ddr2_phy->dll_recalib);
+
+ /* PHY_PAD_CTRL */
+ pad_ctl = ODT_SEL | ODT_EN | DRIVE_SEL(0) |
+ ODT_PULLDOWN(2) | ODT_PULLUP(3) |
+ EXTRA_OEN_CLK(0) | NOEXT_DLL |
+ DLR_DFT_WRCMD | HALF_RATE |
+ DRVSTR_PFET(0xe) | DRVSTR_NFET(0xe) |
+ RCVR_EN | PREAMBLE_DLY(2);
+ writel(pad_ctl, &ddr2_phy->pad_ctrl);
+
+ /* SCL_CONFIG_0 */
+ writel(SCL_BURST8 | SCL_DDR_CONNECTED | SCL_RCAS_LAT(RL) |
+ SCL_ODTCSWW, &ddr2_phy->scl_config_1);
+
+ /* SCL_CONFIG_1 */
+ writel(SCL_CSEN | SCL_WCAS_LAT(WL), &ddr2_phy->scl_config_2);
+
+ /* SCL_LAT */
+ writel(SCL_CAPCLKDLY(3) | SCL_DDRCLKDLY(4), &ddr2_phy->scl_latency);
+}
+
+/* start phy self calibration logic */
+static int ddr2_phy_calib_start(void)
+{
+ struct ddr2_phy_regs *ddr2_phy;
+
+ ddr2_phy = ioremap(PIC32_DDR2P_BASE, sizeof(*ddr2_phy));
+
+ /* DDR Phy SCL Start */
+ writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
+
+ /* Wait for SCL for data byte to pass */
+ return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS,
+ true, CONFIG_SYS_HZ, false);
+}
+
+/* DDR2 Controller initialization */
+
+/* Target Agent Arbiter */
+static void ddr_set_arbiter(struct ddr2_ctrl_regs *ctrl,
+ const struct ddr2_arbiter_params *const param)
+{
+ int i;
+
+ for (i = 0; i < NUM_AGENTS; i++) {
+ /* set min burst size */
+ writel(i * MIN_LIM_WIDTH, &ctrl->tsel);
+ writel(param->min_limit, &ctrl->minlim);
+
+ /* set request period (4 * req_period clocks) */
+ writel(i * RQST_PERIOD_WIDTH, &ctrl->tsel);
+ writel(param->req_period, &ctrl->reqprd);
+
+ /* set number of burst accepted */
+ writel(i * MIN_CMDACPT_WIDTH, &ctrl->tsel);
+ writel(param->min_cmd_acpt, &ctrl->mincmd);
+ }
+}
+
+const struct ddr2_arbiter_params *__weak board_get_ddr_arbiter_params(void)
+{
+ /* default arbiter parameters */
+ static const struct ddr2_arbiter_params arb_params[] = {
+ { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x04,},
+ { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
+ { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
+ { .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
+ { .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
+ };
+
+ return &arb_params[0];
+}
+
+static void host_load_cmd(struct ddr2_ctrl_regs *ctrl, u32 cmd_idx,
+ u32 hostcmd2, u32 hostcmd1, u32 delay)
+{
+ u32 hc_delay;
+
+ hc_delay = max_t(u32, DIV_ROUND_UP(delay, T_CK), 2) - 2;
+ writel(hostcmd1, &ctrl->cmd10[cmd_idx]);
+ writel((hostcmd2 & 0x7ff) | (hc_delay << 11), &ctrl->cmd20[cmd_idx]);
+}
+
+/* init DDR2 Controller */
+void ddr2_ctrl_init(void)
+{
+ u32 wr2prech, rd2prech, wr2rd, wr2rd_cs;
+ u32 ras2ras, ras2cas, prech2ras, temp;
+ const struct ddr2_arbiter_params *arb_params;
+ struct ddr2_ctrl_regs *ctrl;
+
+ ctrl = ioremap(PIC32_DDR2C_BASE, sizeof(*ctrl));
+
+ /* PIC32 DDR2 controller always work in HALF_RATE */
+ writel(HALF_RATE_MODE, &ctrl->memwidth);
+
+ /* Set arbiter configuration per target */
+ arb_params = board_get_ddr_arbiter_params();
+ ddr_set_arbiter(ctrl, arb_params);
+
+ /* Address Configuration, model {CS, ROW, BA, COL} */
+ writel((ROW_ADDR_RSHIFT | (BA_RSHFT << 8) | (CS_ADDR_RSHIFT << 16) |
+ (COL_HI_RSHFT << 24) | (SB_PRI << 29) |
+ (EN_AUTO_PRECH << 30)), &ctrl->memcfg0);
+
+ writel(ROW_ADDR_MASK, &ctrl->memcfg1);
+ writel(COL_HI_MASK, &ctrl->memcfg2);
+ writel(COL_LO_MASK, &ctrl->memcfg3);
+ writel(BA_MASK | (CS_ADDR_MASK << 8), &ctrl->memcfg4);
+
+ /* Refresh Config */
+ writel(REFCNT_CLK(DIV_ROUND_UP(T_RFI, T_CK_CTRL) - 2) |
+ REFDLY_CLK(DIV_ROUND_UP(T_RFC_MIN, T_CK_CTRL) - 2) |
+ MAX_PEND_REF(7),
+ &ctrl->refcfg);
+
+ /* Power Config */
+ writel(ECC_EN(0) | ERR_CORR_EN(0) | EN_AUTO_PWR_DN(0) |
+ EN_AUTO_SELF_REF(3) | PWR_DN_DLY(8) |
+ SELF_REF_DLY(17) | PRECH_PWR_DN_ONLY(0),
+ &ctrl->pwrcfg);
+
+ /* Delay Config */
+ wr2rd = max_t(u32, DIV_ROUND_UP(T_WTR, T_CK_CTRL),
+ DIV_ROUND_UP(T_WTR_TCK, 2)) + WL + BL;
+ wr2rd_cs = max_t(u32, wr2rd - 1, 3);
+ wr2prech = DIV_ROUND_UP(T_WR, T_CK_CTRL) + WL + BL;
+ rd2prech = max_t(u32, DIV_ROUND_UP(T_RTP, T_CK_CTRL),
+ DIV_ROUND_UP(T_RTP_TCK, 2)) + BL - 2;
+ ras2ras = max_t(u32, DIV_ROUND_UP(T_RRD, T_CK_CTRL),
+ DIV_ROUND_UP(T_RRD_TCK, 2)) - 1;
+ ras2cas = DIV_ROUND_UP(T_RCD, T_CK_CTRL) - 1;
+ prech2ras = DIV_ROUND_UP(T_RP, T_CK_CTRL) - 1;
+
+ writel(((wr2rd & 0x0f) |
+ ((wr2rd_cs & 0x0f) << 4) |
+ ((BL - 1) << 8) |
+ (BL << 12) |
+ ((BL - 1) << 16) |
+ ((BL - 1) << 20) |
+ ((BL + 2) << 24) |
+ ((RL - WL + 3) << 28)), &ctrl->dlycfg0);
+
+ writel(((T_CKE_TCK - 1) |
+ (((DIV_ROUND_UP(T_DLLK, 2) - 2) & 0xff) << 8) |
+ ((T_CKE_TCK - 1) << 16) |
+ ((max_t(u32, T_XP_TCK, T_CKE_TCK) - 1) << 20) |
+ ((wr2prech >> 4) << 26) |
+ ((wr2rd >> 4) << 27) |
+ ((wr2rd_cs >> 4) << 28) |
+ (((RL + 5) >> 4) << 29) |
+ ((DIV_ROUND_UP(T_DLLK, 2) >> 8) << 30)), &ctrl->dlycfg1);
+
+ writel((DIV_ROUND_UP(T_RP, T_CK_CTRL) |
+ (rd2prech << 8) |
+ ((wr2prech & 0x0f) << 12) |
+ (ras2ras << 16) |
+ (ras2cas << 20) |
+ (prech2ras << 24) |
+ ((RL + 3) << 28)), &ctrl->dlycfg2);
+
+ writel(((DIV_ROUND_UP(T_RAS_MIN, T_CK_CTRL) - 1) |
+ ((DIV_ROUND_UP(T_RC, T_CK_CTRL) - 1) << 8) |
+ ((DIV_ROUND_UP(T_FAW, T_CK_CTRL) - 1) << 16)),
+ &ctrl->dlycfg3);
+
+ /* ODT Config */
+ writel(0x0, &ctrl->odtcfg);
+ writel(BIT(16), &ctrl->odtencfg);
+ writel(ODTRDLY(RL - 3) | ODTWDLY(WL - 3) | ODTRLEN(2) | ODTWLEN(3),
+ &ctrl->odtcfg);
+
+ /* Transfer Configuration */
+ writel(NXTDATRQDLY(2) | NXDATAVDLY(4) | RDATENDLY(2) |
+ MAX_BURST(3) | (7 << 28) | BIG_ENDIAN(0),
+ &ctrl->xfercfg);
+
+ /* DRAM Initialization */
+ /* CKE high after reset and wait 400 nsec */
+ host_load_cmd(ctrl, 0, 0, IDLE_NOP, 400000);
+
+ /* issue precharge all command */
+ host_load_cmd(ctrl, 1, 0x04, PRECH_ALL_CMD, T_RP + T_CK);
+
+ /* initialize EMR2 */
+ host_load_cmd(ctrl, 2, 0x200, LOAD_MODE_CMD, T_MRD_TCK * T_CK);
+
+ /* initialize EMR3 */
+ host_load_cmd(ctrl, 3, 0x300, LOAD_MODE_CMD, T_MRD_TCK * T_CK);
+
+ /*
+ * RDQS disable, DQSB enable, OCD exit, 150 ohm termination,
+ * AL=0, DLL enable
+ */
+ host_load_cmd(ctrl, 4, 0x100,
+ LOAD_MODE_CMD | (0x40 << 24), T_MRD_TCK * T_CK);
+ /*
+ * PD fast exit, WR REC = T_WR in clocks -1,
+ * DLL reset, CAS = RL, burst = 4
+ */
+ temp = ((DIV_ROUND_UP(T_WR, T_CK) - 1) << 1) | 1;
+ host_load_cmd(ctrl, 5, temp, LOAD_MODE_CMD | (RL << 28) | (2 << 24),
+ T_MRD_TCK * T_CK);
+
+ /* issue precharge all command */
+ host_load_cmd(ctrl, 6, 4, PRECH_ALL_CMD, T_RP + T_CK);
+
+ /* issue refresh command */
+ host_load_cmd(ctrl, 7, 0, REF_CMD, T_RFC_MIN);
+
+ /* issue refresh command */
+ host_load_cmd(ctrl, 8, 0, REF_CMD, T_RFC_MIN);
+
+ /* Mode register programming as before without DLL reset */
+ host_load_cmd(ctrl, 9, temp, LOAD_MODE_CMD | (RL << 28) | (3 << 24),
+ T_MRD_TCK * T_CK);
+
+ /* extended mode register same as before with OCD default */
+ host_load_cmd(ctrl, 10, 0x103, LOAD_MODE_CMD | (0xc << 24),
+ T_MRD_TCK * T_CK);
+
+ /* extended mode register same as before with OCD exit */
+ host_load_cmd(ctrl, 11, 0x100, LOAD_MODE_CMD | (0x4 << 28),
+ 140 * T_CK);
+
+ writel(CMD_VALID | NUMHOSTCMD(11), &ctrl->cmdissue);
+
+ /* start memory initialization */
+ writel(INIT_START, &ctrl->memcon);
+
+ /* wait for all host cmds to be transmitted */
+ wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false,
+ CONFIG_SYS_HZ, false);
+
+ /* inform all cmds issued, ready for normal operation */
+ writel(INIT_START | INIT_DONE, &ctrl->memcon);
+
+ /* perform phy caliberation */
+ if (ddr2_phy_calib_start())
+ printf("ddr2: phy calib failed\n");
+}
+
+phys_size_t ddr2_calculate_size(void)
+{
+ u32 temp;
+
+ temp = 1 << (COL_BITS + BA_BITS + ROW_BITS);
+ /* 16-bit data width between controller and DIMM */
+ temp = temp * CS_BITS * (16 / 8);
+ return (phys_size_t)temp;
+}
diff --git a/drivers/ddr/microchip/ddr2_regs.h b/drivers/ddr/microchip/ddr2_regs.h
new file mode 100644
index 0000000000..0f4b159048
--- /dev/null
+++ b/drivers/ddr/microchip/ddr2_regs.h
@@ -0,0 +1,148 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __MICROCHIP_DDR2_REGS_H
+#define __MICROCHIP_DDR2_REGS_H
+
+#include <linux/bitops.h>
+
+/* DDR2 Controller */
+struct ddr2_ctrl_regs {
+ u32 tsel;
+ u32 minlim;
+ u32 reqprd;
+ u32 mincmd;
+ u32 memcon;
+ u32 memcfg0;
+ u32 memcfg1;
+ u32 memcfg2;
+ u32 memcfg3;
+ u32 memcfg4;
+ u32 refcfg;
+ u32 pwrcfg;
+ u32 dlycfg0;
+ u32 dlycfg1;
+ u32 dlycfg2;
+ u32 dlycfg3;
+ u32 odtcfg;
+ u32 xfercfg;
+ u32 cmdissue;
+ u32 odtencfg;
+ u32 memwidth;
+ u32 unused[11];
+ u32 cmd10[16];
+ u32 cmd20[16];
+};
+
+/* Arbiter Config */
+#define MIN_LIM_WIDTH 5
+#define RQST_PERIOD_WIDTH 8
+#define MIN_CMDACPT_WIDTH 8
+
+/* Refresh Config */
+#define REFCNT_CLK(x) (x)
+#define REFDLY_CLK(x) ((x) << 16)
+#define MAX_PEND_REF(x) ((x) << 24)
+
+/* Power Config */
+#define PRECH_PWR_DN_ONLY(x) ((x) << 22)
+#define SELF_REF_DLY(x) ((x) << 12)
+#define PWR_DN_DLY(x) ((x) << 4)
+#define EN_AUTO_SELF_REF(x) ((x) << 3)
+#define EN_AUTO_PWR_DN(x) ((x) << 2)
+#define ERR_CORR_EN(x) ((x) << 1)
+#define ECC_EN(x) (x)
+
+/* Memory Width */
+#define HALF_RATE_MODE BIT(3)
+
+/* Delay Config */
+#define ODTWLEN(x) ((x) << 20)
+#define ODTRLEN(x) ((x) << 16)
+#define ODTWDLY(x) ((x) << 12)
+#define ODTRDLY(x) ((x) << 8)
+
+/* Xfer Config */
+#define BIG_ENDIAN(x) ((x) << 31)
+#define MAX_BURST(x) ((x) << 24)
+#define RDATENDLY(x) ((x) << 16)
+#define NXDATAVDLY(x) ((x) << 4)
+#define NXTDATRQDLY(x) ((x) << 0)
+
+/* Host Commands */
+#define IDLE_NOP 0x00ffffff
+#define PRECH_ALL_CMD 0x00fff401
+#define REF_CMD 0x00fff801
+#define LOAD_MODE_CMD 0x00fff001
+#define CKE_LOW 0x00ffeffe
+
+#define NUM_HOST_CMDS 12
+
+/* Host CMD Issue */
+#define CMD_VALID BIT(4)
+#define NUMHOSTCMD(x) (x)
+
+/* Memory Control */
+#define INIT_DONE BIT(1)
+#define INIT_START BIT(0)
+
+/* Address Control */
+#define EN_AUTO_PRECH 0
+#define SB_PRI 1
+
+/* DDR2 Phy Register */
+struct ddr2_phy_regs {
+ u32 scl_start;
+ u32 unused1[2];
+ u32 scl_latency;
+ u32 unused2[2];
+ u32 scl_config_1;
+ u32 scl_config_2;
+ u32 pad_ctrl;
+ u32 dll_recalib;
+};
+
+/* PHY PAD CONTROL */
+#define ODT_SEL BIT(0)
+#define ODT_EN BIT(1)
+#define DRIVE_SEL(x) ((x) << 2)
+#define ODT_PULLDOWN(x) ((x) << 4)
+#define ODT_PULLUP(x) ((x) << 6)
+#define EXTRA_OEN_CLK(x) ((x) << 8)
+#define NOEXT_DLL BIT(9)
+#define DLR_DFT_WRCMD BIT(13)
+#define HALF_RATE BIT(14)
+#define DRVSTR_PFET(x) ((x) << 16)
+#define DRVSTR_NFET(x) ((x) << 20)
+#define RCVR_EN BIT(28)
+#define PREAMBLE_DLY(x) ((x) << 29)
+
+/* PHY DLL RECALIBRATE */
+#define RECALIB_CNT(x) ((x) << 8)
+#define DISABLE_RECALIB(x) ((x) << 26)
+#define DELAY_START_VAL(x) ((x) << 28)
+
+/* PHY SCL CONFIG1 */
+#define SCL_BURST8 BIT(0)
+#define SCL_DDR_CONNECTED BIT(1)
+#define SCL_RCAS_LAT(x) ((x) << 4)
+#define SCL_ODTCSWW BIT(24)
+
+/* PHY SCL CONFIG2 */
+#define SCL_CSEN BIT(0)
+#define SCL_WCAS_LAT(x) ((x) << 8)
+
+/* PHY SCL Latency */
+#define SCL_CAPCLKDLY(x) ((x) << 0)
+#define SCL_DDRCLKDLY(x) ((x) << 4)
+
+/* PHY SCL START */
+#define SCL_START BIT(28)
+#define SCL_EN BIT(26)
+#define SCL_LUBPASS (BIT(1) | BIT(0))
+
+#endif /* __MICROCHIP_DDR2_REGS_H */
diff --git a/drivers/ddr/microchip/ddr2_timing.h b/drivers/ddr/microchip/ddr2_timing.h
new file mode 100644
index 0000000000..5895f9d592
--- /dev/null
+++ b/drivers/ddr/microchip/ddr2_timing.h
@@ -0,0 +1,65 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __MICROCHIP_DDR2_TIMING_H
+#define __MICROCHIP_DDR2_TIMING_H
+
+/* MPLL freq is 400MHz */
+#define T_CK 2500 /* 2500 psec */
+#define T_CK_CTRL (T_CK * 2)
+
+/* Burst length in cycles */
+#define BL 2
+/* default CAS latency for all speed grades */
+#define RL 5
+/* default write latency for all speed grades = CL-1 */
+#define WL 4
+
+/* From Micron MT47H64M16HR-3 data sheet */
+#define T_RFC_MIN 127500 /* psec */
+#define T_WR 15000 /* psec */
+#define T_RP 12500 /* psec */
+#define T_RCD 12500 /* psec */
+#define T_RRD 7500 /* psec */
+/* T_RRD_TCK is minimum of 2 clk periods, regardless of freq */
+#define T_RRD_TCK 2
+#define T_WTR 7500 /* psec */
+/* T_WTR_TCK is minimum of 2 clk periods, regardless of freq */
+#define T_WTR_TCK 2
+#define T_RTP 7500 /* psec */
+#define T_RTP_TCK (BL / 2)
+#define T_XP_TCK 2 /* clocks */
+#define T_CKE_TCK 3 /* clocks */
+#define T_XSNR (T_RFC_MIN + 10000) /* psec */
+#define T_DLLK 200 /* clocks */
+#define T_RAS_MIN 45000 /* psec */
+#define T_RC 57500 /* psec */
+#define T_FAW 35000 /* psec */
+#define T_MRD_TCK 2 /* clocks */
+#define T_RFI 7800000 /* psec */
+
+/* DDR Addressing */
+#define COL_BITS 10
+#define BA_BITS 3
+#define ROW_BITS 13
+#define CS_BITS 1
+
+/* DDR Addressing scheme: {CS, ROW, BA, COL} */
+#define COL_HI_RSHFT 0
+#define COL_HI_MASK 0
+#define COL_LO_MASK ((1 << COL_BITS) - 1)
+
+#define BA_RSHFT COL_BITS
+#define BA_MASK ((1 << BA_BITS) - 1)
+
+#define ROW_ADDR_RSHIFT (BA_RSHFT + BA_BITS)
+#define ROW_ADDR_MASK ((1 << ROW_BITS) - 1)
+
+#define CS_ADDR_RSHIFT 0
+#define CS_ADDR_MASK 0
+
+#endif /* __MICROCHIP_DDR2_TIMING_H */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e60e9fd86c..845dc725c5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -83,4 +83,11 @@ config VYBRID_GPIO
help
Say yes here to support Vybrid vf610 GPIOs.
+config PIC32_GPIO
+ bool "Microchip PIC32 GPIO driver"
+ depends on DM_GPIO && MACH_PIC32
+ default y
+ help
+ Say yes here to support Microchip PIC32 GPIOs.
+
endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fb4fd255df..845a6d4493 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -46,4 +46,4 @@ obj-$(CONFIG_STM32_GPIO) += stm32_gpio.o
obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o
obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o
obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o
-
+obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o
diff --git a/drivers/gpio/pic32_gpio.c b/drivers/gpio/pic32_gpio.c
new file mode 100644
index 0000000000..499b4fa5ad
--- /dev/null
+++ b/drivers/gpio/pic32_gpio.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015 Microchip Technology Inc
+ * Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/compat.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <mach/pic32.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Peripheral Pin Control */
+struct pic32_reg_port {
+ struct pic32_reg_atomic ansel;
+ struct pic32_reg_atomic tris;
+ struct pic32_reg_atomic port;
+ struct pic32_reg_atomic lat;
+ struct pic32_reg_atomic open_drain;
+ struct pic32_reg_atomic cnpu;
+ struct pic32_reg_atomic cnpd;
+ struct pic32_reg_atomic cncon;
+};
+
+enum {
+ MICROCHIP_GPIO_DIR_OUT,
+ MICROCHIP_GPIO_DIR_IN,
+ MICROCHIP_GPIOS_PER_BANK = 16,
+};
+
+struct pic32_gpio_priv {
+ struct pic32_reg_port *regs;
+ char name[2];
+};
+
+static int pic32_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(readl(&priv->regs->port.raw) & BIT(offset));
+}
+
+static int pic32_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ if (value)
+ writel(mask, &priv->regs->port.set);
+ else
+ writel(mask, &priv->regs->port.clr);
+
+ return 0;
+}
+
+static int pic32_gpio_direction(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+
+ /* pin in analog mode ? */
+ if (readl(&priv->regs->ansel.raw) & BIT(offset))
+ return -EPERM;
+
+ if (readl(&priv->regs->tris.raw) & BIT(offset))
+ return MICROCHIP_GPIO_DIR_IN;
+ else
+ return MICROCHIP_GPIO_DIR_OUT;
+}
+
+static int pic32_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ writel(mask, &priv->regs->ansel.clr);
+ writel(mask, &priv->regs->tris.set);
+
+ return 0;
+}
+
+static int pic32_gpio_direction_output(struct udevice *dev,
+ unsigned offset, int value)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ writel(mask, &priv->regs->ansel.clr);
+ writel(mask, &priv->regs->tris.clr);
+
+ pic32_gpio_set_value(dev, offset, value);
+ return 0;
+}
+
+static int pic32_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static int pic32_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ int ret = GPIOF_UNUSED;
+
+ switch (pic32_gpio_direction(dev, offset)) {
+ case MICROCHIP_GPIO_DIR_OUT:
+ ret = GPIOF_OUTPUT;
+ break;
+ case MICROCHIP_GPIO_DIR_IN:
+ ret = GPIOF_INPUT;
+ break;
+ default:
+ ret = GPIOF_UNUSED;
+ break;
+ }
+ return ret;
+}
+
+static const struct dm_gpio_ops gpio_pic32_ops = {
+ .direction_input = pic32_gpio_direction_input,
+ .direction_output = pic32_gpio_direction_output,
+ .get_value = pic32_gpio_get_value,
+ .set_value = pic32_gpio_set_value,
+ .get_function = pic32_gpio_get_function,
+ .xlate = pic32_gpio_xlate,
+};
+
+static int pic32_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+ fdt_size_t size;
+ char *end;
+ int bank;
+
+ addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = ioremap(addr, size);
+
+ uc_priv->gpio_count = MICROCHIP_GPIOS_PER_BANK;
+ /* extract bank name */
+ end = strrchr(dev->name, '@');
+ bank = trailing_strtoln(dev->name, end);
+ priv->name[0] = 'A' + bank;
+ uc_priv->bank_name = priv->name;
+
+ return 0;
+}
+
+static const struct udevice_id pic32_gpio_ids[] = {
+ { .compatible = "microchip,pic32mzda-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_pic32) = {
+ .name = "gpio_pic32",
+ .id = UCLASS_GPIO,
+ .of_match = pic32_gpio_ids,
+ .ops = &gpio_pic32_ops,
+ .probe = pic32_gpio_probe,
+ .priv_auto_alloc_size = sizeof(struct pic32_gpio_priv),
+};
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index ceae7bcaec..9f4b766f7a 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -31,4 +31,10 @@ config SH_SDHI
help
Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform
+config PIC32_SDHCI
+ bool "Microchip PIC32 on-chip SDHCI support"
+ depends on DM_MMC && MACH_PIC32
+ help
+ Support for Microchip PIC32 SDHCI controller.
+
endmenu
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 5d357056dd..c9c3e3e938 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -48,4 +48,4 @@ obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
endif
-
+obj-$(CONFIG_PIC32_SDHCI) += pic32_sdhci.o
diff --git a/drivers/mmc/pic32_sdhci.c b/drivers/mmc/pic32_sdhci.c
new file mode 100644
index 0000000000..28da55d2db
--- /dev/null
+++ b/drivers/mmc/pic32_sdhci.c
@@ -0,0 +1,58 @@
+/*
+ * Support of SDHCI for Microchip PIC32 SoC.
+ *
+ * Copyright (C) 2015 Microchip Technology Inc.
+ * Andrei Pistirica <andrei.pistirica@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <dm.h>
+#include <common.h>
+#include <sdhci.h>
+#include <asm/errno.h>
+#include <mach/pic32.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int pic32_sdhci_probe(struct udevice *dev)
+{
+ struct sdhci_host *host = dev_get_priv(dev);
+ const void *fdt = gd->fdt_blob;
+ u32 f_min_max[2];
+ fdt_addr_t addr;
+ fdt_size_t size;
+ int ret;
+
+ addr = fdtdec_get_addr_size(fdt, dev->of_offset, "reg", &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ host->ioaddr = ioremap(addr, size);
+ host->name = (char *)dev->name;
+ host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_CD;
+ host->bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+ "bus-width", 4);
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
+ "clock-freq-min-max", f_min_max, 2);
+ if (ret) {
+ printf("sdhci: clock-freq-min-max not found\n");
+ return ret;
+ }
+
+ return add_sdhci(host, f_min_max[1], f_min_max[0]);
+}
+
+static const struct udevice_id pic32_sdhci_ids[] = {
+ { .compatible = "microchip,pic32mzda-sdhci" },
+ { }
+};
+
+U_BOOT_DRIVER(pic32_sdhci_drv) = {
+ .name = "pic32_sdhci",
+ .id = UCLASS_MMC,
+ .of_match = pic32_sdhci_ids,
+ .probe = pic32_sdhci_probe,
+ .priv_auto_alloc_size = sizeof(struct sdhci_host),
+};
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index ff770b16e2..8586d898fd 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -443,6 +443,12 @@ static int sdhci_init(struct mmc *mmc)
sdhci_set_power(host, fls(mmc->cfg->voltages) - 1);
if (host->quirks & SDHCI_QUIRK_NO_CD) {
+#if defined(CONFIG_PIC32_SDHCI)
+ /* PIC32 SDHCI CD errata:
+ * - set CD_TEST and clear CD_TEST_INS bit
+ */
+ sdhci_writeb(host, SDHCI_CTRL_CD_TEST, SDHCI_HOST_CONTROL);
+#else
unsigned int status;
sdhci_writeb(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST,
@@ -453,6 +459,7 @@ static int sdhci_init(struct mmc *mmc)
(!(status & SDHCI_CARD_STATE_STABLE)) ||
(!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
status = sdhci_readl(host, SDHCI_PRESENT_STATE);
+#endif
}
/* Enable only interrupts served by the SD controller */
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 218e1fee22..bc2f51d958 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -125,4 +125,12 @@ config ZYNQ_GEM
help
This MAC is present in Xilinx Zynq and ZynqMP SoCs.
+config PIC32_ETH
+ bool "Microchip PIC32 Ethernet Support"
+ depends on DM_ETH && MACH_PIC32
+ select PHYLIB
+ help
+ This driver implements 10/100 Mbps Ethernet and MAC layer for
+ Microchip PIC32 microcontrollers.
+
endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 150470c24b..33a81ee547 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -72,3 +72,4 @@ obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
obj-$(CONFIG_VSC9953) += vsc9953.o
+obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index bfd9815abf..34986a29fc 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -69,11 +69,21 @@ static struct phy_driver lan8710_driver = {
.shutdown = &genphy_shutdown,
};
+static struct phy_driver lan8740_driver = {
+ .name = "SMSC LAN8740",
+ .uid = 0x0007c110,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
int phy_smsc_init(void)
{
phy_register(&lan8710_driver);
phy_register(&lan911x_driver);
phy_register(&lan8700_driver);
+ phy_register(&lan8740_driver);
return 0;
}
diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c
new file mode 100644
index 0000000000..167af8bde5
--- /dev/null
+++ b/drivers/net/pic32_eth.c
@@ -0,0 +1,605 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <net.h>
+#include <miiphy.h>
+#include <console.h>
+#include <wait_bit.h>
+#include <asm/gpio.h>
+
+#include "pic32_eth.h"
+
+#define MAX_RX_BUF_SIZE 1536
+#define MAX_RX_DESCR PKTBUFSRX
+#define MAX_TX_DESCR 2
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pic32eth_dev {
+ struct eth_dma_desc rxd_ring[MAX_RX_DESCR];
+ struct eth_dma_desc txd_ring[MAX_TX_DESCR];
+ u32 rxd_idx; /* index of RX desc to read */
+ /* regs */
+ struct pic32_ectl_regs *ectl_regs;
+ struct pic32_emac_regs *emac_regs;
+ /* Phy */
+ struct phy_device *phydev;
+ phy_interface_t phyif;
+ u32 phy_addr;
+ struct gpio_desc rst_gpio;
+};
+
+void __weak board_netphy_reset(void *dev)
+{
+ struct pic32eth_dev *priv = dev;
+
+ if (!dm_gpio_is_valid(&priv->rst_gpio))
+ return;
+
+ /* phy reset */
+ dm_gpio_set_value(&priv->rst_gpio, 0);
+ udelay(300);
+ dm_gpio_set_value(&priv->rst_gpio, 1);
+ udelay(300);
+}
+
+/* Initialize mii(MDIO) interface, discover which PHY is
+ * attached to the device, and configure it properly.
+ */
+static int pic32_mii_init(struct pic32eth_dev *priv)
+{
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+ /* board phy reset */
+ board_netphy_reset(priv);
+
+ /* disable RX, TX & all transactions */
+ writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+ /* wait till busy */
+ wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+ CONFIG_SYS_HZ, false);
+
+ /* turn controller ON to access PHY over MII */
+ writel(ETHCON_ON, &ectl_p->con1.set);
+
+ mdelay(10);
+
+ /* reset MAC */
+ writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert */
+ mdelay(10);
+ writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert */
+
+ /* initialize MDIO/MII */
+ if (priv->phyif == PHY_INTERFACE_MODE_RMII) {
+ writel(EMAC_RMII_RESET, &emac_p->supp.set);
+ mdelay(10);
+ writel(EMAC_RMII_RESET, &emac_p->supp.clr);
+ }
+
+ return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p->mii);
+}
+
+static int pic32_phy_init(struct pic32eth_dev *priv, struct udevice *dev)
+{
+ struct mii_dev *mii;
+
+ mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
+
+ /* find & connect PHY */
+ priv->phydev = phy_connect(mii, priv->phy_addr,
+ dev, priv->phyif);
+ if (!priv->phydev) {
+ printf("%s: %s: Error, PHY connect\n", __FILE__, __func__);
+ return 0;
+ }
+
+ /* Wait for phy to complete reset */
+ mdelay(10);
+
+ /* configure supported modes */
+ priv->phydev->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg;
+
+ priv->phydev->advertising = ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_Autoneg;
+
+ priv->phydev->autoneg = AUTONEG_ENABLE;
+
+ return 0;
+}
+
+/* Configure MAC based on negotiated speed and duplex
+ * reported by PHY.
+ */
+static int pic32_mac_adjust_link(struct pic32eth_dev *priv)
+{
+ struct phy_device *phydev = priv->phydev;
+ struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+ if (!phydev->link) {
+ printf("%s: No link.\n", phydev->dev->name);
+ return -EINVAL;
+ }
+
+ if (phydev->duplex) {
+ writel(EMAC_FULLDUP, &emac_p->cfg2.set);
+ writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw);
+ } else {
+ writel(EMAC_FULLDUP, &emac_p->cfg2.clr);
+ writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
+ }
+
+ switch (phydev->speed) {
+ case SPEED_100:
+ writel(EMAC_RMII_SPD100, &emac_p->supp.set);
+ break;
+ case SPEED_10:
+ writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
+ break;
+ default:
+ printf("%s: Speed was bad\n", phydev->dev->name);
+ return -EINVAL;
+ }
+
+ printf("pic32eth: PHY is %s with %dbase%s, %s\n",
+ phydev->drv->name, phydev->speed,
+ (phydev->port == PORT_TP) ? "T" : "X",
+ (phydev->duplex) ? "full" : "half");
+
+ return 0;
+}
+
+static void pic32_mac_init(struct pic32eth_dev *priv, u8 *macaddr)
+{
+ struct pic32_emac_regs *emac_p = priv->emac_regs;
+ u32 stat = 0, v;
+ u64 expire;
+
+ v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE;
+ writel(v, &emac_p->cfg1.raw);
+
+ v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE |
+ EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP;
+ writel(v, &emac_p->cfg2.raw);
+
+ /* recommended back-to-back inter-packet gap for 10 Mbps half duplex */
+ writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
+
+ /* recommended non-back-to-back interpacket gap is 0xc12 */
+ writel(0xc12, &emac_p->ipgr.raw);
+
+ /* recommended collision window retry limit is 0x370F */
+ writel(0x370f, &emac_p->clrt.raw);
+
+ /* set maximum frame length: allow VLAN tagged frame */
+ writel(0x600, &emac_p->maxf.raw);
+
+ /* set the mac address */
+ writel(macaddr[0] | (macaddr[1] << 8), &emac_p->sa2.raw);
+ writel(macaddr[2] | (macaddr[3] << 8), &emac_p->sa1.raw);
+ writel(macaddr[4] | (macaddr[5] << 8), &emac_p->sa0.raw);
+
+ /* default, enable 10 Mbps operation */
+ writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
+
+ /* wait until link status UP or deadline elapsed */
+ expire = get_ticks() + get_tbclk() * 2;
+ for (; get_ticks() < expire;) {
+ stat = phy_read(priv->phydev, priv->phy_addr, MII_BMSR);
+ if (stat & BMSR_LSTATUS)
+ break;
+ }
+
+ if (!(stat & BMSR_LSTATUS))
+ printf("MAC: Link is DOWN!\n");
+
+ /* delay to stabilize before any tx/rx */
+ mdelay(10);
+}
+
+static void pic32_mac_reset(struct pic32eth_dev *priv)
+{
+ struct pic32_emac_regs *emac_p = priv->emac_regs;
+ struct mii_dev *mii;
+
+ /* Reset MAC */
+ writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
+ mdelay(10);
+
+ /* clear reset */
+ writel(0, &emac_p->cfg1.raw);
+
+ /* Reset MII */
+ mii = priv->phydev->bus;
+ if (mii && mii->reset)
+ mii->reset(mii);
+}
+
+/* initializes the MAC and PHY, then establishes a link */
+static void pic32_ctrl_reset(struct pic32eth_dev *priv)
+{
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ u32 v;
+
+ /* disable RX, TX & any other transactions */
+ writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+ /* wait till busy */
+ wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+ CONFIG_SYS_HZ, false);
+ /* decrement received buffcnt to zero. */
+ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT)
+ writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+
+ /* clear any existing interrupt event */
+ writel(0xffffffff, &ectl_p->irq.clr);
+
+ /* clear RX/TX start address */
+ writel(0xffffffff, &ectl_p->txst.clr);
+ writel(0xffffffff, &ectl_p->rxst.clr);
+
+ /* clear the receive filters */
+ writel(0x00ff, &ectl_p->rxfc.clr);
+
+ /* set the receive filters
+ * ETH_FILT_CRC_ERR_REJECT
+ * ETH_FILT_RUNT_REJECT
+ * ETH_FILT_UCAST_ACCEPT
+ * ETH_FILT_MCAST_ACCEPT
+ * ETH_FILT_BCAST_ACCEPT
+ */
+ v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN |
+ ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN;
+ writel(v, &ectl_p->rxfc.set);
+
+ /* turn controller ON to access PHY over MII */
+ writel(ETHCON_ON, &ectl_p->con1.set);
+}
+
+static void pic32_rx_desc_init(struct pic32eth_dev *priv)
+{
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ struct eth_dma_desc *rxd;
+ u32 idx, bufsz;
+
+ priv->rxd_idx = 0;
+ for (idx = 0; idx < MAX_RX_DESCR; idx++) {
+ rxd = &priv->rxd_ring[idx];
+
+ /* hw owned */
+ rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY;
+
+ /* packet buffer address */
+ rxd->data_buff = virt_to_phys(net_rx_packets[idx]);
+
+ /* link to next desc */
+ rxd->next_ed = virt_to_phys(rxd + 1);
+
+ /* reset status */
+ rxd->stat1 = 0;
+ rxd->stat2 = 0;
+
+ /* decrement bufcnt */
+ writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+ }
+
+ /* link last descr to beginning of list */
+ rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]);
+
+ /* flush rx ring */
+ flush_dcache_range((ulong)priv->rxd_ring,
+ (ulong)priv->rxd_ring + sizeof(priv->rxd_ring));
+
+ /* set rx desc-ring start address */
+ writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p->rxst.raw);
+
+ /* RX Buffer size */
+ bufsz = readl(&ectl_p->con2.raw);
+ bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT);
+ bufsz |= ((MAX_RX_BUF_SIZE / 16) << ETHCON_RXBUFSZ_SHFT);
+ writel(bufsz, &ectl_p->con2.raw);
+
+ /* enable the receiver in hardware which allows hardware
+ * to DMA received pkts to the descriptor pointer address.
+ */
+ writel(ETHCON_RXEN, &ectl_p->con1.set);
+}
+
+static int pic32_eth_start(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+
+ /* controller */
+ pic32_ctrl_reset(priv);
+
+ /* reset MAC */
+ pic32_mac_reset(priv);
+
+ /* configure PHY */
+ phy_config(priv->phydev);
+
+ /* initialize MAC */
+ pic32_mac_init(priv, &pdata->enetaddr[0]);
+
+ /* init RX descriptor; TX descriptors are handled in xmit */
+ pic32_rx_desc_init(priv);
+
+ /* Start up & update link status of PHY */
+ phy_startup(priv->phydev);
+
+ /* adjust mac with phy link status */
+ return pic32_mac_adjust_link(priv);
+}
+
+static void pic32_eth_stop(struct udevice *dev)
+{
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+ /* Reset the phy if the controller is enabled */
+ if (readl(&ectl_p->con1.raw) & ETHCON_ON)
+ phy_reset(priv->phydev);
+
+ /* Shut down the PHY */
+ phy_shutdown(priv->phydev);
+
+ /* Stop rx/tx */
+ writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+ mdelay(10);
+
+ /* reset MAC */
+ writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
+
+ /* clear reset */
+ writel(0, &emac_p->cfg1.raw);
+ mdelay(10);
+
+ /* disable controller */
+ writel(ETHCON_ON, &ectl_p->con1.clr);
+ mdelay(10);
+
+ /* wait until everything is down */
+ wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+ 2 * CONFIG_SYS_HZ, false);
+
+ /* clear any existing interrupt event */
+ writel(0xffffffff, &ectl_p->irq.clr);
+}
+
+static int pic32_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ struct eth_dma_desc *txd;
+ u64 deadline;
+
+ txd = &priv->txd_ring[0];
+
+ /* set proper flags & length in descriptor header */
+ txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN | EDH_BCOUNT(length);
+
+ /* pass buffer address to hardware */
+ txd->data_buff = virt_to_phys(packet);
+
+ debug("%s: %d / .hdr %x, .data_buff %x, .stat %x, .nexted %x\n",
+ __func__, __LINE__, txd->hdr, txd->data_buff, txd->stat2,
+ txd->next_ed);
+
+ /* cache flush (packet) */
+ flush_dcache_range((ulong)packet, (ulong)packet + length);
+
+ /* cache flush (txd) */
+ flush_dcache_range((ulong)txd, (ulong)txd + sizeof(*txd));
+
+ /* pass descriptor table base to h/w */
+ writel(virt_to_phys(txd), &ectl_p->txst.raw);
+
+ /* ready to send enabled, hardware can now send the packet(s) */
+ writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set);
+
+ /* wait until tx has completed and h/w has released ownership
+ * of the tx descriptor or timeout elapsed.
+ */
+ deadline = get_ticks() + get_tbclk();
+ for (;;) {
+ /* check timeout */
+ if (get_ticks() > deadline)
+ return -ETIMEDOUT;
+
+ if (ctrlc())
+ return -EINTR;
+
+ /* tx completed ? */
+ if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) {
+ udelay(1);
+ continue;
+ }
+
+ /* h/w not released ownership yet? */
+ invalidate_dcache_range((ulong)txd, (ulong)txd + sizeof(*txd));
+ if (!(txd->hdr & EDH_EOWN))
+ break;
+ }
+
+ return 0;
+}
+
+static int pic32_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ struct eth_dma_desc *rxd;
+ u32 idx = priv->rxd_idx;
+ u32 rx_count;
+
+ /* find the next ready to receive */
+ rxd = &priv->rxd_ring[idx];
+
+ invalidate_dcache_range((ulong)rxd, (ulong)rxd + sizeof(*rxd));
+ /* check if owned by MAC */
+ if (rxd->hdr & EDH_EOWN)
+ return -EAGAIN;
+
+ /* Sanity check on header: SOP and EOP */
+ if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP | EDH_EOP)) {
+ printf("%s: %s, rx pkt across multiple descr\n",
+ __FILE__, __func__);
+ return 0;
+ }
+
+ debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x, nexted %x\n",
+ __func__, __LINE__, idx, rxd->hdr,
+ rxd->data_buff, rxd->stat2, rxd->next_ed);
+
+ /* Sanity check on rx_stat: OK, CRC */
+ if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) {
+ debug("%s: %s: Error, rx problem detected\n",
+ __FILE__, __func__);
+ return 0;
+ }
+
+ /* invalidate dcache */
+ rx_count = RSV_RX_COUNT(rxd->stat2);
+ invalidate_dcache_range((ulong)net_rx_packets[idx],
+ (ulong)net_rx_packets[idx] + rx_count);
+
+ /* Pass the packet to protocol layer */
+ *packetp = net_rx_packets[idx];
+
+ /* increment number of bytes rcvd (ignore CRC) */
+ return rx_count - 4;
+}
+
+static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+ struct eth_dma_desc *rxd;
+ int idx = priv->rxd_idx;
+
+ /* sanity check */
+ if (packet != net_rx_packets[idx]) {
+ printf("rxd_id %d: packet is not matched,\n", idx);
+ return -EAGAIN;
+ }
+
+ /* prepare for receive */
+ rxd = &priv->rxd_ring[idx];
+ rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN;
+
+ flush_dcache_range((ulong)rxd, (ulong)rxd + sizeof(*rxd));
+
+ /* decrement rx pkt count */
+ writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+
+ debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x, nexted %x\n",
+ __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
+ rxd->stat2, rxd->next_ed);
+
+ priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR;
+
+ return 0;
+}
+
+static const struct eth_ops pic32_eth_ops = {
+ .start = pic32_eth_start,
+ .send = pic32_eth_send,
+ .recv = pic32_eth_recv,
+ .free_pkt = pic32_eth_free_pkt,
+ .stop = pic32_eth_stop,
+};
+
+static int pic32_eth_probe(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ const char *phy_mode;
+ void __iomem *iobase;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ int offset = 0;
+ int phy_addr = -1;
+
+ addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ iobase = ioremap(addr, size);
+ pdata->iobase = (phys_addr_t)addr;
+
+ /* get phy mode */
+ pdata->phy_interface = -1;
+ phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
+ if (phy_mode)
+ pdata->phy_interface = phy_get_interface_by_name(phy_mode);
+ if (pdata->phy_interface == -1) {
+ debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
+ return -EINVAL;
+ }
+
+ /* get phy addr */
+ offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
+ "phy-handle");
+ if (offset > 0)
+ phy_addr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
+
+ /* phy reset gpio */
+ gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
+ "reset-gpios", 0,
+ &priv->rst_gpio, GPIOD_IS_OUT);
+
+ priv->phyif = pdata->phy_interface;
+ priv->phy_addr = phy_addr;
+ priv->ectl_regs = iobase;
+ priv->emac_regs = iobase + PIC32_EMAC1CFG1;
+
+ pic32_mii_init(priv);
+
+ return pic32_phy_init(priv, dev);
+}
+
+static int pic32_eth_remove(struct udevice *dev)
+{
+ struct pic32eth_dev *priv = dev_get_priv(dev);
+ struct mii_dev *bus;
+
+ dm_gpio_free(dev, &priv->rst_gpio);
+ phy_shutdown(priv->phydev);
+ free(priv->phydev);
+ bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
+ mdio_unregister(bus);
+ mdio_free(bus);
+ iounmap(priv->ectl_regs);
+ return 0;
+}
+
+static const struct udevice_id pic32_eth_ids[] = {
+ { .compatible = "microchip,pic32mzda-eth" },
+ { }
+};
+
+U_BOOT_DRIVER(pic32_ethernet) = {
+ .name = "pic32_ethernet",
+ .id = UCLASS_ETH,
+ .of_match = pic32_eth_ids,
+ .probe = pic32_eth_probe,
+ .remove = pic32_eth_remove,
+ .ops = &pic32_eth_ops,
+ .priv_auto_alloc_size = sizeof(struct pic32eth_dev),
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
diff --git a/drivers/net/pic32_eth.h b/drivers/net/pic32_eth.h
new file mode 100644
index 0000000000..be2a1872d8
--- /dev/null
+++ b/drivers/net/pic32_eth.h
@@ -0,0 +1,164 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __MICROCHIP_PIC32_ETH_H_
+#define __MICROCHIP_PIC32_ETH_H_
+
+#include <mach/pic32.h>
+
+/* Ethernet */
+struct pic32_ectl_regs {
+ struct pic32_reg_atomic con1; /* 0x00 */
+ struct pic32_reg_atomic con2; /* 0x10 */
+ struct pic32_reg_atomic txst; /* 0x20 */
+ struct pic32_reg_atomic rxst; /* 0x30 */
+ struct pic32_reg_atomic ht0; /* 0x40 */
+ struct pic32_reg_atomic ht1; /* 0x50 */
+ struct pic32_reg_atomic pmm0; /* 0x60 */
+ struct pic32_reg_atomic pmm1; /* 0x70 */
+ struct pic32_reg_atomic pmcs; /* 0x80 */
+ struct pic32_reg_atomic pmo; /* 0x90 */
+ struct pic32_reg_atomic rxfc; /* 0xa0 */
+ struct pic32_reg_atomic rxwm; /* 0xb0 */
+ struct pic32_reg_atomic ien; /* 0xc0 */
+ struct pic32_reg_atomic irq; /* 0xd0 */
+ struct pic32_reg_atomic stat; /* 0xe0 */
+};
+
+struct pic32_mii_regs {
+ struct pic32_reg_atomic mcfg; /* 0x280 */
+ struct pic32_reg_atomic mcmd; /* 0x290 */
+ struct pic32_reg_atomic madr; /* 0x2a0 */
+ struct pic32_reg_atomic mwtd; /* 0x2b0 */
+ struct pic32_reg_atomic mrdd; /* 0x2c0 */
+ struct pic32_reg_atomic mind; /* 0x2d0 */
+};
+
+struct pic32_emac_regs {
+ struct pic32_reg_atomic cfg1; /* 0x200*/
+ struct pic32_reg_atomic cfg2; /* 0x210*/
+ struct pic32_reg_atomic ipgt; /* 0x220*/
+ struct pic32_reg_atomic ipgr; /* 0x230*/
+ struct pic32_reg_atomic clrt; /* 0x240*/
+ struct pic32_reg_atomic maxf; /* 0x250*/
+ struct pic32_reg_atomic supp; /* 0x260*/
+ struct pic32_reg_atomic test; /* 0x270*/
+ struct pic32_mii_regs mii; /* 0x280 - 0x2d0 */
+ struct pic32_reg_atomic res1; /* 0x2e0 */
+ struct pic32_reg_atomic res2; /* 0x2f0 */
+ struct pic32_reg_atomic sa0; /* 0x300 */
+ struct pic32_reg_atomic sa1; /* 0x310 */
+ struct pic32_reg_atomic sa2; /* 0x320 */
+};
+
+/* ETHCON1 Reg field */
+#define ETHCON_BUFCDEC BIT(0)
+#define ETHCON_RXEN BIT(8)
+#define ETHCON_TXRTS BIT(9)
+#define ETHCON_ON BIT(15)
+
+/* ETHCON2 Reg field */
+#define ETHCON_RXBUFSZ 0x7f
+#define ETHCON_RXBUFSZ_SHFT 0x4
+
+/* ETHSTAT Reg field */
+#define ETHSTAT_BUSY BIT(7)
+#define ETHSTAT_BUFCNT 0x00ff0000
+
+/* ETHRXFC Register fields */
+#define ETHRXFC_BCEN BIT(0)
+#define ETHRXFC_MCEN BIT(1)
+#define ETHRXFC_UCEN BIT(3)
+#define ETHRXFC_RUNTEN BIT(4)
+#define ETHRXFC_CRCOKEN BIT(5)
+
+/* EMAC1CFG1 register offset */
+#define PIC32_EMAC1CFG1 0x0200
+
+/* EMAC1CFG1 register fields */
+#define EMAC_RXENABLE BIT(0)
+#define EMAC_RXPAUSE BIT(2)
+#define EMAC_TXPAUSE BIT(3)
+#define EMAC_SOFTRESET BIT(15)
+
+/* EMAC1CFG2 register fields */
+#define EMAC_FULLDUP BIT(0)
+#define EMAC_LENGTHCK BIT(1)
+#define EMAC_CRCENABLE BIT(4)
+#define EMAC_PADENABLE BIT(5)
+#define EMAC_AUTOPAD BIT(7)
+#define EMAC_EXCESS BIT(14)
+
+/* EMAC1IPGT register magic */
+#define FULLDUP_GAP_TIME 0x15
+#define HALFDUP_GAP_TIME 0x12
+
+/* EMAC1SUPP register fields */
+#define EMAC_RMII_SPD100 BIT(8)
+#define EMAC_RMII_RESET BIT(11)
+
+/* MII Management Configuration Register */
+#define MIIMCFG_RSTMGMT BIT(15)
+#define MIIMCFG_CLKSEL_DIV40 0x0020 /* 100Mhz / 40 */
+
+/* MII Management Command Register */
+#define MIIMCMD_READ BIT(0)
+#define MIIMCMD_SCAN BIT(1)
+
+/* MII Management Address Register */
+#define MIIMADD_REGADDR 0x1f
+#define MIIMADD_REGADDR_SHIFT 0
+#define MIIMADD_PHYADDR_SHIFT 8
+
+/* MII Management Indicator Register */
+#define MIIMIND_BUSY BIT(0)
+#define MIIMIND_NOTVALID BIT(2)
+#define MIIMIND_LINKFAIL BIT(3)
+
+/* Packet Descriptor */
+/* Received Packet Status */
+#define _RSV1_PKT_CSUM 0xffff
+#define _RSV2_CRC_ERR BIT(20)
+#define _RSV2_LEN_ERR BIT(21)
+#define _RSV2_RX_OK BIT(23)
+#define _RSV2_RX_COUNT 0xffff
+
+#define RSV_RX_CSUM(__rsv1) ((__rsv1) & _RSV1_PKT_CSUM)
+#define RSV_RX_COUNT(__rsv2) ((__rsv2) & _RSV2_RX_COUNT)
+#define RSV_RX_OK(__rsv2) ((__rsv2) & _RSV2_RX_OK)
+#define RSV_CRC_ERR(__rsv2) ((__rsv2) & _RSV2_CRC_ERR)
+
+/* Ethernet Hardware Descriptor Header bits */
+#define EDH_EOWN BIT(7)
+#define EDH_NPV BIT(8)
+#define EDH_STICKY BIT(9)
+#define _EDH_BCOUNT 0x07ff0000
+#define EDH_EOP BIT(30)
+#define EDH_SOP BIT(31)
+#define EDH_BCOUNT_SHIFT 16
+#define EDH_BCOUNT(len) ((len) << EDH_BCOUNT_SHIFT)
+
+/* Ethernet Hardware Descriptors
+ * ref: PIC32 Family Reference Manual Table 35-7
+ * This structure represents the layout of the DMA
+ * memory shared between the CPU and the Ethernet
+ * controller.
+ */
+/* TX/RX DMA descriptor */
+struct eth_dma_desc {
+ u32 hdr; /* header */
+ u32 data_buff; /* data buffer address */
+ u32 stat1; /* transmit/receive packet status */
+ u32 stat2; /* transmit/receive packet status */
+ u32 next_ed; /* next descriptor */
+};
+
+#define PIC32_MDIO_NAME "PIC32_EMAC"
+
+int pic32_mdio_init(const char *name, ulong ioaddr);
+
+#endif /* __MICROCHIP_PIC32_ETH_H_*/
diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
new file mode 100644
index 0000000000..578fc96905
--- /dev/null
+++ b/drivers/net/pic32_mdio.c
@@ -0,0 +1,121 @@
+/*
+ * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c.
+ *
+ * Copyright 2015 Microchip Inc.
+ * Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <phy.h>
+#include <miiphy.h>
+#include <errno.h>
+#include <wait_bit.h>
+#include <asm/io.h>
+#include "pic32_eth.h"
+
+static int pic32_mdio_write(struct mii_dev *bus,
+ int addr, int dev_addr,
+ int reg, u16 value)
+{
+ u32 v;
+ struct pic32_mii_regs *mii_regs = bus->priv;
+
+ /* Wait for the previous operation to finish */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+
+ /* Put phyaddr and regaddr into MIIMADD */
+ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+ writel(v, &mii_regs->madr.raw);
+
+ /* Initiate a write command */
+ writel(value, &mii_regs->mwtd.raw);
+
+ /* Wait 30 clock cycles for busy flag to be set */
+ udelay(12);
+
+ /* Wait for write to complete */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+
+ return 0;
+}
+
+static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg)
+{
+ u32 v;
+ struct pic32_mii_regs *mii_regs = bus->priv;
+
+ /* Wait for the previous operation to finish */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+
+ /* Put phyaddr and regaddr into MIIMADD */
+ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+ writel(v, &mii_regs->madr.raw);
+
+ /* Initiate a read command */
+ writel(MIIMCMD_READ, &mii_regs->mcmd.raw);
+
+ /* Wait 30 clock cycles for busy flag to be set */
+ udelay(12);
+
+ /* Wait for read to complete */
+ wait_for_bit(__func__, &mii_regs->mind.raw,
+ MIIMIND_NOTVALID | MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, false);
+
+ /* Clear the command register */
+ writel(0, &mii_regs->mcmd.raw);
+
+ /* Grab the value read from the PHY */
+ v = readl(&mii_regs->mrdd.raw);
+ return v;
+}
+
+static int pic32_mdio_reset(struct mii_dev *bus)
+{
+ struct pic32_mii_regs *mii_regs = bus->priv;
+
+ /* Reset MII (due to new addresses) */
+ writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
+
+ /* Wait for the operation to finish */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+
+ /* Clear reset bit */
+ writel(0, &mii_regs->mcfg);
+
+ /* Wait for the operation to finish */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+
+ /* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */
+ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
+
+ /* Wait for the operation to finish */
+ wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+ false, CONFIG_SYS_HZ, true);
+ return 0;
+}
+
+int pic32_mdio_init(const char *name, ulong ioaddr)
+{
+ struct mii_dev *bus;
+
+ bus = mdio_alloc();
+ if (!bus) {
+ printf("Failed to allocate PIC32-MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->read = pic32_mdio_read;
+ bus->write = pic32_mdio_write;
+ bus->reset = pic32_mdio_reset;
+ strncpy(bus->name, name, sizeof(bus->name));
+ bus->priv = (void *)ioaddr;
+
+ return mdio_register(bus);
+}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 57e6142c50..5dd2dddb44 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -131,6 +131,16 @@ config PINCTRL_SANDBOX
actually does nothing but print debug messages when pinctrl
operations are invoked.
+config PIC32_PINCTRL
+ bool "Microchip PIC32 pin-control and pin-mux driver"
+ depends on DM && MACH_PIC32
+ default y
+ help
+ Supports individual pin selection and configuration for each remappable
+ peripheral available on Microchip PIC32 SoCs. This driver is controlled
+ by a device tree node which contains both GPIO defintion and pin control
+ functions.
+
endif
source "drivers/pinctrl/uniphier/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 70d25dc981..b4f46500eb 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
obj-$(CONFIG_ARCH_UNIPHIER) += uniphier/
+obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o
diff --git a/drivers/pinctrl/pinctrl_pic32.c b/drivers/pinctrl/pinctrl_pic32.c
new file mode 100644
index 0000000000..5cf97ecec8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl_pic32.c
@@ -0,0 +1,363 @@
+/*
+ * Pinctrl driver for Microchip PIC32 SoCs
+ * Copyright (c) 2015 Microchip Technology Inc.
+ * Written by Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <mach/pic32.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* PIC32 has 10 peripheral ports with 16 pins each.
+ * Ports are marked PORTA-PORTK or PORT0-PORT9.
+ */
+enum {
+ PIC32_PORT_A = 0,
+ PIC32_PORT_B = 1,
+ PIC32_PORT_C = 2,
+ PIC32_PORT_D = 3,
+ PIC32_PORT_E = 4,
+ PIC32_PORT_F = 5,
+ PIC32_PORT_G = 6,
+ PIC32_PORT_H = 7,
+ PIC32_PORT_J = 8, /* no PORT_I */
+ PIC32_PORT_K = 9,
+ PIC32_PINS_PER_PORT = 16,
+};
+
+#define PIN_CONFIG_PIC32_DIGITAL (PIN_CONFIG_END + 1)
+#define PIN_CONFIG_PIC32_ANALOG (PIN_CONFIG_END + 2)
+
+/* pin configuration descriptor */
+struct pic32_pin_config {
+ u16 port; /* port number */
+ u16 pin; /* pin number in the port */
+ u32 config; /* one of PIN_CONFIG_* */
+};
+#define PIN_CONFIG(_prt, _pin, _cfg) \
+ {.port = (_prt), .pin = (_pin), .config = (_cfg), }
+
+/* In PIC32 muxing is performed at pin-level through two
+ * different set of registers - one set for input functions,
+ * and other for output functions.
+ * Pin configuration is handled through port register.
+ */
+/* Port control registers */
+struct pic32_reg_port {
+ struct pic32_reg_atomic ansel;
+ struct pic32_reg_atomic tris;
+ struct pic32_reg_atomic port;
+ struct pic32_reg_atomic lat;
+ struct pic32_reg_atomic odc;
+ struct pic32_reg_atomic cnpu;
+ struct pic32_reg_atomic cnpd;
+ struct pic32_reg_atomic cncon;
+ struct pic32_reg_atomic unused[8];
+};
+
+/* Input function mux registers */
+struct pic32_reg_in_mux {
+ u32 unused0;
+ u32 int1[4];
+ u32 unused1;
+ u32 t2ck[8];
+ u32 ic1[9];
+ u32 unused2;
+ u32 ocfar;
+ u32 unused3;
+ u32 u1rx;
+ u32 u1cts;
+ u32 u2rx;
+ u32 u2cts;
+ u32 u3rx;
+ u32 u3cts;
+ u32 u4rx;
+ u32 u4cts;
+ u32 u5rx;
+ u32 u5cts;
+ u32 u6rx;
+ u32 u6cts;
+ u32 unused4;
+ u32 sdi1;
+ u32 ss1;
+ u32 unused5;
+ u32 sdi2;
+ u32 ss2;
+ u32 unused6;
+ u32 sdi3;
+ u32 ss3;
+ u32 unused7;
+ u32 sdi4;
+ u32 ss4;
+ u32 unused8;
+ u32 sdi5;
+ u32 ss5;
+ u32 unused9;
+ u32 sdi6;
+ u32 ss6;
+ u32 c1rx;
+ u32 c2rx;
+ u32 refclki1;
+ u32 refclki2;
+ u32 refclki3;
+ u32 refclki4;
+};
+
+/* output mux register offset */
+#define PPS_OUT(__port, __pin) \
+ (((__port) * PIC32_PINS_PER_PORT + (__pin)) << 2)
+
+
+struct pic32_pinctrl_priv {
+ struct pic32_reg_in_mux *mux_in; /* mux input function */
+ struct pic32_reg_port *pinconf; /* pin configuration*/
+ void __iomem *mux_out; /* mux output function */
+};
+
+enum {
+ PERIPH_ID_UART1,
+ PERIPH_ID_UART2,
+ PERIPH_ID_ETH,
+ PERIPH_ID_USB,
+ PERIPH_ID_SDHCI,
+ PERIPH_ID_I2C1,
+ PERIPH_ID_I2C2,
+ PERIPH_ID_SPI1,
+ PERIPH_ID_SPI2,
+ PERIPH_ID_SQI,
+};
+
+static int pic32_pinconfig_one(struct pic32_pinctrl_priv *priv,
+ u32 port_nr, u32 pin, u32 param)
+{
+ struct pic32_reg_port *port;
+
+ port = &priv->pinconf[port_nr];
+ switch (param) {
+ case PIN_CONFIG_PIC32_DIGITAL:
+ writel(BIT(pin), &port->ansel.clr);
+ break;
+ case PIN_CONFIG_PIC32_ANALOG:
+ writel(BIT(pin), &port->ansel.set);
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ writel(BIT(pin), &port->tris.set);
+ break;
+ case PIN_CONFIG_OUTPUT:
+ writel(BIT(pin), &port->tris.clr);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ writel(BIT(pin), &port->cnpu.set);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ writel(BIT(pin), &port->cnpd.set);
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ writel(BIT(pin), &port->odc.set);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int pic32_pinconfig_set(struct pic32_pinctrl_priv *priv,
+ const struct pic32_pin_config *list, int count)
+{
+ int i;
+
+ for (i = 0 ; i < count; i++)
+ pic32_pinconfig_one(priv, list[i].port,
+ list[i].pin, list[i].config);
+
+ return 0;
+}
+
+static void pic32_eth_pin_config(struct udevice *dev)
+{
+ struct pic32_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct pic32_pin_config configs[] = {
+ /* EMDC - D11 */
+ PIN_CONFIG(PIC32_PORT_D, 11, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_D, 11, PIN_CONFIG_OUTPUT),
+ /* ETXEN */
+ PIN_CONFIG(PIC32_PORT_D, 6, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_D, 6, PIN_CONFIG_OUTPUT),
+ /* ECRSDV */
+ PIN_CONFIG(PIC32_PORT_H, 13, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_H, 13, PIN_CONFIG_INPUT_ENABLE),
+ /* ERXD0 */
+ PIN_CONFIG(PIC32_PORT_H, 8, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_H, 8, PIN_CONFIG_INPUT_ENABLE),
+ PIN_CONFIG(PIC32_PORT_H, 8, PIN_CONFIG_BIAS_PULL_DOWN),
+ /* ERXD1 */
+ PIN_CONFIG(PIC32_PORT_H, 5, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_H, 5, PIN_CONFIG_INPUT_ENABLE),
+ PIN_CONFIG(PIC32_PORT_H, 5, PIN_CONFIG_BIAS_PULL_DOWN),
+ /* EREFCLK */
+ PIN_CONFIG(PIC32_PORT_J, 11, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_J, 11, PIN_CONFIG_INPUT_ENABLE),
+ /* ETXD1 */
+ PIN_CONFIG(PIC32_PORT_J, 9, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_J, 9, PIN_CONFIG_OUTPUT),
+ /* ETXD0 */
+ PIN_CONFIG(PIC32_PORT_J, 8, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_J, 8, PIN_CONFIG_OUTPUT),
+ /* EMDIO */
+ PIN_CONFIG(PIC32_PORT_J, 1, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_J, 1, PIN_CONFIG_INPUT_ENABLE),
+ /* ERXERR */
+ PIN_CONFIG(PIC32_PORT_F, 3, PIN_CONFIG_PIC32_DIGITAL),
+ PIN_CONFIG(PIC32_PORT_F, 3, PIN_CONFIG_INPUT_ENABLE),
+ };
+
+ pic32_pinconfig_set(priv, configs, ARRAY_SIZE(configs));
+}
+
+static int pic32_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+ struct pic32_pinctrl_priv *priv = dev_get_priv(dev);
+
+ switch (func) {
+ case PERIPH_ID_UART2:
+ /* PPS for U2 RX/TX */
+ writel(0x02, priv->mux_out + PPS_OUT(PIC32_PORT_G, 9));
+ writel(0x05, &priv->mux_in->u2rx); /* B0 */
+ /* set digital mode */
+ pic32_pinconfig_one(priv, PIC32_PORT_G, 9,
+ PIN_CONFIG_PIC32_DIGITAL);
+ pic32_pinconfig_one(priv, PIC32_PORT_B, 0,
+ PIN_CONFIG_PIC32_DIGITAL);
+ break;
+ case PERIPH_ID_ETH:
+ pic32_eth_pin_config(dev);
+ break;
+ default:
+ debug("%s: unknown-unhandled case\n", __func__);
+ break;
+ }
+
+ return 0;
+}
+
+static int pic32_pinctrl_get_periph_id(struct udevice *dev,
+ struct udevice *periph)
+{
+ int ret;
+ u32 cell[2];
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+ "interrupts", cell, ARRAY_SIZE(cell));
+ if (ret < 0)
+ return -EINVAL;
+
+ /* interrupt number */
+ switch (cell[0]) {
+ case 112 ... 114:
+ return PERIPH_ID_UART1;
+ case 145 ... 147:
+ return PERIPH_ID_UART2;
+ case 109 ... 111:
+ return PERIPH_ID_SPI1;
+ case 142 ... 144:
+ return PERIPH_ID_SPI2;
+ case 115 ... 117:
+ return PERIPH_ID_I2C1;
+ case 148 ... 150:
+ return PERIPH_ID_I2C2;
+ case 132 ... 133:
+ return PERIPH_ID_USB;
+ case 169:
+ return PERIPH_ID_SQI;
+ case 191:
+ return PERIPH_ID_SDHCI;
+ case 153:
+ return PERIPH_ID_ETH;
+ default:
+ break;
+ }
+
+ return -ENOENT;
+}
+
+static int pic32_pinctrl_set_state_simple(struct udevice *dev,
+ struct udevice *periph)
+{
+ int func;
+
+ debug("%s: periph %s\n", __func__, periph->name);
+ func = pic32_pinctrl_get_periph_id(dev, periph);
+ if (func < 0)
+ return func;
+ return pic32_pinctrl_request(dev, func, 0);
+}
+
+static struct pinctrl_ops pic32_pinctrl_ops = {
+ .set_state_simple = pic32_pinctrl_set_state_simple,
+ .request = pic32_pinctrl_request,
+ .get_periph_id = pic32_pinctrl_get_periph_id,
+};
+
+static int pic32_pinctrl_probe(struct udevice *dev)
+{
+ struct pic32_pinctrl_priv *priv = dev_get_priv(dev);
+ struct fdt_resource res;
+ void *fdt = (void *)gd->fdt_blob;
+ int node = dev->of_offset;
+ int ret;
+
+ ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+ "ppsin", &res);
+ if (ret < 0) {
+ printf("pinctrl: resource \"ppsin\" not found\n");
+ return ret;
+ }
+ priv->mux_in = ioremap(res.start, fdt_resource_size(&res));
+
+ ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+ "ppsout", &res);
+ if (ret < 0) {
+ printf("pinctrl: resource \"ppsout\" not found\n");
+ return ret;
+ }
+ priv->mux_out = ioremap(res.start, fdt_resource_size(&res));
+
+ ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+ "port", &res);
+ if (ret < 0) {
+ printf("pinctrl: resource \"port\" not found\n");
+ return ret;
+ }
+ priv->pinconf = ioremap(res.start, fdt_resource_size(&res));
+
+ return 0;
+}
+
+static int pic32_pinctrl_bind(struct udevice *dev)
+{
+ /* scan child GPIO banks */
+ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+static const struct udevice_id pic32_pinctrl_ids[] = {
+ { .compatible = "microchip,pic32mzda-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_pic32) = {
+ .name = "pinctrl_pic32",
+ .id = UCLASS_PINCTRL,
+ .of_match = pic32_pinctrl_ids,
+ .ops = &pic32_pinctrl_ops,
+ .probe = pic32_pinctrl_probe,
+ .bind = pic32_pinctrl_bind,
+ .priv_auto_alloc_size = sizeof(struct pic32_pinctrl_priv),
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 1ab6128269..fac317610e 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -150,6 +150,14 @@ config DEBUG_UART_PL011
work. The driver will be available until the real driver model
serial is running.
+config DEBUG_UART_PIC32
+ bool "Microchip PIC32"
+ depends on PIC32_SERIAL
+ help
+ Select this to enable a debug UART using the serial_pic32 driver. You
+ will need to provide parameters to make this work. The driver will
+ be available until the real driver model serial is running.
+
endchoice
config DEBUG_UART_BASE
@@ -241,6 +249,13 @@ config FSL_LPUART
Select this to enable a Low Power UART for Freescale VF610 and
QorIQ Layerscape devices.
+config PIC32_SERIAL
+ bool "Support for Microchip PIC32 on-chip UART"
+ depends on DM_SERIAL && MACH_PIC32
+ default y
+ help
+ Support for the UART found on Microchip PIC32 SoC's.
+
config SYS_NS16550
bool "NS16550 UART or compatible"
help
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index dd871478ea..57cd38bf6e 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_MXS_AUART) += mxs_auart.o
obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
+obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c
new file mode 100644
index 0000000000..af9fbbf655
--- /dev/null
+++ b/drivers/serial/serial_pic32.c
@@ -0,0 +1,198 @@
+/*
+ * (c) 2015 Paul Thacker <paul.thacker@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <serial.h>
+#include <wait_bit.h>
+#include <mach/pic32.h>
+#include <dt-bindings/clock/microchip,clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* UART Control Registers */
+#define U_MOD 0x00
+#define U_MODCLR (U_MOD + _CLR_OFFSET)
+#define U_MODSET (U_MOD + _SET_OFFSET)
+#define U_STA 0x10
+#define U_STACLR (U_STA + _CLR_OFFSET)
+#define U_STASET (U_STA + _SET_OFFSET)
+#define U_TXR 0x20
+#define U_RXR 0x30
+#define U_BRG 0x40
+
+/* U_MOD bits */
+#define UART_ENABLE BIT(15)
+
+/* U_STA bits */
+#define UART_RX_ENABLE BIT(12)
+#define UART_TX_BRK BIT(11)
+#define UART_TX_ENABLE BIT(10)
+#define UART_TX_FULL BIT(9)
+#define UART_TX_EMPTY BIT(8)
+#define UART_RX_OVER BIT(1)
+#define UART_RX_DATA_AVAIL BIT(0)
+
+struct pic32_uart_priv {
+ void __iomem *base;
+ ulong uartclk;
+};
+
+/*
+ * Initialize the serial port with the given baudrate.
+ * The settings are always 8 data bits, no parity, 1 stop bit, no start bits.
+ */
+static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate)
+{
+ u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
+
+ /* wait for TX FIFO to empty */
+ wait_for_bit(__func__, base + U_STA, UART_TX_EMPTY,
+ true, CONFIG_SYS_HZ, false);
+
+ /* send break */
+ writel(UART_TX_BRK, base + U_STASET);
+
+ /* disable and clear mode */
+ writel(0, base + U_MOD);
+ writel(0, base + U_STA);
+
+ /* set baud rate generator */
+ writel(div - 1, base + U_BRG);
+
+ /* enable the UART for TX and RX */
+ writel(UART_TX_ENABLE | UART_RX_ENABLE, base + U_STASET);
+
+ /* enable the UART */
+ writel(UART_ENABLE, base + U_MODSET);
+ return 0;
+}
+
+/* Check whether any char pending in RX fifo */
+static int pic32_uart_pending_input(void __iomem *base)
+{
+ /* check if rx buffer overrun error has occurred */
+ if (readl(base + U_STA) & UART_RX_OVER) {
+ readl(base + U_RXR);
+
+ /* clear overrun error to keep receiving */
+ writel(UART_RX_OVER, base + U_STACLR);
+ }
+
+ /* In PIC32 there is no way to know number of outstanding
+ * chars in rx-fifo. Only it can be known whether there is any.
+ */
+ return readl(base + U_STA) & UART_RX_DATA_AVAIL;
+}
+
+static int pic32_uart_pending(struct udevice *dev, bool input)
+{
+ struct pic32_uart_priv *priv = dev_get_priv(dev);
+
+ if (input)
+ return pic32_uart_pending_input(priv->base);
+
+ return !(readl(priv->base + U_STA) & UART_TX_EMPTY);
+}
+
+static int pic32_uart_setbrg(struct udevice *dev, int baudrate)
+{
+ struct pic32_uart_priv *priv = dev_get_priv(dev);
+
+ return pic32_serial_init(priv->base, priv->uartclk, baudrate);
+}
+
+static int pic32_uart_putc(struct udevice *dev, const char ch)
+{
+ struct pic32_uart_priv *priv = dev_get_priv(dev);
+
+ /* Check if Tx FIFO is full */
+ if (readl(priv->base + U_STA) & UART_TX_FULL)
+ return -EAGAIN;
+
+ /* pump the char to tx buffer */
+ writel(ch, priv->base + U_TXR);
+
+ return 0;
+}
+
+static int pic32_uart_getc(struct udevice *dev)
+{
+ struct pic32_uart_priv *priv = dev_get_priv(dev);
+
+ /* return error if RX fifo is empty */
+ if (!pic32_uart_pending_input(priv->base))
+ return -EAGAIN;
+
+ /* read the character from rx buffer */
+ return readl(priv->base + U_RXR) & 0xff;
+}
+
+static int pic32_uart_probe(struct udevice *dev)
+{
+ struct pic32_uart_priv *priv = dev_get_priv(dev);
+ struct udevice *clkdev;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ int ret;
+
+ /* get address */
+ addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->base = ioremap(addr, size);
+
+ /* get clock rate */
+ ret = clk_get_by_index(dev, 0, &clkdev);
+ if (ret < 0)
+ return ret;
+ priv->uartclk = clk_get_periph_rate(clkdev, ret);
+
+ /* initialize serial */
+ return pic32_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE);
+}
+
+static const struct dm_serial_ops pic32_uart_ops = {
+ .putc = pic32_uart_putc,
+ .pending = pic32_uart_pending,
+ .getc = pic32_uart_getc,
+ .setbrg = pic32_uart_setbrg,
+};
+
+static const struct udevice_id pic32_uart_ids[] = {
+ { .compatible = "microchip,pic32mzda-uart" },
+ {}
+};
+
+U_BOOT_DRIVER(pic32_serial) = {
+ .name = "pic32-uart",
+ .id = UCLASS_SERIAL,
+ .of_match = pic32_uart_ids,
+ .probe = pic32_uart_probe,
+ .ops = &pic32_uart_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto_alloc_size = sizeof(struct pic32_uart_priv),
+};
+
+#ifdef CONFIG_DEBUG_UART_PIC32
+#include <debug_uart.h>
+
+static inline void _debug_uart_init(void)
+{
+ void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
+
+ pic32_serial_init(base, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
+}
+
+static inline void _debug_uart_putc(int ch)
+{
+ writel(ch, CONFIG_DEBUG_UART_BASE + U_TXR);
+}
+
+DEBUG_UART_FUNCS
+#endif
OpenPOWER on IntegriCloud