Merge branch 'master' of git://git.denx.de/u-boot-mips

This commit is contained in:
Tom Rini 2016-02-01 16:58:46 -05:00
commit d2427caf54
57 changed files with 3705 additions and 96 deletions

View file

@ -23,6 +23,7 @@ config TARGET_QEMU_MIPS
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 @@ config TARGET_PB1X00
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/imgtec/malta/Kconfig"
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 @@ config MIPS_L1_CACHE_SHIFT
default "4" if MIPS_L1_CACHE_SHIFT_4
default "5"
config DYNAMIC_IO_PORT_BASE
bool
endif
endmenu

View file

@ -8,6 +8,7 @@ libs-y += arch/mips/cpu/
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)

View file

@ -115,7 +115,7 @@ reset:
/* 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 @@ reset:
#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 @@ reset:
/* 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 @@ ENTRY(relocate_code)
* 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

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
dtb-y +=
dtb-$(CONFIG_TARGET_PIC32MZDASK) += pic32mzda_sk.dtb
targets += $(dtb-y)

View file

@ -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>;
};
};

View file

@ -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";
};
&ethernet {
reset-gpios = <&gpioJ 15 0>;
status = "okay";
phy-mode = "rmii";
phy-handle = <&ethernet_phy>;
ethernet_phy: lan8740_phy@0 {
reg = <0>;
};
};

View file

@ -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 */

View file

@ -25,11 +25,6 @@
#include <mangle-port.h>
#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
@ -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
static inline ulong mips_io_port_base(void)
{
DECLARE_GLOBAL_DATA_PTR;
return gd->arch.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 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 @@ static inline type pfx##read##bwlq(const volatile void __iomem *mem) \
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 @@ static inline void pfx##out##bwlq##p(type val, unsigned long port) \
\
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 @@ static inline void pfx##out##bwlq##p(type val, unsigned long port) \
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 @@ static inline type pfx##in##bwlq##p(unsigned long port) \
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(l, u32)
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) \

View file

@ -7,7 +7,6 @@
obj-y += cache.o
obj-y += cache_init.o
obj-y += io.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o

View file

@ -95,6 +95,10 @@ void flush_dcache_range(ulong start_addr, ulong stop)
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 @@ void invalidate_dcache_range(ulong start_addr, ulong stop)
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)

View file

@ -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;

View file

@ -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

View file

@ -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

156
arch/mips/mach-pic32/cpu.c Normal file
View file

@ -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, &regs->raw);
/* Enable prefetch for all */
writel(0x30, &regs->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

View file

@ -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 */

View file

@ -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__ */

View file

@ -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)

View file

@ -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)
;
}

View file

@ -130,24 +130,26 @@ void _machine_restart(void)
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 @@ void pci_init_board(void)
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 @@ void pci_init_board(void)
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,

View file

@ -45,19 +45,19 @@ static struct {
{ 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);
}

View file

@ -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__ */

View file

@ -0,0 +1,13 @@
if TARGET_PIC32MZDASK
config SYS_BOARD
default "pic32mzda"
config SYS_VENDOR
default "microchip"
config SYS_CONFIG_NAME
default "pic32mzdask"
endif

View file

@ -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

View file

@ -0,0 +1,7 @@
#
# (C) Copyright 2015
# Purna Chandra Mandal, purna.mandal@microchip.com.
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y := pic32mzda.o

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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>;
};

View file

@ -0,0 +1,5 @@
* Microchip PIC32 serial UART
Required properties:
- compatible: must be "microchip,pic32mzda-uart".
- reg: exactly one register range.

View file

@ -69,4 +69,5 @@ obj-y += soc/
obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-y += thermal/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
endif

View file

@ -9,3 +9,4 @@ obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o
obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o

433
drivers/clk/clk_pic32.c Normal file
View file

@ -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),
};

View file

@ -0,0 +1,6 @@
#
# Copyright (C) 2015 Microchip Technology Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_MACH_PIC32) += ddr2.o

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 */

View file

@ -83,4 +83,11 @@ config VYBRID_GPIO
help
Say yes here to support Vybrid vf610 GPIOs.
config PIC32_GPIO
bool "Microchip PIC32 GPIO driver"
depends on DM_GPIO && MACH_PIC32
default y
help
Say yes here to support Microchip PIC32 GPIOs.
endmenu

View file

@ -46,4 +46,4 @@ obj-$(CONFIG_STM32_GPIO) += stm32_gpio.o
obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o
obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o
obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o
obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o

174
drivers/gpio/pic32_gpio.c Normal file
View file

@ -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),
};

View file

@ -31,4 +31,10 @@ config SH_SDHI
help
Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform
config PIC32_SDHCI
bool "Microchip PIC32 on-chip SDHCI support"
depends on DM_MMC && MACH_PIC32
help
Support for Microchip PIC32 SDHCI controller.
endmenu

View file

@ -48,4 +48,4 @@ obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
endif
obj-$(CONFIG_PIC32_SDHCI) += pic32_sdhci.o

58
drivers/mmc/pic32_sdhci.c Normal file
View file

@ -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),
};

View file

@ -443,6 +443,12 @@ static int sdhci_init(struct mmc *mmc)
sdhci_set_power(host, fls(mmc->cfg->voltages) - 1);
if (host->quirks & SDHCI_QUIRK_NO_CD) {
#if defined(CONFIG_PIC32_SDHCI)
/* PIC32 SDHCI CD errata:
* - set CD_TEST and clear CD_TEST_INS bit
*/
sdhci_writeb(host, SDHCI_CTRL_CD_TEST, SDHCI_HOST_CONTROL);
#else
unsigned int status;
sdhci_writeb(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST,
@ -453,6 +459,7 @@ static int sdhci_init(struct mmc *mmc)
(!(status & SDHCI_CARD_STATE_STABLE)) ||
(!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
status = sdhci_readl(host, SDHCI_PRESENT_STATE);
#endif
}
/* Enable only interrupts served by the SD controller */

View file

@ -125,4 +125,12 @@ config ZYNQ_GEM
help
This MAC is present in Xilinx Zynq and ZynqMP SoCs.
config PIC32_ETH
bool "Microchip PIC32 Ethernet Support"
depends on DM_ETH && MACH_PIC32
select PHYLIB
help
This driver implements 10/100 Mbps Ethernet and MAC layer for
Microchip PIC32 microcontrollers.
endif # NETDEVICES

View file

@ -72,3 +72,4 @@ obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
obj-$(CONFIG_VSC9953) += vsc9953.o
obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o

View file

@ -69,11 +69,21 @@ static struct phy_driver lan8710_driver = {
.shutdown = &genphy_shutdown,
};
static struct phy_driver lan8740_driver = {
.name = "SMSC LAN8740",
.uid = 0x0007c110,
.mask = 0xffff0,
.features = PHY_BASIC_FEATURES,
.config = &genphy_config_aneg,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
};
int phy_smsc_init(void)
{
phy_register(&lan8710_driver);
phy_register(&lan911x_driver);
phy_register(&lan8700_driver);
phy_register(&lan8740_driver);
return 0;
}

605
drivers/net/pic32_eth.c Normal file
View file

@ -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),
};

164
drivers/net/pic32_eth.h Normal file
View file

@ -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_*/

121
drivers/net/pic32_mdio.c Normal file
View file

@ -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);
}

View file

@ -131,6 +131,16 @@ config PINCTRL_SANDBOX
actually does nothing but print debug messages when pinctrl
operations are invoked.
config PIC32_PINCTRL
bool "Microchip PIC32 pin-control and pin-mux driver"
depends on DM && MACH_PIC32
default y
help
Supports individual pin selection and configuration for each remappable
peripheral available on Microchip PIC32 SoCs. This driver is controlled
by a device tree node which contains both GPIO defintion and pin control
functions.
endif
source "drivers/pinctrl/uniphier/Kconfig"

View file

@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
obj-$(CONFIG_ARCH_UNIPHIER) += uniphier/
obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o

View file

@ -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),
};

View file

@ -150,6 +150,14 @@ config DEBUG_UART_PL011
work. The driver will be available until the real driver model
serial is running.
config DEBUG_UART_PIC32
bool "Microchip PIC32"
depends on PIC32_SERIAL
help
Select this to enable a debug UART using the serial_pic32 driver. You
will need to provide parameters to make this work. The driver will
be available until the real driver model serial is running.
endchoice
config DEBUG_UART_BASE
@ -241,6 +249,13 @@ config FSL_LPUART
Select this to enable a Low Power UART for Freescale VF610 and
QorIQ Layerscape devices.
config PIC32_SERIAL
bool "Support for Microchip PIC32 on-chip UART"
depends on DM_SERIAL && MACH_PIC32
default y
help
Support for the UART found on Microchip PIC32 SoC's.
config SYS_NS16550
bool "NS16550 UART or compatible"
help

View file

@ -41,6 +41,7 @@ obj-$(CONFIG_MXS_AUART) += mxs_auart.o
obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o

View file

@ -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

View file

@ -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 */

View file

@ -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 */