Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
config CLKSRC_OF
bool
config CLKSRC_I8253
bool
config CLKEVT_I8253
bool
config I8253_LOCK
bool
config CLKBLD_I8253
def_bool y if CLKSRC_I8253 || CLKEVT_I8253 || I8253_LOCK
config CLKSRC_MMIO
bool
config DW_APB_TIMER
bool
config DW_APB_TIMER_OF
bool
config ARMADA_370_XP_TIMER
bool
config SUN4I_TIMER
bool
config VT8500_TIMER
bool
config CADENCE_TTC_TIMER
bool
config CLKSRC_NOMADIK_MTU
bool
depends on (ARCH_NOMADIK || ARCH_U8500)
select CLKSRC_MMIO
help
Support for Multi Timer Unit. MTU provides access
to multiple interrupt generating programmable
32-bit free running decrementing counters.
config CLKSRC_NOMADIK_MTU_SCHED_CLOCK
bool
depends on CLKSRC_NOMADIK_MTU
help
Use the Multi Timer Unit as the sched_clock.
config CLKSRC_DBX500_PRCMU
bool "Clocksource PRCMU Timer"
depends on UX500_SOC_DB8500
default y
help
Use the always on PRCMU Timer as clocksource
config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
bool "Clocksource PRCMU Timer sched_clock"
depends on (CLKSRC_DBX500_PRCMU && !CLKSRC_NOMADIK_MTU_SCHED_CLOCK)
default y
help
Use the always on PRCMU Timer as sched_clock
config ARM_ARCH_TIMER
bool
select CLKSRC_OF if OF
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
This option enables support for the Meta per-thread timers.
config CLKSRC_EXYNOS_MCT
def_bool y if ARCH_EXYNOS
help
Support for Multi Core Timer controller on Exynos SoCs.
config CLKSRC_SAMSUNG_PWM
bool
select CLKSRC_MMIO
help
This is a new clocksource driver for the PWM timer found in
Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
for all devicetree enabled platforms. This driver will be
needed only on systems that do not have the Exynos MCT available.

View File

@@ -0,0 +1,31 @@
obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
obj-$(CONFIG_ARCH_MXS) += mxs_timer.o
obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o

View File

@@ -0,0 +1,249 @@
/*
* linux/drivers/clocksource/acpi_pm.c
*
* This file contains the ACPI PM based clocksource.
*
* This code was largely moved from the i386 timer_pm.c file
* which was (C) Dominik Brodowski <linux@brodo.de> 2003
* and contained the following comments:
*
* Driver to use the Power Management Timer (PMTMR) available in some
* southbridges as primary timing source for the Linux kernel.
*
* Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
* timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
*
* This file is licensed under the GPL v2.
*/
#include <linux/acpi_pmtmr.h>
#include <linux/clocksource.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/io.h>
/*
* The I/O port the PMTMR resides at.
* The location is detected during setup_arch(),
* in arch/i386/kernel/acpi/boot.c
*/
u32 pmtmr_ioport __read_mostly;
static inline u32 read_pmtmr(void)
{
/* mask the output to 24 bits */
return inl(pmtmr_ioport) & ACPI_PM_MASK;
}
u32 acpi_pm_read_verified(void)
{
u32 v1 = 0, v2 = 0, v3 = 0;
/*
* It has been reported that because of various broken
* chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock
* source is not latched, you must read it multiple
* times to ensure a safe value is read:
*/
do {
v1 = read_pmtmr();
v2 = read_pmtmr();
v3 = read_pmtmr();
} while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
|| (v3 > v1 && v3 < v2)));
return v2;
}
static cycle_t acpi_pm_read(struct clocksource *cs)
{
return (cycle_t)read_pmtmr();
}
static struct clocksource clocksource_acpi_pm = {
.name = "acpi_pm",
.rating = 200,
.read = acpi_pm_read,
.mask = (cycle_t)ACPI_PM_MASK,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_PCI
static int acpi_pm_good;
static int __init acpi_pm_good_setup(char *__str)
{
acpi_pm_good = 1;
return 1;
}
__setup("acpi_pm_good", acpi_pm_good_setup);
static cycle_t acpi_pm_read_slow(struct clocksource *cs)
{
return (cycle_t)acpi_pm_read_verified();
}
static inline void acpi_pm_need_workaround(void)
{
clocksource_acpi_pm.read = acpi_pm_read_slow;
clocksource_acpi_pm.rating = 120;
}
/*
* PIIX4 Errata:
*
* The power management timer may return improper results when read.
* Although the timer value settles properly after incrementing,
* while incrementing there is a 3 ns window every 69.8 ns where the
* timer value is indeterminate (a 4.2% chance that the data will be
* incorrect when read). As a result, the ACPI free running count up
* timer specification is violated due to erroneous reads.
*/
static void acpi_pm_check_blacklist(struct pci_dev *dev)
{
if (acpi_pm_good)
return;
/* the bug has been fixed in PIIX4M */
if (dev->revision < 3) {
printk(KERN_WARNING "* Found PM-Timer Bug on the chipset."
" Due to workarounds for a bug,\n"
"* this clock source is slow. Consider trying"
" other clock sources\n");
acpi_pm_need_workaround();
}
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
acpi_pm_check_blacklist);
static void acpi_pm_check_graylist(struct pci_dev *dev)
{
if (acpi_pm_good)
return;
printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to"
" workarounds for a bug,\n"
"* this clock source is slow. If you are sure your timer"
" does not have\n"
"* this bug, please use \"acpi_pm_good\" to disable the"
" workaround\n");
acpi_pm_need_workaround();
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
acpi_pm_check_graylist);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE,
acpi_pm_check_graylist);
#endif
#ifndef CONFIG_X86_64
#include <asm/mach_timer.h>
#define PMTMR_EXPECTED_RATE \
((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (PIT_TICK_RATE>>10))
/*
* Some boards have the PMTMR running way too fast. We check
* the PMTMR rate against PIT channel 2 to catch these cases.
*/
static int verify_pmtmr_rate(void)
{
cycle_t value1, value2;
unsigned long count, delta;
mach_prepare_counter();
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
mach_countup(&count);
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
delta = (value2 - value1) & ACPI_PM_MASK;
/* Check that the PMTMR delta is within 5% of what we expect */
if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% "
"of normal - aborting.\n",
100UL * delta / PMTMR_EXPECTED_RATE);
return -1;
}
return 0;
}
#else
#define verify_pmtmr_rate() (0)
#endif
/* Number of monotonicity checks to perform during initialization */
#define ACPI_PM_MONOTONICITY_CHECKS 10
/* Number of reads we try to get two different values */
#define ACPI_PM_READ_CHECKS 10000
static int __init init_acpi_pm_clocksource(void)
{
cycle_t value1, value2;
unsigned int i, j = 0;
if (!pmtmr_ioport)
return -ENODEV;
/* "verify" this timing source: */
for (j = 0; j < ACPI_PM_MONOTONICITY_CHECKS; j++) {
udelay(100 * j);
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
for (i = 0; i < ACPI_PM_READ_CHECKS; i++) {
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
if (value2 == value1)
continue;
if (value2 > value1)
break;
if ((value2 < value1) && ((value2) < 0xFFF))
break;
printk(KERN_INFO "PM-Timer had inconsistent results:"
" 0x%#llx, 0x%#llx - aborting.\n",
value1, value2);
pmtmr_ioport = 0;
return -EINVAL;
}
if (i == ACPI_PM_READ_CHECKS) {
printk(KERN_INFO "PM-Timer failed consistency check "
" (0x%#llx) - aborting.\n", value1);
pmtmr_ioport = 0;
return -ENODEV;
}
}
if (verify_pmtmr_rate() != 0){
pmtmr_ioport = 0;
return -ENODEV;
}
return clocksource_register_hz(&clocksource_acpi_pm,
PMTMR_TICKS_PER_SEC);
}
/* We use fs_initcall because we want the PCI fixups to have run
* but we still need to load before device_initcall
*/
fs_initcall(init_acpi_pm_clocksource);
/*
* Allow an override of the IOPort. Stupid BIOSes do not tell us about
* the PMTimer, but we might know where it is.
*/
static int __init parse_pmtmr(char *arg)
{
unsigned int base;
int ret;
ret = kstrtouint(arg, 16, &base);
if (ret)
return ret;
pr_info("PMTMR IOPort override: 0x%04x -> 0x%04x\n", pmtmr_ioport,
base);
pmtmr_ioport = base;
return 1;
}
__setup("pmtmr=", parse_pmtmr);

View File

@@ -0,0 +1,388 @@
/*
* linux/drivers/clocksource/arm_arch_timer.c
*
* Copyright (C) 2011 ARM Ltd.
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/io.h>
#include <asm/arch_timer.h>
#include <asm/virt.h>
#include <clocksource/arm_arch_timer.h>
static u32 arch_timer_rate;
enum ppi_nr {
PHYS_SECURE_PPI,
PHYS_NONSECURE_PPI,
VIRT_PPI,
HYP_PPI,
MAX_TIMER_PPI
};
static int arch_timer_ppi[MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
static bool arch_timer_use_virtual = true;
/*
* Architected system timer support.
*/
static inline irqreturn_t timer_handler(const int access,
struct clock_event_device *evt)
{
unsigned long ctrl;
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
evt->event_handler(evt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt);
}
static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
}
static inline void timer_set_mode(const int access, int mode)
{
unsigned long ctrl;
switch (mode) {
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
break;
default:
break;
}
}
static void arch_timer_set_mode_virt(enum clock_event_mode mode,
struct clock_event_device *clk)
{
timer_set_mode(ARCH_TIMER_VIRT_ACCESS, mode);
}
static void arch_timer_set_mode_phys(enum clock_event_mode mode,
struct clock_event_device *clk)
{
timer_set_mode(ARCH_TIMER_PHYS_ACCESS, mode);
}
static inline void set_next_event(const int access, unsigned long evt)
{
unsigned long ctrl;
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
ctrl |= ARCH_TIMER_CTRL_ENABLE;
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(access, ARCH_TIMER_REG_TVAL, evt);
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
}
static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *unused)
{
set_next_event(ARCH_TIMER_VIRT_ACCESS, evt);
return 0;
}
static int arch_timer_set_next_event_phys(unsigned long evt,
struct clock_event_device *unused)
{
set_next_event(ARCH_TIMER_PHYS_ACCESS, evt);
return 0;
}
static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
{
clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer";
clk->rating = 450;
if (arch_timer_use_virtual) {
clk->irq = arch_timer_ppi[VIRT_PPI];
clk->set_mode = arch_timer_set_mode_virt;
clk->set_next_event = arch_timer_set_next_event_virt;
} else {
clk->irq = arch_timer_ppi[PHYS_SECURE_PPI];
clk->set_mode = arch_timer_set_mode_phys;
clk->set_next_event = arch_timer_set_next_event_phys;
}
clk->cpumask = cpumask_of(smp_processor_id());
clk->set_mode(CLOCK_EVT_MODE_SHUTDOWN, NULL);
clockevents_config_and_register(clk, arch_timer_rate,
0xf, 0x7fffffff);
if (arch_timer_use_virtual)
enable_percpu_irq(arch_timer_ppi[VIRT_PPI], 0);
else {
enable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], 0);
if (arch_timer_ppi[PHYS_NONSECURE_PPI])
enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], 0);
}
arch_counter_set_user_access();
return 0;
}
static int arch_timer_available(void)
{
u32 freq;
if (arch_timer_rate == 0) {
freq = arch_timer_get_cntfrq();
/* Check the timer frequency. */
if (freq == 0) {
pr_warn("Architected timer frequency not available\n");
return -EINVAL;
}
arch_timer_rate = freq;
}
pr_info_once("Architected local timer running at %lu.%02luMHz (%s).\n",
(unsigned long)arch_timer_rate / 1000000,
(unsigned long)(arch_timer_rate / 10000) % 100,
arch_timer_use_virtual ? "virt" : "phys");
return 0;
}
u32 arch_timer_get_rate(void)
{
return arch_timer_rate;
}
/*
* Some external users of arch_timer_read_counter (e.g. sched_clock) may try to
* call it before it has been initialised. Rather than incur a performance
* penalty checking for initialisation, provide a default implementation that
* won't lead to time appearing to jump backwards.
*/
static u64 arch_timer_read_zero(void)
{
return 0;
}
u64 (*arch_timer_read_counter)(void) = arch_timer_read_zero;
static cycle_t arch_counter_read(struct clocksource *cs)
{
return arch_timer_read_counter();
}
static cycle_t arch_counter_read_cc(const struct cyclecounter *cc)
{
return arch_timer_read_counter();
}
static struct clocksource clocksource_counter = {
.name = "arch_sys_counter",
.rating = 400,
.read = arch_counter_read,
.mask = CLOCKSOURCE_MASK(56),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct cyclecounter cyclecounter = {
.read = arch_counter_read_cc,
.mask = CLOCKSOURCE_MASK(56),
};
static struct timecounter timecounter;
struct timecounter *arch_timer_get_timecounter(void)
{
return &timecounter;
}
static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
{
pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
clk->irq, smp_processor_id());
if (arch_timer_use_virtual)
disable_percpu_irq(arch_timer_ppi[VIRT_PPI]);
else {
disable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI]);
if (arch_timer_ppi[PHYS_NONSECURE_PPI])
disable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI]);
}
clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk);
}
static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
/*
* Grab cpu pointer in each case to avoid spurious
* preemptible warnings
*/
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
arch_timer_setup(this_cpu_ptr(arch_timer_evt));
break;
case CPU_DYING:
arch_timer_stop(this_cpu_ptr(arch_timer_evt));
break;
}
return NOTIFY_OK;
}
static struct notifier_block arch_timer_cpu_nb __cpuinitdata = {
.notifier_call = arch_timer_cpu_notify,
};
static int __init arch_timer_register(void)
{
int err;
int ppi;
err = arch_timer_available();
if (err)
goto out;
arch_timer_evt = alloc_percpu(struct clock_event_device);
if (!arch_timer_evt) {
err = -ENOMEM;
goto out;
}
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
cyclecounter.mult = clocksource_counter.mult;
cyclecounter.shift = clocksource_counter.shift;
timecounter_init(&timecounter, &cyclecounter,
arch_counter_get_cntpct());
if (arch_timer_use_virtual) {
ppi = arch_timer_ppi[VIRT_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_virt,
"arch_timer", arch_timer_evt);
} else {
ppi = arch_timer_ppi[PHYS_SECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) {
ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
if (err)
free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
arch_timer_evt);
}
}
if (err) {
pr_err("arch_timer: can't register interrupt %d (%d)\n",
ppi, err);
goto out_free;
}
err = register_cpu_notifier(&arch_timer_cpu_nb);
if (err)
goto out_free_irq;
/* Immediately configure the timer on the boot CPU */
arch_timer_setup(this_cpu_ptr(arch_timer_evt));
return 0;
out_free_irq:
if (arch_timer_use_virtual)
free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);
else {
free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
arch_timer_evt);
if (arch_timer_ppi[PHYS_NONSECURE_PPI])
free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI],
arch_timer_evt);
}
out_free:
free_percpu(arch_timer_evt);
out:
return err;
}
static void __init arch_timer_init(struct device_node *np)
{
u32 freq;
int i;
if (arch_timer_get_rate()) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
/* Try to determine the frequency from the device tree or CNTFRQ */
if (!of_property_read_u32(np, "clock-frequency", &freq))
arch_timer_rate = freq;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
of_node_put(np);
/*
* If HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so
* that a guest can use the virtual timer instead.
*
* If no interrupt provided for virtual timer, we'll have to
* stick to the physical timer. It'd better be accessible...
*/
if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) {
arch_timer_use_virtual = false;
if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
!arch_timer_ppi[PHYS_NONSECURE_PPI]) {
pr_warn("arch_timer: No interrupt available, giving up\n");
return;
}
}
if (arch_timer_use_virtual)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
arch_timer_register();
arch_timer_arch_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init);

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2012 Simon Arlott
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/bitops.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/sched_clock.h>
#include <asm/irq.h>
#define REG_CONTROL 0x00
#define REG_COUNTER_LO 0x04
#define REG_COUNTER_HI 0x08
#define REG_COMPARE(n) (0x0c + (n) * 4)
#define MAX_TIMER 3
#define DEFAULT_TIMER 3
struct bcm2835_timer {
void __iomem *control;
void __iomem *compare;
int match_mask;
struct clock_event_device evt;
struct irqaction act;
};
static void __iomem *system_clock __read_mostly;
static u32 notrace bcm2835_sched_read(void)
{
return readl_relaxed(system_clock);
}
static void bcm2835_time_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt_dev)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_RESUME:
break;
default:
WARN(1, "%s: unhandled event mode %d\n", __func__, mode);
break;
}
}
static int bcm2835_time_set_next_event(unsigned long event,
struct clock_event_device *evt_dev)
{
struct bcm2835_timer *timer = container_of(evt_dev,
struct bcm2835_timer, evt);
writel_relaxed(readl_relaxed(system_clock) + event,
timer->compare);
return 0;
}
static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
{
struct bcm2835_timer *timer = dev_id;
void (*event_handler)(struct clock_event_device *);
if (readl_relaxed(timer->control) & timer->match_mask) {
writel_relaxed(timer->match_mask, timer->control);
event_handler = ACCESS_ONCE(timer->evt.event_handler);
if (event_handler)
event_handler(&timer->evt);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static void __init bcm2835_timer_init(struct device_node *node)
{
void __iomem *base;
u32 freq;
int irq;
struct bcm2835_timer *timer;
base = of_iomap(node, 0);
if (!base)
panic("Can't remap registers");
if (of_property_read_u32(node, "clock-frequency", &freq))
panic("Can't read clock-frequency");
system_clock = base + REG_COUNTER_LO;
setup_sched_clock(bcm2835_sched_read, 32, freq);
clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
freq, 300, 32, clocksource_mmio_readl_up);
irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
if (irq <= 0)
panic("Can't parse IRQ");
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (!timer)
panic("Can't allocate timer struct\n");
timer->control = base + REG_CONTROL;
timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
timer->match_mask = BIT(DEFAULT_TIMER);
timer->evt.name = node->name;
timer->evt.rating = 300;
timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
timer->evt.set_mode = bcm2835_time_set_mode;
timer->evt.set_next_event = bcm2835_time_set_next_event;
timer->evt.cpumask = cpumask_of(0);
timer->act.name = node->name;
timer->act.flags = IRQF_TIMER | IRQF_SHARED;
timer->act.dev_id = timer;
timer->act.handler = bcm2835_time_interrupt;
if (setup_irq(irq, &timer->act))
panic("Can't set up timer IRQ\n");
clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
pr_info("bcm2835: system timer (irq = %d)\n", irq);
}
CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
bcm2835_timer_init);

View File

@@ -0,0 +1,211 @@
/*
* Copyright (C) 2012 Broadcom Corporation
*
* 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/types.h>
#include <linux/io.h>
#include <asm/mach/time.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define KONA_GPTIMER_STCS_OFFSET 0x00000000
#define KONA_GPTIMER_STCLO_OFFSET 0x00000004
#define KONA_GPTIMER_STCHI_OFFSET 0x00000008
#define KONA_GPTIMER_STCM0_OFFSET 0x0000000C
#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0
#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4
struct kona_bcm_timers {
int tmr_irq;
void __iomem *tmr_regs;
};
static struct kona_bcm_timers timers;
static u32 arch_timer_rate;
/*
* We use the peripheral timers for system tick, the cpu global timer for
* profile tick
*/
static void kona_timer_disable_and_clear(void __iomem *base)
{
uint32_t reg;
/*
* clear and disable interrupts
* We are using compare/match register 0 for our system interrupts
*/
reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
/* Clear compare (0) interrupt */
reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
/* disable compare */
reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
}
static void
kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw)
{
void __iomem *base = IOMEM(timer_base);
int loop_limit = 4;
/*
* Read 64-bit free running counter
* 1. Read hi-word
* 2. Read low-word
* 3. Read hi-word again
* 4.1
* if new hi-word is not equal to previously read hi-word, then
* start from #1
* 4.2
* if new hi-word is equal to previously read hi-word then stop.
*/
while (--loop_limit) {
*msw = readl(base + KONA_GPTIMER_STCHI_OFFSET);
*lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET);
if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET))
break;
}
if (!loop_limit) {
pr_err("bcm_kona_timer: getting counter failed.\n");
pr_err(" Timer will be impacted\n");
}
return;
}
static const struct of_device_id bcm_timer_ids[] __initconst = {
{.compatible = "bcm,kona-timer"},
{},
};
static void __init kona_timers_init(void)
{
struct device_node *node;
u32 freq;
node = of_find_matching_node(NULL, bcm_timer_ids);
if (!node)
panic("No timer");
if (!of_property_read_u32(node, "clock-frequency", &freq))
arch_timer_rate = freq;
else
panic("clock-frequency not set in the .dts file");
/* Setup IRQ numbers */
timers.tmr_irq = irq_of_parse_and_map(node, 0);
/* Setup IO addresses */
timers.tmr_regs = of_iomap(node, 0);
kona_timer_disable_and_clear(timers.tmr_regs);
}
static int kona_timer_set_next_event(unsigned long clc,
struct clock_event_device *unused)
{
/*
* timer (0) is disabled by the timer interrupt already
* so, here we reload the next event value and re-enable
* the timer.
*
* This way, we are potentially losing the time between
* timer-interrupt->set_next_event. CPU local timers, when
* they come in should get rid of skew.
*/
uint32_t lsw, msw;
uint32_t reg;
kona_timer_get_counter(timers.tmr_regs, &msw, &lsw);
/* Load the "next" event tick value */
writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
/* Enable compare */
reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
return 0;
}
static void kona_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *unused)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
/* by default mode is one shot don't do any thing */
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
default:
kona_timer_disable_and_clear(timers.tmr_regs);
}
}
static struct clock_event_device kona_clockevent_timer = {
.name = "timer 1",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = kona_timer_set_next_event,
.set_mode = kona_timer_set_mode
};
static void __init kona_timer_clockevents_init(void)
{
kona_clockevent_timer.cpumask = cpumask_of(0);
clockevents_config_and_register(&kona_clockevent_timer,
arch_timer_rate, 6, 0xffffffff);
}
static irqreturn_t kona_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &kona_clockevent_timer;
kona_timer_disable_and_clear(timers.tmr_regs);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction kona_timer_irq = {
.name = "Kona Timer Tick",
.flags = IRQF_TIMER,
.handler = kona_timer_interrupt,
};
static void __init kona_timer_init(void)
{
kona_timers_init();
kona_timer_clockevents_init();
setup_irq(timers.tmr_irq, &kona_timer_irq);
kona_timer_set_next_event((arch_timer_rate / HZ), NULL);
}
CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer",
kona_timer_init);

View File

@@ -0,0 +1,436 @@
/*
* This file contains driver for the Cadence Triple Timer Counter Rev 06
*
* Copyright (C) 2011-2013 Xilinx
*
* based on arch/mips/kernel/time.c timer driver
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
/*
* This driver configures the 2 16-bit count-up timers as follows:
*
* T1: Timer 1, clocksource for generic timekeeping
* T2: Timer 2, clockevent source for hrtimers
* T3: Timer 3, <unused>
*
* The input frequency to the timer module for emulation is 2.5MHz which is
* common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
* the timers are clocked at 78.125KHz (12.8 us resolution).
* The input frequency to the timer module in silicon is configurable and
* obtained from device tree. The pre-scaler of 32 is used.
*/
/*
* Timer Register Offset Definitions of Timer 1, Increment base address by 4
* and use same offsets for Timer 2
*/
#define TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
#define TTC_CNT_CNTRL_DISABLE_MASK 0x1
/*
* Setup the timers to use pre-scaling, using a fixed value for now that will
* work across most input frequency, but it may need to be more dynamic
*/
#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
#define PRESCALE 2048 /* The exponent must match this */
#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1)
#define CLK_CNTRL_PRESCALE_EN 1
#define CNT_CNTRL_RESET (1 << 4)
/**
* struct ttc_timer - This definition defines local timer structure
*
* @base_addr: Base address of timer
* @clk: Associated clock source
* @clk_rate_change_nb Notifier block for clock rate changes
*/
struct ttc_timer {
void __iomem *base_addr;
struct clk *clk;
struct notifier_block clk_rate_change_nb;
};
#define to_ttc_timer(x) \
container_of(x, struct ttc_timer, clk_rate_change_nb)
struct ttc_timer_clocksource {
struct ttc_timer ttc;
struct clocksource cs;
};
#define to_ttc_timer_clksrc(x) \
container_of(x, struct ttc_timer_clocksource, cs)
struct ttc_timer_clockevent {
struct ttc_timer ttc;
struct clock_event_device ce;
};
#define to_ttc_timer_clkevent(x) \
container_of(x, struct ttc_timer_clockevent, ce)
/**
* ttc_set_interval - Set the timer interval value
*
* @timer: Pointer to the timer instance
* @cycles: Timer interval ticks
**/
static void ttc_set_interval(struct ttc_timer *timer,
unsigned long cycles)
{
u32 ctrl_reg;
/* Disable the counter, set the counter value and re-enable counter */
ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
__raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
/*
* Reset the counter (0x10) so that it starts from 0, one-shot
* mode makes this needed for timing to be right.
*/
ctrl_reg |= CNT_CNTRL_RESET;
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
}
/**
* ttc_clock_event_interrupt - Clock event timer interrupt handler
*
* @irq: IRQ number of the Timer
* @dev_id: void pointer to the ttc_timer instance
*
* returns: Always IRQ_HANDLED - success
**/
static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
{
struct ttc_timer_clockevent *ttce = dev_id;
struct ttc_timer *timer = &ttce->ttc;
/* Acknowledge the interrupt and call event handler */
__raw_readl(timer->base_addr + TTC_ISR_OFFSET);
ttce->ce.event_handler(&ttce->ce);
return IRQ_HANDLED;
}
/**
* __ttc_clocksource_read - Reads the timer counter register
*
* returns: Current timer counter register value
**/
static cycle_t __ttc_clocksource_read(struct clocksource *cs)
{
struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
return (cycle_t)__raw_readl(timer->base_addr +
TTC_COUNT_VAL_OFFSET);
}
/**
* ttc_set_next_event - Sets the time interval for next event
*
* @cycles: Timer interval ticks
* @evt: Address of clock event instance
*
* returns: Always 0 - success
**/
static int ttc_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
ttc_set_interval(timer, cycles);
return 0;
}
/**
* ttc_set_mode - Sets the mode of timer
*
* @mode: Mode to be set
* @evt: Address of clock event instance
**/
static void ttc_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
u32 ctrl_reg;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
ttc_set_interval(timer,
DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.clk),
PRESCALE * HZ));
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
ctrl_reg = __raw_readl(timer->base_addr +
TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
timer->base_addr + TTC_CNT_CNTRL_OFFSET);
break;
case CLOCK_EVT_MODE_RESUME:
ctrl_reg = __raw_readl(timer->base_addr +
TTC_CNT_CNTRL_OFFSET);
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
timer->base_addr + TTC_CNT_CNTRL_OFFSET);
break;
}
}
static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct ttc_timer *ttc = to_ttc_timer(nb);
struct ttc_timer_clocksource *ttccs = container_of(ttc,
struct ttc_timer_clocksource, ttc);
switch (event) {
case POST_RATE_CHANGE:
/*
* Do whatever is necessary to maintain a proper time base
*
* I cannot find a way to adjust the currently used clocksource
* to the new frequency. __clocksource_updatefreq_hz() sounds
* good, but does not work. Not sure what's that missing.
*
* This approach works, but triggers two clocksource switches.
* The first after unregister to clocksource jiffies. And
* another one after the register to the newly registered timer.
*
* Alternatively we could 'waste' another HW timer to ping pong
* between clock sources. That would also use one register and
* one unregister call, but only trigger one clocksource switch
* for the cost of another HW timer used by the OS.
*/
clocksource_unregister(&ttccs->cs);
clocksource_register_hz(&ttccs->cs,
ndata->new_rate / PRESCALE);
/* fall through */
case PRE_RATE_CHANGE:
case ABORT_RATE_CHANGE:
default:
return NOTIFY_DONE;
}
}
static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
{
struct ttc_timer_clocksource *ttccs;
int err;
ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
if (WARN_ON(!ttccs))
return;
ttccs->ttc.clk = clk;
err = clk_prepare_enable(ttccs->ttc.clk);
if (WARN_ON(err)) {
kfree(ttccs);
return;
}
ttccs->ttc.clk_rate_change_nb.notifier_call =
ttc_rate_change_clocksource_cb;
ttccs->ttc.clk_rate_change_nb.next = NULL;
if (clk_notifier_register(ttccs->ttc.clk,
&ttccs->ttc.clk_rate_change_nb))
pr_warn("Unable to register clock notifier.\n");
ttccs->ttc.base_addr = base;
ttccs->cs.name = "ttc_clocksource";
ttccs->cs.rating = 200;
ttccs->cs.read = __ttc_clocksource_read;
ttccs->cs.mask = CLOCKSOURCE_MASK(16);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
/*
* Setup the clock source counter to be an incrementing counter
* with no interrupt and it rolls over at 0xFFFF. Pre-scale
* it by 32 also. Let it start running now.
*/
__raw_writel(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
__raw_writel(CNT_CNTRL_RESET,
ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs,
clk_get_rate(ttccs->ttc.clk) / PRESCALE);
if (WARN_ON(err)) {
kfree(ttccs);
return;
}
}
static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct ttc_timer *ttc = to_ttc_timer(nb);
struct ttc_timer_clockevent *ttcce = container_of(ttc,
struct ttc_timer_clockevent, ttc);
switch (event) {
case POST_RATE_CHANGE:
{
unsigned long flags;
/*
* clockevents_update_freq should be called with IRQ disabled on
* the CPU the timer provides events for. The timer we use is
* common to both CPUs, not sure if we need to run on both
* cores.
*/
local_irq_save(flags);
clockevents_update_freq(&ttcce->ce,
ndata->new_rate / PRESCALE);
local_irq_restore(flags);
/* fall through */
}
case PRE_RATE_CHANGE:
case ABORT_RATE_CHANGE:
default:
return NOTIFY_DONE;
}
}
static void __init ttc_setup_clockevent(struct clk *clk,
void __iomem *base, u32 irq)
{
struct ttc_timer_clockevent *ttcce;
int err;
ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
if (WARN_ON(!ttcce))
return;
ttcce->ttc.clk = clk;
err = clk_prepare_enable(ttcce->ttc.clk);
if (WARN_ON(err)) {
kfree(ttcce);
return;
}
ttcce->ttc.clk_rate_change_nb.notifier_call =
ttc_rate_change_clockevent_cb;
ttcce->ttc.clk_rate_change_nb.next = NULL;
if (clk_notifier_register(ttcce->ttc.clk,
&ttcce->ttc.clk_rate_change_nb))
pr_warn("Unable to register clock notifier.\n");
ttcce->ttc.base_addr = base;
ttcce->ce.name = "ttc_clockevent";
ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ttcce->ce.set_next_event = ttc_set_next_event;
ttcce->ce.set_mode = ttc_set_mode;
ttcce->ce.rating = 200;
ttcce->ce.irq = irq;
ttcce->ce.cpumask = cpu_possible_mask;
/*
* Setup the clock event timer to be an interval timer which
* is prescaled by 32 using the interval interrupt. Leave it
* disabled for now.
*/
__raw_writel(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
__raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
err = request_irq(irq, ttc_clock_event_interrupt,
IRQF_DISABLED | IRQF_TIMER,
ttcce->ce.name, ttcce);
if (WARN_ON(err)) {
kfree(ttcce);
return;
}
clockevents_config_and_register(&ttcce->ce,
clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe);
}
/**
* ttc_timer_init - Initialize the timer
*
* Initializes the timer hardware and register the clock source and clock event
* timers with Linux kernal timer framework
*/
static void __init ttc_timer_init(struct device_node *timer)
{
unsigned int irq;
void __iomem *timer_baseaddr;
struct clk *clk;
static int initialized;
if (initialized)
return;
initialized = 1;
/*
* Get the 1st Triple Timer Counter (TTC) block from the device tree
* and use it. Note that the event timer uses the interrupt and it's the
* 2nd TTC hence the irq_of_parse_and_map(,1)
*/
timer_baseaddr = of_iomap(timer, 0);
if (!timer_baseaddr) {
pr_err("ERROR: invalid timer base address\n");
BUG();
}
irq = irq_of_parse_and_map(timer, 1);
if (irq <= 0) {
pr_err("ERROR: invalid interrupt number\n");
BUG();
}
clk = of_clk_get_by_name(timer, "cpu_1x");
if (IS_ERR(clk)) {
pr_err("ERROR: timer input clock not found\n");
BUG();
}
ttc_setup_clocksource(clk, timer_baseaddr);
ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
}
CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) ST-Ericsson SA 2011
*
* License Terms: GNU General Public License v2
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson
* Author: Sundar Iyer for ST-Ericsson
* sched_clock implementation is based on:
* plat-nomadik/timer.c Linus Walleij <linus.walleij@stericsson.com>
*
* DBx500-PRCMU Timer
* The PRCMU has 5 timers which are available in a always-on
* power domain. We use the Timer 4 for our always-on clock
* source on DB8500 and Timer 3 on DB5500.
*/
#include <linux/clockchips.h>
#include <linux/clksrc-dbx500-prcmu.h>
#include <asm/sched_clock.h>
#define RATE_32K 32768
#define TIMER_MODE_CONTINOUS 0x1
#define TIMER_DOWNCOUNT_VAL 0xffffffff
#define PRCMU_TIMER_REF 0
#define PRCMU_TIMER_DOWNCOUNT 0x4
#define PRCMU_TIMER_MODE 0x8
#define SCHED_CLOCK_MIN_WRAP 131072 /* 2^32 / 32768 */
static void __iomem *clksrc_dbx500_timer_base;
static cycle_t clksrc_dbx500_prcmu_read(struct clocksource *cs)
{
u32 count, count2;
do {
count = readl(clksrc_dbx500_timer_base +
PRCMU_TIMER_DOWNCOUNT);
count2 = readl(clksrc_dbx500_timer_base +
PRCMU_TIMER_DOWNCOUNT);
} while (count2 != count);
/* Negate because the timer is a decrementing counter */
return ~count;
}
static struct clocksource clocksource_dbx500_prcmu = {
.name = "dbx500-prcmu-timer",
.rating = 300,
.read = clksrc_dbx500_prcmu_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
static u32 notrace dbx500_prcmu_sched_clock_read(void)
{
if (unlikely(!clksrc_dbx500_timer_base))
return 0;
return clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
}
#endif
void __init clksrc_dbx500_prcmu_init(void __iomem *base)
{
clksrc_dbx500_timer_base = base;
/*
* The A9 sub system expects the timer to be configured as
* a continous looping timer.
* The PRCMU should configure it but if it for some reason
* don't we do it here.
*/
if (readl(clksrc_dbx500_timer_base + PRCMU_TIMER_MODE) !=
TIMER_MODE_CONTINOUS) {
writel(TIMER_MODE_CONTINOUS,
clksrc_dbx500_timer_base + PRCMU_TIMER_MODE);
writel(TIMER_DOWNCOUNT_VAL,
clksrc_dbx500_timer_base + PRCMU_TIMER_REF);
}
#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
setup_sched_clock(dbx500_prcmu_sched_clock_read,
32, RATE_32K);
#endif
clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/init.h>
#include <linux/of.h>
#include <linux/clocksource.h>
extern struct of_device_id __clksrc_of_table[];
static const struct of_device_id __clksrc_of_table_sentinel
__used __section(__clksrc_of_table_end);
void __init clocksource_of_init(void)
{
struct device_node *np;
const struct of_device_id *match;
clocksource_of_init_fn init_func;
for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
init_func = match->data;
init_func(np);
}
}

View File

@@ -0,0 +1,191 @@
/*
* Clock event driver for the CS5535/CS5536
*
* Copyright (C) 2006, Advanced Micro Devices, Inc.
* Copyright (C) 2007 Andres Salomon <dilinger@debian.org>
* Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
*/
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/cs5535.h>
#include <linux/clockchips.h>
#define DRV_NAME "cs5535-clockevt"
static int timer_irq;
module_param_named(irq, timer_irq, int, 0644);
MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
/*
* We are using the 32.768kHz input clock - it's the only one that has the
* ranges we find desirable. The following table lists the suitable
* divisors and the associated Hz, minimum interval and the maximum interval:
*
* Divisor Hz Min Delta (s) Max Delta (s)
* 1 32768 .00048828125 2.000
* 2 16384 .0009765625 4.000
* 4 8192 .001953125 8.000
* 8 4096 .00390625 16.000
* 16 2048 .0078125 32.000
* 32 1024 .015625 64.000
* 64 512 .03125 128.000
* 128 256 .0625 256.000
* 256 128 .125 512.000
*/
static unsigned int cs5535_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
static struct cs5535_mfgpt_timer *cs5535_event_clock;
/* Selected from the table above */
#define MFGPT_DIVISOR 16
#define MFGPT_SCALE 4 /* divisor = 2^(scale) */
#define MFGPT_HZ (32768 / MFGPT_DIVISOR)
#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
/*
* The MFGPT timers on the CS5536 provide us with suitable timers to use
* as clock event sources - not as good as a HPET or APIC, but certainly
* better than the PIT. This isn't a general purpose MFGPT driver, but
* a simplified one designed specifically to act as a clock event source.
* For full details about the MFGPT, please consult the CS5536 data sheet.
*/
static void disable_timer(struct cs5535_mfgpt_timer *timer)
{
/* avoid races by clearing CMP1 and CMP2 unconditionally */
cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
(uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
MFGPT_SETUP_CMP2);
}
static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
{
cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
}
static void mfgpt_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
disable_timer(cs5535_event_clock);
if (mode == CLOCK_EVT_MODE_PERIODIC)
start_timer(cs5535_event_clock, MFGPT_PERIODIC);
cs5535_tick_mode = mode;
}
static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
{
start_timer(cs5535_event_clock, delta);
return 0;
}
static struct clock_event_device cs5535_clockevent = {
.name = DRV_NAME,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_mode = mfgpt_set_mode,
.set_next_event = mfgpt_next_event,
.rating = 250,
};
static irqreturn_t mfgpt_tick(int irq, void *dev_id)
{
uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
/* See if the interrupt was for us */
if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
return IRQ_NONE;
/* Turn off the clock (and clear the event) */
disable_timer(cs5535_event_clock);
if (cs5535_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
return IRQ_HANDLED;
/* Clear the counter */
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
/* Restart the clock in periodic mode */
if (cs5535_tick_mode == CLOCK_EVT_MODE_PERIODIC)
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
cs5535_clockevent.event_handler(&cs5535_clockevent);
return IRQ_HANDLED;
}
static struct irqaction mfgptirq = {
.handler = mfgpt_tick,
.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED,
.name = DRV_NAME,
};
static int __init cs5535_mfgpt_init(void)
{
struct cs5535_mfgpt_timer *timer;
int ret;
uint16_t val;
timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
if (!timer) {
printk(KERN_ERR DRV_NAME ": Could not allocate MFGPT timer\n");
return -ENODEV;
}
cs5535_event_clock = timer;
/* Set up the IRQ on the MFGPT side */
if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
timer_irq);
goto err_timer;
}
/* And register it with the kernel */
ret = setup_irq(timer_irq, &mfgptirq);
if (ret) {
printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
goto err_irq;
}
/* Set the clock scale and enable the event mode for CMP2 */
val = MFGPT_SCALE | (3 << 8);
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
/* Set up the clock event */
printk(KERN_INFO DRV_NAME
": Registering MFGPT timer as a clock event, using IRQ %d\n",
timer_irq);
clockevents_config_and_register(&cs5535_clockevent, MFGPT_HZ,
0xF, 0xFFFE);
return 0;
err_irq:
cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
err_timer:
cs5535_mfgpt_free_timer(cs5535_event_clock);
printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
return -EIO;
}
module_init(cs5535_mfgpt_init);
MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,113 @@
#include <linux/clocksource.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/timex.h>
#include <linux/init.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/mach_timer.h>
#define CYCLONE_CBAR_ADDR 0xFEB00CD0 /* base address ptr */
#define CYCLONE_PMCC_OFFSET 0x51A0 /* offset to control register */
#define CYCLONE_MPCS_OFFSET 0x51A8 /* offset to select register */
#define CYCLONE_MPMC_OFFSET 0x51D0 /* offset to count register */
#define CYCLONE_TIMER_FREQ 99780000 /* 100Mhz, but not really */
#define CYCLONE_TIMER_MASK CLOCKSOURCE_MASK(32) /* 32 bit mask */
int use_cyclone = 0;
static void __iomem *cyclone_ptr;
static cycle_t read_cyclone(struct clocksource *cs)
{
return (cycle_t)readl(cyclone_ptr);
}
static struct clocksource clocksource_cyclone = {
.name = "cyclone",
.rating = 250,
.read = read_cyclone,
.mask = CYCLONE_TIMER_MASK,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init init_cyclone_clocksource(void)
{
unsigned long base; /* saved value from CBAR */
unsigned long offset;
u32 __iomem* volatile cyclone_timer; /* Cyclone MPMC0 register */
u32 __iomem* reg;
int i;
/* make sure we're on a summit box: */
if (!use_cyclone)
return -ENODEV;
printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
/* find base address: */
offset = CYCLONE_CBAR_ADDR;
reg = ioremap_nocache(offset, sizeof(reg));
if (!reg) {
printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
return -ENODEV;
}
/* even on 64bit systems, this is only 32bits: */
base = readl(reg);
iounmap(reg);
if (!base) {
printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
return -ENODEV;
}
/* setup PMCC: */
offset = base + CYCLONE_PMCC_OFFSET;
reg = ioremap_nocache(offset, sizeof(reg));
if (!reg) {
printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
return -ENODEV;
}
writel(0x00000001,reg);
iounmap(reg);
/* setup MPCS: */
offset = base + CYCLONE_MPCS_OFFSET;
reg = ioremap_nocache(offset, sizeof(reg));
if (!reg) {
printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
return -ENODEV;
}
writel(0x00000001,reg);
iounmap(reg);
/* map in cyclone_timer: */
offset = base + CYCLONE_MPMC_OFFSET;
cyclone_timer = ioremap_nocache(offset, sizeof(u64));
if (!cyclone_timer) {
printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
return -ENODEV;
}
/* quick test to make sure its ticking: */
for (i = 0; i < 3; i++){
u32 old = readl(cyclone_timer);
int stall = 100;
while (stall--)
barrier();
if (readl(cyclone_timer) == old) {
printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
iounmap(cyclone_timer);
cyclone_timer = NULL;
return -ENODEV;
}
}
cyclone_ptr = cyclone_timer;
return clocksource_register_hz(&clocksource_cyclone,
CYCLONE_TIMER_FREQ);
}
arch_initcall(init_cyclone_clocksource);

View File

@@ -0,0 +1,401 @@
/*
* (C) Copyright 2009 Intel Corporation
* Author: Jacob Pan (jacob.jun.pan@intel.com)
*
* Shared with ARM platforms, Jamie Iles, Picochip 2011
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Support for the Synopsys DesignWare APB Timers.
*/
#include <linux/dw_apb_timer.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
#define APBT_MIN_PERIOD 4
#define APBT_MIN_DELTA_USEC 200
#define APBTMR_N_LOAD_COUNT 0x00
#define APBTMR_N_CURRENT_VALUE 0x04
#define APBTMR_N_CONTROL 0x08
#define APBTMR_N_EOI 0x0c
#define APBTMR_N_INT_STATUS 0x10
#define APBTMRS_INT_STATUS 0xa0
#define APBTMRS_EOI 0xa4
#define APBTMRS_RAW_INT_STATUS 0xa8
#define APBTMRS_COMP_VERSION 0xac
#define APBTMR_CONTROL_ENABLE (1 << 0)
/* 1: periodic, 0:free running. */
#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1)
#define APBTMR_CONTROL_INT (1 << 2)
static inline struct dw_apb_clock_event_device *
ced_to_dw_apb_ced(struct clock_event_device *evt)
{
return container_of(evt, struct dw_apb_clock_event_device, ced);
}
static inline struct dw_apb_clocksource *
clocksource_to_dw_apb_clocksource(struct clocksource *cs)
{
return container_of(cs, struct dw_apb_clocksource, cs);
}
static unsigned long apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
{
return readl(timer->base + offs);
}
static void apbt_writel(struct dw_apb_timer *timer, unsigned long val,
unsigned long offs)
{
writel(val, timer->base + offs);
}
static void apbt_disable_int(struct dw_apb_timer *timer)
{
unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
ctrl |= APBTMR_CONTROL_INT;
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
}
/**
* dw_apb_clockevent_pause() - stop the clock_event_device from running
*
* @dw_ced: The APB clock to stop generating events.
*/
void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
{
disable_irq(dw_ced->timer.irq);
apbt_disable_int(&dw_ced->timer);
}
static void apbt_eoi(struct dw_apb_timer *timer)
{
apbt_readl(timer, APBTMR_N_EOI);
}
static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
{
struct clock_event_device *evt = data;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
if (!evt->event_handler) {
pr_info("Spurious APBT timer interrupt %d", irq);
return IRQ_NONE;
}
if (dw_ced->eoi)
dw_ced->eoi(&dw_ced->timer);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static void apbt_enable_int(struct dw_apb_timer *timer)
{
unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
/* clear pending intr */
apbt_readl(timer, APBTMR_N_EOI);
ctrl &= ~APBTMR_CONTROL_INT;
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
}
static void apbt_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
unsigned long ctrl;
unsigned long period;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
pr_debug("%s CPU %d mode=%d\n", __func__, first_cpu(*evt->cpumask),
mode);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl |= APBTMR_CONTROL_MODE_PERIODIC;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/*
* DW APB p. 46, have to disable timer before load counter,
* may cause sync problem.
*/
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
udelay(1);
pr_debug("Setting clock period %lu for HZ %d\n", period, HZ);
apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT);
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
break;
case CLOCK_EVT_MODE_ONESHOT:
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
/*
* set free running mode, this mode will let timer reload max
* timeout which will give time (3min on 25MHz clock) to rearm
* the next event, therefore emulate the one-shot mode.
*/
ctrl &= ~APBTMR_CONTROL_ENABLE;
ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/* write again to set free running mode */
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/*
* DW APB p. 46, load counter with all 1s before starting free
* running mode.
*/
apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT);
ctrl &= ~APBTMR_CONTROL_INT;
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
break;
case CLOCK_EVT_MODE_RESUME:
apbt_enable_int(&dw_ced->timer);
break;
}
}
static int apbt_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned long ctrl;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
/* Disable timer */
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/* write new count */
apbt_writel(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
/**
* dw_apb_clockevent_init() - use an APB timer as a clock_event_device
*
* @cpu: The CPU the events will be targeted at.
* @name: The name used for the timer and the IRQ for it.
* @rating: The rating to give the timer.
* @base: I/O base for the timer registers.
* @irq: The interrupt number to use for the timer.
* @freq: The frequency that the timer counts at.
*
* This creates a clock_event_device for using with the generic clock layer
* but does not start and register it. This should be done with
* dw_apb_clockevent_register() as the next step. If this is the first time
* it has been called for a timer then the IRQ will be requested, if not it
* just be enabled to allow CPU hotplug to avoid repeatedly requesting and
* releasing the IRQ.
*/
struct dw_apb_clock_event_device *
dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
void __iomem *base, int irq, unsigned long freq)
{
struct dw_apb_clock_event_device *dw_ced =
kzalloc(sizeof(*dw_ced), GFP_KERNEL);
int err;
if (!dw_ced)
return NULL;
dw_ced->timer.base = base;
dw_ced->timer.irq = irq;
dw_ced->timer.freq = freq;
clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD);
dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff,
&dw_ced->ced);
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
dw_ced->ced.cpumask = cpumask_of(cpu);
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
dw_ced->ced.set_mode = apbt_set_mode;
dw_ced->ced.set_next_event = apbt_next_event;
dw_ced->ced.irq = dw_ced->timer.irq;
dw_ced->ced.rating = rating;
dw_ced->ced.name = name;
dw_ced->irqaction.name = dw_ced->ced.name;
dw_ced->irqaction.handler = dw_apb_clockevent_irq;
dw_ced->irqaction.dev_id = &dw_ced->ced;
dw_ced->irqaction.irq = irq;
dw_ced->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL |
IRQF_NOBALANCING |
IRQF_DISABLED;
dw_ced->eoi = apbt_eoi;
err = setup_irq(irq, &dw_ced->irqaction);
if (err) {
pr_err("failed to request timer irq\n");
kfree(dw_ced);
dw_ced = NULL;
}
return dw_ced;
}
/**
* dw_apb_clockevent_resume() - resume a clock that has been paused.
*
* @dw_ced: The APB clock to resume.
*/
void dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced)
{
enable_irq(dw_ced->timer.irq);
}
/**
* dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ.
*
* @dw_ced: The APB clock to stop generating the events.
*/
void dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced)
{
free_irq(dw_ced->timer.irq, &dw_ced->ced);
}
/**
* dw_apb_clockevent_register() - register the clock with the generic layer
*
* @dw_ced: The APB clock to register as a clock_event_device.
*/
void dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced)
{
apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL);
clockevents_register_device(&dw_ced->ced);
apbt_enable_int(&dw_ced->timer);
}
/**
* dw_apb_clocksource_start() - start the clocksource counting.
*
* @dw_cs: The clocksource to start.
*
* This is used to start the clocksource before registration and can be used
* to enable calibration of timers.
*/
void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
{
/*
* start count down from 0xffff_ffff. this is done by toggling the
* enable bit then load initial load count to ~0.
*/
unsigned long ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT);
/* enable, mask interrupt */
ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT);
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
/* read it once to get cached counter value initialized */
dw_apb_clocksource_read(dw_cs);
}
static cycle_t __apbt_read_clocksource(struct clocksource *cs)
{
unsigned long current_count;
struct dw_apb_clocksource *dw_cs =
clocksource_to_dw_apb_clocksource(cs);
current_count = apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
return (cycle_t)~current_count;
}
static void apbt_restart_clocksource(struct clocksource *cs)
{
struct dw_apb_clocksource *dw_cs =
clocksource_to_dw_apb_clocksource(cs);
dw_apb_clocksource_start(dw_cs);
}
/**
* dw_apb_clocksource_init() - use an APB timer as a clocksource.
*
* @rating: The rating to give the clocksource.
* @name: The name for the clocksource.
* @base: The I/O base for the timer registers.
* @freq: The frequency that the timer counts at.
*
* This creates a clocksource using an APB timer but does not yet register it
* with the clocksource system. This should be done with
* dw_apb_clocksource_register() as the next step.
*/
struct dw_apb_clocksource *
dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base,
unsigned long freq)
{
struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL);
if (!dw_cs)
return NULL;
dw_cs->timer.base = base;
dw_cs->timer.freq = freq;
dw_cs->cs.name = name;
dw_cs->cs.rating = rating;
dw_cs->cs.read = __apbt_read_clocksource;
dw_cs->cs.mask = CLOCKSOURCE_MASK(32);
dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
dw_cs->cs.resume = apbt_restart_clocksource;
return dw_cs;
}
/**
* dw_apb_clocksource_register() - register the APB clocksource.
*
* @dw_cs: The clocksource to register.
*/
void dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs)
{
clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq);
}
/**
* dw_apb_clocksource_read() - read the current value of a clocksource.
*
* @dw_cs: The clocksource to read.
*/
cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs)
{
return (cycle_t)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
}
/**
* dw_apb_clocksource_unregister() - unregister and free a clocksource.
*
* @dw_cs: The clocksource to unregister/free.
*/
void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs)
{
clocksource_unregister(&dw_cs->cs);
kfree(dw_cs);
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2012 Altera Corporation
* Copyright (c) 2011 Picochip Ltd., Jamie Iles
*
* Modified from mach-picoxcell/time.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/dw_apb_timer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/time.h>
#include <asm/sched_clock.h>
static void timer_get_base_and_rate(struct device_node *np,
void __iomem **base, u32 *rate)
{
*base = of_iomap(np, 0);
if (!*base)
panic("Unable to map regs for %s", np->name);
if (of_property_read_u32(np, "clock-freq", rate) &&
of_property_read_u32(np, "clock-frequency", rate))
panic("No clock-frequency property for %s", np->name);
}
static void add_clockevent(struct device_node *event_timer)
{
void __iomem *iobase;
struct dw_apb_clock_event_device *ced;
u32 irq, rate;
irq = irq_of_parse_and_map(event_timer, 0);
if (irq == 0)
panic("No IRQ for clock event timer");
timer_get_base_and_rate(event_timer, &iobase, &rate);
ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq,
rate);
if (!ced)
panic("Unable to initialise clockevent device");
dw_apb_clockevent_register(ced);
}
static void add_clocksource(struct device_node *source_timer)
{
void __iomem *iobase;
struct dw_apb_clocksource *cs;
u32 rate;
timer_get_base_and_rate(source_timer, &iobase, &rate);
cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
if (!cs)
panic("Unable to initialise clocksource device");
dw_apb_clocksource_start(cs);
dw_apb_clocksource_register(cs);
}
static void __iomem *sched_io_base;
static u32 read_sched_clock(void)
{
return __raw_readl(sched_io_base);
}
static const struct of_device_id sptimer_ids[] __initconst = {
{ .compatible = "picochip,pc3x2-rtc" },
{ .compatible = "snps,dw-apb-timer-sp" },
{ /* Sentinel */ },
};
static void init_sched_clock(void)
{
struct device_node *sched_timer;
u32 rate;
sched_timer = of_find_matching_node(NULL, sptimer_ids);
if (!sched_timer)
panic("No RTC for sched clock to use");
timer_get_base_and_rate(sched_timer, &sched_io_base, &rate);
of_node_put(sched_timer);
setup_sched_clock(read_sched_clock, 32, rate);
}
static const struct of_device_id osctimer_ids[] __initconst = {
{ .compatible = "picochip,pc3x2-timer" },
{ .compatible = "snps,dw-apb-timer-osc" },
{},
};
void __init dw_apb_timer_init(void)
{
struct device_node *event_timer, *source_timer;
event_timer = of_find_matching_node(NULL, osctimer_ids);
if (!event_timer)
panic("No timer for clockevent");
add_clockevent(event_timer);
source_timer = of_find_matching_node(event_timer, osctimer_ids);
if (!source_timer)
panic("No timer for clocksource");
add_clocksource(source_timer);
of_node_put(source_timer);
init_sched_clock();
}

View File

@@ -0,0 +1,417 @@
/*
* Emma Mobile Timer Support - STI
*
* Copyright (C) 2012 Magnus Damm
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/slab.h>
#include <linux/module.h>
enum { USER_CLOCKSOURCE, USER_CLOCKEVENT, USER_NR };
struct em_sti_priv {
void __iomem *base;
struct clk *clk;
struct platform_device *pdev;
unsigned int active[USER_NR];
unsigned long rate;
raw_spinlock_t lock;
struct clock_event_device ced;
struct clocksource cs;
};
#define STI_CONTROL 0x00
#define STI_COMPA_H 0x10
#define STI_COMPA_L 0x14
#define STI_COMPB_H 0x18
#define STI_COMPB_L 0x1c
#define STI_COUNT_H 0x20
#define STI_COUNT_L 0x24
#define STI_COUNT_RAW_H 0x28
#define STI_COUNT_RAW_L 0x2c
#define STI_SET_H 0x30
#define STI_SET_L 0x34
#define STI_INTSTATUS 0x40
#define STI_INTRAWSTATUS 0x44
#define STI_INTENSET 0x48
#define STI_INTENCLR 0x4c
#define STI_INTFFCLR 0x50
static inline unsigned long em_sti_read(struct em_sti_priv *p, int offs)
{
return ioread32(p->base + offs);
}
static inline void em_sti_write(struct em_sti_priv *p, int offs,
unsigned long value)
{
iowrite32(value, p->base + offs);
}
static int em_sti_enable(struct em_sti_priv *p)
{
int ret;
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
return ret;
}
/* configure channel, periodic mode and maximum timeout */
p->rate = clk_get_rate(p->clk);
/* reset the counter */
em_sti_write(p, STI_SET_H, 0x40000000);
em_sti_write(p, STI_SET_L, 0x00000000);
/* mask and clear pending interrupts */
em_sti_write(p, STI_INTENCLR, 3);
em_sti_write(p, STI_INTFFCLR, 3);
/* enable updates of counter registers */
em_sti_write(p, STI_CONTROL, 1);
return 0;
}
static void em_sti_disable(struct em_sti_priv *p)
{
/* mask interrupts */
em_sti_write(p, STI_INTENCLR, 3);
/* stop clock */
clk_disable(p->clk);
}
static cycle_t em_sti_count(struct em_sti_priv *p)
{
cycle_t ticks;
unsigned long flags;
/* the STI hardware buffers the 48-bit count, but to
* break it out into two 32-bit access the registers
* must be accessed in a certain order.
* Always read STI_COUNT_H before STI_COUNT_L.
*/
raw_spin_lock_irqsave(&p->lock, flags);
ticks = (cycle_t)(em_sti_read(p, STI_COUNT_H) & 0xffff) << 32;
ticks |= em_sti_read(p, STI_COUNT_L);
raw_spin_unlock_irqrestore(&p->lock, flags);
return ticks;
}
static cycle_t em_sti_set_next(struct em_sti_priv *p, cycle_t next)
{
unsigned long flags;
raw_spin_lock_irqsave(&p->lock, flags);
/* mask compare A interrupt */
em_sti_write(p, STI_INTENCLR, 1);
/* update compare A value */
em_sti_write(p, STI_COMPA_H, next >> 32);
em_sti_write(p, STI_COMPA_L, next & 0xffffffff);
/* clear compare A interrupt source */
em_sti_write(p, STI_INTFFCLR, 1);
/* unmask compare A interrupt */
em_sti_write(p, STI_INTENSET, 1);
raw_spin_unlock_irqrestore(&p->lock, flags);
return next;
}
static irqreturn_t em_sti_interrupt(int irq, void *dev_id)
{
struct em_sti_priv *p = dev_id;
p->ced.event_handler(&p->ced);
return IRQ_HANDLED;
}
static int em_sti_start(struct em_sti_priv *p, unsigned int user)
{
unsigned long flags;
int used_before;
int ret = 0;
raw_spin_lock_irqsave(&p->lock, flags);
used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
if (!used_before)
ret = em_sti_enable(p);
if (!ret)
p->active[user] = 1;
raw_spin_unlock_irqrestore(&p->lock, flags);
return ret;
}
static void em_sti_stop(struct em_sti_priv *p, unsigned int user)
{
unsigned long flags;
int used_before, used_after;
raw_spin_lock_irqsave(&p->lock, flags);
used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
p->active[user] = 0;
used_after = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
if (used_before && !used_after)
em_sti_disable(p);
raw_spin_unlock_irqrestore(&p->lock, flags);
}
static struct em_sti_priv *cs_to_em_sti(struct clocksource *cs)
{
return container_of(cs, struct em_sti_priv, cs);
}
static cycle_t em_sti_clocksource_read(struct clocksource *cs)
{
return em_sti_count(cs_to_em_sti(cs));
}
static int em_sti_clocksource_enable(struct clocksource *cs)
{
int ret;
struct em_sti_priv *p = cs_to_em_sti(cs);
ret = em_sti_start(p, USER_CLOCKSOURCE);
if (!ret)
__clocksource_updatefreq_hz(cs, p->rate);
return ret;
}
static void em_sti_clocksource_disable(struct clocksource *cs)
{
em_sti_stop(cs_to_em_sti(cs), USER_CLOCKSOURCE);
}
static void em_sti_clocksource_resume(struct clocksource *cs)
{
em_sti_clocksource_enable(cs);
}
static int em_sti_register_clocksource(struct em_sti_priv *p)
{
struct clocksource *cs = &p->cs;
memset(cs, 0, sizeof(*cs));
cs->name = dev_name(&p->pdev->dev);
cs->rating = 200;
cs->read = em_sti_clocksource_read;
cs->enable = em_sti_clocksource_enable;
cs->disable = em_sti_clocksource_disable;
cs->suspend = em_sti_clocksource_disable;
cs->resume = em_sti_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(48);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&p->pdev->dev, "used as clock source\n");
/* Register with dummy 1 Hz value, gets updated in ->enable() */
clocksource_register_hz(cs, 1);
return 0;
}
static struct em_sti_priv *ced_to_em_sti(struct clock_event_device *ced)
{
return container_of(ced, struct em_sti_priv, ced);
}
static void em_sti_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
struct em_sti_priv *p = ced_to_em_sti(ced);
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_ONESHOT:
em_sti_stop(p, USER_CLOCKEVENT);
break;
default:
break;
}
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
em_sti_start(p, USER_CLOCKEVENT);
clockevents_config(&p->ced, p->rate);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
em_sti_stop(p, USER_CLOCKEVENT);
break;
default:
break;
}
}
static int em_sti_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct em_sti_priv *p = ced_to_em_sti(ced);
cycle_t next;
int safe;
next = em_sti_set_next(p, em_sti_count(p) + delta);
safe = em_sti_count(p) < (next - 1);
return !safe;
}
static void em_sti_register_clockevent(struct em_sti_priv *p)
{
struct clock_event_device *ced = &p->ced;
memset(ced, 0, sizeof(*ced));
ced->name = dev_name(&p->pdev->dev);
ced->features = CLOCK_EVT_FEAT_ONESHOT;
ced->rating = 200;
ced->cpumask = cpumask_of(0);
ced->set_next_event = em_sti_clock_event_next;
ced->set_mode = em_sti_clock_event_mode;
dev_info(&p->pdev->dev, "used for clock events\n");
/* Register with dummy 1 Hz value, gets updated in ->set_mode() */
clockevents_config_and_register(ced, 1, 2, 0xffffffff);
}
static int em_sti_probe(struct platform_device *pdev)
{
struct em_sti_priv *p;
struct resource *res;
int irq, ret;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
ret = -ENOMEM;
goto err0;
}
p->pdev = pdev;
platform_set_drvdata(pdev, p);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
ret = -EINVAL;
goto err0;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -EINVAL;
goto err0;
}
/* map memory, let base point to the STI instance */
p->base = ioremap_nocache(res->start, resource_size(res));
if (p->base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
ret = -ENXIO;
goto err0;
}
/* get hold of clock */
p->clk = clk_get(&pdev->dev, "sclk");
if (IS_ERR(p->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err1;
}
if (request_irq(irq, em_sti_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
dev_name(&pdev->dev), p)) {
dev_err(&pdev->dev, "failed to request low IRQ\n");
ret = -ENOENT;
goto err2;
}
raw_spin_lock_init(&p->lock);
em_sti_register_clockevent(p);
em_sti_register_clocksource(p);
return 0;
err2:
clk_put(p->clk);
err1:
iounmap(p->base);
err0:
kfree(p);
return ret;
}
static int em_sti_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
static const struct of_device_id em_sti_dt_ids[] = {
{ .compatible = "renesas,em-sti", },
{},
};
MODULE_DEVICE_TABLE(of, em_sti_dt_ids);
static struct platform_driver em_sti_device_driver = {
.probe = em_sti_probe,
.remove = em_sti_remove,
.driver = {
.name = "em_sti",
.of_match_table = em_sti_dt_ids,
}
};
static int __init em_sti_init(void)
{
return platform_driver_register(&em_sti_device_driver);
}
static void __exit em_sti_exit(void)
{
platform_driver_unregister(&em_sti_device_driver);
}
subsys_initcall(em_sti_init);
module_exit(em_sti_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,558 @@
/* linux/arch/arm/mach-exynos4/mct.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS4 MCT(Multi-Core Timer) support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/percpu.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/clocksource.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#define EXYNOS4_MCTREG(x) (x)
#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110)
#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200)
#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204)
#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208)
#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240)
#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244)
#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
#define EXYNOS4_MCT_L_MASK (0xffffff00)
#define MCT_L_TCNTB_OFFSET (0x00)
#define MCT_L_ICNTB_OFFSET (0x08)
#define MCT_L_TCON_OFFSET (0x20)
#define MCT_L_INT_CSTAT_OFFSET (0x30)
#define MCT_L_INT_ENB_OFFSET (0x34)
#define MCT_L_WSTAT_OFFSET (0x40)
#define MCT_G_TCON_START (1 << 8)
#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
#define MCT_G_TCON_COMP0_ENABLE (1 << 0)
#define MCT_L_TCON_INTERVAL_MODE (1 << 2)
#define MCT_L_TCON_INT_START (1 << 1)
#define MCT_L_TCON_TIMER_START (1 << 0)
#define TICK_BASE_CNT 1
enum {
MCT_INT_SPI,
MCT_INT_PPI
};
enum {
MCT_G0_IRQ,
MCT_G1_IRQ,
MCT_G2_IRQ,
MCT_G3_IRQ,
MCT_L0_IRQ,
MCT_L1_IRQ,
MCT_L2_IRQ,
MCT_L3_IRQ,
MCT_NR_IRQS,
};
static void __iomem *reg_base;
static unsigned long clk_rate;
static unsigned int mct_int_type;
static int mct_irqs[MCT_NR_IRQS];
struct mct_clock_event_device {
struct clock_event_device *evt;
unsigned long base;
char name[10];
};
static void exynos4_mct_write(unsigned int value, unsigned long offset)
{
unsigned long stat_addr;
u32 mask;
u32 i;
__raw_writel(value, reg_base + offset);
if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
switch (offset & EXYNOS4_MCT_L_MASK) {
case MCT_L_TCON_OFFSET:
mask = 1 << 3; /* L_TCON write status */
break;
case MCT_L_ICNTB_OFFSET:
mask = 1 << 1; /* L_ICNTB write status */
break;
case MCT_L_TCNTB_OFFSET:
mask = 1 << 0; /* L_TCNTB write status */
break;
default:
return;
}
} else {
switch (offset) {
case EXYNOS4_MCT_G_TCON:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 16; /* G_TCON write status */
break;
case EXYNOS4_MCT_G_COMP0_L:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 0; /* G_COMP0_L write status */
break;
case EXYNOS4_MCT_G_COMP0_U:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 1; /* G_COMP0_U write status */
break;
case EXYNOS4_MCT_G_COMP0_ADD_INCR:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
break;
case EXYNOS4_MCT_G_CNT_L:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 0; /* G_CNT_L write status */
break;
case EXYNOS4_MCT_G_CNT_U:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 1; /* G_CNT_U write status */
break;
default:
return;
}
}
/* Wait maximum 1 ms until written values are applied */
for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
if (__raw_readl(reg_base + stat_addr) & mask) {
__raw_writel(mask, reg_base + stat_addr);
return;
}
panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
}
/* Clocksource handling */
static void exynos4_mct_frc_start(u32 hi, u32 lo)
{
u32 reg;
exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
reg |= MCT_G_TCON_START;
exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
}
static cycle_t exynos4_frc_read(struct clocksource *cs)
{
unsigned int lo, hi;
u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
do {
hi = hi2;
lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L);
hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
} while (hi != hi2);
return ((cycle_t)hi << 32) | lo;
}
static void exynos4_frc_resume(struct clocksource *cs)
{
exynos4_mct_frc_start(0, 0);
}
struct clocksource mct_frc = {
.name = "mct-frc",
.rating = 400,
.read = exynos4_frc_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.resume = exynos4_frc_resume,
};
static void __init exynos4_clocksource_init(void)
{
exynos4_mct_frc_start(0, 0);
if (clocksource_register_hz(&mct_frc, clk_rate))
panic("%s: can't register clocksource\n", mct_frc.name);
}
static void exynos4_mct_comp0_stop(void)
{
unsigned int tcon;
tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
}
static void exynos4_mct_comp0_start(enum clock_event_mode mode,
unsigned long cycles)
{
unsigned int tcon;
cycle_t comp_cycle;
tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
if (mode == CLOCK_EVT_MODE_PERIODIC) {
tcon |= MCT_G_TCON_COMP0_AUTO_INC;
exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
}
comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
tcon |= MCT_G_TCON_COMP0_ENABLE;
exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
}
static int exynos4_comp_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
exynos4_mct_comp0_start(evt->mode, cycles);
return 0;
}
static void exynos4_comp_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
unsigned long cycles_per_jiffy;
exynos4_mct_comp0_stop();
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
cycles_per_jiffy =
(((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
exynos4_mct_comp0_start(mode, cycles_per_jiffy);
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_RESUME:
break;
}
}
static struct clock_event_device mct_comp_device = {
.name = "mct-comp",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.rating = 250,
.set_next_event = exynos4_comp_set_next_event,
.set_mode = exynos4_comp_set_mode,
};
static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction mct_comp_event_irq = {
.name = "mct_comp_irq",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = exynos4_mct_comp_isr,
.dev_id = &mct_comp_device,
};
static void exynos4_clockevent_init(void)
{
mct_comp_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&mct_comp_device, clk_rate,
0xf, 0xffffffff);
setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
}
#ifdef CONFIG_LOCAL_TIMERS
static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
/* Clock event handling */
static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
{
unsigned long tmp;
unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
tmp = __raw_readl(reg_base + offset);
if (tmp & mask) {
tmp &= ~mask;
exynos4_mct_write(tmp, offset);
}
}
static void exynos4_mct_tick_start(unsigned long cycles,
struct mct_clock_event_device *mevt)
{
unsigned long tmp;
exynos4_mct_tick_stop(mevt);
tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
/* update interrupt count buffer */
exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
/* enable MCT tick interrupt */
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
MCT_L_TCON_INTERVAL_MODE;
exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
}
static int exynos4_tick_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
exynos4_mct_tick_start(cycles, mevt);
return 0;
}
static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
unsigned long cycles_per_jiffy;
exynos4_mct_tick_stop(mevt);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
cycles_per_jiffy =
(((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
exynos4_mct_tick_start(cycles_per_jiffy, mevt);
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_RESUME:
break;
}
}
static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
{
struct clock_event_device *evt = mevt->evt;
/*
* This is for supporting oneshot mode.
* Mct would generate interrupt periodically
* without explicit stopping.
*/
if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
exynos4_mct_tick_stop(mevt);
/* Clear the MCT tick interrupt */
if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
return 1;
} else {
return 0;
}
}
static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
{
struct mct_clock_event_device *mevt = dev_id;
struct clock_event_device *evt = mevt->evt;
exynos4_mct_tick_clear(mevt);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction mct_tick0_event_irq = {
.name = "mct_tick0_irq",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = exynos4_mct_tick_isr,
};
static struct irqaction mct_tick1_event_irq = {
.name = "mct_tick1_irq",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = exynos4_mct_tick_isr,
};
static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
unsigned int cpu = smp_processor_id();
mevt = this_cpu_ptr(&percpu_mct_tick);
mevt->evt = evt;
mevt->base = EXYNOS4_MCT_L_BASE(cpu);
sprintf(mevt->name, "mct_tick%d", cpu);
evt->name = mevt->name;
evt->cpumask = cpumask_of(cpu);
evt->set_next_event = exynos4_tick_set_next_event;
evt->set_mode = exynos4_tick_set_mode;
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 450;
clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
0xf, 0x7fffffff);
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
if (mct_int_type == MCT_INT_SPI) {
if (cpu == 0) {
mct_tick0_event_irq.dev_id = mevt;
evt->irq = mct_irqs[MCT_L0_IRQ];
setup_irq(evt->irq, &mct_tick0_event_irq);
} else {
mct_tick1_event_irq.dev_id = mevt;
evt->irq = mct_irqs[MCT_L1_IRQ];
setup_irq(evt->irq, &mct_tick1_event_irq);
irq_set_affinity(evt->irq, cpumask_of(1));
}
} else {
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
}
return 0;
}
static void exynos4_local_timer_stop(struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
if (mct_int_type == MCT_INT_SPI)
if (cpu == 0)
remove_irq(evt->irq, &mct_tick0_event_irq);
else
remove_irq(evt->irq, &mct_tick1_event_irq);
else
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
.setup = exynos4_local_timer_setup,
.stop = exynos4_local_timer_stop,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{
struct clk *mct_clk, *tick_clk;
tick_clk = np ? of_clk_get_by_name(np, "fin_pll") :
clk_get(NULL, "fin_pll");
if (IS_ERR(tick_clk))
panic("%s: unable to determine tick clock rate\n", __func__);
clk_rate = clk_get_rate(tick_clk);
mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct");
if (IS_ERR(mct_clk))
panic("%s: unable to retrieve mct clock instance\n", __func__);
clk_prepare_enable(mct_clk);
reg_base = base;
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
#ifdef CONFIG_LOCAL_TIMERS
if (mct_int_type == MCT_INT_PPI) {
int err;
err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
exynos4_mct_tick_isr, "MCT",
&percpu_mct_tick);
WARN(err, "MCT: can't request IRQ %d (%d)\n",
mct_irqs[MCT_L0_IRQ], err);
}
local_timer_register(&exynos4_mct_tick_ops);
#endif /* CONFIG_LOCAL_TIMERS */
}
void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
{
mct_irqs[MCT_G0_IRQ] = irq_g0;
mct_irqs[MCT_L0_IRQ] = irq_l0;
mct_irqs[MCT_L1_IRQ] = irq_l1;
mct_int_type = MCT_INT_SPI;
exynos4_timer_resources(NULL, base);
exynos4_clocksource_init();
exynos4_clockevent_init();
}
static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
u32 nr_irqs, i;
mct_int_type = int_type;
/* This driver uses only one global timer interrupt */
mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
/*
* Find out the number of local irqs specified. The local
* timer irqs are specified after the four global timer
* irqs are specified.
*/
#ifdef CONFIG_OF
nr_irqs = of_irq_count(np);
#else
nr_irqs = 0;
#endif
for (i = MCT_L0_IRQ; i < nr_irqs; i++)
mct_irqs[i] = irq_of_parse_and_map(np, i);
exynos4_timer_resources(np, of_iomap(np, 0));
exynos4_clocksource_init();
exynos4_clockevent_init();
}
static void __init mct_init_spi(struct device_node *np)
{
return mct_init_dt(np, MCT_INT_SPI);
}
static void __init mct_init_ppi(struct device_node *np)
{
return mct_init_dt(np, MCT_INT_PPI);
}
CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);

186
drivers/clocksource/i8253.c Normal file
View File

@@ -0,0 +1,186 @@
/*
* i8253 PIT clocksource
*/
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/timex.h>
#include <linux/module.h>
#include <linux/i8253.h>
#include <linux/smp.h>
/*
* Protects access to I/O ports
*
* 0040-0043 : timer0, i8253 / i8254
* 0061-0061 : NMI Control Register which contains two speaker control bits.
*/
DEFINE_RAW_SPINLOCK(i8253_lock);
EXPORT_SYMBOL(i8253_lock);
#ifdef CONFIG_CLKSRC_I8253
/*
* Since the PIT overflows every tick, its not very useful
* to just read by itself. So use jiffies to emulate a free
* running counter:
*/
static cycle_t i8253_read(struct clocksource *cs)
{
static int old_count;
static u32 old_jifs;
unsigned long flags;
int count;
u32 jifs;
raw_spin_lock_irqsave(&i8253_lock, flags);
/*
* Although our caller may have the read side of jiffies_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry. (Namely, old_jifs and old_count.) So we must treat
* jiffies as volatile despite the lock. We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count (that is,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count), it cannot be newer.
*/
jifs = jiffies;
outb_p(0x00, PIT_MODE); /* latch the count ASAP */
count = inb_p(PIT_CH0); /* read the latched count */
count |= inb_p(PIT_CH0) << 8;
/* VIA686a test code... reset the latch if count > max + 1 */
if (count > PIT_LATCH) {
outb_p(0x34, PIT_MODE);
outb_p(PIT_LATCH & 0xff, PIT_CH0);
outb_p(PIT_LATCH >> 8, PIT_CH0);
count = PIT_LATCH - 1;
}
/*
* It's possible for count to appear to go the wrong way for a
* couple of reasons:
*
* 1. The timer counter underflows, but we haven't handled the
* resulting interrupt and incremented jiffies yet.
* 2. Hardware problem with the timer, not giving us continuous time,
* the counter does small "jumps" upwards on some Pentium systems,
* (see c't 95/10 page 335 for Neptun bug.)
*
* Previous attempts to handle these cases intelligently were
* buggy, so we just do the simple thing now.
*/
if (count > old_count && jifs == old_jifs)
count = old_count;
old_count = count;
old_jifs = jifs;
raw_spin_unlock_irqrestore(&i8253_lock, flags);
count = (PIT_LATCH - 1) - count;
return (cycle_t)(jifs * PIT_LATCH) + count;
}
static struct clocksource i8253_cs = {
.name = "pit",
.rating = 110,
.read = i8253_read,
.mask = CLOCKSOURCE_MASK(32),
};
int __init clocksource_i8253_init(void)
{
return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE);
}
#endif
#ifdef CONFIG_CLKEVT_I8253
/*
* Initialize the PIT timer.
*
* This is also called after resume to bring the PIT into operation again.
*/
static void init_pit_timer(enum clock_event_mode mode,
struct clock_event_device *evt)
{
raw_spin_lock(&i8253_lock);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
/* binary, mode 2, LSB/MSB, ch 0 */
outb_p(0x34, PIT_MODE);
outb_p(PIT_LATCH & 0xff , PIT_CH0); /* LSB */
outb_p(PIT_LATCH >> 8 , PIT_CH0); /* MSB */
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
if (evt->mode == CLOCK_EVT_MODE_PERIODIC ||
evt->mode == CLOCK_EVT_MODE_ONESHOT) {
outb_p(0x30, PIT_MODE);
outb_p(0, PIT_CH0);
outb_p(0, PIT_CH0);
}
break;
case CLOCK_EVT_MODE_ONESHOT:
/* One shot setup */
outb_p(0x38, PIT_MODE);
break;
case CLOCK_EVT_MODE_RESUME:
/* Nothing to do here */
break;
}
raw_spin_unlock(&i8253_lock);
}
/*
* Program the next event in oneshot mode
*
* Delta is given in PIT ticks
*/
static int pit_next_event(unsigned long delta, struct clock_event_device *evt)
{
raw_spin_lock(&i8253_lock);
outb_p(delta & 0xff , PIT_CH0); /* LSB */
outb_p(delta >> 8 , PIT_CH0); /* MSB */
raw_spin_unlock(&i8253_lock);
return 0;
}
/*
* On UP the PIT can serve all of the possible timer functions. On SMP systems
* it can be solely used for the global tick.
*/
struct clock_event_device i8253_clockevent = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC,
.set_mode = init_pit_timer,
.set_next_event = pit_next_event,
};
/*
* Initialize the conversion factor and the min/max deltas of the clock event
* structure and register the clock event source with the framework.
*/
void __init clockevent_i8253_init(bool oneshot)
{
if (oneshot)
i8253_clockevent.features |= CLOCK_EVT_FEAT_ONESHOT;
/*
* Start pit with the boot cpu mask. x86 might make it global
* when it is used as broadcast device later.
*/
i8253_clockevent.cpumask = cpumask_of(smp_processor_id());
clockevents_config_and_register(&i8253_clockevent, PIT_TICK_RATE,
0xF, 0x7FFF);
}
#endif

View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 2005-2013 Imagination Technologies Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Support for Meta per-thread timers.
*
* Meta hardware threads have 2 timers. The background timer (TXTIMER) is used
* as a free-running time base (hz clocksource), and the interrupt timer
* (TXTIMERI) is used for the timer interrupt (clock event). Both counters
* traditionally count at approximately 1MHz.
*/
#include <clocksource/metag_generic.h>
#include <linux/cpu.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <asm/clock.h>
#include <asm/hwthread.h>
#include <asm/core_reg.h>
#include <asm/metag_mem.h>
#include <asm/tbx.h>
#define HARDWARE_FREQ 1000000 /* 1MHz */
#define HARDWARE_DIV 1 /* divide by 1 = 1MHz clock */
#define HARDWARE_TO_NS_SHIFT 10 /* convert ticks to ns */
static unsigned int hwtimer_freq = HARDWARE_FREQ;
static DEFINE_PER_CPU(struct clock_event_device, local_clockevent);
static DEFINE_PER_CPU(char [11], local_clockevent_name);
static int metag_timer_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
__core_reg_set(TXTIMERI, -delta);
return 0;
}
static void metag_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_RESUME:
break;
case CLOCK_EVT_MODE_SHUTDOWN:
/* We should disable the IRQ here */
break;
case CLOCK_EVT_MODE_PERIODIC:
case CLOCK_EVT_MODE_UNUSED:
WARN_ON(1);
break;
};
}
static cycle_t metag_clocksource_read(struct clocksource *cs)
{
return __core_reg_get(TXTIMER);
}
static struct clocksource clocksource_metag = {
.name = "META",
.rating = 200,
.mask = CLOCKSOURCE_MASK(32),
.read = metag_clocksource_read,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static irqreturn_t metag_timer_interrupt(int irq, void *dummy)
{
struct clock_event_device *evt = &__get_cpu_var(local_clockevent);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction metag_timer_irq = {
.name = "META core timer",
.handler = metag_timer_interrupt,
.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU,
};
unsigned long long sched_clock(void)
{
unsigned long long ticks = __core_reg_get(TXTIMER);
return ticks << HARDWARE_TO_NS_SHIFT;
}
static void __cpuinit arch_timer_setup(unsigned int cpu)
{
unsigned int txdivtime;
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
char *name = per_cpu(local_clockevent_name, cpu);
txdivtime = __core_reg_get(TXDIVTIME);
txdivtime &= ~TXDIVTIME_DIV_BITS;
txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS);
__core_reg_set(TXDIVTIME, txdivtime);
sprintf(name, "META %d", cpu);
clk->name = name;
clk->features = CLOCK_EVT_FEAT_ONESHOT,
clk->rating = 200,
clk->shift = 12,
clk->irq = tbisig_map(TBID_SIGNUM_TRT),
clk->set_mode = metag_timer_set_mode,
clk->set_next_event = metag_timer_set_next_event,
clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift);
clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk);
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
clk->cpumask = cpumask_of(cpu);
clockevents_register_device(clk);
/*
* For all non-boot CPUs we need to synchronize our free
* running clock (TXTIMER) with the boot CPU's clock.
*
* While this won't be accurate, it should be close enough.
*/
if (cpu) {
unsigned int thread0 = cpu_2_hwthread_id[0];
unsigned long val;
val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0);
__core_reg_set(TXTIMER, val);
}
}
static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
int cpu = (long)hcpu;
switch (action) {
case CPU_STARTING:
case CPU_STARTING_FROZEN:
arch_timer_setup(cpu);
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
.notifier_call = arch_timer_cpu_notify,
};
int __init metag_generic_timer_init(void)
{
/*
* On Meta 2 SoCs, the actual frequency of the timer is based on the
* Meta core clock speed divided by an integer, so it is only
* approximately 1MHz. Calculating the real frequency here drastically
* reduces clock skew on these SoCs.
*/
#ifdef CONFIG_METAG_META21
hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1);
#endif
clocksource_register_hz(&clocksource_metag, hwtimer_freq);
setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq);
/* Configure timer on boot CPU */
arch_timer_setup(smp_processor_id());
/* Hook cpu boot to configure other CPU's timers */
register_cpu_notifier(&arch_timer_cpu_nb);
return 0;
}

View File

@@ -0,0 +1,73 @@
/*
* Generic MMIO clocksource support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clocksource.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
struct clocksource_mmio {
void __iomem *reg;
struct clocksource clksrc;
};
static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *c)
{
return container_of(c, struct clocksource_mmio, clksrc);
}
cycle_t clocksource_mmio_readl_up(struct clocksource *c)
{
return readl_relaxed(to_mmio_clksrc(c)->reg);
}
cycle_t clocksource_mmio_readl_down(struct clocksource *c)
{
return ~readl_relaxed(to_mmio_clksrc(c)->reg);
}
cycle_t clocksource_mmio_readw_up(struct clocksource *c)
{
return readw_relaxed(to_mmio_clksrc(c)->reg);
}
cycle_t clocksource_mmio_readw_down(struct clocksource *c)
{
return ~(unsigned)readw_relaxed(to_mmio_clksrc(c)->reg);
}
/**
* clocksource_mmio_init - Initialize a simple mmio based clocksource
* @base: Virtual address of the clock readout register
* @name: Name of the clocksource
* @hz: Frequency of the clocksource in Hz
* @rating: Rating of the clocksource
* @bits: Number of valid bits
* @read: One of clocksource_mmio_read*() above
*/
int __init clocksource_mmio_init(void __iomem *base, const char *name,
unsigned long hz, int rating, unsigned bits,
cycle_t (*read)(struct clocksource *))
{
struct clocksource_mmio *cs;
if (bits > 32 || bits < 16)
return -EINVAL;
cs = kzalloc(sizeof(struct clocksource_mmio), GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->reg = base;
cs->clksrc.name = name;
cs->clksrc.rating = rating;
cs->clksrc.read = read;
cs->clksrc.mask = CLOCKSOURCE_MASK(bits);
cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&cs->clksrc, hz);
}

View File

@@ -0,0 +1,304 @@
/*
* Copyright (C) 2000-2001 Deep Blue Solutions
* Copyright (C) 2002 Shane Nay (shane@minirl.com)
* Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com)
* Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
* Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
*
* 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/stmp_device.h>
#include <asm/mach/time.h>
#include <asm/sched_clock.h>
/*
* There are 2 versions of the timrot on Freescale MXS-based SoCs.
* The v1 on MX23 only gets 16 bits counter, while v2 on MX28
* extends the counter to 32 bits.
*
* The implementation uses two timers, one for clock_event and
* another for clocksource. MX28 uses timrot 0 and 1, while MX23
* uses 0 and 2.
*/
#define MX23_TIMROT_VERSION_OFFSET 0x0a0
#define MX28_TIMROT_VERSION_OFFSET 0x120
#define BP_TIMROT_MAJOR_VERSION 24
#define BV_TIMROT_VERSION_1 0x01
#define BV_TIMROT_VERSION_2 0x02
#define timrot_is_v1() (timrot_major_version == BV_TIMROT_VERSION_1)
/*
* There are 4 registers for each timrotv2 instance, and 2 registers
* for each timrotv1. So address step 0x40 in macros below strides
* one instance of timrotv2 while two instances of timrotv1.
*
* As the result, HW_TIMROT_XXXn(1) defines the address of timrot1
* on MX28 while timrot2 on MX23.
*/
/* common between v1 and v2 */
#define HW_TIMROT_ROTCTRL 0x00
#define HW_TIMROT_TIMCTRLn(n) (0x20 + (n) * 0x40)
/* v1 only */
#define HW_TIMROT_TIMCOUNTn(n) (0x30 + (n) * 0x40)
/* v2 only */
#define HW_TIMROT_RUNNING_COUNTn(n) (0x30 + (n) * 0x40)
#define HW_TIMROT_FIXED_COUNTn(n) (0x40 + (n) * 0x40)
#define BM_TIMROT_TIMCTRLn_RELOAD (1 << 6)
#define BM_TIMROT_TIMCTRLn_UPDATE (1 << 7)
#define BM_TIMROT_TIMCTRLn_IRQ_EN (1 << 14)
#define BM_TIMROT_TIMCTRLn_IRQ (1 << 15)
#define BP_TIMROT_TIMCTRLn_SELECT 0
#define BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL 0x8
#define BV_TIMROTv2_TIMCTRLn_SELECT__32KHZ_XTAL 0xb
#define BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS 0xf
static struct clock_event_device mxs_clockevent_device;
static enum clock_event_mode mxs_clockevent_mode = CLOCK_EVT_MODE_UNUSED;
static void __iomem *mxs_timrot_base;
static u32 timrot_major_version;
static inline void timrot_irq_disable(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR);
}
static inline void timrot_irq_enable(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_SET);
}
static void timrot_irq_acknowledge(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR);
}
static cycle_t timrotv1_get_cycles(struct clocksource *cs)
{
return ~((__raw_readl(mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1))
& 0xffff0000) >> 16);
}
static int timrotv1_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
/* timrot decrements the count */
__raw_writel(evt, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(0));
return 0;
}
static int timrotv2_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
/* timrot decrements the count */
__raw_writel(evt, mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(0));
return 0;
}
static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
timrot_irq_acknowledge();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction mxs_timer_irq = {
.name = "MXS Timer Tick",
.dev_id = &mxs_clockevent_device,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = mxs_timer_interrupt,
};
#ifdef DEBUG
static const char *clock_event_mode_label[] const = {
[CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC",
[CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT",
[CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN",
[CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED"
};
#endif /* DEBUG */
static void mxs_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
/* Disable interrupt in timer module */
timrot_irq_disable();
if (mode != mxs_clockevent_mode) {
/* Set event time into the furthest future */
if (timrot_is_v1())
__raw_writel(0xffff,
mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1));
else
__raw_writel(0xffffffff,
mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1));
/* Clear pending interrupt */
timrot_irq_acknowledge();
}
#ifdef DEBUG
pr_info("%s: changing mode from %s to %s\n", __func__,
clock_event_mode_label[mxs_clockevent_mode],
clock_event_mode_label[mode]);
#endif /* DEBUG */
/* Remember timer mode */
mxs_clockevent_mode = mode;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
pr_err("%s: Periodic mode is not implemented\n", __func__);
break;
case CLOCK_EVT_MODE_ONESHOT:
timrot_irq_enable();
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_RESUME:
/* Left event sources disabled, no more interrupts appear */
break;
}
}
static struct clock_event_device mxs_clockevent_device = {
.name = "mxs_timrot",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_mode = mxs_set_mode,
.set_next_event = timrotv2_set_next_event,
.rating = 200,
};
static int __init mxs_clockevent_init(struct clk *timer_clk)
{
if (timrot_is_v1())
mxs_clockevent_device.set_next_event = timrotv1_set_next_event;
mxs_clockevent_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&mxs_clockevent_device,
clk_get_rate(timer_clk),
timrot_is_v1() ? 0xf : 0x2,
timrot_is_v1() ? 0xfffe : 0xfffffffe);
return 0;
}
static struct clocksource clocksource_mxs = {
.name = "mxs_timer",
.rating = 200,
.read = timrotv1_get_cycles,
.mask = CLOCKSOURCE_MASK(16),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u32 notrace mxs_read_sched_clock_v2(void)
{
return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1));
}
static int __init mxs_clocksource_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
if (timrot_is_v1())
clocksource_register_hz(&clocksource_mxs, c);
else {
clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1),
"mxs_timer", c, 200, 32, clocksource_mmio_readl_down);
setup_sched_clock(mxs_read_sched_clock_v2, 32, c);
}
return 0;
}
static void __init mxs_timer_init(struct device_node *np)
{
struct clk *timer_clk;
int irq;
mxs_timrot_base = of_iomap(np, 0);
WARN_ON(!mxs_timrot_base);
timer_clk = of_clk_get(np, 0);
if (IS_ERR(timer_clk)) {
pr_err("%s: failed to get clk\n", __func__);
return;
}
clk_prepare_enable(timer_clk);
/*
* Initialize timers to a known state
*/
stmp_reset_block(mxs_timrot_base + HW_TIMROT_ROTCTRL);
/* get timrot version */
timrot_major_version = __raw_readl(mxs_timrot_base +
(of_device_is_compatible(np, "fsl,imx23-timrot") ?
MX23_TIMROT_VERSION_OFFSET :
MX28_TIMROT_VERSION_OFFSET));
timrot_major_version >>= BP_TIMROT_MAJOR_VERSION;
/* one for clock_event */
__raw_writel((timrot_is_v1() ?
BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL :
BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) |
BM_TIMROT_TIMCTRLn_UPDATE |
BM_TIMROT_TIMCTRLn_IRQ_EN,
mxs_timrot_base + HW_TIMROT_TIMCTRLn(0));
/* another for clocksource */
__raw_writel((timrot_is_v1() ?
BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL :
BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) |
BM_TIMROT_TIMCTRLn_RELOAD,
mxs_timrot_base + HW_TIMROT_TIMCTRLn(1));
/* set clocksource timer fixed count to the maximum */
if (timrot_is_v1())
__raw_writel(0xffff,
mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1));
else
__raw_writel(0xffffffff,
mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1));
/* init and register the timer to the framework */
mxs_clocksource_init(timer_clk);
mxs_clockevent_init(timer_clk);
/* Make irqs happen */
irq = irq_of_parse_and_map(np, 0);
setup_irq(irq, &mxs_timer_irq);
}
CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);

View File

@@ -0,0 +1,249 @@
/*
* Copyright (C) 2008 STMicroelectronics
* Copyright (C) 2010 Alessandro Rubini
* Copyright (C) 2010 Linus Walleij for ST-Ericsson
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/platform_data/clocksource-nomadik-mtu.h>
#include <asm/mach/time.h>
#include <asm/sched_clock.h>
/*
* The MTU device hosts four different counters, with 4 set of
* registers. These are register names.
*/
#define MTU_IMSC 0x00 /* Interrupt mask set/clear */
#define MTU_RIS 0x04 /* Raw interrupt status */
#define MTU_MIS 0x08 /* Masked interrupt status */
#define MTU_ICR 0x0C /* Interrupt clear register */
/* per-timer registers take 0..3 as argument */
#define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
#define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
#define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
/* bits for the control register */
#define MTU_CRn_ENA 0x80
#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
#define MTU_CRn_PRESCALE_MASK 0x0c
#define MTU_CRn_PRESCALE_1 0x00
#define MTU_CRn_PRESCALE_16 0x04
#define MTU_CRn_PRESCALE_256 0x08
#define MTU_CRn_32BITS 0x02
#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
/* Other registers are usual amba/primecell registers, currently not used */
#define MTU_ITCR 0xff0
#define MTU_ITOP 0xff4
#define MTU_PERIPH_ID0 0xfe0
#define MTU_PERIPH_ID1 0xfe4
#define MTU_PERIPH_ID2 0xfe8
#define MTU_PERIPH_ID3 0xfeC
#define MTU_PCELL0 0xff0
#define MTU_PCELL1 0xff4
#define MTU_PCELL2 0xff8
#define MTU_PCELL3 0xffC
static void __iomem *mtu_base;
static bool clkevt_periodic;
static u32 clk_prescale;
static u32 nmdk_cycle; /* write-once */
static struct delay_timer mtu_delay_timer;
#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel.
*/
static u32 notrace nomadik_read_sched_clock(void)
{
if (unlikely(!mtu_base))
return 0;
return -readl(mtu_base + MTU_VAL(0));
}
#endif
static unsigned long nmdk_timer_read_current_timer(void)
{
return ~readl_relaxed(mtu_base + MTU_VAL(0));
}
/* Clockevent device: use one-shot mode */
static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
{
writel(1 << 1, mtu_base + MTU_IMSC);
writel(evt, mtu_base + MTU_LR(1));
/* Load highest value, enable device, enable interrupts */
writel(MTU_CRn_ONESHOT | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(1));
return 0;
}
void nmdk_clkevt_reset(void)
{
if (clkevt_periodic) {
/* Timer: configure load and background-load, and fire it up */
writel(nmdk_cycle, mtu_base + MTU_LR(1));
writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
writel(MTU_CRn_PERIODIC | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(1));
writel(1 << 1, mtu_base + MTU_IMSC);
} else {
/* Generate an interrupt to start the clockevent again */
(void) nmdk_clkevt_next(nmdk_cycle, NULL);
}
}
static void nmdk_clkevt_mode(enum clock_event_mode mode,
struct clock_event_device *dev)
{
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
clkevt_periodic = true;
nmdk_clkevt_reset();
break;
case CLOCK_EVT_MODE_ONESHOT:
clkevt_periodic = false;
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
writel(0, mtu_base + MTU_IMSC);
/* disable timer */
writel(0, mtu_base + MTU_CR(1));
/* load some high default value */
writel(0xffffffff, mtu_base + MTU_LR(1));
break;
case CLOCK_EVT_MODE_RESUME:
break;
}
}
void nmdk_clksrc_reset(void)
{
/* Disable */
writel(0, mtu_base + MTU_CR(0));
/* ClockSource: configure load and background-load, and fire it up */
writel(nmdk_cycle, mtu_base + MTU_LR(0));
writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(0));
}
static void nmdk_clkevt_resume(struct clock_event_device *cedev)
{
nmdk_clkevt_reset();
nmdk_clksrc_reset();
}
static struct clock_event_device nmdk_clkevt = {
.name = "mtu_1",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.rating = 200,
.set_mode = nmdk_clkevt_mode,
.set_next_event = nmdk_clkevt_next,
.resume = nmdk_clkevt_resume,
};
/*
* IRQ Handler for timer 1 of the MTU block.
*/
static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evdev = dev_id;
writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
evdev->event_handler(evdev);
return IRQ_HANDLED;
}
static struct irqaction nmdk_timer_irq = {
.name = "Nomadik Timer Tick",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = nmdk_timer_interrupt,
.dev_id = &nmdk_clkevt,
};
void __init nmdk_timer_init(void __iomem *base, int irq)
{
unsigned long rate;
struct clk *clk0, *pclk0;
mtu_base = base;
pclk0 = clk_get_sys("mtu0", "apb_pclk");
BUG_ON(IS_ERR(pclk0));
BUG_ON(clk_prepare(pclk0) < 0);
BUG_ON(clk_enable(pclk0) < 0);
clk0 = clk_get_sys("mtu0", NULL);
BUG_ON(IS_ERR(clk0));
BUG_ON(clk_prepare(clk0) < 0);
BUG_ON(clk_enable(clk0) < 0);
/*
* Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
* for ux500.
* Use a divide-by-16 counter if the tick rate is more than 32MHz.
* At 32 MHz, the timer (with 32 bit counter) can be programmed
* to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
* with 16 gives too low timer resolution.
*/
rate = clk_get_rate(clk0);
if (rate > 32000000) {
rate /= 16;
clk_prescale = MTU_CRn_PRESCALE_16;
} else {
clk_prescale = MTU_CRn_PRESCALE_1;
}
/* Cycles for periodic mode */
nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
/* Timer 0 is the free running clocksource */
nmdk_clksrc_reset();
if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
rate, 200, 32, clocksource_mmio_readl_down))
pr_err("timer: failed to initialize clock source %s\n",
"mtu_0");
#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
setup_sched_clock(nomadik_read_sched_clock, 32, rate);
#endif
/* Timer 1 is used for events, register irq and clockevents */
setup_irq(irq, &nmdk_timer_irq);
nmdk_clkevt.cpumask = cpumask_of(0);
nmdk_clkevt.irq = irq;
clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
mtu_delay_timer.freq = rate;
register_current_timer_delay(&mtu_delay_timer);
}

View File

@@ -0,0 +1,494 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* samsung - Common hr-timer support (s3c and s5p)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <clocksource/samsung_pwm.h>
#include <asm/sched_clock.h>
/*
* Clocksource driver
*/
#define REG_TCFG0 0x00
#define REG_TCFG1 0x04
#define REG_TCON 0x08
#define REG_TINT_CSTAT 0x44
#define REG_TCNTB(chan) (0x0c + 12 * (chan))
#define REG_TCMPB(chan) (0x10 + 12 * (chan))
#define TCFG0_PRESCALER_MASK 0xff
#define TCFG0_PRESCALER1_SHIFT 8
#define TCFG1_SHIFT(x) ((x) * 4)
#define TCFG1_MUX_MASK 0xf
#define TCON_START(chan) (1 << (4 * (chan) + 0))
#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
#define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
DEFINE_SPINLOCK(samsung_pwm_lock);
EXPORT_SYMBOL(samsung_pwm_lock);
struct samsung_pwm_clocksource {
void __iomem *base;
unsigned int irq[SAMSUNG_PWM_NUM];
struct samsung_pwm_variant variant;
struct clk *timerclk;
unsigned int event_id;
unsigned int source_id;
unsigned int tcnt_max;
unsigned int tscaler_div;
unsigned int tdiv;
unsigned long clock_count_per_tick;
};
static struct samsung_pwm_clocksource pwm;
static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
{
unsigned long flags;
u8 shift = 0;
u32 reg;
if (channel >= 2)
shift = TCFG0_PRESCALER1_SHIFT;
spin_lock_irqsave(&samsung_pwm_lock, flags);
reg = readl(pwm.base + REG_TCFG0);
reg &= ~(TCFG0_PRESCALER_MASK << shift);
reg |= (prescale - 1) << shift;
writel(reg, pwm.base + REG_TCFG0);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
{
u8 shift = TCFG1_SHIFT(channel);
unsigned long flags;
u32 reg;
u8 bits;
bits = (fls(divisor) - 1) - pwm.variant.div_base;
spin_lock_irqsave(&samsung_pwm_lock, flags);
reg = readl(pwm.base + REG_TCFG1);
reg &= ~(TCFG1_MUX_MASK << shift);
reg |= bits << shift;
writel(reg, pwm.base + REG_TCFG1);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_stop(unsigned int channel)
{
unsigned long tcon;
unsigned long flags;
if (channel > 0)
++channel;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = __raw_readl(pwm.base + REG_TCON);
tcon &= ~TCON_START(channel);
__raw_writel(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
{
unsigned long tcon;
unsigned long flags;
unsigned int tcon_chan = channel;
if (tcon_chan > 0)
++tcon_chan;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = __raw_readl(pwm.base + REG_TCON);
tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
tcon |= TCON_MANUALUPDATE(tcon_chan);
__raw_writel(tcnt, pwm.base + REG_TCNTB(channel));
__raw_writel(tcnt, pwm.base + REG_TCMPB(channel));
__raw_writel(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_start(unsigned int channel, bool periodic)
{
unsigned long tcon;
unsigned long flags;
if (channel > 0)
++channel;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = __raw_readl(pwm.base + REG_TCON);
tcon &= ~TCON_MANUALUPDATE(channel);
tcon |= TCON_START(channel);
if (periodic)
tcon |= TCON_AUTORELOAD(channel);
else
tcon &= ~TCON_AUTORELOAD(channel);
__raw_writel(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static int samsung_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
/*
* This check is needed to account for internal rounding
* errors inside clockevents core, which might result in
* passing cycles = 0, which in turn would not generate any
* timer interrupt and hang the system.
*
* Another solution would be to set up the clockevent device
* with min_delta = 2, but this would unnecessarily increase
* the minimum sleep period.
*/
if (!cycles)
cycles = 1;
samsung_time_setup(pwm.event_id, cycles);
samsung_time_start(pwm.event_id, false);
return 0;
}
static void samsung_timer_resume(void)
{
/* event timer restart */
samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
samsung_time_start(pwm.event_id, true);
/* source timer restart */
samsung_time_setup(pwm.source_id, pwm.tcnt_max);
samsung_time_start(pwm.source_id, true);
}
static void samsung_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
samsung_time_stop(pwm.event_id);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
samsung_time_start(pwm.event_id, true);
break;
case CLOCK_EVT_MODE_ONESHOT:
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
break;
case CLOCK_EVT_MODE_RESUME:
samsung_timer_resume();
break;
}
}
static struct clock_event_device time_event_device = {
.name = "samsung_event_timer",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = samsung_set_next_event,
.set_mode = samsung_set_mode,
};
static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
if (pwm.variant.has_tint_cstat) {
u32 mask = (1 << pwm.event_id);
writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
}
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction samsung_clock_event_irq = {
.name = "samsung_time_irq",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = samsung_clock_event_isr,
.dev_id = &time_event_device,
};
static void __init samsung_clockevent_init(void)
{
unsigned long pclk;
unsigned long clock_rate;
unsigned int irq_number;
pclk = clk_get_rate(pwm.timerclk);
samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
pwm.clock_count_per_tick = clock_rate / HZ;
time_event_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&time_event_device,
clock_rate, 1, pwm.tcnt_max);
irq_number = pwm.irq[pwm.event_id];
setup_irq(irq_number, &samsung_clock_event_irq);
if (pwm.variant.has_tint_cstat) {
u32 mask = (1 << pwm.event_id);
writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
}
}
static void __iomem *samsung_timer_reg(void)
{
switch (pwm.source_id) {
case 0:
case 1:
case 2:
case 3:
return pwm.base + pwm.source_id * 0x0c + 0x14;
case 4:
return pwm.base + 0x40;
default:
BUG();
}
}
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel. We accept that
* this wraps around for now, since it is just a relative time
* stamp. (Inspired by U300 implementation.)
*/
static u32 notrace samsung_read_sched_clock(void)
{
void __iomem *reg = samsung_timer_reg();
if (!reg)
return 0;
return ~__raw_readl(reg);
}
static void __init samsung_clocksource_init(void)
{
void __iomem *reg = samsung_timer_reg();
unsigned long pclk;
unsigned long clock_rate;
int ret;
pclk = clk_get_rate(pwm.timerclk);
samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
samsung_time_setup(pwm.source_id, pwm.tcnt_max);
samsung_time_start(pwm.source_id, true);
setup_sched_clock(samsung_read_sched_clock,
pwm.variant.bits, clock_rate);
ret = clocksource_mmio_init(reg, "samsung_clocksource_timer",
clock_rate, 250, pwm.variant.bits,
clocksource_mmio_readl_down);
if (ret)
panic("samsung_clocksource_timer: can't register clocksource\n");
}
static void __init samsung_timer_resources(void)
{
pwm.timerclk = clk_get(NULL, "timers");
if (IS_ERR(pwm.timerclk))
panic("failed to get timers clock for timer");
clk_prepare_enable(pwm.timerclk);
pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
if (pwm.variant.bits == 16) {
pwm.tscaler_div = 25;
pwm.tdiv = 2;
} else {
pwm.tscaler_div = 2;
pwm.tdiv = 1;
}
}
/*
* PWM master driver
*/
static void __init _samsung_pwm_clocksource_init(void)
{
u8 mask;
int channel;
mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
channel = fls(mask) - 1;
if (channel < 0)
panic("failed to find PWM channel for clocksource");
pwm.source_id = channel;
mask &= ~(1 << channel);
channel = fls(mask) - 1;
if (channel < 0)
panic("failed to find PWM channel for clock event");
pwm.event_id = channel;
samsung_timer_resources();
samsung_clockevent_init();
samsung_clocksource_init();
}
void __init samsung_pwm_clocksource_init(void __iomem *base,
unsigned int *irqs, struct samsung_pwm_variant *variant)
{
pwm.base = base;
memcpy(&pwm.variant, variant, sizeof(pwm.variant));
memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));
_samsung_pwm_clocksource_init();
}
#ifdef CONFIG_CLKSRC_OF
static void __init samsung_pwm_alloc(struct device_node *np,
const struct samsung_pwm_variant *variant)
{
struct resource res;
struct property *prop;
const __be32 *cur;
u32 val;
int i;
memcpy(&pwm.variant, variant, sizeof(pwm.variant));
for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
pwm.irq[i] = irq_of_parse_and_map(np, i);
of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
if (val >= SAMSUNG_PWM_NUM) {
pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
__func__);
continue;
}
pwm.variant.output_mask |= 1 << val;
}
of_address_to_resource(np, 0, &res);
if (!request_mem_region(res.start,
resource_size(&res), "samsung-pwm")) {
pr_err("%s: failed to request IO mem region\n", __func__);
return;
}
pwm.base = ioremap(res.start, resource_size(&res));
if (!pwm.base) {
pr_err("%s: failed to map PWM registers\n", __func__);
release_mem_region(res.start, resource_size(&res));
return;
}
_samsung_pwm_clocksource_init();
}
static const struct samsung_pwm_variant s3c24xx_variant = {
.bits = 16,
.div_base = 1,
.has_tint_cstat = false,
.tclk_mask = (1 << 4),
};
static void __init s3c2410_pwm_clocksource_init(struct device_node *np)
{
samsung_pwm_alloc(np, &s3c24xx_variant);
}
CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
static const struct samsung_pwm_variant s3c64xx_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),
};
static void __init s3c64xx_pwm_clocksource_init(struct device_node *np)
{
samsung_pwm_alloc(np, &s3c64xx_variant);
}
CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
static const struct samsung_pwm_variant s5p64x0_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = 0,
};
static void __init s5p64x0_pwm_clocksource_init(struct device_node *np)
{
samsung_pwm_alloc(np, &s5p64x0_variant);
}
CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
static const struct samsung_pwm_variant s5p_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = (1 << 5),
};
static void __init s5p_pwm_clocksource_init(struct device_node *np)
{
samsung_pwm_alloc(np, &s5p_variant);
}
CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
#endif

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2006 Jim Cromie
*
* This is a clocksource driver for the Geode SCx200's 1 or 27 MHz
* high-resolution timer. The Geode SC-1100 (at least) has a buggy
* time stamp counter (TSC), which loses time unless 'idle=poll' is
* given as a boot-arg. In its absence, the Generic Timekeeping code
* will detect and de-rate the bad TSC, allowing this timer to take
* over timekeeping duties.
*
* Based on work by John Stultz, and Ted Phelps (in a 2.6.12-rc6 patch)
*
* 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/clocksource.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/scx200.h>
#define NAME "scx200_hrt"
static int mhz27;
module_param(mhz27, int, 0); /* load time only */
MODULE_PARM_DESC(mhz27, "count at 27.0 MHz (default is 1.0 MHz)");
static int ppm;
module_param(ppm, int, 0); /* load time only */
MODULE_PARM_DESC(ppm, "+-adjust to actual XO freq (ppm)");
/* HiRes Timer configuration register address */
#define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
/* and config settings */
#define HR_TMEN (1 << 0) /* timer interrupt enable */
#define HR_TMCLKSEL (1 << 1) /* 1|0 counts at 27|1 MHz */
#define HR_TM27MPD (1 << 2) /* 1 turns off input clock (power-down) */
/* The base timer frequency, * 27 if selected */
#define HRT_FREQ 1000000
static cycle_t read_hrt(struct clocksource *cs)
{
/* Read the timer value */
return (cycle_t) inl(scx200_cb_base + SCx200_TIMER_OFFSET);
}
static struct clocksource cs_hrt = {
.name = "scx200_hrt",
.rating = 250,
.read = read_hrt,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
/* mult, shift are set based on mhz27 flag */
};
static int __init init_hrt_clocksource(void)
{
u32 freq;
/* Make sure scx200 has initialized the configuration block */
if (!scx200_cb_present())
return -ENODEV;
/* Reserve the timer's ISA io-region for ourselves */
if (!request_region(scx200_cb_base + SCx200_TIMER_OFFSET,
SCx200_TIMER_SIZE,
"NatSemi SCx200 High-Resolution Timer")) {
pr_warn("unable to lock timer region\n");
return -ENODEV;
}
/* write timer config */
outb(HR_TMEN | (mhz27 ? HR_TMCLKSEL : 0),
scx200_cb_base + SCx200_TMCNFG_OFFSET);
freq = (HRT_FREQ + ppm);
if (mhz27)
freq *= 27;
pr_info("enabling scx200 high-res timer (%s MHz +%d ppm)\n", mhz27 ? "27":"1", ppm);
return clocksource_register_hz(&cs_hrt, freq);
}
module_init(init_hrt_clocksource);
MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
MODULE_DESCRIPTION("clocksource on SCx200 HiRes Timer");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,846 @@
/*
* SuperH Timer Support - CMT
*
* Copyright (C) 2008 Magnus Damm
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
struct sh_cmt_priv {
void __iomem *mapbase;
struct clk *clk;
unsigned long width; /* 16 or 32 bit version of hardware block */
unsigned long overflow_bit;
unsigned long clear_bits;
struct irqaction irqaction;
struct platform_device *pdev;
unsigned long flags;
unsigned long match_value;
unsigned long next_match_value;
unsigned long max_match_value;
unsigned long rate;
raw_spinlock_t lock;
struct clock_event_device ced;
struct clocksource cs;
unsigned long total_cycles;
bool cs_enabled;
/* callbacks for CMSTR and CMCSR access */
unsigned long (*read_control)(void __iomem *base, unsigned long offs);
void (*write_control)(void __iomem *base, unsigned long offs,
unsigned long value);
/* callbacks for CMCNT and CMCOR access */
unsigned long (*read_count)(void __iomem *base, unsigned long offs);
void (*write_count)(void __iomem *base, unsigned long offs,
unsigned long value);
};
/* Examples of supported CMT timer register layouts and I/O access widths:
*
* "16-bit counter and 16-bit control" as found on sh7263:
* CMSTR 0xfffec000 16-bit
* CMCSR 0xfffec002 16-bit
* CMCNT 0xfffec004 16-bit
* CMCOR 0xfffec006 16-bit
*
* "32-bit counter and 16-bit control" as found on sh7372, sh73a0, r8a7740:
* CMSTR 0xffca0000 16-bit
* CMCSR 0xffca0060 16-bit
* CMCNT 0xffca0064 32-bit
* CMCOR 0xffca0068 32-bit
*/
static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs)
{
return ioread16(base + (offs << 1));
}
static unsigned long sh_cmt_read32(void __iomem *base, unsigned long offs)
{
return ioread32(base + (offs << 2));
}
static void sh_cmt_write16(void __iomem *base, unsigned long offs,
unsigned long value)
{
iowrite16(value, base + (offs << 1));
}
static void sh_cmt_write32(void __iomem *base, unsigned long offs,
unsigned long value)
{
iowrite32(value, base + (offs << 2));
}
#define CMCSR 0 /* channel register */
#define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */
static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
return p->read_control(p->mapbase - cfg->channel_offset, 0);
}
static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p)
{
return p->read_control(p->mapbase, CMCSR);
}
static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p)
{
return p->read_count(p->mapbase, CMCNT);
}
static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p,
unsigned long value)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
p->write_control(p->mapbase - cfg->channel_offset, 0, value);
}
static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p,
unsigned long value)
{
p->write_control(p->mapbase, CMCSR, value);
}
static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p,
unsigned long value)
{
p->write_count(p->mapbase, CMCNT, value);
}
static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p,
unsigned long value)
{
p->write_count(p->mapbase, CMCOR, value);
}
static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
int *has_wrapped)
{
unsigned long v1, v2, v3;
int o1, o2;
o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
o2 = o1;
v1 = sh_cmt_read_cmcnt(p);
v2 = sh_cmt_read_cmcnt(p);
v3 = sh_cmt_read_cmcnt(p);
o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
*has_wrapped = o1;
return v2;
}
static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_cmt_lock, flags);
value = sh_cmt_read_cmstr(p);
if (start)
value |= 1 << cfg->timer_bit;
else
value &= ~(1 << cfg->timer_bit);
sh_cmt_write_cmstr(p, value);
raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);
}
static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
{
int k, ret;
pm_runtime_get_sync(&p->pdev->dev);
dev_pm_syscore_device(&p->pdev->dev, true);
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
goto err0;
}
/* make sure channel is disabled */
sh_cmt_start_stop_ch(p, 0);
/* configure channel, periodic mode and maximum timeout */
if (p->width == 16) {
*rate = clk_get_rate(p->clk) / 512;
sh_cmt_write_cmcsr(p, 0x43);
} else {
*rate = clk_get_rate(p->clk) / 8;
sh_cmt_write_cmcsr(p, 0x01a4);
}
sh_cmt_write_cmcor(p, 0xffffffff);
sh_cmt_write_cmcnt(p, 0);
/*
* According to the sh73a0 user's manual, as CMCNT can be operated
* only by the RCLK (Pseudo 32 KHz), there's one restriction on
* modifying CMCNT register; two RCLK cycles are necessary before
* this register is either read or any modification of the value
* it holds is reflected in the LSI's actual operation.
*
* While at it, we're supposed to clear out the CMCNT as of this
* moment, so make sure it's processed properly here. This will
* take RCLKx2 at maximum.
*/
for (k = 0; k < 100; k++) {
if (!sh_cmt_read_cmcnt(p))
break;
udelay(1);
}
if (sh_cmt_read_cmcnt(p)) {
dev_err(&p->pdev->dev, "cannot clear CMCNT\n");
ret = -ETIMEDOUT;
goto err1;
}
/* enable channel */
sh_cmt_start_stop_ch(p, 1);
return 0;
err1:
/* stop clock */
clk_disable(p->clk);
err0:
return ret;
}
static void sh_cmt_disable(struct sh_cmt_priv *p)
{
/* disable channel */
sh_cmt_start_stop_ch(p, 0);
/* disable interrupts in CMT block */
sh_cmt_write_cmcsr(p, 0);
/* stop clock */
clk_disable(p->clk);
dev_pm_syscore_device(&p->pdev->dev, false);
pm_runtime_put(&p->pdev->dev);
}
/* private flags */
#define FLAG_CLOCKEVENT (1 << 0)
#define FLAG_CLOCKSOURCE (1 << 1)
#define FLAG_REPROGRAM (1 << 2)
#define FLAG_SKIPEVENT (1 << 3)
#define FLAG_IRQCONTEXT (1 << 4)
static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
int absolute)
{
unsigned long new_match;
unsigned long value = p->next_match_value;
unsigned long delay = 0;
unsigned long now = 0;
int has_wrapped;
now = sh_cmt_get_counter(p, &has_wrapped);
p->flags |= FLAG_REPROGRAM; /* force reprogram */
if (has_wrapped) {
/* we're competing with the interrupt handler.
* -> let the interrupt handler reprogram the timer.
* -> interrupt number two handles the event.
*/
p->flags |= FLAG_SKIPEVENT;
return;
}
if (absolute)
now = 0;
do {
/* reprogram the timer hardware,
* but don't save the new match value yet.
*/
new_match = now + value + delay;
if (new_match > p->max_match_value)
new_match = p->max_match_value;
sh_cmt_write_cmcor(p, new_match);
now = sh_cmt_get_counter(p, &has_wrapped);
if (has_wrapped && (new_match > p->match_value)) {
/* we are changing to a greater match value,
* so this wrap must be caused by the counter
* matching the old value.
* -> first interrupt reprograms the timer.
* -> interrupt number two handles the event.
*/
p->flags |= FLAG_SKIPEVENT;
break;
}
if (has_wrapped) {
/* we are changing to a smaller match value,
* so the wrap must be caused by the counter
* matching the new value.
* -> save programmed match value.
* -> let isr handle the event.
*/
p->match_value = new_match;
break;
}
/* be safe: verify hardware settings */
if (now < new_match) {
/* timer value is below match value, all good.
* this makes sure we won't miss any match events.
* -> save programmed match value.
* -> let isr handle the event.
*/
p->match_value = new_match;
break;
}
/* the counter has reached a value greater
* than our new match value. and since the
* has_wrapped flag isn't set we must have
* programmed a too close event.
* -> increase delay and retry.
*/
if (delay)
delay <<= 1;
else
delay = 1;
if (!delay)
dev_warn(&p->pdev->dev, "too long delay\n");
} while (delay);
}
static void __sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta)
{
if (delta > p->max_match_value)
dev_warn(&p->pdev->dev, "delta out of range\n");
p->next_match_value = delta;
sh_cmt_clock_event_program_verify(p, 0);
}
static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta)
{
unsigned long flags;
raw_spin_lock_irqsave(&p->lock, flags);
__sh_cmt_set_next(p, delta);
raw_spin_unlock_irqrestore(&p->lock, flags);
}
static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
{
struct sh_cmt_priv *p = dev_id;
/* clear flags */
sh_cmt_write_cmcsr(p, sh_cmt_read_cmcsr(p) & p->clear_bits);
/* update clock source counter to begin with if enabled
* the wrap flag should be cleared by the timer specific
* isr before we end up here.
*/
if (p->flags & FLAG_CLOCKSOURCE)
p->total_cycles += p->match_value + 1;
if (!(p->flags & FLAG_REPROGRAM))
p->next_match_value = p->max_match_value;
p->flags |= FLAG_IRQCONTEXT;
if (p->flags & FLAG_CLOCKEVENT) {
if (!(p->flags & FLAG_SKIPEVENT)) {
if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) {
p->next_match_value = p->max_match_value;
p->flags |= FLAG_REPROGRAM;
}
p->ced.event_handler(&p->ced);
}
}
p->flags &= ~FLAG_SKIPEVENT;
if (p->flags & FLAG_REPROGRAM) {
p->flags &= ~FLAG_REPROGRAM;
sh_cmt_clock_event_program_verify(p, 1);
if (p->flags & FLAG_CLOCKEVENT)
if ((p->ced.mode == CLOCK_EVT_MODE_SHUTDOWN)
|| (p->match_value == p->next_match_value))
p->flags &= ~FLAG_REPROGRAM;
}
p->flags &= ~FLAG_IRQCONTEXT;
return IRQ_HANDLED;
}
static int sh_cmt_start(struct sh_cmt_priv *p, unsigned long flag)
{
int ret = 0;
unsigned long flags;
raw_spin_lock_irqsave(&p->lock, flags);
if (!(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
ret = sh_cmt_enable(p, &p->rate);
if (ret)
goto out;
p->flags |= flag;
/* setup timeout if no clockevent */
if ((flag == FLAG_CLOCKSOURCE) && (!(p->flags & FLAG_CLOCKEVENT)))
__sh_cmt_set_next(p, p->max_match_value);
out:
raw_spin_unlock_irqrestore(&p->lock, flags);
return ret;
}
static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag)
{
unsigned long flags;
unsigned long f;
raw_spin_lock_irqsave(&p->lock, flags);
f = p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE);
p->flags &= ~flag;
if (f && !(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
sh_cmt_disable(p);
/* adjust the timeout to maximum if only clocksource left */
if ((flag == FLAG_CLOCKEVENT) && (p->flags & FLAG_CLOCKSOURCE))
__sh_cmt_set_next(p, p->max_match_value);
raw_spin_unlock_irqrestore(&p->lock, flags);
}
static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs)
{
return container_of(cs, struct sh_cmt_priv, cs);
}
static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
{
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
unsigned long flags, raw;
unsigned long value;
int has_wrapped;
raw_spin_lock_irqsave(&p->lock, flags);
value = p->total_cycles;
raw = sh_cmt_get_counter(p, &has_wrapped);
if (unlikely(has_wrapped))
raw += p->match_value + 1;
raw_spin_unlock_irqrestore(&p->lock, flags);
return value + raw;
}
static int sh_cmt_clocksource_enable(struct clocksource *cs)
{
int ret;
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
WARN_ON(p->cs_enabled);
p->total_cycles = 0;
ret = sh_cmt_start(p, FLAG_CLOCKSOURCE);
if (!ret) {
__clocksource_updatefreq_hz(cs, p->rate);
p->cs_enabled = true;
}
return ret;
}
static void sh_cmt_clocksource_disable(struct clocksource *cs)
{
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
WARN_ON(!p->cs_enabled);
sh_cmt_stop(p, FLAG_CLOCKSOURCE);
p->cs_enabled = false;
}
static void sh_cmt_clocksource_suspend(struct clocksource *cs)
{
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
sh_cmt_stop(p, FLAG_CLOCKSOURCE);
pm_genpd_syscore_poweroff(&p->pdev->dev);
}
static void sh_cmt_clocksource_resume(struct clocksource *cs)
{
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
pm_genpd_syscore_poweron(&p->pdev->dev);
sh_cmt_start(p, FLAG_CLOCKSOURCE);
}
static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
char *name, unsigned long rating)
{
struct clocksource *cs = &p->cs;
cs->name = name;
cs->rating = rating;
cs->read = sh_cmt_clocksource_read;
cs->enable = sh_cmt_clocksource_enable;
cs->disable = sh_cmt_clocksource_disable;
cs->suspend = sh_cmt_clocksource_suspend;
cs->resume = sh_cmt_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&p->pdev->dev, "used as clock source\n");
/* Register with dummy 1 Hz value, gets updated in ->enable() */
clocksource_register_hz(cs, 1);
return 0;
}
static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced)
{
return container_of(ced, struct sh_cmt_priv, ced);
}
static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic)
{
struct clock_event_device *ced = &p->ced;
sh_cmt_start(p, FLAG_CLOCKEVENT);
/* TODO: calculate good shift from rate and counter bit width */
ced->shift = 32;
ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
ced->max_delta_ns = clockevent_delta2ns(p->max_match_value, ced);
ced->min_delta_ns = clockevent_delta2ns(0x1f, ced);
if (periodic)
sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1);
else
sh_cmt_set_next(p, p->max_match_value);
}
static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC:
case CLOCK_EVT_MODE_ONESHOT:
sh_cmt_stop(p, FLAG_CLOCKEVENT);
break;
default:
break;
}
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
dev_info(&p->pdev->dev, "used for periodic clock events\n");
sh_cmt_clock_event_start(p, 1);
break;
case CLOCK_EVT_MODE_ONESHOT:
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
sh_cmt_clock_event_start(p, 0);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
sh_cmt_stop(p, FLAG_CLOCKEVENT);
break;
default:
break;
}
}
static int sh_cmt_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
if (likely(p->flags & FLAG_IRQCONTEXT))
p->next_match_value = delta - 1;
else
sh_cmt_set_next(p, delta - 1);
return 0;
}
static void sh_cmt_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_cmt(ced)->pdev->dev);
}
static void sh_cmt_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_cmt(ced)->pdev->dev);
}
static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
char *name, unsigned long rating)
{
struct clock_event_device *ced = &p->ced;
memset(ced, 0, sizeof(*ced));
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->features |= CLOCK_EVT_FEAT_ONESHOT;
ced->rating = rating;
ced->cpumask = cpumask_of(0);
ced->set_next_event = sh_cmt_clock_event_next;
ced->set_mode = sh_cmt_clock_event_mode;
ced->suspend = sh_cmt_clock_event_suspend;
ced->resume = sh_cmt_clock_event_resume;
dev_info(&p->pdev->dev, "used for clock events\n");
clockevents_register_device(ced);
}
static int sh_cmt_register(struct sh_cmt_priv *p, char *name,
unsigned long clockevent_rating,
unsigned long clocksource_rating)
{
if (clockevent_rating)
sh_cmt_register_clockevent(p, name, clockevent_rating);
if (clocksource_rating)
sh_cmt_register_clocksource(p, name, clocksource_rating);
return 0;
}
static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
int irq, ret;
ret = -ENXIO;
memset(p, 0, sizeof(*p));
p->pdev = pdev;
if (!cfg) {
dev_err(&p->pdev->dev, "missing platform data\n");
goto err0;
}
res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
goto err0;
}
irq = platform_get_irq(p->pdev, 0);
if (irq < 0) {
dev_err(&p->pdev->dev, "failed to get irq\n");
goto err0;
}
/* map memory, let mapbase point to our channel */
p->mapbase = ioremap_nocache(res->start, resource_size(res));
if (p->mapbase == NULL) {
dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
goto err0;
}
/* request irq using setup_irq() (too early for request_irq()) */
p->irqaction.name = dev_name(&p->pdev->dev);
p->irqaction.handler = sh_cmt_interrupt;
p->irqaction.dev_id = p;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_NOBALANCING;
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "cmt_fck");
if (IS_ERR(p->clk)) {
dev_err(&p->pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err1;
}
p->read_control = sh_cmt_read16;
p->write_control = sh_cmt_write16;
if (resource_size(res) == 6) {
p->width = 16;
p->read_count = sh_cmt_read16;
p->write_count = sh_cmt_write16;
p->overflow_bit = 0x80;
p->clear_bits = ~0x80;
} else {
p->width = 32;
p->read_count = sh_cmt_read32;
p->write_count = sh_cmt_write32;
p->overflow_bit = 0x8000;
p->clear_bits = ~0xc000;
}
if (p->width == (sizeof(p->max_match_value) * 8))
p->max_match_value = ~0;
else
p->max_match_value = (1 << p->width) - 1;
p->match_value = p->max_match_value;
raw_spin_lock_init(&p->lock);
ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev),
cfg->clockevent_rating,
cfg->clocksource_rating);
if (ret) {
dev_err(&p->pdev->dev, "registration failed\n");
goto err2;
}
p->cs_enabled = false;
ret = setup_irq(irq, &p->irqaction);
if (ret) {
dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
goto err2;
}
platform_set_drvdata(pdev, p);
return 0;
err2:
clk_put(p->clk);
err1:
iounmap(p->mapbase);
err0:
return ret;
}
static int sh_cmt_probe(struct platform_device *pdev)
{
struct sh_cmt_priv *p = platform_get_drvdata(pdev);
struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (p) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
ret = sh_cmt_setup(p, pdev);
if (ret) {
kfree(p);
pm_runtime_idle(&pdev->dev);
return ret;
}
if (is_early_platform_device(pdev))
return 0;
out:
if (cfg->clockevent_rating || cfg->clocksource_rating)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0;
}
static int sh_cmt_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
static struct platform_driver sh_cmt_device_driver = {
.probe = sh_cmt_probe,
.remove = sh_cmt_remove,
.driver = {
.name = "sh_cmt",
}
};
static int __init sh_cmt_init(void)
{
return platform_driver_register(&sh_cmt_device_driver);
}
static void __exit sh_cmt_exit(void)
{
platform_driver_unregister(&sh_cmt_device_driver);
}
early_platform_init("earlytimer", &sh_cmt_device_driver);
subsys_initcall(sh_cmt_init);
module_exit(sh_cmt_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH CMT Timer Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,394 @@
/*
* SuperH Timer Support - MTU2
*
* Copyright (C) 2009 Magnus Damm
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
struct sh_mtu2_priv {
void __iomem *mapbase;
struct clk *clk;
struct irqaction irqaction;
struct platform_device *pdev;
unsigned long rate;
unsigned long periodic;
struct clock_event_device ced;
};
static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
#define TSTR -1 /* shared register */
#define TCR 0 /* channel register */
#define TMDR 1 /* channel register */
#define TIOR 2 /* channel register */
#define TIER 3 /* channel register */
#define TSR 4 /* channel register */
#define TCNT 5 /* channel register */
#define TGR 6 /* channel register */
static unsigned long mtu2_reg_offs[] = {
[TCR] = 0,
[TMDR] = 1,
[TIOR] = 2,
[TIER] = 4,
[TSR] = 5,
[TCNT] = 6,
[TGR] = 8,
};
static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
if (reg_nr == TSTR)
return ioread8(base + cfg->channel_offset);
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
return ioread16(base + offs);
else
return ioread8(base + offs);
}
static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr,
unsigned long value)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
if (reg_nr == TSTR) {
iowrite8(value, base + cfg->channel_offset);
return;
}
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
iowrite16(value, base + offs);
else
iowrite8(value, base + offs);
}
static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
value = sh_mtu2_read(p, TSTR);
if (start)
value |= 1 << cfg->timer_bit;
else
value &= ~(1 << cfg->timer_bit);
sh_mtu2_write(p, TSTR, value);
raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
}
static int sh_mtu2_enable(struct sh_mtu2_priv *p)
{
int ret;
pm_runtime_get_sync(&p->pdev->dev);
dev_pm_syscore_device(&p->pdev->dev, true);
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
return ret;
}
/* make sure channel is disabled */
sh_mtu2_start_stop_ch(p, 0);
p->rate = clk_get_rate(p->clk) / 64;
p->periodic = (p->rate + HZ/2) / HZ;
/* "Periodic Counter Operation" */
sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */
sh_mtu2_write(p, TIOR, 0);
sh_mtu2_write(p, TGR, p->periodic);
sh_mtu2_write(p, TCNT, 0);
sh_mtu2_write(p, TMDR, 0);
sh_mtu2_write(p, TIER, 0x01);
/* enable channel */
sh_mtu2_start_stop_ch(p, 1);
return 0;
}
static void sh_mtu2_disable(struct sh_mtu2_priv *p)
{
/* disable channel */
sh_mtu2_start_stop_ch(p, 0);
/* stop clock */
clk_disable(p->clk);
dev_pm_syscore_device(&p->pdev->dev, false);
pm_runtime_put(&p->pdev->dev);
}
static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
{
struct sh_mtu2_priv *p = dev_id;
/* acknowledge interrupt */
sh_mtu2_read(p, TSR);
sh_mtu2_write(p, TSR, 0xfe);
/* notify clockevent layer */
p->ced.event_handler(&p->ced);
return IRQ_HANDLED;
}
static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced)
{
return container_of(ced, struct sh_mtu2_priv, ced);
}
static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced);
int disabled = 0;
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC:
sh_mtu2_disable(p);
disabled = 1;
break;
default:
break;
}
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
dev_info(&p->pdev->dev, "used for periodic clock events\n");
sh_mtu2_enable(p);
break;
case CLOCK_EVT_MODE_UNUSED:
if (!disabled)
sh_mtu2_disable(p);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
default:
break;
}
}
static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->pdev->dev);
}
static void sh_mtu2_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->pdev->dev);
}
static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
char *name, unsigned long rating)
{
struct clock_event_device *ced = &p->ced;
int ret;
memset(ced, 0, sizeof(*ced));
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->rating = rating;
ced->cpumask = cpumask_of(0);
ced->set_mode = sh_mtu2_clock_event_mode;
ced->suspend = sh_mtu2_clock_event_suspend;
ced->resume = sh_mtu2_clock_event_resume;
dev_info(&p->pdev->dev, "used for clock events\n");
clockevents_register_device(ced);
ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) {
dev_err(&p->pdev->dev, "failed to request irq %d\n",
p->irqaction.irq);
return;
}
}
static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
unsigned long clockevent_rating)
{
if (clockevent_rating)
sh_mtu2_register_clockevent(p, name, clockevent_rating);
return 0;
}
static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
int irq, ret;
ret = -ENXIO;
memset(p, 0, sizeof(*p));
p->pdev = pdev;
if (!cfg) {
dev_err(&p->pdev->dev, "missing platform data\n");
goto err0;
}
platform_set_drvdata(pdev, p);
res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
goto err0;
}
irq = platform_get_irq(p->pdev, 0);
if (irq < 0) {
dev_err(&p->pdev->dev, "failed to get irq\n");
goto err0;
}
/* map memory, let mapbase point to our channel */
p->mapbase = ioremap_nocache(res->start, resource_size(res));
if (p->mapbase == NULL) {
dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
goto err0;
}
/* setup data for setup_irq() (too early for request_irq()) */
p->irqaction.name = dev_name(&p->pdev->dev);
p->irqaction.handler = sh_mtu2_interrupt;
p->irqaction.dev_id = p;
p->irqaction.irq = irq;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_NOBALANCING;
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "mtu2_fck");
if (IS_ERR(p->clk)) {
dev_err(&p->pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err1;
}
return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev),
cfg->clockevent_rating);
err1:
iounmap(p->mapbase);
err0:
return ret;
}
static int sh_mtu2_probe(struct platform_device *pdev)
{
struct sh_mtu2_priv *p = platform_get_drvdata(pdev);
struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (p) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
ret = sh_mtu2_setup(p, pdev);
if (ret) {
kfree(p);
platform_set_drvdata(pdev, NULL);
pm_runtime_idle(&pdev->dev);
return ret;
}
if (is_early_platform_device(pdev))
return 0;
out:
if (cfg->clockevent_rating)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0;
}
static int sh_mtu2_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent */
}
static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe,
.remove = sh_mtu2_remove,
.driver = {
.name = "sh_mtu2",
}
};
static int __init sh_mtu2_init(void)
{
return platform_driver_register(&sh_mtu2_device_driver);
}
static void __exit sh_mtu2_exit(void)
{
platform_driver_unregister(&sh_mtu2_device_driver);
}
early_platform_init("earlytimer", &sh_mtu2_device_driver);
subsys_initcall(sh_mtu2_init);
module_exit(sh_mtu2_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH MTU2 Timer Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,557 @@
/*
* SuperH Timer Support - TMU
*
* Copyright (C) 2009 Magnus Damm
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
struct sh_tmu_priv {
void __iomem *mapbase;
struct clk *clk;
struct irqaction irqaction;
struct platform_device *pdev;
unsigned long rate;
unsigned long periodic;
struct clock_event_device ced;
struct clocksource cs;
bool cs_enabled;
unsigned int enable_count;
};
static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
#define TSTR -1 /* shared register */
#define TCOR 0 /* channel register */
#define TCNT 1 /* channel register */
#define TCR 2 /* channel register */
static inline unsigned long sh_tmu_read(struct sh_tmu_priv *p, int reg_nr)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
if (reg_nr == TSTR)
return ioread8(base - cfg->channel_offset);
offs = reg_nr << 2;
if (reg_nr == TCR)
return ioread16(base + offs);
else
return ioread32(base + offs);
}
static inline void sh_tmu_write(struct sh_tmu_priv *p, int reg_nr,
unsigned long value)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
if (reg_nr == TSTR) {
iowrite8(value, base - cfg->channel_offset);
return;
}
offs = reg_nr << 2;
if (reg_nr == TCR)
iowrite16(value, base + offs);
else
iowrite32(value, base + offs);
}
static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start)
{
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_tmu_lock, flags);
value = sh_tmu_read(p, TSTR);
if (start)
value |= 1 << cfg->timer_bit;
else
value &= ~(1 << cfg->timer_bit);
sh_tmu_write(p, TSTR, value);
raw_spin_unlock_irqrestore(&sh_tmu_lock, flags);
}
static int __sh_tmu_enable(struct sh_tmu_priv *p)
{
int ret;
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
return ret;
}
/* make sure channel is disabled */
sh_tmu_start_stop_ch(p, 0);
/* maximum timeout */
sh_tmu_write(p, TCOR, 0xffffffff);
sh_tmu_write(p, TCNT, 0xffffffff);
/* configure channel to parent clock / 4, irq off */
p->rate = clk_get_rate(p->clk) / 4;
sh_tmu_write(p, TCR, 0x0000);
/* enable channel */
sh_tmu_start_stop_ch(p, 1);
return 0;
}
static int sh_tmu_enable(struct sh_tmu_priv *p)
{
if (p->enable_count++ > 0)
return 0;
pm_runtime_get_sync(&p->pdev->dev);
dev_pm_syscore_device(&p->pdev->dev, true);
return __sh_tmu_enable(p);
}
static void __sh_tmu_disable(struct sh_tmu_priv *p)
{
/* disable channel */
sh_tmu_start_stop_ch(p, 0);
/* disable interrupts in TMU block */
sh_tmu_write(p, TCR, 0x0000);
/* stop clock */
clk_disable(p->clk);
}
static void sh_tmu_disable(struct sh_tmu_priv *p)
{
if (WARN_ON(p->enable_count == 0))
return;
if (--p->enable_count > 0)
return;
__sh_tmu_disable(p);
dev_pm_syscore_device(&p->pdev->dev, false);
pm_runtime_put(&p->pdev->dev);
}
static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta,
int periodic)
{
/* stop timer */
sh_tmu_start_stop_ch(p, 0);
/* acknowledge interrupt */
sh_tmu_read(p, TCR);
/* enable interrupt */
sh_tmu_write(p, TCR, 0x0020);
/* reload delta value in case of periodic timer */
if (periodic)
sh_tmu_write(p, TCOR, delta);
else
sh_tmu_write(p, TCOR, 0xffffffff);
sh_tmu_write(p, TCNT, delta);
/* start timer */
sh_tmu_start_stop_ch(p, 1);
}
static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
{
struct sh_tmu_priv *p = dev_id;
/* disable or acknowledge interrupt */
if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT)
sh_tmu_write(p, TCR, 0x0000);
else
sh_tmu_write(p, TCR, 0x0020);
/* notify clockevent layer */
p->ced.event_handler(&p->ced);
return IRQ_HANDLED;
}
static struct sh_tmu_priv *cs_to_sh_tmu(struct clocksource *cs)
{
return container_of(cs, struct sh_tmu_priv, cs);
}
static cycle_t sh_tmu_clocksource_read(struct clocksource *cs)
{
struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
return sh_tmu_read(p, TCNT) ^ 0xffffffff;
}
static int sh_tmu_clocksource_enable(struct clocksource *cs)
{
struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
int ret;
if (WARN_ON(p->cs_enabled))
return 0;
ret = sh_tmu_enable(p);
if (!ret) {
__clocksource_updatefreq_hz(cs, p->rate);
p->cs_enabled = true;
}
return ret;
}
static void sh_tmu_clocksource_disable(struct clocksource *cs)
{
struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
if (WARN_ON(!p->cs_enabled))
return;
sh_tmu_disable(p);
p->cs_enabled = false;
}
static void sh_tmu_clocksource_suspend(struct clocksource *cs)
{
struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
if (!p->cs_enabled)
return;
if (--p->enable_count == 0) {
__sh_tmu_disable(p);
pm_genpd_syscore_poweroff(&p->pdev->dev);
}
}
static void sh_tmu_clocksource_resume(struct clocksource *cs)
{
struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
if (!p->cs_enabled)
return;
if (p->enable_count++ == 0) {
pm_genpd_syscore_poweron(&p->pdev->dev);
__sh_tmu_enable(p);
}
}
static int sh_tmu_register_clocksource(struct sh_tmu_priv *p,
char *name, unsigned long rating)
{
struct clocksource *cs = &p->cs;
cs->name = name;
cs->rating = rating;
cs->read = sh_tmu_clocksource_read;
cs->enable = sh_tmu_clocksource_enable;
cs->disable = sh_tmu_clocksource_disable;
cs->suspend = sh_tmu_clocksource_suspend;
cs->resume = sh_tmu_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(32);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&p->pdev->dev, "used as clock source\n");
/* Register with dummy 1 Hz value, gets updated in ->enable() */
clocksource_register_hz(cs, 1);
return 0;
}
static struct sh_tmu_priv *ced_to_sh_tmu(struct clock_event_device *ced)
{
return container_of(ced, struct sh_tmu_priv, ced);
}
static void sh_tmu_clock_event_start(struct sh_tmu_priv *p, int periodic)
{
struct clock_event_device *ced = &p->ced;
sh_tmu_enable(p);
clockevents_config(ced, p->rate);
if (periodic) {
p->periodic = (p->rate + HZ/2) / HZ;
sh_tmu_set_next(p, p->periodic, 1);
}
}
static void sh_tmu_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
int disabled = 0;
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC:
case CLOCK_EVT_MODE_ONESHOT:
sh_tmu_disable(p);
disabled = 1;
break;
default:
break;
}
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
dev_info(&p->pdev->dev, "used for periodic clock events\n");
sh_tmu_clock_event_start(p, 1);
break;
case CLOCK_EVT_MODE_ONESHOT:
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
sh_tmu_clock_event_start(p, 0);
break;
case CLOCK_EVT_MODE_UNUSED:
if (!disabled)
sh_tmu_disable(p);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
default:
break;
}
}
static int sh_tmu_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
/* program new delta value */
sh_tmu_set_next(p, delta, 0);
return 0;
}
static void sh_tmu_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->pdev->dev);
}
static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->pdev->dev);
}
static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
char *name, unsigned long rating)
{
struct clock_event_device *ced = &p->ced;
int ret;
memset(ced, 0, sizeof(*ced));
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->features |= CLOCK_EVT_FEAT_ONESHOT;
ced->rating = rating;
ced->cpumask = cpumask_of(0);
ced->set_next_event = sh_tmu_clock_event_next;
ced->set_mode = sh_tmu_clock_event_mode;
ced->suspend = sh_tmu_clock_event_suspend;
ced->resume = sh_tmu_clock_event_resume;
dev_info(&p->pdev->dev, "used for clock events\n");
clockevents_config_and_register(ced, 1, 0x300, 0xffffffff);
ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) {
dev_err(&p->pdev->dev, "failed to request irq %d\n",
p->irqaction.irq);
return;
}
}
static int sh_tmu_register(struct sh_tmu_priv *p, char *name,
unsigned long clockevent_rating,
unsigned long clocksource_rating)
{
if (clockevent_rating)
sh_tmu_register_clockevent(p, name, clockevent_rating);
else if (clocksource_rating)
sh_tmu_register_clocksource(p, name, clocksource_rating);
return 0;
}
static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
int irq, ret;
ret = -ENXIO;
memset(p, 0, sizeof(*p));
p->pdev = pdev;
if (!cfg) {
dev_err(&p->pdev->dev, "missing platform data\n");
goto err0;
}
platform_set_drvdata(pdev, p);
res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
goto err0;
}
irq = platform_get_irq(p->pdev, 0);
if (irq < 0) {
dev_err(&p->pdev->dev, "failed to get irq\n");
goto err0;
}
/* map memory, let mapbase point to our channel */
p->mapbase = ioremap_nocache(res->start, resource_size(res));
if (p->mapbase == NULL) {
dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
goto err0;
}
/* setup data for setup_irq() (too early for request_irq()) */
p->irqaction.name = dev_name(&p->pdev->dev);
p->irqaction.handler = sh_tmu_interrupt;
p->irqaction.dev_id = p;
p->irqaction.irq = irq;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_NOBALANCING;
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "tmu_fck");
if (IS_ERR(p->clk)) {
dev_err(&p->pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err1;
}
p->cs_enabled = false;
p->enable_count = 0;
return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev),
cfg->clockevent_rating,
cfg->clocksource_rating);
err1:
iounmap(p->mapbase);
err0:
return ret;
}
static int sh_tmu_probe(struct platform_device *pdev)
{
struct sh_tmu_priv *p = platform_get_drvdata(pdev);
struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (p) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
ret = sh_tmu_setup(p, pdev);
if (ret) {
kfree(p);
platform_set_drvdata(pdev, NULL);
pm_runtime_idle(&pdev->dev);
return ret;
}
if (is_early_platform_device(pdev))
return 0;
out:
if (cfg->clockevent_rating || cfg->clocksource_rating)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0;
}
static int sh_tmu_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
static struct platform_driver sh_tmu_device_driver = {
.probe = sh_tmu_probe,
.remove = sh_tmu_remove,
.driver = {
.name = "sh_tmu",
}
};
static int __init sh_tmu_init(void)
{
return platform_driver_register(&sh_tmu_device_driver);
}
static void __exit sh_tmu_exit(void)
{
platform_driver_unregister(&sh_tmu_device_driver);
}
early_platform_init("earlytimer", &sh_tmu_device_driver);
subsys_initcall(sh_tmu_init);
module_exit(sh_tmu_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH TMU Timer Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,148 @@
/*
* Allwinner A1X SoCs timer handling.
*
* Copyright (C) 2012 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* Based on code from
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Benn Huang <benn@allwinnertech.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>
#define TIMER_IRQ_EN_REG 0x00
#define TIMER_IRQ_EN(val) (1 << 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
static void __iomem *timer_base;
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));
break;
case CLOCK_EVT_MODE_ONESHOT:
writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0));
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
default:
writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0));
break;
}
}
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));
return 0;
}
static struct clock_event_device sun4i_clockevent = {
.name = "sun4i_tick",
.rating = 300,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_mode = sun4i_clkevt_mode,
.set_next_event = sun4i_clkevt_next_event,
};
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
writel(0x1, timer_base + TIMER_IRQ_ST_REG);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction sun4i_timer_irq = {
.name = "sun4i_timer0",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = sun4i_timer_interrupt,
.dev_id = &sun4i_clockevent,
};
static void __init sun4i_timer_init(struct device_node *node)
{
unsigned long rate = 0;
struct clk *clk;
int ret, irq;
u32 val;
timer_base = of_iomap(node, 0);
if (!timer_base)
panic("Can't map registers");
irq = irq_of_parse_and_map(node, 0);
if (irq <= 0)
panic("Can't parse IRQ");
clk = of_clk_get(node, 0);
if (IS_ERR(clk))
panic("Can't get timer clock");
rate = clk_get_rate(clk);
writel(rate / (TIMER_SCAL * HZ),
timer_base + TIMER_INTVAL_REG(0));
/* 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));
/* set mode to auto reload */
val = readl(timer_base + TIMER_CTL_REG(0));
writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0));
ret = setup_irq(irq, &sun4i_timer_irq);
if (ret)
pr_warn("failed to setup irq %d\n", irq);
/* Enable timer0 interrupt */
val = readl(timer_base + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
sun4i_clockevent.cpumask = cpumask_of(0);
clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL,
0x1, 0xff);
}
CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
sun4i_timer_init);

View File

@@ -0,0 +1,329 @@
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/atmel_tc.h>
/*
* We're configured to use a specific TC block, one that's not hooked
* up to external hardware, to provide a time solution:
*
* - Two channels combine to create a free-running 32 bit counter
* with a base rate of 5+ MHz, packaged as a clocksource (with
* resolution better than 200 nsec).
* - Some chips support 32 bit counter. A single channel is used for
* this 32 bit free-running counter. the second channel is not used.
*
* - The third channel may be used to provide a 16-bit clockevent
* source, used in either periodic or oneshot mode. This runs
* at 32 KiHZ, and can handle delays of up to two seconds.
*
* A boot clocksource and clockevent source are also currently needed,
* unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so
* this code can be used when init_timers() is called, well before most
* devices are set up. (Some low end AT91 parts, which can run uClinux,
* have only the timers in one TC block... they currently don't support
* the tclib code, because of that initialization issue.)
*
* REVISIT behavior during system suspend states... we should disable
* all clocks and save the power. Easily done for clockevent devices,
* but clocksources won't necessarily get the needed notifications.
* For deeper system sleep states, this will be mandatory...
*/
static void __iomem *tcaddr;
static cycle_t tc_get_cycles(struct clocksource *cs)
{
unsigned long flags;
u32 lower, upper;
raw_local_irq_save(flags);
do {
upper = __raw_readl(tcaddr + ATMEL_TC_REG(1, CV));
lower = __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
} while (upper != __raw_readl(tcaddr + ATMEL_TC_REG(1, CV)));
raw_local_irq_restore(flags);
return (upper << 16) | lower;
}
static cycle_t tc_get_cycles32(struct clocksource *cs)
{
return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
}
static struct clocksource clksrc = {
.name = "tcb_clksrc",
.rating = 200,
.read = tc_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_GENERIC_CLOCKEVENTS
struct tc_clkevt_device {
struct clock_event_device clkevt;
struct clk *clk;
void __iomem *regs;
};
static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
{
return container_of(clkevt, struct tc_clkevt_device, clkevt);
}
/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
* because using one of the divided clocks would usually mean the
* tick rate can never be less than several dozen Hz (vs 0.5 Hz).
*
* A divided clock could be good for high resolution timers, since
* 30.5 usec resolution can seem "low".
*/
static u32 timer_clock;
static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
{
struct tc_clkevt_device *tcd = to_tc_clkevt(d);
void __iomem *regs = tcd->regs;
if (tcd->clkevt.mode == CLOCK_EVT_MODE_PERIODIC
|| tcd->clkevt.mode == CLOCK_EVT_MODE_ONESHOT) {
__raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR));
__raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
clk_disable(tcd->clk);
}
switch (m) {
/* By not making the gentime core emulate periodic mode on top
* of oneshot, we get lower overhead and improved accuracy.
*/
case CLOCK_EVT_MODE_PERIODIC:
clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and restart */
__raw_writel(timer_clock
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
regs + ATMEL_TC_REG(2, CMR));
__raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
/* Enable clock and interrupts on RC compare */
__raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
/* go go gadget! */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
regs + ATMEL_TC_REG(2, CCR));
break;
case CLOCK_EVT_MODE_ONESHOT:
clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and stop */
__raw_writel(timer_clock | ATMEL_TC_CPCSTOP
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
regs + ATMEL_TC_REG(2, CMR));
__raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
/* set_next_event() configures and starts the timer */
break;
default:
break;
}
}
static int tc_next_event(unsigned long delta, struct clock_event_device *d)
{
__raw_writel(delta, tcaddr + ATMEL_TC_REG(2, RC));
/* go go gadget! */
__raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
tcaddr + ATMEL_TC_REG(2, CCR));
return 0;
}
static struct tc_clkevt_device clkevt = {
.clkevt = {
.name = "tc_clkevt",
.features = CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT,
/* Should be lower than at91rm9200's system timer */
.rating = 125,
.set_next_event = tc_next_event,
.set_mode = tc_mode,
},
};
static irqreturn_t ch2_irq(int irq, void *handle)
{
struct tc_clkevt_device *dev = handle;
unsigned int sr;
sr = __raw_readl(dev->regs + ATMEL_TC_REG(2, SR));
if (sr & ATMEL_TC_CPCS) {
dev->clkevt.event_handler(&dev->clkevt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static struct irqaction tc_irqaction = {
.name = "tc_clkevt",
.flags = IRQF_TIMER | IRQF_DISABLED,
.handler = ch2_irq,
};
static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
{
struct clk *t2_clk = tc->clk[2];
int irq = tc->irq[2];
clkevt.regs = tc->regs;
clkevt.clk = t2_clk;
tc_irqaction.dev_id = &clkevt;
timer_clock = clk32k_divisor_idx;
clkevt.clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
setup_irq(irq, &tc_irqaction);
}
#else /* !CONFIG_GENERIC_CLOCKEVENTS */
static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
{
/* NOTHING */
}
#endif
static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
__raw_writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP /* free-run */
| ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
| ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
tcaddr + ATMEL_TC_REG(0, CMR));
__raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
__raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
/* channel 1: waveform mode, input TIOA0 */
__raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(1, CMR));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
/* chain channel 0 to channel 1*/
__raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
/* then reset all the timers */
__raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}
static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8 */
__raw_writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(0, CMR));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
/* then reset all the timers */
__raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}
static int __init tcb_clksrc_init(void)
{
static char bootinfo[] __initdata
= KERN_DEBUG "%s: tc%d at %d.%03d MHz\n";
struct platform_device *pdev;
struct atmel_tc *tc;
struct clk *t0_clk;
u32 rate, divided_rate = 0;
int best_divisor_idx = -1;
int clk32k_divisor_idx = -1;
int i;
tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name);
if (!tc) {
pr_debug("can't alloc TC for clocksource\n");
return -ENODEV;
}
tcaddr = tc->regs;
pdev = tc->pdev;
t0_clk = tc->clk[0];
clk_enable(t0_clk);
/* How fast will we be counting? Pick something over 5 MHz. */
rate = (u32) clk_get_rate(t0_clk);
for (i = 0; i < 5; i++) {
unsigned divisor = atmel_tc_divisors[i];
unsigned tmp;
/* remember 32 KiHz clock for later */
if (!divisor) {
clk32k_divisor_idx = i;
continue;
}
tmp = rate / divisor;
pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
if (best_divisor_idx > 0) {
if (tmp < 5 * 1000 * 1000)
continue;
}
divided_rate = tmp;
best_divisor_idx = i;
}
printk(bootinfo, clksrc.name, CONFIG_ATMEL_TCB_CLKSRC_BLOCK,
divided_rate / 1000000,
((divided_rate + 500000) % 1000000) / 1000);
if (tc->tcb_config && tc->tcb_config->counter_width == 32) {
/* use apropriate function to read 32 bit counter */
clksrc.read = tc_get_cycles32;
/* setup ony channel 0 */
tcb_setup_single_chan(tc, best_divisor_idx);
} else {
/* tclib will give us three clocks no matter what the
* underlying platform supports.
*/
clk_enable(tc->clk[1]);
/* setup both channel 0 & 1 */
tcb_setup_dual_chan(tc, best_divisor_idx);
}
/* and away we go! */
clocksource_register_hz(&clksrc, divided_rate);
/* channel 2: periodic and oneshot timer support */
setup_clkevents(tc, clk32k_divisor_idx);
return 0;
}
arch_initcall(tcb_clksrc_init);

View File

@@ -0,0 +1,263 @@
/*
* Copyright (C) 2010 Google, Inc.
*
* Author:
* Colin Cross <ccross@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/time.h>
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
#define RTC_SECONDS 0x08
#define RTC_SHADOW_SECONDS 0x0c
#define RTC_MILLISECONDS 0x10
#define TIMERUS_CNTR_1US 0x10
#define TIMERUS_USEC_CFG 0x14
#define TIMERUS_CNTR_FREEZE 0x4c
#define TIMER1_BASE 0x0
#define TIMER2_BASE 0x8
#define TIMER3_BASE 0x50
#define TIMER4_BASE 0x58
#define TIMER_PTV 0x0
#define TIMER_PCR 0x4
static void __iomem *timer_reg_base;
static void __iomem *rtc_base;
static struct timespec persistent_ts;
static u64 persistent_ms, last_persistent_ms;
#define timer_writel(value, reg) \
__raw_writel(value, timer_reg_base + (reg))
#define timer_readl(reg) \
__raw_readl(timer_reg_base + (reg))
static int tegra_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
u32 reg;
reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0);
timer_writel(reg, TIMER3_BASE + TIMER_PTV);
return 0;
}
static void tegra_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
u32 reg;
timer_writel(0, TIMER3_BASE + TIMER_PTV);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
reg = 0xC0000000 | ((1000000/HZ)-1);
timer_writel(reg, TIMER3_BASE + TIMER_PTV);
break;
case CLOCK_EVT_MODE_ONESHOT:
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_RESUME:
break;
}
}
static struct clock_event_device tegra_clockevent = {
.name = "timer0",
.rating = 300,
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.set_next_event = tegra_timer_set_next_event,
.set_mode = tegra_timer_set_mode,
};
static u32 notrace tegra_read_sched_clock(void)
{
return timer_readl(TIMERUS_CNTR_1US);
}
/*
* tegra_rtc_read - Reads the Tegra RTC registers
* Care must be taken that this funciton is not called while the
* tegra_rtc driver could be executing to avoid race conditions
* on the RTC shadow register
*/
static u64 tegra_rtc_read_ms(void)
{
u32 ms = readl(rtc_base + RTC_MILLISECONDS);
u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
return (u64)s * MSEC_PER_SEC + ms;
}
/*
* tegra_read_persistent_clock - Return time from a persistent clock.
*
* Reads the time from a source which isn't disabled during PM, the
* 32k sync timer. Convert the cycles elapsed since last read into
* nsecs and adds to a monotonically increasing timespec.
* Care must be taken that this funciton is not called while the
* tegra_rtc driver could be executing to avoid race conditions
* on the RTC shadow register
*/
static void tegra_read_persistent_clock(struct timespec *ts)
{
u64 delta;
struct timespec *tsp = &persistent_ts;
last_persistent_ms = persistent_ms;
persistent_ms = tegra_rtc_read_ms();
delta = persistent_ms - last_persistent_ms;
timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
*ts = *tsp;
}
static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
timer_writel(1<<30, TIMER3_BASE + TIMER_PCR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction tegra_timer_irq = {
.name = "timer0",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH,
.handler = tegra_timer_interrupt,
.dev_id = &tegra_clockevent,
};
static void __init tegra20_init_timer(struct device_node *np)
{
struct clk *clk;
unsigned long rate;
int ret;
timer_reg_base = of_iomap(np, 0);
if (!timer_reg_base) {
pr_err("Can't map timer registers\n");
BUG();
}
tegra_timer_irq.irq = irq_of_parse_and_map(np, 2);
if (tegra_timer_irq.irq <= 0) {
pr_err("Failed to map timer IRQ\n");
BUG();
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");
rate = 12000000;
} else {
clk_prepare_enable(clk);
rate = clk_get_rate(clk);
}
of_node_put(np);
switch (rate) {
case 12000000:
timer_writel(0x000b, TIMERUS_USEC_CFG);
break;
case 13000000:
timer_writel(0x000c, TIMERUS_USEC_CFG);
break;
case 19200000:
timer_writel(0x045f, TIMERUS_USEC_CFG);
break;
case 26000000:
timer_writel(0x0019, TIMERUS_USEC_CFG);
break;
default:
WARN(1, "Unknown clock rate");
}
pr_info("Initial usec timer %u\n", tegra_read_sched_clock());
setup_sched_clock(tegra_read_sched_clock, 32, 1000000);
if (clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US,
"timer_us", 1000000, 300, 32, clocksource_mmio_readl_up)) {
pr_err("Failed to register clocksource\n");
BUG();
}
ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq);
if (ret) {
pr_err("Failed to register timer IRQ: %d\n", ret);
BUG();
}
tegra_clockevent.cpumask = cpu_all_mask;
tegra_clockevent.irq = tegra_timer_irq.irq;
clockevents_config_and_register(&tegra_clockevent, 1000000,
0x1, 0x1fffffff);
}
CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
static void __init tegra20_init_rtc(struct device_node *np)
{
struct clk *clk;
rtc_base = of_iomap(np, 0);
if (!rtc_base) {
pr_err("Can't map RTC registers");
BUG();
}
/*
* rtc registers are used by read_persistent_clock, keep the rtc clock
* enabled
*/
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
pr_warn("Unable to get rtc-tegra clock\n");
else
clk_prepare_enable(clk);
of_node_put(np);
register_persistent_clock(NULL, tegra_read_persistent_clock);
}
CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
#ifdef CONFIG_PM
static u32 usec_config;
void tegra_timer_suspend(void)
{
usec_config = timer_readl(TIMERUS_USEC_CFG);
}
void tegra_timer_resume(void)
{
timer_writel(usec_config, TIMERUS_USEC_CFG);
}
#endif

View File

@@ -0,0 +1,301 @@
/*
* Marvell Armada 370/XP SoC timer handling.
*
* Copyright (C) 2012 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.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.
*
* Timer 0 is used as free-running clocksource, while timer 1 is
* used as clock_event_device.
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <asm/sched_clock.h>
#include <asm/localtimer.h>
#include <linux/percpu.h>
/*
* Timer block registers.
*/
#define TIMER_CTRL_OFF 0x0000
#define TIMER0_EN 0x0001
#define TIMER0_RELOAD_EN 0x0002
#define TIMER0_25MHZ 0x0800
#define TIMER0_DIV(div) ((div) << 19)
#define TIMER1_EN 0x0004
#define TIMER1_RELOAD_EN 0x0008
#define TIMER1_25MHZ 0x1000
#define TIMER1_DIV(div) ((div) << 22)
#define TIMER_EVENTS_STATUS 0x0004
#define TIMER0_CLR_MASK (~0x1)
#define TIMER1_CLR_MASK (~0x100)
#define TIMER0_RELOAD_OFF 0x0010
#define TIMER0_VAL_OFF 0x0014
#define TIMER1_RELOAD_OFF 0x0018
#define TIMER1_VAL_OFF 0x001c
#define LCL_TIMER_EVENTS_STATUS 0x0028
/* Global timers are connected to the coherency fabric clock, and the
below divider reduces their incrementing frequency. */
#define TIMER_DIVIDER_SHIFT 5
#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT)
/*
* SoC-specific data.
*/
static void __iomem *timer_base, *local_base;
static unsigned int timer_clk;
static bool timer25Mhz = true;
/*
* Number of timer ticks per jiffy.
*/
static u32 ticks_per_jiffy;
static struct clock_event_device __percpu **percpu_armada_370_xp_evt;
static u32 notrace armada_370_xp_read_sched_clock(void)
{
return ~readl(timer_base + TIMER0_VAL_OFF);
}
/*
* Clockevent handling.
*/
static int
armada_370_xp_clkevt_next_event(unsigned long delta,
struct clock_event_device *dev)
{
u32 u;
/*
* Clear clockevent timer interrupt.
*/
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
/*
* Setup new clockevent timer value.
*/
writel(delta, local_base + TIMER0_VAL_OFF);
/*
* Enable the timer.
*/
u = readl(local_base + TIMER_CTRL_OFF);
u = ((u & ~TIMER0_RELOAD_EN) | TIMER0_EN |
TIMER0_DIV(TIMER_DIVIDER_SHIFT));
writel(u, local_base + TIMER_CTRL_OFF);
return 0;
}
static void
armada_370_xp_clkevt_mode(enum clock_event_mode mode,
struct clock_event_device *dev)
{
u32 u;
if (mode == CLOCK_EVT_MODE_PERIODIC) {
/*
* Setup timer to fire at 1/HZ intervals.
*/
writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF);
writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF);
/*
* Enable timer.
*/
u = readl(local_base + TIMER_CTRL_OFF);
writel((u | TIMER0_EN | TIMER0_RELOAD_EN |
TIMER0_DIV(TIMER_DIVIDER_SHIFT)),
local_base + TIMER_CTRL_OFF);
} else {
/*
* Disable timer.
*/
u = readl(local_base + TIMER_CTRL_OFF);
writel(u & ~TIMER0_EN, local_base + TIMER_CTRL_OFF);
/*
* ACK pending timer interrupt.
*/
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
}
}
static struct clock_event_device armada_370_xp_clkevt = {
.name = "armada_370_xp_per_cpu_tick",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.shift = 32,
.rating = 300,
.set_next_event = armada_370_xp_clkevt_next_event,
.set_mode = armada_370_xp_clkevt_mode,
};
static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
{
/*
* ACK timer interrupt and call event handler.
*/
struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
evt->event_handler(evt);
return IRQ_HANDLED;
}
/*
* Setup the local clock events for a CPU.
*/
static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt)
{
u32 u;
int cpu = smp_processor_id();
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
u = readl(local_base + TIMER_CTRL_OFF);
if (timer25Mhz)
writel(u | TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
else
writel(u & ~TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
evt->name = armada_370_xp_clkevt.name;
evt->irq = armada_370_xp_clkevt.irq;
evt->features = armada_370_xp_clkevt.features;
evt->shift = armada_370_xp_clkevt.shift;
evt->rating = armada_370_xp_clkevt.rating,
evt->set_next_event = armada_370_xp_clkevt_next_event,
evt->set_mode = armada_370_xp_clkevt_mode,
evt->cpumask = cpumask_of(cpu);
*__this_cpu_ptr(percpu_armada_370_xp_evt) = evt;
clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
enable_percpu_irq(evt->irq, 0);
return 0;
}
static void armada_370_xp_timer_stop(struct clock_event_device *evt)
{
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
disable_percpu_irq(evt->irq);
}
static struct local_timer_ops armada_370_xp_local_timer_ops __cpuinitdata = {
.setup = armada_370_xp_timer_setup,
.stop = armada_370_xp_timer_stop,
};
void __init armada_370_xp_timer_init(void)
{
u32 u;
struct device_node *np;
int res;
np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer");
timer_base = of_iomap(np, 0);
WARN_ON(!timer_base);
local_base = of_iomap(np, 1);
if (of_find_property(np, "marvell,timer-25Mhz", NULL)) {
/* The fixed 25MHz timer is available so let's use it */
u = readl(local_base + TIMER_CTRL_OFF);
writel(u | TIMER0_25MHZ,
local_base + TIMER_CTRL_OFF);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u | TIMER0_25MHZ,
timer_base + TIMER_CTRL_OFF);
timer_clk = 25000000;
} else {
unsigned long rate = 0;
struct clk *clk = of_clk_get(np, 0);
WARN_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
u = readl(local_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ),
local_base + TIMER_CTRL_OFF);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ),
timer_base + TIMER_CTRL_OFF);
timer_clk = rate / TIMER_DIVIDER;
timer25Mhz = false;
}
/*
* We use timer 0 as clocksource, and private(local) timer 0
* for clockevents
*/
armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4);
ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
/*
* Set scale and timer for sched_clock.
*/
setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk);
/*
* Setup free-running clocksource timer (interrupts
* disabled).
*/
writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
u = readl(timer_base + TIMER_CTRL_OFF);
writel((u | TIMER0_EN | TIMER0_RELOAD_EN |
TIMER0_DIV(TIMER_DIVIDER_SHIFT)), timer_base + TIMER_CTRL_OFF);
clocksource_mmio_init(timer_base + TIMER0_VAL_OFF,
"armada_370_xp_clocksource",
timer_clk, 300, 32, clocksource_mmio_readl_down);
/* Register the clockevent on the private timer of CPU 0 */
armada_370_xp_clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&armada_370_xp_clkevt,
timer_clk, 1, 0xfffffffe);
percpu_armada_370_xp_evt = alloc_percpu(struct clock_event_device *);
/*
* Setup clockevent timer (interrupt-driven).
*/
*__this_cpu_ptr(percpu_armada_370_xp_evt) = &armada_370_xp_clkevt;
res = request_percpu_irq(armada_370_xp_clkevt.irq,
armada_370_xp_timer_interrupt,
armada_370_xp_clkevt.name,
percpu_armada_370_xp_evt);
if (!res) {
enable_percpu_irq(armada_370_xp_clkevt.irq, 0);
#ifdef CONFIG_LOCAL_TIMERS
local_timer_register(&armada_370_xp_local_timer_ops);
#endif
}
}

View File

@@ -0,0 +1,299 @@
/*
* System timer for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <asm/sched_clock.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0018
#define SIRFSOC_TIMER_MATCH_1 0x001c
#define SIRFSOC_TIMER_COUNTER_0 0x0048
#define SIRFSOC_TIMER_COUNTER_1 0x004c
#define SIRFSOC_TIMER_INTR_STATUS 0x0060
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
#define SIRFSOC_TIMER_REG_CNT 6
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
SIRFSOC_TIMER_WATCHDOG_EN,
SIRFSOC_TIMER_32COUNTER_0_CTRL,
SIRFSOC_TIMER_32COUNTER_1_CTRL,
SIRFSOC_TIMER_64COUNTER_CTRL,
SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
};
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
static void __iomem *sirfsoc_timer_base;
/* disable count and interrupt */
static inline void sirfsoc_timer_count_disable(int idx)
{
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
}
/* enable count and interrupt */
static inline void sirfsoc_timer_count_enable(int idx)
{
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
}
/* timer interrupt handler */
static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
int cpu = smp_processor_id();
/* clear timer interrupt */
writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
sirfsoc_timer_count_disable(cpu);
ce->event_handler(ce);
return IRQ_HANDLED;
}
/* read 64-bit timer counter */
static cycle_t sirfsoc_timer_read(struct clocksource *cs)
{
u64 cycles;
writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
return cycles;
}
static int sirfsoc_timer_set_next_event(unsigned long delta,
struct clock_event_device *ce)
{
int cpu = smp_processor_id();
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
4 * cpu);
writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
4 * cpu);
/* enable the tick */
sirfsoc_timer_count_enable(cpu);
return 0;
}
static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *ce)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
/* enable in set_next_event */
break;
default:
break;
}
sirfsoc_timer_count_disable(smp_processor_id());
}
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
}
static struct clock_event_device sirfsoc_clockevent = {
.name = "sirfsoc_clockevent",
.rating = 200,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_mode = sirfsoc_timer_set_mode,
.set_next_event = sirfsoc_timer_set_next_event,
};
static struct clocksource sirfsoc_clocksource = {
.name = "sirfsoc_clocksource",
.rating = 200,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = sirfsoc_timer_read,
.suspend = sirfsoc_clocksource_suspend,
.resume = sirfsoc_clocksource_resume,
};
static struct irqaction sirfsoc_timer_irq = {
.name = "sirfsoc_timer0",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = sirfsoc_timer_interrupt,
.dev_id = &sirfsoc_clockevent,
};
#ifdef CONFIG_LOCAL_TIMERS
static struct irqaction sirfsoc_timer1_irq = {
.name = "sirfsoc_timer1",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = sirfsoc_timer_interrupt,
};
static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
{
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
ce->irq = sirfsoc_timer1_irq.irq;
ce->name = "local_timer";
ce->features = sirfsoc_clockevent.features;
ce->rating = sirfsoc_clockevent.rating;
ce->set_mode = sirfsoc_timer_set_mode;
ce->set_next_event = sirfsoc_timer_set_next_event;
ce->shift = sirfsoc_clockevent.shift;
ce->mult = sirfsoc_clockevent.mult;
ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
sirfsoc_timer1_irq.dev_id = ce;
BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
clockevents_register_device(ce);
return 0;
}
static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
{
sirfsoc_timer_count_disable(1);
remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
}
static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
.setup = sirfsoc_local_timer_setup,
.stop = sirfsoc_local_timer_stop,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init sirfsoc_clockevent_init(void)
{
clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
sirfsoc_clockevent.max_delta_ns =
clockevent_delta2ns(-2, &sirfsoc_clockevent);
sirfsoc_clockevent.min_delta_ns =
clockevent_delta2ns(2, &sirfsoc_clockevent);
sirfsoc_clockevent.cpumask = cpumask_of(0);
clockevents_register_device(&sirfsoc_clockevent);
#ifdef CONFIG_LOCAL_TIMERS
local_timer_register(&sirfsoc_local_timer_ops);
#endif
}
/* initialize the kernel jiffy timer source */
static void __init sirfsoc_marco_timer_init(void)
{
unsigned long rate;
u32 timer_div;
struct clk *clk;
/* timer's input clock is io clock */
clk = clk_get_sys("io", NULL);
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
BUG_ON(rate < CLOCK_TICK_RATE);
BUG_ON(rate % CLOCK_TICK_RATE);
/* Initialize the timer dividers */
timer_div = rate / CLOCK_TICK_RATE - 1;
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
/* Initialize timer counters to 0 */
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
/* Clear all interrupts */
writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
sirfsoc_clockevent_init();
}
static void __init sirfsoc_of_timer_init(struct device_node *np)
{
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base)
panic("unable to map timer cpu registers\n");
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
if (!sirfsoc_timer_irq.irq)
panic("No irq passed for timer0 via DT\n");
#ifdef CONFIG_LOCAL_TIMERS
sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
if (!sirfsoc_timer1_irq.irq)
panic("No irq passed for timer1 via DT\n");
#endif
sirfsoc_marco_timer_init();
}
CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init );

View File

@@ -0,0 +1,215 @@
/*
* System timer for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <asm/sched_clock.h>
#include <asm/mach/time.h>
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0008
#define SIRFSOC_TIMER_MATCH_1 0x000C
#define SIRFSOC_TIMER_MATCH_2 0x0010
#define SIRFSOC_TIMER_MATCH_3 0x0014
#define SIRFSOC_TIMER_MATCH_4 0x0018
#define SIRFSOC_TIMER_MATCH_5 0x001C
#define SIRFSOC_TIMER_STATUS 0x0020
#define SIRFSOC_TIMER_INT_EN 0x0024
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
#define SIRFSOC_TIMER_DIV 0x002C
#define SIRFSOC_TIMER_LATCH 0x0030
#define SIRFSOC_TIMER_LATCHED_LO 0x0034
#define SIRFSOC_TIMER_LATCHED_HI 0x0038
#define SIRFSOC_TIMER_WDT_INDEX 5
#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
#define SIRFSOC_TIMER_REG_CNT 11
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
};
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
static void __iomem *sirfsoc_timer_base;
/* timer0 interrupt handler */
static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
/* clear timer0 interrupt */
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
ce->event_handler(ce);
return IRQ_HANDLED;
}
/* read 64-bit timer counter */
static cycle_t sirfsoc_timer_read(struct clocksource *cs)
{
u64 cycles;
/* latch the 64-bit timer counter */
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return cycles;
}
static int sirfsoc_timer_set_next_event(unsigned long delta,
struct clock_event_device *ce)
{
unsigned long now, next;
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
next = now + delta;
writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return next - now > delta ? -ETIME : 0;
}
static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *ce)
{
u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
WARN_ON(1);
break;
case CLOCK_EVT_MODE_ONESHOT:
writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_RESUME:
break;
}
}
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
}
static struct clock_event_device sirfsoc_clockevent = {
.name = "sirfsoc_clockevent",
.rating = 200,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_mode = sirfsoc_timer_set_mode,
.set_next_event = sirfsoc_timer_set_next_event,
};
static struct clocksource sirfsoc_clocksource = {
.name = "sirfsoc_clocksource",
.rating = 200,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = sirfsoc_timer_read,
.suspend = sirfsoc_clocksource_suspend,
.resume = sirfsoc_clocksource_resume,
};
static struct irqaction sirfsoc_timer_irq = {
.name = "sirfsoc_timer0",
.flags = IRQF_TIMER,
.irq = 0,
.handler = sirfsoc_timer_interrupt,
.dev_id = &sirfsoc_clockevent,
};
/* Overwrite weak default sched_clock with more precise one */
static u32 notrace sirfsoc_read_sched_clock(void)
{
return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff);
}
static void __init sirfsoc_clockevent_init(void)
{
sirfsoc_clockevent.cpumask = cpumask_of(0);
clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
2, -2);
}
/* initialize the kernel jiffy timer source */
static void __init sirfsoc_prima2_timer_init(struct device_node *np)
{
unsigned long rate;
struct clk *clk;
/* timer's input clock is io clock */
clk = clk_get_sys("io", NULL);
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
BUG_ON(rate < CLOCK_TICK_RATE);
BUG_ON(rate % CLOCK_TICK_RATE);
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base)
panic("unable to map timer cpu registers\n");
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE);
BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
sirfsoc_clockevent_init();
}
CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init);

View File

@@ -0,0 +1,168 @@
/*
* arch/arm/mach-vt8500/timer.c
*
* Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This file is copied and modified from the original timer.c provided by
* Alexey Charkov. Minor changes have been made for Device Tree Support.
*/
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <asm/mach/time.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define VT8500_TIMER_OFFSET 0x0100
#define VT8500_TIMER_HZ 3000000
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0010
#define TIMER_STATUS_VAL 0x0014
#define TIMER_IER_VAL 0x001c /* interrupt enable */
#define TIMER_CTRL_VAL 0x0020
#define TIMER_AS_VAL 0x0024 /* access status */
#define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
#define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
#define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
static void __iomem *regbase;
static cycle_t vt8500_timer_read(struct clocksource *cs)
{
int loops = msecs_to_loops(10);
writel(3, regbase + TIMER_CTRL_VAL);
while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
&& --loops)
cpu_relax();
return readl(regbase + TIMER_COUNT_VAL);
}
static struct clocksource clocksource = {
.name = "vt8500_timer",
.rating = 200,
.read = vt8500_timer_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int vt8500_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
int loops = msecs_to_loops(10);
cycle_t alarm = clocksource.read(&clocksource) + cycles;
while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
&& --loops)
cpu_relax();
writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
return -ETIME;
writel(1, regbase + TIMER_IER_VAL);
return 0;
}
static void vt8500_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
writel(readl(regbase + TIMER_CTRL_VAL) | 1,
regbase + TIMER_CTRL_VAL);
writel(0, regbase + TIMER_IER_VAL);
break;
}
}
static struct clock_event_device clockevent = {
.name = "vt8500_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = vt8500_timer_set_next_event,
.set_mode = vt8500_timer_set_mode,
};
static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
writel(0xf, regbase + TIMER_STATUS_VAL);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction irq = {
.name = "vt8500_timer",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = vt8500_timer_interrupt,
.dev_id = &clockevent,
};
static void __init vt8500_timer_init(struct device_node *np)
{
int timer_irq;
regbase = of_iomap(np, 0);
if (!regbase) {
pr_err("%s: Missing iobase description in Device Tree\n",
__func__);
of_node_put(np);
return;
}
timer_irq = irq_of_parse_and_map(np, 0);
if (!timer_irq) {
pr_err("%s: Missing irq description in Device Tree\n",
__func__);
of_node_put(np);
return;
}
writel(1, regbase + TIMER_CTRL_VAL);
writel(0xf, regbase + TIMER_STATUS_VAL);
writel(~0, regbase + TIMER_MATCH_VAL);
if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
__func__, clocksource.name);
clockevent.cpumask = cpumask_of(0);
if (setup_irq(timer_irq, &irq))
pr_err("%s: setup_irq failed for %s\n", __func__,
clockevent.name);
clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
4, 0xf0000000);
}
CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);