Commit 55be6084 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'timers-core-2022-10-05' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull timer updates from Thomas Gleixner:
 "A boring time, timekeeping, timers update:

   - No core code changes

   - No new clocksource/event driver

   - Cleanup of the TI DM clocksource/event driver

   - The usual set of device tree binding updates

   - Small improvement, fixes and cleanups all over the place"

* tag 'timers-core-2022-10-05' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits)
  clocksource/drivers/arm_arch_timer: Fix CNTPCT_LO and CNTVCT_LO value
  clocksource/drivers/imx-sysctr: handle nxp,no-divider property
  dt-bindings: timer: nxp,sysctr-timer: add nxp,no-divider property
  clocksource/drivers/timer-ti-dm: Get clock in probe with devm_clk_get()
  clocksource/drivers/timer-ti-dm: Add flag to detect omap1
  clocksource/drivers/timer-ti-dm: Move struct omap_dm_timer fields to driver
  clocksource/drivers/timer-ti-dm: Use runtime PM directly and check errors
  clocksource/drivers/timer-ti-dm: Move private defines to the driver
  clocksource/drivers/timer-ti-dm: Simplify register access further
  clocksource/drivers/timer-ti-dm: Simplify register writes with dmtimer_write()
  clocksource/drivers/timer-ti-dm: Simplify register reads with dmtimer_read()
  clocksource/drivers/timer-ti-dm: Drop unused functions
  clocksource/drivers/timer-gxp: Add missing error handling in gxp_timer_probe
  clocksource/drivers/arm_arch_timer: Fix handling of ARM erratum 858921
  clocksource/drivers/exynos_mct: Enable building on ARTPEC
  clocksource/drivers/exynos_mct: Support local-timers property
  clocksource/drivers/exynos_mct: Support frc-shared property
  dt-bindings: timer: exynos4210-mct: Add ARTPEC-8 MCT support
  clocksource/drivers/sun4i: Add definition of clear interrupt
  clocksource/drivers/renesas-ostm: Add support for RZ/V2L SoC
  ...
parents 7f6dcffb 6cb5ce13
......@@ -25,6 +25,7 @@ Required properties:
For those SoCs that use SYST
* "mediatek,mt8183-timer" for MT8183 compatible timers (SYST)
* "mediatek,mt8186-timer" for MT8186 compatible timers (SYST)
* "mediatek,mt8188-timer" for MT8188 compatible timers (SYST)
* "mediatek,mt8192-timer" for MT8192 compatible timers (SYST)
* "mediatek,mt8195-timer" for MT8195 compatible timers (SYST)
* "mediatek,mt7629-timer" for MT7629 compatible timers (SYST)
......
......@@ -32,6 +32,10 @@ properties:
clock-names:
const: per
nxp,no-divider:
description: if present, means there is no internal base clk divider.
type: boolean
required:
- compatible
- reg
......
......@@ -37,6 +37,7 @@ properties:
- renesas,tmu-r8a77990 # R-Car E3
- renesas,tmu-r8a77995 # R-Car D3
- renesas,tmu-r8a779a0 # R-Car V3U
- renesas,tmu-r8a779f0 # R-Car S4-8
- const: renesas,tmu
reg:
......
......@@ -25,6 +25,7 @@ properties:
- samsung,exynos4412-mct
- items:
- enum:
- axis,artpec8-mct
- samsung,exynos3250-mct
- samsung,exynos5250-mct
- samsung,exynos5260-mct
......@@ -45,6 +46,19 @@ properties:
reg:
maxItems: 1
samsung,frc-shared:
type: boolean
description: |
Indicates that the hardware requires that this processor share the
free-running counter with a different (main) processor.
samsung,local-timers:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 16
description: |
List of indices of local timers usable from this processor.
interrupts:
description: |
Interrupts should be put in specific order. This is, the local timer
......@@ -74,6 +88,17 @@ required:
- reg
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- axis,artpec8-mct
then:
properties:
samsung,local-timers: false
samsung,frc-shared: false
- if:
properties:
compatible:
......@@ -101,6 +126,7 @@ allOf:
compatible:
contains:
enum:
- axis,artpec8-mct
- samsung,exynos5260-mct
- samsung,exynos5420-mct
- samsung,exynos5433-mct
......
......@@ -434,7 +434,7 @@ config ATMEL_TCB_CLKSRC
config CLKSRC_EXYNOS_MCT
bool "Exynos multi core timer driver" if COMPILE_TEST
depends on ARM || ARM64
depends on ARCH_EXYNOS || COMPILE_TEST
depends on ARCH_ARTPEC || ARCH_EXYNOS || COMPILE_TEST
help
Support for Multi Core Timer controller on Exynos SoCs.
......
......@@ -44,8 +44,8 @@
#define CNTACR_RWVT BIT(4)
#define CNTACR_RWPT BIT(5)
#define CNTVCT_LO 0x00
#define CNTPCT_LO 0x08
#define CNTPCT_LO 0x00
#define CNTVCT_LO 0x08
#define CNTFRQ 0x10
#define CNTP_CVAL_LO 0x20
#define CNTP_CTL 0x2c
......@@ -473,6 +473,8 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = {
.desc = "ARM erratum 858921",
.read_cntpct_el0 = arm64_858921_read_cntpct_el0,
.read_cntvct_el0 = arm64_858921_read_cntvct_el0,
.set_next_event_phys = erratum_set_next_event_phys,
.set_next_event_virt = erratum_set_next_event_virt,
},
#endif
#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
......
......@@ -33,7 +33,7 @@
#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_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * (x)))
#define EXYNOS4_MCT_L_MASK (0xffffff00)
#define MCT_L_TCNTB_OFFSET (0x00)
......@@ -66,6 +66,8 @@
#define MCT_L0_IRQ 4
/* Max number of IRQ as per DT binding document */
#define MCT_NR_IRQS 20
/* Max number of local timers */
#define MCT_NR_LOCAL (MCT_NR_IRQS - MCT_L0_IRQ)
enum {
MCT_INT_SPI,
......@@ -233,9 +235,16 @@ static cycles_t exynos4_read_current_timer(void)
}
#endif
static int __init exynos4_clocksource_init(void)
static int __init exynos4_clocksource_init(bool frc_shared)
{
exynos4_mct_frc_start();
/*
* When the frc is shared, the main processer should have already
* turned it on and we shouldn't be writing to TCON.
*/
if (frc_shared)
mct_frc.resume = NULL;
else
exynos4_mct_frc_start();
#if defined(CONFIG_ARM)
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
......@@ -449,7 +458,6 @@ static int exynos4_mct_starting_cpu(unsigned int cpu)
per_cpu_ptr(&percpu_mct_tick, cpu);
struct clock_event_device *evt = &mevt->evt;
mevt->base = EXYNOS4_MCT_L_BASE(cpu);
snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu);
evt->name = mevt->name;
......@@ -520,8 +528,17 @@ static int __init exynos4_timer_resources(struct device_node *np)
return 0;
}
/**
* exynos4_timer_interrupts - initialize MCT interrupts
* @np: device node for MCT
* @int_type: interrupt type, MCT_INT_PPI or MCT_INT_SPI
* @local_idx: array mapping CPU numbers to local timer indices
* @nr_local: size of @local_idx array
*/
static int __init exynos4_timer_interrupts(struct device_node *np,
unsigned int int_type)
unsigned int int_type,
const u32 *local_idx,
size_t nr_local)
{
int nr_irqs, i, err, cpu;
......@@ -554,13 +571,21 @@ static int __init exynos4_timer_interrupts(struct device_node *np,
} else {
for_each_possible_cpu(cpu) {
int mct_irq;
unsigned int irq_idx;
struct mct_clock_event_device *pcpu_mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
if (cpu >= nr_local) {
err = -EINVAL;
goto out_irq;
}
irq_idx = MCT_L0_IRQ + local_idx[cpu];
pcpu_mevt->evt.irq = -1;
if (MCT_L0_IRQ + cpu >= ARRAY_SIZE(mct_irqs))
if (irq_idx >= ARRAY_SIZE(mct_irqs))
break;
mct_irq = mct_irqs[MCT_L0_IRQ + cpu];
mct_irq = mct_irqs[irq_idx];
irq_set_status_flags(mct_irq, IRQ_NOAUTOEN);
if (request_irq(mct_irq,
......@@ -576,6 +601,17 @@ static int __init exynos4_timer_interrupts(struct device_node *np,
}
}
for_each_possible_cpu(cpu) {
struct mct_clock_event_device *mevt = per_cpu_ptr(&percpu_mct_tick, cpu);
if (cpu >= nr_local) {
err = -EINVAL;
goto out_irq;
}
mevt->base = EXYNOS4_MCT_L_BASE(local_idx[cpu]);
}
/* Install hotplug callbacks which configure the timer on this CPU */
err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING,
"clockevents/exynos4/mct_timer:starting",
......@@ -605,20 +641,49 @@ static int __init exynos4_timer_interrupts(struct device_node *np,
static int __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
bool frc_shared = of_property_read_bool(np, "samsung,frc-shared");
u32 local_idx[MCT_NR_LOCAL] = {0};
int nr_local;
int ret;
nr_local = of_property_count_u32_elems(np, "samsung,local-timers");
if (nr_local == 0)
return -EINVAL;
if (nr_local > 0) {
if (nr_local > ARRAY_SIZE(local_idx))
return -EINVAL;
ret = of_property_read_u32_array(np, "samsung,local-timers",
local_idx, nr_local);
if (ret)
return ret;
} else {
int i;
nr_local = ARRAY_SIZE(local_idx);
for (i = 0; i < nr_local; i++)
local_idx[i] = i;
}
ret = exynos4_timer_resources(np);
if (ret)
return ret;
ret = exynos4_timer_interrupts(np, int_type);
ret = exynos4_timer_interrupts(np, int_type, local_idx, nr_local);
if (ret)
return ret;
ret = exynos4_clocksource_init();
ret = exynos4_clocksource_init(frc_shared);
if (ret)
return ret;
/*
* When the FRC is shared with a main processor, this secondary
* processor cannot use the global comparator.
*/
if (frc_shared)
return ret;
return exynos4_clockevent_init();
}
......
......@@ -224,7 +224,7 @@ static int __init ostm_init(struct device_node *np)
TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
#ifdef CONFIG_ARCH_R9A07G044
#ifdef CONFIG_ARCH_RZG2L
static int __init ostm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......
......@@ -171,6 +171,7 @@ static int gxp_timer_probe(struct platform_device *pdev)
{
struct platform_device *gxp_watchdog_device;
struct device *dev = &pdev->dev;
int ret;
if (!gxp_timer) {
pr_err("Gxp Timer not initialized, cannot create watchdog");
......@@ -187,7 +188,11 @@ static int gxp_timer_probe(struct platform_device *pdev)
gxp_watchdog_device->dev.platform_data = gxp_timer->counter;
gxp_watchdog_device->dev.parent = dev;
return platform_device_add(gxp_watchdog_device);
ret = platform_device_add(gxp_watchdog_device);
if (ret)
platform_device_put(gxp_watchdog_device);
return ret;
}
static const struct of_device_id gxp_timer_of_match[] = {
......
......@@ -134,8 +134,10 @@ static int __init sysctr_timer_init(struct device_node *np)
if (ret)
return ret;
/* system counter clock is divided by 3 internally */
to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
if (!of_property_read_bool(np, "nxp,no-divider")) {
/* system counter clock is divided by 3 internally */
to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
}
sys_ctr_base = timer_of_base(&to_sysctr);
cmpcr = readl(sys_ctr_base + CMPCR);
......
......@@ -26,6 +26,7 @@
#define TIMER_IRQ_EN_REG 0x00
#define TIMER_IRQ_EN(val) BIT(val)
#define TIMER_IRQ_ST_REG 0x04
#define TIMER_IRQ_CLEAR(val) BIT(val)
#define TIMER_CTL_REG(val) (0x10 * val + 0x10)
#define TIMER_CTL_ENABLE BIT(0)
#define TIMER_CTL_RELOAD BIT(1)
......@@ -123,7 +124,7 @@ static int sun4i_clkevt_next_event(unsigned long evt,
static void sun4i_timer_clear_interrupt(void __iomem *base)
{
writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG);
writel(TIMER_IRQ_CLEAR(0), base + TIMER_IRQ_ST_REG);
}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
......
......@@ -33,6 +33,116 @@
#include <clocksource/timer-ti-dm.h>
/*
* timer errata flags
*
* Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This
* errata prevents us from using posted mode on these devices, unless the
* timer counter register is never read. For more details please refer to
* the OMAP3/4/5 errata documents.
*/
#define OMAP_TIMER_ERRATA_I103_I767 0x80000000
/* posted mode types */
#define OMAP_TIMER_NONPOSTED 0x00
#define OMAP_TIMER_POSTED 0x01
/* register offsets with the write pending bit encoded */
#define WPSHIFT 16
#define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
| (WP_TCLR << WPSHIFT))
#define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
| (WP_TCRR << WPSHIFT))
#define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
| (WP_TLDR << WPSHIFT))
#define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
| (WP_TTGR << WPSHIFT))
#define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
| (WP_TMAR << WPSHIFT))
#define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
| (WP_TPIR << WPSHIFT))
#define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
| (WP_TNIR << WPSHIFT))
#define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
| (WP_TCVR << WPSHIFT))
#define OMAP_TIMER_TICK_INT_MASK_SET_REG \
(_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT))
#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
(_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
struct timer_regs {
u32 ocp_cfg;
u32 tidr;
u32 tier;
u32 twer;
u32 tclr;
u32 tcrr;
u32 tldr;
u32 ttrg;
u32 twps;
u32 tmar;
u32 tcar1;
u32 tsicr;
u32 tcar2;
u32 tpir;
u32 tnir;
u32 tcvr;
u32 tocr;
u32 towr;
};
struct dmtimer {
struct omap_dm_timer cookie;
int id;
int irq;
struct clk *fclk;
void __iomem *io_base;
int irq_stat; /* TISR/IRQSTATUS interrupt status */
int irq_ena; /* irq enable */
int irq_dis; /* irq disable, only on v2 ip */
void __iomem *pend; /* write pending */
void __iomem *func_base; /* function register base */
atomic_t enabled;
unsigned long rate;
unsigned reserved:1;
unsigned posted:1;
unsigned omap1:1;
struct timer_regs context;
int revision;
u32 capability;
u32 errata;
struct platform_device *pdev;
struct list_head node;
struct notifier_block nb;
};
static u32 omap_reserved_systimers;
static LIST_HEAD(omap_timer_list);
static DEFINE_SPINLOCK(dm_timer_lock);
......@@ -44,27 +154,56 @@ enum {
REQUEST_BY_NODE,
};
static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
int posted)
/**
* dmtimer_read - read timer registers in posted and non-posted mode
* @timer: timer pointer over which read operation to perform
* @reg: lowest byte holds the register offset
*
* The posted mode bit is encoded in reg. Note that in posted mode, write
* pending bit must be checked. Otherwise a read of a non completed write
* will produce an error.
*/
static inline u32 dmtimer_read(struct dmtimer *timer, u32 reg)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
u16 wp, offset;
wp = reg >> WPSHIFT;
offset = reg & 0xff;
/* Wait for a possible write pending bit in posted mode */
if (wp && timer->posted)
while (readl_relaxed(timer->pend) & wp)
cpu_relax();
return readl_relaxed(timer->func_base + (reg & 0xff));
return readl_relaxed(timer->func_base + offset);
}
static inline void __omap_dm_timer_write(struct omap_dm_timer *timer,
u32 reg, u32 val, int posted)
/**
* dmtimer_write - write timer registers in posted and non-posted mode
* @timer: timer pointer over which write operation is to perform
* @reg: lowest byte holds the register offset
* @value: data to write into the register
*
* The posted mode bit is encoded in reg. Note that in posted mode, the write
* pending bit must be checked. Otherwise a write on a register which has a
* pending write will be lost.
*/
static inline void dmtimer_write(struct dmtimer *timer, u32 reg, u32 val)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
u16 wp, offset;
wp = reg >> WPSHIFT;
offset = reg & 0xff;
/* Wait for a possible write pending bit in posted mode */
if (wp && timer->posted)
while (readl_relaxed(timer->pend) & wp)
cpu_relax();
writel_relaxed(val, timer->func_base + (reg & 0xff));
writel_relaxed(val, timer->func_base + offset);
}
static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
static inline void __omap_dm_timer_init_regs(struct dmtimer *timer)
{
u32 tidr;
......@@ -72,16 +211,16 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
tidr = readl_relaxed(timer->io_base);
if (!(tidr >> 16)) {
timer->revision = 1;
timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->irq_stat = OMAP_TIMER_V1_STAT_OFFSET;
timer->irq_ena = OMAP_TIMER_V1_INT_EN_OFFSET;
timer->irq_dis = OMAP_TIMER_V1_INT_EN_OFFSET;
timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
timer->func_base = timer->io_base;
} else {
timer->revision = 2;
timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR;
timer->irq_stat = OMAP_TIMER_V2_IRQSTATUS - OMAP_TIMER_V2_FUNC_OFFSET;
timer->irq_ena = OMAP_TIMER_V2_IRQENABLE_SET - OMAP_TIMER_V2_FUNC_OFFSET;
timer->irq_dis = OMAP_TIMER_V2_IRQENABLE_CLR - OMAP_TIMER_V2_FUNC_OFFSET;
timer->pend = timer->io_base +
_OMAP_TIMER_WRITE_PEND_OFFSET +
OMAP_TIMER_V2_FUNC_OFFSET;
......@@ -99,35 +238,34 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
* complete. Enabling this feature can improve performance for writing to the
* timer registers.
*/
static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
static inline void __omap_dm_timer_enable_posted(struct dmtimer *timer)
{
if (timer->posted)
return;
if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) {
timer->posted = OMAP_TIMER_NONPOSTED;
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0, 0);
dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0);
return;
}
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
OMAP_TIMER_CTRL_POSTED, 0);
dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, OMAP_TIMER_CTRL_POSTED);
timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
timer->posted = OMAP_TIMER_POSTED;
}
static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
int posted, unsigned long rate)
static inline void __omap_dm_timer_stop(struct dmtimer *timer,
unsigned long rate)
{
u32 l;
l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
if (l & OMAP_TIMER_CTRL_ST) {
l &= ~0x1;
__omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
#ifdef CONFIG_ARCH_OMAP2PLUS
/* Readback to make sure write has completed */
__omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
/*
* Wait for functional clock period x 3.5 to make sure that
* timer is stopped
......@@ -137,104 +275,59 @@ static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
}
/* Ack possibly pending interrupt */
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat);
dmtimer_write(timer, timer->irq_stat, OMAP_TIMER_INT_OVERFLOW);
}
static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer,
unsigned int value)
static inline void __omap_dm_timer_int_enable(struct dmtimer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_ena);
__omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
dmtimer_write(timer, timer->irq_ena, value);
dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value);
}
static inline unsigned int
__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted)
__omap_dm_timer_read_counter(struct dmtimer *timer)
{
return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted);
return dmtimer_read(timer, OMAP_TIMER_COUNTER_REG);
}
static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer,
static inline void __omap_dm_timer_write_status(struct dmtimer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_stat);
dmtimer_write(timer, timer->irq_stat, value);
}
/**
* omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
* @timer: timer pointer over which read operation to perform
* @reg: lowest byte holds the register offset
*
* The posted mode bit is encoded in reg. Note that in posted mode write
* pending bit must be checked. Otherwise a read of a non completed write
* will produce an error.
*/
static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
static void omap_timer_restore_context(struct dmtimer *timer)
{
WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
return __omap_dm_timer_read(timer, reg, timer->posted);
dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, timer->context.ocp_cfg);
dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, timer->context.twer);
dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, timer->context.tcrr);
dmtimer_write(timer, OMAP_TIMER_LOAD_REG, timer->context.tldr);
dmtimer_write(timer, OMAP_TIMER_MATCH_REG, timer->context.tmar);
dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, timer->context.tsicr);
dmtimer_write(timer, timer->irq_ena, timer->context.tier);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, timer->context.tclr);
}
/**
* omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
* @timer: timer pointer over which write operation is to perform
* @reg: lowest byte holds the register offset
* @value: data to write into the register
*
* The posted mode bit is encoded in reg. Note that in posted mode the write
* pending bit must be checked. Otherwise a write on a register which has a
* pending write will be lost.
*/
static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
u32 value)
static void omap_timer_save_context(struct dmtimer *timer)
{
WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
__omap_dm_timer_write(timer, reg, value, timer->posted);
}
static void omap_timer_restore_context(struct omap_dm_timer *timer)
{
__omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET,
timer->context.ocp_cfg, 0);
omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
timer->context.twer);
omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
timer->context.tcrr);
omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
timer->context.tldr);
omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
timer->context.tmar);
omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
timer->context.tsicr);
writel_relaxed(timer->context.tier, timer->irq_ena);
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
timer->context.tclr);
}
static void omap_timer_save_context(struct omap_dm_timer *timer)
{
timer->context.ocp_cfg =
__omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
timer->context.tclr =
omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
timer->context.twer =
omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG);
timer->context.tldr =
omap_dm_timer_read_reg(timer, OMAP_TIMER_LOAD_REG);
timer->context.tmar =
omap_dm_timer_read_reg(timer, OMAP_TIMER_MATCH_REG);
timer->context.tier = readl_relaxed(timer->irq_ena);
timer->context.tsicr =
omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG);
timer->context.ocp_cfg = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET);
timer->context.tclr = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
timer->context.twer = dmtimer_read(timer, OMAP_TIMER_WAKEUP_EN_REG);
timer->context.tldr = dmtimer_read(timer, OMAP_TIMER_LOAD_REG);
timer->context.tmar = dmtimer_read(timer, OMAP_TIMER_MATCH_REG);
timer->context.tier = dmtimer_read(timer, timer->irq_ena);
timer->context.tsicr = dmtimer_read(timer, OMAP_TIMER_IF_CTRL_REG);
}
static int omap_timer_context_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
struct omap_dm_timer *timer;
struct dmtimer *timer;
timer = container_of(nb, struct omap_dm_timer, nb);
timer = container_of(nb, struct dmtimer, nb);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
......@@ -256,18 +349,17 @@ static int omap_timer_context_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
static int omap_dm_timer_reset(struct omap_dm_timer *timer)
static int omap_dm_timer_reset(struct dmtimer *timer)
{
u32 l, timeout = 100000;
if (timer->revision != 1)
return -EINVAL;
omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
do {
l = __omap_dm_timer_read(timer,
OMAP_TIMER_V1_SYS_STAT_OFFSET, 0);
l = dmtimer_read(timer, OMAP_TIMER_V1_SYS_STAT_OFFSET);
} while (!l && timeout--);
if (!timeout) {
......@@ -276,22 +368,38 @@ static int omap_dm_timer_reset(struct omap_dm_timer *timer)
}
/* Configure timer for smart-idle mode */
l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
l = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET);
l |= 0x2 << 0x3;
__omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0);
dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l);
timer->posted = 0;
return 0;
}
static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
/*
* Functions exposed to PWM and remoteproc drivers via platform_data.
* Do not use these in the driver, these will get deprecated and will
* will be replaced by Linux generic framework functions such as
* chained interrupts and clock framework.
*/
static struct dmtimer *to_dmtimer(struct omap_dm_timer *cookie)
{
if (!cookie)
return NULL;
return container_of(cookie, struct dmtimer, cookie);
}
static int omap_dm_timer_set_source(struct omap_dm_timer *cookie, int source)
{
int ret;
const char *parent_name;
struct clk *parent;
struct dmtimer_platform_data *pdata;
struct dmtimer *timer;
timer = to_dmtimer(cookie);
if (unlikely(!timer) || IS_ERR(timer->fclk))
return -EINVAL;
......@@ -316,7 +424,7 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
* use the clock framework to set the parent clock. To be removed
* once OMAP1 migrated to using clock framework for dmtimers
*/
if (pdata && pdata->set_timer_src)
if (timer->omap1 && pdata && pdata->set_timer_src)
return pdata->set_timer_src(timer->pdev, source);
#if defined(CONFIG_COMMON_CLK)
......@@ -341,44 +449,44 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
return ret;
}
static void omap_dm_timer_enable(struct omap_dm_timer *timer)
static void omap_dm_timer_enable(struct omap_dm_timer *cookie)
{
pm_runtime_get_sync(&timer->pdev->dev);
struct dmtimer *timer = to_dmtimer(cookie);
struct device *dev = &timer->pdev->dev;
int rc;
rc = pm_runtime_resume_and_get(dev);
if (rc)
dev_err(dev, "could not enable timer\n");
}
static void omap_dm_timer_disable(struct omap_dm_timer *timer)
static void omap_dm_timer_disable(struct omap_dm_timer *cookie)
{
pm_runtime_put_sync(&timer->pdev->dev);
struct dmtimer *timer = to_dmtimer(cookie);
struct device *dev = &timer->pdev->dev;
pm_runtime_put_sync(dev);
}
static int omap_dm_timer_prepare(struct omap_dm_timer *timer)
static int omap_dm_timer_prepare(struct dmtimer *timer)
{
struct device *dev = &timer->pdev->dev;
int rc;
/*
* FIXME: OMAP1 devices do not use the clock framework for dmtimers so
* do not call clk_get() for these devices.
*/
if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
timer->fclk = clk_get(&timer->pdev->dev, "fck");
if (WARN_ON_ONCE(IS_ERR(timer->fclk))) {
dev_err(&timer->pdev->dev, ": No fclk handle.\n");
return -EINVAL;
}
}
omap_dm_timer_enable(timer);
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
if (timer->capability & OMAP_TIMER_NEEDS_RESET) {
rc = omap_dm_timer_reset(timer);
if (rc) {
omap_dm_timer_disable(timer);
pm_runtime_put_sync(dev);
return rc;
}
}
__omap_dm_timer_enable_posted(timer);
omap_dm_timer_disable(timer);
pm_runtime_put_sync(dev);
return 0;
}
......@@ -388,19 +496,9 @@ static inline u32 omap_dm_timer_reserved_systimer(int id)
return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0;
}
int omap_dm_timer_reserve_systimer(int id)
static struct dmtimer *_omap_dm_timer_request(int req_type, void *data)
{
if (omap_dm_timer_reserved_systimer(id))
return -ENODEV;
omap_reserved_systimers |= (1 << (id - 1));
return 0;
}
static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data)
{
struct omap_dm_timer *timer = NULL, *t;
struct dmtimer *timer = NULL, *t;
struct device_node *np = NULL;
unsigned long flags;
u32 cap = 0;
......@@ -484,11 +582,19 @@ static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data)
static struct omap_dm_timer *omap_dm_timer_request(void)
{
return _omap_dm_timer_request(REQUEST_ANY, NULL);
struct dmtimer *timer;
timer = _omap_dm_timer_request(REQUEST_ANY, NULL);
if (!timer)
return NULL;
return &timer->cookie;
}
static struct omap_dm_timer *omap_dm_timer_request_specific(int id)
{
struct dmtimer *timer;
/* Requesting timer by ID is not supported when device tree is used */
if (of_have_populated_dt()) {
pr_warn("%s: Please use omap_dm_timer_request_by_node()\n",
......@@ -496,21 +602,11 @@ static struct omap_dm_timer *omap_dm_timer_request_specific(int id)
return NULL;
}
return _omap_dm_timer_request(REQUEST_BY_ID, &id);
}
timer = _omap_dm_timer_request(REQUEST_BY_ID, &id);
if (!timer)
return NULL;
/**
* omap_dm_timer_request_by_cap - Request a timer by capability
* @cap: Bit mask of capabilities to match
*
* Find a timer based upon capabilities bit mask. Callers of this function
* should use the definitions found in the plat/dmtimer.h file under the
* comment "timer capabilities used in hwmod database". Returns pointer to
* timer handle on success and a NULL pointer on failure.
*/
struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)
{
return _omap_dm_timer_request(REQUEST_BY_CAP, &cap);
return &timer->cookie;
}
/**
......@@ -522,26 +618,34 @@ struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)
*/
static struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np)
{
struct dmtimer *timer;
if (!np)
return NULL;
return _omap_dm_timer_request(REQUEST_BY_NODE, np);
timer = _omap_dm_timer_request(REQUEST_BY_NODE, np);
if (!timer)
return NULL;
return &timer->cookie;
}
static int omap_dm_timer_free(struct omap_dm_timer *timer)
static int omap_dm_timer_free(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
clk_put(timer->fclk);
WARN_ON(!timer->reserved);
timer->reserved = 0;
return 0;
}
int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
int omap_dm_timer_get_irq(struct omap_dm_timer *cookie)
{
struct dmtimer *timer = to_dmtimer(cookie);
if (timer)
return timer->irq;
return -EINVAL;
......@@ -550,7 +654,7 @@ int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
#if defined(CONFIG_ARCH_OMAP1)
#include <linux/soc/ti/omap1-io.h>
static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *cookie)
{
return NULL;
}
......@@ -562,7 +666,7 @@ static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
int i = 0;
struct omap_dm_timer *timer = NULL;
struct dmtimer *timer = NULL;
unsigned long flags;
/* If ARMXOR cannot be idled this function call is unnecessary */
......@@ -574,7 +678,7 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
list_for_each_entry(timer, &omap_timer_list, node) {
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
if (l & OMAP_TIMER_CTRL_ST) {
if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
inputmask &= ~(1 << 1);
......@@ -590,8 +694,10 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
#else
static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *cookie)
{
struct dmtimer *timer = to_dmtimer(cookie);
if (timer && !IS_ERR(timer->fclk))
return timer->fclk;
return NULL;
......@@ -606,95 +712,125 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
#endif
int omap_dm_timer_trigger(struct omap_dm_timer *timer)
{
if (unlikely(!timer || !atomic_read(&timer->enabled))) {
pr_err("%s: timer not available or enabled.\n", __func__);
return -EINVAL;
}
omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
return 0;
}
static int omap_dm_timer_start(struct omap_dm_timer *timer)
static int omap_dm_timer_start(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
struct device *dev;
int rc;
u32 l;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
if (!(l & OMAP_TIMER_CTRL_ST)) {
l |= OMAP_TIMER_CTRL_ST;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
}
return 0;
}
static int omap_dm_timer_stop(struct omap_dm_timer *timer)
static int omap_dm_timer_stop(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
struct device *dev;
unsigned long rate = 0;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
if (!(timer->capability & OMAP_TIMER_NEEDS_RESET))
dev = &timer->pdev->dev;
if (!timer->omap1)
rate = clk_get_rate(timer->fclk);
__omap_dm_timer_stop(timer, timer->posted, rate);
__omap_dm_timer_stop(timer, rate);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static int omap_dm_timer_set_load(struct omap_dm_timer *timer,
static int omap_dm_timer_set_load(struct omap_dm_timer *cookie,
unsigned int load)
{
struct dmtimer *timer;
struct device *dev;
int rc;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
dmtimer_write(timer, OMAP_TIMER_LOAD_REG, load);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
static int omap_dm_timer_set_match(struct omap_dm_timer *cookie, int enable,
unsigned int match)
{
struct dmtimer *timer;
struct device *dev;
int rc;
u32 l;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
if (enable)
l |= OMAP_TIMER_CTRL_CE;
else
l &= ~OMAP_TIMER_CTRL_CE;
omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
dmtimer_write(timer, OMAP_TIMER_MATCH_REG, match);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
static int omap_dm_timer_set_pwm(struct omap_dm_timer *cookie, int def_on,
int toggle, int trigger, int autoreload)
{
struct dmtimer *timer;
struct device *dev;
int rc;
u32 l;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
OMAP_TIMER_CTRL_PT | (0x03 << 10) | OMAP_TIMER_CTRL_AR);
if (def_on)
......@@ -704,57 +840,86 @@ static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
l |= trigger << 10;
if (autoreload)
l |= OMAP_TIMER_CTRL_AR;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static int omap_dm_timer_get_pwm_status(struct omap_dm_timer *timer)
static int omap_dm_timer_get_pwm_status(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
struct device *dev;
int rc;
u32 l;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
omap_dm_timer_disable(timer);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
pm_runtime_put_sync(dev);
return l;
}
static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer,
int prescaler)
static int omap_dm_timer_set_prescaler(struct omap_dm_timer *cookie,
int prescaler)
{
struct dmtimer *timer;
struct device *dev;
int rc;
u32 l;
timer = to_dmtimer(cookie);
if (unlikely(!timer) || prescaler < -1 || prescaler > 7)
return -EINVAL;
omap_dm_timer_enable(timer);
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG);
l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
if (prescaler >= 0) {
l |= OMAP_TIMER_CTRL_PRE;
l |= prescaler << 2;
}
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
static int omap_dm_timer_set_int_enable(struct omap_dm_timer *cookie,
unsigned int value)
{
struct dmtimer *timer;
struct device *dev;
int rc;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
__omap_dm_timer_int_enable(timer, value);
omap_dm_timer_disable(timer);
pm_runtime_put_sync(dev);
return 0;
}
......@@ -765,42 +930,55 @@ static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
*
* Disables the specified timer interrupts for a timer.
*/
static int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)
static int omap_dm_timer_set_int_disable(struct omap_dm_timer *cookie, u32 mask)
{
struct dmtimer *timer;
struct device *dev;
u32 l = mask;
int rc;
timer = to_dmtimer(cookie);
if (unlikely(!timer))
return -EINVAL;
omap_dm_timer_enable(timer);
dev = &timer->pdev->dev;
rc = pm_runtime_resume_and_get(dev);
if (rc)
return rc;
if (timer->revision == 1)
l = readl_relaxed(timer->irq_ena) & ~mask;
l = dmtimer_read(timer, timer->irq_ena) & ~mask;
dmtimer_write(timer, timer->irq_dis, l);
l = dmtimer_read(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask;
dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, l);
writel_relaxed(l, timer->irq_dis);
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask;
omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l);
pm_runtime_put_sync(dev);
omap_dm_timer_disable(timer);
return 0;
}
static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
unsigned int l;
timer = to_dmtimer(cookie);
if (unlikely(!timer || !atomic_read(&timer->enabled))) {
pr_err("%s: timer not available or enabled.\n", __func__);
return 0;
}
l = readl_relaxed(timer->irq_stat);
l = dmtimer_read(timer, timer->irq_stat);
return l;
}
static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
static int omap_dm_timer_write_status(struct omap_dm_timer *cookie, unsigned int value)
{
struct dmtimer *timer;
timer = to_dmtimer(cookie);
if (unlikely(!timer || !atomic_read(&timer->enabled)))
return -EINVAL;
......@@ -809,49 +987,39 @@ static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int
return 0;
}
static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *cookie)
{
struct dmtimer *timer;
timer = to_dmtimer(cookie);
if (unlikely(!timer || !atomic_read(&timer->enabled))) {
pr_err("%s: timer not iavailable or enabled.\n", __func__);
return 0;
}
return __omap_dm_timer_read_counter(timer, timer->posted);
return __omap_dm_timer_read_counter(timer);
}
static int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
static int omap_dm_timer_write_counter(struct omap_dm_timer *cookie, unsigned int value)
{
struct dmtimer *timer;
timer = to_dmtimer(cookie);
if (unlikely(!timer || !atomic_read(&timer->enabled))) {
pr_err("%s: timer not available or enabled.\n", __func__);
return -EINVAL;
}
omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, value);
/* Save the context */
timer->context.tcrr = value;
return 0;
}
int omap_dm_timers_active(void)
{
struct omap_dm_timer *timer;
list_for_each_entry(timer, &omap_timer_list, node) {
if (!timer->reserved)
continue;
if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
OMAP_TIMER_CTRL_ST) {
return 1;
}
}
return 0;
}
static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev)
{
struct omap_dm_timer *timer = dev_get_drvdata(dev);
struct dmtimer *timer = dev_get_drvdata(dev);
atomic_set(&timer->enabled, 0);
......@@ -865,7 +1033,7 @@ static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev)
static int __maybe_unused omap_dm_timer_runtime_resume(struct device *dev)
{
struct omap_dm_timer *timer = dev_get_drvdata(dev);
struct dmtimer *timer = dev_get_drvdata(dev);
if (!(timer->capability & OMAP_TIMER_ALWON) && timer->func_base)
omap_timer_restore_context(timer);
......@@ -892,7 +1060,7 @@ static const struct of_device_id omap_timer_match[];
static int omap_dm_timer_probe(struct platform_device *pdev)
{
unsigned long flags;
struct omap_dm_timer *timer;
struct dmtimer *timer;
struct device *dev = &pdev->dev;
const struct dmtimer_platform_data *pdata;
int ret;
......@@ -916,7 +1084,6 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
if (timer->irq < 0)
return timer->irq;
timer->fclk = ERR_PTR(-ENODEV);
timer->io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(timer->io_base))
return PTR_ERR(timer->io_base);
......@@ -938,6 +1105,17 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
}
timer->omap1 = timer->capability & OMAP_TIMER_NEEDS_RESET;
/* OMAP1 devices do not yet use the clock framework for dmtimers */
if (!timer->omap1) {
timer->fclk = devm_clk_get(dev, "fck");
if (IS_ERR(timer->fclk))
return PTR_ERR(timer->fclk);
} else {
timer->fclk = ERR_PTR(-ENODEV);
}
if (!(timer->capability & OMAP_TIMER_ALWON)) {
timer->nb.notifier_call = omap_timer_context_notifier;
cpu_pm_register_notifier(&timer->nb);
......@@ -950,11 +1128,11 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
if (!timer->reserved) {
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(dev, "%s: pm_runtime_get_sync failed!\n",
__func__);
goto err_get_sync;
goto err_disable;
}
__omap_dm_timer_init_regs(timer);
pm_runtime_put(dev);
......@@ -969,8 +1147,7 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
return 0;
err_get_sync:
pm_runtime_put_noidle(dev);
err_disable:
pm_runtime_disable(dev);
return ret;
}
......@@ -985,7 +1162,7 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
*/
static int omap_dm_timer_remove(struct platform_device *pdev)
{
struct omap_dm_timer *timer;
struct dmtimer *timer;
unsigned long flags;
int ret = -EINVAL;
......
......@@ -52,10 +52,6 @@
#define OMAP_TIMER_TRIGGER_OVERFLOW 0x01
#define OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02
/* posted mode types */
#define OMAP_TIMER_NONPOSTED 0x00
#define OMAP_TIMER_POSTED 0x01
/* timer capabilities used in hwmod database */
#define OMAP_TIMER_SECURE 0x80000000
#define OMAP_TIMER_ALWON 0x40000000
......@@ -63,73 +59,13 @@
#define OMAP_TIMER_NEEDS_RESET 0x10000000
#define OMAP_TIMER_HAS_DSP_IRQ 0x08000000
/*
* timer errata flags
*
* Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This
* errata prevents us from using posted mode on these devices, unless the
* timer counter register is never read. For more details please refer to
* the OMAP3/4/5 errata documents.
*/
#define OMAP_TIMER_ERRATA_I103_I767 0x80000000
struct timer_regs {
u32 ocp_cfg;
u32 tidr;
u32 tier;
u32 twer;
u32 tclr;
u32 tcrr;
u32 tldr;
u32 ttrg;
u32 twps;
u32 tmar;
u32 tcar1;
u32 tsicr;
u32 tcar2;
u32 tpir;
u32 tnir;
u32 tcvr;
u32 tocr;
u32 towr;
};
struct omap_dm_timer {
int id;
int irq;
struct clk *fclk;
void __iomem *io_base;
void __iomem *irq_stat; /* TISR/IRQSTATUS interrupt status */
void __iomem *irq_ena; /* irq enable */
void __iomem *irq_dis; /* irq disable, only on v2 ip */
void __iomem *pend; /* write pending */
void __iomem *func_base; /* function register base */
atomic_t enabled;
unsigned long rate;
unsigned reserved:1;
unsigned posted:1;
struct timer_regs context;
int revision;
u32 capability;
u32 errata;
struct platform_device *pdev;
struct list_head node;
struct notifier_block nb;
};
int omap_dm_timer_reserve_systimer(int id);
struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap);
int omap_dm_timer_get_irq(struct omap_dm_timer *timer);
u32 omap_dm_timer_modify_idlect_mask(u32 inputmask);
int omap_dm_timer_trigger(struct omap_dm_timer *timer);
int omap_dm_timers_active(void);
/*
* Do not use the defines below, they are not needed. They should be only
* used by dmtimer.c and sys_timer related code.
......@@ -199,52 +135,4 @@ int omap_dm_timers_active(void);
#define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */
#define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */
/* register offsets with the write pending bit encoded */
#define WPSHIFT 16
#define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
| (WP_TCLR << WPSHIFT))
#define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
| (WP_TCRR << WPSHIFT))
#define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
| (WP_TLDR << WPSHIFT))
#define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
| (WP_TTGR << WPSHIFT))
#define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
| (WP_TMAR << WPSHIFT))
#define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
| (WP_NONE << WPSHIFT))
#define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
| (WP_TPIR << WPSHIFT))
#define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
| (WP_TNIR << WPSHIFT))
#define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
| (WP_TCVR << WPSHIFT))
#define OMAP_TIMER_TICK_INT_MASK_SET_REG \
(_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT))
#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
(_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
#endif /* __CLOCKSOURCE_DMTIMER_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment