| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #include <autoconf.h> |
| #include <platsupport/gen_config.h> |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <utils/util.h> |
| #include <utils/arith.h> |
| #include <utils/stringify.h> |
| #include <utils/attribute.h> |
| |
| #include <platsupport/io.h> |
| #include <platsupport/mux.h> |
| #include <platsupport/plat/mux.h> |
| #include <platsupport/gpio.h> |
| #include <platsupport/plat/gpio.h> |
| |
| #include "mux_gpio_priv.h" |
| #include "../../services.h" |
| |
| /** @file TK1 Mux driver. |
| * |
| * This file contains routines that manipulate the TK1 mux controller and set |
| * up the signals on pins. |
| * |
| * PREREQUISITES: |
| * This driver currently assumes that the MMIO registers it accesses are mapped |
| * as strongly ordered and uncached. The driver makes no attempts whatsoever at |
| * managing the write buffer or managing ordering of reads and writes. |
| */ |
| |
| #define MUX_REG_LOCK_SHIFT (7) |
| #define MUX_REG_OPEN_DRAIN_SHIFT (6) |
| #define MUX_REG_ENABLE_SHIFT (5) |
| #define MUX_REG_TRISTATE_SHIFT (4) |
| #define MUX_REG_PUPD_SHIFT (2) |
| #define MUX_REG_PUPD_MASK (0x3) |
| #define MUX_REG_SFIO_SELECT_SHIFT (0) |
| #define MUX_REG_SFIO_SELECT_MASK (0x3) |
| |
| #define MUX_REG_PUPD_NORMAL (0) |
| #define MUX_REG_PUPD_PULLDOWN (1) |
| #define MUX_REG_PUPD_PULLUP (2) |
| |
| #define MUX_REG_TRISTATE_NORMAL (0) |
| #define MUX_REG_TRISTATE_TRISTATE (1) |
| |
| /* Throughout this driver, the terms "pad" and "pin" are interchangeable. |
| * |
| * Each pin's input and output buffers are distinct and can be enabled |
| * separately and operated concurrently (to support bidirectional signals). |
| * |
| * So we need to specify for each pin whether one, or both (or neither) of the |
| * buffers needs to be enabled. |
| * |
| * Furthermore, each pin's output buffer can be driven by one of several output |
| * drivers: push-pull, open-drain, high-z, weak-pullup, weak-pulldown. |
| */ |
| #define P_IN BIT(0) |
| #define P_PUSHPULL BIT(6) |
| #define P_BOTH (P_IN | P_PUSHPULL) |
| #define P_OPEN_DRAIN BIT(2) |
| #define P_TRISTATE BIT(3) |
| #define P_PULLUP BIT(4) |
| #define P_PULLDOWN BIT(5) |
| |
| #ifdef CONFIG_DEBUG_BUILD |
| #define PINMAP_COMPOSE_NAME(_name) .name = STRINGIFY(_n), |
| #else |
| #define PINMAP_COMPOSE_NAME(_name) |
| #endif |
| |
| #define PINMAP_PINDESC(_gp, _mro, _msv, _in_out) \ |
| { \ |
| .gpio_pin = _gp, .mux_reg_index = _mro, .mux_sfio_value = _msv, \ |
| .flags = _in_out \ |
| } |
| |
| #define PINMAP_NULLDESC PINMAP_PINDESC(-1, -1, 0xF, 0) |
| |
| #define PINMAP_1PIN(_n, _gp0, _mro0, _msv0, _io0) \ |
| [_n] = { \ |
| PINMAP_COMPOSE_NAME(_n) \ |
| { \ |
| PINMAP_PINDESC(_gp0, _mro0, _msv0, _io0), \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC \ |
| } \ |
| } |
| |
| #define PINMAP_2PIN(_n, _gp0, _mro0, _msv0, _io0, _gp1, _mro1, _msv1, _io1) \ |
| [_n] = { \ |
| PINMAP_COMPOSE_NAME(_n) \ |
| { \ |
| PINMAP_PINDESC(_gp0, _mro0, _msv0, _io0), \ |
| PINMAP_PINDESC(_gp1, _mro1, _msv1, _io1), \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC \ |
| } \ |
| } |
| |
| #define PINMAP_3PIN(_n, _gp0, _mro0, _msv0, _io0, _gp1, _mro1, _msv1, _io1, _gp2, _mro2, _msv2, _io2) \ |
| [_n] = { \ |
| PINMAP_COMPOSE_NAME(_n) \ |
| { \ |
| PINMAP_PINDESC(_gp0, _mro0, _msv0, _io0), \ |
| PINMAP_PINDESC(_gp1, _mro1, _msv1, _io1), \ |
| PINMAP_PINDESC(_gp2, _mro2, _msv2, _io2), \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, \ |
| PINMAP_NULLDESC \ |
| } \ |
| } |
| |
| #define PINMAP_4PIN(_n, _gp0, _mro0, _msv0, _io0, _gp1, _mro1, _msv1, _io1, _gp2, _mro2, _msv2, _io2, _gp3, _mro3, _msv3, _io3) \ |
| [_n] = { \ |
| PINMAP_COMPOSE_NAME(_n) \ |
| { \ |
| PINMAP_PINDESC(_gp0, _mro0, _msv0, _io0), \ |
| PINMAP_PINDESC(_gp1, _mro1, _msv1, _io1), \ |
| PINMAP_PINDESC(_gp2, _mro2, _msv2, _io2), \ |
| PINMAP_PINDESC(_gp3, _mro3, _msv3, _io3), \ |
| PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC, PINMAP_NULLDESC \ |
| } \ |
| } |
| |
| typedef struct tk1_mux_feature_pinmap_ { |
| #ifdef CONFIG_DEBUG_BUILD |
| const char *name; |
| #endif |
| struct tk1_mux_pin_desc { |
| uint32_t flags; |
| int16_t gpio_pin, mux_reg_index; |
| uint8_t mux_sfio_value; |
| } pins[8]; |
| } tk1_mux_feature_pinmap_t; |
| |
| static inline const char *get_feature_name_string(tk1_mux_feature_pinmap_t *var) |
| { |
| #ifdef CONFIG_DEBUG_BUILD |
| return var->name; |
| #else |
| return "<Unknown>"; |
| #endif |
| } |
| |
| /* Array of descriptors for each controller that we may want to enable. |
| * |
| * Each descriptor has a list of pins that the controller sends and receives |
| * signals on. Each of the pins' required configuration parameters is also |
| * given. |
| * |
| * Basically, to add support for a new mux feature, just fill out a new entry |
| * in this list. |
| */ |
| tk1_mux_feature_pinmap_t pinmaps[NMUX_FEATURES] = { |
| /* UARTs are a 2 or 4 signal pinout: RTS, CTS, TX, RX. */ |
| PINMAP_2PIN(MUX_FEATURE_UARTA, GPIO_PS1, MUX_PAD_KB_ROW9_PS1, 3, P_BOTH, GPIO_PS2, MUX_PAD_KB_ROW10_PS2, 3, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_UARTB, GPIO_PC3, MUX_PAD_UART2_RXD_PC3, 0, P_BOTH, |
| GPIO_PC2, MUX_PAD_UART2_TXD_PC2, 0, P_BOTH, |
| GPIO_PJ6, MUX_PAD_UART2_RTS_N_PJ6, 1, P_BOTH, |
| GPIO_PJ5, MUX_PAD_UART2_CTS_N_PJ5, 1, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_UARTC, GPIO_PW6, MUX_PAD_UART3_TXD_PW6, 0, P_BOTH, |
| GPIO_PW7, MUX_PAD_UART3_RXD_PW7, 0, P_BOTH, |
| GPIO_PA1, MUX_PAD_UART3_CTS_N_PA1, 0, P_BOTH, |
| GPIO_PC0, MUX_PAD_UART3_RTS_N_PC0, 0, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_UARTD, GPIO_PJ7, MUX_PAD_PJ7, 0, P_BOTH, |
| GPIO_PB0, MUX_PAD_PB0, 0, P_BOTH, |
| GPIO_PB1, MUX_PAD_PB1, 0, P_BOTH, |
| GPIO_PK7, MUX_PAD_PK7, 0, P_BOTH), |
| |
| /* SPI is a 4 signal pinout: CS, SCLK, MOSI, MISO. |
| * SPI1 is a special case, though I don't recall why. |
| */ |
| PINMAP_3PIN(MUX_FEATURE_SPI1, |
| GPIO_PY0, MUX_PAD_ULPI_CLK_PY0, 0, P_BOTH, |
| GPIO_PY1, MUX_PAD_ULPI_DIR_PY1, 0, P_BOTH, |
| GPIO_PY2, MUX_PAD_ULPI_NXT_PY2, 0, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_SPI2, GPIO_PO5, MUX_PAD_ULPI_DATA4_PO5, 0, P_BOTH, |
| GPIO_PO6, MUX_PAD_ULPI_DATA5_PO6, 0, P_BOTH, |
| GPIO_PO7, MUX_PAD_ULPI_DATA6_PO7, 0, P_BOTH, |
| GPIO_PO0, MUX_PAD_ULPI_DATA7_PO0, 0, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_SPI3, GPIO_PO1, MUX_PAD_ULPI_DATA0_PO1, 0, P_BOTH, |
| GPIO_PO2, MUX_PAD_ULPI_DATA1_PO2, 0, P_BOTH, |
| GPIO_PO3, MUX_PAD_ULPI_DATA2_PO3, 0, P_BOTH, |
| GPIO_PO4, MUX_PAD_ULPI_DATA3_PO4, 0, P_BOTH), |
| PINMAP_4PIN(MUX_FEATURE_SPI4, GPIO_PG4, MUX_PAD_PG4, 3, P_BOTH, |
| GPIO_PG5, MUX_PAD_PG5, 3, P_BOTH, |
| GPIO_PG6, MUX_PAD_PG6, 3, P_BOTH, |
| GPIO_PG7, MUX_PAD_PG7, 3, P_BOTH), |
| |
| /* These are being mux settings to be used to configure the pins in GPIO |
| * mode, which means that the SFIO function we select is irrelevant. |
| * |
| * Just enable both the input and output buffers, and select the SFIO |
| * function that the firmware places the pin into by default upon #RESET. |
| */ |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PS3, GPIO_PS3, MUX_PAD_KB_ROW11_PS3, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PS4, GPIO_PS4, MUX_PAD_KB_ROW12_PS4, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PR0, GPIO_PR0, MUX_PAD_KB_ROW0_PR0, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PR6, GPIO_PR6, MUX_PAD_KB_ROW6_PR6, 0, P_BOTH), |
| |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PS5, GPIO_PS5, MUX_PAD_KB_ROW13_PS5, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PT0, GPIO_PT0, MUX_PAD_KB_ROW16_PT0, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PS6, GPIO_PS6, MUX_PAD_KB_ROW14_PS6, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PS2, GPIO_PS2, MUX_PAD_KB_ROW10_PS2, 0, P_BOTH), |
| |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PA3, GPIO_PA3, MUX_PAD_DAP2_SCLK_PA3, 0, P_IN), |
| |
| PINMAP_2PIN(MUX_FEATURE_I2C0, GPIO_PC4, MUX_PAD_GEN1_I2C_SCL_PC4, 0, P_IN | P_OPEN_DRAIN, |
| GPIO_PC5, MUX_PAD_GEN1_I2C_SDA_PC5, 0, P_IN | P_OPEN_DRAIN), |
| PINMAP_2PIN(MUX_FEATURE_I2C1, GPIO_PT5, MUX_PAD_GEN2_I2C_SCL_PT5, 0, P_IN | P_OPEN_DRAIN, |
| GPIO_PT6, MUX_PAD_GEN2_I2C_SDA_PT6, 0, P_IN | P_OPEN_DRAIN), |
| PINMAP_2PIN(MUX_FEATURE_I2C2, GPIO_PBB1, MUX_PAD_CAM_I2C_SCL_PBB1, 1, P_IN | P_OPEN_DRAIN, |
| GPIO_PBB2, MUX_PAD_CAM_I2C_SDA_PBB2, 1, P_IN | P_OPEN_DRAIN), |
| PINMAP_2PIN(MUX_FEATURE_I2C3, GPIO_PV4, MUX_PAD_DDC_SCL_PV4, 0, P_IN, |
| GPIO_PV5, MUX_PAD_DDC_SDA_PV5, 0, P_IN), |
| |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PC4, GPIO_PC4, MUX_PAD_GEN1_I2C_SCL_PC4, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PC5, GPIO_PC5, MUX_PAD_GEN1_I2C_SDA_PC5, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PBB1, GPIO_PBB1, MUX_PAD_GEN2_I2C_SCL_PT5, 0, P_BOTH), |
| PINMAP_1PIN(MUX_FEATURE_GPIO_PBB2, GPIO_PBB2, MUX_PAD_GEN2_I2C_SDA_PT6, 0, P_BOTH), |
| |
| /* This is a configuration that overloads UARTB's RTS and CTS pins to allow |
| * us to use them as GPIO outputs. |
| * |
| * I was using them so I could sample measure the IRQ incoming and ACK times |
| * of the PPM IRQ in the quadcopter repo. This can be removed, but it also |
| * doesn't harm anything by being here. |
| */ |
| PINMAP_2PIN(MUX_FEATURE_PPM_MIRROR, |
| GPIO_PJ6, MUX_PAD_UART2_RTS_N_PJ6, 1, P_PUSHPULL, |
| GPIO_PJ5, MUX_PAD_UART2_CTS_N_PJ5, 1, P_PUSHPULL) |
| }; |
| |
| typedef struct tegra_mux_state { |
| volatile uint32_t *pinmux_misc; |
| volatile uint32_t *pinmux_aux; |
| gpio_sys_t *gpio_sys; |
| } tegra_mux_state_t; |
| |
| static const tegra_mux_state_t *tk1_mux_get_priv(const mux_sys_t *mux_sys) |
| { |
| assert(mux_sys != NULL); |
| return (const tegra_mux_state_t *)mux_sys->priv; |
| } |
| |
| static volatile uint32_t *tk1_mux_get_group_reg_handle_for_pin(const mux_sys_t *ms, int mux_reg_index) |
| { |
| volatile uint32_t *ret; |
| int group_offset; |
| |
| ret = tk1_mux_get_priv(ms)->pinmux_misc; |
| assert(ret != NULL); |
| group_offset = tk1_mux_get_group_offset_for_pin(mux_reg_index); |
| assert(group_offset > 0); |
| return &ret[group_offset / sizeof(uint32_t)]; |
| } |
| |
| static void tk1_mux_set_drive_strength_for_pin(const mux_sys_t *ms, |
| int mux_reg_index, |
| uint8_t drive_up_strength, |
| uint8_t drive_down_strength) |
| { |
| volatile uint32_t *reg; |
| int group_offset; |
| uint32_t drup_mask, drup_shift, drdown_mask, drdown_shift; |
| |
| reg = tk1_mux_get_group_reg_handle_for_pin(ms, mux_reg_index); |
| group_offset = tk1_mux_get_group_offset_for_pin(mux_reg_index); |
| if (group_offset < 0) { |
| /* Return early if there is no group configuration register for this |
| * pin. |
| */ |
| return; |
| } |
| ZF_LOGD("Group off 0x%x: previous val: 0x%x.", group_offset, *reg); |
| |
| drup_shift = tk1_mux_get_bitinfo_for_group(group_offset, BITOFF_DRIVEUP); |
| drup_mask = tk1_mux_get_bitinfo_for_group(group_offset, BITMASK_DRIVEUP); |
| drdown_shift = tk1_mux_get_bitinfo_for_group(group_offset, BITOFF_DRIVEDOWN); |
| drdown_mask = tk1_mux_get_bitinfo_for_group(group_offset, BITMASK_DRIVEDOWN); |
| |
| *reg &= ~(drup_mask << drup_shift); |
| *reg &= ~(drdown_mask << drdown_shift); |
| |
| *reg |= (drive_up_strength & drup_mask) << drup_shift; |
| *reg |= (drive_down_strength & drdown_mask) << drdown_shift; |
| ZF_LOGD("Group off 0x%x: new val: 0x%x.", group_offset, *reg); |
| } |
| |
| static int tk1_mux_set_pin_params(const mux_sys_t *mux, struct tk1_mux_pin_desc *desc, |
| enum mux_gpio_dir mux_gpio_dir) |
| { |
| uint32_t regval, requiredval; |
| const tegra_mux_state_t *s = tk1_mux_get_priv(mux); |
| |
| regval = s->pinmux_aux[desc->mux_reg_index] & 0xFF; |
| |
| /* There is a "lock" bit in each of the mux registers. This lock bit |
| * prevents us from modifying the configuration of the pads. |
| * |
| * If the lock bit is UNSET, we can just reconfigure the pad. |
| * If the lock bit is SET however, we can't reconfigure the pad. |
| */ |
| requiredval = ((desc->mux_sfio_value & MUX_REG_SFIO_SELECT_MASK) << MUX_REG_SFIO_SELECT_SHIFT); |
| |
| /* Enable input buffer if P_IN */ |
| if (desc->flags & P_IN) { |
| requiredval |= BIT(MUX_REG_ENABLE_SHIFT); |
| } |
| /* Set output driver to pushpull if P_PUSHPULL. */ |
| if (desc->flags & P_PUSHPULL) { |
| if (desc->flags & P_OPEN_DRAIN) { |
| ZF_LOGE("Can't enable both pushpull and open-drain output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_TRISTATE) { |
| ZF_LOGE("Can't enable both pushpull and tristate output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) { |
| ZF_LOGE("Can't enable both pushpull and pull-resistor output drivers."); |
| return -1; |
| } |
| requiredval &= ~BIT(MUX_REG_TRISTATE_SHIFT); |
| } |
| if (desc->flags & P_TRISTATE) { |
| if (desc->flags & P_OPEN_DRAIN) { |
| ZF_LOGE("Can't enable both tristate and open-drain output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_PUSHPULL) { |
| ZF_LOGE("Can't enable both tristate and pushpull output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) { |
| ZF_LOGE("Can't enable both tristate and pull-resistor output drivers."); |
| return -1; |
| } |
| requiredval |= BIT(MUX_REG_TRISTATE_SHIFT); |
| } |
| if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) { |
| if ((desc->flags & P_PULLUP) && (desc->flags & P_PULLDOWN)) { |
| ZF_LOGE("Can't enable both pullup and pulldown at once."); |
| return -1; |
| } |
| if (desc->flags & P_OPEN_DRAIN) { |
| ZF_LOGE("Can't enable both pullup and opendrain output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_TRISTATE) { |
| ZF_LOGE("Can't enable both pullup and tristate output drivers."); |
| return -1; |
| } |
| requiredval &= ~(MUX_REG_PUPD_MASK << MUX_REG_PUPD_SHIFT); |
| requiredval |= ((desc->flags & P_PULLUP) |
| ? MUX_REG_PUPD_PULLUP |
| : MUX_REG_PUPD_PULLDOWN) |
| << MUX_REG_PUPD_SHIFT; |
| } |
| if (desc->flags & P_OPEN_DRAIN) { |
| if (desc->flags & P_PUSHPULL) { |
| ZF_LOGE("Can't enable both open-drain and pushpull output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_TRISTATE) { |
| ZF_LOGE("Can't enable both open-drain and tristate output drivers."); |
| return -1; |
| } |
| if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) { |
| ZF_LOGE("Can't enable both open-drain and pullup/pulldown output drivers."); |
| return -1; |
| } |
| requiredval |= BIT(MUX_REG_OPEN_DRAIN_SHIFT); |
| } |
| |
| if (regval == requiredval) { |
| return 0; |
| } |
| |
| if (regval & BIT(MUX_REG_LOCK_SHIFT)) { |
| /* IF the lock bit is set, we can't change it */ |
| ZF_LOGE("Failed to change pin SFIO function to %d: Bit is locked.", |
| desc->mux_sfio_value); |
| return -1; |
| } |
| |
| s->pinmux_aux[desc->mux_reg_index] = requiredval; |
| /* Just in case the writes are being silently ignored. */ |
| assert(s->pinmux_aux[desc->mux_reg_index] == requiredval); |
| return 0; |
| } |
| |
| static bool pin_is_pull_up_by_default(int mux_reg_index) |
| { |
| /* This is a list of the pins whose default state on #RESET is Pull-up. */ |
| const uint16_t pull_up_pin_indexes[] = { |
| MUX_PAD_ULPI_DATA0_PO1, MUX_PAD_ULPI_DATA1_PO2, MUX_PAD_ULPI_DATA2_PO3, |
| MUX_PAD_ULPI_DATA3_PO4, MUX_PAD_ULPI_DATA4_PO5, MUX_PAD_ULPI_DATA5_PO6, |
| MUX_PAD_ULPI_DATA6_PO7, MUX_PAD_ULPI_DATA7_PO0, MUX_PAD_SDMMC1_CMD_PZ1, |
| MUX_PAD_SDMMC1_DAT3_PY4, MUX_PAD_SDMMC1_DAT2_PY5, MUX_PAD_SDMMC1_DAT1_PY6, |
| MUX_PAD_SDMMC1_DAT0_PY7, |
| MUX_PAD_UART2_RXD_PC3, MUX_PAD_UART2_TXD_PC2, MUX_PAD_UART2_RTS_N_PJ6, |
| MUX_PAD_UART2_CTS_N_PJ5, MUX_PAD_UART3_TXD_PW6, MUX_PAD_UART3_RXD_PW7, |
| MUX_PAD_UART3_RTS_N_PC0, MUX_PAD_UART3_RTS_N_PC0, |
| MUX_PAD_PC7, MUX_PAD_PI5, MUX_PAD_PI7, MUX_PAD_PK0, MUX_PAD_PJ0, |
| MUX_PAD_PJ2, MUX_PAD_PK3, MUX_PAD_PK4, MUX_PAD_PK2, MUX_PAD_PI3, |
| MUX_PAD_PI6, MUX_PAD_PH4, MUX_PAD_PH6, MUX_PAD_PH7, MUX_PAD_PI0, |
| MUX_PAD_PI1, MUX_PAD_PI2, MUX_PAD_SDMMC4_CMD_PT7, MUX_PAD_SDMMC4_DAT0_PAA0, |
| MUX_PAD_SDMMC4_DAT1_PAA1, MUX_PAD_SDMMC4_DAT2_PAA2, MUX_PAD_SDMMC4_DAT3_PAA3, |
| MUX_PAD_SDMMC4_DAT4_PAA4, MUX_PAD_SDMMC4_DAT5_PAA5, MUX_PAD_SDMMC4_DAT6_PAA6, |
| MUX_PAD_SDMMC4_DAT7_PAA7, |
| MUX_PAD_CAM_MCLK_PCC0, |
| MUX_PAD_PCC1, MUX_PAD_PCC2, MUX_PAD_JTAG_RTCK, |
| MUX_PAD_KB_COL0_PQ0, MUX_PAD_KB_COL1_PQ1, MUX_PAD_KB_COL2_PQ2, |
| MUX_PAD_KB_COL3_PQ3, MUX_PAD_KB_COL4_PQ4, MUX_PAD_KB_COL5_PQ5, |
| MUX_PAD_KB_COL6_PQ6, MUX_PAD_KB_COL7_PQ7, |
| MUX_PAD_SPDIF_OUT_PK5, |
| MUX_PAD_GPIO_X3_AUD_PX3, |
| MUX_PAD_DVFS_CLK_PX2, |
| MUX_PAD_GPIO_X5_AUD_PX5, MUX_PAD_GPIO_X6_AUD_PX6, |
| MUX_PAD_SDMMC1_CMD_PZ1, MUX_PAD_SDMMC3_DAT0_PB7, MUX_PAD_SDMMC3_DAT1_PB6, |
| MUX_PAD_SDMMC3_DAT2_PB5, MUX_PAD_SDMMC3_DAT3_PB4, |
| MUX_PAD_SDMMC1_WP_N_PV3, MUX_PAD_SDMMC3_CD_N_PV2, |
| MUX_PAD_GPIO_W2_AUD_PW2, MUX_PAD_GPIO_W3_AUD_PW3 |
| }; |
| |
| for (int i = 0; i < ARRAY_SIZE(pull_up_pin_indexes); i++) { |
| /* Each register is 32 bits, so indexes need to be multiplied by 4 to |
| * get the byte-offset from the base. |
| */ |
| if (pull_up_pin_indexes[i] == mux_reg_index * 4) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static void tk1_mux_set_pin_unused(volatile uint32_t *regs, uint16_t mux_reg_index, |
| bool enable_input_buffer) |
| { |
| int pupd_val; |
| |
| /* TK1 TRM, sec 8.10.3 "Unused Pins": |
| * "For each unused MPIO, assert its tristate and disable its input " |
| * buffer. For pins whose internal pull-up is enabled during power- |
| * on-reset, assert the internal pull-up. Otherwise, assert the internal |
| * pull-down." |
| * |
| * Furthermore: |
| * |
| * TK1 TRM, sec 8.4.1 "Per Pad Options": |
| * "Tristate (high-z) option: Disables or enables the pad’s output driver. |
| * This setting overrides any other functional setting and also whether pad |
| * is selected for SFIO or GPIO. Can be used when the pad direction changes |
| * or the pad is assigned to different SFIO to avoid glitches." |
| * |
| * So this function will set a given pin to its unused state by enabling |
| * its tristate mode (which is its output mode), and then either asserting |
| * pull-up or pull-down. |
| * |
| * This function will be used both to disable pins that are not being used, |
| * and also as an intermediate transition when changing the mux settings |
| * for a pin (as advised by the manual, in sec 8.4.1). |
| */ |
| |
| /* See sec 8.10.3 above: if the pin is pull up on #RESET, then assert |
| * pull-up, else assert pull-down. |
| */ |
| if (pin_is_pull_up_by_default(mux_reg_index)) { |
| pupd_val = MUX_REG_PUPD_PULLUP; |
| } else { |
| pupd_val = MUX_REG_PUPD_PULLDOWN; |
| } |
| |
| regs[mux_reg_index] = 0 |
| /* Tristate on */ |
| | MUX_REG_TRISTATE_TRISTATE << MUX_REG_TRISTATE_SHIFT |
| /* Enable/disable the input buffer. |
| * |
| * We should disable it, but the problem is that if we |
| * disable the input buffer on the pin, and then later |
| * on somebody wants to use the pin as an GPIO input pin, |
| * it would mean that the GPIO driver would have to call |
| * down into the mux driver (this driver). |
| * |
| * But this driver already depends on the GPIO driver, so |
| * that would create a circular dependency. |
| * |
| * Thankfully, leaving the input buffer on shouldn't be |
| * detrimental because, according to the TRM sec 8.4.1, |
| * the tristate option overrides all other options. |
| */ |
| | (!!enable_input_buffer) << MUX_REG_ENABLE_SHIFT |
| /* Assert either pull-up or pull-down. */ |
| | pupd_val << MUX_REG_PUPD_SHIFT; |
| } |
| |
| static int tk1_mux_feature_enable(const mux_sys_t *mux, mux_feature_t feat, |
| enum mux_gpio_dir mux_gpio_dir) |
| { |
| int error; |
| tk1_mux_feature_pinmap_t *map; |
| const tegra_mux_state_t *s = tk1_mux_get_priv(mux); |
| |
| assert(feat < NMUX_FEATURES); |
| |
| map = &pinmaps[feat]; |
| for (int i = 0; i < ARRAY_SIZE(map->pins); i++) { |
| /* Pin list is terminated by -1 as pin number. */ |
| if (map->pins[i].gpio_pin == -1) { |
| break; |
| } |
| |
| ZF_LOGD("Feature enable: feat %d, Mux pad index 0x%x(offset 0x%x), GPIO %d. Reg state before 0x%x.", |
| feat, |
| map->pins[i].mux_reg_index, |
| map->pins[i].mux_reg_index * sizeof(uint32_t), |
| map->pins[i].gpio_pin, |
| s->pinmux_aux[map->pins[i].mux_reg_index]); |
| |
| if (mux_gpio_dir == MUX_DIR_NOT_A_GPIO) { |
| /* According to TK1 TRM sec 8.4.1, we should set the pin to default |
| * values (asserting tristate) when we're reconfiguring it to a |
| * different SFIO, to prevent glitches during the configuration |
| * transition. |
| * |
| * If the SFIO function being changed to is the same though, |
| * we shouldn't call set_pin_unused() because that has to potential |
| * to unnecessarily pull the pin up or down according to its |
| * default #RESET voltage level. |
| */ |
| if ((s->pinmux_aux[map->pins[i].mux_reg_index] |
| & MUX_REG_SFIO_SELECT_MASK) != map->pins[i].mux_sfio_value) { |
| tk1_mux_set_pin_unused(s->pinmux_aux, |
| map->pins[i].mux_reg_index, true); |
| } |
| |
| /* First, attempt to set the pin into SFIO mode IF it's going to be |
| * used to bring out an SFIO signal. |
| * |
| * If it'll be used to bring out a GPIO signal, leave it alone in |
| * whatever mode it was in, and let the GPIO driver set it up when |
| * the user tells it to. |
| */ |
| error = gpio_set_pad_mode(s->gpio_sys, map->pins[i].gpio_pin, |
| SFIO_MODE, |
| 0 /* Doesn't matter for SFIO mode. */); |
| if (error) { |
| ZF_LOGE("Failed to set pin %d for feature %s. Aborting.", |
| i, get_feature_name_string(map)); |
| return -1; |
| } |
| } |
| |
| /* Next set up the rest of the parameters. */ |
| error = tk1_mux_set_pin_params(mux, &map->pins[i], mux_gpio_dir); |
| if (error) { |
| ZF_LOGE("Failed to pinmux params for pin %d of feature %s. " |
| "Aborting.", |
| i, get_feature_name_string(map)); |
| return -1; |
| } |
| |
| /* Set the drive strength divisor to 8 for moderate drive strength. */ |
| tk1_mux_set_drive_strength_for_pin(mux, |
| map->pins[i].mux_reg_index, |
| 8, 8); |
| ZF_LOGD("Feature enable: feat %d, Mux pad index 0x%x(offset 0x%x), GPIO %d. Reg state AFTER 0x%x.", |
| feat, |
| map->pins[i].mux_reg_index, |
| map->pins[i].mux_reg_index * sizeof(uint32_t), |
| map->pins[i].gpio_pin, |
| s->pinmux_aux[map->pins[i].mux_reg_index]); |
| } |
| |
| return 0; |
| } |
| |
| static int tk1_mux_feature_disable(const mux_sys_t *mux, mux_feature_t feat) |
| { |
| tk1_mux_feature_pinmap_t *map; |
| const tegra_mux_state_t *s = tk1_mux_get_priv(mux); |
| |
| assert(feat < NMUX_FEATURES); |
| |
| map = &pinmaps[feat]; |
| for (int i = 0; i < ARRAY_SIZE(map->pins); i++) { |
| /* Pin list is terminated by -1 as pin number. */ |
| if (map->pins[i].gpio_pin == -1) { |
| break; |
| } |
| |
| tk1_mux_set_pin_unused(s->pinmux_aux, map->pins[i].mux_reg_index, false); |
| } |
| |
| return 0; |
| } |
| |
| static void tk1_mux_set_all_pins_to_default_state(volatile uint32_t *mux_regs) |
| { |
| /* TODO: |
| * We should cycle through all valid pins here and call |
| * tk1_mux_set_pin_unused() on each one. |
| */ |
| } |
| |
| int tegra_mux_init(volatile void *pinmux_misc, volatile void *pinmux_aux, |
| ps_io_ops_t *io_ops, |
| gpio_sys_t *gpio_sys, mux_sys_t *self) |
| { |
| int error; |
| tegra_mux_state_t *state; |
| |
| if (!gpio_sys_valid(gpio_sys)) { |
| ZF_LOGE("Invalid GPIO driver instance handle."); |
| return -EINVAL; |
| }; |
| |
| error = ps_malloc(&io_ops->malloc_ops, sizeof(*state), (void **)&state); |
| if (error != 0 || state == NULL) { |
| ZF_LOGE("Failed to alloc TK1 Mux instance internal state."); |
| return -1; |
| } |
| |
| state->pinmux_misc = pinmux_misc; |
| state->pinmux_aux = pinmux_aux; |
| state->gpio_sys = gpio_sys; |
| |
| self->priv = state; |
| self->feature_enable = &tk1_mux_feature_enable; |
| self->feature_disable = &tk1_mux_feature_disable; |
| |
| tk1_mux_set_all_pins_to_default_state(tk1_mux_get_priv(self)->pinmux_aux); |
| |
| ZF_LOGE("Mux misc for dbgcfg @vaddrs 0x%p, 0x%p: values 0x%x, 0x%x (offsets are 0x%x, 0x%x).", |
| tk1_mux_get_group_reg_handle_for_pin(self, MUX_PAD_GEN1_I2C_SCL_PC4), |
| tk1_mux_get_group_reg_handle_for_pin(self, MUX_PAD_CAM_I2C_SCL_PBB1), |
| *tk1_mux_get_group_reg_handle_for_pin(self, MUX_PAD_GEN1_I2C_SCL_PC4), |
| *tk1_mux_get_group_reg_handle_for_pin(self, MUX_PAD_CAM_I2C_SCL_PBB1), |
| tk1_mux_get_group_offset_for_pin(MUX_PAD_GEN1_I2C_SCL_PC4), |
| tk1_mux_get_group_offset_for_pin(MUX_PAD_CAM_I2C_SCL_PBB1)); |
| return 0; |
| } |
| |
| int mux_sys_init(ps_io_ops_t *io_ops, void *dependencies, mux_sys_t *mux) |
| { |
| void *pinmux_misc_vaddr = NULL, *pinmux_aux_vaddr = NULL; |
| gpio_sys_t *gpio_sys = (gpio_sys_t *)dependencies; |
| |
| pinmux_misc_vaddr = RESOURCE(io_ops, TK1_MUX_MISC); |
| if (pinmux_misc_vaddr == NULL) { |
| ZF_LOGE("Failed to map in pinmux_misc frame."); |
| return -1; |
| } |
| pinmux_aux_vaddr = RESOURCE(io_ops, TK1_MUX_AUX); |
| if (pinmux_aux_vaddr == NULL) { |
| ZF_LOGE("Failed to map in pinmux_aux frame."); |
| return -1; |
| } |
| |
| return tegra_mux_init(pinmux_misc_vaddr, pinmux_aux_vaddr, io_ops, |
| gpio_sys, mux); |
| } |