#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <machine/fdt.h>
#include <dev/ofw/fdt.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/openfirm.h>
#include <dev/ic/bcm2835_mbox.h>
#include <dev/ic/bcm2835_vcprop.h>
enum {
BCMCLOCK_CLOCK_TIMER = 17,
BCMCLOCK_CLOCK_UART = 19,
BCMCLOCK_CLOCK_VPU = 20,
BCMCLOCK_CLOCK_V3D = 21,
BCMCLOCK_CLOCK_ISP = 22,
BCMCLOCK_CLOCK_H264 = 23,
BCMCLOCK_CLOCK_VEC = 24,
BCMCLOCK_CLOCK_HSM = 25,
BCMCLOCK_CLOCK_SDRAM = 26,
BCMCLOCK_CLOCK_TSENS = 27,
BCMCLOCK_CLOCK_EMMC = 28,
BCMCLOCK_CLOCK_PERIIMAGE = 29,
BCMCLOCK_CLOCK_PWM = 30,
BCMCLOCK_CLOCK_PCM = 31,
BCMCLOCK_NCLOCK
};
struct bcmclock_softc {
struct device sc_dev;
struct clock_device sc_cd;
};
int bcmclock_match(struct device *, void *, void *);
void bcmclock_attach(struct device *, struct device *, void *);
const struct cfattach bcmclock_ca = {
sizeof(struct bcmclock_softc),
bcmclock_match,
bcmclock_attach,
};
uint32_t bcmclock_get_frequency(void *, uint32_t *);
struct cfdriver bcmclock_cd = { NULL, "bcmclock", DV_DULL };
int
bcmclock_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return (OF_is_compatible(faa->fa_node, "brcm,bcm2711-cprman") ||
OF_is_compatible(faa->fa_node, "brcm,bcm2835-cprman"));
}
void
bcmclock_attach(struct device *parent, struct device *self, void *aux)
{
struct bcmclock_softc *sc = (struct bcmclock_softc *)self;
struct fdt_attach_args *faa = aux;
printf("\n");
sc->sc_cd.cd_node = faa->fa_node;
sc->sc_cd.cd_cookie = sc;
sc->sc_cd.cd_get_frequency = bcmclock_get_frequency;
clock_register(&sc->sc_cd);
}
uint32_t
bcmclock_get_frequency(void *cookie, uint32_t *cells)
{
struct request {
struct vcprop_buffer_hdr vb_hdr;
struct vcprop_tag_clockrate vbt_clkrate;
struct vcprop_tag end;
} __packed;
uint32_t result;
struct request req = {
.vb_hdr = {
.vpb_len = sizeof(req),
.vpb_rcode = VCPROP_PROCESS_REQUEST,
},
.vbt_clkrate = {
.tag = {
.vpt_tag = VCPROPTAG_GET_CLOCKRATE,
.vpt_len = VCPROPTAG_LEN(req.vbt_clkrate),
.vpt_rcode = VCPROPTAG_REQUEST
},
},
.end = {
.vpt_tag = VCPROPTAG_NULL
}
};
switch (cells[0]) {
case BCMCLOCK_CLOCK_TIMER:
break;
case BCMCLOCK_CLOCK_UART:
req.vbt_clkrate.id = VCPROP_CLK_UART;
break;
case BCMCLOCK_CLOCK_VPU:
req.vbt_clkrate.id = VCPROP_CLK_CORE;
break;
case BCMCLOCK_CLOCK_V3D:
req.vbt_clkrate.id = VCPROP_CLK_V3D;
break;
case BCMCLOCK_CLOCK_ISP:
req.vbt_clkrate.id = VCPROP_CLK_ISP;
break;
case BCMCLOCK_CLOCK_H264:
req.vbt_clkrate.id = VCPROP_CLK_H264;
break;
case BCMCLOCK_CLOCK_VEC:
break;
case BCMCLOCK_CLOCK_HSM:
break;
case BCMCLOCK_CLOCK_SDRAM:
req.vbt_clkrate.id = VCPROP_CLK_SDRAM;
break;
case BCMCLOCK_CLOCK_TSENS:
break;
case BCMCLOCK_CLOCK_EMMC:
req.vbt_clkrate.id = VCPROP_CLK_EMMC;
break;
case BCMCLOCK_CLOCK_PERIIMAGE:
break;
case BCMCLOCK_CLOCK_PWM:
req.vbt_clkrate.id = VCPROP_CLK_PWM;
break;
case BCMCLOCK_CLOCK_PCM:
break;
}
if (req.vbt_clkrate.id == 0) {
printf("bcmclock[unknown]: request to unknown clock type %d\n",
cells[0]);
return 0;
}
bcmmbox_post(BCMMBOX_CHANARM2VC, &req, sizeof(req), &result);
if (vcprop_tag_success_p(&req.vbt_clkrate.tag))
return req.vbt_clkrate.rate;
printf("%s: vcprop result %x:%x\n", __func__, req.vb_hdr.vpb_rcode,
req.vbt_clkrate.tag.vpt_rcode);
return 0;
}