From dd4ee63fa43cf13190891a5df62676054942b52e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 10 Jan 2015 20:12:02 +0000 Subject: mcs814x: add 3.14 kernel support Signed-off-by: Florian Fainelli SVN-Revision: 43926 --- .../arch/arm/boot/dts/dlan-usb-extender.dts | 76 ++++++ .../files-3.14/arch/arm/boot/dts/mcs8140.dtsi | 189 ++++++++++++++ .../files-3.14/arch/arm/boot/dts/rbt-832.dts | 89 +++++++ .../files-3.14/arch/arm/mach-mcs814x/Kconfig | 29 +++ .../files-3.14/arch/arm/mach-mcs814x/Makefile | 5 + .../files-3.14/arch/arm/mach-mcs814x/Makefile.boot | 4 + .../arch/arm/mach-mcs814x/board-mcs8140-dt.c | 45 ++++ .../files-3.14/arch/arm/mach-mcs814x/clock.c | 271 +++++++++++++++++++++ .../files-3.14/arch/arm/mach-mcs814x/common.c | 165 +++++++++++++ .../files-3.14/arch/arm/mach-mcs814x/common.h | 15 ++ .../arch/arm/mach-mcs814x/include/mach/cpu.h | 16 ++ .../arm/mach-mcs814x/include/mach/debug-macro.S | 11 + .../arm/mach-mcs814x/include/mach/entry-macro.S | 6 + .../arch/arm/mach-mcs814x/include/mach/gpio.h | 21 ++ .../arch/arm/mach-mcs814x/include/mach/hardware.h | 16 ++ .../arch/arm/mach-mcs814x/include/mach/io.h | 27 ++ .../arch/arm/mach-mcs814x/include/mach/irqs.h | 17 ++ .../arch/arm/mach-mcs814x/include/mach/mcs814x.h | 53 ++++ .../arch/arm/mach-mcs814x/include/mach/memory.h | 16 ++ .../arch/arm/mach-mcs814x/include/mach/param.h | 15 ++ .../arch/arm/mach-mcs814x/include/mach/system.h | 18 ++ .../arch/arm/mach-mcs814x/include/mach/timex.h | 18 ++ .../arm/mach-mcs814x/include/mach/uncompress.h | 40 +++ .../mcs814x/files-3.14/arch/arm/mach-mcs814x/irq.c | 89 +++++++ .../files-3.14/arch/arm/mach-mcs814x/timer.c | 132 ++++++++++ 25 files changed, 1383 insertions(+) create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/boot/dts/dlan-usb-extender.dts create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/boot/dts/mcs8140.dtsi create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/boot/dts/rbt-832.dts create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Kconfig create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile.boot create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/board-mcs8140-dt.c create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/clock.c create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.c create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/cpu.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/debug-macro.S create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/entry-macro.S create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/gpio.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/hardware.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/io.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/irqs.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/mcs814x.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/memory.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/param.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/system.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/timex.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/uncompress.h create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/irq.c create mode 100644 target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/timer.c (limited to 'target/linux/mcs814x/files-3.14/arch') diff --git a/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/dlan-usb-extender.dts b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/dlan-usb-extender.dts new file mode 100644 index 0000000000..0c85b94c0a --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/dlan-usb-extender.dts @@ -0,0 +1,76 @@ +/* + * dlan-usb-extender.dts - Device Tree file for Devolo dLAN USB Extender + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2 + */ + +/dts-v1/; +/include/ "mcs8140.dtsi" + +/ { + model = "Devolo dLAN USB Extender"; + compatible = "devolo,dlan-usb-extender", "moschip,mcs8140", "moschip,mcs814x"; + + chosen { + bootargs = "mem=16M console=ttyS0,57600 earlyprintk"; + }; + + ahb { + vci { + eth0: ethernet@40084000 { + phy = <&phy0>; + phy-mode = "mii"; + + phy0: ethernet-phy@0 { + reg = <8>; + }; + }; + + adc { + sdram: memory@0,0 { + reg = <0 0 0x1000000>; + }; + + nor: flash@7,0 { + + partition@0 { + label = "ArmBoot"; + reg = <0 0x30000>; + }; + partition@30000 { + label = "Config1"; + reg = <0x30000 0x10000>; + }; + partition@40000 { + label = "Config2"; + reg = <0x40000 0x10000>; + }; + partition@50000 { + label = "kernel"; + reg = <0x50000 0x100000>; + }; + partition@150000 { + label = "rootfs"; + reg = <0x150000 0x3C0000>; + }; + partition@50001 { + label = "linux"; + reg = <0x50000 0x4C0000>; + }; + }; + }; + + leds { + compatible = "gpio-leds"; + + usb { + label = "dlan-usb-extender:green:usb"; + gpios = <&gpio 19 0>; // gpio 19 active high + }; + }; + }; + }; +}; + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/mcs8140.dtsi b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/mcs8140.dtsi new file mode 100644 index 0000000000..b7e8eb4fa4 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/mcs8140.dtsi @@ -0,0 +1,189 @@ +/* + * mcs8140.dtsi - Device Tree Include file for Moschip MCS8140 family SoC + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2. + */ + +/include/ "skeleton.dtsi" + +/ { + model = "Moschip MCS8140 family SoC"; + compatible = "moschip,mcs8140"; + interrupt-parent = <&intc>; + + aliases { + serial0 = &uart0; + eth0 = ð0; + }; + + cpus { + cpu@0 { + compatible = "arm,arm926ejs"; + }; + }; + + ahb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vci { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + eth0: ethernet@40084000 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "moschip,nuport-mac"; + reg = <0x40084000 0xd8 // mac + 0x40080000 0x58>; // dma channels + interrupts = <4 5 29>; /* tx, rx, link */ + nuport-mac,buffer-shifting; + nuport-mac,link-activity = <0>; + }; + + tso@40088000 { + reg = <0x40088000 0x1c>; + interrupts = <7>; + }; + + i2s@4008c000 { + compatible = "moschip,mcs814x-i2s"; + reg = <0x4008c000 0x18>; + interrupts = <8>; + }; + + ipsec@40094000 { + compatible = "moschip,mcs814x-ipsec"; + reg = <0x40094000 0x1d8>; + interrupts = <16>; + }; + + rng@4009c000 { + compatible = "moschip,mcs814x-rng"; + reg = <0x4009c000 0x8>; + }; + + memc@400a8000 { + reg = <0x400a8000 0x58>; + }; + + list-proc@400ac0c0 { + reg = <0x400ac0c0 0x38>; + interrupts = <19 27>; // done, error + }; + + gpio: gpio@400d0000 { + compatible = "moschip,mcs814x-gpio"; + reg = <0x400d0000 0x670>; + interrupts = <10>; + #gpio-cells = <2>; + gpio-controller; + num-gpios = <20>; + }; + + eepio: gpio@400d4000 { + compatible = "moschip,mcs814x-gpio"; + reg = <0x400d4000 0x470>; + #gpio-cells = <2>; + gpio-controller; + num-gpios = <4>; + }; + + uart0: serial@400dc000 { + compatible = "ns16550"; + reg = <0x400dc000 0x20>; + clock-frequency = <50000000>; + reg-shift = <2>; + interrupts = <21>; + status = "okay"; + }; + + intc: interrupt-controller@400e4000 { + #interrupt-cells = <1>; + compatible = "moschip,mcs814x-intc"; + interrupt-controller; + interrupt-parent; + reg = <0x400e4000 0x48>; + }; + + m2m@400e8000 { + reg = <0x400e8000 0x24>; + interrupts = <17>; + }; + + eth-filters@400ec000 { + reg = <0x400ec000 0x80>; + }; + + timer: timer@400f800c { + compatible = "moschip,mcs814x-timer"; + interrupts = <0>; + reg = <0x400f800c 0x8>; + }; + + watchdog@400f8014 { + compatible = "moschip,mcs814x-wdt"; + reg = <0x400f8014 0x8>; + }; + + adc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + // 8 64MB chip-selects + ranges = <0 0 0x00000000 0x4000000 // sdram + 1 0 0x04000000 0x4000000 // sdram + 2 0 0x08000000 0x4000000 // reserved + 3 0 0x0c000000 0x4000000 // flash/localbus + 4 0 0x10000000 0x4000000 // flash/localbus + 5 0 0x14000000 0x4000000 // flash/localbus + 6 0 0x18000000 0x4000000 // flash/localbus + 7 0 0x1c000000 0x4000000>; // flash/localbus + + sdram: memory@0,0 { + reg = <0 0 0>; + }; + + nor: flash@7,0 { + reg = <7 0 0x4000000>; + compatible = "cfi-flash"; + bank-width = <1>; // 8-bit external flash + #address-cells = <1>; + #size-cells = <1>; + }; + }; + + usb0: ehci@400fc000 { + compatible = "moschip,mcs814x-ehci", "usb-ehci"; + reg = <0x400fc000 0x74>; + interrupts = <2>; + }; + + usb1: ohci@400fd000 { + compatible = "moschip,mcs814x-ohci", "ohci-le"; + reg = <0x400fd000 0x74>; + interrupts = <11>; + }; + + usb2: ohci@400fe000 { + compatible = "moschip,mcs814x-ohci", "ohci-le"; + reg = <0x400fe000 0x74>; + interrupts = <12>; + }; + + usb3: otg@400ff000 { + compatible = "moschip,msc814x-otg", "usb-otg"; + reg = <0x400ff000 0x1000>; + interrupts = <13>; + }; + }; + + }; +}; diff --git a/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/rbt-832.dts b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/rbt-832.dts new file mode 100644 index 0000000000..9949c8ecf3 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/boot/dts/rbt-832.dts @@ -0,0 +1,89 @@ +/* + * rbt-832.dts - Device Tree file for Tigal RBT-832 + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2 + */ + +/dts-v1/; +/include/ "mcs8140.dtsi" + +/ { + model = "Tigal RBT-832"; + compatible = "tigal,rbt-832", "moschip,mcs8140", "moschip,mcs814x"; + + chosen { + bootargs = "mem=32M console=ttyS0,115200 earlyprintk"; + }; + + ahb { + vci { + eth0: ethernet@40084000 { + nuport-mac,link-activity = <0x01>; + phy = <&phy0>; + phy-mode = "mii"; + + phy0: ethernet-phy@0 { + reg = <1>; + }; + }; + + adc { + sdram: memory@0,0 { + reg = <0 0 0x2000000>; + }; + + nor: flash@7,0 { + + partition@0 { + label = "ArmBoot"; + reg = <0 0x40000>; + }; + partition@30000 { + label = "Enviroment"; + reg = <0x40000 0x20000>; + }; + partition@50000 { + label = "bZimage"; + reg = <0x60000 0x1a0000>; + }; + partition@150000 { + label = "UserFS"; + reg = <0x200000 0x600000>; + }; + }; + }; + + leds { + compatible = "gpio-leds"; + + ethernet { + label = "rbt-832:red:ethernet"; + gpios = <&gpio 0 0>; // gpio 0 active high + }; + + usb0 { + label = "rbt-832:red:usb0"; + gpios = <&gpio 4 0>; // gpio 4 active high + }; + + usb1 { + label = "rbt-832:red:usb1"; + gpios = <&gpio 3 0>; // gpio 3 active high + }; + + usb2 { + label = "rbt-832:red:usb2"; + gpios = <&gpio 2 0>; // gpio 2 active high + }; + + usb3 { + label = "rbt-832:red:usb3"; + gpios = <&gpio 1 0>; // gpio 1 active high + }; + }; + }; + }; +}; + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Kconfig b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Kconfig new file mode 100644 index 0000000000..372c9b87b9 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Kconfig @@ -0,0 +1,29 @@ +if ARCH_MCS814X + +config MCS8140 + bool + select CPU_ARM926T + +menu "Moschip MCS8140 boards" + +config MACH_DLAN_USB_EXT + bool "Devolo dLAN USB Extender" + select MCS8140 + select NEW_LEDS + select LEDS_CLASS + select LEDS_GPIO + help + Machine support for the Devolo dLAN USB Extender + +config MACH_RBT_832 + bool "Tigal RBT-832" + select MCS8140 + select NEW_LEDS + select LEDS_CLASS + select LEDS_GPIO + help + Machine support for the Tigal RBT-832 board + +endmenu + +endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile new file mode 100644 index 0000000000..68c5f4a9f5 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile @@ -0,0 +1,5 @@ +obj-y += clock.o +obj-y += common.o +obj-y += irq.o +obj-y += timer.o +obj-y += board-mcs8140-dt.o diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile.boot b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile.boot new file mode 100644 index 0000000000..b5725078d7 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/Makefile.boot @@ -0,0 +1,4 @@ + zreladdr-y := 0x00008000 + +dtb-$(CONFIG_MACH_DLAN_USB_EXT) += dlan-usb-extender.dtb +dtb-$(CONFIG_MACH_RBT_832) += rbt-832.dtb diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/board-mcs8140-dt.c b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/board-mcs8140-dt.c new file mode 100644 index 0000000000..0d11450c73 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/board-mcs8140-dt.c @@ -0,0 +1,45 @@ +/* + * Setup code for Moschip MCS8140-based board using Device Tree + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2. + */ +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#include +#include +#include +#include +#include + +static void __init mcs814x_dt_device_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + mcs814x_init_machine(); +} + +static const char *mcs8140_dt_board_compat[] __initdata = { + "moschip,mcs8140", + NULL, /* sentinel */ +}; + +DT_MACHINE_START(mcs8140_dt, "Moschip MCS8140 board") + /* Maintainer: Florian Fainelli */ + .map_io = mcs814x_map_io, + .init_early = mcs814x_clk_init, + .init_irq = mcs814x_of_irq_init, + .init_time = mcs814x_timer_init, + .init_machine = mcs814x_dt_device_init, + .restart = mcs814x_restart, + .dt_compat = mcs8140_dt_board_compat, + .handle_irq = mcs814x_handle_irq, +MACHINE_END + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/clock.c b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/clock.c new file mode 100644 index 0000000000..413bfecaa1 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/clock.c @@ -0,0 +1,271 @@ +/* + * Moschip MCS814x clock routines + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +struct clk_ops { + unsigned long (*get_rate)(struct clk *clk); + int (*set_rate)(struct clk *clk, unsigned long rate); + struct clk *(*get_parent)(struct clk *clk); + int (*enable)(struct clk *clk, int enable); +}; + +struct clk { + struct clk *parent; /* parent clk */ + unsigned long rate; /* clock rate in Hz */ + unsigned long divider; /* clock divider */ + u32 usecount; /* reference count */ + struct clk_ops *ops; /* clock operation */ + u32 enable_reg; /* clock enable register */ + u32 enable_mask; /* clock enable mask */ +}; + +static unsigned long clk_divide_parent(struct clk *clk) +{ + if (clk->parent && clk->divider) + return clk_get_rate(clk->parent) / clk->divider; + else + return 0; +} + +static int clk_local_onoff_enable(struct clk *clk, int enable) +{ + u32 tmp; + + /* no enable_reg means the clock is always enabled */ + if (!clk->enable_reg) + return 0; + + tmp = readl_relaxed(mcs814x_sysdbg_base + clk->enable_reg); + if (!enable) + tmp &= ~clk->enable_mask; + else + tmp |= clk->enable_mask; + + writel_relaxed(tmp, mcs814x_sysdbg_base + clk->enable_reg); + + return 0; +} + +static struct clk_ops default_clk_ops = { + .get_rate = clk_divide_parent, + .enable = clk_local_onoff_enable, +}; + +static DEFINE_SPINLOCK(clocks_lock); + +static const unsigned long cpu_freq_table[] = { + 175000, + 300000, + 125000, + 137500, + 212500, + 250000, + 162500, + 187500, + 162500, + 150000, + 225000, + 237500, + 200000, + 262500, + 275000, + 287500 +}; + +static struct clk clk_cpu; + +/* System clock is fixed at 50Mhz */ +static struct clk clk_sys = { + .rate = 50 * MHZ, +}; + +static struct clk clk_sdram; + +static struct clk clk_timer0 = { + .parent = &clk_sdram, + .divider = 2, + .ops = &default_clk_ops, +}; + +static struct clk clk_timer1_2 = { + .parent = &clk_sys, +}; + +/* Watchdog clock is system clock / 128 */ +static struct clk clk_wdt = { + .parent = &clk_sys, + .divider = 128, + .ops = &default_clk_ops, +}; + +static struct clk clk_emac = { + .ops = &default_clk_ops, + .enable_reg = SYSDBG_SYSCTL, + .enable_mask = SYSCTL_EMAC, +}; + +static struct clk clk_ephy = { + .ops = &default_clk_ops, + .enable_reg = SYSDBG_PLL_CTL, + .enable_mask = ~SYSCTL_EPHY, /* active low */ +}; + +static struct clk clk_cipher = { + .ops = &default_clk_ops, + .enable_reg = SYSDBG_SYSCTL, + .enable_mask = SYSCTL_CIPHER, +}; + +#define CLK(_dev, _con, _clk) \ +{ .dev_id = (_dev), .con_id = (_con), .clk = (_clk) }, + +static struct clk_lookup mcs814x_chip_clks[] = { + CLK("cpu", NULL, &clk_cpu) + CLK("sys", NULL, &clk_sys) + CLK("sdram", NULL, &clk_sdram) + /* 32-bits timer0 */ + CLK("timer0", NULL, &clk_timer0) + /* 16-bits timer1 */ + CLK("timer1", NULL, &clk_timer1_2) + /* 64-bits timer2, same as timer 1 */ + CLK("timer2", NULL, &clk_timer1_2) + CLK(NULL, "wdt", &clk_wdt) + CLK(NULL, "emac", &clk_emac) + CLK(NULL, "ephy", &clk_ephy) + CLK(NULL, "cipher", &clk_cipher) +}; + +static void local_clk_disable(struct clk *clk) +{ + WARN_ON(!clk->usecount); + + if (clk->usecount > 0) { + clk->usecount--; + + if ((clk->usecount == 0) && (clk->ops->enable)) + clk->ops->enable(clk, 0); + + if (clk->parent) + local_clk_disable(clk->parent); + } +} + +static int local_clk_enable(struct clk *clk) +{ + int ret = 0; + + if (clk->parent) + ret = local_clk_enable(clk->parent); + + if (ret) + return ret; + + if ((clk->usecount == 0) && (clk->ops->enable)) + ret = clk->ops->enable(clk, 1); + + if (!ret) + clk->usecount++; + else if (clk->parent && clk->parent->ops->enable) + local_clk_disable(clk->parent); + + return ret; +} + +int clk_enable(struct clk *clk) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + ret = local_clk_enable(clk); + spin_unlock_irqrestore(&clocks_lock, flags); + + return ret; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + local_clk_disable(clk); + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (unlikely(IS_ERR_OR_NULL(clk))) + return 0; + + if (clk->rate) + return clk->rate; + + if (clk->ops && clk->ops->get_rate) + return clk->ops->get_rate(clk); + + return clk_get_rate(clk->parent); +} +EXPORT_SYMBOL(clk_get_rate); + +struct clk *clk_get_parent(struct clk *clk) +{ + unsigned long flags; + + if (unlikely(IS_ERR_OR_NULL(clk))) + return NULL; + + if (!clk->ops || !clk->ops->get_parent) + return clk->parent; + + spin_lock_irqsave(&clocks_lock, flags); + clk->parent = clk->ops->get_parent(clk); + spin_unlock_irqrestore(&clocks_lock, flags); + + return clk->parent; +} +EXPORT_SYMBOL(clk_get_parent); + +void __init mcs814x_clk_init(void) +{ + u32 bs1; + u8 cpu_freq; + + clkdev_add_table(mcs814x_chip_clks, ARRAY_SIZE(mcs814x_chip_clks)); + + /* read the bootstrap registers to know the exact clocking scheme */ + bs1 = readl_relaxed(mcs814x_sysdbg_base + SYSDBG_BS1); + cpu_freq = (bs1 >> CPU_FREQ_SHIFT) & CPU_FREQ_MASK; + + pr_info("CPU frequency: %lu (kHz)\n", cpu_freq_table[cpu_freq]); + clk_cpu.rate = cpu_freq * KHZ; + + /* read SDRAM frequency */ + if (bs1 & SDRAM_FREQ_BIT) + clk_sdram.rate = 100 * MHZ; + else + clk_sdram.rate = 133 * MHZ; + + pr_info("SDRAM frequency: %lu (MHz)\n", clk_sdram.rate / MHZ); +} + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.c b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.c new file mode 100644 index 0000000000..d44f17155a --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.c @@ -0,0 +1,165 @@ +/* + * arch/arm/mach-mcs814x/common.c + * + * Core functions for Moschip MCS814x SoCs + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +void __iomem *mcs814x_sysdbg_base; + +static struct map_desc mcs814x_io_desc[] __initdata = { + { + .virtual = MCS814X_IO_BASE, + .pfn = __phys_to_pfn(MCS814X_IO_START), + .length = MCS814X_IO_SIZE, + .type = MT_DEVICE + }, +}; + +struct cpu_mode { + const char *name; + int gpio_start; + int gpio_end; +}; + +static const struct cpu_mode cpu_modes[] = { + { + .name = "I2S", + .gpio_start = 4, + .gpio_end = 8, + }, + { + .name = "UART", + .gpio_start = 4, + .gpio_end = 9, + }, + { + .name = "External MII", + .gpio_start = 0, + .gpio_end = 16, + }, + { + .name = "Normal", + .gpio_start = -1, + .gpio_end = -1, + }, +}; + +static void mcs814x_eth_hardware_filter_set(u8 value) +{ + u32 reg; + + reg = readl_relaxed(MCS814X_VIRT_BASE + MCS814X_DBGLED); + if (value) + reg |= 0x80; + else + reg &= ~0x80; + writel_relaxed(reg, MCS814X_VIRT_BASE + MCS814X_DBGLED); +} + +static void mcs814x_eth_led_cfg_set(u8 cfg) +{ + u32 reg; + + reg = readl_relaxed(mcs814x_sysdbg_base + SYSDBG_BS2); + reg &= ~LED_CFG_MASK; + reg |= cfg; + writel_relaxed(reg, mcs814x_sysdbg_base + SYSDBG_BS2); +} + +static void mcs814x_eth_buffer_shifting_set(u8 value) +{ + u8 reg; + + reg = readb_relaxed(mcs814x_sysdbg_base + SYSDBG_SYSCTL_MAC); + if (value) + reg |= BUF_SHIFT_BIT; + else + reg &= ~BUF_SHIFT_BIT; + writeb_relaxed(reg, mcs814x_sysdbg_base + SYSDBG_SYSCTL_MAC); +} + +static struct of_device_id mcs814x_eth_ids[] __initdata = { + { .compatible = "moschip,nuport-mac", }, + { /* sentinel */ }, +}; + +/* Configure platform specific knobs based on ethernet device node + * properties */ +static void mcs814x_eth_init(void) +{ + struct device_node *np; + const unsigned int *intspec; + + np = of_find_matching_node(NULL, mcs814x_eth_ids); + if (!np) + return; + + /* hardware filter must always be enabled */ + mcs814x_eth_hardware_filter_set(1); + + intspec = of_get_property(np, "nuport-mac,buffer-shifting", NULL); + if (!intspec) + mcs814x_eth_buffer_shifting_set(0); + else + mcs814x_eth_buffer_shifting_set(1); + + intspec = of_get_property(np, "nuport-mac,link-activity", NULL); + if (intspec) + mcs814x_eth_led_cfg_set(be32_to_cpup(intspec)); + + of_node_put(np); +} + +void __init mcs814x_init_machine(void) +{ + u32 bs2, cpu_mode; + int gpio; + + bs2 = readl_relaxed(mcs814x_sysdbg_base + SYSDBG_BS2); + cpu_mode = (bs2 >> CPU_MODE_SHIFT) & CPU_MODE_MASK; + + pr_info("CPU mode: %s\n", cpu_modes[cpu_mode].name); + + /* request the gpios since the pins are muxed for functionnality */ + for (gpio = cpu_modes[cpu_mode].gpio_start; + gpio == cpu_modes[cpu_mode].gpio_end; gpio++) { + if (gpio != -1) + gpio_request(gpio, cpu_modes[cpu_mode].name); + } + + mcs814x_eth_init(); +} + +void __init mcs814x_map_io(void) +{ + iotable_init(mcs814x_io_desc, ARRAY_SIZE(mcs814x_io_desc)); + + mcs814x_sysdbg_base = ioremap(MCS814X_IO_START + MCS814X_SYSDBG, + MCS814X_SYSDBG_SIZE); + if (!mcs814x_sysdbg_base) + panic("unable to remap sysdbg base"); +} + +void mcs814x_restart(enum reboot_mode mode, const char *cmd) +{ + writel_relaxed(~(1 << 31), mcs814x_sysdbg_base); +} diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.h new file mode 100644 index 0000000000..c9e4039584 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/common.h @@ -0,0 +1,15 @@ +#ifndef __ARCH_MCS814X_COMMON_H +#define __ARCH_MCS814X_COMMON_H + +#include + +void mcs814x_map_io(void); +void mcs814x_clk_init(void); +void mcs814x_of_irq_init(void); +void mcs814x_init_machine(void); +void mcs814x_handle_irq(struct pt_regs *regs); +void mcs814x_restart(char mode, const char *cmd); +void mcs814x_timer_init(void); +extern void __iomem *mcs814x_sysdbg_base; + +#endif /* __ARCH_MCS814X_COMMON_H */ diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/cpu.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/cpu.h new file mode 100644 index 0000000000..1ef3c4a036 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/cpu.h @@ -0,0 +1,16 @@ +#ifndef __ASM_ARCH_CPU_H__ +#define __ASM_ARCH_CPU_H__ + +#include + +#define MCS8140_ID 0x41069260 /* ARM926EJ-S */ +#define MCS814X_MASK 0xff0ffff0 + +#ifdef CONFIG_MCS8140 +/* Moschip MCS8140 is based on an ARM926EJ-S core */ +#define soc_is_mcs8140() ((read_cpuid_id() & MCS814X_MASK) == MCS8140_ID) +#else +#define soc_is_mcs8140() (0) +#endif /* !CONFIG_MCS8140 */ + +#endif /* __ASM_ARCH_CPU_H__ */ diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/debug-macro.S b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/debug-macro.S new file mode 100644 index 0000000000..93ecea4ed2 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/debug-macro.S @@ -0,0 +1,11 @@ +#include + + .macro addruart, rp, rv, tmp + ldr \rp, =MCS814X_PHYS_BASE + ldr \rv, =MCS814X_VIRT_BASE + orr \rp, \rp, #MCS814X_UART + orr \rv, \rv, #MCS814X_UART + .endm + +#define UART_SHIFT 2 +#include diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/entry-macro.S b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/entry-macro.S new file mode 100644 index 0000000000..16d2d6d1af --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/entry-macro.S @@ -0,0 +1,6 @@ +#include + .macro disable_fiq + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/gpio.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/gpio.h new file mode 100644 index 0000000000..20240c2ea2 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/gpio.h @@ -0,0 +1,21 @@ +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +/* new generic GPIO API */ +#include + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep + +static inline int gpio_to_irq(unsigned gpio) +{ + return -EINVAL; +} + +static inline int irq_to_gpio(unsigned irq) +{ + return -EINVAL; +} + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/hardware.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/hardware.h new file mode 100644 index 0000000000..529f648ae6 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/hardware.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#include "mcs814x.h" + +#endif + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/io.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/io.h new file mode 100644 index 0000000000..80e56f6ca7 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/io.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * Copyright (C) 2012, Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +/* + * We don't support ins[lb]/outs[lb]. Make them fault. + */ +#define __raw_readsb(p, d, l) do { *(int *)0 = 0; } while (0) +#define __raw_readsl(p, d, l) do { *(int *)0 = 0; } while (0) +#define __raw_writesb(p, d, l) do { *(int *)0 = 0; } while (0) +#define __raw_writesl(p, d, l) do { *(int *)0 = 0; } while (0) + +#define __io(a) __typesafe_io(a) +#define __mem_pci(a) (a) + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/irqs.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/irqs.h new file mode 100644 index 0000000000..7b9e07c1a7 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/irqs.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H + +#define FIQ_START 0 + +#define NR_IRQS 32 + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/mcs814x.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/mcs814x.h new file mode 100644 index 0000000000..8252d33a7d --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/mcs814x.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_MCS814X_H +#define __ASM_ARCH_MCS814X_H + +#define MCS814X_IO_BASE 0xF0000000 +#define MCS814X_IO_START 0x40000000 +#define MCS814X_IO_SIZE 0x00100000 + +/* IRQ controller register offset */ +#define MCS814X_IRQ_ICR 0x00 +#define MCS814X_IRQ_ISR 0x04 +#define MCS814X_IRQ_MASK 0x20 +#define MCS814X_IRQ_STS0 0x40 + +#define MCS814X_PHYS_BASE 0x40000000 +#define MCS814X_VIRT_BASE MCS814X_IO_BASE + +#define MCS814X_UART 0x000DC000 +#define MCS814X_DBGLED 0x000EC000 +#define MCS814X_SYSDBG 0x000F8000 +#define MCS814X_SYSDBG_SIZE 0x50 + +/* System configuration and bootstrap registers */ +#define SYSDBG_BS1 0x00 +#define CPU_FREQ_SHIFT 27 +#define CPU_FREQ_MASK 0x0F +#define SDRAM_FREQ_BIT (1 << 22) + +#define SYSDBG_BS2 0x04 +#define LED_CFG_MASK 0x03 +#define CPU_MODE_SHIFT 23 +#define CPU_MODE_MASK 0x03 + +#define SYSDBG_SYSCTL_MAC 0x1d +#define BUF_SHIFT_BIT (1 << 0) + +#define SYSDBG_SYSCTL 0x08 +#define SYSCTL_EMAC (1 << 0) +#define SYSCTL_EPHY (1 << 0) /* active low */ +#define SYSCTL_CIPHER (1 << 16) + +#define SYSDBG_PLL_CTL 0x3C + +#endif /* __ASM_ARCH_MCS814X_H */ + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/memory.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/memory.h new file mode 100644 index 0000000000..ad87c7ba6a --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/memory.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * Copyright (C) 2012, Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PLAT_PHYS_OFFSET UL(0x00000000) + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/param.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/param.h new file mode 100644 index 0000000000..7ffe70b7d5 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/param.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_PARAM_H +#define __ASM_ARCH_PARAM_H + +#define HZ 100 + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/system.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/system.h new file mode 100644 index 0000000000..cf5453df25 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/system.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * Copyright (C) 2012 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/timex.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/timex.h new file mode 100644 index 0000000000..f05c8eeb6d --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/timex.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2003 Artec Design Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_TIMEX_H +#define __ASM_ARCH_TIMEX_H + +/* + * Timex specification for MCS814X + */ +#define CLOCK_TICK_RATE 100 + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/uncompress.h b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/uncompress.h new file mode 100644 index 0000000000..cf3ed9a1a6 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/include/mach/uncompress.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012, Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_UNCOMPRESS_H +#define __ASM_ARCH_UNCOMPRESS_H + +#include +#include +#include +#include + +#define UART_SHIFT (2) + +/* cannot be static because the code will be inlined */ +void __iomem *uart_base; + +static inline void putc(int c) +{ + while (!(__raw_readb(uart_base + (UART_LSR << UART_SHIFT)) & UART_LSR_TEMT)); + __raw_writeb(c, uart_base + (UART_TX << UART_SHIFT)); +} + +static inline void flush(void) +{ +} + +static inline void arch_decomp_setup(void) +{ + if (soc_is_mcs8140()) + uart_base = (void __iomem *)(MCS814X_PHYS_BASE +MCS814X_UART); +} + +#define arch_decomp_wdog() + +#endif diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/irq.c b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/irq.c new file mode 100644 index 0000000000..f84c412839 --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/irq.c @@ -0,0 +1,89 @@ +/* + * Moschip MCS814x generic interrupt controller routines + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under the GPLv2 + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void __iomem *mcs814x_intc_base; + +static void __init mcs814x_alloc_gc(void __iomem *base, unsigned int irq_start, + unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("mcs814x-intc", 1, + irq_start, base, handle_level_irq); + if (!gc) + panic("unable to allocate generic irq chip"); + + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_unmask_enable_reg; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->regs.mask = MCS814X_IRQ_MASK; + ct->regs.enable = MCS814X_IRQ_ICR; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST, 0); + + /* Clear all interrupts */ + writel_relaxed(0xffffffff, base + MCS814X_IRQ_ICR); +} + +asmlinkage void __exception_irq_entry mcs814x_handle_irq(struct pt_regs *regs) +{ + u32 status, irq; + + do { + /* read the status register */ + status = __raw_readl(mcs814x_intc_base + MCS814X_IRQ_STS0); + if (!status) + break; + + irq = ffs(status) - 1; + status |= (1 << irq); + /* clear the interrupt */ + __raw_writel(status, mcs814x_intc_base + MCS814X_IRQ_STS0); + /* call the generic handler */ + handle_IRQ(irq, regs); + + } while (1); +} + +static const struct of_device_id mcs814x_intc_ids[] = { + { .compatible = "moschip,mcs814x-intc" }, + { /* sentinel */ }, +}; + +void __init mcs814x_of_irq_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, mcs814x_intc_ids); + if (!np) + panic("unable to find compatible intc node in dtb\n"); + + mcs814x_intc_base = of_iomap(np, 0); + if (!mcs814x_intc_base) + panic("unable to map intc cpu registers\n"); + + irq_domain_add_simple(np, 32, 0, &irq_generic_chip_ops, NULL); + + of_node_put(np); + + mcs814x_alloc_gc(mcs814x_intc_base, 0, 32); +} + diff --git a/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/timer.c b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/timer.c new file mode 100644 index 0000000000..ff9d44aa6e --- /dev/null +++ b/target/linux/mcs814x/files-3.14/arch/arm/mach-mcs814x/timer.c @@ -0,0 +1,132 @@ +/* + * Moschip MCS814x timer routines + * + * Copyright (C) 2012, Florian Fainelli + * + * Licensed under GPLv2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Timer block registers */ +#define TIMER_VAL 0x00 +#define TIMER_CTL 0x04 +#define TIMER_CTL_EN 0x01 +#define TIMER_CTL_DBG 0x02 + +static u32 last_reload; +static u32 timer_correct; +static u32 clock_rate; +static u32 timer_reload_value; +static void __iomem *mcs814x_timer_base; + +static inline u32 ticks2usecs(u32 x) +{ + return x / (clock_rate / 1000000); +} + +/* + * Returns number of ms since last clock interrupt. Note that interrupts + * will have been disabled by do_gettimeoffset() + */ +static u32 mcs814x_gettimeoffset(void) +{ + u32 ticks = readl_relaxed(mcs814x_timer_base + TIMER_VAL); + + if (ticks < last_reload) + return ticks2usecs(ticks + (u32)(0xffffffff - last_reload)); + else + return ticks2usecs(ticks - last_reload); +} + + +static irqreturn_t mcs814x_timer_interrupt(int irq, void *dev_id) +{ + u32 count = readl_relaxed(mcs814x_timer_base + TIMER_VAL); + + /* take into account delay up to this moment */ + last_reload = count + timer_correct + timer_reload_value; + + if (last_reload < timer_reload_value) { + last_reload = timer_reload_value; + } else { + if (timer_correct == 0) + timer_correct = readl_relaxed(mcs814x_timer_base + TIMER_VAL) - count; + } + writel_relaxed(last_reload, mcs814x_timer_base + TIMER_VAL); + + timer_tick(); + + return IRQ_HANDLED; +} + +static struct irqaction mcs814x_timer_irq = { + .name = "mcs814x-timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = mcs814x_timer_interrupt, +}; + +static struct of_device_id mcs814x_timer_ids[] = { + { .compatible = "moschip,mcs814x-timer" }, + { /* sentinel */ }, +}; + +static void __init mcs814x_of_timer_init(void) +{ + struct device_node *np; + const unsigned int *intspec; + + np = of_find_matching_node(NULL, mcs814x_timer_ids); + if (!np) + panic("unable to find compatible timer node in dtb"); + + mcs814x_timer_base = of_iomap(np, 0); + if (!mcs814x_timer_base) + panic("unable to remap timer cpu registers"); + + intspec = of_get_property(np, "interrupts", NULL); + if (!intspec) + panic("no interrupts property for timer"); + + mcs814x_timer_irq.irq = be32_to_cpup(intspec); +} + +void __init mcs814x_timer_init(void) +{ + struct clk *clk; + + arch_gettimeoffset = mcs814x_gettimeoffset; + + clk = clk_get_sys("timer0", NULL); + if (IS_ERR_OR_NULL(clk)) + panic("unable to get timer0 clock"); + + clock_rate = clk_get_rate(clk); + + mcs814x_of_timer_init(); + + pr_info("Timer frequency: %d (kHz)\n", clock_rate / 1000); + + timer_reload_value = 0xffffffff - (clock_rate / HZ); + + /* disable timer */ + writel_relaxed(~TIMER_CTL_EN, mcs814x_timer_base + TIMER_CTL); + writel_relaxed(timer_reload_value, mcs814x_timer_base + TIMER_VAL); + last_reload = timer_reload_value; + + setup_irq(mcs814x_timer_irq.irq, &mcs814x_timer_irq); + /* enable timer, stop timer in debug mode */ + writel_relaxed(TIMER_CTL_EN | TIMER_CTL_DBG, + mcs814x_timer_base + TIMER_CTL); +} -- cgit v1.2.3