Home | History | Annotate | Line # | Download | only in fdt
      1 /* $NetBSD: pinctrl_single.c,v 1.7 2026/08/27 20:06:29 yurix Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: pinctrl_single.c,v 1.7 2026/08/27 20:06:29 yurix Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/systm.h>
     36 #include <sys/gpio.h>
     37 
     38 #include <dev/fdt/fdtvar.h>
     39 
     40 #define	PINCTRL_FLAG_PINCONF	 __BIT(0)	/* supports generic pinconf */
     41 #define PINCTRL_FLAG_BIT_PER_MUX __BIT(1)	/* bit-per-mux flag from DT */
     42 
     43 struct pinctrl_single_config {
     44 	uint32_t	flags;
     45 };
     46 
     47 static const struct pinctrl_single_config pinctrl_config = {
     48 };
     49 
     50 static const struct pinctrl_single_config pinconf_config = {
     51 	.flags = PINCTRL_FLAG_PINCONF
     52 };
     53 
     54 static const struct device_compatible_entry compat_data[] = {
     55 	{ .compat = "pinctrl-single",	.data = &pinctrl_config },
     56 	{ .compat = "pinconf-single",	.data = &pinconf_config },
     57 	DEVICE_COMPAT_EOL
     58 };
     59 
     60 struct pinctrl_single_softc {
     61 	device_t			sc_dev;
     62 	int				sc_phandle;
     63 	bus_space_tag_t			sc_bst;
     64 	bus_space_handle_t		sc_bsh;
     65 	uint32_t			sc_flags;
     66 	u_int				sc_regwidth;
     67 	u_int				sc_funcmask;
     68 };
     69 
     70 static void
     71 pinctrl_single_pins_write(struct pinctrl_single_softc *sc, u_int off, u_int val,
     72 	u_int mask)
     73 {
     74 	union {
     75 		uint32_t reg32;
     76 		uint16_t reg16;
     77 		uint8_t reg8;
     78 	} u;
     79 
     80 	aprint_debug_dev(sc->sc_dev,
     81 	    "writing %#x mask %#x to %#x\n", val, mask, off);
     82 
     83 	switch (sc->sc_regwidth) {
     84 	case 8:
     85 		u.reg8 = bus_space_read_1(sc->sc_bst, sc->sc_bsh, off);
     86 		u.reg8 &= ~mask;
     87 		u.reg8 |= val;
     88 		bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, u.reg8);
     89 		break;
     90 	case 16:
     91 		u.reg16 = bus_space_read_2(sc->sc_bst, sc->sc_bsh, off);
     92 		u.reg16 &= ~mask;
     93 		u.reg16 |= val;
     94 		bus_space_write_2(sc->sc_bst, sc->sc_bsh, off, u.reg16);
     95 		break;
     96 	case 32:
     97 		u.reg32 = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
     98 		u.reg32 &= ~mask;
     99 		u.reg32 |= val;
    100 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, u.reg32);
    101 		break;
    102 	default:
    103 		device_printf(sc->sc_dev, "%s: unsupported reg width %d\n",
    104 		    __func__, sc->sc_regwidth);
    105 		break;
    106 	}
    107 }
    108 
    109 static int
    110 pinctrl_single_pins_set_config(device_t dev, const void *data, size_t len)
    111 {
    112 	struct pinctrl_single_softc * const sc = device_private(dev);
    113 	const u_int *pins;
    114 	int pinslen;
    115 
    116 	switch (len) {
    117 	case 4:
    118 		if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX))
    119 			return -1;
    120 		break;
    121 	case 8:
    122 		break;
    123 	default:
    124 		return -1;
    125 	}
    126 
    127 	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
    128 
    129 	if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX)) {
    130 		pins = fdtbus_get_prop(phandle, "pinctrl-single,bits",
    131 		    &pinslen);
    132 	} else {
    133 		pins = fdtbus_get_prop(phandle, "pinctrl-single,pins",
    134 		    &pinslen);
    135 	}
    136 	if (pins == NULL)
    137 		return -1;
    138 
    139 	while (pinslen >= 4 + len) {
    140 		if (ISSET(sc->sc_flags, PINCTRL_FLAG_BIT_PER_MUX)) {
    141 			const int off = be32toh(pins[0]);
    142 			const int val = be32toh(pins[1]);
    143 			const int mask = be32toh(pins[2]);
    144 
    145 			pinctrl_single_pins_write(sc, off, val, mask);
    146 		} else {
    147 			const int off = be32toh(pins[0]);
    148 			const int val = be32toh(pins[1]);
    149 			const int mux = len == 4 ? 0 : be32toh(pins[2]);
    150 
    151 			pinctrl_single_pins_write(sc, off, val | mux,
    152 			    sc->sc_funcmask);
    153 		}
    154 
    155 		pins += 1 + (len / 4);
    156 		pinslen -= (4 + len);
    157 	}
    158 
    159 	return 0;
    160 }
    161 
    162 static struct fdtbus_pinctrl_controller_func pinctrl_single_pins_funcs = {
    163 	.set_config = pinctrl_single_pins_set_config,
    164 };
    165 
    166 static int
    167 pinctrl_single_match(device_t parent, cfdata_t cf, void *aux)
    168 {
    169 	struct fdt_attach_args * const faa = aux;
    170 
    171 	return of_compatible_match(faa->faa_phandle, compat_data);
    172 }
    173 
    174 static void
    175 pinctrl_single_attach(device_t parent, device_t self, void *aux)
    176 {
    177 	struct pinctrl_single_softc * const sc = device_private(self);
    178 	struct fdt_attach_args * const faa = aux;
    179 	const int phandle = faa->faa_phandle;
    180 	const struct pinctrl_single_config *conf;
    181 	bus_addr_t addr;
    182 	bus_size_t size;
    183 	int child;
    184 
    185 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    186 		aprint_error(": couldn't get registers\n");
    187 		return;
    188 	}
    189 
    190 	conf = of_compatible_lookup(phandle, compat_data)->data;
    191 
    192 	sc->sc_dev = self;
    193 	sc->sc_phandle = phandle;
    194 	sc->sc_bst = faa->faa_bst;
    195 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    196 		aprint_error(": couldn't map registers\n");
    197 		return;
    198 	}
    199 	sc->sc_flags = conf->flags;
    200 
    201 	if (of_getprop_uint32(phandle, "pinctrl-single,register-width", &sc->sc_regwidth) != 0) {
    202 		aprint_error(": missing 'pinctrl-single,register-width' property\n");
    203 		return;
    204 	}
    205 	if (of_getprop_uint32(phandle, "pinctrl-single,function-mask", &sc->sc_funcmask) != 0) {
    206 		aprint_error(": missing 'pinctrl-single,function-mask' property\n");
    207 		return;
    208 	}
    209 
    210 	switch (sc->sc_regwidth) {
    211 	case 8:
    212 	case 16:
    213 	case 32:
    214 		break;
    215 	default:
    216 		aprint_error(": register width %d not supported\n", sc->sc_regwidth);
    217 		return;
    218 	}
    219 
    220 	if (of_hasprop(phandle, "pinctrl-single,bit-per-mux")) {
    221 		sc->sc_flags |= PINCTRL_FLAG_BIT_PER_MUX;
    222 	}
    223 
    224 	aprint_naive("\n");
    225 	aprint_normal("\n");
    226 
    227 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
    228 		if (of_hasprop(child, "pinctrl-single,pins")
    229 		    || of_hasprop(child, "pinctrl-single,bits")) {
    230 			fdtbus_register_pinctrl_config(self, child,
    231 			    &pinctrl_single_pins_funcs);
    232 		}
    233 	}
    234 
    235 	fdtbus_pinctrl_set_config(phandle, "default");
    236 }
    237 
    238 CFATTACH_DECL_NEW(pinctrl_single, sizeof(struct pinctrl_single_softc),
    239 	pinctrl_single_match, pinctrl_single_attach, NULL, NULL);
    240