162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * J-Core SoC PIT/clocksource driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Smart Energy Instruments, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/clockchips.h> 1262306a36Sopenharmony_ci#include <linux/clocksource.h> 1362306a36Sopenharmony_ci#include <linux/sched_clock.h> 1462306a36Sopenharmony_ci#include <linux/cpu.h> 1562306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/of_irq.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define PIT_IRQ_SHIFT 12 2062306a36Sopenharmony_ci#define PIT_PRIO_SHIFT 20 2162306a36Sopenharmony_ci#define PIT_ENABLE_SHIFT 26 2262306a36Sopenharmony_ci#define PIT_PRIO_MASK 0xf 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define REG_PITEN 0x00 2562306a36Sopenharmony_ci#define REG_THROT 0x10 2662306a36Sopenharmony_ci#define REG_COUNT 0x14 2762306a36Sopenharmony_ci#define REG_BUSPD 0x18 2862306a36Sopenharmony_ci#define REG_SECHI 0x20 2962306a36Sopenharmony_ci#define REG_SECLO 0x24 3062306a36Sopenharmony_ci#define REG_NSEC 0x28 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct jcore_pit { 3362306a36Sopenharmony_ci struct clock_event_device ced; 3462306a36Sopenharmony_ci void __iomem *base; 3562306a36Sopenharmony_ci unsigned long periodic_delta; 3662306a36Sopenharmony_ci u32 enable_val; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void __iomem *jcore_pit_base; 4062306a36Sopenharmony_cistatic struct jcore_pit __percpu *jcore_pit_percpu; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic notrace u64 jcore_sched_clock_read(void) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 seclo, nsec, seclo0; 4562306a36Sopenharmony_ci __iomem void *base = jcore_pit_base; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci seclo = readl(base + REG_SECLO); 4862306a36Sopenharmony_ci do { 4962306a36Sopenharmony_ci seclo0 = seclo; 5062306a36Sopenharmony_ci nsec = readl(base + REG_NSEC); 5162306a36Sopenharmony_ci seclo = readl(base + REG_SECLO); 5262306a36Sopenharmony_ci } while (seclo0 != seclo); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return seclo * NSEC_PER_SEC + nsec; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic u64 jcore_clocksource_read(struct clocksource *cs) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return jcore_sched_clock_read(); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int jcore_pit_disable(struct jcore_pit *pit) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci writel(0, pit->base + REG_PITEN); 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int jcore_pit_set(unsigned long delta, struct jcore_pit *pit) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci jcore_pit_disable(pit); 7162306a36Sopenharmony_ci writel(delta, pit->base + REG_THROT); 7262306a36Sopenharmony_ci writel(pit->enable_val, pit->base + REG_PITEN); 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int jcore_pit_set_state_shutdown(struct clock_event_device *ced) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return jcore_pit_disable(pit); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int jcore_pit_set_state_oneshot(struct clock_event_device *ced) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return jcore_pit_disable(pit); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int jcore_pit_set_state_periodic(struct clock_event_device *ced) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return jcore_pit_set(pit->periodic_delta, pit); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int jcore_pit_set_next_event(unsigned long delta, 9862306a36Sopenharmony_ci struct clock_event_device *ced) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return jcore_pit_set(delta, pit); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int jcore_pit_local_init(unsigned cpu) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct jcore_pit *pit = this_cpu_ptr(jcore_pit_percpu); 10862306a36Sopenharmony_ci unsigned buspd, freq; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pr_info("Local J-Core PIT init on cpu %u\n", cpu); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci buspd = readl(pit->base + REG_BUSPD); 11362306a36Sopenharmony_ci freq = DIV_ROUND_CLOSEST(NSEC_PER_SEC, buspd); 11462306a36Sopenharmony_ci pit->periodic_delta = DIV_ROUND_CLOSEST(NSEC_PER_SEC, HZ * buspd); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci clockevents_config_and_register(&pit->ced, freq, 1, ULONG_MAX); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic irqreturn_t jcore_timer_interrupt(int irq, void *dev_id) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct jcore_pit *pit = this_cpu_ptr(dev_id); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (clockevent_state_oneshot(&pit->ced)) 12662306a36Sopenharmony_ci jcore_pit_disable(pit); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci pit->ced.event_handler(&pit->ced); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return IRQ_HANDLED; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int __init jcore_pit_init(struct device_node *node) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int err; 13662306a36Sopenharmony_ci unsigned pit_irq, cpu; 13762306a36Sopenharmony_ci unsigned long hwirq; 13862306a36Sopenharmony_ci u32 irqprio, enable_val; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci jcore_pit_base = of_iomap(node, 0); 14162306a36Sopenharmony_ci if (!jcore_pit_base) { 14262306a36Sopenharmony_ci pr_err("Error: Cannot map base address for J-Core PIT\n"); 14362306a36Sopenharmony_ci return -ENXIO; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci pit_irq = irq_of_parse_and_map(node, 0); 14762306a36Sopenharmony_ci if (!pit_irq) { 14862306a36Sopenharmony_ci pr_err("Error: J-Core PIT has no IRQ\n"); 14962306a36Sopenharmony_ci return -ENXIO; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pr_info("Initializing J-Core PIT at %p IRQ %d\n", 15362306a36Sopenharmony_ci jcore_pit_base, pit_irq); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci err = clocksource_mmio_init(jcore_pit_base, "jcore_pit_cs", 15662306a36Sopenharmony_ci NSEC_PER_SEC, 400, 32, 15762306a36Sopenharmony_ci jcore_clocksource_read); 15862306a36Sopenharmony_ci if (err) { 15962306a36Sopenharmony_ci pr_err("Error registering clocksource device: %d\n", err); 16062306a36Sopenharmony_ci return err; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci sched_clock_register(jcore_sched_clock_read, 32, NSEC_PER_SEC); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci jcore_pit_percpu = alloc_percpu(struct jcore_pit); 16662306a36Sopenharmony_ci if (!jcore_pit_percpu) { 16762306a36Sopenharmony_ci pr_err("Failed to allocate memory for clock event device\n"); 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci err = request_irq(pit_irq, jcore_timer_interrupt, 17262306a36Sopenharmony_ci IRQF_TIMER | IRQF_PERCPU, 17362306a36Sopenharmony_ci "jcore_pit", jcore_pit_percpu); 17462306a36Sopenharmony_ci if (err) { 17562306a36Sopenharmony_ci pr_err("pit irq request failed: %d\n", err); 17662306a36Sopenharmony_ci free_percpu(jcore_pit_percpu); 17762306a36Sopenharmony_ci return err; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * The J-Core PIT is not hard-wired to a particular IRQ, but 18262306a36Sopenharmony_ci * integrated with the interrupt controller such that the IRQ it 18362306a36Sopenharmony_ci * generates is programmable, as follows: 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * The bit layout of the PIT enable register is: 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * .....e..ppppiiiiiiii............ 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * where the .'s indicate unrelated/unused bits, e is enable, 19062306a36Sopenharmony_ci * p is priority, and i is hard irq number. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * For the PIT included in AIC1 (obsolete but still in use), 19362306a36Sopenharmony_ci * any hard irq (trap number) can be programmed via the 8 19462306a36Sopenharmony_ci * iiiiiiii bits, and a priority (0-15) is programmable 19562306a36Sopenharmony_ci * separately in the pppp bits. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * For the PIT included in AIC2 (current), the programming 19862306a36Sopenharmony_ci * interface is equivalent modulo interrupt mapping. This is 19962306a36Sopenharmony_ci * why a different compatible tag was not used. However only 20062306a36Sopenharmony_ci * traps 64-127 (the ones actually intended to be used for 20162306a36Sopenharmony_ci * interrupts, rather than syscalls/exceptions/etc.) can be 20262306a36Sopenharmony_ci * programmed (the high 2 bits of i are ignored) and the 20362306a36Sopenharmony_ci * priority pppp is <<2'd and or'd onto the irq number. This 20462306a36Sopenharmony_ci * choice seems to have been made on the hardware engineering 20562306a36Sopenharmony_ci * side under an assumption that preserving old AIC1 priority 20662306a36Sopenharmony_ci * mappings was important. Future models will likely ignore 20762306a36Sopenharmony_ci * the pppp field. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci hwirq = irq_get_irq_data(pit_irq)->hwirq; 21062306a36Sopenharmony_ci irqprio = (hwirq >> 2) & PIT_PRIO_MASK; 21162306a36Sopenharmony_ci enable_val = (1U << PIT_ENABLE_SHIFT) 21262306a36Sopenharmony_ci | (hwirq << PIT_IRQ_SHIFT) 21362306a36Sopenharmony_ci | (irqprio << PIT_PRIO_SHIFT); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for_each_present_cpu(cpu) { 21662306a36Sopenharmony_ci struct jcore_pit *pit = per_cpu_ptr(jcore_pit_percpu, cpu); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pit->base = of_iomap(node, cpu); 21962306a36Sopenharmony_ci if (!pit->base) { 22062306a36Sopenharmony_ci pr_err("Unable to map PIT for cpu %u\n", cpu); 22162306a36Sopenharmony_ci continue; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci pit->ced.name = "jcore_pit"; 22562306a36Sopenharmony_ci pit->ced.features = CLOCK_EVT_FEAT_PERIODIC 22662306a36Sopenharmony_ci | CLOCK_EVT_FEAT_ONESHOT 22762306a36Sopenharmony_ci | CLOCK_EVT_FEAT_PERCPU; 22862306a36Sopenharmony_ci pit->ced.cpumask = cpumask_of(cpu); 22962306a36Sopenharmony_ci pit->ced.rating = 400; 23062306a36Sopenharmony_ci pit->ced.irq = pit_irq; 23162306a36Sopenharmony_ci pit->ced.set_state_shutdown = jcore_pit_set_state_shutdown; 23262306a36Sopenharmony_ci pit->ced.set_state_periodic = jcore_pit_set_state_periodic; 23362306a36Sopenharmony_ci pit->ced.set_state_oneshot = jcore_pit_set_state_oneshot; 23462306a36Sopenharmony_ci pit->ced.set_next_event = jcore_pit_set_next_event; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pit->enable_val = enable_val; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci cpuhp_setup_state(CPUHP_AP_JCORE_TIMER_STARTING, 24062306a36Sopenharmony_ci "clockevents/jcore:starting", 24162306a36Sopenharmony_ci jcore_pit_local_init, NULL); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciTIMER_OF_DECLARE(jcore_pit, "jcore,pit", jcore_pit_init); 247