cevt-sb1250.c (3661B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2000, 2001 Broadcom Corporation 4 */ 5#include <linux/clockchips.h> 6#include <linux/interrupt.h> 7#include <linux/irq.h> 8#include <linux/percpu.h> 9#include <linux/smp.h> 10 11#include <asm/addrspace.h> 12#include <asm/io.h> 13#include <asm/time.h> 14 15#include <asm/sibyte/sb1250.h> 16#include <asm/sibyte/sb1250_regs.h> 17#include <asm/sibyte/sb1250_int.h> 18#include <asm/sibyte/sb1250_scd.h> 19 20#define IMR_IP2_VAL K_INT_MAP_I0 21#define IMR_IP3_VAL K_INT_MAP_I1 22#define IMR_IP4_VAL K_INT_MAP_I2 23 24/* 25 * The general purpose timer ticks at 1MHz independent if 26 * the rest of the system 27 */ 28 29static int sibyte_shutdown(struct clock_event_device *evt) 30{ 31 void __iomem *cfg; 32 33 cfg = IOADDR(A_SCD_TIMER_REGISTER(smp_processor_id(), R_SCD_TIMER_CFG)); 34 35 /* Stop the timer until we actually program a shot */ 36 __raw_writeq(0, cfg); 37 38 return 0; 39} 40 41static int sibyte_set_periodic(struct clock_event_device *evt) 42{ 43 unsigned int cpu = smp_processor_id(); 44 void __iomem *cfg, *init; 45 46 cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); 47 init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); 48 49 __raw_writeq(0, cfg); 50 __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, init); 51 __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, cfg); 52 53 return 0; 54} 55 56static int sibyte_next_event(unsigned long delta, struct clock_event_device *cd) 57{ 58 unsigned int cpu = smp_processor_id(); 59 void __iomem *cfg, *init; 60 61 cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); 62 init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); 63 64 __raw_writeq(0, cfg); 65 __raw_writeq(delta - 1, init); 66 __raw_writeq(M_SCD_TIMER_ENABLE, cfg); 67 68 return 0; 69} 70 71static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) 72{ 73 unsigned int cpu = smp_processor_id(); 74 struct clock_event_device *cd = dev_id; 75 void __iomem *cfg; 76 unsigned long tmode; 77 78 if (clockevent_state_periodic(cd)) 79 tmode = M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS; 80 else 81 tmode = 0; 82 83 /* ACK interrupt */ 84 cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); 85 ____raw_writeq(tmode, cfg); 86 87 cd->event_handler(cd); 88 89 return IRQ_HANDLED; 90} 91 92static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent); 93static DEFINE_PER_CPU(char [18], sibyte_hpt_name); 94 95void sb1250_clockevent_init(void) 96{ 97 unsigned int cpu = smp_processor_id(); 98 unsigned int irq = K_INT_TIMER_0 + cpu; 99 struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu); 100 unsigned char *name = per_cpu(sibyte_hpt_name, cpu); 101 unsigned long flags = IRQF_PERCPU | IRQF_TIMER; 102 103 /* Only have 4 general purpose timers, and we use last one as hpt */ 104 BUG_ON(cpu > 2); 105 106 sprintf(name, "sb1250-counter-%d", cpu); 107 cd->name = name; 108 cd->features = CLOCK_EVT_FEAT_PERIODIC | 109 CLOCK_EVT_FEAT_ONESHOT; 110 clockevent_set_clock(cd, V_SCD_TIMER_FREQ); 111 cd->max_delta_ns = clockevent_delta2ns(0x7fffff, cd); 112 cd->max_delta_ticks = 0x7fffff; 113 cd->min_delta_ns = clockevent_delta2ns(2, cd); 114 cd->min_delta_ticks = 2; 115 cd->rating = 200; 116 cd->irq = irq; 117 cd->cpumask = cpumask_of(cpu); 118 cd->set_next_event = sibyte_next_event; 119 cd->set_state_shutdown = sibyte_shutdown; 120 cd->set_state_periodic = sibyte_set_periodic; 121 cd->set_state_oneshot = sibyte_shutdown; 122 clockevents_register_device(cd); 123 124 sb1250_mask_irq(cpu, irq); 125 126 /* 127 * Map the timer interrupt to IP[4] of this cpu 128 */ 129 __raw_writeq(IMR_IP4_VAL, 130 IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) + 131 (irq << 3))); 132 133 sb1250_unmask_irq(cpu, irq); 134 135 irq_set_affinity(irq, cpumask_of(cpu)); 136 if (request_irq(irq, sibyte_counter_handler, flags, name, cd)) 137 pr_err("Failed to request irq %d (%s)\n", irq, name); 138}