xref: /openbsd-src/sys/dev/fdt/imxpwm.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /*	$OpenBSD: imxpwm.c,v 1.2 2022/04/06 18:59:28 naddy Exp $	*/
28174da49Spatrick /*
38174da49Spatrick  * Copyright (c) 2018-2020 Patrick Wildt <patrick@blueri.se>
48174da49Spatrick  *
58174da49Spatrick  * Permission to use, copy, modify, and distribute this software for any
68174da49Spatrick  * purpose with or without fee is hereby granted, provided that the above
78174da49Spatrick  * copyright notice and this permission notice appear in all copies.
88174da49Spatrick  *
98174da49Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108174da49Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118174da49Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128174da49Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138174da49Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148174da49Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158174da49Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168174da49Spatrick  */
178174da49Spatrick 
188174da49Spatrick #include <sys/param.h>
198174da49Spatrick #include <sys/systm.h>
208174da49Spatrick #include <sys/malloc.h>
218174da49Spatrick #include <sys/device.h>
228174da49Spatrick #include <sys/sysctl.h>
238174da49Spatrick 
248174da49Spatrick #include <machine/bus.h>
258174da49Spatrick #include <machine/fdt.h>
268174da49Spatrick 
278174da49Spatrick #include <dev/ofw/openfirm.h>
288174da49Spatrick #include <dev/ofw/fdt.h>
298174da49Spatrick #include <dev/ofw/ofw_clock.h>
308174da49Spatrick #include <dev/ofw/ofw_pinctrl.h>
318174da49Spatrick #include <dev/ofw/ofw_misc.h>
328174da49Spatrick 
338174da49Spatrick #define PWM_CR				0x00
348174da49Spatrick #define  PWM_CR_EN				(1 << 0)
358174da49Spatrick #define  PWM_CR_SWR				(1 << 3)
368174da49Spatrick #define  PWM_CR_CLKSRC_IPG			(1 << 16)
378174da49Spatrick #define  PWM_CR_CLKSRC_IPG_HIGH			(2 << 16)
388174da49Spatrick #define  PWM_CR_DBGEN				(1 << 22)
398174da49Spatrick #define  PWM_CR_WAITEN				(1 << 23)
408174da49Spatrick #define  PWM_CR_DOZEEN				(1 << 24)
418174da49Spatrick #define  PWM_CR_PRESCALER(x)			((((x) - 1) & 0xfff) << 4)
428174da49Spatrick #define  PWM_CR_PRESCALER_SHIFT			4
438174da49Spatrick #define  PWM_CR_PRESCALER_MASK			0xfff
448174da49Spatrick #define PWM_SR				0x04
458174da49Spatrick #define  PWM_SR_FIFOAV_4WORDS			0x4
468174da49Spatrick #define  PWM_SR_FIFOAV_MASK			0x7
478174da49Spatrick #define PWM_SAR				0x0c
488174da49Spatrick #define PWM_PR				0x10
498174da49Spatrick #define  PWM_PR_MAX				0xfffe
508174da49Spatrick 
518174da49Spatrick #define NS_PER_S			1000000000
528174da49Spatrick 
538174da49Spatrick #define HREAD4(sc, reg)							\
548174da49Spatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
558174da49Spatrick #define HWRITE4(sc, reg, val)						\
568174da49Spatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
578174da49Spatrick #define HSET4(sc, reg, bits)						\
588174da49Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
598174da49Spatrick #define HCLR4(sc, reg, bits)						\
608174da49Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
618174da49Spatrick 
628174da49Spatrick struct imxpwm_softc {
638174da49Spatrick 	struct device		sc_dev;
648174da49Spatrick 	bus_space_tag_t		sc_iot;
658174da49Spatrick 	bus_space_handle_t	sc_ioh;
668174da49Spatrick 
678174da49Spatrick 	uint32_t		sc_dcycles;
688174da49Spatrick 	uint32_t		sc_clkin;
698174da49Spatrick 	struct pwm_device	sc_pd;
708174da49Spatrick };
718174da49Spatrick 
728174da49Spatrick int	imxpwm_match(struct device *, void *, void *);
738174da49Spatrick void	imxpwm_attach(struct device *, struct device *, void *);
748174da49Spatrick 
75*471aeecfSnaddy const struct cfattach imxpwm_ca = {
768174da49Spatrick 	sizeof(struct imxpwm_softc), imxpwm_match, imxpwm_attach
778174da49Spatrick };
788174da49Spatrick 
798174da49Spatrick struct cfdriver imxpwm_cd = {
808174da49Spatrick 	NULL, "imxpwm", DV_DULL
818174da49Spatrick };
828174da49Spatrick 
838174da49Spatrick int	imxpwm_get_state(void *, uint32_t *, struct pwm_state *);
848174da49Spatrick int	imxpwm_set_state(void *, uint32_t *, struct pwm_state *);
858174da49Spatrick 
868174da49Spatrick int
imxpwm_match(struct device * parent,void * match,void * aux)878174da49Spatrick imxpwm_match(struct device *parent, void *match, void *aux)
888174da49Spatrick {
898174da49Spatrick 	struct fdt_attach_args *faa = aux;
908174da49Spatrick 
918174da49Spatrick 	return OF_is_compatible(faa->fa_node, "fsl,imx27-pwm");
928174da49Spatrick }
938174da49Spatrick 
948174da49Spatrick void
imxpwm_attach(struct device * parent,struct device * self,void * aux)958174da49Spatrick imxpwm_attach(struct device *parent, struct device *self, void *aux)
968174da49Spatrick {
978174da49Spatrick 	struct imxpwm_softc *sc = (struct imxpwm_softc *)self;
988174da49Spatrick 	struct fdt_attach_args *faa = aux;
998174da49Spatrick 
1008174da49Spatrick 	if (faa->fa_nreg < 1)
1018174da49Spatrick 		return;
1028174da49Spatrick 
1038174da49Spatrick 	sc->sc_clkin = clock_get_frequency(faa->fa_node, "per");
1048174da49Spatrick 	if (sc->sc_clkin == 0) {
1058174da49Spatrick 		printf(": no clock\n");
1068174da49Spatrick 		return;
1078174da49Spatrick 	}
1088174da49Spatrick 
1098174da49Spatrick 	sc->sc_iot = faa->fa_iot;
1108174da49Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1118174da49Spatrick 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1128174da49Spatrick 		printf(": can't map registers");
1138174da49Spatrick 		return;
1148174da49Spatrick 	}
1158174da49Spatrick 
1168174da49Spatrick 	printf("\n");
1178174da49Spatrick 
1188174da49Spatrick 	pinctrl_byname(faa->fa_node, "default");
1198174da49Spatrick 
1208174da49Spatrick 	clock_enable_all(faa->fa_node);
1218174da49Spatrick 	reset_deassert_all(faa->fa_node);
1228174da49Spatrick 
1238174da49Spatrick 	sc->sc_pd.pd_node = faa->fa_node;
1248174da49Spatrick 	sc->sc_pd.pd_cookie = sc;
1258174da49Spatrick 	sc->sc_pd.pd_get_state = imxpwm_get_state;
1268174da49Spatrick 	sc->sc_pd.pd_set_state = imxpwm_set_state;
1278174da49Spatrick 
1288174da49Spatrick 	pwm_register(&sc->sc_pd);
1298174da49Spatrick }
1308174da49Spatrick 
1318174da49Spatrick int
imxpwm_get_state(void * cookie,uint32_t * cells,struct pwm_state * ps)1328174da49Spatrick imxpwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
1338174da49Spatrick {
1348174da49Spatrick 	struct imxpwm_softc *sc = cookie;
1358174da49Spatrick 	uint64_t dcycles, pcycles, prescale, pwmclk;
1368174da49Spatrick 	int enabled = 0;
1378174da49Spatrick 
1388174da49Spatrick 	prescale = ((HREAD4(sc, PWM_CR) >> PWM_CR_PRESCALER_SHIFT) &
1398174da49Spatrick 	    PWM_CR_PRESCALER_MASK) + 1;
1408174da49Spatrick 	pwmclk = (sc->sc_clkin + (prescale / 2)) / prescale;
1418174da49Spatrick 	if (pwmclk == 0)
1428174da49Spatrick 		return EINVAL;
1438174da49Spatrick 
1448174da49Spatrick 	if (HREAD4(sc, PWM_CR) & PWM_CR_EN)
1458174da49Spatrick 		enabled = 1;
1468174da49Spatrick 
1478174da49Spatrick 	pcycles = HREAD4(sc, PWM_PR);
1488174da49Spatrick 	if (pcycles >= PWM_PR_MAX)
1498174da49Spatrick 		pcycles = PWM_PR_MAX;
1508174da49Spatrick 	pcycles = (pcycles + 2) * NS_PER_S;
1518174da49Spatrick 	pcycles = (pcycles + (pwmclk / 2)) / pwmclk;
1528174da49Spatrick 
1538174da49Spatrick 	dcycles = sc->sc_dcycles;
1548174da49Spatrick 	if (enabled)
1558174da49Spatrick 		dcycles = HREAD4(sc, PWM_SAR);
1568174da49Spatrick 	dcycles = dcycles * NS_PER_S;
1578174da49Spatrick 	dcycles = (dcycles + (pwmclk / 2)) / pwmclk;
1588174da49Spatrick 
1598174da49Spatrick 	memset(ps, 0, sizeof(struct pwm_state));
1608174da49Spatrick 	ps->ps_period = pcycles;
1618174da49Spatrick 	ps->ps_pulse_width = dcycles;
1628174da49Spatrick 	ps->ps_enabled = enabled;
1638174da49Spatrick 	return 0;
1648174da49Spatrick }
1658174da49Spatrick 
1668174da49Spatrick int
imxpwm_set_state(void * cookie,uint32_t * cells,struct pwm_state * ps)1678174da49Spatrick imxpwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
1688174da49Spatrick {
1698174da49Spatrick 	struct imxpwm_softc *sc = cookie;
1708174da49Spatrick 	uint64_t dcycles, pcycles, prescale;
1718174da49Spatrick 	int i;
1728174da49Spatrick 
1738174da49Spatrick 	if (ps->ps_enabled) {
1748174da49Spatrick 		pcycles = sc->sc_clkin;
1758174da49Spatrick 		pcycles = (pcycles * ps->ps_period) / NS_PER_S;
1768174da49Spatrick 		prescale = pcycles / 0x10000 + 1;
1778174da49Spatrick 
1788174da49Spatrick 		if (ps->ps_period == 0 || prescale == 0)
1798174da49Spatrick 			return EINVAL;
1808174da49Spatrick 
1818174da49Spatrick 		pcycles = pcycles / prescale;
1828174da49Spatrick 		dcycles = (pcycles * ps->ps_pulse_width) / ps->ps_period;
1838174da49Spatrick 
1848174da49Spatrick 		if (pcycles > 2)
1858174da49Spatrick 			pcycles -= 2;
1868174da49Spatrick 		else
1878174da49Spatrick 			pcycles = 0;
1888174da49Spatrick 	}
1898174da49Spatrick 
1908174da49Spatrick 	/* disable and flush fifo */
1918174da49Spatrick 	HCLR4(sc, PWM_CR, PWM_CR_EN);
1928174da49Spatrick 	HWRITE4(sc, PWM_CR, PWM_CR_SWR);
1938174da49Spatrick 	for (i = 0; i < 5; i++) {
1948174da49Spatrick 		delay(1000);
1958174da49Spatrick 		if ((HREAD4(sc, PWM_CR) & PWM_CR_SWR) == 0)
1968174da49Spatrick 			break;
1978174da49Spatrick 	}
1988174da49Spatrick 	if (i == 5) {
1998174da49Spatrick 		printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
2008174da49Spatrick 		return ETIMEDOUT;
2018174da49Spatrick 	}
2028174da49Spatrick 
2038174da49Spatrick 	if (ps->ps_enabled) {
2048174da49Spatrick 		HWRITE4(sc, PWM_SAR, dcycles);
2058174da49Spatrick 		HWRITE4(sc, PWM_PR, pcycles);
2068174da49Spatrick 
2078174da49Spatrick 		sc->sc_dcycles = dcycles;
2088174da49Spatrick 
2098174da49Spatrick 		HWRITE4(sc, PWM_CR, PWM_CR_PRESCALER(prescale) |
2108174da49Spatrick 		    PWM_CR_DOZEEN | PWM_CR_WAITEN |
2118174da49Spatrick 		    PWM_CR_DBGEN | PWM_CR_CLKSRC_IPG_HIGH |
2128174da49Spatrick 		    PWM_CR_EN);
2138174da49Spatrick 	}
2148174da49Spatrick 
2158174da49Spatrick 	return 0;
2168174da49Spatrick }
217