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