blob: da2e269dc7755e01feeb66c30baa07af9bb9ff3d [file] [log] [blame]
/*
* Copyright 2015-2017, NVIDIA Corporation
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
/*
* This MUX driver is a port from NVIDIA's L4T tx2 pinctrl drivers.
*
* Parts of the function-to-pin map table has been taken from NVIDIA's L4T
* kernel sources.
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <utils/util.h>
#include <platsupport/io.h>
#include <platsupport/mux.h>
#include <platsupport/gpio.h>
#include <platsupportports/plat/gpio.h>
#include <platsupportports/plat/mux.h>
#define MUX_REG_SCHMT_SHIFT (12)
#define MUX_REG_SF_SHIFT (10)
#define MUX_REG_ENABLE_SHIFT (6)
#define MUX_REG_OPEN_DRAIN_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_SF_GPIO (0)
#define MUX_REG_SF_HSIO (1)
#define MUX_REG_PUPD_NONE (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)
#define P_SCHMT BIT(6)
#define PINMAP_PINDESC(_gp, _mro, _msv, _in_out) \
{ \
.gpio_pin = _gp, .mux_reg_offset = _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_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_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_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_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 \
} \
}
#define PINMAP_6PIN(_n, _gp0, _mro0, _msv0, _io0, _gp1, _mro1, _msv1, _io1, _gp2, _mro2, _msv2, _io2, _gp3, _mro3, _msv3, _io3, _gp4, _mro4, _msv4, _io4, _gp5, _mro5, _msv5, _io5) \
[_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_PINDESC(_gp4, _mro4, _msv4, _io4), \
PINMAP_PINDESC(_gp5, _mro5, _msv5, _io5), \
PINMAP_NULLDESC, PINMAP_NULLDESC \
} \
}
typedef struct tx2_mux_feature_pinmap_ {
struct tx2_mux_pin_desc {
uint32_t flags;
int16_t gpio_pin, mux_reg_offset;
uint8_t mux_sfio_value;
} pins[8];
} tx2_mux_feature_pinmap_t;
tx2_mux_feature_pinmap_t pinmaps[NMUX_FEATURES] = {
PINMAP_2PIN(MUX_FEATURE_UARTA, GPIO_PT0, MUX_PAD_UART1_TX_PT0, 0, P_PUSHPULL,
GPIO_PT1, MUX_PAD_UART1_RX_PT1, 0, P_IN | P_PULLUP | P_TRISTATE),
PINMAP_4PIN(MUX_FEATURE_UARTB, GPIO_PX0, MUX_PAD_UART2_TX_PX0, 0, P_PUSHPULL,
GPIO_PX1, MUX_PAD_UART2_RX_PX1, 0, P_IN | P_PULLUP | P_TRISTATE,
GPIO_PX2, MUX_PAD_UART2_RTS_PX2, 0, P_PUSHPULL,
GPIO_PX3, MUX_PAD_UART2_CTS_PX3, 0, P_IN | P_PULLUP | P_TRISTATE),
PINMAP_4PIN(MUX_FEATURE_UARTD, GPIO_PB0, MUX_PAD_UART4_TX_PB0, 0, P_PUSHPULL,
GPIO_PB1, MUX_PAD_UART4_RX_PB1, 0, P_IN | P_PULLUP | P_TRISTATE,
GPIO_PB2, MUX_PAD_UART4_RTS_PB2, 0, P_PUSHPULL,
GPIO_PB3, MUX_PAD_UART4_CTS_PB3, 0, P_IN | P_PULLUP | P_TRISTATE),
PINMAP_4PIN(MUX_FEATURE_SPI1, GPIO_PH0, MUX_PAD_GPIO_WAN5_PH0, 2, P_PUSHPULL,
GPIO_PH1, MUX_PAD_GPIO_WAN6_PH1, 2, P_IN | P_PULLDOWN | P_TRISTATE,
GPIO_PH2, MUX_PAD_GPIO_WAN7_PH2, 2, P_PUSHPULL,
GPIO_PH3, MUX_PAD_GPIO_WAN8_PH3, 2, P_PULLUP),
/* I can't find which SPI pin routes to which MUX pin, so both directions are enabled */
PINMAP_4PIN(MUX_FEATURE_SPI3, GPIO_PX4, MUX_PAD_UART5_TX_PX4, 1, P_BOTH,
GPIO_PX5, MUX_PAD_UART5_RX_PX5, 1, P_BOTH,
GPIO_PX6, MUX_PAD_UART5_RTS_PX6, 1, P_BOTH,
GPIO_PX7, MUX_PAD_UART5_CTS_PX7, 1, P_BOTH),
PINMAP_4PIN(MUX_FEATURE_SPI4, GPIO_PN3, MUX_PAD_GPIO_CAM4_PN3, 1, P_PUSHPULL,
GPIO_PN4, MUX_PAD_GPIO_CAM5_PN4, 1, P_IN | P_PULLDOWN | P_TRISTATE,
GPIO_PN5, MUX_PAD_GPIO_CAM6_PN5, 1, P_PUSHPULL,
GPIO_PN6, MUX_PAD_GPIO_CAM7_PN6, 1, P_PULLUP),
PINMAP_2PIN(MUX_FEATURE_I2C1, GPIO_PC5, MUX_PAD_GEN1_I2C_SCL_PC5, 0, P_IN | P_OPEN_DRAIN,
GPIO_PC6, MUX_PAD_GEN1_I2C_SDA_PC6, 0, P_IN | P_OPEN_DRAIN),
PINMAP_2PIN(MUX_FEATURE_I2C3, GPIO_PO2, MUX_PAD_CAM_I2C_SCL_PO2, 0, P_IN,
GPIO_PO3, MUX_PAD_CAM_I2C_SDA_PO3, 0, P_IN),
PINMAP_2PIN(MUX_FEATURE_I2C7, GPIO_PL0, MUX_PAD_GEN7_I2C_SCL_PL0, 0, P_IN,
GPIO_PL1, MUX_PAD_GEN7_I2C_SDA_PL1, 0, P_IN),
PINMAP_2PIN(MUX_FEATURE_I2C9, GPIO_PL2, MUX_PAD_GEN9_I2C_SCL_PL2, 0, P_IN,
GPIO_PL3, MUX_PAD_GEN9_I2C_SDA_PL3, 0, P_IN),
PINMAP_6PIN(MUX_FEATURE_EQOS_RX, GPIO_PF3, MUX_PAD_EQOS_RXC_PF3, 0, P_IN | P_TRISTATE,
GPIO_PE6, MUX_PAD_EQOS_RD0_PE6, 0, P_IN | P_TRISTATE,
GPIO_PE7, MUX_PAD_EQOS_RD1_PE7, 0, P_IN | P_TRISTATE,
GPIO_PF0, MUX_PAD_EQOS_RD2_PF0, 0, P_IN | P_TRISTATE,
GPIO_PF1, MUX_PAD_EQOS_RD3_PF1, 0, P_IN | P_TRISTATE,
GPIO_PF2, MUX_PAD_EQOS_RX_CTL_PF2, 0, P_IN | P_TRISTATE),
PINMAP_6PIN(MUX_FEATURE_EQOS_TX, GPIO_PE0, MUX_PAD_EQOS_TXC_PE0, 0, P_PUSHPULL,
GPIO_PE1, MUX_PAD_EQOS_TD0_PE1, 0, P_PUSHPULL,
GPIO_PE2, MUX_PAD_EQOS_TD1_PE2, 0, P_PUSHPULL,
GPIO_PE3, MUX_PAD_EQOS_TD2_PE3, 0, P_PUSHPULL,
GPIO_PE4, MUX_PAD_EQOS_TD3_PE4, 0, P_PUSHPULL,
GPIO_PE5, MUX_PAD_EQOS_TX_CTL_PE5, 0, P_PUSHPULL),
PINMAP_2PIN(MUX_FEATURE_EQOS_MDIO, GPIO_PF4, MUX_PAD_EQOS_MDIO_PF4, 0, P_IN | P_PULLUP,
GPIO_PF5, MUX_PAD_EQOS_MDC_PF5, 0, P_PUSHPULL),
/* GPIOs don't require the SFIO function to be set properly. Just enable both buffers
* and let the GPIO interface handle the rest of the configuration */
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ0, GPIO_PJ0, MUX_PAD_DAP1_SCLK_PJ0, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ1, GPIO_PJ1, MUX_PAD_DAP1_DOUT_PJ1, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ2, GPIO_PJ2, MUX_PAD_DAP1_DIN_PJ2, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ3, GPIO_PJ3, MUX_PAD_DAP1_FS_PJ3, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ4, GPIO_PJ4, MUX_PAD_AUD_MCLK_PJ4, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ5, GPIO_PJ5, MUX_PAD_GPIO_AUD0_PJ5, 0, P_BOTH),
PINMAP_1PIN(MUX_FEATURE_GPIO_PJ6, GPIO_PJ6, MUX_PAD_GPIO_AUD1_PJ6, 0, P_BOTH),
};
enum mux_register_type {
CONTROL_REGISTER,
DRIVE_STRENGTH_REGISTER,
};
static bool is_valid_feature(enum mux_feature feature)
{
return 0 <= feature && feature <= NMUX_FEATURES;
}
static inline volatile uint32_t *tx2_mux_get_register(void *mux_base, uint16_t mux_reg_offset,
enum mux_register_type reg_type)
{
uintptr_t regs = (uintptr_t) mux_base;
int offset = (reg_type == CONTROL_REGISTER ? 0 : 4);
ZF_LOGD("Getting register type %d for pin with offset %hx, vaddr is = 0x%llx",
reg_type, mux_reg_offset, regs + mux_reg_offset + offset);
return (volatile uint32_t *)(regs + mux_reg_offset + offset);
}
static int tx2_mux_set_pin_params(const mux_sys_t *mux, struct tx2_mux_pin_desc *desc, enum mux_gpio_dir dir)
{
volatile uint32_t *pin_reg = tx2_mux_get_register(mux->priv, desc->mux_reg_offset, CONTROL_REGISTER);
uint32_t regval = *pin_reg;
/* Turn on tristate first whenever we set the pins parameters to avoid glitching. */
*pin_reg = BIT(MUX_REG_TRISTATE_SHIFT);
assert(*pin_reg == BIT(MUX_REG_TRISTATE_SHIFT));
/* Or in the right SFIO function */
uint32_t requiredval = (desc->mux_sfio_value & MUX_REG_SFIO_SELECT_MASK) << MUX_REG_SFIO_SELECT_MASK;
ZF_LOGD("Setting params for pin with offset %hx", desc->mux_reg_offset);
/* Enable input buffer if P_IN */
if (desc->flags & P_IN) {
requiredval |= BIT(MUX_REG_ENABLE_SHIFT);
}
/* Enable schmitt trigger if P_SCHMT */
if (desc->flags & P_SCHMT) {
requiredval |= BIT(MUX_REG_SCHMT_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 -EINVAL;
}
if (desc->flags & P_TRISTATE) {
ZF_LOGE("Can't enable both pushpull and tristate output drivers.");
return -EINVAL;
}
if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) {
ZF_LOGE("Can't enable both pushpull and pull-resistor output drivers.");
return -EINVAL;
}
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 -EINVAL;
}
if (desc->flags & P_PUSHPULL) {
ZF_LOGE("Can't enable both tristate and pushpull output drivers.");
return -EINVAL;
}
if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) {
ZF_LOGE("Can't enable both tristate and pull-resistor output drivers.");
return -EINVAL;
}
requiredval |= MUX_REG_TRISTATE_TRISTATE << 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 -EINVAL;
}
if (desc->flags & P_OPEN_DRAIN) {
ZF_LOGE("Can't enable both pullup and opendrain output drivers.");
return -EINVAL;
}
if (desc->flags & P_TRISTATE) {
ZF_LOGE("Can't enable both pullup and tristate output drivers.");
return -EINVAL;
}
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 -EINVAL;
}
if (desc->flags & P_TRISTATE) {
ZF_LOGE("Can't enable both open-drain and tristate output drivers.");
return -EINVAL;
}
if (desc->flags & P_PULLUP || desc->flags & P_PULLDOWN) {
ZF_LOGE("Can't enable both open-drain and pullup/pulldown output drivers.");
return -EINVAL;
}
requiredval |= BIT(MUX_REG_OPEN_DRAIN_SHIFT);
}
if (dir == MUX_DIR_NOT_A_GPIO) {
requiredval |= BIT(MUX_REG_SFIO_SELECT_SHIFT);
}
if (regval == requiredval) {
return 0;
}
*pin_reg = requiredval;
ZF_LOGD("pin_reg = 0x%lx", *pin_reg);
/* Just in case the writes are being silently ignored. */
assert(*pin_reg == requiredval);
return 0;
}
static int tx2_mux_feature_enable(const mux_sys_t *mux, mux_feature_t feature, enum mux_gpio_dir dir)
{
int error = 0;
if (!is_valid_feature(feature)) {
ZF_LOGE("Not a valid feature");
return -EINVAL;
}
if (dir != MUX_DIR_NOT_A_GPIO) {
if (!(MUX_FEATURE_GPIO_PJ0 <= feature && feature <= MUX_FEATURE_GPIO_PJ6)) {
ZF_LOGE("Not a valid GPIO feature");
return -EINVAL;
}
}
ZF_LOGD("Enabling feature %d", feature);
tx2_mux_feature_pinmap_t *map = &pinmaps[feature];
for (int i = 0; i < ARRAY_SIZE(map->pins); i++) {
if (map->pins[i].gpio_pin == -1) {
/* The pin list is terminated by -1 */
break;
}
error = tx2_mux_set_pin_params(mux, &map->pins[i], dir);
if (error) {
ZF_LOGE("Failed to set pinmux params for pin %d of feature %zd", i, feature);
return error;
}
}
return 0;
}
static inline void tx2_mux_disable_pin(const mux_sys_t *mux, struct tx2_mux_pin_desc *desc)
{
/* 8.29.3 of the TRM:
* For each unused MPIO, assert its tristate (TRISTATE_CONTROL) bit and
* disable its input buffer (E_INPUT) bit. Disable the Pullup/Pull down control to
* minimize the unnecessary power consumption on the unused pins.
*
* If all of the pins in a pad-control group are unused, set the drive strengths and slew rates to minimum.
*
* If all of the pins on a power rail are unused, assert E_NOIOPOWER for that rail in the PMC registers.
*/
volatile uint32_t *pin_reg = tx2_mux_get_register(mux->priv, desc->mux_reg_offset, CONTROL_REGISTER);
*pin_reg = 0 | (MUX_REG_TRISTATE_TRISTATE << MUX_REG_TRISTATE_SHIFT);
ZF_LOGD("*pin_reg = 0x%lx", *pin_reg);
/* Just in case the writes are being ignored */
assert(*pin_reg == (MUX_REG_TRISTATE_TRISTATE << MUX_REG_TRISTATE_SHIFT));
}
static int tx2_mux_feature_disable(const mux_sys_t *mux, mux_feature_t feature)
{
if (!is_valid_feature(feature)) {
return -EINVAL;
}
tx2_mux_feature_pinmap_t *map = &pinmaps[feature];
for (int i = 0; i < ARRAY_SIZE(map->pins); i++) {
if (map->pins[i].gpio_pin == -1) {
/* The pin list is terminated by -1 */
break;
}
tx2_mux_disable_pin(mux, &map->pins[i]);
}
return 0;
}
int mux_sys_init(ps_io_ops_t *io_ops, UNUSED void *dependencies, mux_sys_t *mux)
{
void *pinmux_vaddr = NULL;
pinmux_vaddr = ps_io_map(&io_ops->io_mapper, (uintptr_t) TX2_MUX_PADDR, TX2_MUX_SIZE, 0, PS_MEM_NORMAL);
if (pinmux_vaddr == NULL) {
ZF_LOGE("Failed to map in pinmux frames.");
return -1;
}
ZF_LOGD("pinmux_vaddr = 0x%llx", pinmux_vaddr);
mux->priv = pinmux_vaddr;
mux->feature_enable = &tx2_mux_feature_enable;
mux->feature_disable = &tx2_mux_feature_disable;
return 0;
}