1f137e463SAndrew Isaacson /* 2f137e463SAndrew Isaacson * Copyright (C) 2000,2001,2004 Broadcom Corporation 3f137e463SAndrew Isaacson * 4f137e463SAndrew Isaacson * This program is free software; you can redistribute it and/or 5f137e463SAndrew Isaacson * modify it under the terms of the GNU General Public License 6f137e463SAndrew Isaacson * as published by the Free Software Foundation; either version 2 7f137e463SAndrew Isaacson * of the License, or (at your option) any later version. 8f137e463SAndrew Isaacson * 9f137e463SAndrew Isaacson * This program is distributed in the hope that it will be useful, 10f137e463SAndrew Isaacson * but WITHOUT ANY WARRANTY; without even the implied warranty of 11f137e463SAndrew Isaacson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12f137e463SAndrew Isaacson * GNU General Public License for more details. 13f137e463SAndrew Isaacson * 14f137e463SAndrew Isaacson * You should have received a copy of the GNU General Public License 15f137e463SAndrew Isaacson * along with this program; if not, write to the Free Software 16f137e463SAndrew Isaacson * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17f137e463SAndrew Isaacson */ 18f137e463SAndrew Isaacson 19f137e463SAndrew Isaacson /* 20f137e463SAndrew Isaacson * These are routines to set up and handle interrupts from the 21f137e463SAndrew Isaacson * bcm1480 general purpose timer 0. We're using the timer as a 22f137e463SAndrew Isaacson * system clock, so we set it up to run at 100 Hz. On every 23f137e463SAndrew Isaacson * interrupt, we update our idea of what the time of day is, 24f137e463SAndrew Isaacson * then call do_timer() in the architecture-independent kernel 25f137e463SAndrew Isaacson * code to do general bookkeeping (e.g. update jiffies, run 26f137e463SAndrew Isaacson * bottom halves, etc.) 27f137e463SAndrew Isaacson */ 28e9f874b6SRalf Baechle #include <linux/clockchips.h> 29f137e463SAndrew Isaacson #include <linux/interrupt.h> 30d527eef5SRalf Baechle #include <linux/percpu.h> 31f137e463SAndrew Isaacson #include <linux/spinlock.h> 32f137e463SAndrew Isaacson 33f137e463SAndrew Isaacson #include <asm/irq.h> 34f137e463SAndrew Isaacson #include <asm/addrspace.h> 35f137e463SAndrew Isaacson #include <asm/time.h> 36f137e463SAndrew Isaacson #include <asm/io.h> 37f137e463SAndrew Isaacson 38f137e463SAndrew Isaacson #include <asm/sibyte/bcm1480_regs.h> 39f137e463SAndrew Isaacson #include <asm/sibyte/sb1250_regs.h> 40f137e463SAndrew Isaacson #include <asm/sibyte/bcm1480_int.h> 41f137e463SAndrew Isaacson #include <asm/sibyte/bcm1480_scd.h> 42f137e463SAndrew Isaacson 43f137e463SAndrew Isaacson #include <asm/sibyte/sb1250.h> 44f137e463SAndrew Isaacson 45f137e463SAndrew Isaacson 46f137e463SAndrew Isaacson #define IMR_IP2_VAL K_BCM1480_INT_MAP_I0 47f137e463SAndrew Isaacson #define IMR_IP3_VAL K_BCM1480_INT_MAP_I1 48f137e463SAndrew Isaacson #define IMR_IP4_VAL K_BCM1480_INT_MAP_I2 49f137e463SAndrew Isaacson 5016b7b2acSAtsushi Nemoto #ifdef CONFIG_SIMULATION 5116b7b2acSAtsushi Nemoto #define BCM1480_HPT_VALUE 50000 5216b7b2acSAtsushi Nemoto #else 5316b7b2acSAtsushi Nemoto #define BCM1480_HPT_VALUE 1000000 5416b7b2acSAtsushi Nemoto #endif 5516b7b2acSAtsushi Nemoto 56f137e463SAndrew Isaacson extern int bcm1480_steal_irq(int irq); 57f137e463SAndrew Isaacson 58e9f874b6SRalf Baechle void __init plat_time_init(void) 59f137e463SAndrew Isaacson { 60e9f874b6SRalf Baechle unsigned int cpu = smp_processor_id(); 61e9f874b6SRalf Baechle unsigned int irq = K_BCM1480_INT_TIMER_0 + cpu; 62f137e463SAndrew Isaacson 63e9f874b6SRalf Baechle BUG_ON(cpu > 3); /* Only have 4 general purpose timers */ 64f137e463SAndrew Isaacson 65f137e463SAndrew Isaacson bcm1480_mask_irq(cpu, irq); 66f137e463SAndrew Isaacson 67f137e463SAndrew Isaacson /* Map the timer interrupt to ip[4] of this cpu */ 68f137e463SAndrew Isaacson __raw_writeq(IMR_IP4_VAL, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) 69f137e463SAndrew Isaacson + (irq<<3))); 70f137e463SAndrew Isaacson 71f137e463SAndrew Isaacson bcm1480_unmask_irq(cpu, irq); 72f137e463SAndrew Isaacson bcm1480_steal_irq(irq); 73e9f874b6SRalf Baechle } 74e9f874b6SRalf Baechle 75e9f874b6SRalf Baechle /* 76e9f874b6SRalf Baechle * The general purpose timer ticks at 1 Mhz independent if 77e9f874b6SRalf Baechle * the rest of the system 78e9f874b6SRalf Baechle */ 79e9f874b6SRalf Baechle static void sibyte_set_mode(enum clock_event_mode mode, 80e9f874b6SRalf Baechle struct clock_event_device *evt) 81e9f874b6SRalf Baechle { 82e9f874b6SRalf Baechle unsigned int cpu = smp_processor_id(); 83e9f874b6SRalf Baechle void __iomem *timer_cfg, *timer_init; 84e9f874b6SRalf Baechle 85e9f874b6SRalf Baechle timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); 869ee5389cSAtsushi Nemoto timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); 87e9f874b6SRalf Baechle 88e9f874b6SRalf Baechle switch (mode) { 89e9f874b6SRalf Baechle case CLOCK_EVT_MODE_PERIODIC: 90e9f874b6SRalf Baechle __raw_writeq(0, timer_cfg); 91e9f874b6SRalf Baechle __raw_writeq(BCM1480_HPT_VALUE / HZ - 1, timer_init); 92e9f874b6SRalf Baechle __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, 93e9f874b6SRalf Baechle timer_cfg); 94e9f874b6SRalf Baechle break; 95e9f874b6SRalf Baechle 96e9f874b6SRalf Baechle case CLOCK_EVT_MODE_ONESHOT: 97e9f874b6SRalf Baechle /* Stop the timer until we actually program a shot */ 98e9f874b6SRalf Baechle case CLOCK_EVT_MODE_SHUTDOWN: 99e9f874b6SRalf Baechle __raw_writeq(0, timer_cfg); 100e9f874b6SRalf Baechle break; 101e9f874b6SRalf Baechle 102e9f874b6SRalf Baechle case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */ 103d527eef5SRalf Baechle case CLOCK_EVT_MODE_RESUME: 104e9f874b6SRalf Baechle ; 105e9f874b6SRalf Baechle } 106e9f874b6SRalf Baechle } 107e9f874b6SRalf Baechle 108d527eef5SRalf Baechle static int sibyte_next_event(unsigned long delta, struct clock_event_device *cd) 109d527eef5SRalf Baechle { 110d527eef5SRalf Baechle unsigned int cpu = smp_processor_id(); 111d527eef5SRalf Baechle void __iomem *timer_init; 112d527eef5SRalf Baechle unsigned int cnt; 113d527eef5SRalf Baechle int res; 114d527eef5SRalf Baechle 115d527eef5SRalf Baechle timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); 116d527eef5SRalf Baechle cnt = __raw_readq(timer_init); 117d527eef5SRalf Baechle cnt += delta; 118d527eef5SRalf Baechle __raw_writeq(cnt, timer_init); 119d527eef5SRalf Baechle res = ((long)(__raw_readq(timer_init) - cnt ) > 0) ? -ETIME : 0; 120d527eef5SRalf Baechle 121d527eef5SRalf Baechle return res; 122d527eef5SRalf Baechle } 123d527eef5SRalf Baechle 124d527eef5SRalf Baechle static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent); 125e9f874b6SRalf Baechle 126e9f874b6SRalf Baechle static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) 127e9f874b6SRalf Baechle { 128e9f874b6SRalf Baechle unsigned int cpu = smp_processor_id(); 129d527eef5SRalf Baechle struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu); 130e9f874b6SRalf Baechle 131e9f874b6SRalf Baechle /* Reset the timer */ 132e9f874b6SRalf Baechle __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, 133e9f874b6SRalf Baechle IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); 134e9f874b6SRalf Baechle cd->event_handler(cd); 135e9f874b6SRalf Baechle 136e9f874b6SRalf Baechle return IRQ_HANDLED; 137e9f874b6SRalf Baechle } 138e9f874b6SRalf Baechle 139e9f874b6SRalf Baechle static struct irqaction sibyte_counter_irqaction = { 140e9f874b6SRalf Baechle .handler = sibyte_counter_handler, 141e9f874b6SRalf Baechle .flags = IRQF_DISABLED | IRQF_PERCPU, 142e9f874b6SRalf Baechle .name = "timer", 143e9f874b6SRalf Baechle }; 144e9f874b6SRalf Baechle 145f137e463SAndrew Isaacson /* 146f137e463SAndrew Isaacson * This interrupt is "special" in that it doesn't use the request_irq 147f137e463SAndrew Isaacson * way to hook the irq line. The timer interrupt is initialized early 148f137e463SAndrew Isaacson * enough to make this a major pain, and it's also firing enough to 149f137e463SAndrew Isaacson * warrant a bit of special case code. bcm1480_timer_interrupt is 150f137e463SAndrew Isaacson * called directly from irq_handler.S when IP[4] is set during an 151f137e463SAndrew Isaacson * interrupt 152f137e463SAndrew Isaacson */ 153d527eef5SRalf Baechle void __cpuinit sb1480_clockevent_init(void) 154e9f874b6SRalf Baechle { 155e9f874b6SRalf Baechle unsigned int cpu = smp_processor_id(); 156e9f874b6SRalf Baechle unsigned int irq = K_BCM1480_INT_TIMER_0 + cpu; 157d527eef5SRalf Baechle struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu); 158d527eef5SRalf Baechle 159d527eef5SRalf Baechle cd->name = "bcm1480-counter"; 160d527eef5SRalf Baechle cd->features = CLOCK_EVT_FEAT_PERIODIC | 161d527eef5SRalf Baechle CLOCK_EVT_MODE_ONESHOT; 162d527eef5SRalf Baechle cd->set_next_event = sibyte_next_event; 163d527eef5SRalf Baechle cd->set_mode = sibyte_set_mode; 164d527eef5SRalf Baechle cd->irq = irq; 165d527eef5SRalf Baechle clockevent_set_clock(cd, BCM1480_HPT_VALUE); 166e9f874b6SRalf Baechle 167e9f874b6SRalf Baechle setup_irq(irq, &sibyte_counter_irqaction); 168f137e463SAndrew Isaacson } 169f137e463SAndrew Isaacson 17000598560SAtsushi Nemoto static cycle_t bcm1480_hpt_read(void) 171f137e463SAndrew Isaacson { 17216b7b2acSAtsushi Nemoto /* We assume this function is called xtime_lock held. */ 173f137e463SAndrew Isaacson unsigned long count = 174f137e463SAndrew Isaacson __raw_readq(IOADDR(A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT))); 17516b7b2acSAtsushi Nemoto return (jiffies + 1) * (BCM1480_HPT_VALUE / HZ) - count; 17616b7b2acSAtsushi Nemoto } 177f137e463SAndrew Isaacson 178d527eef5SRalf Baechle struct clocksource bcm1480_clocksource = { 179d527eef5SRalf Baechle .name = "MIPS", 180d527eef5SRalf Baechle .rating = 200, 181d527eef5SRalf Baechle .read = bcm1480_hpt_read, 182d527eef5SRalf Baechle .mask = CLOCKSOURCE_MASK(32), 183d527eef5SRalf Baechle .shift = 32, 184d527eef5SRalf Baechle .flags = CLOCK_SOURCE_IS_CONTINUOUS, 185d527eef5SRalf Baechle }; 186d527eef5SRalf Baechle 187d527eef5SRalf Baechle void __init sb1480_clocksource_init(void) 188d527eef5SRalf Baechle { 189d527eef5SRalf Baechle struct clocksource *cs = &bcm1480_clocksource; 190d527eef5SRalf Baechle 191d527eef5SRalf Baechle clocksource_set_clock(cs, BCM1480_HPT_VALUE); 192d527eef5SRalf Baechle clocksource_register(cs); 193d527eef5SRalf Baechle } 194d527eef5SRalf Baechle 19516b7b2acSAtsushi Nemoto void __init bcm1480_hpt_setup(void) 19616b7b2acSAtsushi Nemoto { 19716b7b2acSAtsushi Nemoto mips_hpt_frequency = BCM1480_HPT_VALUE; 198d527eef5SRalf Baechle sb1480_clocksource_init(); 199e9f874b6SRalf Baechle sb1480_clockevent_init(); 200f137e463SAndrew Isaacson } 201