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