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