zephyr/drivers/pinmux/pinmux_b91.c

246 lines
5.8 KiB
C

/*
* Copyright (c) 2021 Telink Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT telink_b91_pinmux
#include "analog.h"
#include <drivers/pinmux.h>
/**
* GPIO Function Enable Register
* ADDR PINS
* gpio_en: PORT_A[0-7]
* gpio_en + 1*8: PORT_B[0-7]
* gpio_en + 2*8: PORT_C[0-7]
* gpio_en + 3*8: PORT_D[0-7]
* gpio_en + 4*8: PORT_E[0-7]
* gpio_en + 5*8: PORT_F[0-7]
*/
#define reg_gpio_en(pin) (*(volatile uint8_t *)((uint32_t)DT_INST_REG_ADDR_BY_NAME(0, gpio_en) + \
((pin >> 8) * 8)))
/**
* Function Multiplexer Register
* ADDR PINS
* pin_mux: PORT_A[0-3]
* pin_mux + 1: PORT_A[4-7]
* pin_mux + 2: PORT_B[0-3]
* pin_mux + 3: PORT_B[4-7]
* pin_mux + 4: PORT_C[0-3]
* pin_mux + 5: PORT_C[4-7]
* pin_mux + 6: PORT_D[0-3]
* pin_mux + 7: PORT_D[4-7]
* pin_mux + 0x20: PORT_E[0-3]
* pin_mux + 0x21: PORT_E[4-7]
* pin_mux + 0x26: PORT_F[0-3]
* pin_mux + 0x27: PORT_F[4-7]
*/
#define reg_pin_mux(pin) (*(volatile uint8_t *)((uint32_t)DT_INST_REG_ADDR_BY_NAME(0, pin_mux) + \
(((pin >> 8) < 4) ? ((pin >> 8) * 2) : 0) + \
(((pin >> 8) == 4) ? 0x20 : 0) + \
(((pin >> 8) == 5) ? 0x26 : 0) + \
((pin & 0x0f0) ? 1 : 0)))
/**
* Pull Up resistors enable
* ADDR PINS
* pull_up_en: PORT_A[0-3]
* pull_up_en + 1: PORT_A[4-7]
* pull_up_en + 2: PORT_B[0-3]
* pull_up_en + 3: PORT_B[4-7]
* pull_up_en + 4: PORT_C[0-3]
* pull_up_en + 5: PORT_C[4-7]
* pull_up_en + 6: PORT_D[0-3]
* pull_up_en + 7: PORT_D[4-7]
* pull_up_en + 8: PORT_E[0-3]
* pull_up_en + 9: PORT_E[4-7]
* pull_up_en + 10: PORT_F[0-3]
* pull_up_en + 11: PORT_F[4-7]
*/
#define reg_pull_up_en(pin) ((uint8_t)(DT_INST_REG_ADDR_BY_NAME(0, pull_up_en) + \
((pin >> 8) * 2) + \
((pin & 0xf0) ? 1 : 0)))
/* GPIO Pull-Up options */
#define PINMUX_B91_PULLUP_DISABLE ((uint8_t)0u)
#define PINMUX_B91_PULLUP_10K ((uint8_t)3u)
/* Get PinMux configuration */
#define GET_CFG(dev) ((const struct pinmux_b91_config *)dev->config)
/* B91 PinMux config structure */
struct pinmux_b91_config {
uint8_t pad_mul_sel;
};
/* Act as GPIO function disable */
static inline void pinmux_b91_gpio_function_disable(uint32_t pin)
{
uint8_t bit = pin & 0xff;
reg_gpio_en(pin) &= ~bit;
}
/* Get function value bits start position (offset) */
static inline int pinmux_b91_get_func_offset(uint32_t pin, uint8_t *offset)
{
switch ((pin & 0x0fu) != 0u ? pin & 0x0fu : (pin & 0xf0u) >> 4u) {
case BIT(0):
*offset = 0u;
break;
case BIT(1):
*offset = 2u;
break;
case BIT(2):
*offset = 4u;
break;
case BIT(3):
*offset = 6u;
break;
default:
return -EINVAL;
}
return 0;
}
/* Set pin's pull-up/down resistor */
static void pinmux_b91_set_pull_up(uint32_t pin, uint8_t val)
{
uint8_t mask = 0;
uint8_t analog_reg = reg_pull_up_en(pin);
if (pin & 0x11) {
val = val;
mask = 0xfc;
} else if (pin & 0x22) {
val = val << 2;
mask = 0xf3;
} else if (pin & 0x44) {
val = val << 4;
mask = 0xcf;
} else if (pin & 0x88) {
val = val << 6;
mask = 0x3f;
} else {
return;
}
analog_write_reg8(analog_reg, (analog_read_reg8(analog_reg) & mask) | val);
}
/* API implementation: init */
static int pinmux_b91_init(const struct device *dev)
{
const struct pinmux_b91_config *cfg = GET_CFG(dev);
reg_gpio_pad_mul_sel |= cfg->pad_mul_sel;
return 0;
}
/* API implementation: set */
static int pinmux_b91_set(const struct device *dev, uint32_t pin, uint32_t func)
{
ARG_UNUSED(dev);
uint8_t mask = 0;
uint8_t offset = 0;
int32_t status = 0;
/* calculate offset and mask for the func value */
status = pinmux_b91_get_func_offset(pin, &offset);
if (status != 0) {
return status;
}
mask = (uint8_t)~(BIT(offset) | BIT(offset + 1));
/* disable GPIO function (can be enabled back by GPIO init using GPIO driver) */
pinmux_b91_gpio_function_disable(pin);
/* set func value */
reg_pin_mux(pin) = (reg_pin_mux(pin) & mask) | (func << offset);
return status;
}
/* API implementation: get */
static int pinmux_b91_get(const struct device *dev, uint32_t pin, uint32_t *func)
{
ARG_UNUSED(dev);
uint8_t mask = 0u;
uint8_t offset = 0;
int32_t status = 0;
/* calculate offset and mask for the func value */
status = pinmux_b91_get_func_offset(pin, &offset);
if (status != 0) {
return status;
}
mask = (uint8_t)(BIT(offset) | BIT(offset + 1));
/* get func value */
*func = (reg_pin_mux(pin) & mask) >> offset;
return status;
}
/* API implementation: pullup */
static int pinmux_b91_pullup(const struct device *dev, uint32_t pin, uint8_t func)
{
ARG_UNUSED(dev);
switch (func) {
case PINMUX_PULLUP_ENABLE:
pinmux_b91_set_pull_up(pin, PINMUX_B91_PULLUP_10K);
break;
case PINMUX_PULLUP_DISABLE:
pinmux_b91_set_pull_up(pin, PINMUX_B91_PULLUP_DISABLE);
break;
default:
return -ENOTSUP;
}
return 0;
}
/* API implementation: input */
static int pinmux_b91_input(const struct device *dev, uint32_t pin, uint8_t func)
{
ARG_UNUSED(dev);
ARG_UNUSED(pin);
ARG_UNUSED(func);
/* Implemented by GPIO driver */
return -ENOTSUP;
}
/* PinMux driver APIs structure */
static const struct pinmux_driver_api pinmux_b91_api = {
.set = pinmux_b91_set,
.get = pinmux_b91_get,
.pullup = pinmux_b91_pullup,
.input = pinmux_b91_input,
};
static const struct pinmux_b91_config pinmux_b91_cfg = {
.pad_mul_sel = DT_INST_PROP(0, pad_mul_sel)
};
/* PinMux driver registration */
DEVICE_DT_INST_DEFINE(0, pinmux_b91_init,
NULL, NULL, &pinmux_b91_cfg, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&pinmux_b91_api);