xref: /netbsd-src/sys/arch/arm/nxp/imx6_pwm.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /*	$NetBSD: imx6_pwm.c,v 1.3 2021/01/27 03:10:20 thorpej Exp $	*/
28644267aSskrll /*-
38644267aSskrll  * Copyright (c) 2019  Genetec Corporation.  All rights reserved.
48644267aSskrll  * Written by Hashimoto Kenichi for Genetec Corporation.
58644267aSskrll  *
68644267aSskrll  * Redistribution and use in source and binary forms, with or without
78644267aSskrll  * modification, are permitted provided that the following conditions
88644267aSskrll  * are met:
98644267aSskrll  * 1. Redistributions of source code must retain the above copyright
108644267aSskrll  *    notice, this list of conditions and the following disclaimer.
118644267aSskrll  * 2. Redistributions in binary form must reproduce the above copyright
128644267aSskrll  *    notice, this list of conditions and the following disclaimer in the
138644267aSskrll  *    documentation and/or other materials provided with the distribution.
148644267aSskrll  *
158644267aSskrll  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168644267aSskrll  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178644267aSskrll  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188644267aSskrll  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198644267aSskrll  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
208644267aSskrll  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
218644267aSskrll  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
228644267aSskrll  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
238644267aSskrll  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248644267aSskrll  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258644267aSskrll  * SUCH DAMAGE.
268644267aSskrll  */
278644267aSskrll 
288644267aSskrll #include <sys/cdefs.h>
29*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: imx6_pwm.c,v 1.3 2021/01/27 03:10:20 thorpej Exp $");
308644267aSskrll 
318644267aSskrll #include <sys/types.h>
328644267aSskrll #include <sys/param.h>
338644267aSskrll #include <sys/bus.h>
348644267aSskrll #include <sys/device.h>
358644267aSskrll 
368644267aSskrll #include <arm/imx/imxpwmvar.h>
378644267aSskrll 
388644267aSskrll #include <dev/fdt/fdtvar.h>
398644267aSskrll 
408644267aSskrll struct imxpwm_fdt_softc {
418644267aSskrll 	struct imxpwm_softc sc_imxpwm; /* Must be first */
428644267aSskrll };
438644267aSskrll 
448644267aSskrll static int imx6_pwm_match(device_t, cfdata_t, void *);
458644267aSskrll static void imx6_pwm_attach(device_t, device_t, void *);
468644267aSskrll 
478644267aSskrll CFATTACH_DECL_NEW(imxpwm_fdt, sizeof(struct imxpwm_fdt_softc),
488644267aSskrll     imx6_pwm_match, imx6_pwm_attach, NULL, NULL);
498644267aSskrll 
508644267aSskrll static pwm_tag_t
imxpwm_get_tag(device_t dev,const void * data,size_t len)518644267aSskrll imxpwm_get_tag(device_t dev, const void *data, size_t len)
528644267aSskrll {
538644267aSskrll 	struct imxpwm_fdt_softc * const ifsc = device_private(dev);
548644267aSskrll 	struct imxpwm_softc * const sc = &ifsc->sc_imxpwm;
558644267aSskrll 	const u_int *pwm = data;
568644267aSskrll 
578644267aSskrll 	if (len < 12)
588644267aSskrll 		return NULL;
598644267aSskrll 
608644267aSskrll 	const u_int index = be32toh(pwm[1]);
618644267aSskrll 	if (index != 0)
628644267aSskrll 		return NULL;
638644267aSskrll 	const u_int period = be32toh(pwm[2]);
648644267aSskrll 
658644267aSskrll 	sc->sc_conf.period = period;
668644267aSskrll 	if (len >= 16) {
678644267aSskrll 		const u_int polarity = be32toh(pwm[3]);
688644267aSskrll 		sc->sc_conf.period = polarity ? PWM_ACTIVE_LOW : PWM_ACTIVE_HIGH;
698644267aSskrll 	}
708644267aSskrll 
718644267aSskrll 	return &sc->sc_pwm;
728644267aSskrll }
738644267aSskrll 
748644267aSskrll static struct fdtbus_pwm_controller_func imxpwm_funcs = {
758644267aSskrll 	.get_tag = imxpwm_get_tag
768644267aSskrll };
778644267aSskrll 
78*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
79*6e54367aSthorpej 	{ .compat = "fsl,imx6q-pwm" },
80*6e54367aSthorpej 	DEVICE_COMPAT_EOL
81*6e54367aSthorpej };
82*6e54367aSthorpej 
838644267aSskrll static int
imx6_pwm_match(device_t parent,cfdata_t cf,void * aux)848644267aSskrll imx6_pwm_match(device_t parent, cfdata_t cf, void *aux)
858644267aSskrll {
868644267aSskrll 	struct fdt_attach_args * const faa = aux;
878644267aSskrll 
88*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
898644267aSskrll }
908644267aSskrll 
918644267aSskrll void
imx6_pwm_attach(device_t parent,device_t self,void * aux)928644267aSskrll imx6_pwm_attach(device_t parent, device_t self, void *aux)
938644267aSskrll {
948644267aSskrll 	struct imxpwm_fdt_softc * const ifsc = device_private(self);
958644267aSskrll 	struct imxpwm_softc * const sc = &ifsc->sc_imxpwm;
968644267aSskrll 	struct fdt_attach_args * const faa = aux;
978644267aSskrll 	const int phandle = faa->faa_phandle;
988644267aSskrll 	char intrstr[128];
998644267aSskrll 	bus_addr_t addr;
1008644267aSskrll 	bus_size_t size;
1018644267aSskrll 	int error;
1028644267aSskrll 
1038644267aSskrll 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1048644267aSskrll 		aprint_error(": couldn't get PWM registers\n");
1058644267aSskrll 		return;
1068644267aSskrll 	}
1078644267aSskrll 
1088644267aSskrll 	sc->sc_dev = self;
1098644267aSskrll 	sc->sc_iot = faa->faa_bst;
1108644267aSskrll 
1118644267aSskrll 	error = bus_space_map(sc->sc_iot, addr, size, 0,
1128644267aSskrll 	    &sc->sc_ioh);
1138644267aSskrll 	if (error) {
1148644267aSskrll 		aprint_error(": couldn't map gpc registers: %d\n", error);
1158644267aSskrll 		return;
1168644267aSskrll 	}
1178644267aSskrll 
1188644267aSskrll 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1198644267aSskrll 		aprint_error_dev(self, "failed to decode interrupt\n");
1208644267aSskrll 		return;
1218644267aSskrll 	}
1228644267aSskrll 
12382b8374aSjmcneill 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
12482b8374aSjmcneill 	    0, imxpwm_intr, sc, device_xname(self));
1258644267aSskrll 	if (sc->sc_ih == NULL) {
1268644267aSskrll 		aprint_error_dev(self, "failed to establish interrupt on %s\n",
1278644267aSskrll 		    intrstr);
1288644267aSskrll 		return;
1298644267aSskrll 	}
1308644267aSskrll 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
1318644267aSskrll 
1328644267aSskrll 	sc->sc_clk = fdtbus_clock_get(phandle, "per");
1338644267aSskrll 	if (sc->sc_clk == NULL) {
1348644267aSskrll 		aprint_error(": couldn't get clk\n");
1358644267aSskrll 		return;
1368644267aSskrll 	}
1378644267aSskrll 	sc->sc_freq = clk_get_rate(sc->sc_clk);
1388644267aSskrll 
1398644267aSskrll 	imxpwm_attach_common(sc);
1408644267aSskrll 
1418644267aSskrll 	fdtbus_register_pwm_controller(self, phandle,
1428644267aSskrll 	    &imxpwm_funcs);
1438644267aSskrll 
1448644267aSskrll 	return;
1458644267aSskrll }
1468644267aSskrll 
147