Merge branch 'master' of git://git.denx.de/u-boot-mips
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 1b39c4c..f852a1f 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -23,6 +23,7 @@
config TARGET_MALTA
bool "Support malta"
+ select DYNAMIC_IO_PORT_BASE
select SUPPORTS_BIG_ENDIAN
select SUPPORTS_LITTLE_ENDIAN
select SUPPORTS_CPU_MIPS32_R1
@@ -54,6 +55,11 @@
select SYS_MIPS_CACHE_INIT_RAM_LOAD
select MIPS_TUNE_4KC
+config MACH_PIC32
+ bool "Support Microchip PIC32"
+ select OF_CONTROL
+ select DM
+
endchoice
source "board/dbau1x00/Kconfig"
@@ -61,6 +67,7 @@
source "board/micronas/vct/Kconfig"
source "board/pb1x00/Kconfig"
source "board/qemu-mips/Kconfig"
+source "arch/mips/mach-pic32/Kconfig"
if MIPS
@@ -217,6 +224,9 @@
default "4" if MIPS_L1_CACHE_SHIFT_4
default "5"
+config DYNAMIC_IO_PORT_BASE
+ bool
+
endif
endmenu
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 2133e7e..aec5a15 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -8,6 +8,7 @@
libs-y += arch/mips/lib/
machine-$(CONFIG_SOC_AU1X00) += au1x00
+machine-$(CONFIG_MACH_PIC32) += pic32
machdirs := $(patsubst %,arch/mips/mach-%/,$(machine-y))
libs-y += $(machdirs)
diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S
index e95cdca..d2c31ae 100644
--- a/arch/mips/cpu/start.S
+++ b/arch/mips/cpu/start.S
@@ -115,7 +115,7 @@
/* Clear watch registers */
MTC0 zero, CP0_WATCHLO
- MTC0 zero, CP0_WATCHHI
+ mtc0 zero, CP0_WATCHHI
/* WP(Watch Pending), SW0/1 should be cleared */
mtc0 zero, CP0_CAUSE
@@ -161,14 +161,14 @@
#endif
/* Set up temporary stack */
- PTR_LI t0, -16
+ li t0, -16
PTR_LI t1, CONFIG_SYS_INIT_SP_ADDR
and sp, t1, t0 # force 16 byte alignment
PTR_SUB sp, sp, GD_SIZE # reserve space for gd
and sp, sp, t0 # force 16 byte alignment
move k0, sp # save gd pointer
#ifdef CONFIG_SYS_MALLOC_F_LEN
- PTR_LI t2, CONFIG_SYS_MALLOC_F_LEN
+ li t2, CONFIG_SYS_MALLOC_F_LEN
PTR_SUB sp, sp, t2 # reserve space for early malloc
and sp, sp, t0 # force 16 byte alignment
#endif
@@ -177,15 +177,15 @@
/* Clear gd */
move t0, k0
1:
- sw zero, 0(t0)
+ PTR_S zero, 0(t0)
blt t0, t1, 1b
- PTR_ADDI t0, 4
+ PTR_ADDI t0, PTRSIZE
#ifdef CONFIG_SYS_MALLOC_F_LEN
- PTR_ADDU t0, k0, GD_MALLOC_BASE # gd->malloc_base offset
- sw sp, 0(t0)
+ PTR_S sp, GD_MALLOC_BASE(k0) # gd->malloc_base offset
#endif
+ move a0, zero # a0 <-- boot_flags = 0
PTR_LA t9, board_init_f
jr t9
move ra, zero
@@ -224,11 +224,11 @@
* t2 = source end address
*/
1:
- lw t3, 0(t0)
- sw t3, 0(t1)
- PTR_ADDU t0, 4
+ PTR_L t3, 0(t0)
+ PTR_S t3, 0(t1)
+ PTR_ADDU t0, PTRSIZE
blt t0, t2, 1b
- PTR_ADDU t1, 4
+ PTR_ADDU t1, PTRSIZE
/* If caches were enabled, we would have to flush them here. */
PTR_SUB a1, t1, s2 # a1 <-- size
diff --git a/arch/mips/dts/Makefile b/arch/mips/dts/Makefile
index 47b6eb5..b513918 100644
--- a/arch/mips/dts/Makefile
+++ b/arch/mips/dts/Makefile
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
-dtb-y +=
+dtb-$(CONFIG_TARGET_PIC32MZDASK) += pic32mzda_sk.dtb
targets += $(dtb-y)
diff --git a/arch/mips/dts/pic32mzda.dtsi b/arch/mips/dts/pic32mzda.dtsi
new file mode 100644
index 0000000..7d180d9
--- /dev/null
+++ b/arch/mips/dts/pic32mzda.dtsi
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 Microchip Technology, Inc.
+ * Purna Chandra Mandal, <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/clock/microchip,clock.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "skeleton.dtsi"
+
+/ {
+ compatible = "microchip,pic32mzda", "microchip,pic32mz";
+
+ aliases {
+ gpio0 = &gpioA;
+ gpio1 = &gpioB;
+ gpio2 = &gpioC;
+ gpio3 = &gpioD;
+ gpio4 = &gpioE;
+ gpio5 = &gpioF;
+ gpio6 = &gpioG;
+ gpio7 = &gpioH;
+ gpio8 = &gpioJ;
+ gpio9 = &gpioK;
+ };
+
+ cpus {
+ cpu@0 {
+ compatible = "mips,mips14kc";
+ };
+ };
+
+ clock: clk@1f801200 {
+ compatible = "microchip,pic32mzda-clk";
+ reg = <0x1f801200 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ uart1: serial@1f822000 {
+ compatible = "microchip,pic32mzda-uart";
+ reg = <0x1f822000 0x50>;
+ interrupts = <112 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clock PB2CLK>;
+ };
+
+ uart2: serial@1f822200 {
+ compatible = "microchip,pic32mzda-uart";
+ reg = <0x1f822200 0x50>;
+ interrupts = <145 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clock PB2CLK>;
+ status = "disabled";
+ };
+
+ uart6: serial@1f822a00 {
+ compatible = "microchip,pic32mzda-uart";
+ reg = <0x1f822a00 0x50>;
+ interrupts = <188 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clock PB2CLK>;
+ status = "disabled";
+ };
+
+ evic: interrupt-controller@1f810000 {
+ compatible = "microchip,pic32mzda-evic";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x1f810000 0x1000>;
+ };
+
+ pinctrl: pinctrl@1f801400 {
+ compatible = "microchip,pic32mzda-pinctrl";
+ reg = <0x1f801400 0x100>, /* in */
+ <0x1f801500 0x200>, /* out */
+ <0x1f860000 0xa00>; /* port */
+ reg-names = "ppsin","ppsout","port";
+ status = "disabled";
+
+ ranges = <0 0x1f860000 0xa00>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ gpioA: gpio0@0 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x000 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioB: gpio1@100 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x100 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioC: gpio2@200 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x200 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioD: gpio3@300 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x300 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioE: gpio4@400 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x400 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioF: gpio5@500 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x500 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioG: gpio6@600 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x600 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioH: gpio7@700 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x700 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioJ: gpio8@800 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x800 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpioK: gpio9@900 {
+ compatible = "microchip,pic32mzda-gpio";
+ reg = <0x900 0x48>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+ sdhci: sdhci@1f8ec000 {
+ compatible = "microchip,pic32mzda-sdhci";
+ reg = <0x1f8ec000 0x100>;
+ interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clock REF4CLK>, <&clock PB5CLK>;
+ clock-names = "base_clk", "sys_clk";
+ clock-freq-min-max = <25000000>,<25000000>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ ethernet: ethernet@1f882000 {
+ compatible = "microchip,pic32mzda-eth";
+ reg = <0x1f882000 0x1000>;
+ interrupts = <153 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clock PB5CLK>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+};
diff --git a/arch/mips/dts/pic32mzda_sk.dts b/arch/mips/dts/pic32mzda_sk.dts
new file mode 100644
index 0000000..e5ce0bd
--- /dev/null
+++ b/arch/mips/dts/pic32mzda_sk.dts
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Purna Chandra Mandal, purna.mandal@microchip.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/dts-v1/;
+
+#include "pic32mzda.dtsi"
+
+/ {
+ model = "Microchip PIC32MZDASK";
+ compatible = "microchip,pic32mzdask", "microchip,pic32mzda";
+
+ aliases {
+ console = &uart2;
+ serial0 = &uart2;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+};
+
+&clock {
+ microchip,refo2-frequency = <50000000>;
+ microchip,refo4-frequency = <25000000>;
+ microchip,refo5-frequency = <40000000>;
+ status = "okay";
+ u-boot,dm-pre-reloc;
+};
+
+&pinctrl {
+ status = "okay";
+ u-boot,dm-pre-reloc;
+};
+
+&uart2 {
+ status = "okay";
+ u-boot,dm-pre-reloc;
+};
+
+&sdhci {
+ status = "okay";
+};
+
+ðernet {
+ reset-gpios = <&gpioJ 15 0>;
+ status = "okay";
+ phy-mode = "rmii";
+ phy-handle = <ðernet_phy>;
+ ethernet_phy: lan8740_phy@0 {
+ reg = <0>;
+ };
+};
\ No newline at end of file
diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h
index 2d9a0c9..a1ca257 100644
--- a/arch/mips/include/asm/global_data.h
+++ b/arch/mips/include/asm/global_data.h
@@ -12,6 +12,9 @@
/* Architecture-specific global data */
struct arch_global_data {
+#ifdef CONFIG_DYNAMIC_IO_PORT_BASE
+ unsigned long io_port_base;
+#endif
#ifdef CONFIG_JZSOC
/* There are other clocks in the jz4740 */
unsigned long per_clk; /* Peripheral bus clock */
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index f71e342..723a60a 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -26,11 +26,6 @@
#include <spaces.h>
/*
- * Slowdown I/O port space accesses for antique hardware.
- */
-#undef CONF_SLOWDOWN_IO
-
-/*
* Raw operations are never swapped in software. OTOH values that raw
* operations are working on may or may not have been swapped by the bus
* hardware. An example use would be for flash memory that's used for
@@ -46,57 +41,36 @@
#define IO_SPACE_LIMIT 0xffff
-/*
- * On MIPS I/O ports are memory mapped, so we access them using normal
- * load/store instructions. mips_io_port_base is the virtual address to
- * which all ports are being mapped. For sake of efficiency some code
- * assumes that this is an address that can be loaded with a single lui
- * instruction, so the lower 16 bits must be zero. Should be true on
- * on any sane architecture; generic code does not use this assumption.
- */
-extern const unsigned long mips_io_port_base;
+#ifdef CONFIG_DYNAMIC_IO_PORT_BASE
-/*
- * Gcc will generate code to load the value of mips_io_port_base after each
- * function call which may be fairly wasteful in some cases. So we don't
- * play quite by the book. We tell gcc mips_io_port_base is a long variable
- * which solves the code generation issue. Now we need to violate the
- * aliasing rules a little to make initialization possible and finally we
- * will need the barrier() to fight side effects of the aliasing chat.
- * This trickery will eventually collapse under gcc's optimizer. Oh well.
- */
+static inline ulong mips_io_port_base(void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ return gd->arch.io_port_base;
+}
+
static inline void set_io_port_base(unsigned long base)
{
- * (unsigned long *) &mips_io_port_base = base;
+ DECLARE_GLOBAL_DATA_PTR;
+
+ gd->arch.io_port_base = base;
barrier();
}
-/*
- * Thanks to James van Artsdalen for a better timing-fix than
- * the two short jumps: using outb's to a nonexistent port seems
- * to guarantee better timings even on fast machines.
- *
- * On the other hand, I'd like to be sure of a non-existent port:
- * I feel a bit unsafe about using 0x80 (should be safe, though)
- *
- * Linus
- *
- */
+#else /* !CONFIG_DYNAMIC_IO_PORT_BASE */
-#define __SLOW_DOWN_IO \
- __asm__ __volatile__( \
- "sb\t$0,0x80(%0)" \
- : : "r" (mips_io_port_base));
+static inline ulong mips_io_port_base(void)
+{
+ return 0;
+}
-#ifdef CONF_SLOWDOWN_IO
-#ifdef REALLY_SLOW_IO
-#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
-#else
-#define SLOW_DOWN_IO __SLOW_DOWN_IO
-#endif
-#else
-#define SLOW_DOWN_IO
-#endif
+static inline void set_io_port_base(unsigned long base)
+{
+ BUG_ON(base);
+}
+
+#endif /* !CONFIG_DYNAMIC_IO_PORT_BASE */
/*
* virt_to_phys - map virtual addresses to physical
@@ -316,7 +290,7 @@
return pfx##ioswab##bwlq(__mem, __val); \
}
-#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, p, slow) \
+#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, p) \
\
static inline void pfx##out##bwlq##p(type val, unsigned long port) \
{ \
@@ -325,7 +299,7 @@
\
war_octeon_io_reorder_wmb(); \
\
- __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \
+ __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base() + port); \
\
__val = pfx##ioswab##bwlq(__addr, val); \
\
@@ -333,7 +307,6 @@
BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \
\
*__addr = __val; \
- slow; \
} \
\
static inline type pfx##in##bwlq##p(unsigned long port) \
@@ -341,12 +314,11 @@
volatile type *__addr; \
type __val; \
\
- __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \
+ __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base() + port); \
\
BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \
\
__val = *__addr; \
- slow; \
\
return pfx##ioswab##bwlq(__addr, __val); \
}
@@ -367,8 +339,8 @@
BUILDIO_MEM(q, u64)
#define __BUILD_IOPORT_PFX(bus, bwlq, type) \
- __BUILD_IOPORT_SINGLE(bus, bwlq, type, ,) \
- __BUILD_IOPORT_SINGLE(bus, bwlq, type, _p, SLOW_DOWN_IO)
+ __BUILD_IOPORT_SINGLE(bus, bwlq, type, ) \
+ __BUILD_IOPORT_SINGLE(bus, bwlq, type, _p)
#define BUILDIO_IOPORT(bwlq, type) \
__BUILD_IOPORT_PFX(, bwlq, type) \
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index ac536da..b7ce5df 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -7,7 +7,6 @@
obj-y += cache.o
obj-y += cache_init.o
-obj-y += io.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c
index bf8ff59..7482005 100644
--- a/arch/mips/lib/cache.c
+++ b/arch/mips/lib/cache.c
@@ -95,6 +95,10 @@
const void *addr = (const void *)(start_addr & ~(lsize - 1));
const void *aend = (const void *)((stop - 1) & ~(lsize - 1));
+ /* aend will be miscalculated when size is zero, so we return here */
+ if (start_addr == stop)
+ return;
+
while (1) {
mips_cache(HIT_WRITEBACK_INV_D, addr);
if (addr == aend)
@@ -109,6 +113,10 @@
const void *addr = (const void *)(start_addr & ~(lsize - 1));
const void *aend = (const void *)((stop - 1) & ~(lsize - 1));
+ /* aend will be miscalculated when size is zero, so we return here */
+ if (start_addr == stop)
+ return;
+
while (1) {
mips_cache(HIT_INVALIDATE_D, addr);
if (addr == aend)
diff --git a/arch/mips/lib/io.c b/arch/mips/lib/io.c
deleted file mode 100644
index b2d4a09..0000000
--- a/arch/mips/lib/io.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * (C) Copyright 2003
- * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-/*
- * mips_io_port_base is the begin of the address space to which x86 style
- * I/O ports are mapped.
- */
-const unsigned long mips_io_port_base = -1;
diff --git a/arch/mips/mach-pic32/Kconfig b/arch/mips/mach-pic32/Kconfig
new file mode 100644
index 0000000..2e38bb7
--- /dev/null
+++ b/arch/mips/mach-pic32/Kconfig
@@ -0,0 +1,35 @@
+menu "Microchip PIC32 platforms"
+ depends on MACH_PIC32
+
+config SYS_SOC
+ default "pic32mzda" if SOC_PIC32MZDA
+
+choice
+ prompt "PIC32 SoC select"
+
+config SOC_PIC32MZDA
+ bool "Microchip PIC32MZ[DA] family"
+ select SUPPORTS_LITTLE_ENDIAN
+ select SUPPORTS_CPU_MIPS32_R1
+ select SUPPORTS_CPU_MIPS32_R2
+ select MIPS_L1_CACHE_SHIFT_4
+ select SYS_MIPS_CACHE_INIT_RAM_LOAD
+ help
+ This supports Microchip PIC32MZ[DA] family of microcontrollers.
+
+endchoice
+
+choice
+ prompt "Board select"
+
+config TARGET_PIC32MZDASK
+ bool "Microchip PIC32MZ[DA] Starter Kit"
+ depends on SOC_PIC32MZDA
+ help
+ This supports Microchip PIC32MZ[DA] Starter Kit.
+
+endchoice
+
+source "board/microchip/pic32mzda/Kconfig"
+
+endmenu
diff --git a/arch/mips/mach-pic32/Makefile b/arch/mips/mach-pic32/Makefile
new file mode 100644
index 0000000..e321e65
--- /dev/null
+++ b/arch/mips/mach-pic32/Makefile
@@ -0,0 +1,7 @@
+# (C) Copyright 2015
+# Purna Chandra Mandal, purna.mandal@microchip.com.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y = cpu.o lowlevel_init.o reset.o
\ No newline at end of file
diff --git a/arch/mips/mach-pic32/cpu.c b/arch/mips/mach-pic32/cpu.c
new file mode 100644
index 0000000..f2ee911
--- /dev/null
+++ b/arch/mips/mach-pic32/cpu.c
@@ -0,0 +1,156 @@
+/*
+ * 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 <mach/pic32.h>
+#include <mach/ddr.h>
+#include <dt-bindings/clock/microchip,clock.h>
+
+/* Flash prefetch */
+#define PRECON 0x00
+
+/* Flash ECCCON */
+#define ECC_MASK 0x03
+#define ECC_SHIFT 4
+
+#define CLK_MHZ(x) ((x) / 1000000)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong clk_get_cpu_rate(void)
+{
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+ if (ret) {
+ panic("uclass-clk: device not found\n");
+ return 0;
+ }
+
+ return clk_get_rate(dev);
+}
+
+/* initialize prefetch module related to cpu_clk */
+static void prefetch_init(void)
+{
+ struct pic32_reg_atomic *regs;
+ const void __iomem *base;
+ int v, nr_waits;
+ ulong rate;
+
+ /* cpu frequency in MHZ */
+ rate = clk_get_cpu_rate() / 1000000;
+
+ /* get flash ECC type */
+ base = pic32_get_syscfg_base();
+ v = (readl(base + CFGCON) >> ECC_SHIFT) & ECC_MASK;
+
+ if (v < 2) {
+ if (rate < 66)
+ nr_waits = 0;
+ else if (rate < 133)
+ nr_waits = 1;
+ else
+ nr_waits = 2;
+ } else {
+ if (rate <= 83)
+ nr_waits = 0;
+ else if (rate <= 166)
+ nr_waits = 1;
+ else
+ nr_waits = 2;
+ }
+
+ regs = ioremap(PREFETCH_BASE + PRECON, sizeof(*regs));
+ writel(nr_waits, ®s->raw);
+
+ /* Enable prefetch for all */
+ writel(0x30, ®s->set);
+ iounmap(regs);
+}
+
+/* arch specific CPU init after DM */
+int arch_cpu_init_dm(void)
+{
+ /* flash prefetch */
+ prefetch_init();
+ return 0;
+}
+
+/* Un-gate DDR2 modules (gated by default) */
+static void ddr2_pmd_ungate(void)
+{
+ void __iomem *regs;
+
+ regs = pic32_get_syscfg_base();
+ writel(0, regs + PMD7);
+}
+
+/* initialize the DDR2 Controller and DDR2 PHY */
+phys_size_t initdram(int board_type)
+{
+ ddr2_pmd_ungate();
+ ddr2_phy_init();
+ ddr2_ctrl_init();
+ return ddr2_calculate_size();
+}
+
+int misc_init_r(void)
+{
+ set_io_port_base(0);
+ return 0;
+}
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+const char *get_core_name(void)
+{
+ u32 proc_id;
+ const char *str;
+
+ proc_id = read_c0_prid();
+ switch (proc_id) {
+ case 0x19e28:
+ str = "PIC32MZ[DA]";
+ break;
+ default:
+ str = "UNKNOWN";
+ }
+
+ return str;
+}
+#endif
+#ifdef CONFIG_CMD_CLK
+int soc_clk_dump(void)
+{
+ int i, ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+ if (ret) {
+ printf("clk-uclass not found\n");
+ return ret;
+ }
+
+ printf("PLL Speed: %lu MHz\n",
+ CLK_MHZ(clk_get_periph_rate(dev, PLLCLK)));
+ printf("CPU Speed: %lu MHz\n", CLK_MHZ(clk_get_rate(dev)));
+ printf("MPLL Speed: %lu MHz\n",
+ CLK_MHZ(clk_get_periph_rate(dev, MPLL)));
+
+ for (i = PB1CLK; i <= PB7CLK; i++)
+ printf("PB%d Clock Speed: %lu MHz\n", i - PB1CLK + 1,
+ CLK_MHZ(clk_get_periph_rate(dev, i)));
+
+ for (i = REF1CLK; i <= REF5CLK; i++)
+ printf("REFO%d Clock Speed: %lu MHz\n", i - REF1CLK + 1,
+ CLK_MHZ(clk_get_periph_rate(dev, i)));
+ return 0;
+}
+#endif
diff --git a/arch/mips/mach-pic32/include/mach/ddr.h b/arch/mips/mach-pic32/include/mach/ddr.h
new file mode 100644
index 0000000..00abfa3
--- /dev/null
+++ b/arch/mips/mach-pic32/include/mach/ddr.h
@@ -0,0 +1,32 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __MICROCHIP_PIC32_DDR_H
+#define __MICROCHIP_PIC32_DDR_H
+
+/* called by initdram() function */
+void ddr2_phy_init(void);
+void ddr2_ctrl_init(void);
+phys_size_t ddr2_calculate_size(void);
+
+/* Maximum number of agents */
+#define NUM_AGENTS 5
+
+/* Board can provide agent specific parameters for arbitration by
+ * filling struct ddr2_arbiter_params for all the agents and
+ * implementing board_get_ddr_arbiter_params() to return the filled
+ * structure.
+ */
+struct ddr2_arbiter_params {
+ u32 min_limit; /* min bursts to execute per arbitration */
+ u32 req_period; /* request period threshold for accepted cmds */
+ u32 min_cmd_acpt; /* min number of accepted cmds */
+};
+
+const struct ddr2_arbiter_params *board_get_ddr_arbiter_params(void);
+
+#endif /* __MICROCHIP_PIC32_DDR_H */
diff --git a/arch/mips/mach-pic32/include/mach/pic32.h b/arch/mips/mach-pic32/include/mach/pic32.h
new file mode 100644
index 0000000..16bfacf
--- /dev/null
+++ b/arch/mips/mach-pic32/include/mach/pic32.h
@@ -0,0 +1,79 @@
+/*
+ * (c) 2015 Paul Thacker <paul.thacker@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __PIC32_REGS_H__
+#define __PIC32_REGS_H__
+
+#include <asm/io.h>
+
+/* System Configuration */
+#define PIC32_CFG_BASE 0x1f800000
+
+/* System config register offsets */
+#define CFGCON 0x0000
+#define DEVID 0x0020
+#define SYSKEY 0x0030
+#define PMD1 0x0040
+#define PMD7 0x00a0
+#define CFGEBIA 0x00c0
+#define CFGEBIC 0x00d0
+#define CFGPG 0x00e0
+#define CFGMPLL 0x0100
+
+/* Non Volatile Memory (NOR flash) */
+#define PIC32_NVM_BASE (PIC32_CFG_BASE + 0x0600)
+/* Oscillator Configuration */
+#define PIC32_OSC_BASE (PIC32_CFG_BASE + 0x1200)
+/* Peripheral Pin Select Input */
+#define PPS_IN_BASE 0x1f801400
+/* Peripheral Pin Select Output */
+#define PPS_OUT_BASE 0x1f801500
+/* Pin Config */
+#define PINCTRL_BASE 0x1f860000
+
+/* USB Core */
+#define PIC32_USB_CORE_BASE 0x1f8e3000
+#define PIC32_USB_CTRL_BASE 0x1f884000
+
+/* SPI1-SPI6 */
+#define PIC32_SPI1_BASE 0x1f821000
+
+/* Prefetch Module */
+#define PREFETCH_BASE 0x1f8e0000
+
+/* DDR2 Controller */
+#define PIC32_DDR2C_BASE 0x1f8e8000
+
+/* DDR2 PHY */
+#define PIC32_DDR2P_BASE 0x1f8e9100
+
+/* EBI */
+#define PIC32_EBI_BASE 0x1f8e1000
+
+/* SQI */
+#define PIC32_SQI_BASE 0x1f8e2000
+
+struct pic32_reg_atomic {
+ u32 raw;
+ u32 clr;
+ u32 set;
+ u32 inv;
+};
+
+#define _CLR_OFFSET 0x04
+#define _SET_OFFSET 0x08
+#define _INV_OFFSET 0x0c
+
+static inline void __iomem *pic32_get_syscfg_base(void)
+{
+ return (void __iomem *)CKSEG1ADDR(PIC32_CFG_BASE);
+}
+
+/* Core */
+const char *get_core_name(void);
+
+#endif /* __PIC32_REGS_H__ */
diff --git a/arch/mips/mach-pic32/lowlevel_init.S b/arch/mips/mach-pic32/lowlevel_init.S
new file mode 100644
index 0000000..e37bebb
--- /dev/null
+++ b/arch/mips/mach-pic32/lowlevel_init.S
@@ -0,0 +1,27 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+*/
+
+#include <config.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/asm.h>
+
+LEAF(lowlevel_init)
+ /*
+ * Establish Cause
+ * (set IV bit)
+ */
+ li t1, 0x00800000
+ mtc0 t1, CP0_CAUSE
+
+ /* Establish Wired (and Random) */
+ mtc0 zero, CP0_WIRED
+ nop
+
+ jr ra
+ nop
+ END(lowlevel_init)
diff --git a/arch/mips/mach-pic32/reset.c b/arch/mips/mach-pic32/reset.c
new file mode 100644
index 0000000..66c6833
--- /dev/null
+++ b/arch/mips/mach-pic32/reset.c
@@ -0,0 +1,36 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <mach/pic32.h>
+
+/* SYSKEY */
+#define UNLOCK_KEY1 0xaa996655
+#define UNLOCK_KEY2 0x556699aa
+#define LOCK_KEY 0
+
+#define RSWRST 0x1250
+
+void _machine_restart(void)
+{
+ void __iomem *base;
+
+ base = pic32_get_syscfg_base();
+
+ /* unlock sequence */
+ writel(LOCK_KEY, base + SYSKEY);
+ writel(UNLOCK_KEY1, base + SYSKEY);
+ writel(UNLOCK_KEY2, base + SYSKEY);
+
+ /* soft reset */
+ writel(0x1, base + RSWRST);
+ (void) readl(base + RSWRST);
+
+ while (1)
+ ;
+}
diff --git a/board/imgtec/malta/malta.c b/board/imgtec/malta/malta.c
index cae4a21..e31331a 100644
--- a/board/imgtec/malta/malta.c
+++ b/board/imgtec/malta/malta.c
@@ -130,24 +130,26 @@
int board_early_init_f(void)
{
- void *io_base;
+ ulong io_base;
/* choose correct PCI I/O base */
switch (malta_sys_con()) {
case SYSCON_GT64120:
- io_base = (void *)CKSEG1ADDR(MALTA_GT_PCIIO_BASE);
+ io_base = CKSEG1ADDR(MALTA_GT_PCIIO_BASE);
break;
case SYSCON_MSC01:
- io_base = (void *)CKSEG1ADDR(MALTA_MSC01_PCIIO_BASE);
+ io_base = CKSEG1ADDR(MALTA_MSC01_PCIIO_BASE);
break;
default:
return -1;
}
+ set_io_port_base(io_base);
+
/* setup FDC37M817 super I/O controller */
- malta_superio_init(io_base);
+ malta_superio_init();
return 0;
}
@@ -179,8 +181,6 @@
switch (malta_sys_con()) {
case SYSCON_GT64120:
- set_io_port_base(CKSEG1ADDR(MALTA_GT_PCIIO_BASE));
-
gt64120_pci_init((void *)CKSEG1ADDR(MALTA_GT_BASE),
0x00000000, 0x00000000, CONFIG_SYS_MEM_SIZE,
0x10000000, 0x10000000, 128 * 1024 * 1024,
@@ -189,8 +189,6 @@
default:
case SYSCON_MSC01:
- set_io_port_base(CKSEG1ADDR(MALTA_MSC01_PCIIO_BASE));
-
msc01_pci_init((void *)CKSEG1ADDR(MALTA_MSC01_PCI_BASE),
0x00000000, 0x00000000, CONFIG_SYS_MEM_SIZE,
MALTA_MSC01_PCIMEM_MAP,
diff --git a/board/imgtec/malta/superio.c b/board/imgtec/malta/superio.c
index eaa14df..7865ae2 100644
--- a/board/imgtec/malta/superio.c
+++ b/board/imgtec/malta/superio.c
@@ -45,19 +45,19 @@
{ SIOCONF_ACTIVATE, 0x01 },
};
-void malta_superio_init(void *io_base)
+void malta_superio_init(void)
{
unsigned i;
/* enter config state */
- writeb(SIOCONF_ENTER_SETUP, io_base + SIO_CONF_PORT);
+ outb(SIOCONF_ENTER_SETUP, SIO_CONF_PORT);
/* configure peripherals */
for (i = 0; i < ARRAY_SIZE(sio_config); i++) {
- writeb(sio_config[i].key, io_base + SIO_CONF_PORT);
- writeb(sio_config[i].data, io_base + SIO_DATA_PORT);
+ outb(sio_config[i].key, SIO_CONF_PORT);
+ outb(sio_config[i].data, SIO_DATA_PORT);
}
/* exit config state */
- writeb(SIOCONF_EXIT_SETUP, io_base + SIO_CONF_PORT);
+ outb(SIOCONF_EXIT_SETUP, SIO_CONF_PORT);
}
diff --git a/board/imgtec/malta/superio.h b/board/imgtec/malta/superio.h
index 1450da5..271c462 100644
--- a/board/imgtec/malta/superio.h
+++ b/board/imgtec/malta/superio.h
@@ -10,6 +10,6 @@
#ifndef __BOARD_MALTA_SUPERIO_H__
#define __BOARD_MALTA_SUPERIO_H__
-extern void malta_superio_init(void *io_base);
+void malta_superio_init(void);
#endif /* __BOARD_MALTA_SUPERIO_H__ */
diff --git a/board/microchip/pic32mzda/Kconfig b/board/microchip/pic32mzda/Kconfig
new file mode 100644
index 0000000..8acb393
--- /dev/null
+++ b/board/microchip/pic32mzda/Kconfig
@@ -0,0 +1,13 @@
+
+if TARGET_PIC32MZDASK
+
+config SYS_BOARD
+ default "pic32mzda"
+
+config SYS_VENDOR
+ default "microchip"
+
+config SYS_CONFIG_NAME
+ default "pic32mzdask"
+
+endif
diff --git a/board/microchip/pic32mzda/MAINTAINERS b/board/microchip/pic32mzda/MAINTAINERS
new file mode 100644
index 0000000..c934f1a
--- /dev/null
+++ b/board/microchip/pic32mzda/MAINTAINERS
@@ -0,0 +1,6 @@
+PIC32MZDASK BOARD
+M: Purna Chandra Mandal <purna.mandal@microchip.com>
+S: Maintained
+F: board/microchip/pic32mzda/
+F: include/configs/pic32mzdask.h
+F: configs/pic32mzdask_defconfig
diff --git a/board/microchip/pic32mzda/Makefile b/board/microchip/pic32mzda/Makefile
new file mode 100644
index 0000000..3629530
--- /dev/null
+++ b/board/microchip/pic32mzda/Makefile
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2015
+# Purna Chandra Mandal, purna.mandal@microchip.com.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+obj-y := pic32mzda.o
diff --git a/board/microchip/pic32mzda/README b/board/microchip/pic32mzda/README
new file mode 100644
index 0000000..91d16ab
--- /dev/null
+++ b/board/microchip/pic32mzda/README
@@ -0,0 +1,22 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ */
+
+PIC32MZ[DA] Starter Kit
+----------------------------------------
+PIC32MZ[DA] Starter Kit is based on PIC32MZ[DA] family of micro-controller.
+This family is powered by MIPS M14KEC 32bit general purpose core and has
+advanced microcontroller features and peripherals.
+
+This processor boots with proprietary stage1 bootloader running from internal
+boot-flash. Stage1 bootloader inturns locates and jumps to U-Boot programmed
+on internal program-flash. Finally U-Boot loads OS image (along with other
+required files for booting) from either uSD card, or ethernet, or from USB
+storage.
+
+To boot Linux following three files are mandatory - uEnv.txt (custom U-Boot
+environment file), uImage, *.dtb (platform device-tree-blob file).
+
+U-Boot jumps to Linux using UHI specification.
+
+Visit http://microchip.com for details.
diff --git a/board/microchip/pic32mzda/pic32mzda.c b/board/microchip/pic32mzda/pic32mzda.c
new file mode 100644
index 0000000..afe2ab8
--- /dev/null
+++ b/board/microchip/pic32mzda/pic32mzda.c
@@ -0,0 +1,31 @@
+/*
+ * Microchip PIC32MZ[DA] Starter Kit board
+ *
+ * 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 <clk.h>
+#include <mach/pic32.h>
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+int checkboard(void)
+{
+ ulong rate = 0;
+ struct udevice *dev;
+
+ printf("Core: %s\n", get_core_name());
+
+ if (!uclass_get_device(UCLASS_CLK, 0, &dev)) {
+ rate = clk_get_rate(dev);
+ printf("CPU Speed: %lu MHz\n", rate / 1000000);
+ }
+
+ return 0;
+}
+#endif
diff --git a/configs/pic32mzdask_defconfig b/configs/pic32mzdask_defconfig
new file mode 100644
index 0000000..169a2ac
--- /dev/null
+++ b/configs/pic32mzdask_defconfig
@@ -0,0 +1,34 @@
+CONFIG_MIPS=y
+CONFIG_SYS_MALLOC_F_LEN=0x600
+CONFIG_DM_SERIAL=y
+CONFIG_DM_GPIO=y
+CONFIG_MACH_PIC32=y
+# CONFIG_MIPS_BOOT_ENV_LEGACY is not set
+CONFIG_MIPS_BOOT_FDT=y
+CONFIG_DEFAULT_DEVICE_TREE="pic32mzda_sk"
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="dask # "
+# CONFIG_CMD_IMLS is not set
+# CONFIG_CMD_SAVEENV is not set
+CONFIG_LOOPW=y
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_MEMINFO=y
+# CONFIG_CMD_FLASH is not set
+# CONFIG_CMD_FPGA is not set
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_RARP=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_TIME=y
+CONFIG_OF_EMBED=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_CLK=y
+CONFIG_DM_MMC=y
+CONFIG_PIC32_SDHCI=y
+CONFIG_DM_ETH=y
+CONFIG_PIC32_ETH=y
+CONFIG_PINCTRL=y
+# CONFIG_PINCTRL_FULL is not set
+CONFIG_SYS_VSNPRINTF=y
+CONFIG_USE_TINY_PRINTF=y
+CONFIG_CMD_DHRYSTONE=y
diff --git a/doc/device-tree-bindings/clock/microchip,pic32-clock.txt b/doc/device-tree-bindings/clock/microchip,pic32-clock.txt
new file mode 100644
index 0000000..f185ce0
--- /dev/null
+++ b/doc/device-tree-bindings/clock/microchip,pic32-clock.txt
@@ -0,0 +1,33 @@
+* Microchip PIC32 Clock and Oscillator
+
+Microchip PIC32 clock tree consists of few oscillators, PLLs,
+multiplexers and few divider modules capable of supplying clocks
+to various controllers within SoC and also to off-chip.
+
+PIC32 clock controller output is defined by indices as defined
+in [0]
+
+[0] include/dt-bindings/clock/microchip,clock.h
+
+Required Properties:
+- compatible: should be "microchip,pic32mzda_clk"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- #clock-cells: should be 1.
+
+Example: Clock controller node:
+
+ clock: clk@1f801200 {
+ compatible = "microchip,pic32mzda-clk";
+ reg = <0x1f801200 0x1000>;
+ };
+
+Example: UART controller node that consumes the clock generated by the clock
+controller:
+
+ uart1: serial@1f822000 {
+ compatible = "microchip,pic32mzda-uart";
+ reg = <0xbf822000 0x50>;
+ interrupts = <112 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clock PB2CLK>;
+ };
diff --git a/doc/device-tree-bindings/serial/microchip,pic32-uart.txt b/doc/device-tree-bindings/serial/microchip,pic32-uart.txt
new file mode 100644
index 0000000..f00e215
--- /dev/null
+++ b/doc/device-tree-bindings/serial/microchip,pic32-uart.txt
@@ -0,0 +1,5 @@
+* Microchip PIC32 serial UART
+
+Required properties:
+- compatible: must be "microchip,pic32mzda-uart".
+- reg: exactly one register range.
diff --git a/drivers/Makefile b/drivers/Makefile
index 6294048..e7eab66 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -69,4 +69,5 @@
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 8aa81f4..c9144e3 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,3 +9,4 @@
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 0000000..5d88354
--- /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 0000000..305c48b
--- /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 0000000..6056418
--- /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 0000000..0f4b159
--- /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 0000000..5895f9d
--- /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 e60e9fd..845dc72 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -83,4 +83,11 @@
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 fb4fd25..845a6d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -46,4 +46,4 @@
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 0000000..499b4fa
--- /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 ceae7bc..9f4b766 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -31,4 +31,10 @@
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 5d35705..c9c3e3e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -48,4 +48,4 @@
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 0000000..28da55d
--- /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 ff770b1..8586d89 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -443,6 +443,12 @@
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 @@
(!(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 218e1fe..bc2f51d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -125,4 +125,12 @@
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 150470c..33a81ee 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -72,3 +72,4 @@
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 bfd9815..34986a2 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -69,11 +69,21 @@
.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 0000000..167af8b
--- /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 0000000..be2a187
--- /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 0000000..578fc96
--- /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 57e6142..5dd2ddd 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -131,6 +131,16 @@
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 70d25dc..b4f4650 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -9,3 +9,4 @@
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 0000000..5cf97ec
--- /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 1ab6128..fac3176 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -150,6 +150,14 @@
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 @@
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 dd87147..57cd38b 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -41,6 +41,7 @@
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 0000000..af9fbbf
--- /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
diff --git a/include/configs/pic32mzdask.h b/include/configs/pic32mzdask.h
new file mode 100644
index 0000000..3ea1194
--- /dev/null
+++ b/include/configs/pic32mzdask.h
@@ -0,0 +1,168 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Microchip PIC32MZ[DA] Starter Kit.
+ */
+
+#ifndef __PIC32MZDASK_CONFIG_H
+#define __PIC32MZDASK_CONFIG_H
+
+/* System Configuration */
+#define CONFIG_SYS_TEXT_BASE 0x9d004000 /* .text */
+#define CONFIG_DISPLAY_BOARDINFO
+
+/*--------------------------------------------
+ * CPU configuration
+ */
+/* CPU Timer rate */
+#define CONFIG_SYS_MIPS_TIMER_FREQ 100000000
+
+/* Cache Configuration */
+#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT
+
+/*----------------------------------------------------------------------
+ * Memory Layout
+ */
+#define CONFIG_SYS_SRAM_BASE 0x80000000
+#define CONFIG_SYS_SRAM_SIZE 0x00080000 /* 512K */
+
+/* Initial RAM for temporary stack, global data */
+#define CONFIG_SYS_INIT_RAM_SIZE 0x10000
+#define CONFIG_SYS_INIT_RAM_ADDR \
+ (CONFIG_SYS_SRAM_BASE + CONFIG_SYS_SRAM_SIZE - CONFIG_SYS_INIT_RAM_SIZE)
+#define CONFIG_SYS_INIT_SP_ADDR \
+ (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_RAM_SIZE - 1)
+
+/* SDRAM Configuration (for final code, data, stack, heap) */
+#define CONFIG_SYS_SDRAM_BASE 0x88000000
+#define CONFIG_SYS_MALLOC_LEN (256 << 10)
+#define CONFIG_SYS_BOOTPARAMS_LEN (4 << 10)
+#define CONFIG_STACKSIZE (4 << 10) /* regular stack */
+
+#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE
+#define CONFIG_SYS_MONITOR_LEN (192 << 10)
+
+#define CONFIG_SYS_LOAD_ADDR 0x88500000 /* default load address */
+#define CONFIG_SYS_ENV_ADDR 0x88300000
+#define CONFIG_SYS_FDT_ADDR 0x89d00000
+
+/* Memory Test */
+#define CONFIG_SYS_MEMTEST_START 0x88000000
+#define CONFIG_SYS_MEMTEST_END 0x88080000
+
+/*----------------------------------------------------------------------
+ * Commands
+ */
+#define CONFIG_SYS_LONGHELP /* undef to save memory */
+#define CONFIG_CMD_CLK
+
+/*-------------------------------------------------
+ * FLASH configuration
+ */
+#define CONFIG_SYS_NO_FLASH
+
+/*------------------------------------------------------------
+ * Console Configuration
+ */
+#define CONFIG_BAUDRATE 115200
+#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */
+#define CONFIG_SYS_MAXARGS 16 /* max number of command args*/
+#define CONFIG_SYS_PBSIZE \
+ (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
+#define CONFIG_CMDLINE_EDITING 1
+
+/*-----------------------------------------------------------------------
+ * Networking Configuration
+ */
+#define CONFIG_MII
+#define CONFIG_PHY_SMSC
+#define CONFIG_SYS_RX_ETH_BUFFER 8
+#define CONFIG_NET_RETRY_COUNT 20
+#define CONFIG_ARP_TIMEOUT 500 /* millisec */
+
+#define CONFIG_CMD_MII
+
+/*
+ * BOOTP options
+ */
+#define CONFIG_BOOTP_BOOTFILESIZE
+#define CONFIG_BOOTP_BOOTPATH
+#define CONFIG_BOOTP_GATEWAY
+#define CONFIG_BOOTP_HOSTNAME
+
+/*
+ * Handover flattened device tree (dtb file) to Linux kernel
+ */
+#define CONFIG_OF_LIBFDT 1
+
+/*-----------------------------------------------------------------------
+ * SDHC Configuration
+ */
+#define CONFIG_SDHCI
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_CMD_MMC
+
+/*-----------------------------------------------------------------------
+ * File System Configuration
+ */
+/* FAT FS */
+#define CONFIG_DOS_PARTITION
+#define CONFIG_PARTITION_UUIDS
+#define CONFIG_SUPPORT_VFAT
+#define CONFIG_FS_FAT
+#define CONFIG_FAT_WRITE
+#define CONFIG_CMD_FS_GENERIC
+#define CONFIG_CMD_PART
+#define CONFIG_CMD_FAT
+
+/* EXT4 FS */
+#define CONFIG_FS_EXT4
+#define CONFIG_CMD_EXT2
+#define CONFIG_CMD_EXT4
+#define CONFIG_CMD_EXT4_WRITE
+
+/* -------------------------------------------------
+ * Environment
+ */
+#define CONFIG_ENV_IS_NOWHERE 1
+#define CONFIG_ENV_SIZE 0x4000
+
+/* ---------------------------------------------------------------------
+ * Board boot configuration
+ */
+#define CONFIG_TIMESTAMP /* Print image info with timestamp */
+#define CONFIG_BOOTDELAY 5
+
+#define MEM_LAYOUT_ENV_SETTINGS \
+ "kernel_addr_r="__stringify(CONFIG_SYS_LOAD_ADDR)"\0" \
+ "fdt_addr_r="__stringify(CONFIG_SYS_FDT_ADDR)"\0" \
+ "scriptaddr="__stringify(CONFIG_SYS_ENV_ADDR)"\0"
+
+#define CONFIG_LEGACY_BOOTCMD_ENV \
+ "legacy_bootcmd= " \
+ "if load mmc 0 ${scriptaddr} uEnv.txt; then " \
+ "env import -tr ${scriptaddr} ${filesize}; " \
+ "if test -n \"${bootcmd_uenv}\" ; then " \
+ "echo Running bootcmd_uenv ...; " \
+ "run bootcmd_uenv; " \
+ "fi; " \
+ "fi; \0"
+
+#define BOOT_TARGET_DEVICES(func) \
+ func(MMC, mmc, 0) \
+ func(DHCP, dhcp, na)
+
+#include <config_distro_bootcmd.h>
+
+#define CONFIG_EXTRA_ENV_SETTINGS \
+ MEM_LAYOUT_ENV_SETTINGS \
+ CONFIG_LEGACY_BOOTCMD_ENV \
+ BOOTENV
+
+#undef CONFIG_BOOTCOMMAND
+#define CONFIG_BOOTCOMMAND "run distro_bootcmd || run legacy_bootcmd"
+
+#endif /* __PIC32MZDASK_CONFIG_H */
diff --git a/include/dt-bindings/clock/microchip,clock.h b/include/dt-bindings/clock/microchip,clock.h
new file mode 100644
index 0000000..93c222d
--- /dev/null
+++ b/include/dt-bindings/clock/microchip,clock.h
@@ -0,0 +1,29 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef __CLK_MICROCHIP_PIC32
+#define __CLK_MICROCHIP_PIC32
+
+/* clock output indices */
+#define BASECLK 0
+#define PLLCLK 1
+#define MPLL 2
+#define SYSCLK 3
+#define PB1CLK 4
+#define PB2CLK 5
+#define PB3CLK 6
+#define PB4CLK 7
+#define PB5CLK 8
+#define PB6CLK 9
+#define PB7CLK 10
+#define REF1CLK 11
+#define REF2CLK 12
+#define REF3CLK 13
+#define REF4CLK 14
+#define REF5CLK 15
+
+#endif /* __CLK_MICROCHIP_PIC32 */