1 /* $OpenBSD: qcpmicgpio.c,v 1.3 2022/11/09 19:25:50 patrick Exp $ */ 2 /* 3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/malloc.h> 20 #include <sys/systm.h> 21 22 #include <machine/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/fdt/spmivar.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_gpio.h> 29 #include <dev/ofw/fdt.h> 30 31 /* Registers. */ 32 #define GPIO_TYPE 0x04 33 #define GPIO_TYPE_VAL 0x10 34 #define GPIO_SUBTYPE 0x05 35 #define GPIO_SUBTYPE_GPIO_4CH 0x1 36 #define GPIO_SUBTYPE_GPIOC_4CH 0x5 37 #define GPIO_SUBTYPE_GPIO_8CH 0x9 38 #define GPIO_SUBTYPE_GPIOC_8CH 0xd 39 #define GPIO_SUBTYPE_GPIO_LV 0x10 40 #define GPIO_SUBTYPE_GPIO_MV 0x11 41 #define GPIO_PIN_OFF(x) (0x100 * (x)) 42 #define GPIO_PIN_STATUS 0x10 43 #define GPIO_PIN_STATUS_ON (1 << 0) 44 #define GPIO_PIN_MODE 0x40 45 #define GPIO_PIN_MODE_VALUE (1 << 0) 46 #define GPIO_PIN_MODE_DIR_SHIFT 4 47 #define GPIO_PIN_MODE_DIR_MASK 0x7 48 #define GPIO_PIN_MODE_DIR_LVMV_SHIFT 0 49 #define GPIO_PIN_MODE_DIR_LVMV_MASK 0x3 50 #define GPIO_PIN_MODE_DIR_DIGITAL_IN 0 51 #define GPIO_PIN_MODE_DIR_DIGITAL_OUT 1 52 #define GPIO_PIN_MODE_DIR_DIGITAL_IO 2 53 #define GPIO_PIN_MODE_DIR_ANALOG_PT 3 54 #define GPIO_PIN_LVMV_DOUT_CTL 0x44 55 #define GPIO_PIN_LVMV_DOUT_CTL_INVERT (1U << 7) 56 57 struct qcpmicgpio_softc { 58 struct device sc_dev; 59 int sc_node; 60 61 spmi_tag_t sc_tag; 62 int8_t sc_sid; 63 uint16_t sc_addr; 64 65 int sc_is_lv_mv; 66 67 int sc_npins; 68 struct gpio_controller sc_gc; 69 }; 70 71 int qcpmicgpio_match(struct device *, void *, void *); 72 void qcpmicgpio_attach(struct device *, struct device *, void *); 73 74 const struct cfattach qcpmicgpio_ca = { 75 sizeof(struct qcpmicgpio_softc), qcpmicgpio_match, qcpmicgpio_attach 76 }; 77 78 struct cfdriver qcpmicgpio_cd = { 79 NULL, "qcpmicgpio", DV_DULL 80 }; 81 82 uint8_t qcpmicgpio_read(struct qcpmicgpio_softc *, uint16_t); 83 void qcpmicgpio_write(struct qcpmicgpio_softc *, uint16_t, uint8_t); 84 85 void qcpmicgpio_config_pin(void *, uint32_t *, int); 86 int qcpmicgpio_get_pin(void *, uint32_t *); 87 void qcpmicgpio_set_pin(void *, uint32_t *, int); 88 89 int 90 qcpmicgpio_match(struct device *parent, void *match, void *aux) 91 { 92 struct spmi_attach_args *saa = aux; 93 94 return OF_is_compatible(saa->sa_node, "qcom,spmi-gpio"); 95 } 96 97 void 98 qcpmicgpio_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct spmi_attach_args *saa = aux; 101 struct qcpmicgpio_softc *sc = (struct qcpmicgpio_softc *)self; 102 uint8_t reg; 103 104 sc->sc_addr = OF_getpropint(saa->sa_node, "reg", -1); 105 if (sc->sc_addr < 0) { 106 printf(": can't find registers\n"); 107 return; 108 } 109 110 sc->sc_node = saa->sa_node; 111 sc->sc_tag = saa->sa_tag; 112 sc->sc_sid = saa->sa_sid; 113 114 if (OF_is_compatible(saa->sa_node, "qcom,pm8350-gpio")) 115 sc->sc_npins = 10; 116 if (OF_is_compatible(saa->sa_node, "qcom,pm8350c-gpio")) 117 sc->sc_npins = 9; 118 if (OF_is_compatible(saa->sa_node, "qcom,pmr735a-gpio")) 119 sc->sc_npins = 4; 120 121 if (!sc->sc_npins) { 122 printf(": no pins\n"); 123 return; 124 } 125 126 if (qcpmicgpio_read(sc, GPIO_TYPE) != GPIO_TYPE_VAL) { 127 printf(": not a GPIO block\n"); 128 return; 129 } 130 131 reg = qcpmicgpio_read(sc, GPIO_SUBTYPE); 132 if (reg == GPIO_SUBTYPE_GPIO_LV || reg == GPIO_SUBTYPE_GPIO_MV) 133 sc->sc_is_lv_mv = 1; 134 135 sc->sc_gc.gc_node = saa->sa_node; 136 sc->sc_gc.gc_cookie = sc; 137 sc->sc_gc.gc_config_pin = qcpmicgpio_config_pin; 138 sc->sc_gc.gc_get_pin = qcpmicgpio_get_pin; 139 sc->sc_gc.gc_set_pin = qcpmicgpio_set_pin; 140 gpio_controller_register(&sc->sc_gc); 141 142 printf("\n"); 143 } 144 145 void 146 qcpmicgpio_config_pin(void *cookie, uint32_t *cells, int config) 147 { 148 struct qcpmicgpio_softc *sc = cookie; 149 uint32_t pin = cells[0]; 150 uint8_t reg; 151 int val; 152 153 if (pin >= sc->sc_npins) 154 return; 155 156 if (config & GPIO_CONFIG_OUTPUT) 157 val = GPIO_PIN_MODE_DIR_DIGITAL_OUT; 158 else 159 val = GPIO_PIN_MODE_DIR_DIGITAL_IN; 160 161 if (sc->sc_is_lv_mv) { 162 qcpmicgpio_write(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE, val); 163 } else { 164 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE); 165 reg &= ~(GPIO_PIN_MODE_DIR_MASK << GPIO_PIN_MODE_DIR_SHIFT); 166 reg |= val << GPIO_PIN_MODE_DIR_SHIFT; 167 qcpmicgpio_write(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE, reg); 168 } 169 } 170 171 int 172 qcpmicgpio_get_pin(void *cookie, uint32_t *cells) 173 { 174 struct qcpmicgpio_softc *sc = cookie; 175 uint32_t pin = cells[0]; 176 uint32_t flags = cells[1]; 177 uint8_t reg; 178 int val = 0; 179 180 if (pin >= sc->sc_npins) 181 return 0; 182 183 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE); 184 if (sc->sc_is_lv_mv) { 185 reg >>= GPIO_PIN_MODE_DIR_LVMV_SHIFT; 186 reg &= GPIO_PIN_MODE_DIR_LVMV_MASK; 187 } else { 188 reg >>= GPIO_PIN_MODE_DIR_SHIFT; 189 reg &= GPIO_PIN_MODE_DIR_MASK; 190 } 191 192 if (reg == GPIO_PIN_MODE_DIR_DIGITAL_IN || 193 reg == GPIO_PIN_MODE_DIR_DIGITAL_IO) { 194 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_STATUS); 195 val = !!(reg & GPIO_PIN_STATUS_ON); 196 } 197 198 if (reg == GPIO_PIN_MODE_DIR_DIGITAL_OUT) { 199 if (sc->sc_is_lv_mv) { 200 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + 201 GPIO_PIN_LVMV_DOUT_CTL); 202 val = !!(reg & GPIO_PIN_LVMV_DOUT_CTL_INVERT); 203 } else { 204 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + 205 GPIO_PIN_MODE); 206 val = !!(reg & GPIO_PIN_MODE_VALUE); 207 } 208 } 209 210 if (flags & GPIO_ACTIVE_LOW) 211 val = !val; 212 return val; 213 } 214 215 void 216 qcpmicgpio_set_pin(void *cookie, uint32_t *cells, int val) 217 { 218 struct qcpmicgpio_softc *sc = cookie; 219 uint32_t pin = cells[0]; 220 uint32_t flags = cells[1]; 221 uint8_t reg; 222 223 if (pin >= sc->sc_npins) 224 return; 225 226 if (flags & GPIO_ACTIVE_LOW) 227 val = !val; 228 229 if (sc->sc_is_lv_mv) { 230 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + 231 GPIO_PIN_LVMV_DOUT_CTL); 232 reg &= ~GPIO_PIN_LVMV_DOUT_CTL_INVERT; 233 if (val) 234 reg |= GPIO_PIN_LVMV_DOUT_CTL_INVERT; 235 qcpmicgpio_write(sc, GPIO_PIN_OFF(pin) + 236 GPIO_PIN_LVMV_DOUT_CTL, reg); 237 } else { 238 reg = qcpmicgpio_read(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE); 239 reg &= ~GPIO_PIN_MODE_VALUE; 240 if (val) 241 reg |= GPIO_PIN_MODE_VALUE; 242 qcpmicgpio_write(sc, GPIO_PIN_OFF(pin) + GPIO_PIN_MODE, reg); 243 } 244 } 245 246 uint8_t 247 qcpmicgpio_read(struct qcpmicgpio_softc *sc, uint16_t reg) 248 { 249 uint8_t val = 0; 250 int error; 251 252 error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, 253 sc->sc_addr + reg, &val, sizeof(val)); 254 if (error) 255 printf("%s: error reading\n", sc->sc_dev.dv_xname); 256 257 return val; 258 } 259 260 void 261 qcpmicgpio_write(struct qcpmicgpio_softc *sc, uint16_t reg, uint8_t val) 262 { 263 int error; 264 265 error = spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL, 266 sc->sc_addr + reg, &val, sizeof(val)); 267 if (error) 268 printf("%s: error writing\n", sc->sc_dev.dv_xname); 269 } 270