diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2007-10-22 10:38:44 +0100 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-10-22 22:09:00 +0100 |
commit | d04533650f64fe3367e180f3e488d92205152cd3 (patch) | |
tree | 5f183668d97d9655a8517e61afd46bfa2f80b101 /arch/mips/sibyte/bcm1480 | |
parent | 06d428d719dece96c01532b62df4140f4e69a308 (diff) |
[MIPS] time: SMP-proofing of Sibyte clockevent/clocksource code.
The BCM148 has 4 cores but there are also just 4 generic timers available
so use the ZBbus cycle counter instead of it. In addition the ZBbus
counter also offers a much higher resolution and 64-bit counting so I'm
considering a later complete conversion to it once I figure out if all
members of the Sibyte SOC family support it - the docs seem to agree but
the headers files seem to disagree ...
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/sibyte/bcm1480')
-rw-r--r-- | arch/mips/sibyte/bcm1480/irq.c | 75 | ||||
-rw-r--r-- | arch/mips/sibyte/bcm1480/smp.c | 4 | ||||
-rw-r--r-- | arch/mips/sibyte/bcm1480/time.c | 117 |
3 files changed, 93 insertions, 103 deletions
diff --git a/arch/mips/sibyte/bcm1480/irq.c b/arch/mips/sibyte/bcm1480/irq.c index 7aa79bf63c4a..10299bafeab7 100644 --- a/arch/mips/sibyte/bcm1480/irq.c +++ b/arch/mips/sibyte/bcm1480/irq.c @@ -452,6 +452,43 @@ static void bcm1480_kgdb_interrupt(void) extern void bcm1480_mailbox_interrupt(void); +static inline void dispatch_ip4(void) +{ + int cpu = smp_processor_id(); + int irq = K_BCM1480_INT_TIMER_0 + cpu; + + /* Reset the timer */ + __raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS, + IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); + + do_IRQ(irq); +} + +static inline void dispatch_ip2(void) +{ + unsigned long long mask_h, mask_l; + unsigned int cpu = smp_processor_id(); + unsigned long base; + + /* + * Default...we've hit an IP[2] interrupt, which means we've got to + * check the 1480 interrupt registers to figure out what to do. Need + * to detect which CPU we're on, now that smp_affinity is supported. + */ + base = A_BCM1480_IMR_MAPPER(cpu); + mask_h = __raw_readq( + IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H)); + mask_l = __raw_readq( + IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L)); + + if (mask_h) { + if (mask_h ^ 1) + do_IRQ(fls64(mask_h) - 1); + else if (mask_l) + do_IRQ(63 + fls64(mask_l)); + } +} + asmlinkage void plat_irq_dispatch(void) { unsigned int pending; @@ -469,17 +506,8 @@ asmlinkage void plat_irq_dispatch(void) else #endif - if (pending & CAUSEF_IP4) { - int cpu = smp_processor_id(); - int irq = K_BCM1480_INT_TIMER_0 + cpu; - - /* Reset the timer */ - __raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS, - IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); - - do_IRQ(irq); - } - + if (pending & CAUSEF_IP4) + dispatch_ip4(); #ifdef CONFIG_SMP else if (pending & CAUSEF_IP3) bcm1480_mailbox_interrupt(); @@ -490,27 +518,6 @@ asmlinkage void plat_irq_dispatch(void) bcm1480_kgdb_interrupt(); /* KGDB (uart 1) */ #endif - else if (pending & CAUSEF_IP2) { - unsigned long long mask_h, mask_l; - unsigned long base; - - /* - * Default...we've hit an IP[2] interrupt, which means we've - * got to check the 1480 interrupt registers to figure out what - * to do. Need to detect which CPU we're on, now that - * smp_affinity is supported. - */ - base = A_BCM1480_IMR_MAPPER(smp_processor_id()); - mask_h = __raw_readq( - IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H)); - mask_l = __raw_readq( - IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L)); - - if (mask_h) { - if (mask_h ^ 1) - do_IRQ(fls64(mask_h) - 1); - else - do_IRQ(63 + fls64(mask_l)); - } - } + else if (pending & CAUSEF_IP2) + dispatch_ip2(); } diff --git a/arch/mips/sibyte/bcm1480/smp.c b/arch/mips/sibyte/bcm1480/smp.c index 02b266a31c46..436ba78359ab 100644 --- a/arch/mips/sibyte/bcm1480/smp.c +++ b/arch/mips/sibyte/bcm1480/smp.c @@ -58,7 +58,7 @@ static void *mailbox_0_regs[] = { /* * SMP init and finish on secondary CPUs */ -void bcm1480_smp_init(void) +void __cpuinit bcm1480_smp_init(void) { unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | STATUSF_IP1 | STATUSF_IP0; @@ -67,7 +67,7 @@ void bcm1480_smp_init(void) change_c0_status(ST0_IM, imask); } -void bcm1480_smp_finish(void) +void __cpuinit bcm1480_smp_finish(void) { extern void sb1480_clockevent_init(void); diff --git a/arch/mips/sibyte/bcm1480/time.c b/arch/mips/sibyte/bcm1480/time.c index c730744aa474..610f0253954d 100644 --- a/arch/mips/sibyte/bcm1480/time.c +++ b/arch/mips/sibyte/bcm1480/time.c @@ -15,22 +15,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -/* - * These are routines to set up and handle interrupts from the - * bcm1480 general purpose timer 0. We're using the timer as a - * system clock, so we set it up to run at 100 Hz. On every - * interrupt, we update our idea of what the time of day is, - * then call do_timer() in the architecture-independent kernel - * code to do general bookkeeping (e.g. update jiffies, run - * bottom halves, etc.) - */ #include <linux/clockchips.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/percpu.h> #include <linux/spinlock.h> -#include <asm/irq.h> #include <asm/addrspace.h> #include <asm/time.h> #include <asm/io.h> @@ -47,33 +37,10 @@ #define IMR_IP3_VAL K_BCM1480_INT_MAP_I1 #define IMR_IP4_VAL K_BCM1480_INT_MAP_I2 -#ifdef CONFIG_SIMULATION -#define BCM1480_HPT_VALUE 50000 -#else -#define BCM1480_HPT_VALUE 1000000 -#endif - extern int bcm1480_steal_irq(int irq); -void __init plat_time_init(void) -{ - unsigned int cpu = smp_processor_id(); - unsigned int irq = K_BCM1480_INT_TIMER_0 + cpu; - - BUG_ON(cpu > 3); /* Only have 4 general purpose timers */ - - bcm1480_mask_irq(cpu, irq); - - /* Map the timer interrupt to ip[4] of this cpu */ - __raw_writeq(IMR_IP4_VAL, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) - + (irq<<3))); - - bcm1480_unmask_irq(cpu, irq); - bcm1480_steal_irq(irq); -} - /* - * The general purpose timer ticks at 1 Mhz independent if + * The general purpose timer ticks at 1MHz independent if * the rest of the system */ static void sibyte_set_mode(enum clock_event_mode mode, @@ -88,7 +55,7 @@ static void sibyte_set_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: __raw_writeq(0, timer_cfg); - __raw_writeq(BCM1480_HPT_VALUE / HZ - 1, timer_init); + __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, timer_init); __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, timer_cfg); break; @@ -121,80 +88,96 @@ static int sibyte_next_event(unsigned long delta, struct clock_event_device *cd) return res; } -static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent); - static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) { unsigned int cpu = smp_processor_id(); - struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu); + struct clock_event_device *cd = dev_id; + void __iomem *timer_cfg; + + timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); /* Reset the timer */ __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, - IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); + timer_cfg); cd->event_handler(cd); return IRQ_HANDLED; } -static struct irqaction sibyte_counter_irqaction = { - .handler = sibyte_counter_handler, - .flags = IRQF_DISABLED | IRQF_PERCPU, - .name = "timer", -}; +static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent); +static DEFINE_PER_CPU(struct irqaction, sibyte_hpt_irqaction); +static DEFINE_PER_CPU(char [18], sibyte_hpt_name); -/* - * This interrupt is "special" in that it doesn't use the request_irq - * way to hook the irq line. The timer interrupt is initialized early - * enough to make this a major pain, and it's also firing enough to - * warrant a bit of special case code. bcm1480_timer_interrupt is - * called directly from irq_handler.S when IP[4] is set during an - * interrupt - */ void __cpuinit sb1480_clockevent_init(void) { unsigned int cpu = smp_processor_id(); unsigned int irq = K_BCM1480_INT_TIMER_0 + cpu; + struct irqaction *action = &per_cpu(sibyte_hpt_irqaction, cpu); struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu); + unsigned char *name = per_cpu(sibyte_hpt_name, cpu); + + BUG_ON(cpu > 3); /* Only have 4 general purpose timers */ - cd->name = "bcm1480-counter"; + sprintf(name, "bcm1480-counter %d", cpu); + cd->name = name; cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_MODE_ONESHOT; + clockevent_set_clock(cd, V_SCD_TIMER_FREQ); + cd->max_delta_ns = clockevent_delta2ns(0x7fffff, cd); + cd->min_delta_ns = clockevent_delta2ns(1, cd); + cd->rating = 200; + cd->irq = irq; + cd->cpumask = cpumask_of_cpu(cpu); cd->set_next_event = sibyte_next_event; cd->set_mode = sibyte_set_mode; - cd->irq = irq; - clockevent_set_clock(cd, BCM1480_HPT_VALUE); + clockevents_register_device(cd); + + bcm1480_mask_irq(cpu, irq); + + /* + * Map timer interrupt to IP[4] of this cpu + */ + __raw_writeq(IMR_IP4_VAL, + IOADDR(A_BCM1480_IMR_REGISTER(cpu, + R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) + (irq << 3))); - setup_irq(irq, &sibyte_counter_irqaction); + bcm1480_unmask_irq(cpu, irq); + bcm1480_steal_irq(irq); + + action->handler = sibyte_counter_handler; + action->flags = IRQF_DISABLED | IRQF_PERCPU; + action->name = name; + action->dev_id = cd; + setup_irq(irq, action); } static cycle_t bcm1480_hpt_read(void) { - /* We assume this function is called xtime_lock held. */ - unsigned long count = - __raw_readq(IOADDR(A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT))); - return (jiffies + 1) * (BCM1480_HPT_VALUE / HZ) - count; + return (cycle_t) __raw_readq(IOADDR(A_SCD_ZBBUS_CYCLE_COUNT)); } struct clocksource bcm1480_clocksource = { - .name = "MIPS", + .name = "zbbus-cycles", .rating = 200, .read = bcm1480_hpt_read, - .mask = CLOCKSOURCE_MASK(32), - .shift = 32, + .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; void __init sb1480_clocksource_init(void) { struct clocksource *cs = &bcm1480_clocksource; + unsigned int plldiv; + unsigned long zbbus; - clocksource_set_clock(cs, BCM1480_HPT_VALUE); + plldiv = G_BCM1480_SYS_PLL_DIV(__raw_readq(IOADDR(A_SCD_SYSTEM_CFG))); + zbbus = ((plldiv >> 1) * 50000000) + ((plldiv & 1) * 25000000); + clocksource_set_clock(cs, zbbus); clocksource_register(cs); } -void __init bcm1480_hpt_setup(void) +void __init plat_time_init(void) { - mips_hpt_frequency = BCM1480_HPT_VALUE; sb1480_clocksource_init(); sb1480_clockevent_init(); } |