Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/arm/generic_timer.c
Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <dev/fdt/fdt_common.h> | #include <dev/fdt/fdt_common.h> | ||||
#include <dev/ofw/openfirm.h> | #include <dev/ofw/openfirm.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/fdt.h> | |||||
#define GT_CTRL_ENABLE (1 << 0) | #define GT_CTRL_ENABLE (1 << 0) | ||||
#define GT_CTRL_INT_MASK (1 << 1) | #define GT_CTRL_INT_MASK (1 << 1) | ||||
#define GT_CTRL_INT_STAT (1 << 2) | #define GT_CTRL_INT_STAT (1 << 2) | ||||
#define GT_REG_CTRL 0 | #define GT_REG_CTRL 0 | ||||
#define GT_REG_TVAL 1 | #define GT_REG_TVAL 1 | ||||
#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ | #define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ | ||||
Show All 28 Lines | static struct timecounter arm_tmr_timecount = { | ||||
.tc_name = "ARM MPCore Timecounter", | .tc_name = "ARM MPCore Timecounter", | ||||
.tc_get_timecount = arm_tmr_get_timecount, | .tc_get_timecount = arm_tmr_get_timecount, | ||||
.tc_poll_pps = NULL, | .tc_poll_pps = NULL, | ||||
.tc_counter_mask = ~0u, | .tc_counter_mask = ~0u, | ||||
.tc_frequency = 0, | .tc_frequency = 0, | ||||
.tc_quality = 1000, | .tc_quality = 1000, | ||||
}; | }; | ||||
#ifdef __arm__ | |||||
imp: This set of changes (for get/set_el*) could be done separately. IT looks self contained. If you… | |||||
andrewAuthorUnsubmitted Not Done Inline ActionsDone andrew: Done | |||||
#define get_el0(x) cp15_## x ##_get() | |||||
#define get_el1(x) cp15_## x ##_get() | |||||
#define set_el0(x, val) cp15_## x ##_set(val) | |||||
#define set_el1(x, val) cp15_## x ##_set(val) | |||||
#else | |||||
#define get_el0(x) READ_SPECIALREG(x ##_el0) | |||||
#define get_el1(x) READ_SPECIALREG(x ##_el1) | |||||
#define set_el0(x, val) WRITE_SPECIALREG(x ##_el0, val) | |||||
#define set_el1(x, val) WRITE_SPECIALREG(x ##_el1, val) | |||||
#endif | |||||
static int | static int | ||||
get_freq(void) | get_freq(void) | ||||
{ | { | ||||
uint32_t val; | return (get_el0(cntfrq)); | ||||
/* cntfrq */ | |||||
__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); | |||||
return (val); | |||||
} | } | ||||
static long | static long | ||||
get_cntxct(bool physical) | get_cntxct(bool physical) | ||||
{ | { | ||||
uint64_t val; | uint64_t val; | ||||
isb(); | isb(); | ||||
if (physical) | if (physical) | ||||
/* cntpct */ | val = get_el0(cntpct); | ||||
__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); | |||||
else | else | ||||
/* cntvct */ | val = get_el0(cntvct); | ||||
__asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (val)); | |||||
return (val); | return (val); | ||||
} | } | ||||
static int | static int | ||||
set_ctrl(uint32_t val, bool physical) | set_ctrl(uint32_t val, bool physical) | ||||
{ | { | ||||
if (physical) | if (physical) | ||||
/* cntp_ctl */ | set_el0(cntp_ctl, val); | ||||
__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : | |||||
[val] "r" (val)); | |||||
else | else | ||||
/* cntv_ctl */ | set_el0(cntv_ctl, val); | ||||
__asm volatile("mcr p15, 0, %[val], c14, c3, 1" : : | |||||
[val] "r" (val)); | |||||
isb(); | isb(); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
set_tval(uint32_t val, bool physical) | set_tval(uint32_t val, bool physical) | ||||
{ | { | ||||
if (physical) | if (physical) | ||||
/* cntp_tval */ | set_el0(cntp_tval, val); | ||||
__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : | |||||
[val] "r" (val)); | |||||
else | else | ||||
/* cntv_tval */ | set_el0(cntv_tval, val); | ||||
__asm volatile("mcr p15, 0, %[val], c14, c3, 0" : : | |||||
[val] "r" (val)); | |||||
isb(); | isb(); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
get_ctrl(bool physical) | get_ctrl(bool physical) | ||||
{ | { | ||||
uint32_t val; | uint32_t val; | ||||
if (physical) | if (physical) | ||||
/* cntp_ctl */ | val = get_el0(cntp_ctl); | ||||
__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); | |||||
else | else | ||||
/* cntv_ctl */ | val = get_el0(cntv_ctl); | ||||
__asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val)); | |||||
return (val); | return (val); | ||||
} | } | ||||
static void | static void | ||||
disable_user_access(void) | disable_user_access(void) | ||||
{ | { | ||||
uint32_t cntkctl; | uint32_t cntkctl; | ||||
__asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); | cntkctl = get_el1(cntkctl); | ||||
cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN | | cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN | | ||||
GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN); | GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN); | ||||
__asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); | set_el1(cntkctl, cntkctl); | ||||
isb(); | isb(); | ||||
} | } | ||||
static unsigned | static unsigned | ||||
arm_tmr_get_timecount(struct timecounter *tc) | arm_tmr_get_timecount(struct timecounter *tc) | ||||
{ | { | ||||
return (get_cntxct(arm_tmr_sc->physical)); | return (get_cntxct(arm_tmr_sc->physical)); | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
arm_tmr_probe(device_t dev) | arm_tmr_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (!ofw_bus_is_compatible(dev, "arm,armv7-timer")) | if (ofw_bus_is_compatible(dev, "arm,armv7-timer")) { | ||||
return (ENXIO); | |||||
device_set_desc(dev, "ARMv7 Generic Timer"); | device_set_desc(dev, "ARMv7 Generic Timer"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} else if (ofw_bus_is_compatible(dev, "arm,armv8-timer")) { | |||||
device_set_desc(dev, "ARMv8 Generic Timer"); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | } | ||||
return (ENXIO); | |||||
} | |||||
static int | static int | ||||
arm_tmr_attach(device_t dev) | arm_tmr_attach(device_t dev) | ||||
{ | { | ||||
struct arm_tmr_softc *sc; | struct arm_tmr_softc *sc; | ||||
phandle_t node; | phandle_t node; | ||||
pcell_t clock; | pcell_t clock; | ||||
int error; | int error; | ||||
int i; | int i; | ||||
Show All 19 Lines | if (sc->clkfreq == 0) { | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
if (bus_alloc_resources(dev, timer_spec, sc->res)) { | if (bus_alloc_resources(dev, timer_spec, sc->res)) { | ||||
device_printf(dev, "could not allocate resources\n"); | device_printf(dev, "could not allocate resources\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
#ifdef __arm__ | |||||
sc->physical = true; | sc->physical = true; | ||||
#else | |||||
sc->physical = false; | |||||
#endif | |||||
arm_tmr_sc = sc; | arm_tmr_sc = sc; | ||||
/* Setup secure, non-secure and virtual IRQs handler */ | /* Setup secure, non-secure and virtual IRQs handler */ | ||||
for (i = 0; i < 3; i++) { | for (i = 0; i < 3; i++) { | ||||
error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, | error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, | ||||
arm_tmr_intr, NULL, sc, &sc->ihl[i]); | arm_tmr_intr, NULL, sc, &sc->ihl[i]); | ||||
if (error) { | if (error) { | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | DELAY(int usec) | ||||
/* | /* | ||||
* Check the timers are setup, if not just | * Check the timers are setup, if not just | ||||
* use a for loop for the meantime | * use a for loop for the meantime | ||||
*/ | */ | ||||
if (arm_tmr_sc == NULL) { | if (arm_tmr_sc == NULL) { | ||||
for (; usec > 0; usec--) | for (; usec > 0; usec--) | ||||
for (counts = 200; counts > 0; counts--) | for (counts = 200; counts > 0; counts--) | ||||
/* | /* | ||||
* Prevent gcc from optimizing | * Prevent the compiler from optimizing | ||||
* out the loop | * out the loop | ||||
*/ | */ | ||||
cpufunc_nullop(); | cpufunc_nullop(); | ||||
return; | return; | ||||
} | } | ||||
/* Get the number of times to count */ | /* Get the number of times to count */ | ||||
counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); | counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); | ||||
Show All 20 Lines |
This set of changes (for get/set_el*) could be done separately. IT looks self contained. If you want.