Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
Merge tag 'timers-v5.14' of https://git.linaro.org/people/daniel.lezc…
Browse files Browse the repository at this point in the history
…ano/linux into timers/core

Pull clockevent/source updates from Daniel Lezcano:

 - Remove arch_timer_rate1 variable as it is unused in the architected
   ARM timer (Jisheng Zhang)

 - Minor cleanups (whitespace, constification, ...) for the Samsung pwm
   timer (Krzysztof Kozlowski)

 - Acknowledge and disable the timer interrupt at suspend time to
   prevent the suspend to be aborted by the ATF if there is a pending
   one on the Mediatek timer (Evan Benn)

 - Save and restore the configuration register at suspend/resume time
   for TI dm timer (Tony Lindgren)

 - Set the scene for the next timers support by renaming the array
   variables on the Ingenic time (Zhou Yanjie)

 - Add the clock rate change notification to adjust the prescalar value
   and compensate the clock source on the ARM global timer (Andrea
   Merello)

 - Add missing variable static annotation on the ARM global timer (Zou
   Wei)

 - Remove a duplicate argument when building the bits field on the ARM
   global timer (Wan Jiabing)

 - Improve the timer workaround function by reducing the loop on the
   Allwinner A64 timer (Samuel Holland)

 - Do no restore the register context in case of error on the TI dm
   timer (Tony Lindgren)

Link: https://lore.kernel.org/r/65ed5f60-d7a5-b4ae-ff78-0382d4671cc5@linaro.org
  • Loading branch information
KAGA-KOKO committed Jun 18, 2021
2 parents 245a057 + 3d41fff commit f6b6a80
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 32 deletions.
2 changes: 1 addition & 1 deletion arch/arm/mach-zynq/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ config ARCH_ZYNQ
select ARCH_SUPPORTS_BIG_ENDIAN
select ARM_AMBA
select ARM_GIC
select ARM_GLOBAL_TIMER if !CPU_FREQ
select ARM_GLOBAL_TIMER
select CADENCE_TTC_TIMER
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
Expand Down
14 changes: 14 additions & 0 deletions drivers/clocksource/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,20 @@ config ARM_GLOBAL_TIMER
help
This option enables support for the ARM global timer unit.

config ARM_GT_INITIAL_PRESCALER_VAL
int "ARM global timer initial prescaler value"
default 2 if ARCH_ZYNQ
default 1
depends on ARM_GLOBAL_TIMER
help
When the ARM global timer initializes, its current rate is declared
to the kernel and maintained forever. Should it's parent clock
change, the driver tries to fix the timer's internal prescaler.
On some machs (i.e. Zynq) the initial prescaler value thus poses
bounds about how much the parent clock is allowed to decrease or
increase wrt the initial clock value.
This affects CPU_FREQ max delta from the initial frequency.

config ARM_TIMER_SP804
bool "Support for Dual Timer SP804 module" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP
Expand Down
3 changes: 1 addition & 2 deletions drivers/clocksource/arm_arch_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)

static u32 arch_timer_rate __ro_after_init;
u32 arch_timer_rate1 __ro_after_init;
static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI] __ro_after_init;

static const char *arch_timer_ppi_names[ARCH_TIMER_MAX_TIMER_PPI] = {
Expand Down Expand Up @@ -365,7 +364,7 @@ static u64 notrace arm64_858921_read_cntvct_el0(void)
do { \
_val = read_sysreg(reg); \
_retries--; \
} while (((_val + 1) & GENMASK(9, 0)) <= 1 && _retries); \
} while (((_val + 1) & GENMASK(8, 0)) <= 1 && _retries); \
\
WARN_ON_ONCE(!_retries); \
_val; \
Expand Down
122 changes: 112 additions & 10 deletions drivers/clocksource/arm_global_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */
#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */
#define GT_CONTROL_AUTO_INC BIT(3) /* banked */
#define GT_CONTROL_PRESCALER_SHIFT 8
#define GT_CONTROL_PRESCALER_MAX 0xF
#define GT_CONTROL_PRESCALER_MASK (GT_CONTROL_PRESCALER_MAX << \
GT_CONTROL_PRESCALER_SHIFT)

#define GT_INT_STATUS 0x0c
#define GT_INT_STATUS_EVENT_FLAG BIT(0)
Expand All @@ -39,14 +43,16 @@
#define GT_COMP1 0x14
#define GT_AUTO_INC 0x18

#define MAX_F_ERR 50
/*
* We are expecting to be clocked by the ARM peripheral clock.
*
* Note: it is assumed we are using a prescaler value of zero, so this is
* the units for all operations.
*/
static void __iomem *gt_base;
static unsigned long gt_clk_rate;
static struct notifier_block gt_clk_rate_change_nb;
static u32 gt_psv_new, gt_psv_bck, gt_target_rate;
static int gt_ppi;
static struct clock_event_device __percpu *gt_evt;

Expand Down Expand Up @@ -96,7 +102,10 @@ static void gt_compare_set(unsigned long delta, int periodic)
unsigned long ctrl;

counter += delta;
ctrl = GT_CONTROL_TIMER_ENABLE;
ctrl = readl(gt_base + GT_CONTROL);
ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE |
GT_CONTROL_AUTO_INC);
ctrl |= GT_CONTROL_TIMER_ENABLE;
writel_relaxed(ctrl, gt_base + GT_CONTROL);
writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0);
writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1);
Expand All @@ -123,7 +132,7 @@ static int gt_clockevent_shutdown(struct clock_event_device *evt)

static int gt_clockevent_set_periodic(struct clock_event_device *evt)
{
gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
gt_compare_set(DIV_ROUND_CLOSEST(gt_target_rate, HZ), 1);
return 0;
}

Expand Down Expand Up @@ -177,7 +186,7 @@ static int gt_starting_cpu(unsigned int cpu)
clk->cpumask = cpumask_of(cpu);
clk->rating = 300;
clk->irq = gt_ppi;
clockevents_config_and_register(clk, gt_clk_rate,
clockevents_config_and_register(clk, gt_target_rate,
1, 0xffffffff);
enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
return 0;
Expand Down Expand Up @@ -232,9 +241,28 @@ static struct delay_timer gt_delay_timer = {
.read_current_timer = gt_read_long,
};

static void gt_write_presc(u32 psv)
{
u32 reg;

reg = readl(gt_base + GT_CONTROL);
reg &= ~GT_CONTROL_PRESCALER_MASK;
reg |= psv << GT_CONTROL_PRESCALER_SHIFT;
writel(reg, gt_base + GT_CONTROL);
}

static u32 gt_read_presc(void)
{
u32 reg;

reg = readl(gt_base + GT_CONTROL);
reg &= GT_CONTROL_PRESCALER_MASK;
return reg >> GT_CONTROL_PRESCALER_SHIFT;
}

static void __init gt_delay_timer_init(void)
{
gt_delay_timer.freq = gt_clk_rate;
gt_delay_timer.freq = gt_target_rate;
register_current_timer_delay(&gt_delay_timer);
}

Expand All @@ -243,18 +271,81 @@ static int __init gt_clocksource_init(void)
writel(0, gt_base + GT_CONTROL);
writel(0, gt_base + GT_COUNTER0);
writel(0, gt_base + GT_COUNTER1);
/* enables timer on all the cores */
writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
/* set prescaler and enable timer on all the cores */
writel(((CONFIG_ARM_GT_INITIAL_PRESCALER_VAL - 1) <<
GT_CONTROL_PRESCALER_SHIFT)
| GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);

#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
sched_clock_register(gt_sched_clock_read, 64, gt_clk_rate);
sched_clock_register(gt_sched_clock_read, 64, gt_target_rate);
#endif
return clocksource_register_hz(&gt_clocksource, gt_clk_rate);
return clocksource_register_hz(&gt_clocksource, gt_target_rate);
}

static int gt_clk_rate_change_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;

switch (event) {
case PRE_RATE_CHANGE:
{
int psv;

psv = DIV_ROUND_CLOSEST(ndata->new_rate,
gt_target_rate);

if (abs(gt_target_rate - (ndata->new_rate / psv)) > MAX_F_ERR)
return NOTIFY_BAD;

psv--;

/* prescaler within legal range? */
if (psv < 0 || psv > GT_CONTROL_PRESCALER_MAX)
return NOTIFY_BAD;

/*
* store timer clock ctrl register so we can restore it in case
* of an abort.
*/
gt_psv_bck = gt_read_presc();
gt_psv_new = psv;
/* scale down: adjust divider in post-change notification */
if (ndata->new_rate < ndata->old_rate)
return NOTIFY_DONE;

/* scale up: adjust divider now - before frequency change */
gt_write_presc(psv);
break;
}
case POST_RATE_CHANGE:
/* scale up: pre-change notification did the adjustment */
if (ndata->new_rate > ndata->old_rate)
return NOTIFY_OK;

/* scale down: adjust divider now - after frequency change */
gt_write_presc(gt_psv_new);
break;

case ABORT_RATE_CHANGE:
/* we have to undo the adjustment in case we scale up */
if (ndata->new_rate < ndata->old_rate)
return NOTIFY_OK;

/* restore original register value */
gt_write_presc(gt_psv_bck);
break;
default:
return NOTIFY_DONE;
}

return NOTIFY_DONE;
}

static int __init global_timer_of_register(struct device_node *np)
{
struct clk *gt_clk;
static unsigned long gt_clk_rate;
int err = 0;

/*
Expand Down Expand Up @@ -292,11 +383,20 @@ static int __init global_timer_of_register(struct device_node *np)
}

gt_clk_rate = clk_get_rate(gt_clk);
gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL;
gt_clk_rate_change_nb.notifier_call =
gt_clk_rate_change_cb;
err = clk_notifier_register(gt_clk, &gt_clk_rate_change_nb);
if (err) {
pr_warn("Unable to register clock notifier\n");
goto out_clk;
}

gt_evt = alloc_percpu(struct clock_event_device);
if (!gt_evt) {
pr_warn("global-timer: can't allocate memory\n");
err = -ENOMEM;
goto out_clk;
goto out_clk_nb;
}

err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
Expand Down Expand Up @@ -326,6 +426,8 @@ static int __init global_timer_of_register(struct device_node *np)
free_percpu_irq(gt_ppi, gt_evt);
out_free:
free_percpu(gt_evt);
out_clk_nb:
clk_notifier_unregister(gt_clk, &gt_clk_rate_change_nb);
out_clk:
clk_disable_unprepare(gt_clk);
out_unmap:
Expand Down
10 changes: 5 additions & 5 deletions drivers/clocksource/ingenic-sysost.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ static const struct clk_ops ingenic_ost_global_timer_ops = {

static const char * const ingenic_ost_clk_parents[] = { "ext" };

static const struct ingenic_ost_clk_info ingenic_ost_clk_info[] = {
static const struct ingenic_ost_clk_info x1000_ost_clk_info[] = {
[OST_CLK_PERCPU_TIMER] = {
.init_data = {
.name = "percpu timer",
Expand Down Expand Up @@ -414,14 +414,14 @@ static const struct ingenic_soc_info x1000_soc_info = {
.num_channels = 2,
};

static const struct of_device_id __maybe_unused ingenic_ost_of_match[] __initconst = {
{ .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info, },
static const struct of_device_id __maybe_unused ingenic_ost_of_matches[] __initconst = {
{ .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info },
{ /* sentinel */ }
};

static int __init ingenic_ost_probe(struct device_node *np)
{
const struct of_device_id *id = of_match_node(ingenic_ost_of_match, np);
const struct of_device_id *id = of_match_node(ingenic_ost_of_matches, np);
struct ingenic_ost *ost;
unsigned int i;
int ret;
Expand Down Expand Up @@ -462,7 +462,7 @@ static int __init ingenic_ost_probe(struct device_node *np)
ost->clocks->num = ost->soc_info->num_channels;

for (i = 0; i < ost->clocks->num; i++) {
ret = ingenic_ost_register_clock(ost, i, &ingenic_ost_clk_info[i], ost->clocks);
ret = ingenic_ost_register_clock(ost, i, &x1000_ost_clk_info[i], ost->clocks);
if (ret) {
pr_crit("%s: Cannot register clock %d\n", __func__, i);
goto err_unregister_ost_clocks;
Expand Down
Loading

0 comments on commit f6b6a80

Please sign in to comment.