--- /dev/null
+/*
+ * 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);