1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice unmodified, this list of conditions, and the following 11 * 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 AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, 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 /* 30 * This is a pinmux/gpio controller for the IPQ4018/IPQ4019. 31 */ 32 33 #include <sys/cdefs.h> 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/bus.h> 37 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/rman.h> 41 #include <sys/lock.h> 42 #include <sys/malloc.h> 43 #include <sys/mutex.h> 44 #include <sys/gpio.h> 45 46 #include <machine/bus.h> 47 #include <machine/resource.h> 48 #include <dev/gpio/gpiobusvar.h> 49 50 #include <dev/fdt/fdt_common.h> 51 #include <dev/ofw/ofw_bus.h> 52 #include <dev/ofw/ofw_bus_subr.h> 53 54 #include <dev/fdt/fdt_pinctrl.h> 55 56 #include "qcom_tlmm_var.h" 57 58 #include "qcom_tlmm_ipq4018_reg.h" 59 #include "qcom_tlmm_ipq4018_hw.h" 60 61 #include "gpio_if.h" 62 63 /* 64 * Set the pin function. This is a hardware and pin specific mapping. 65 * 66 * Returns 0 if OK, an errno if an error was encountered. 67 */ 68 int 69 qcom_tlmm_ipq4018_hw_pin_set_function(struct qcom_tlmm_softc *sc, 70 int pin, int function) 71 { 72 uint32_t reg; 73 74 GPIO_LOCK_ASSERT(sc); 75 76 if (pin >= sc->gpio_npins) 77 return (EINVAL); 78 79 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 80 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 81 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK 82 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT); 83 reg |= (function & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK) 84 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT; 85 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 86 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 87 88 return (0); 89 } 90 91 /* 92 * Get the pin function. This is a hardware and pin specific mapping. 93 * 94 * Returns 0 if OK, an errno if a nerror was encountered. 95 */ 96 int 97 qcom_tlmm_ipq4018_hw_pin_get_function(struct qcom_tlmm_softc *sc, 98 int pin, int *function) 99 { 100 uint32_t reg; 101 102 GPIO_LOCK_ASSERT(sc); 103 104 if (pin >= sc->gpio_npins) 105 return (EINVAL); 106 107 108 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 109 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 110 reg = reg >> QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT; 111 reg &= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK; 112 *function = reg; 113 114 return (0); 115 } 116 117 /* 118 * Set the OE bit to be output. This assumes the port is configured 119 * as a GPIO port. 120 */ 121 int 122 qcom_tlmm_ipq4018_hw_pin_set_oe_output(struct qcom_tlmm_softc *sc, 123 int pin) 124 { 125 uint32_t reg; 126 127 GPIO_LOCK_ASSERT(sc); 128 129 if (pin >= sc->gpio_npins) 130 return (EINVAL); 131 132 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 133 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 134 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE; 135 GPIO_WRITE(sc, 136 QCOM_TLMM_IPQ4018_REG_PIN(pin, QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), 137 reg); 138 139 return (0); 140 } 141 142 /* 143 * Set the OE bit to be input. This assumes the port is configured 144 * as a GPIO port. 145 */ 146 int 147 qcom_tlmm_ipq4018_hw_pin_set_oe_input(struct qcom_tlmm_softc *sc, 148 int pin) 149 { 150 uint32_t reg; 151 152 GPIO_LOCK_ASSERT(sc); 153 154 if (pin >= sc->gpio_npins) 155 return (EINVAL); 156 157 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 158 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 159 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE; 160 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 161 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 162 163 return (0); 164 } 165 166 /* 167 * Get the GPIO pin direction. is_output is set to true if the pin 168 * is an output pin, false if it's set to an input pin. 169 */ 170 int 171 qcom_tlmm_ipq4018_hw_pin_get_oe_state(struct qcom_tlmm_softc *sc, 172 int pin, bool *is_output) 173 { 174 uint32_t reg; 175 176 GPIO_LOCK_ASSERT(sc); 177 178 if (pin >= sc->gpio_npins) 179 return (EINVAL); 180 181 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 182 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 183 *is_output = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE); 184 185 return (0); 186 } 187 188 189 /* 190 * Set the given GPIO pin to the given value. 191 */ 192 int 193 qcom_tlmm_ipq4018_hw_pin_set_output_value(struct qcom_tlmm_softc *sc, 194 uint32_t pin, unsigned int value) 195 { 196 uint32_t reg; 197 198 GPIO_LOCK_ASSERT(sc); 199 200 if (pin >= sc->gpio_npins) 201 return (EINVAL); 202 203 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 204 QCOM_TLMM_IPQ4018_REG_PIN_IO)); 205 if (value) 206 reg |= QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN; 207 else 208 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN; 209 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 210 QCOM_TLMM_IPQ4018_REG_PIN_IO), reg); 211 212 return (0); 213 } 214 215 /* 216 * Get the input state of the current GPIO pin. 217 */ 218 int 219 qcom_tlmm_ipq4018_hw_pin_get_output_value(struct qcom_tlmm_softc *sc, 220 uint32_t pin, unsigned int *val) 221 { 222 uint32_t reg; 223 224 GPIO_LOCK_ASSERT(sc); 225 226 if (pin >= sc->gpio_npins) 227 return (EINVAL); 228 229 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 230 QCOM_TLMM_IPQ4018_REG_PIN_IO)); 231 232 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS); 233 234 return (0); 235 } 236 237 238 /* 239 * Get the input state of the current GPIO pin. 240 */ 241 int 242 qcom_tlmm_ipq4018_hw_pin_get_input_value(struct qcom_tlmm_softc *sc, 243 uint32_t pin, unsigned int *val) 244 { 245 uint32_t reg; 246 247 GPIO_LOCK_ASSERT(sc); 248 249 if (pin >= sc->gpio_npins) 250 return (EINVAL); 251 252 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 253 QCOM_TLMM_IPQ4018_REG_PIN_IO)); 254 255 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS); 256 257 return (0); 258 } 259 260 /* 261 * Toggle the current output pin value. 262 */ 263 int 264 qcom_tlmm_ipq4018_hw_pin_toggle_output_value( 265 struct qcom_tlmm_softc *sc, uint32_t pin) 266 { 267 uint32_t reg; 268 269 GPIO_LOCK_ASSERT(sc); 270 271 if (pin >= sc->gpio_npins) 272 return (EINVAL); 273 274 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 275 QCOM_TLMM_IPQ4018_REG_PIN_IO)); 276 if ((reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN) == 0) 277 reg |= QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN; 278 else 279 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN; 280 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 281 QCOM_TLMM_IPQ4018_REG_PIN_IO), reg); 282 283 return (0); 284 } 285 286 /* 287 * Configure the pull-up / pull-down top-level configuration. 288 * 289 * This doesn't configure the resistor values, just what's enabled/disabled. 290 */ 291 int 292 qcom_tlmm_ipq4018_hw_pin_set_pupd_config( 293 struct qcom_tlmm_softc *sc, uint32_t pin, 294 qcom_tlmm_pin_pupd_config_t pupd) 295 { 296 uint32_t reg; 297 298 GPIO_LOCK_ASSERT(sc); 299 300 if (pin >= sc->gpio_npins) 301 return (EINVAL); 302 303 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 304 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 305 306 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK 307 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT); 308 309 switch (pupd) { 310 case QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE: 311 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_DISABLE 312 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT; 313 break; 314 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN: 315 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLDOWN 316 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT; 317 break; 318 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP: 319 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLUP 320 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT; 321 break; 322 case QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD: 323 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_BUSHOLD 324 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT; 325 break; 326 } 327 328 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 329 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 330 331 return (0); 332 } 333 334 /* 335 * Fetch the current pull-up / pull-down configuration. 336 */ 337 int 338 qcom_tlmm_ipq4018_hw_pin_get_pupd_config( 339 struct qcom_tlmm_softc *sc, uint32_t pin, 340 qcom_tlmm_pin_pupd_config_t *pupd) 341 { 342 uint32_t reg; 343 344 GPIO_LOCK_ASSERT(sc); 345 346 if (pin >= sc->gpio_npins) 347 return (EINVAL); 348 349 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 350 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 351 352 reg >>= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT; 353 reg &= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK; 354 355 switch (reg) { 356 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_DISABLE: 357 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE; 358 break; 359 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLDOWN: 360 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN; 361 break; 362 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLUP: 363 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP; 364 break; 365 default: 366 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE; 367 break; 368 } 369 370 return (0); 371 } 372 373 /* 374 * Set the drive strength in mA. 375 */ 376 int 377 qcom_tlmm_ipq4018_hw_pin_set_drive_strength( 378 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t drv) 379 { 380 uint32_t reg; 381 382 GPIO_LOCK_ASSERT(sc); 383 384 if (pin >= sc->gpio_npins) 385 return (EINVAL); 386 387 /* Convert mA to hardware */ 388 if (drv > 16 || drv < 2) 389 return (EINVAL); 390 drv = (drv / 2) - 1; 391 392 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 393 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 394 395 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT 396 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK); 397 reg |= (drv & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK) 398 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT; 399 400 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 401 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 402 403 return (0); 404 } 405 406 /* 407 * Get the drive strength in mA. 408 */ 409 int 410 qcom_tlmm_ipq4018_hw_pin_get_drive_strength( 411 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t *drv) 412 { 413 uint32_t reg; 414 415 GPIO_LOCK_ASSERT(sc); 416 417 if (pin >= sc->gpio_npins) 418 return (EINVAL); 419 420 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 421 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 422 423 *drv = (reg >> QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT) 424 & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK; 425 426 *drv = (*drv + 1) * 2; 427 428 return (0); 429 } 430 431 432 /* 433 * Enable/disable whether this pin is passed through to a VM. 434 */ 435 int 436 qcom_tlmm_ipq4018_hw_pin_set_vm( 437 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable) 438 { 439 uint32_t reg; 440 441 GPIO_LOCK_ASSERT(sc); 442 443 if (pin >= sc->gpio_npins) 444 return (EINVAL); 445 446 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 447 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 448 449 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE; 450 if (enable) 451 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE; 452 453 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 454 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 455 456 return (0); 457 } 458 459 /* 460 * Get the VM configuration bit. 461 */ 462 int 463 qcom_tlmm_ipq4018_hw_pin_get_vm( 464 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable) 465 { 466 uint32_t reg; 467 468 GPIO_LOCK_ASSERT(sc); 469 470 if (pin >= sc->gpio_npins) 471 return (EINVAL); 472 473 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 474 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 475 476 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE); 477 478 return (0); 479 } 480 481 /* 482 * Enable/disable open drain. 483 */ 484 int 485 qcom_tlmm_ipq4018_hw_pin_set_open_drain( 486 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable) 487 { 488 uint32_t reg; 489 490 GPIO_LOCK_ASSERT(sc); 491 492 if (pin >= sc->gpio_npins) 493 return (EINVAL); 494 495 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 496 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 497 498 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE; 499 if (enable) 500 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE; 501 502 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 503 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg); 504 505 return (0); 506 } 507 508 /* 509 * Get the open drain configuration bit. 510 */ 511 int 512 qcom_tlmm_ipq4018_hw_pin_get_open_drain( 513 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable) 514 { 515 uint32_t reg; 516 517 GPIO_LOCK_ASSERT(sc); 518 519 if (pin >= sc->gpio_npins) 520 return (EINVAL); 521 522 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin, 523 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL)); 524 525 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE); 526 527 return (0); 528 } 529