#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <dev/fdt/fdtvar.h>
#include <arm/fdt/arm_fdtvar.h>
#include <arm/imx/imx23_timrotreg.h>
#include <arm/imx/imx23var.h>
#include <arm/pic/picvar.h>
#include "opt_arm_timer.h"
#ifdef __HAVE_GENERIC_CPU_INITCLOCKS
void imx23_timrot_cpu_initclocks(void);
#else
#define imx23_timrot_cpu_initclocks cpu_initclocks
#endif
struct imx23_timrot_softc {
bus_space_tag_t sc_iot;
bus_space_handle_t sc_hdl;
};
static int imx23_timrot_match(device_t, cfdata_t, void *);
static void imx23_timrot_attach(device_t, device_t, void *);
static void imx23_timrot_reset(struct imx23_timrot_softc *);
int imx23_timrot_systimer_irq(void *frame);
int imx23_timrot_stattimer_irq(void *);
static void imx23_timrot_setstatclockrate(int);
CFATTACH_DECL_NEW(imx23timrot, sizeof(struct imx23_timrot_softc),
imx23_timrot_match, imx23_timrot_attach, NULL, NULL);
static const struct device_compatible_entry compat_data[] = {
{ .compat = "fsl,imx23-timrot" },
{ .compat = "fsl,timrot" },
DEVICE_COMPAT_EOL
};
static struct imx23_timrot_softc *timer_sc = NULL;
#define TIMROT_SOFT_RST_LOOP 455
#define TIMROT_READ(sc, reg) \
bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
#define TIMROT_WRITE(sc, reg, val) \
bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
#define TIMER_WRITE(sc, reg, val) \
bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
#define TIMER_WRITE_2(sc, reg, val) \
bus_space_write_2(sc->sc_iot, sc->sc_hdl, (reg), (val))
#define SELECT_32KHZ 0x8
#define SOURCE_32KHZ_HZ 32000
#define IRQ_EN HW_TIMROT_TIMCTRL0_IRQ_EN
#define UPDATE HW_TIMROT_TIMCTRL0_UPDATE
#define RELOAD HW_TIMROT_TIMCTRL0_RELOAD
static int
imx23_timrot_match(device_t parent, cfdata_t match, void *aux)
{
struct fdt_attach_args * const faa = aux;
return of_compatible_match(faa->faa_phandle, compat_data);
}
static void
imx23_timrot_attach(device_t parent, device_t self, void *aux)
{
struct imx23_timrot_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
char intrstr[128];
timer_sc = sc;
sc->sc_iot = faa->faa_bst;
stathz = (hz>>1);
bus_addr_t addr;
bus_size_t size;
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
aprint_error(": couldn't get register address\n");
return;
}
if (bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_hdl)) {
aprint_error(": couldn't map registers\n");
return;
}
imx23_timrot_reset(sc);
if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
aprint_error(": failed to decode interrupt\n");
return;
}
void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_CLOCK, 0,
imx23_timrot_systimer_irq, NULL,
device_xname(self));
if (ih == NULL) {
aprint_error_dev(self,
"couldn't install systimer interrupt handler\n");
return;
}
aprint_normal(": systimer on %s", intrstr);
if (!fdtbus_intr_str(phandle, 1, intrstr, sizeof(intrstr))) {
aprint_error(": failed to decode interrupt\n");
return;
}
ih = fdtbus_intr_establish_xname(phandle, 1, IPL_CLOCK, 0,
imx23_timrot_stattimer_irq, NULL,
device_xname(self));
if (ih == NULL) {
aprint_error_dev(self,
"couldn't install stattimer interrupt handler\n");
return;
}
aprint_normal(": stattimer on %s\n", intrstr);
arm_fdt_timer_register(imx23_timrot_cpu_initclocks);
arm_fdt_timer_register_setstatclockrate(imx23_timrot_setstatclockrate);
}
void
imx23_timrot_cpu_initclocks(void)
{
struct imx23_timrot_softc *sc = timer_sc;
uint32_t ctrl = IRQ_EN | UPDATE | RELOAD | SELECT_32KHZ;
TIMER_WRITE_2(sc, HW_TIMROT_TIMCOUNT0,
__SHIFTIN(SOURCE_32KHZ_HZ / hz - 1,
HW_TIMROT_TIMCOUNT0_FIXED_COUNT));
TIMER_WRITE(sc, HW_TIMROT_TIMCTRL0, ctrl);
TIMER_WRITE_2(sc, HW_TIMROT_TIMCOUNT1,
__SHIFTIN(SOURCE_32KHZ_HZ / stathz - 1,
HW_TIMROT_TIMCOUNT1_FIXED_COUNT));
TIMER_WRITE(sc, HW_TIMROT_TIMCTRL1, ctrl);
}
static void
imx23_timrot_setstatclockrate(int newhz)
{
struct imx23_timrot_softc *sc = timer_sc;
TIMER_WRITE_2(sc, HW_TIMROT_TIMCOUNT1,
__SHIFTIN(SOURCE_32KHZ_HZ / newhz - 1,
HW_TIMROT_TIMCOUNT1_FIXED_COUNT));
}
int
imx23_timrot_systimer_irq(void *frame)
{
struct imx23_timrot_softc *sc = timer_sc;
hardclock(frame);
TIMER_WRITE(sc, HW_TIMROT_TIMCTRL0_CLR, HW_TIMROT_TIMCTRL0_IRQ);
return 1;
}
int
imx23_timrot_stattimer_irq(void *frame)
{
struct imx23_timrot_softc *sc = timer_sc;
statclock(frame);
TIMER_WRITE(sc, HW_TIMROT_TIMCTRL1_CLR, HW_TIMROT_TIMCTRL1_IRQ);
return 1;
}
static void
imx23_timrot_reset(struct imx23_timrot_softc *sc)
{
unsigned int loop;
TIMROT_WRITE(sc, HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_SFTRST);
loop = 0;
while ((TIMROT_READ(sc, HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_SFTRST) ||
(loop < TIMROT_SOFT_RST_LOOP))
loop++;
TIMROT_WRITE(sc, HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_CLKGATE);
TIMROT_WRITE(sc, HW_TIMROT_ROTCTRL_SET, HW_TIMROT_ROTCTRL_SFTRST);
while (!(TIMROT_READ(sc, HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_CLKGATE))
continue;
TIMROT_WRITE(sc, HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_SFTRST);
loop = 0;
while ((TIMROT_READ(sc, HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_SFTRST) ||
(loop < TIMROT_SOFT_RST_LOOP))
loop++;
TIMROT_WRITE(sc, HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_CLKGATE);
while (TIMROT_READ(sc, HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_CLKGATE)
continue;
}