diff options
Diffstat (limited to 'arch/mips/loongson/loongson-3')
-rw-r--r-- | arch/mips/loongson/loongson-3/Makefile | 4 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/hpet.c | 257 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/irq.c | 16 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/numa.c | 12 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/platform.c | 43 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/smp.c | 70 |
6 files changed, 366 insertions, 36 deletions
diff --git a/arch/mips/loongson/loongson-3/Makefile b/arch/mips/loongson/loongson-3/Makefile index b4df775b9f3..622fead5ebc 100644 --- a/arch/mips/loongson/loongson-3/Makefile +++ b/arch/mips/loongson/loongson-3/Makefile @@ -1,8 +1,10 @@ # # Makefile for Loongson-3 family machines # -obj-y += irq.o cop2-ex.o +obj-y += irq.o cop2-ex.o platform.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o + +obj-$(CONFIG_RS780_HPET) += hpet.o diff --git a/arch/mips/loongson/loongson-3/hpet.c b/arch/mips/loongson/loongson-3/hpet.c new file mode 100644 index 00000000000..e898d68668a --- /dev/null +++ b/arch/mips/loongson/loongson-3/hpet.c @@ -0,0 +1,257 @@ +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/percpu.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> + +#include <asm/hpet.h> +#include <asm/time.h> + +#define SMBUS_CFG_BASE (loongson_sysconf.ht_control_base + 0x0300a000) +#define SMBUS_PCI_REG40 0x40 +#define SMBUS_PCI_REG64 0x64 +#define SMBUS_PCI_REGB4 0xb4 + +static DEFINE_SPINLOCK(hpet_lock); +DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device); + +static unsigned int smbus_read(int offset) +{ + return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset); +} + +static void smbus_write(int offset, int data) +{ + *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data; +} + +static void smbus_enable(int offset, int bit) +{ + unsigned int cfg = smbus_read(offset); + + cfg |= bit; + smbus_write(offset, cfg); +} + +static int hpet_read(int offset) +{ + return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset); +} + +static void hpet_write(int offset, int data) +{ + *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data; +} + +static void hpet_start_counter(void) +{ + unsigned int cfg = hpet_read(HPET_CFG); + + cfg |= HPET_CFG_ENABLE; + hpet_write(HPET_CFG, cfg); +} + +static void hpet_stop_counter(void) +{ + unsigned int cfg = hpet_read(HPET_CFG); + + cfg &= ~HPET_CFG_ENABLE; + hpet_write(HPET_CFG, cfg); +} + +static void hpet_reset_counter(void) +{ + hpet_write(HPET_COUNTER, 0); + hpet_write(HPET_COUNTER + 4, 0); +} + +static void hpet_restart_counter(void) +{ + hpet_stop_counter(); + hpet_reset_counter(); + hpet_start_counter(); +} + +static void hpet_enable_legacy_int(void) +{ + /* Do nothing on Loongson-3 */ +} + +static void hpet_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + int cfg = 0; + + spin_lock(&hpet_lock); + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + pr_info("set clock event to periodic mode!\n"); + /* stop counter */ + hpet_stop_counter(); + + /* enables the timer0 to generate a periodic interrupt */ + cfg = hpet_read(HPET_T0_CFG); + cfg &= ~HPET_TN_LEVEL; + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | + HPET_TN_SETVAL | HPET_TN_32BIT; + hpet_write(HPET_T0_CFG, cfg); + + /* set the comparator */ + hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL); + udelay(1); + hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL); + + /* start counter */ + hpet_start_counter(); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + cfg = hpet_read(HPET_T0_CFG); + cfg &= ~HPET_TN_ENABLE; + hpet_write(HPET_T0_CFG, cfg); + break; + case CLOCK_EVT_MODE_ONESHOT: + pr_info("set clock event to one shot mode!\n"); + cfg = hpet_read(HPET_T0_CFG); + /* set timer0 type + * 1 : periodic interrupt + * 0 : non-periodic(oneshot) interrupt + */ + cfg &= ~HPET_TN_PERIODIC; + cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; + hpet_write(HPET_T0_CFG, cfg); + break; + case CLOCK_EVT_MODE_RESUME: + hpet_enable_legacy_int(); + break; + } + spin_unlock(&hpet_lock); +} + +static int hpet_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned int cnt; + int res; + + cnt = hpet_read(HPET_COUNTER); + cnt += delta; + hpet_write(HPET_T0_CMP, cnt); + + res = ((int)(hpet_read(HPET_COUNTER) - cnt) > 0) ? -ETIME : 0; + return res; +} + +static irqreturn_t hpet_irq_handler(int irq, void *data) +{ + int is_irq; + struct clock_event_device *cd; + unsigned int cpu = smp_processor_id(); + + is_irq = hpet_read(HPET_STATUS); + if (is_irq & HPET_T0_IRS) { + /* clear the TIMER0 irq status register */ + hpet_write(HPET_STATUS, HPET_T0_IRS); + cd = &per_cpu(hpet_clockevent_device, cpu); + cd->event_handler(cd); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static struct irqaction hpet_irq = { + .handler = hpet_irq_handler, + .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER, + .name = "hpet", +}; + +/* + * hpet address assignation and irq setting should be done in bios. + * but pmon don't do this, we just setup here directly. + * The operation under is normal. unfortunately, hpet_setup process + * is before pci initialize. + * + * { + * struct pci_dev *pdev; + * + * pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + * pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR); + * + * ... + * } + */ +static void hpet_setup(void) +{ + /* set hpet base address */ + smbus_write(SMBUS_PCI_REGB4, HPET_ADDR); + + /* enable decodeing of access to HPET MMIO*/ + smbus_enable(SMBUS_PCI_REG40, (1 << 28)); + + /* HPET irq enable */ + smbus_enable(SMBUS_PCI_REG64, (1 << 10)); + + hpet_enable_legacy_int(); +} + +void __init setup_hpet_timer(void) +{ + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + + hpet_setup(); + + cd = &per_cpu(hpet_clockevent_device, cpu); + cd->name = "hpet"; + cd->rating = 320; + cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + cd->set_mode = hpet_set_mode; + cd->set_next_event = hpet_next_event; + cd->irq = HPET_T0_IRQ; + cd->cpumask = cpumask_of(cpu); + clockevent_set_clock(cd, HPET_FREQ); + cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); + cd->min_delta_ns = 5000; + + clockevents_register_device(cd); + setup_irq(HPET_T0_IRQ, &hpet_irq); + pr_info("hpet clock event device register\n"); +} + +static cycle_t hpet_read_counter(struct clocksource *cs) +{ + return (cycle_t)hpet_read(HPET_COUNTER); +} + +static void hpet_suspend(struct clocksource *cs) +{ +} + +static void hpet_resume(struct clocksource *cs) +{ + hpet_setup(); + hpet_restart_counter(); +} + +static struct clocksource csrc_hpet = { + .name = "hpet", + /* mips clocksource rating is less than 300, so hpet is better. */ + .rating = 300, + .read = hpet_read_counter, + .mask = CLOCKSOURCE_MASK(32), + /* oneshot mode work normal with this flag */ + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .suspend = hpet_suspend, + .resume = hpet_resume, + .mult = 0, + .shift = 10, +}; + +int __init init_hpet_clocksource(void) +{ + csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift); + return clocksource_register_hz(&csrc_hpet, HPET_FREQ); +} + +arch_initcall(init_hpet_clocksource); diff --git a/arch/mips/loongson/loongson-3/irq.c b/arch/mips/loongson/loongson-3/irq.c index ca1c62af518..21221edda7a 100644 --- a/arch/mips/loongson/loongson-3/irq.c +++ b/arch/mips/loongson/loongson-3/irq.c @@ -9,7 +9,7 @@ #include "smp.h" -unsigned int ht_irq[] = {1, 3, 4, 5, 6, 7, 8, 12, 14, 15}; +unsigned int ht_irq[] = {0, 1, 3, 4, 5, 6, 7, 8, 12, 14, 15}; static void ht_irqdispatch(void) { @@ -55,8 +55,8 @@ static inline void mask_loongson_irq(struct irq_data *d) /* Workaround: UART IRQ may deliver to any core */ if (d->irq == LOONGSON_UART_IRQ) { int cpu = smp_processor_id(); - int node_id = cpu / loongson_sysconf.cores_per_node; - int core_id = cpu % loongson_sysconf.cores_per_node; + int node_id = cpu_logical_map(cpu) / loongson_sysconf.cores_per_node; + int core_id = cpu_logical_map(cpu) % loongson_sysconf.cores_per_node; u64 intenclr_addr = smp_group[node_id] | (u64)(&LOONGSON_INT_ROUTER_INTENCLR); u64 introuter_lpc_addr = smp_group[node_id] | @@ -72,8 +72,8 @@ static inline void unmask_loongson_irq(struct irq_data *d) /* Workaround: UART IRQ may deliver to any core */ if (d->irq == LOONGSON_UART_IRQ) { int cpu = smp_processor_id(); - int node_id = cpu / loongson_sysconf.cores_per_node; - int core_id = cpu % loongson_sysconf.cores_per_node; + int node_id = cpu_logical_map(cpu) / loongson_sysconf.cores_per_node; + int core_id = cpu_logical_map(cpu) % loongson_sysconf.cores_per_node; u64 intenset_addr = smp_group[node_id] | (u64)(&LOONGSON_INT_ROUTER_INTENSET); u64 introuter_lpc_addr = smp_group[node_id] | @@ -102,10 +102,12 @@ void irq_router_init(void) int i; /* route LPC int to cpu core0 int 0 */ - LOONGSON_INT_ROUTER_LPC = LOONGSON_INT_CORE0_INT0; + LOONGSON_INT_ROUTER_LPC = + LOONGSON_INT_COREx_INTy(loongson_sysconf.boot_cpu_id, 0); /* route HT1 int0 ~ int7 to cpu core0 INT1*/ for (i = 0; i < 8; i++) - LOONGSON_INT_ROUTER_HT1(i) = LOONGSON_INT_CORE0_INT1; + LOONGSON_INT_ROUTER_HT1(i) = + LOONGSON_INT_COREx_INTy(loongson_sysconf.boot_cpu_id, 1); /* enable HT1 interrupt */ LOONGSON_HT1_INTN_EN(0) = 0xffffffff; /* enable router interrupt intenset */ diff --git a/arch/mips/loongson/loongson-3/numa.c b/arch/mips/loongson/loongson-3/numa.c index 42323bcc5d2..6cae0e75de2 100644 --- a/arch/mips/loongson/loongson-3/numa.c +++ b/arch/mips/loongson/loongson-3/numa.c @@ -224,7 +224,7 @@ static void __init node_mem_init(unsigned int node) static __init void prom_meminit(void) { - unsigned int node, cpu; + unsigned int node, cpu, active_cpu = 0; cpu_node_probe(); init_topology_matrix(); @@ -240,8 +240,14 @@ static __init void prom_meminit(void) node = cpu / loongson_sysconf.cores_per_node; if (node >= num_online_nodes()) node = 0; - pr_info("NUMA: set cpumask cpu %d on node %d\n", cpu, node); - cpu_set(cpu, __node_data[(node)]->cpumask); + + if (loongson_sysconf.reserved_cpus_mask & (1<<cpu)) + continue; + + cpu_set(active_cpu, __node_data[(node)]->cpumask); + pr_info("NUMA: set cpumask cpu %d on node %d\n", active_cpu, node); + + active_cpu++; } } diff --git a/arch/mips/loongson/loongson-3/platform.c b/arch/mips/loongson/loongson-3/platform.c new file mode 100644 index 00000000000..25a97cc0ee3 --- /dev/null +++ b/arch/mips/loongson/loongson-3/platform.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin, wuzhangjin@gmail.com + * Xiang Yu, xiangy@lemote.com + * Chen Huacai, chenhc@lemote.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <asm/bootinfo.h> +#include <boot_param.h> +#include <loongson_hwmon.h> +#include <workarounds.h> + +static int __init loongson3_platform_init(void) +{ + int i; + struct platform_device *pdev; + + if (loongson_sysconf.ecname[0] != '\0') + platform_device_register_simple(loongson_sysconf.ecname, -1, NULL, 0); + + for (i = 0; i < loongson_sysconf.nr_sensors; i++) { + if (loongson_sysconf.sensors[i].type > SENSOR_FAN) + continue; + + pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + pdev->name = loongson_sysconf.sensors[i].name; + pdev->id = loongson_sysconf.sensors[i].id; + pdev->dev.platform_data = &loongson_sysconf.sensors[i]; + platform_device_register(pdev); + } + + return 0; +} + +arch_initcall(loongson3_platform_init); diff --git a/arch/mips/loongson/loongson-3/smp.c b/arch/mips/loongson/loongson-3/smp.c index d8c63af6c7c..e2eb688b543 100644 --- a/arch/mips/loongson/loongson-3/smp.c +++ b/arch/mips/loongson/loongson-3/smp.c @@ -25,6 +25,7 @@ #include <asm/tlbflush.h> #include <asm/cacheflush.h> #include <loongson.h> +#include <workarounds.h> #include "smp.h" @@ -239,7 +240,7 @@ static void ipi_mailbox_buf_init(void) */ static void loongson3_send_ipi_single(int cpu, unsigned int action) { - loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]); + loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu_logical_map(cpu)]); } static void @@ -248,7 +249,7 @@ loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) unsigned int i; for_each_cpu(i, mask) - loongson3_ipi_write32((u32)action, ipi_set0_regs[i]); + loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu_logical_map(i)]); } void loongson3_ipi_interrupt(struct pt_regs *regs) @@ -257,10 +258,10 @@ void loongson3_ipi_interrupt(struct pt_regs *regs) unsigned int action, c0count; /* Load the ipi register to figure out what we're supposed to do */ - action = loongson3_ipi_read32(ipi_status0_regs[cpu]); + action = loongson3_ipi_read32(ipi_status0_regs[cpu_logical_map(cpu)]); /* Clear the ipi register to clear the interrupt */ - loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu]); + loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu_logical_map(cpu)]); if (action & SMP_RESCHEDULE_YOURSELF) scheduler_ipi(); @@ -291,12 +292,14 @@ static void loongson3_init_secondary(void) /* Set interrupt mask, but don't enable */ change_c0_status(ST0_IM, imask); - for (i = 0; i < loongson_sysconf.nr_cpus; i++) - loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]); + for (i = 0; i < num_possible_cpus(); i++) + loongson3_ipi_write32(0xffffffff, ipi_en0_regs[cpu_logical_map(i)]); - cpu_data[cpu].package = cpu / loongson_sysconf.cores_per_package; - cpu_data[cpu].core = cpu % loongson_sysconf.cores_per_package; per_cpu(cpu_state, cpu) = CPU_ONLINE; + cpu_data[cpu].core = + cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; + cpu_data[cpu].package = + cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; i = 0; __this_cpu_write(core0_c0count, 0); @@ -314,37 +317,50 @@ static void loongson3_init_secondary(void) static void loongson3_smp_finish(void) { + int cpu = smp_processor_id(); + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); local_irq_enable(); loongson3_ipi_write64(0, - (void *)(ipi_mailbox_buf[smp_processor_id()]+0x0)); + (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0)); pr_info("CPU#%d finished, CP0_ST=%x\n", smp_processor_id(), read_c0_status()); } static void __init loongson3_smp_setup(void) { - int i, num; + int i = 0, num = 0; /* i: physical id, num: logical id */ init_cpu_possible(cpu_none_mask); - set_cpu_possible(0, true); - - __cpu_number_map[0] = 0; - __cpu_logical_map[0] = 0; /* For unified kernel, NR_CPUS is the maximum possible value, * loongson_sysconf.nr_cpus is the really present value */ - for (i = 1, num = 0; i < loongson_sysconf.nr_cpus; i++) { - set_cpu_possible(i, true); - __cpu_number_map[i] = ++num; - __cpu_logical_map[num] = i; + while (i < loongson_sysconf.nr_cpus) { + if (loongson_sysconf.reserved_cpus_mask & (1<<i)) { + /* Reserved physical CPU cores */ + __cpu_number_map[i] = -1; + } else { + __cpu_number_map[i] = num; + __cpu_logical_map[num] = i; + set_cpu_possible(num, true); + num++; + } + i++; } + pr_info("Detected %i available CPU(s)\n", num); + + while (num < loongson_sysconf.nr_cpus) { + __cpu_logical_map[num] = -1; + num++; + } + ipi_set0_regs_init(); ipi_clear0_regs_init(); ipi_status0_regs_init(); ipi_en0_regs_init(); ipi_mailbox_buf_init(); - pr_info("Detected %i available secondary CPU(s)\n", num); + cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package; + cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package; } static void __init loongson3_prepare_cpus(unsigned int max_cpus) @@ -371,10 +387,14 @@ static void loongson3_boot_secondary(int cpu, struct task_struct *idle) pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n", cpu, startargs[0], startargs[1], startargs[2]); - loongson3_ipi_write64(startargs[3], (void *)(ipi_mailbox_buf[cpu]+0x18)); - loongson3_ipi_write64(startargs[2], (void *)(ipi_mailbox_buf[cpu]+0x10)); - loongson3_ipi_write64(startargs[1], (void *)(ipi_mailbox_buf[cpu]+0x8)); - loongson3_ipi_write64(startargs[0], (void *)(ipi_mailbox_buf[cpu]+0x0)); + loongson3_ipi_write64(startargs[3], + (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x18)); + loongson3_ipi_write64(startargs[2], + (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x10)); + loongson3_ipi_write64(startargs[1], + (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x8)); + loongson3_ipi_write64(startargs[0], + (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0)); } #ifdef CONFIG_HOTPLUG_CPU @@ -568,7 +588,7 @@ void loongson3_disable_clock(int cpu) if (loongson_sysconf.cputype == Loongson_3A) { LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id)); } else if (loongson_sysconf.cputype == Loongson_3B) { - if (!cpuhotplug_workaround) + if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG)) LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3)); } } @@ -581,7 +601,7 @@ void loongson3_enable_clock(int cpu) if (loongson_sysconf.cputype == Loongson_3A) { LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id); } else if (loongson_sysconf.cputype == Loongson_3B) { - if (!cpuhotplug_workaround) + if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG)) LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3); } } |