From 2d55003a6b84e8894bce1feff1b49566c6f69bf7 Mon Sep 17 00:00:00 2001 From: Siva Rama Krishna Potu Date: Tue, 14 Jul 2026 19:11:17 +0530 Subject: [PATCH 1/2] drivers: qcom: add TLMM GPIO and pinctrl driver Add a Qualcomm TLMM GPIO and pinctrl driver implementing the OP-TEE gpio_ops and pinctrl_ops interfaces. Signed-off-by: Siva Rama Krishna Potu --- core/drivers/qcom/sub.mk | 1 + core/drivers/qcom/tlmm/gpio.c | 268 ++++++++++++++++++++++++++ core/drivers/qcom/tlmm/pinctrl.c | 251 ++++++++++++++++++++++++ core/drivers/qcom/tlmm/sub.mk | 5 + core/drivers/qcom/tlmm/tlmm_priv.h | 96 +++++++++ core/include/drivers/qcom/tlmm/tlmm.h | 96 +++++++++ 6 files changed, 717 insertions(+) create mode 100644 core/drivers/qcom/tlmm/gpio.c create mode 100644 core/drivers/qcom/tlmm/pinctrl.c create mode 100644 core/drivers/qcom/tlmm/sub.mk create mode 100644 core/drivers/qcom/tlmm/tlmm_priv.h create mode 100644 core/include/drivers/qcom/tlmm/tlmm.h diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 56e7d0625..e056e5542 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -12,3 +12,4 @@ subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh subdirs-$(CFG_QCOM_QFPROM) += qfprom subdirs-$(CFG_QCOM_XPUV4) += xpu +subdirs-$(CFG_QCOM_TLMM) += tlmm diff --git a/core/drivers/qcom/tlmm/gpio.c b/core/drivers/qcom/tlmm/gpio.c new file mode 100644 index 000000000..f1d221299 --- /dev/null +++ b/core/drivers/qcom/tlmm/gpio.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlmm_priv.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, TLMM_BASE, TLMM_BASE_SIZE); + +struct tlmm_chip tlmm = { + .lock = MUTEX_INITIALIZER, +}; + +static const struct gpio_ops tlmm_gpio_ops; + +uint32_t tlmm_tile_offset(const struct tlmm_chip *chip, unsigned int pin) +{ + const struct tlmm_desc *d = chip->desc; + unsigned int t = 0; + vaddr_t id_reg = 0; + + if (d->num_tiles <= 1) + return d->tile_offsets[0]; + + for (t = 0; t < d->num_tiles; t++) { + id_reg = chip->base + + d->tile_offsets[t] + + (vaddr_t)d->pin_reg_width * pin + + TLMM_REG_ID_STATUS; + if (io_read32(id_reg) & TLMM_ID_STATUS_PRESENT) + return d->tile_offsets[t]; + } + + EMSG("TLMM: pin %u not found in any tile, defaulting to tile 0", pin); + return d->tile_offsets[0]; +} + +vaddr_t tlmm_pin_reg(const struct tlmm_chip *chip, unsigned int pin, + uint32_t reg_off) +{ + return chip->base + + tlmm_tile_offset(chip, pin) + + (vaddr_t)chip->desc->pin_reg_width * pin + + reg_off; +} + +void tlmm_claim_egpio_unlocked(struct tlmm_chip *chip, unsigned int pin) +{ + vaddr_t reg = TLMM_GPIO_CFG(chip, pin); + uint32_t val = io_read32(reg); + + if (!chip->desc->has_egpio || !(val & TLMM_CFG_EGPIO_PRESENT)) + return; + + if (val & TLMM_CFG_EGPIO_ENABLE) + return; + + io_write32(reg, val | TLMM_CFG_EGPIO_ENABLE); +} + +void tlmm_write_cfg_unlocked(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask) +{ + vaddr_t reg = TLMM_GPIO_CFG(chip, pin); + uint32_t val = 0; + + tlmm_claim_egpio_unlocked(chip, pin); + + val = io_read32(reg); + clear_mask &= ~TLMM_CFG_PRESERVE_MASK; + val = (val & ~clear_mask) | set_mask; + io_write32(reg, val); +} + +void tlmm_write_cfg(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask) +{ + mutex_lock(&chip->lock); + tlmm_write_cfg_unlocked(chip, pin, clear_mask, set_mask); + mutex_unlock(&chip->lock); +} + +static bool is_tlmm_chip(struct gpio_chip *chip) +{ + return chip && chip->ops == &tlmm_gpio_ops; +} + +static struct tlmm_chip *chip_to_tlmm(struct gpio_chip *chip) +{ + assert(is_tlmm_chip(chip)); + return container_of(chip, struct tlmm_chip, gpio_chip); +} + +bool tlmm_pin_is_owned_unlocked(unsigned int pin) +{ + if (!tlmm.desc) + return false; + + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return false; + + return tlmm.pin_owners[pin / 32] & BIT(pin % 32); +} + +void tlmm_restore_lp(struct tlmm_chip *chip, unsigned int pin) +{ + uint32_t lp_val = io_read32(TLMM_GPIO_LP_CFG(chip, pin)); + + if (!(lp_val & TLMM_LP_CFG_APPLIED)) + return; + + io_write32(TLMM_GPIO_IN_OUT(chip, pin), + (lp_val & TLMM_LP_CFG_OUT_VAL) ? TLMM_IN_OUT_OUT_BIT : U(0)); + + io_write32(TLMM_GPIO_CFG(chip, pin), lp_val & TLMM_LP_CFG_CONFIG_MASK); +} + +TEE_Result tlmm_request_pin(unsigned int pin) +{ + if (!tlmm.desc) + return TEE_ERROR_BAD_STATE; + + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&tlmm.lock); + if (tlmm_pin_is_owned_unlocked(pin)) { + mutex_unlock(&tlmm.lock); + EMSG("TLMM: pin %u already owned", pin); + return TEE_ERROR_BUSY; + } + tlmm.pin_owners[pin / 32] |= BIT(pin % 32); + mutex_unlock(&tlmm.lock); + + return TEE_SUCCESS; +} + +void tlmm_release_pin(unsigned int pin) +{ + if (!tlmm.desc) + return; + + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return; + + mutex_lock(&tlmm.lock); + if (!tlmm_pin_is_owned_unlocked(pin)) { + mutex_unlock(&tlmm.lock); + EMSG("TLMM: pin %u not owned, release rejected", pin); + return; + } + tlmm_restore_lp(&tlmm, pin); + tlmm.pin_owners[pin / 32] &= ~BIT(pin % 32); + mutex_unlock(&tlmm.lock); +} + +static enum gpio_dir tlmm_get_direction(struct gpio_chip *chip, + unsigned int pin) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + if (pin >= tc->desc->num_gpios) { + EMSG("TLMM: pin %u out of range", pin); + return GPIO_DIR_IN; + } + + return (io_read32(TLMM_GPIO_CFG(tc, pin)) & TLMM_CFG_OE) ? + GPIO_DIR_OUT : GPIO_DIR_IN; +} + +static void tlmm_set_direction(struct gpio_chip *chip, unsigned int pin, + enum gpio_dir dir) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + assert(pin < tc->desc->num_gpios); + + mutex_lock(&tc->lock); + if (!tlmm_pin_is_owned_unlocked(pin)) { + mutex_unlock(&tc->lock); + EMSG("TLMM: pin %u not owned, set_direction rejected", pin); + return; + } + if (dir == GPIO_DIR_OUT) + tlmm_write_cfg_unlocked(tc, pin, 0, TLMM_CFG_OE); + else + tlmm_write_cfg_unlocked(tc, pin, TLMM_CFG_OE, 0); + mutex_unlock(&tc->lock); +} + +static enum gpio_level tlmm_get_value(struct gpio_chip *chip, unsigned int pin) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + if (pin >= tc->desc->num_gpios) { + EMSG("TLMM: pin %u out of range", pin); + return GPIO_LEVEL_LOW; + } + + return (io_read32(TLMM_GPIO_IN_OUT(tc, pin)) & TLMM_IN_OUT_IN_BIT) ? + GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; +} + +static void tlmm_set_value(struct gpio_chip *chip, unsigned int pin, + enum gpio_level level) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + assert(pin < tc->desc->num_gpios); + + mutex_lock(&tc->lock); + if (!tlmm_pin_is_owned_unlocked(pin)) { + mutex_unlock(&tc->lock); + EMSG("TLMM: pin %u not owned, set_value rejected", pin); + return; + } + tlmm_claim_egpio_unlocked(tc, pin); + io_write32(TLMM_GPIO_IN_OUT(tc, pin), + (level == GPIO_LEVEL_HIGH) ? TLMM_IN_OUT_OUT_BIT : U(0)); + mutex_unlock(&tc->lock); +} + +static const struct gpio_ops tlmm_gpio_ops = { + .get_direction = tlmm_get_direction, + .set_direction = tlmm_set_direction, + .get_value = tlmm_get_value, + .set_value = tlmm_set_value, +}; + +struct gpio_chip *tlmm_get_chip(void) +{ + if (!tlmm.desc) + return NULL; + + return &tlmm.gpio_chip; +} + +static TEE_Result tlmm_init(void) +{ + const struct tlmm_desc *desc = &tlmm_soc_desc; + + tlmm.base = (vaddr_t)phys_to_virt(desc->base, MEM_AREA_IO_SEC, + desc->size); + if (!tlmm.base) { + EMSG("TLMM: failed to map base 0x%"PRIxPA, desc->base); + return TEE_ERROR_GENERIC; + } + + tlmm.desc = desc; + tlmm.gpio_chip.ops = &tlmm_gpio_ops; + + IMSG("TLMM: base=0x%"PRIxPA", %u GPIOs", desc->base, desc->num_gpios); + + return TEE_SUCCESS; +} + +driver_init(tlmm_init); diff --git a/core/drivers/qcom/tlmm/pinctrl.c b/core/drivers/qcom/tlmm/pinctrl.c new file mode 100644 index 000000000..449c87f23 --- /dev/null +++ b/core/drivers/qcom/tlmm/pinctrl.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "tlmm_priv.h" + +struct tlmm_pin_conf { + struct tlmm_chip *chip; + uint32_t func; + uint32_t pull; + uint32_t drive_hw; + bool strong_pull; + bool applied; + unsigned int pin_count; + unsigned int pins[]; +}; + +static TEE_Result tlmm_conf_apply(struct pinconf *conf) +{ + struct tlmm_pin_conf *pc = conf->priv; + struct tlmm_chip *chip = pc->chip; + unsigned int i = 0; + uint32_t clear = 0; + uint32_t set = 0; + + mutex_lock(&chip->lock); + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + if (tlmm_pin_is_owned_unlocked(p)) { + EMSG("TLMM: pin %u already owned, conf_apply rejected", + p); + mutex_unlock(&chip->lock); + return TEE_ERROR_BUSY; + } + } + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + chip->pin_owners[p / 32] |= BIT(p % 32); + } + /* + * @applied tracks pin ownership, not hardware state: it tells + * conf_free() that this conf claimed the pins above and so must + * release them. Set it while still holding the lock, together with the + * claim it mirrors, so ownership can never be granted without a + * matching release. + */ + pc->applied = true; + mutex_unlock(&chip->lock); + + clear = TLMM_CFG_PULL_MASK | + TLMM_CFG_FUNC_MASK | + TLMM_CFG_DRIVE_MASK | + TLMM_CFG_STRONG_PULL; + + set = (pc->pull & TLMM_CFG_PULL_MASK) | + ((pc->func << TLMM_CFG_FUNC_SHIFT) & TLMM_CFG_FUNC_MASK) | + ((pc->drive_hw << TLMM_CFG_DRIVE_SHIFT) & TLMM_CFG_DRIVE_MASK); + + if (chip->desc->has_strong_pull && + pc->strong_pull && + pc->pull == TLMM_PULL_UP) + set |= TLMM_CFG_STRONG_PULL; + + for (i = 0; i < pc->pin_count; i++) { + DMSG("TLMM pinctrl: pin %u func=%u pull=%u drive_hw=%u", + pc->pins[i], pc->func, pc->pull, pc->drive_hw); + tlmm_write_cfg(chip, pc->pins[i], clear, set); + } + + return TEE_SUCCESS; +} + +static void tlmm_conf_free(struct pinconf *conf) +{ + struct tlmm_pin_conf *pc = conf->priv; + struct tlmm_chip *chip = pc->chip; + unsigned int i = 0; + + if (pc->applied) { + mutex_lock(&chip->lock); + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + tlmm_restore_lp(chip, p); + chip->pin_owners[p / 32] &= ~BIT(p % 32); + } + mutex_unlock(&chip->lock); + } + + free(conf); +} + +static const struct pinctrl_ops tlmm_pinctrl_ops = { + .conf_apply = tlmm_conf_apply, + .conf_free = tlmm_conf_free, +}; + +/* + * Drive strength is 2 mA steps from 2 mA (0) to 16 mA (7). + */ +static uint32_t tlmm_drive_to_hw(unsigned int drive_ma) +{ + if (drive_ma <= 2) + return 0; + + return MIN(DIV_ROUND_UP(drive_ma - 2, 2), 7U); +} + +static TEE_Result tlmm_make_pinconf(const unsigned int *pins, + unsigned int pin_count, + uint32_t func, uint32_t pull, + unsigned int drive_ma, bool strong_pull, + struct pinconf **out_conf) +{ + struct pinconf *conf = NULL; + struct tlmm_pin_conf *pc = NULL; + size_t alloc_size = 0; + unsigned int i = 0; + + if (!pins || !pin_count || !out_conf) + return TEE_ERROR_BAD_PARAMETERS; + + if (!tlmm.desc) + return TEE_ERROR_BAD_STATE; + + if (pin_count > TLMM_MAX_PINS_PER_GROUP) { + EMSG("TLMM: pin_count %u exceeds max %u", + pin_count, TLMM_MAX_PINS_PER_GROUP); + return TEE_ERROR_BAD_PARAMETERS; + } + + for (i = 0; i < pin_count; i++) { + /* + * Bound against both the SoC pin count and the ownership + * bitmap size, since the two are independent limits. + */ + if (pins[i] >= tlmm.desc->num_gpios || + pins[i] >= TLMM_MAX_GPIOS) { + EMSG("TLMM: pin %u out of range (num_gpios %u, max %u)", + pins[i], tlmm.desc->num_gpios, TLMM_MAX_GPIOS); + return TEE_ERROR_BAD_PARAMETERS; + } + } + + alloc_size = sizeof(*conf) + sizeof(*pc) + + pin_count * sizeof(pc->pins[0]); + + conf = calloc(1, alloc_size); + if (!conf) + return TEE_ERROR_OUT_OF_MEMORY; + + pc = (struct tlmm_pin_conf *)(conf + 1); + + pc->chip = &tlmm; + pc->func = func; + pc->pull = pull; + pc->drive_hw = tlmm_drive_to_hw(drive_ma); + pc->strong_pull = strong_pull; + pc->pin_count = pin_count; + memcpy(pc->pins, pins, pin_count * sizeof(pc->pins[0])); + + conf->ops = &tlmm_pinctrl_ops; + conf->priv = pc; + + *out_conf = conf; + return TEE_SUCCESS; +} + +TEE_Result tlmm_make_pin_state(const struct tlmm_pin_group *groups, + unsigned int group_count, + struct pinctrl_state **out_state) +{ + struct pinctrl_state *state = NULL; + unsigned int i = 0; + unsigned int built = 0; + TEE_Result res = TEE_SUCCESS; + + if (!groups || !group_count || !out_state) + return TEE_ERROR_BAD_PARAMETERS; + + state = calloc(1, sizeof(*state) + + group_count * sizeof(*state->confs)); + if (!state) + return TEE_ERROR_OUT_OF_MEMORY; + + for (built = 0; built < group_count; built++) { + res = tlmm_make_pinconf(groups[built].pins, + groups[built].pin_count, + groups[built].func, + groups[built].pull, + groups[built].drive_ma, + groups[built].strong_pull, + &state->confs[built]); + if (res) { + for (i = 0; i < built; i++) { + struct pinconf *c = state->confs[i]; + + c->ops->conf_free(c); + } + free(state); + return res; + } + } + + state->conf_count = group_count; + *out_state = state; + return TEE_SUCCESS; +} + +TEE_Result tlmm_apply_pin_state(struct pinctrl_state *state) +{ + unsigned int i = 0; + TEE_Result res = TEE_SUCCESS; + + if (!state) + return TEE_ERROR_BAD_PARAMETERS; + + for (i = 0; i < state->conf_count; i++) { + res = state->confs[i]->ops->conf_apply(state->confs[i]); + if (res) { + EMSG("TLMM: conf_apply failed for group %u: %#"PRIx32, + i, res); + return res; + } + } + + return TEE_SUCCESS; +} + +void tlmm_free_pin_state(struct pinctrl_state *state) +{ + unsigned int i = 0; + + if (!state) + return; + + for (i = 0; i < state->conf_count; i++) + state->confs[i]->ops->conf_free(state->confs[i]); + + free(state); +} diff --git a/core/drivers/qcom/tlmm/sub.mk b/core/drivers/qcom/tlmm/sub.mk new file mode 100644 index 000000000..4ff2e9512 --- /dev/null +++ b/core/drivers/qcom/tlmm/sub.mk @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + +srcs-$(CFG_QCOM_TLMM) += gpio.c +srcs-$(CFG_QCOM_TLMM) += pinctrl.c diff --git a/core/drivers/qcom/tlmm/tlmm_priv.h b/core/drivers/qcom/tlmm/tlmm_priv.h new file mode 100644 index 000000000..3fca50bd5 --- /dev/null +++ b/core/drivers/qcom/tlmm/tlmm_priv.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_TLMM_PRIV_H +#define __QCOM_TLMM_PRIV_H + +#include +#include +#include +#include +#include +#include + +#define TLMM_CFG_PULL_MASK GENMASK_32(1, 0) +#define TLMM_CFG_FUNC_SHIFT U(2) +#define TLMM_CFG_FUNC_MASK GENMASK_32(5, 2) +#define TLMM_CFG_DRIVE_SHIFT U(6) +#define TLMM_CFG_DRIVE_MASK GENMASK_32(8, 6) +#define TLMM_CFG_OE BIT(9) +#define TLMM_CFG_HIHYS BIT(10) +#define TLMM_CFG_EGPIO_PRESENT BIT(11) +#define TLMM_CFG_EGPIO_ENABLE BIT(12) +#define TLMM_CFG_STRONG_PULL BIT(13) +#define TLMM_CFG_PRESERVE_MASK TLMM_CFG_HIHYS + +#define TLMM_IN_OUT_IN_BIT BIT(0) +#define TLMM_IN_OUT_OUT_BIT BIT(1) + +#define TLMM_ID_STATUS_PRESENT BIT(0) + +/* + * GPIO_LP_CFG bits [9:0] match GPIO_CFG, but bit 10 is the stored output + * level and bit 11 flags a valid low-power config, so restore-on-release is + * not a plain masked copy into GPIO_CFG. + */ +#define TLMM_LP_CFG_OUT_VAL BIT(10) +#define TLMM_LP_CFG_APPLIED BIT(11) +#define TLMM_LP_CFG_CONFIG_MASK GENMASK_32(9, 0) + +#define TLMM_MAX_GPIOS U(256) + +#define TLMM_REG_CFG U(0x00) +#define TLMM_REG_IN_OUT U(0x04) +#define TLMM_REG_ID_STATUS U(0x10) +#define TLMM_REG_LP_CFG U(0x14) + +struct tlmm_chip { + struct gpio_chip gpio_chip; + vaddr_t base; + const struct tlmm_desc *desc; + /* Serialises @pin_owners and the GPIO_CFG read-modify-writes */ + struct mutex lock; + uint32_t pin_owners[TLMM_MAX_GPIOS / 32]; +}; + +uint32_t tlmm_tile_offset(const struct tlmm_chip *chip, unsigned int pin); +vaddr_t tlmm_pin_reg(const struct tlmm_chip *chip, unsigned int pin, + uint32_t reg_off); + +/* + * A GPIO_CFG update is a read-modify-write, and the pin ownership bitmap must + * not change between the ownership check and the register write, so both are + * serialised by @chip->lock. Callers already holding the lock use the + * _unlocked() variant; the locking wrapper is for callers that do not. + * + * @chip->lock is a mutex, so every caller must be in thread context. + */ +void tlmm_write_cfg_unlocked(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask); +void tlmm_write_cfg(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask); + +/* Both require @chip->lock to be held by the caller */ +bool tlmm_pin_is_owned_unlocked(unsigned int pin); +void tlmm_restore_lp(struct tlmm_chip *chip, unsigned int pin); + +/* + * EGPIO_PRESENT is read-only and set by hardware only on pads shared with an + * Island TLMM, where EGPIO_ENABLE picks the owner: 1 gives the pad to the chip + * TLMM, 0 leaves it with the Island domain. It reads 0 out of reset, so a + * shared pad comes up Island-owned and the registers this driver programs do + * not reach it until the pad is claimed. + */ +void tlmm_claim_egpio_unlocked(struct tlmm_chip *chip, unsigned int pin); + +#define TLMM_GPIO_CFG(c, p) tlmm_pin_reg((c), (p), TLMM_REG_CFG) +#define TLMM_GPIO_IN_OUT(c, p) tlmm_pin_reg((c), (p), TLMM_REG_IN_OUT) +#define TLMM_GPIO_ID_STATUS(c, p) \ + tlmm_pin_reg((c), (p), TLMM_REG_ID_STATUS) +#define TLMM_GPIO_LP_CFG(c, p) tlmm_pin_reg((c), (p), TLMM_REG_LP_CFG) + +extern struct tlmm_chip tlmm; + +#endif /* __QCOM_TLMM_PRIV_H */ diff --git a/core/include/drivers/qcom/tlmm/tlmm.h b/core/include/drivers/qcom/tlmm/tlmm.h new file mode 100644 index 000000000..7bed26ef8 --- /dev/null +++ b/core/include/drivers/qcom/tlmm/tlmm.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Qualcomm TLMM GPIO and pinctrl driver - public API. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DRIVERS_QCOM_TLMM_TLMM_H +#define __DRIVERS_QCOM_TLMM_TLMM_H + +#include +#include +#include +#include +#include +#include + +#define TLMM_MAX_TILES U(5) + +struct tlmm_desc { + paddr_t base; + size_t size; + uint32_t pin_reg_width; + uint32_t num_tiles; + uint32_t tile_offsets[TLMM_MAX_TILES]; + unsigned int num_gpios; + bool has_egpio; + bool has_strong_pull; +}; + +/* SoC-specific hardware descriptor, defined per platform in tlmm_soc_data.c */ +extern const struct tlmm_desc tlmm_soc_desc; + +#define TLMM_PULL_NONE U(0) +#define TLMM_PULL_DOWN U(1) +#define TLMM_PULL_KEEPER U(2) +#define TLMM_PULL_UP U(3) + +#define TLMM_MAX_PINS_PER_GROUP U(64) + +/* + * tlmm_get_chip() - Get the GPIO chip for the TLMM pin controller + * + * Returns NULL if the driver failed to initialise, since a failed initcall + * does not stop the boot. + */ +struct gpio_chip *tlmm_get_chip(void); + +TEE_Result tlmm_request_pin(unsigned int pin); +void tlmm_release_pin(unsigned int pin); + +struct tlmm_pin_group { + const unsigned int *pins; + unsigned int pin_count; + uint32_t func; + uint32_t pull; + unsigned int drive_ma; + bool strong_pull; +}; + +/* + * tlmm_make_pin_state() - Build a pin state from a list of pin groups + * @groups: Pin groups to describe, one pinconf is created per group + * @group_count: Number of entries in @groups + * @out_state: Filled in with the new state on success, untouched otherwise + * + * The returned state holds no pin ownership until it is applied. Release it + * with tlmm_free_pin_state(). + */ +TEE_Result tlmm_make_pin_state(const struct tlmm_pin_group *groups, + unsigned int group_count, + struct pinctrl_state **out_state); + +/* + * tlmm_apply_pin_state() - Claim the pins of @state and program them + * @state: State previously built by tlmm_make_pin_state() + * + * Groups are applied in order and each claims its pins exclusively, so this + * fails with TEE_ERROR_BUSY if any pin is already owned. On failure the groups + * applied so far keep their pins claimed: @state is left partially applied and + * the caller must still pass it to tlmm_free_pin_state(), which releases + * exactly the groups that were applied. Not doing so leaks pin ownership and + * no later request for those pins can succeed. + */ +TEE_Result tlmm_apply_pin_state(struct pinctrl_state *state); + +/* + * tlmm_free_pin_state() - Release any claimed pins and free @state + * @state: State to release, may be NULL + * + * Restores the low-power configuration of every pin claimed by @state and + * drops its ownership, whether @state was fully or only partially applied. + */ +void tlmm_free_pin_state(struct pinctrl_state *state); + +#endif /* __DRIVERS_QCOM_TLMM_TLMM_H */ From ed42f52543cedc6fc26ca2ed2fe047f1017172d6 Mon Sep 17 00:00:00 2001 From: Siva Rama Krishna Potu Date: Tue, 14 Jul 2026 19:12:21 +0530 Subject: [PATCH 2/2] drivers: qcom: tlmm: enable TLMM driver for lemans Enable CFG_QCOM_TLMM for lemans and add the SoC hardware descriptor. Signed-off-by: Siva Rama Krishna Potu --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 2 ++ .../arm/plat-qcom/hoya/lemans/target_config.h | 3 +++ core/drivers/qcom/tlmm/lemans/tlmm_soc_data.c | 21 +++++++++++++++++++ core/drivers/qcom/tlmm/sub.mk | 1 + 4 files changed, 27 insertions(+) create mode 100644 core/drivers/qcom/tlmm/lemans/tlmm_soc_data.c diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 1cf345223..e02d86048 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -1,6 +1,8 @@ CFG_DRIVERS_CLK ?= y CFG_DRIVERS_QCOM_CLK ?= y +CFG_QCOM_TLMM ?= y + CFG_QCOM_DIAG_LOG ?= $(CFG_TEE_CORE_DEBUG) ifneq ($(CFG_INSECURE),y) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h index 757c67c61..2b3dae0c1 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -30,6 +30,9 @@ #define IMEM_BASE UL(0x14680000) #define IMEM_SIZE UL(0x32000) +#define TLMM_BASE UL(0x0f000000) +#define TLMM_BASE_SIZE UL(0x01000000) + #define TURING_0_BASE UL(0x24000000) #define TURING_0_SIZE UL(0x03000000) diff --git a/core/drivers/qcom/tlmm/lemans/tlmm_soc_data.c b/core/drivers/qcom/tlmm/lemans/tlmm_soc_data.c new file mode 100644 index 000000000..d55cb0e66 --- /dev/null +++ b/core/drivers/qcom/tlmm/lemans/tlmm_soc_data.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * LeMans TLMM hardware descriptor. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +const struct tlmm_desc tlmm_soc_desc = { + .base = TLMM_BASE, + .size = TLMM_BASE_SIZE, + .pin_reg_width = 0x1000, + .num_tiles = 1, + .tile_offsets = { [0] = 0x100000 }, + .num_gpios = 149, + .has_egpio = true, + .has_strong_pull = false, +}; diff --git a/core/drivers/qcom/tlmm/sub.mk b/core/drivers/qcom/tlmm/sub.mk index 4ff2e9512..956e0a06e 100644 --- a/core/drivers/qcom/tlmm/sub.mk +++ b/core/drivers/qcom/tlmm/sub.mk @@ -3,3 +3,4 @@ srcs-$(CFG_QCOM_TLMM) += gpio.c srcs-$(CFG_QCOM_TLMM) += pinctrl.c +srcs-$(CFG_QCOM_TLMM) += $(PLATFORM_FLAVOR)/tlmm_soc_data.c