diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2013-08-09 13:03:47 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2013-08-09 13:03:47 +1000 |
commit | 70f6471246bce2761c4d541fc954a06f093b8af6 (patch) | |
tree | 5c8564f8eddd02df6d19ce5cfeb04f3b28af4a57 /drivers | |
parent | e91d307be5014490c404bd501a9fd9fc3d061da5 (diff) | |
parent | ef85938077674b176ae9bbe605fcf9dcc62428bc (diff) |
Merge remote-tracking branch 'tip/auto-latest'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/apei/erst.c | 51 | ||||
-rw-r--r-- | drivers/acpi/apei/ghes.c | 38 | ||||
-rw-r--r-- | drivers/acpi/apei/hest.c | 38 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/cadence_ttc_timer.c | 13 | ||||
-rw-r--r-- | drivers/clocksource/moxart_timer.c | 164 | ||||
-rw-r--r-- | drivers/clocksource/sun4i_timer.c | 110 | ||||
-rw-r--r-- | drivers/clocksource/time-orion.c | 2 |
8 files changed, 346 insertions, 71 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 88d0b0f9f92b..40ffcdafbc6f 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -39,7 +39,8 @@ #include "apei-internal.h" -#define ERST_PFX "ERST: " +#undef pr_fmt +#define pr_fmt(fmt) "ERST: " fmt /* ERST command status */ #define ERST_STATUS_SUCCESS 0x0 @@ -109,8 +110,7 @@ static inline int erst_errno(int command_status) static int erst_timedout(u64 *t, u64 spin_unit) { if ((s64)*t < spin_unit) { - pr_warning(FW_WARN ERST_PFX - "Firmware does not respond in time\n"); + pr_warn(FW_WARN "Firmware does not respond in time.\n"); return 1; } *t -= spin_unit; @@ -186,8 +186,8 @@ static int erst_exec_stall(struct apei_exec_context *ctx, if (ctx->value > FIRMWARE_MAX_STALL) { if (!in_nmi()) - pr_warning(FW_WARN ERST_PFX - "Too long stall time for stall instruction: %llx.\n", + pr_warn(FW_WARN + "Too long stall time for stall instruction: 0x%llx.\n", ctx->value); stall_time = FIRMWARE_MAX_STALL; } else @@ -206,8 +206,8 @@ static int erst_exec_stall_while_true(struct apei_exec_context *ctx, if (ctx->var1 > FIRMWARE_MAX_STALL) { if (!in_nmi()) - pr_warning(FW_WARN ERST_PFX - "Too long stall time for stall while true instruction: %llx.\n", + pr_warn(FW_WARN + "Too long stall time for stall while true instruction: 0x%llx.\n", ctx->var1); stall_time = FIRMWARE_MAX_STALL; } else @@ -271,8 +271,7 @@ static int erst_exec_move_data(struct apei_exec_context *ctx, /* ioremap does not work in interrupt context */ if (in_interrupt()) { - pr_warning(ERST_PFX - "MOVE_DATA can not be used in interrupt context"); + pr_warn("MOVE_DATA can not be used in interrupt context.\n"); return -EBUSY; } @@ -522,8 +521,7 @@ retry: ERST_RECORD_ID_CACHE_SIZE_MAX); if (new_size <= erst_record_id_cache.size) { if (printk_ratelimit()) - pr_warning(FW_WARN ERST_PFX - "too many record ID!\n"); + pr_warn(FW_WARN "too many record IDs!\n"); return 0; } alloc_size = new_size * sizeof(entries[0]); @@ -759,8 +757,7 @@ static int __erst_clear_from_storage(u64 record_id) static void pr_unimpl_nvram(void) { if (printk_ratelimit()) - pr_warning(ERST_PFX - "NVRAM ERST Log Address Range is not implemented yet\n"); + pr_warn("NVRAM ERST Log Address Range not implemented yet.\n"); } static int __erst_write_to_nvram(const struct cper_record_header *record) @@ -1120,7 +1117,7 @@ static int __init erst_init(void) goto err; if (erst_disable) { - pr_info(ERST_PFX + pr_info( "Error Record Serialization Table (ERST) support is disabled.\n"); goto err; } @@ -1131,14 +1128,14 @@ static int __init erst_init(void) goto err; else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); - pr_err(ERST_PFX "Failed to get table, %s\n", msg); + pr_err("Failed to get table, %s\n", msg); rc = -EINVAL; goto err; } rc = erst_check_table(erst_tab); if (rc) { - pr_err(FW_BUG ERST_PFX "ERST table is invalid\n"); + pr_err(FW_BUG "ERST table is invalid.\n"); goto err; } @@ -1156,21 +1153,19 @@ static int __init erst_init(void) rc = erst_get_erange(&erst_erange); if (rc) { if (rc == -ENODEV) - pr_info(ERST_PFX + pr_info( "The corresponding hardware device or firmware implementation " "is not available.\n"); else - pr_err(ERST_PFX - "Failed to get Error Log Address Range.\n"); + pr_err("Failed to get Error Log Address Range.\n"); goto err_unmap_reg; } r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); if (!r) { - pr_err(ERST_PFX - "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n", - (unsigned long long)erst_erange.base, - (unsigned long long)erst_erange.base + erst_erange.size); + pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n", + (unsigned long long)erst_erange.base, + (unsigned long long)erst_erange.base + erst_erange.size - 1); rc = -EIO; goto err_unmap_reg; } @@ -1180,7 +1175,7 @@ static int __init erst_init(void) if (!erst_erange.vaddr) goto err_release_erange; - pr_info(ERST_PFX + pr_info( "Error Record Serialization Table (ERST) support is initialized.\n"); buf = kmalloc(erst_erange.size, GFP_KERNEL); @@ -1192,15 +1187,15 @@ static int __init erst_init(void) rc = pstore_register(&erst_info); if (rc) { if (rc != -EPERM) - pr_info(ERST_PFX - "Could not register with persistent store\n"); + pr_info( + "Could not register with persistent store.\n"); erst_info.buf = NULL; erst_info.bufsize = 0; kfree(buf); } } else - pr_err(ERST_PFX - "Failed to allocate %lld bytes for persistent store error log\n", + pr_err( + "Failed to allocate %lld bytes for persistent store error log.\n", erst_erange.size); return 0; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index ec9b57d428a1..8ec37bbdd699 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -409,6 +409,34 @@ static void ghes_clear_estatus(struct ghes *ghes) ghes->flags &= ~GHES_TO_CLEAR; } +static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) +{ +#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE + unsigned long pfn; + int sec_sev = ghes_severity(gdata->error_severity); + struct cper_sec_mem_err *mem_err; + mem_err = (struct cper_sec_mem_err *)(gdata + 1); + + if (sec_sev == GHES_SEV_CORRECTED && + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED) && + (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS)) { + pfn = mem_err->physical_addr >> PAGE_SHIFT; + if (pfn_valid(pfn)) + memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE); + else if (printk_ratelimit()) + pr_warn(FW_WARN GHES_PFX + "Invalid address in generic error data: %#llx\n", + mem_err->physical_addr); + } + if (sev == GHES_SEV_RECOVERABLE && + sec_sev == GHES_SEV_RECOVERABLE && + mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { + pfn = mem_err->physical_addr >> PAGE_SHIFT; + memory_failure_queue(pfn, 0, 0); + } +#endif +} + static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { @@ -428,15 +456,7 @@ static void ghes_do_proc(struct ghes *ghes, apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, mem_err); #endif -#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE - if (sev == GHES_SEV_RECOVERABLE && - sec_sev == GHES_SEV_RECOVERABLE && - mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { - unsigned long pfn; - pfn = mem_err->physical_addr >> PAGE_SHIFT; - memory_failure_queue(pfn, 0, 0); - } -#endif + ghes_handle_memory_failure(gdata, sev); } #ifdef CONFIG_ACPI_APEI_PCIEAER else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index f5ef5d54e4ac..502024502b13 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -36,6 +36,7 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <acpi/apei.h> +#include <asm/mce.h> #include "apei-internal.h" @@ -121,6 +122,40 @@ int apei_hest_parse(apei_hest_func_t func, void *data) } EXPORT_SYMBOL_GPL(apei_hest_parse); +/* + * Check if firmware advertises firmware first mode. We need FF bit to be set + * along with a set of MC banks which work in FF mode. + */ +static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) +{ + int i; + struct acpi_hest_ia_corrected *cmc; + struct acpi_hest_ia_error_bank *mc_bank; + + if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) + return 0; + + cmc = (struct acpi_hest_ia_corrected *)hest_hdr; + if (!cmc->enabled) + return 0; + + /* + * We expect HEST to provide a list of MC banks that report errors + * in firmware first mode. Otherwise, return non-zero value to + * indicate that we are done parsing HEST. + */ + if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || !cmc->num_hardware_banks) + return 1; + + pr_info(HEST_PFX "Enabling Firmware First mode for corrected errors.\n"); + + mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1); + for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++) + mce_disable_bank(mc_bank->bank_number); + + return 1; +} + struct ghes_arr { struct platform_device **ghes_devs; unsigned int count; @@ -227,6 +262,9 @@ void __init acpi_hest_init(void) goto err; } + if (!acpi_disable_cmcff) + apei_hest_parse(hest_parse_cmc, NULL); + if (!ghes_disable) { rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); if (rc) diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 8b00c5cebfa4..704d6d342adc 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ORION_TIMER) += time-orion.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_ARCH_MARCO) += timer-marco.o +obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 4cbe28c74631..b2bb3a4bc205 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -21,7 +21,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/slab.h> -#include <linux/clk-provider.h> +#include <linux/sched_clock.h> /* * This driver configures the 2 16-bit count-up timers as follows: @@ -95,6 +95,8 @@ struct ttc_timer_clockevent { #define to_ttc_timer_clkevent(x) \ container_of(x, struct ttc_timer_clockevent, ce) +static void __iomem *ttc_sched_clock_val_reg; + /** * ttc_set_interval - Set the timer interval value * @@ -156,6 +158,11 @@ static cycle_t __ttc_clocksource_read(struct clocksource *cs) TTC_COUNT_VAL_OFFSET); } +static u32 notrace ttc_sched_clock_read(void) +{ + return __raw_readl(ttc_sched_clock_val_reg); +} + /** * ttc_set_next_event - Sets the time interval for next event * @@ -297,6 +304,10 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) kfree(ttccs); return; } + + ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET; + setup_sched_clock(ttc_sched_clock_read, 16, + clk_get_rate(ttccs->ttc.clk) / PRESCALE); } static int ttc_rate_change_clockevent_cb(struct notifier_block *nb, diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c new file mode 100644 index 000000000000..08a5943b3e42 --- /dev/null +++ b/drivers/clocksource/moxart_timer.c @@ -0,0 +1,164 @@ +/* + * MOXA ART SoCs timer handling. + * + * Copyright (C) 2013 Jonas Jensen + * + * Jonas Jensen <jonas.jensen@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/io.h> +#include <linux/clocksource.h> + +#define TIMER1_BASE 0x00 +#define TIMER2_BASE 0x10 +#define TIMER3_BASE 0x20 + +#define REG_COUNT 0x0 /* writable */ +#define REG_LOAD 0x4 +#define REG_MATCH1 0x8 +#define REG_MATCH2 0xC + +#define TIMER_CR 0x30 +#define TIMER_INTR_STATE 0x34 +#define TIMER_INTR_MASK 0x38 + +/* + * TIMER_CR flags: + * + * TIMEREG_CR_*_CLOCK 0: PCLK, 1: EXT1CLK + * TIMEREG_CR_*_INT overflow interrupt enable bit + */ +#define TIMEREG_CR_1_ENABLE BIT(0) +#define TIMEREG_CR_1_CLOCK BIT(1) +#define TIMEREG_CR_1_INT BIT(2) +#define TIMEREG_CR_2_ENABLE BIT(3) +#define TIMEREG_CR_2_CLOCK BIT(4) +#define TIMEREG_CR_2_INT BIT(5) +#define TIMEREG_CR_3_ENABLE BIT(6) +#define TIMEREG_CR_3_CLOCK BIT(7) +#define TIMEREG_CR_3_INT BIT(8) +#define TIMEREG_CR_COUNT_UP BIT(9) + +#define TIMER1_ENABLE (TIMEREG_CR_2_ENABLE | TIMEREG_CR_1_ENABLE) +#define TIMER1_DISABLE (TIMEREG_CR_2_ENABLE) + +static void __iomem *base; +static unsigned int clock_count_per_tick; + +static void moxart_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + switch (mode) { + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_ONESHOT: + writel(TIMER1_DISABLE, base + TIMER_CR); + writel(~0, base + TIMER1_BASE + REG_LOAD); + break; + case CLOCK_EVT_MODE_PERIODIC: + writel(clock_count_per_tick, base + TIMER1_BASE + REG_LOAD); + writel(TIMER1_ENABLE, base + TIMER_CR); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + writel(TIMER1_DISABLE, base + TIMER_CR); + break; + } +} + +static int moxart_clkevt_next_event(unsigned long cycles, + struct clock_event_device *unused) +{ + u32 u; + + writel(TIMER1_DISABLE, base + TIMER_CR); + + u = readl(base + TIMER1_BASE + REG_COUNT) - cycles; + writel(u, base + TIMER1_BASE + REG_MATCH1); + + writel(TIMER1_ENABLE, base + TIMER_CR); + + return 0; +} + +static struct clock_event_device moxart_clockevent = { + .name = "moxart_timer", + .rating = 200, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = moxart_clkevt_mode, + .set_next_event = moxart_clkevt_next_event, +}; + +static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + evt->event_handler(evt); + return IRQ_HANDLED; +} + +static struct irqaction moxart_timer_irq = { + .name = "moxart-timer", + .flags = IRQF_TIMER, + .handler = moxart_timer_interrupt, + .dev_id = &moxart_clockevent, +}; + +static void __init moxart_timer_init(struct device_node *node) +{ + int ret, irq; + unsigned long pclk; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) + panic("%s: of_iomap failed\n", node->full_name); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("%s: irq_of_parse_and_map failed\n", node->full_name); + + ret = setup_irq(irq, &moxart_timer_irq); + if (ret) + panic("%s: setup_irq failed\n", node->full_name); + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) + panic("%s: of_clk_get failed\n", node->full_name); + + pclk = clk_get_rate(clk); + + if (clocksource_mmio_init(base + TIMER2_BASE + REG_COUNT, + "moxart_timer", pclk, 200, 32, + clocksource_mmio_readl_down)) + panic("%s: clocksource_mmio_init failed\n", node->full_name); + + clock_count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ); + + writel(~0, base + TIMER2_BASE + REG_LOAD); + writel(TIMEREG_CR_2_ENABLE, base + TIMER_CR); + + moxart_clockevent.cpumask = cpumask_of(0); + moxart_clockevent.irq = irq; + + /* + * documentation is not publicly available: + * min_delta / max_delta obtained by trial-and-error, + * max_delta 0xfffffffe should be ok because count + * register size is u32 + */ + clockevents_config_and_register(&moxart_clockevent, pclk, + 0x4, 0xfffffffe); +} +CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init); diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index d4674e78ef35..8ead0258740a 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -19,42 +19,83 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqreturn.h> +#include <linux/sched_clock.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #define TIMER_IRQ_EN_REG 0x00 -#define TIMER_IRQ_EN(val) (1 << val) +#define TIMER_IRQ_EN(val) BIT(val) #define TIMER_IRQ_ST_REG 0x04 #define TIMER_CTL_REG(val) (0x10 * val + 0x10) -#define TIMER_CTL_ENABLE (1 << 0) -#define TIMER_CTL_AUTORELOAD (1 << 1) -#define TIMER_CTL_ONESHOT (1 << 7) -#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) -#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) - -#define TIMER_SCAL 16 +#define TIMER_CTL_ENABLE BIT(0) +#define TIMER_CTL_RELOAD BIT(1) +#define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) +#define TIMER_CTL_CLK_SRC_OSC24M (1) +#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) +#define TIMER_CTL_ONESHOT BIT(7) +#define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) +#define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) static void __iomem *timer_base; +static u32 ticks_per_jiffy; + +/* + * When we disable a timer, we need to wait at least for 2 cycles of + * the timer source clock. We will use for that the clocksource timer + * that is already setup and runs at the same frequency than the other + * timers, and we never will be disabled. + */ +static void sun4i_clkevt_sync(void) +{ + u32 old = readl(timer_base + TIMER_CNTVAL_REG(1)); + + while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < 3) + cpu_relax(); +} + +static void sun4i_clkevt_time_stop(u8 timer) +{ + u32 val = readl(timer_base + TIMER_CTL_REG(timer)); + writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer)); + sun4i_clkevt_sync(); +} + +static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay) +{ + writel(delay, timer_base + TIMER_INTVAL_REG(timer)); +} + +static void sun4i_clkevt_time_start(u8 timer, bool periodic) +{ + u32 val = readl(timer_base + TIMER_CTL_REG(timer)); + + if (periodic) + val &= ~TIMER_CTL_ONESHOT; + else + val |= TIMER_CTL_ONESHOT; + + writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, + timer_base + TIMER_CTL_REG(timer)); +} static void sun4i_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *clk) { - u32 u = readl(timer_base + TIMER_CTL_REG(0)); - switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - u &= ~(TIMER_CTL_ONESHOT); - writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); + sun4i_clkevt_time_stop(0); + sun4i_clkevt_time_setup(0, ticks_per_jiffy); + sun4i_clkevt_time_start(0, true); break; - case CLOCK_EVT_MODE_ONESHOT: - writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); + sun4i_clkevt_time_stop(0); + sun4i_clkevt_time_start(0, false); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: - writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); + sun4i_clkevt_time_stop(0); break; } } @@ -62,10 +103,9 @@ static void sun4i_clkevt_mode(enum clock_event_mode mode, static int sun4i_clkevt_next_event(unsigned long evt, struct clock_event_device *unused) { - u32 u = readl(timer_base + TIMER_CTL_REG(0)); - writel(evt, timer_base + TIMER_CNTVAL_REG(0)); - writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, - timer_base + TIMER_CTL_REG(0)); + sun4i_clkevt_time_stop(0); + sun4i_clkevt_time_setup(0, evt); + sun4i_clkevt_time_start(0, false); return 0; } @@ -96,6 +136,11 @@ static struct irqaction sun4i_timer_irq = { .dev_id = &sun4i_clockevent, }; +static u32 sun4i_timer_sched_read(void) +{ + return ~readl(timer_base + TIMER_CNTVAL_REG(1)); +} + static void __init sun4i_timer_init(struct device_node *node) { unsigned long rate = 0; @@ -114,22 +159,23 @@ static void __init sun4i_timer_init(struct device_node *node) clk = of_clk_get(node, 0); if (IS_ERR(clk)) panic("Can't get timer clock"); + clk_prepare_enable(clk); rate = clk_get_rate(clk); - writel(rate / (TIMER_SCAL * HZ), - timer_base + TIMER_INTVAL_REG(0)); + writel(~0, timer_base + TIMER_INTVAL_REG(1)); + writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | + TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), + timer_base + TIMER_CTL_REG(1)); + + setup_sched_clock(sun4i_timer_sched_read, 32, rate); + clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name, + rate, 300, 32, clocksource_mmio_readl_down); - /* set clock source to HOSC, 16 pre-division */ - val = readl(timer_base + TIMER_CTL_REG(0)); - val &= ~(0x07 << 4); - val &= ~(0x03 << 2); - val |= (4 << 4) | (1 << 2); - writel(val, timer_base + TIMER_CTL_REG(0)); + ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - /* set mode to auto reload */ - val = readl(timer_base + TIMER_CTL_REG(0)); - writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); + writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), + timer_base + TIMER_CTL_REG(0)); ret = setup_irq(irq, &sun4i_timer_irq); if (ret) @@ -141,8 +187,8 @@ static void __init sun4i_timer_init(struct device_node *node) sun4i_clockevent.cpumask = cpumask_of(0); - clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL, - 0x1, 0xff); + clockevents_config_and_register(&sun4i_clockevent, rate, 0x1, + 0xffffffff); } CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", sun4i_timer_init); diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c index ecbeb6810215..9c7f018a67ca 100644 --- a/drivers/clocksource/time-orion.c +++ b/drivers/clocksource/time-orion.c @@ -19,7 +19,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/spinlock.h> -#include <asm/sched_clock.h> +#include <linux/sched_clock.h> #define TIMER_CTRL 0x00 #define TIMER0_EN BIT(0) |