timer: enable percpu local timer for m8b
authorJianxin Pan <jianxin.pan@amlogic.com>
Wed, 19 Apr 2017 09:48:18 +0000 (17:48 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 20 Apr 2017 13:19:40 +0000 (06:19 -0700)
PD#141217: enable  percpu local timer for m8b
1. TimerA ~ TimerD for CPU0 ~ CPU3
2. TimerE is clock source
3. TimerE is delay source
4. hrtimer is enabled

Change-Id: I79b0b73f5ab5ec4d2321faa8ea5cba25e28550b2
Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
MAINTAINERS
arch/arm/boot/dts/amlogic/meson8b.dtsi
arch/arm/configs/meson32_defconfig
arch/arm/mach-meson/Kconfig
drivers/amlogic/clocksource/Kconfig
drivers/amlogic/clocksource/Makefile
drivers/amlogic/clocksource/meson_timer.c [new file with mode: 0644]

index 295c508..73a7f50 100644 (file)
@@ -13813,3 +13813,7 @@ F: drivers/amlogic/pwm/pwm_meson_util.c
 AMLOGIC M400 BSP
 M: Peipeng Zhao <peipeng.zhao@amlogic.com>
 F: arch/arm/boot/dts/amlogic/meson8b_m400.dts
+
+AMLOGIC LOCAL TIMER
+M: Jianxin Pan <jianxin.pan@amlogic.com>
+F: drivers/amlogic/clocksource
index 1d82d2d..747478d 100644 (file)
@@ -35,6 +35,7 @@
                        compatible = "arm,cortex-a5";
                        next-level-cache = <&L2>;
                        reg = <0x200>;
+                       timer=<&timer_a>;
                };
 
                cpu@201 {
@@ -42,6 +43,7 @@
                        compatible = "arm,cortex-a5";
                        next-level-cache = <&L2>;
                        reg = <0x1>;
+                       timer=<&timer_b>;
                };
 
                cpu@202 {
@@ -49,6 +51,7 @@
                        compatible = "arm,cortex-a5";
                        next-level-cache = <&L2>;
                        reg = <0x2>;
+                       timer=<&timer_c>;
                };
 
                cpu@203 {
@@ -56,6 +59,7 @@
                        compatible = "arm,cortex-a5";
                        next-level-cache = <&L2>;
                        reg = <0x3>;
+                       timer=<&timer_d>;
                };
        };
 
                                        <0xc11081a8 4>,
                                        <0xd9040000 4>;
                };
-               timer@c1109940 {
-                       compatible = "amlogic,meson6-timer";
-                       reg = <0xc1109940 0x18>;
-                       interrupts = <0 10 1>;
-               };
 
+       timer{
+               compatible = "arm, meson-timer";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+               reg=   <0xc1109940 4 0xc1109954 4>;
+               timer_a:timer-a{
+                       timer_name="MESON TIMER-A";
+                       clockevent-rating=<300>;
+                       clockevent-shift=<20>;
+                       clockevent-features=<0x03>;
+                       interrupts = <0 10 0>;
+                       bit_enable=<16>;
+                       bit_mode=<12>;
+                       bit_resolution=<0>;
+                       reg=   <0xc1109944 4>;
+               };
+               timer_b:timer-b{
+                       timer_name="MESON TIMER-B";
+                       clockevent-rating=<300>;
+                       clockevent-shift=<20>;
+                       clockevent-features=<0x03>;
+                       interrupts = <0 11 0>;
+                       bit_enable=<17>;
+                       bit_mode=<13>;
+                       bit_resolution=<2>;
+                       reg=<0xc1109948 4>;
+               };
+               timer_c:timer-c{
+                       timer_name="MESON TIMER-C";
+                       clockevent-rating=<300>;
+                       clockevent-shift=<20>;
+                       clockevent-features=<0x03>;
+                       interrupts = <0 6 0>;
+                       bit_enable=<18>;
+                       bit_mode=<14>;
+                       bit_resolution=<4>;
+                       reg=<0xc110994c 4>;
+               };
+               timer_d:timer-d{
+                       timer_name="MESON TIMER-D";
+                       clockevent-rating=<300>;
+                       clockevent-shift=<20>;
+                       clockevent-features=<0x03>;
+                       interrupts = <0 29 0>;
+                       bit_enable=<19>;
+                       bit_mode=<15>;
+                       bit_resolution=<6>;
+                       reg=<0xc1109950 4>;
+               };
+       };
                uart_AO: serial@c81004c0 {
                        compatible = "amlogic, meson-uart";
                        reg = <0xc81004c0 0x18>;
index eb80aa7..b259ba1 100644 (file)
@@ -55,6 +55,8 @@ CONFIG_AMLOGIC_I2C_MASTER=y
 CONFIG_AMLOGIC_CPU_VERSION=y
 CONFIG_AMLOGIC_M8B_MESON32_VERSION=y
 CONFIG_AMLOGIC_REG_ACCESS=y
+CONFIG_AMLOGIC_TIMER=y
+CONFIG_AMLOGIC_LOCAL_TIMER=y
 CONFIG_AMLOGIC_CLK=y
 # CONFIG_AMLOGIC_RESET is not set
 CONFIG_AMLOGIC_M8B_CLK=y
@@ -140,7 +142,6 @@ CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=16
 CONFIG_STAGING=y
 CONFIG_CHROME_PLATFORMS=y
-CONFIG_ARM_TIMER_SP804=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_PM_DEVFREQ=y
 CONFIG_EXTCON=y
index 5e808a2..3243900 100644 (file)
@@ -23,6 +23,5 @@ config MACH_MESON8
 config MACH_MESON8B
        bool "Amlogic Meson8b SoCs support"
        default ARCH_MESON
-       select MESON6_TIMER
 
 endif
index 136e889..beb7d12 100644 (file)
@@ -4,6 +4,13 @@ menuconfig AMLOGIC_TIMER
     help
       This is the Amlogic Meson driver interface driver
 if AMLOGIC_TIMER
+config AMLOGIC_LOCAL_TIMER
+       bool "meson local timer support"
+       def_bool n
+       depends on AMLOGIC_TIMER
+       select CLKSRC_OF if OF
+       help
+         This is a new clocksource driver for amlogic timer
 config AMLOGIC_BC_TIMER
        bool "meson broadcast timer support"
        def_bool n
index 83a0ca2..486a3c8 100644 (file)
@@ -1 +1,2 @@
+obj-$(CONFIG_AMLOGIC_LOCAL_TIMER)      += meson_timer.o
 obj-$(CONFIG_AMLOGIC_BC_TIMER) += meson_bc_timer.o
diff --git a/drivers/amlogic/clocksource/meson_timer.c b/drivers/amlogic/clocksource/meson_timer.c
new file mode 100644 (file)
index 0000000..563cc17
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * drivers/amlogic/clocksource/meson_timer.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/stat.h>
+#include <asm/memory.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <asm/smp_plat.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/cpu.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "meson_timer: " fmt
+
+/***********************************************************************
+ * System timer
+ **********************************************************************/
+#define TIMER_E_RESOLUTION_BIT         8
+#define TIMER_E_ENABLE_BIT        20
+#define TIMER_E_RESOLUTION_MASK       (7UL << TIMER_E_RESOLUTION_BIT)
+#define TIMER_E_RESOLUTION_SYS           0
+#define TIMER_E_RESOLUTION_1us           1
+#define TIMER_E_RESOLUTION_10us          2
+#define TIMER_E_RESOLUTION_100us         3
+#define TIMER_E_RESOLUTION_1ms           4
+
+#define TIMER_RESOLUTION_1us            0
+#define TIMER_RESOLUTION_10us           1
+#define TIMER_RESOLUTION_100us          2
+#define TIMER_RESOLUTION_1ms            3
+
+struct meson_clock {
+       struct irqaction        irq;
+       const char *name;       /*A,B,C,D,F,G,H,I*/
+       int     bit_enable;
+       int     bit_mode;
+       int     bit_resolution;
+       void __iomem *mux_reg;
+       void __iomem *reg;
+       unsigned int init_flag;
+};
+
+
+static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
+static DEFINE_PER_CPU(struct meson_clock, percpu_mesonclock);
+
+static void __iomem *timer_ctrl_base;
+static void __iomem *clocksrc_base;
+
+static inline void aml_set_reg32_mask(void __iomem *_reg, const uint32_t _mask)
+{
+       uint32_t _val;
+
+       _val = readl_relaxed(_reg) | _mask;
+
+       writel_relaxed(_val, _reg);
+}
+
+static inline void aml_write_reg32(void __iomem *_reg, const uint32_t _value)
+{
+       writel_relaxed(_value, _reg);
+}
+
+static inline void aml_clr_reg32_mask(void __iomem *_reg, const uint32_t _mask)
+{
+       writel_relaxed((readl_relaxed(_reg) & (~(_mask))), _reg);
+}
+
+static inline void
+aml_set_reg32_bits(void __iomem *_reg, const uint32_t _value,
+               const uint32_t _start, const uint32_t _len)
+{
+       writel_relaxed(((readl_relaxed(_reg) &
+                                       ~(((1L << (_len))-1) << (_start))) |
+                               (((_value)&((1L<<(_len))-1)) << (_start))),
+                               _reg);
+}
+
+static cycle_t cycle_read_timerE(struct clocksource *cs)
+{
+       return (cycle_t) readl_relaxed(clocksrc_base);
+}
+static unsigned long cycle_read_timerE1(void)
+{
+       return (unsigned long) readl_relaxed(clocksrc_base);
+}
+static struct clocksource clocksource_timer_e = {
+       .name   = "Timer-E",
+       .rating = 300,
+       .read   = cycle_read_timerE,
+       .mask   = CLOCKSOURCE_MASK(32),
+       .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+static u64 notrace meson8_read_sched_clock(void)
+{
+       return (u64)readl_relaxed(clocksrc_base);
+}
+static void __init meson_clocksource_init(void)
+{
+       aml_clr_reg32_mask(timer_ctrl_base, TIMER_E_RESOLUTION_MASK);
+       aml_set_reg32_mask(timer_ctrl_base,
+                       TIMER_E_RESOLUTION_1us << TIMER_E_RESOLUTION_BIT);
+
+       clocksource_timer_e.shift = 22;
+       clocksource_timer_e.mult = 4194304000u;
+       clocksource_register_khz(&clocksource_timer_e, 1000);
+       sched_clock_register(meson8_read_sched_clock, 32, USEC_PER_SEC);
+}
+
+static int meson_set_next_event(unsigned long evt,
+                               struct clock_event_device *dev)
+{
+       int cpuidx = smp_processor_id();
+       struct meson_clock *clk =  &per_cpu(percpu_mesonclock, cpuidx);
+       /* use a big number to clear previous trigger cleanly */
+       aml_set_reg32_mask(clk->reg, evt & 0xffff);
+       /* then set next event */
+       aml_set_reg32_bits(clk->reg, evt, 0, 16);
+       return 0;
+}
+
+/* Clock event timer interrupt handler */
+static irqreturn_t meson_timer_interrupt(int irq, void *dev_id)
+{
+       struct clock_event_device *evt = dev_id;
+
+       if (evt == NULL || evt->event_handler == NULL) {
+               WARN_ONCE(evt == NULL || evt->event_handler == NULL,
+                       "%p %s %p %d", evt, evt?evt->name:NULL,
+                       evt?evt->event_handler:NULL, irq);
+               return IRQ_HANDLED;
+       }
+       evt->event_handler(evt);
+       return IRQ_HANDLED;
+
+}
+
+void local_timer_setup_data(int cpuidx)
+{
+               phandle phandle =  -1;
+               u32 hwid = 0;
+               struct device_node *cpu, *cpus, *timer;
+               struct meson_clock *mclk = &per_cpu(percpu_mesonclock, cpuidx);
+               struct clock_event_device *clock_evt =
+                       &per_cpu(percpu_clockevent, cpuidx);
+               cpus = of_find_node_by_path("/cpus");
+               if (!cpus)
+                       return;
+               for_each_child_of_node(cpus, cpu) {
+                       if (hwid == cpuidx)
+                               break;
+                       hwid++;
+               }
+               if (hwid != cpuidx)
+                       cpu = of_get_next_child(cpus, NULL);
+
+               if (of_property_read_u32(cpu, "timer", &phandle)) {
+                       pr_info(" * missing timer property\n");
+                       return;
+               }
+               timer = of_find_node_by_phandle(phandle);
+               if (!timer) {
+                       pr_info(" * %s missing timer phandle\n",
+                               cpu->full_name);
+                       return;
+               }
+               if (of_property_read_string(timer, "timer_name",
+                       &clock_evt->name))
+                       return;
+
+               if (of_property_read_u32(timer, "clockevent-rating",
+                       &clock_evt->rating))
+                       return;
+
+               if (of_property_read_u32(timer, "clockevent-shift",
+                       &clock_evt->shift))
+                       return;
+
+               if (of_property_read_u32(timer, "clockevent-features",
+                       &clock_evt->features))
+                       return;
+
+               if (of_property_read_u32(timer, "bit_enable",
+                       &mclk->bit_enable))
+                       return;
+
+               if (of_property_read_u32(timer, "bit_mode",
+                       &mclk->bit_mode))
+                       return;
+
+               if (of_property_read_u32(timer, "bit_resolution",
+                       &mclk->bit_resolution))
+                       return;
+
+               mclk->mux_reg = timer_ctrl_base;
+               mclk->reg = of_iomap(timer, 0);
+               pr_info("%s mclk->mux_reg =%p,mclk->reg =%p\n",
+                               clock_evt->name, mclk->mux_reg, mclk->reg);
+               mclk->irq.irq = irq_of_parse_and_map(timer, 0);
+
+}
+int  clockevent_local_timer(unsigned int cpu)
+       {
+               int cpuidx = smp_processor_id();
+
+               struct meson_clock *mclk = &per_cpu(percpu_mesonclock, cpuidx);
+               struct clock_event_device *clock_evt =
+                       &per_cpu(percpu_clockevent, cpuidx);
+
+               aml_set_reg32_mask(mclk->mux_reg,
+                       ((1 << mclk->bit_mode)
+                       |(TIMER_RESOLUTION_1us << mclk->bit_resolution)));
+               aml_write_reg32(mclk->reg, 9999);
+
+               clock_evt->mult = div_sc(1000000, NSEC_PER_SEC, 20);
+               clock_evt->max_delta_ns =
+                       clockevent_delta2ns(0xfffe, clock_evt);
+               clock_evt->min_delta_ns = clockevent_delta2ns(1, clock_evt);
+               clock_evt->set_next_event = meson_set_next_event;
+               clock_evt->cpumask = cpumask_of(cpuidx);
+               clock_evt->irq = mclk->irq.irq;
+               mclk->irq.dev_id = clock_evt;
+               mclk->irq.handler = meson_timer_interrupt;
+               mclk->irq.name = clock_evt->name;
+               mclk->irq.flags =
+                       IRQF_TIMER | IRQF_IRQPOLL | IRQF_TRIGGER_RISING;
+               clockevents_register_device(clock_evt);
+               setup_irq(mclk->irq.irq, &mclk->irq);
+               if (cpuidx)
+                       irq_force_affinity(mclk->irq.irq, cpumask_of(cpuidx));
+               enable_percpu_irq(mclk->irq.irq, 0);
+               return 0;
+       }
+
+int  meson_local_timer_stop(unsigned int cpuidx)
+{
+       struct meson_clock *clk;
+       struct clock_event_device *clock_evt =
+               &per_cpu(percpu_clockevent, cpuidx);
+       if (!clock_evt) {
+               pr_err("meson_local_timer_stop: null evt!\n");
+               return 0;
+       }
+       if (cpuidx == 0)
+               BUG();
+       clk = &per_cpu(percpu_mesonclock, cpuidx);
+
+       aml_clr_reg32_mask(clk->mux_reg, (1 << clk->bit_enable));
+       disable_percpu_irq(clk->irq.irq);
+       return 0;
+}
+
+int __init meson_timer_init(struct device_node *np)
+{
+       int i;
+       static struct delay_timer aml_delay_timer;
+
+       timer_ctrl_base = of_iomap(np, 0);
+       clocksrc_base = of_iomap(np, 1);
+       meson_clocksource_init();
+       for (i = 0; i < nr_cpu_ids; i++)
+               local_timer_setup_data(i);
+       cpuhp_setup_state(CPUHP_AP_ARM_ARCH_TIMER_STARTING,
+                               "AP_ARM_ARCH_TIMER_STARTING",
+                               clockevent_local_timer, meson_local_timer_stop);
+
+       aml_delay_timer.read_current_timer = &cycle_read_timerE1;
+       aml_delay_timer.freq = USEC_PER_SEC;
+       register_current_timer_delay(&aml_delay_timer);
+       return 0;
+}
+CLOCKSOURCE_OF_DECLARE(meson_timer, "arm, meson-timer", meson_timer_init);