diff --git a/arch/arm/boot/dts/celestial-cnc1800l.dts b/arch/arm/boot/dts/celestial-cnc1800l.dts index 54c91212f..98318744e 100644 --- a/arch/arm/boot/dts/celestial-cnc1800l.dts +++ b/arch/arm/boot/dts/celestial-cnc1800l.dts @@ -7,8 +7,12 @@ model = "Cavium Celestial CNC1800L"; compatible = "cavium,cnc1800l"; + aliases { + serial0 = &uart0; + }; + chosen { - stdout-path = "/uart@801f1000:115200"; + stdout-path = "serial0:115200n8"; }; @@ -24,7 +28,6 @@ cpu { compatible = "arm,arm1176jzf"; device_type = "cpu"; - // clocks = <&timer0>; }; }; @@ -49,29 +52,31 @@ }; timer0: timer@801e2000 { - compatible = "snps,dw-apb-timer"; - interrupts = <10>; + compatible = "snps,dw-apb-timer-cnc1800l"; reg = <0x801e2000 0x100>; - clocks = <&timclk>, <&pclk>; - clock-names = "timer", "pclk"; - // clock-frequency = <94500000>; - // #clock-cells = <1>; + clocks = <&pclk>; + }; + + timer1: timer@80270000 { + compatible = "cavium,cnc1800l-timer"; + reg = <0x80270000 0x100>; + clocks = <&timclk>; + interrupts = <4>; }; - watchdog0: wd@801e1000 { compatible = "snps,dw-wdt"; reg = <0x801e1000 0x100>; interrupts = <0>; clocks = <&pclk>; - // resets = <&rst WDT0_RESET>; }; - uart0: uart@801f1000 { - compatible = "ns8250"; + uart0: serial@801f1000 { + compatible = "snps,dw-apb-uart"; reg = <0x801f1000 0x100>; - clock-frequency = <47250000>; + reg-io-width = <1>; + clocks = <&pclk>; interrupts = <12>; reg-shift = <2>; }; diff --git a/arch/arm/mach-celestial/Kconfig b/arch/arm/mach-celestial/Kconfig index 4c5f4fcb2..e683036c0 100644 --- a/arch/arm/mach-celestial/Kconfig +++ b/arch/arm/mach-celestial/Kconfig @@ -2,9 +2,9 @@ config MACH_CELESTIAL bool "Celestial CNC1800L" depends on ARCH_MULTI_V6 select DW_APB_ICTL - select DW_APB_TIMER - select DW_APB_TIMER_OF select SERIAL_8250 select SERIAL_8250_CONSOLE + select CNC1800L_TIMER + select CNC1800L_APBTIMER help Support for Cavium CNC1800L Soc. diff --git a/arch/arm/mach-celestial/cnc1800l_machine.c b/arch/arm/mach-celestial/cnc1800l_machine.c index 41c33d9d5..6fc2d455d 100644 --- a/arch/arm/mach-celestial/cnc1800l_machine.c +++ b/arch/arm/mach-celestial/cnc1800l_machine.c @@ -17,10 +17,10 @@ #define CNC1800L_VIRT_UART0 0xfec00000 static struct map_desc cnc1800l_of_io_desc[] __initdata = { -#ifdef CONFIG_DEBUG_UART_8250 +#ifdef CONFIG_DEBUG_CELESTIAL_UART { .virtual = CNC1800L_VIRT_UART0, - .pfn = __phys_to_pfn(CNC1800L_VIRT_UART0), + .pfn = __phys_to_pfn(0x801f1000), .length = SZ_4K, .type = MT_DEVICE, }, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 08f8cb944..c60ef917e 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -178,6 +178,21 @@ config ASM9260_TIMER help Enables support for the ASM9260 timer. +config CNC1800L_TIMER + bool "CNC1800L timer driver" if COMPILE_TEST + select CLKSRC_MMIO + select TIMER_OF + help + Enables support for the CNC1800L timer. + +config CNC1800L_APBTIMER + bool "CNC1800L Synopsys APB timer driver" if COMPILE_TEST + select CLKSRC_MMIO + select TIMER_OF + help + Enables support for the CNC1800L APB timer. + + config CLKSRC_NOMADIK_MTU bool "Nomakdik clocksource driver" if COMPILE_TEST depends on ARM diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index c17ee32a7..9d54abb3b 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -58,6 +58,9 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-milbeaut.o obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o obj-$(CONFIG_RDA_TIMER) += timer-rda.o +obj-$(CONFIG_CNC1800L_TIMER) += cnc1800l_timer.o +obj-$(CONFIG_CNC1800L_APBTIMER) += cnc1800l_apbtimer.o + obj-$(CONFIG_ARC_TIMERS) += arc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/cnc1800l_apbtimer.c b/drivers/clocksource/cnc1800l_apbtimer.c new file mode 100644 index 000000000..c6a3114cb --- /dev/null +++ b/drivers/clocksource/cnc1800l_apbtimer.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +* arch/arm/mach-celestial/include/mach/time.h +* +* This file contains the hardware definitions of the Celestial Platform. +* +* Copyright (C) 2010 Celestial Semiconductor +* Copyright (C) 2011 Cavium + +* Author: Xaodong Fan +*/ + +/* +* Copyright (C) 2014 Oleksij Rempel +*/ + + +#define APB_TIMER_1_LOADCOUNT 0 +#define APB_TIMER_1_CUR_VAL 0x04 +#define APB_TIMER_1_CTRL 0x08 +#define APB_TIMER_1_EOI 0x0c +#define APB_TIMER_1_INT_STATUS 0x10 + +#define APB_TIMER_2_LOADCOUNT 0x14 +#define APB_TIMER_2_CUR_VAL 0x18 +#define APB_TIMER_2_CTRL 0x1C +#define APB_TIMER_2_EOI 0x20 +#define APB_TIMER_2_INT_STATUS 0x24 + +#define APB_TIMERS_INT_STATUS 0xA0 +#define APB_TIMERS_EOI 0xA4 +#define APB_TIMERS_RAW_INT_STATUS 0xA8 + + +#define APB_TIMER_CTL_ENABLE (1 <<0) +#define APB_TIMER_CTL_PERIODIC (1<<1) +#define APB_TIMER_CTL_INTMASK (1<<2) + + +static void __iomem *cncat_base; + +static u64 cnc1800l_apbtimer_read(struct clocksource *cs){ + volatile unsigned int timeval_high,timeval_high2, timeval; + unsigned long flags; + + raw_local_irq_save(flags); + timeval_high = readw(cncat_base + APB_TIMER_1_CUR_VAL + 2); + timeval = readw(cncat_base + APB_TIMER_1_CUR_VAL); + timeval_high2 = readw(cncat_base + APB_TIMER_1_CUR_VAL + 2); + if(timeval_high2 != timeval_high){ + timeval = timeval_high2 << 16 | readw(cncat_base + APB_TIMER_1_CUR_VAL); + }else{ + timeval = timeval_high << 16 | timeval; + } + + raw_local_irq_restore(flags); + + return ((u32)0xffffffff - timeval); +} + +static struct clocksource cnc1800l_clksrc = { + .name = "cnc_clocksource", + .shift = 27, + .rating = 300, + .read = cnc1800l_apbtimer_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static int __init cnc1800l_apbtimer_init(struct device_node *np) +{ + struct clk *clk; + int ret; + unsigned long rate,u; + + cncat_base = of_io_request_and_map(np, 0, np->name); + if (IS_ERR(cncat_base)) { + pr_err("%pOFn: unable to map resource\n", np); + return PTR_ERR(cncat_base); + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("Failed to get clk!\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("Failed to enable clk!\n"); + return ret; + } + + rate = clk_get_rate(clk); + + writel_relaxed(u & (~APB_TIMER_CTL_ENABLE),cncat_base + APB_TIMER_1_CTRL); + writew_relaxed(0xffff,cncat_base + APB_TIMER_1_LOADCOUNT); + writew_relaxed(0xffff,cncat_base + APB_TIMER_1_LOADCOUNT + 2); + + u = readl(cncat_base + APB_TIMER_1_CTRL); + writel_relaxed((u | APB_TIMER_CTL_INTMASK) & (~APB_TIMER_CTL_PERIODIC),cncat_base + APB_TIMER_1_CTRL); + + clocksource_register_hz(&cnc1800l_clksrc,rate); + + u = readl(cncat_base + APB_TIMER_1_CTRL); + writel_relaxed(u | APB_TIMER_CTL_ENABLE | APB_TIMER_CTL_INTMASK | APB_TIMER_CTL_PERIODIC, cncat_base + APB_TIMER_1_CTRL); + return 0; +} +TIMER_OF_DECLARE(cnc1800l_apbtimer, "snps,dw-apb-timer-cnc1800l", cnc1800l_apbtimer_init); diff --git a/drivers/clocksource/cnc1800l_timer.c b/drivers/clocksource/cnc1800l_timer.c new file mode 100644 index 000000000..e7ee9d6ce --- /dev/null +++ b/drivers/clocksource/cnc1800l_timer.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +* arch/arm/mach-celestial/include/mach/time.h +* +* This file contains the hardware definitions of the Celestial Platform. +* +* Copyright (C) 2010 Celestial Semiconductor +* Copyright (C) 2011 Cavium + +* Author: Xaodong Fan +*/ + +/* +* Copyright (C) 2014 Oleksij Rempel +*/ + + +#define TIMER_PRE_LOAD_0 0x0 +#define TIMER_PRE_LOAD_1 0x4 +#define TIMER_THRESHOLD_0 0x8 +#define TIMER_THRESHOLD_1 0xc +#define TIMER_MODE 0x10 +#define TIMER_INTR_EN 0x14 +#define TIMER_INTR_CLR 0x18 +#define TIMER_ENABLE 0x1c +#define TIMER_CURRENT_VAL_0 0x20 +#define TIMER_CURRENT_VAL_1 0x24 +#define TIMER_INTR_STATUS 0x28 +#define TIMER_INTR_RAW_STATUS 0x2c + +static void __iomem *cnct_base; +static unsigned long cnct_reload; + +static int cnc1800l_timer_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long flags; + if(delta == 0){ + return -ETIME; + } + raw_local_irq_save(flags); + + writel_relaxed(0,cnct_base + TIMER_ENABLE); + writel_relaxed(0x7,cnct_base +TIMER_INTR_CLR); + writel_relaxed(0x0,cnct_base +TIMER_INTR_CLR); + + writel_relaxed(delta,cnct_base + TIMER_THRESHOLD_0); + writel_relaxed(0,cnct_base + TIMER_PRE_LOAD_0); + + writel_relaxed(0x1,cnct_base + TIMER_MODE); + writel_relaxed(0x1,cnct_base + TIMER_INTR_EN); + writel_relaxed(0x1,cnct_base + TIMER_ENABLE); + + raw_local_irq_restore(flags); + + return 0; +} + +static int cnc1800l_timer_shutdown(struct clock_event_device *evt) +{ + writel_relaxed(0x0, cnct_base + TIMER_ENABLE); + return 0; +} + +static int cnc1800l_timer_set_periodic(struct clock_event_device *evt) +{ + unsigned long flags; + raw_local_irq_save(flags); + + writel_relaxed(0,cnct_base + TIMER_ENABLE); + writel_relaxed(cnct_reload,cnct_base + TIMER_THRESHOLD_0); + writel_relaxed(0,cnct_base + TIMER_PRE_LOAD_0); + writel_relaxed(0,cnct_base + TIMER_MODE); + writel_relaxed(0x1,cnct_base + TIMER_INTR_EN); + writel_relaxed(0x1,cnct_base + TIMER_ENABLE); + + raw_local_irq_restore(flags); + return 0; +} + +static int cnc1800l_timer_set_oneshot(struct clock_event_device *evt) +{ + unsigned long flags; + raw_local_irq_save(flags); + + writel_relaxed(0x7, cnct_base + TIMER_INTR_CLR); + writel_relaxed(0x0, cnct_base + TIMER_INTR_CLR); + writel_relaxed(0x0,cnct_base + TIMER_ENABLE); + + raw_local_irq_restore(flags); + return 0; +} + +static struct clock_event_device event_dev = { + .name = "cnc1800l-timer", + .rating = 400, + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .set_next_event = cnc1800l_timer_set_next_event, + .set_state_shutdown = cnc1800l_timer_shutdown, + .set_state_periodic = cnc1800l_timer_set_periodic, + .set_state_oneshot = cnc1800l_timer_set_oneshot, + .tick_resume = cnc1800l_timer_shutdown, +}; + +static irqreturn_t cnc1800l_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + writel_relaxed(0x1,cnct_base + TIMER_INTR_CLR); + writel_relaxed(0x0,cnct_base + TIMER_INTR_CLR); + + evt->event_handler(evt); + return IRQ_HANDLED; +} + +static int __init cnc1800l_timer_init(struct device_node *np) +{ + int irq; + struct clk *clk; + int ret; + unsigned long rate; + + cnct_base = of_io_request_and_map(np, 0, np->name); + if (IS_ERR(cnct_base)) { + pr_err("%pOFn: unable to map resource\n", np); + return PTR_ERR(cnct_base); + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("Failed to get clk!\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("Failed to enable clk!\n"); + return ret; + } + + irq = irq_of_parse_and_map(np, 0); + ret = request_irq(irq, cnc1800l_timer_interrupt, IRQF_TIMER, + "cnc1800l-timer", &event_dev); + if (ret) { + pr_err("Failed to setup irq!\n"); + return ret; + } + + writel_relaxed(0x0,cnct_base + TIMER_INTR_EN); + writel_relaxed(0x0,cnct_base + TIMER_ENABLE); + writel_relaxed(0x1,cnct_base + TIMER_INTR_CLR); + writel_relaxed(0x0,cnct_base + TIMER_INTR_CLR); + + rate = clk_get_rate(clk); + + cnct_reload = DIV_ROUND_CLOSEST(rate,HZ); + + event_dev.cpumask = cpumask_of(0); + clockevents_config_and_register(&event_dev, rate, 0xf, 0xfffffffc); + + return 0; +} +TIMER_OF_DECLARE(cnc1800l_timer, "cavium,cnc1800l-timer", + cnc1800l_timer_init);