#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.
*/ staticvoid __iomem *gt_base; staticstruct notifier_block gt_clk_rate_change_nb; static u32 gt_psv_new, gt_psv_bck; staticunsignedlong gt_target_rate; staticint gt_ppi; staticstruct clock_event_device __percpu *gt_evt;
/* * To get the value from the Global Timer Counter register proceed as follows: * 1. Read the upper 32-bit timer counter register * 2. Read the lower 32-bit timer counter register * 3. Read the upper 32-bit timer counter register again. If the value is * different to the 32-bit upper value read previously, go back to step 2. * Otherwise the 64-bit timer counter value is correct.
*/ static u64 notrace _gt_counter_read(void)
{
u64 counter;
u32 lower;
u32 upper, old_upper;
/* * To ensure that updates to comparator value register do not set the * Interrupt Status Register proceed as follows: * 1. Clear the Comp Enable bit in the Timer Control Register. * 2. Write the lower 32-bit Comparator Value Register. * 3. Write the upper 32-bit Comparator Value Register. * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
*/ staticvoid gt_compare_set(unsignedlong delta, int periodic)
{
u64 counter = gt_counter_read(); unsignedlong ctrl;
if (!(readl_relaxed(gt_base + GT_INT_STATUS) &
GT_INT_STATUS_EVENT_FLAG)) return IRQ_NONE;
/** * ERRATA 740657( Global Timer can send 2 interrupts for * the same event in single-shot mode) * Workaround: * Either disable single-shot mode. * Or * Modify the Interrupt Handler to avoid the * offending sequence. This is achieved by clearing * the Global Timer flag _after_ having incremented * the Comparator register value to a higher value.
*/ if (clockevent_state_oneshot(evt))
gt_compare_set(ULONG_MAX, 0);
/* prescaler within legal range? */ if (!FIELD_FIT(GT_CONTROL_PRESCALER_MASK, psv)) 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;
}
/* * In A9 r2p0 the comparators for each processor with the global timer * fire when the timer value is greater than or equal to. In previous * revisions the comparators fired when the timer value was equal to.
*/ if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9
&& (read_cpuid_id() & 0xf0000f) < 0x200000) {
pr_warn("global-timer: non support for this cpu version.\n"); return -ENOSYS;
}
gt_ppi = irq_of_parse_and_map(np, 0); if (!gt_ppi) {
pr_warn("global-timer: unable to parse irq\n"); return -EINVAL;
}
gt_base = of_iomap(np, 0); if (!gt_base) {
pr_warn("global-timer: invalid base address\n"); return -ENXIO;
}
gt_clk = of_clk_get(np, 0); if (!IS_ERR(gt_clk)) {
err = clk_prepare_enable(gt_clk); if (err) goto out_unmap;
} else {
pr_warn("global-timer: clk not found\n");
err = -EINVAL; goto out_unmap;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.