From d04533650f64fe3367e180f3e488d92205152cd3 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 22 Oct 2007 10:38:44 +0100 Subject: [PATCH] [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 --- arch/mips/sibyte/bcm1480/irq.c | 75 +++++++++++++------------ arch/mips/sibyte/bcm1480/smp.c | 4 +- arch/mips/sibyte/bcm1480/time.c | 117 +++++++++++++++++---------------------- arch/mips/sibyte/sb1250/irq.c | 35 ++++++------ arch/mips/sibyte/sb1250/smp.c | 4 +- arch/mips/sibyte/sb1250/time.c | 88 ++++++++++++++--------------- include/asm-mips/sibyte/sb1250.h | 2 - 7 files changed, 158 insertions(+), 167 deletions(-) diff --git a/arch/mips/sibyte/bcm1480/irq.c b/arch/mips/sibyte/bcm1480/irq.c index 7aa79bf..10299ba 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 02b266a..436ba78 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 c730744..610f025 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 #include +#include #include #include -#include #include #include #include @@ -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(); } diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c index 500d17e..53780a1 100644 --- a/arch/mips/sibyte/sb1250/irq.c +++ b/arch/mips/sibyte/sb1250/irq.c @@ -402,6 +402,22 @@ static void sb1250_kgdb_interrupt(void) extern void sb1250_mailbox_interrupt(void); +static inline void dispatch_ip2(void) +{ + unsigned int cpu = smp_processor_id(); + unsigned long long mask; + + /* + * Default...we've hit an IP[2] interrupt, which means we've got to + * check the 1250 interrupt registers to figure out what to do. Need + * to detect which CPU we're on, now that smp_affinity is supported. + */ + mask = __raw_readq(IOADDR(A_IMR_REGISTER(cpu, + R_IMR_INTERRUPT_STATUS_BASE))); + if (mask) + do_IRQ(fls64(mask) - 1); +} + asmlinkage void plat_irq_dispatch(void) { unsigned int cpu = smp_processor_id(); @@ -434,21 +450,8 @@ asmlinkage void plat_irq_dispatch(void) sb1250_kgdb_interrupt(); #endif - else if (pending & CAUSEF_IP2) { - unsigned long long mask; - - /* - * Default...we've hit an IP[2] interrupt, which means we've - * got to check the 1250 interrupt registers to figure out what - * to do. Need to detect which CPU we're on, now that - * smp_affinity is supported. - */ - mask = __raw_readq(IOADDR(A_IMR_REGISTER(smp_processor_id(), - R_IMR_INTERRUPT_STATUS_BASE))); - if (mask) - do_IRQ(fls64(mask) - 1); - else - spurious_interrupt(); - } else + else if (pending & CAUSEF_IP2) + dispatch_ip2(); + else spurious_interrupt(); } diff --git a/arch/mips/sibyte/sb1250/smp.c b/arch/mips/sibyte/sb1250/smp.c index aaa4f30..3f52c95 100644 --- a/arch/mips/sibyte/sb1250/smp.c +++ b/arch/mips/sibyte/sb1250/smp.c @@ -46,7 +46,7 @@ static void *mailbox_regs[] = { /* * SMP init and finish on secondary CPUs */ -void sb1250_smp_init(void) +void __cpuinit sb1250_smp_init(void) { unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | STATUSF_IP1 | STATUSF_IP0; @@ -55,7 +55,7 @@ void sb1250_smp_init(void) change_c0_status(ST0_IM, imask); } -void sb1250_smp_finish(void) +void __cpuinit sb1250_smp_finish(void) { extern void sb1250_clockevent_init(void); diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c index 9ef5462..a41e908 100644 --- a/arch/mips/sibyte/sb1250/time.c +++ b/arch/mips/sibyte/sb1250/time.c @@ -52,26 +52,6 @@ extern int sb1250_steal_irq(int irq); -static cycle_t sb1250_hpt_read(void); - -void __init sb1250_hpt_setup(void) -{ - int cpu = smp_processor_id(); - - if (!cpu) { - /* Setup hpt using timer #3 but do not enable irq for it */ - __raw_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG))); - __raw_writeq(SB1250_HPT_VALUE, - IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_INIT))); - __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, - IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG))); - - mips_hpt_frequency = V_SCD_TIMER_FREQ; - clocksource_mips.read = sb1250_hpt_read; - clocksource_mips.mask = M_SCD_TIMER_INIT; - } -} - /* * The general purpose timer ticks at 1 Mhz independent if * the rest of the system @@ -121,18 +101,14 @@ sibyte_next_event(unsigned long delta, struct clock_event_device *evt) return 0; } -struct clock_event_device sibyte_hpt_clockevent = { - .name = "sb1250-counter", - .features = CLOCK_EVT_FEAT_PERIODIC, - .set_mode = sibyte_set_mode, - .set_next_event = sibyte_next_event, - .shift = 32, - .irq = 0, -}; - static irqreturn_t sibyte_counter_handler(int irq, void *dev_id) { - struct clock_event_device *cd = &sibyte_hpt_clockevent; + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd = dev_id; + + /* ACK interrupt */ + ____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, + IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG))); cd->event_handler(cd); @@ -145,15 +121,35 @@ static struct irqaction sibyte_irqaction = { .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); + void __cpuinit sb1250_clockevent_init(void) { - struct clock_event_device *cd = &sibyte_hpt_clockevent; unsigned int cpu = smp_processor_id(); - int irq = K_INT_TIMER_0 + cpu; + unsigned int irq = K_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); /* Only have 4 general purpose timers, and we use last one as hpt */ BUG_ON(cpu > 2); + 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; + clockevents_register_device(cd); + sb1250_mask_irq(cpu, irq); /* Map the timer interrupt to ip[4] of this cpu */ @@ -165,17 +161,11 @@ void __cpuinit sb1250_clockevent_init(void) sb1250_unmask_irq(cpu, irq); sb1250_steal_irq(irq); - /* - * 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. sb1250_timer_interrupt is - * called directly from irq_handler.S when IP[4] is set during an - * interrupt - */ + action->handler = sibyte_counter_handler; + action->flags = IRQF_DISABLED | IRQF_PERCPU; + action->name = name; + action->dev_id = cd; setup_irq(irq, &sibyte_irqaction); - - clockevents_register_device(cd); } /* @@ -195,8 +185,7 @@ struct clocksource bcm1250_clocksource = { .name = "MIPS", .rating = 200, .read = sb1250_hpt_read, - .mask = CLOCKSOURCE_MASK(32), - .shift = 32, + .mask = CLOCKSOURCE_MASK(23), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -204,6 +193,17 @@ void __init sb1250_clocksource_init(void) { struct clocksource *cs = &bcm1250_clocksource; + /* Setup hpt using timer #3 but do not enable irq for it */ + __raw_writeq(0, + IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, + R_SCD_TIMER_CFG))); + __raw_writeq(SB1250_HPT_VALUE, + IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, + R_SCD_TIMER_INIT))); + __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS, + IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, + R_SCD_TIMER_CFG))); + clocksource_set_clock(cs, V_SCD_TIMER_FREQ); clocksource_register(cs); } diff --git a/include/asm-mips/sibyte/sb1250.h b/include/asm-mips/sibyte/sb1250.h index 494aa65..0dad844 100644 --- a/include/asm-mips/sibyte/sb1250.h +++ b/include/asm-mips/sibyte/sb1250.h @@ -45,13 +45,11 @@ extern unsigned int soc_type; extern unsigned int periph_rev; extern unsigned int zbbus_mhz; -extern void sb1250_hpt_setup(void); extern void sb1250_time_init(void); extern void sb1250_mask_irq(int cpu, int irq); extern void sb1250_unmask_irq(int cpu, int irq); extern void sb1250_smp_finish(void); -extern void bcm1480_hpt_setup(void); extern void bcm1480_time_init(void); extern void bcm1480_mask_irq(int cpu, int irq); extern void bcm1480_unmask_irq(int cpu, int irq); -- 2.7.4