zephyr/drivers/pinmux/pinmux_esp32.c

187 lines
4.5 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_pinmux
/* Include esp-idf headers first to avoid redefining BIT() macro */
#include <soc/gpio_reg.h>
#include <soc/io_mux_reg.h>
#include <soc/soc.h>
#include <errno.h>
#include <sys/util.h>
#include <drivers/pinmux.h>
/* DR_REG_IO_MUX_BASE is a 32-bit constant. Define a pin mux table
* using only offsets, in order to reduce ROM footprint.
* This table has been compiled from information present in "ESP32
* Technical Reference Manual", "IO_MUX Pad List". The items in
* this array covers only the first function of each I/O pin.
* Items with offset `0` are not present in the documentation, and
* trying to configure them will result in -EINVAL being returned.
*
* Note: DR_REG_IO_MUX_BASE here is used to extract GPIO_X register offset.
* Don't replace it by device tree value, because PERIPHS_IO_MUX_
* is "internally" depends on it.
*/
#define PIN(id) ((PERIPHS_IO_MUX_ ## id ## _U) - (DR_REG_IO_MUX_BASE))
static const uint8_t pin_mux_off[] = {
PIN(GPIO0), PIN(U0TXD), PIN(GPIO2), PIN(U0RXD),
PIN(GPIO4), PIN(GPIO5), PIN(SD_CLK), PIN(SD_DATA0),
PIN(SD_DATA1), PIN(SD_DATA2), PIN(SD_DATA3), PIN(SD_CMD),
PIN(MTDI), PIN(MTCK), PIN(MTMS), PIN(MTDO),
PIN(GPIO16), PIN(GPIO17), PIN(GPIO18), PIN(GPIO19),
0, PIN(GPIO21), PIN(GPIO22), PIN(GPIO23),
0, PIN(GPIO25), PIN(GPIO26), PIN(GPIO27),
0, 0, 0, 0,
PIN(GPIO32), PIN(GPIO33), PIN(GPIO34), PIN(GPIO35),
PIN(GPIO36), PIN(GPIO37), PIN(GPIO38), PIN(GPIO39)
};
#undef PIN
static uint32_t *reg_for_pin(uint32_t pin)
{
uint8_t off;
if (pin >= ARRAY_SIZE(pin_mux_off)) {
return NULL;
}
off = pin_mux_off[pin];
if (!off) {
return NULL;
}
return (uint32_t *)(DT_INST_REG_ADDR(0) + off);
}
static int set_reg(uint32_t pin, uint32_t clr_mask, uint32_t set_mask)
{
volatile uint32_t *reg = reg_for_pin(pin);
uint32_t v;
if (!reg) {
return -EINVAL;
}
v = *reg;
v &= ~clr_mask;
v |= set_mask;
*reg = v;
return 0;
}
static int pinmux_set(struct device *dev, uint32_t pin, uint32_t func)
{
ARG_UNUSED(dev);
/* FIXME: Drive strength (FUN_DRV) is also set here to its maximum
* value due to a deficiency in the pinmux API. This setting is
* part of the GPIO API.
*/
if (func > 6) {
return -EINVAL;
}
return set_reg(pin, MCU_SEL_M, func<<MCU_SEL_S | 2<<FUN_DRV_S);
}
static int pinmux_get(struct device *dev, uint32_t pin, uint32_t *func)
{
volatile uint32_t *reg = reg_for_pin(pin);
if (!reg) {
return -EINVAL;
}
*func = (*reg & MCU_SEL_M) >> MCU_SEL_S;
ARG_UNUSED(dev);
return 0;
}
static int pinmux_pullup(struct device *dev, uint32_t pin, uint8_t func)
{
switch (func) {
case PINMUX_PULLUP_DISABLE:
return set_reg(pin, FUN_PU, FUN_PD);
case PINMUX_PULLUP_ENABLE:
return set_reg(pin, FUN_PD, FUN_PU);
}
ARG_UNUSED(dev);
return -EINVAL;
}
#define CFG(id) ((GPIO_ ## id ## _REG) & 0xff)
static int pinmux_input(struct device *dev, uint32_t pin, uint8_t func)
{
static const uint8_t offs[2][3] = {
{ CFG(ENABLE1_W1TC), CFG(ENABLE1_W1TS), 32 },
{ CFG(ENABLE_W1TC), CFG(ENABLE_W1TS), 0 },
};
const uint8_t *line = offs[pin < 32];
volatile uint32_t *reg;
int r;
/* Since PINMUX_INPUT_ENABLED == 1 and PINMUX_OUTPUT_ENABLED == 0,
* we can not set a gpio port as input and output at the same time,
* So we always set the gpio as input. Thus, the gpio can be used on
* I2C drivers for example.
*/
r = set_reg(pin, 0, FUN_IE);
if (func == PINMUX_INPUT_ENABLED) {
reg = (uint32_t *)(DR_REG_GPIO_BASE + line[0]);
} else if (func == PINMUX_OUTPUT_ENABLED) {
if (pin >= 34U && pin <= 39U) {
/* These pins are input only */
return -EINVAL;
}
reg = (uint32_t *)(DR_REG_GPIO_BASE + line[1]);
} else {
return -EINVAL;
}
if (r < 0) {
return r;
}
*reg = BIT(pin - line[2]);
ARG_UNUSED(dev);
return 0;
}
#undef CFG
static struct pinmux_driver_api api_funcs = {
.set = pinmux_set,
.get = pinmux_get,
.pullup = pinmux_pullup,
.input = pinmux_input
};
static int pinmux_initialize(struct device *device)
{
uint32_t pin;
for (pin = 0U; pin < ARRAY_SIZE(pin_mux_off); pin++) {
pinmux_set(NULL, pin, 0);
}
ARG_UNUSED(device);
return 0;
}
/* Initialize using PRE_KERNEL_1 priority so that GPIO can use the pin
* mux driver.
*/
DEVICE_AND_API_INIT(pmux_dev, CONFIG_PINMUX_NAME,
&pinmux_initialize, NULL, NULL,
PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&api_funcs);