/* * Copyright (c) 2021 Telink Semiconductor * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT telink_b91_pinmux #include "analog.h" #include /** * 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);