14b5e93edSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 24b5e93edSEtienne Carriere /* 3bd03c8c3SGatien Chevallier * Copyright (c) 2017-2024, STMicroelectronics 44b5e93edSEtienne Carriere * 54b5e93edSEtienne Carriere * STM32 GPIO driver is used as pin controller for stm32mp SoCs. 64b5e93edSEtienne Carriere */ 74b5e93edSEtienne Carriere 84b5e93edSEtienne Carriere #include <assert.h> 969715ce9SEtienne Carriere #include <compiler.h> 1097391ffbSEtienne Carriere #include <drivers/clk.h> 1197391ffbSEtienne Carriere #include <drivers/clk_dt.h> 12a650c9cbSEtienne Carriere #include <drivers/firewall.h> 13420a32c5SEtienne Carriere #include <drivers/gpio.h> 14b38386fbSEtienne Carriere #include <drivers/pinctrl.h> 154b5e93edSEtienne Carriere #include <drivers/stm32_gpio.h> 16bd03c8c3SGatien Chevallier #include <drivers/stm32_rif.h> 17a650c9cbSEtienne Carriere #include <dt-bindings/gpio/stm32mp_gpio.h> 184b5e93edSEtienne Carriere #include <io.h> 194b5e93edSEtienne Carriere #include <kernel/dt.h> 2065401337SJens Wiklander #include <kernel/boot.h> 214b5e93edSEtienne Carriere #include <kernel/panic.h> 22bfc43b68SGatien Chevallier #include <kernel/pm.h> 234b5e93edSEtienne Carriere #include <kernel/spinlock.h> 24a2fc83d1SJerome Forissier #include <libfdt.h> 254b5e93edSEtienne Carriere #include <mm/core_memprot.h> 264b5e93edSEtienne Carriere #include <stdbool.h> 2769715ce9SEtienne Carriere #include <stdint.h> 284b5e93edSEtienne Carriere #include <stm32_util.h> 299818a481SEtienne Carriere #include <sys/queue.h> 304b5e93edSEtienne Carriere #include <trace.h> 314b5e93edSEtienne Carriere #include <util.h> 324b5e93edSEtienne Carriere 331001585eSEtienne Carriere #ifndef CFG_DRIVERS_GPIO 341001585eSEtienne Carriere #error stm32_gpio driver expects CFG_DRIVERS_GPIO 351001585eSEtienne Carriere #endif 361001585eSEtienne Carriere 374b5e93edSEtienne Carriere #define GPIO_PIN_MAX 15 384b5e93edSEtienne Carriere 395eed568cSGatien Chevallier #define GPIO_MODER_OFFSET U(0x00) 405eed568cSGatien Chevallier #define GPIO_OTYPER_OFFSET U(0x04) 415eed568cSGatien Chevallier #define GPIO_OSPEEDR_OFFSET U(0x08) 425eed568cSGatien Chevallier #define GPIO_PUPDR_OFFSET U(0x0c) 435eed568cSGatien Chevallier #define GPIO_IDR_OFFSET U(0x10) 445eed568cSGatien Chevallier #define GPIO_ODR_OFFSET U(0x14) 455eed568cSGatien Chevallier #define GPIO_BSRR_OFFSET U(0x18) 465eed568cSGatien Chevallier #define GPIO_AFRL_OFFSET U(0x20) 475eed568cSGatien Chevallier #define GPIO_AFRH_OFFSET U(0x24) 485eed568cSGatien Chevallier #define GPIO_SECR_OFFSET U(0x30) 49bd03c8c3SGatien Chevallier #define GPIO_PRIVCFGR_OFFSET U(0x34) 50bd03c8c3SGatien Chevallier #define GPIO_RCFGLOCKR_OFFSET U(0x38) 51bd03c8c3SGatien Chevallier #define GPIO_CIDCFGR(x) (U(0x50) + U(0x8) * (x)) 52bd03c8c3SGatien Chevallier #define GPIO_SEMCR(x) (U(0x54) + U(0x8) * (x)) 534b5e93edSEtienne Carriere 545eed568cSGatien Chevallier #define GPIO_ALT_LOWER_LIMIT U(0x8) 554b5e93edSEtienne Carriere 56bd03c8c3SGatien Chevallier /* 57bd03c8c3SGatien Chevallier * CIDCFGR register bitfields 58bd03c8c3SGatien Chevallier */ 59bd03c8c3SGatien Chevallier #define GPIO_CIDCFGR_SEMWL_MASK GENMASK_32(23, 16) 60bd03c8c3SGatien Chevallier #define GPIO_CIDCFGR_SCID_MASK GENMASK_32(6, 4) 61bd03c8c3SGatien Chevallier #define GPIO_CIDCFGR_CONF_MASK (_CIDCFGR_CFEN | _CIDCFGR_SEMEN | \ 62bd03c8c3SGatien Chevallier GPIO_CIDCFGR_SCID_MASK | \ 63bd03c8c3SGatien Chevallier GPIO_CIDCFGR_SEMWL_MASK) 64bd03c8c3SGatien Chevallier 65bd03c8c3SGatien Chevallier /* 66bd03c8c3SGatien Chevallier * PRIVCFGR register bitfields 67bd03c8c3SGatien Chevallier */ 68bd03c8c3SGatien Chevallier #define GPIO_PRIVCFGR_MASK GENMASK_32(15, 0) 69bd03c8c3SGatien Chevallier 70bd03c8c3SGatien Chevallier /* 71bd03c8c3SGatien Chevallier * SECCFGR register bitfields 72bd03c8c3SGatien Chevallier */ 73bd03c8c3SGatien Chevallier #define GPIO_SECCFGR_MASK GENMASK_32(15, 0) 74bd03c8c3SGatien Chevallier 75bd03c8c3SGatien Chevallier /* 76bd03c8c3SGatien Chevallier * RCFGLOCKR register bitfields 77bd03c8c3SGatien Chevallier */ 78bd03c8c3SGatien Chevallier #define GPIO_RCFGLOCKR_MASK GENMASK_32(15, 0) 79bd03c8c3SGatien Chevallier 80bd03c8c3SGatien Chevallier /* 81bd03c8c3SGatien Chevallier * SEMCR register bitfields 82bd03c8c3SGatien Chevallier */ 83bd03c8c3SGatien Chevallier #define GPIO_SEMCR_SCID_M GENMASK_32(6, 4) 84bd03c8c3SGatien Chevallier 854b5e93edSEtienne Carriere #define GPIO_MODE_MASK GENMASK_32(1, 0) 864b5e93edSEtienne Carriere #define GPIO_OSPEED_MASK GENMASK_32(1, 0) 874b5e93edSEtienne Carriere #define GPIO_PUPD_PULL_MASK GENMASK_32(1, 0) 88729093d5SLionel Debieve #define GPIO_ALTERNATE_MASK GENMASK_32(3, 0) 894b5e93edSEtienne Carriere 905eed568cSGatien Chevallier #define DT_GPIO_BANK_SHIFT U(12) 914b5e93edSEtienne Carriere #define DT_GPIO_BANK_MASK GENMASK_32(16, 12) 925eed568cSGatien Chevallier #define DT_GPIO_PIN_SHIFT U(8) 934b5e93edSEtienne Carriere #define DT_GPIO_PIN_MASK GENMASK_32(11, 8) 944b5e93edSEtienne Carriere #define DT_GPIO_MODE_MASK GENMASK_32(7, 0) 954b5e93edSEtienne Carriere 969818a481SEtienne Carriere #define DT_GPIO_BANK_NAME0 "GPIOA" 979818a481SEtienne Carriere 9869715ce9SEtienne Carriere #define GPIO_MODE_INPUT U(0x0) 9969715ce9SEtienne Carriere #define GPIO_MODE_OUTPUT U(0x1) 10069715ce9SEtienne Carriere #define GPIO_MODE_ALTERNATE U(0x2) 10169715ce9SEtienne Carriere #define GPIO_MODE_ANALOG U(0x3) 10269715ce9SEtienne Carriere 10369715ce9SEtienne Carriere #define GPIO_OTYPE_PUSH_PULL U(0x0) 10469715ce9SEtienne Carriere #define GPIO_OTYPE_OPEN_DRAIN U(0x1) 10569715ce9SEtienne Carriere 10669715ce9SEtienne Carriere #define GPIO_OSPEED_LOW U(0x0) 10769715ce9SEtienne Carriere #define GPIO_OSPEED_MEDIUM U(0x1) 10869715ce9SEtienne Carriere #define GPIO_OSPEED_HIGH U(0x2) 10969715ce9SEtienne Carriere #define GPIO_OSPEED_VERY_HIGH U(0x3) 11069715ce9SEtienne Carriere 11169715ce9SEtienne Carriere #define GPIO_PUPD_NO_PULL U(0x0) 11269715ce9SEtienne Carriere #define GPIO_PUPD_PULL_UP U(0x1) 11369715ce9SEtienne Carriere #define GPIO_PUPD_PULL_DOWN U(0x2) 11469715ce9SEtienne Carriere 11569715ce9SEtienne Carriere #define GPIO_OD_LEVEL_LOW U(0x0) 11669715ce9SEtienne Carriere #define GPIO_OD_LEVEL_HIGH U(0x1) 11769715ce9SEtienne Carriere 118bd03c8c3SGatien Chevallier #define GPIO_MAX_CID_SUPPORTED U(3) 119bd03c8c3SGatien Chevallier 12069715ce9SEtienne Carriere /* 12169715ce9SEtienne Carriere * GPIO configuration description structured as single 16bit word 12269715ce9SEtienne Carriere * for efficient save/restore when GPIO pin suspends or resumes. 12369715ce9SEtienne Carriere * 12469715ce9SEtienne Carriere * @mode: One of GPIO_MODE_* 12569715ce9SEtienne Carriere * @otype: One of GPIO_OTYPE_* 12669715ce9SEtienne Carriere * @ospeed: One of GPIO_OSPEED_* 12769715ce9SEtienne Carriere * @pupd: One of GPIO_PUPD_* 12869715ce9SEtienne Carriere * @od: One of GPIO_OD_* 12969715ce9SEtienne Carriere * @af: Alternate function numerical ID between 0 and 15 13069715ce9SEtienne Carriere */ 13169715ce9SEtienne Carriere struct gpio_cfg { 13269715ce9SEtienne Carriere uint16_t mode: 2; 13369715ce9SEtienne Carriere uint16_t otype: 1; 13469715ce9SEtienne Carriere uint16_t ospeed: 2; 13569715ce9SEtienne Carriere uint16_t pupd: 2; 13669715ce9SEtienne Carriere uint16_t od: 1; 13769715ce9SEtienne Carriere uint16_t af: 4; 13869715ce9SEtienne Carriere }; 13969715ce9SEtienne Carriere 14069715ce9SEtienne Carriere /* 14169715ce9SEtienne Carriere * Description of a pin and its muxing 14269715ce9SEtienne Carriere * 14369715ce9SEtienne Carriere * @bank: GPIO bank identifier as assigned by the platform 14469715ce9SEtienne Carriere * @pin: Pin number in the GPIO bank 14569715ce9SEtienne Carriere * @cfg: Pin configuration 14669715ce9SEtienne Carriere */ 14769715ce9SEtienne Carriere struct stm32_pinctrl { 14869715ce9SEtienne Carriere uint8_t bank; 14969715ce9SEtienne Carriere uint8_t pin; 15069715ce9SEtienne Carriere struct gpio_cfg cfg; 15169715ce9SEtienne Carriere }; 15269715ce9SEtienne Carriere 153b38386fbSEtienne Carriere /* 154b38386fbSEtienne Carriere * struct stm32_pinctrl_array - Array of pins in a pin control state 155b38386fbSEtienne Carriere * @count: Number of cells in @pinctrl 156b38386fbSEtienne Carriere * @pinctrl: Pin control configuration 157b38386fbSEtienne Carriere */ 158b38386fbSEtienne Carriere struct stm32_pinctrl_array { 159b38386fbSEtienne Carriere size_t count; 160b38386fbSEtienne Carriere struct stm32_pinctrl pinctrl[]; 161b38386fbSEtienne Carriere }; 162b38386fbSEtienne Carriere 1639818a481SEtienne Carriere /** 1649818a481SEtienne Carriere * struct stm32_gpio_bank - GPIO bank instance 1659818a481SEtienne Carriere * 1669818a481SEtienne Carriere * @base: base address of the GPIO controller registers. 1679818a481SEtienne Carriere * @clock: clock identifier. 168420a32c5SEtienne Carriere * @gpio_chip: GPIO chip reference for that GPIO bank 1699818a481SEtienne Carriere * @ngpios: number of GPIOs. 1709818a481SEtienne Carriere * @bank_id: Id of the bank. 1719818a481SEtienne Carriere * @lock: lock protecting the GPIO bank access. 172bd03c8c3SGatien Chevallier * @rif_cfg: RIF configuration data 173bd03c8c3SGatien Chevallier * @seccfgr: non-RIF bank secure configuration data 174bd03c8c3SGatien Chevallier * @sec_support: True if bank supports pin security protection, else false 175bd03c8c3SGatien Chevallier * @ready: True if configuration is applied, else false 176bd03c8c3SGatien Chevallier * @is_tdcid: True if OP-TEE runs as Trusted Domain CID 1779818a481SEtienne Carriere * @link: Link in bank list 1789818a481SEtienne Carriere */ 1799818a481SEtienne Carriere struct stm32_gpio_bank { 1809818a481SEtienne Carriere vaddr_t base; 1819818a481SEtienne Carriere struct clk *clock; 182420a32c5SEtienne Carriere struct gpio_chip gpio_chip; 1839818a481SEtienne Carriere unsigned int ngpios; 1849818a481SEtienne Carriere unsigned int bank_id; 1859818a481SEtienne Carriere unsigned int lock; 186bd03c8c3SGatien Chevallier struct rif_conf_data *rif_cfg; 187bd03c8c3SGatien Chevallier uint32_t seccfgr; 188b4893304SGatien Chevallier bool sec_support; 189bd03c8c3SGatien Chevallier bool ready; 190bd03c8c3SGatien Chevallier bool is_tdcid; 1919818a481SEtienne Carriere STAILQ_ENTRY(stm32_gpio_bank) link; 1929818a481SEtienne Carriere }; 1939818a481SEtienne Carriere 194bfc43b68SGatien Chevallier /* 195bfc43b68SGatien Chevallier * struct stm32_gpio_pm_state - Consumed GPIO for PM purpose 196bfc43b68SGatien Chevallier * @gpio_pinctrl: Reference and configuration state for a consumed GPIO 197bfc43b68SGatien Chevallier * @level: GPIO level 198bfc43b68SGatien Chevallier * @link: Link in consumed GPIO list 199bfc43b68SGatien Chevallier */ 200bfc43b68SGatien Chevallier struct stm32_gpio_pm_state { 201bfc43b68SGatien Chevallier struct stm32_pinctrl gpio_pinctrl; 202bfc43b68SGatien Chevallier uint8_t level; 203bfc43b68SGatien Chevallier SLIST_ENTRY(stm32_gpio_pm_state) link; 204bfc43b68SGatien Chevallier }; 205bfc43b68SGatien Chevallier 206b4893304SGatien Chevallier /** 207e569f6adSEtienne Carriere * Compatibility information of supported banks 208b4893304SGatien Chevallier * 209b4893304SGatien Chevallier * @gpioz: True if bank is a GPIOZ bank 210b4893304SGatien Chevallier * @secure_control: Identify GPIO security bank capability. 211bd03c8c3SGatien Chevallier * @secure_extended: Identify RIF presence. 212e569f6adSEtienne Carriere */ 213e569f6adSEtienne Carriere struct bank_compat { 214e569f6adSEtienne Carriere bool gpioz; 215b4893304SGatien Chevallier bool secure_control; 216bd03c8c3SGatien Chevallier bool secure_extended; 217e569f6adSEtienne Carriere }; 218e569f6adSEtienne Carriere 2194b5e93edSEtienne Carriere static unsigned int gpio_lock; 2204b5e93edSEtienne Carriere 2219818a481SEtienne Carriere static STAILQ_HEAD(, stm32_gpio_bank) bank_list = 2229818a481SEtienne Carriere STAILQ_HEAD_INITIALIZER(bank_list); 2239818a481SEtienne Carriere 224bfc43b68SGatien Chevallier static SLIST_HEAD(, stm32_gpio_pm_state) consumed_gpios_head; 225bfc43b68SGatien Chevallier 226420a32c5SEtienne Carriere static bool is_stm32_gpio_chip(struct gpio_chip *chip); 227420a32c5SEtienne Carriere 228420a32c5SEtienne Carriere static struct stm32_gpio_bank *gpio_chip_to_bank(struct gpio_chip *chip) 229420a32c5SEtienne Carriere { 230420a32c5SEtienne Carriere return container_of(chip, struct stm32_gpio_bank, gpio_chip); 231420a32c5SEtienne Carriere } 232420a32c5SEtienne Carriere 233420a32c5SEtienne Carriere static enum gpio_level stm32_gpio_get_level(struct gpio_chip *chip, 234420a32c5SEtienne Carriere unsigned int gpio_pin) 235420a32c5SEtienne Carriere { 236420a32c5SEtienne Carriere struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip); 237420a32c5SEtienne Carriere enum gpio_level level = GPIO_LEVEL_HIGH; 238420a32c5SEtienne Carriere unsigned int reg_offset = 0; 239420a32c5SEtienne Carriere unsigned int mode = 0; 240420a32c5SEtienne Carriere 241420a32c5SEtienne Carriere assert(gpio_pin < bank->ngpios); 2422fd102ebSEtienne Carriere 2432fd102ebSEtienne Carriere if (clk_enable(bank->clock)) 2442fd102ebSEtienne Carriere panic(); 245420a32c5SEtienne Carriere 246420a32c5SEtienne Carriere mode = (io_read32(bank->base + GPIO_MODER_OFFSET) >> (gpio_pin << 1)) & 247420a32c5SEtienne Carriere GPIO_MODE_MASK; 248420a32c5SEtienne Carriere 249420a32c5SEtienne Carriere switch (mode) { 250420a32c5SEtienne Carriere case GPIO_MODE_INPUT: 251420a32c5SEtienne Carriere reg_offset = GPIO_IDR_OFFSET; 252420a32c5SEtienne Carriere break; 253420a32c5SEtienne Carriere case GPIO_MODE_OUTPUT: 254420a32c5SEtienne Carriere reg_offset = GPIO_ODR_OFFSET; 255420a32c5SEtienne Carriere break; 256420a32c5SEtienne Carriere default: 257420a32c5SEtienne Carriere panic(); 258420a32c5SEtienne Carriere } 259420a32c5SEtienne Carriere 260420a32c5SEtienne Carriere if (io_read32(bank->base + reg_offset) & BIT(gpio_pin)) 261420a32c5SEtienne Carriere level = GPIO_LEVEL_HIGH; 262420a32c5SEtienne Carriere else 263420a32c5SEtienne Carriere level = GPIO_LEVEL_LOW; 264420a32c5SEtienne Carriere 265420a32c5SEtienne Carriere clk_disable(bank->clock); 266420a32c5SEtienne Carriere 267420a32c5SEtienne Carriere return level; 268420a32c5SEtienne Carriere } 269420a32c5SEtienne Carriere 270420a32c5SEtienne Carriere static void stm32_gpio_set_level(struct gpio_chip *chip, unsigned int gpio_pin, 271420a32c5SEtienne Carriere enum gpio_level level) 272420a32c5SEtienne Carriere { 273420a32c5SEtienne Carriere struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip); 274420a32c5SEtienne Carriere 275420a32c5SEtienne Carriere assert(gpio_pin < bank->ngpios); 2762fd102ebSEtienne Carriere 2772fd102ebSEtienne Carriere if (clk_enable(bank->clock)) 2782fd102ebSEtienne Carriere panic(); 279420a32c5SEtienne Carriere 280420a32c5SEtienne Carriere assert(((io_read32(bank->base + GPIO_MODER_OFFSET) >> 281420a32c5SEtienne Carriere (gpio_pin << 1)) & GPIO_MODE_MASK) == GPIO_MODE_OUTPUT); 282420a32c5SEtienne Carriere 283420a32c5SEtienne Carriere if (level == GPIO_LEVEL_HIGH) 284420a32c5SEtienne Carriere io_write32(bank->base + GPIO_BSRR_OFFSET, BIT(gpio_pin)); 285420a32c5SEtienne Carriere else 286420a32c5SEtienne Carriere io_write32(bank->base + GPIO_BSRR_OFFSET, BIT(gpio_pin + 16)); 287420a32c5SEtienne Carriere 288420a32c5SEtienne Carriere clk_disable(bank->clock); 289420a32c5SEtienne Carriere } 290420a32c5SEtienne Carriere 291420a32c5SEtienne Carriere static enum gpio_dir stm32_gpio_get_direction(struct gpio_chip *chip, 292420a32c5SEtienne Carriere unsigned int gpio_pin) 293420a32c5SEtienne Carriere { 294420a32c5SEtienne Carriere struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip); 295420a32c5SEtienne Carriere uint32_t mode = 0; 296420a32c5SEtienne Carriere 297420a32c5SEtienne Carriere assert(gpio_pin < bank->ngpios); 2982fd102ebSEtienne Carriere 2992fd102ebSEtienne Carriere if (clk_enable(bank->clock)) 3002fd102ebSEtienne Carriere panic(); 301420a32c5SEtienne Carriere 302420a32c5SEtienne Carriere mode = (io_read32(bank->base + GPIO_MODER_OFFSET) >> (gpio_pin << 1)) & 303420a32c5SEtienne Carriere GPIO_MODE_MASK; 304420a32c5SEtienne Carriere 305420a32c5SEtienne Carriere clk_disable(bank->clock); 306420a32c5SEtienne Carriere 307420a32c5SEtienne Carriere switch (mode) { 308420a32c5SEtienne Carriere case GPIO_MODE_INPUT: 309420a32c5SEtienne Carriere return GPIO_DIR_IN; 310420a32c5SEtienne Carriere case GPIO_MODE_OUTPUT: 311420a32c5SEtienne Carriere return GPIO_DIR_OUT; 312420a32c5SEtienne Carriere default: 313420a32c5SEtienne Carriere panic(); 314420a32c5SEtienne Carriere } 315420a32c5SEtienne Carriere } 316420a32c5SEtienne Carriere 317420a32c5SEtienne Carriere static void stm32_gpio_set_direction(struct gpio_chip *chip, 318420a32c5SEtienne Carriere unsigned int gpio_pin, 319420a32c5SEtienne Carriere enum gpio_dir direction) 320420a32c5SEtienne Carriere { 321420a32c5SEtienne Carriere struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip); 322420a32c5SEtienne Carriere uint32_t exceptions = 0; 323420a32c5SEtienne Carriere uint32_t mode = 0; 324420a32c5SEtienne Carriere 325420a32c5SEtienne Carriere assert(gpio_pin < bank->ngpios); 326420a32c5SEtienne Carriere 327420a32c5SEtienne Carriere if (direction == GPIO_DIR_IN) 328420a32c5SEtienne Carriere mode = GPIO_MODE_INPUT; 329420a32c5SEtienne Carriere else 330420a32c5SEtienne Carriere mode = GPIO_MODE_OUTPUT; 331420a32c5SEtienne Carriere 3322fd102ebSEtienne Carriere if (clk_enable(bank->clock)) 3332fd102ebSEtienne Carriere panic(); 334420a32c5SEtienne Carriere exceptions = cpu_spin_lock_xsave(&gpio_lock); 335420a32c5SEtienne Carriere io_clrsetbits32(bank->base + GPIO_MODER_OFFSET, 336420a32c5SEtienne Carriere SHIFT_U32(GPIO_MODE_MASK, gpio_pin << 1), 337420a32c5SEtienne Carriere SHIFT_U32(mode, gpio_pin << 1)); 338420a32c5SEtienne Carriere cpu_spin_unlock_xrestore(&gpio_lock, exceptions); 339420a32c5SEtienne Carriere clk_disable(bank->clock); 340420a32c5SEtienne Carriere } 341420a32c5SEtienne Carriere 342bfc43b68SGatien Chevallier /* Forward reference to the PM callback handler for consumed GPIOs */ 343bfc43b68SGatien Chevallier static TEE_Result consumed_gpios_pm(enum pm_op op, unsigned int pm_hint, 344bfc43b68SGatien Chevallier const struct pm_callback_handle *pm_hdl); 345bfc43b68SGatien Chevallier 346430c415aSEtienne Carriere /* Forward reference to RIF semaphore release helper function */ 347430c415aSEtienne Carriere static void release_rif_semaphore_if_acquired(struct stm32_gpio_bank *bank, 348430c415aSEtienne Carriere unsigned int pin); 349430c415aSEtienne Carriere 350bfc43b68SGatien Chevallier static void stm32_gpio_put_gpio(struct gpio_chip *chip, struct gpio *gpio) 351420a32c5SEtienne Carriere { 352bfc43b68SGatien Chevallier struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip); 353bfc43b68SGatien Chevallier struct stm32_gpio_pm_state *tstate = NULL; 354bfc43b68SGatien Chevallier struct stm32_gpio_pm_state *state = NULL; 355bfc43b68SGatien Chevallier uint32_t exceptions = 0; 356bfc43b68SGatien Chevallier 357420a32c5SEtienne Carriere assert(is_stm32_gpio_chip(chip)); 358bfc43b68SGatien Chevallier 359bfc43b68SGatien Chevallier exceptions = cpu_spin_lock_xsave(&gpio_lock); 360bfc43b68SGatien Chevallier 361bfc43b68SGatien Chevallier SLIST_FOREACH_SAFE(state, &consumed_gpios_head, link, tstate) { 362bfc43b68SGatien Chevallier if (state->gpio_pinctrl.bank == bank->bank_id && 363bfc43b68SGatien Chevallier state->gpio_pinctrl.pin == gpio->pin) { 364bfc43b68SGatien Chevallier SLIST_REMOVE(&consumed_gpios_head, state, 365bfc43b68SGatien Chevallier stm32_gpio_pm_state, link); 366bfc43b68SGatien Chevallier unregister_pm_driver_cb(consumed_gpios_pm, state); 367430c415aSEtienne Carriere release_rif_semaphore_if_acquired(bank, gpio->pin); 368bfc43b68SGatien Chevallier free(state); 369420a32c5SEtienne Carriere free(gpio); 370bfc43b68SGatien Chevallier break; 371bfc43b68SGatien Chevallier } 372bfc43b68SGatien Chevallier } 373bfc43b68SGatien Chevallier assert(state); 374bfc43b68SGatien Chevallier 375bfc43b68SGatien Chevallier cpu_spin_unlock_xrestore(&gpio_lock, exceptions); 376420a32c5SEtienne Carriere } 377420a32c5SEtienne Carriere 378420a32c5SEtienne Carriere static const struct gpio_ops stm32_gpio_ops = { 379420a32c5SEtienne Carriere .get_direction = stm32_gpio_get_direction, 380420a32c5SEtienne Carriere .set_direction = stm32_gpio_set_direction, 381420a32c5SEtienne Carriere .get_value = stm32_gpio_get_level, 382420a32c5SEtienne Carriere .set_value = stm32_gpio_set_level, 383420a32c5SEtienne Carriere .put = stm32_gpio_put_gpio, 384420a32c5SEtienne Carriere }; 385420a32c5SEtienne Carriere 386420a32c5SEtienne Carriere static bool __maybe_unused is_stm32_gpio_chip(struct gpio_chip *chip) 387420a32c5SEtienne Carriere { 388420a32c5SEtienne Carriere return chip && chip->ops == &stm32_gpio_ops; 389420a32c5SEtienne Carriere } 390420a32c5SEtienne Carriere 391077d486eSEtienne Carriere static struct stm32_gpio_bank *stm32_gpio_get_bank(unsigned int bank_id) 3924b5e93edSEtienne Carriere { 393077d486eSEtienne Carriere struct stm32_gpio_bank *bank = NULL; 3944b5e93edSEtienne Carriere 395077d486eSEtienne Carriere STAILQ_FOREACH(bank, &bank_list, link) 396077d486eSEtienne Carriere if (bank_id == bank->bank_id) 397077d486eSEtienne Carriere return bank; 398077d486eSEtienne Carriere 399077d486eSEtienne Carriere panic(); 400077d486eSEtienne Carriere } 401077d486eSEtienne Carriere 402430c415aSEtienne Carriere #if defined(CFG_STM32_RIF) 403*4675225eSEtienne Carriere static bool pin_is_accessible(struct stm32_gpio_bank *bank, unsigned int pin) 404*4675225eSEtienne Carriere { 405*4675225eSEtienne Carriere bool accessible = false; 406*4675225eSEtienne Carriere uint32_t cidcfgr = 0; 407*4675225eSEtienne Carriere 408*4675225eSEtienne Carriere if (!bank->rif_cfg) 409*4675225eSEtienne Carriere return true; 410*4675225eSEtienne Carriere 411*4675225eSEtienne Carriere if (clk_enable(bank->clock)) 412*4675225eSEtienne Carriere panic(); 413*4675225eSEtienne Carriere 414*4675225eSEtienne Carriere cidcfgr = io_read32(bank->base + GPIO_CIDCFGR(pin)); 415*4675225eSEtienne Carriere 416*4675225eSEtienne Carriere if (!(cidcfgr & _CIDCFGR_CFEN)) { 417*4675225eSEtienne Carriere /* Resource can be accessed when CID filtering is disabled */ 418*4675225eSEtienne Carriere accessible = true; 419*4675225eSEtienne Carriere } else if (stm32_rif_scid_ok(cidcfgr, GPIO_CIDCFGR_SCID_MASK, 420*4675225eSEtienne Carriere RIF_CID1)) { 421*4675225eSEtienne Carriere /* Resource can be accessed if CID1 is statically allowed */ 422*4675225eSEtienne Carriere accessible = true; 423*4675225eSEtienne Carriere } else if (stm32_rif_semaphore_enabled_and_ok(cidcfgr, RIF_CID1)) { 424*4675225eSEtienne Carriere /* CID1 is allowed to request the semaphore */ 425*4675225eSEtienne Carriere accessible = true; 426*4675225eSEtienne Carriere } 427*4675225eSEtienne Carriere 428*4675225eSEtienne Carriere clk_disable(bank->clock); 429*4675225eSEtienne Carriere 430*4675225eSEtienne Carriere return accessible; 431*4675225eSEtienne Carriere } 432*4675225eSEtienne Carriere 433430c415aSEtienne Carriere static TEE_Result acquire_rif_semaphore_if_needed(struct stm32_gpio_bank *bank, 434430c415aSEtienne Carriere unsigned int pin) 435430c415aSEtienne Carriere { 436430c415aSEtienne Carriere TEE_Result res = TEE_SUCCESS; 437430c415aSEtienne Carriere uint32_t cidcfgr = 0; 438430c415aSEtienne Carriere 439430c415aSEtienne Carriere if (!bank->rif_cfg) 440430c415aSEtienne Carriere return TEE_SUCCESS; 441430c415aSEtienne Carriere 442430c415aSEtienne Carriere res = clk_enable(bank->clock); 443430c415aSEtienne Carriere if (res) 444430c415aSEtienne Carriere return res; 445430c415aSEtienne Carriere 446430c415aSEtienne Carriere cidcfgr = io_read32(bank->base + GPIO_CIDCFGR(pin)); 447430c415aSEtienne Carriere 448430c415aSEtienne Carriere if (stm32_rif_semaphore_enabled_and_ok(cidcfgr, RIF_CID1)) 449430c415aSEtienne Carriere res = stm32_rif_acquire_semaphore(bank->base + GPIO_SEMCR(pin), 450430c415aSEtienne Carriere GPIO_MAX_CID_SUPPORTED); 451430c415aSEtienne Carriere 452430c415aSEtienne Carriere clk_disable(bank->clock); 453430c415aSEtienne Carriere 454430c415aSEtienne Carriere return res; 455430c415aSEtienne Carriere } 456430c415aSEtienne Carriere 457430c415aSEtienne Carriere static uint32_t semaphore_current_cid(struct stm32_gpio_bank *bank, 458430c415aSEtienne Carriere unsigned int pin) 459430c415aSEtienne Carriere { 460430c415aSEtienne Carriere return (io_read32(bank->base + GPIO_SEMCR(pin)) >> 461430c415aSEtienne Carriere _CIDCFGR_SCID_SHIFT) & 462430c415aSEtienne Carriere GENMASK_32(GPIO_MAX_CID_SUPPORTED - 1, 0); 463430c415aSEtienne Carriere } 464430c415aSEtienne Carriere 465430c415aSEtienne Carriere static void release_rif_semaphore_if_acquired(struct stm32_gpio_bank *bank, 466430c415aSEtienne Carriere unsigned int pin) 467430c415aSEtienne Carriere { 468430c415aSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 469430c415aSEtienne Carriere uint32_t cidcfgr = 0; 470430c415aSEtienne Carriere 471430c415aSEtienne Carriere if (!bank->rif_cfg) 472430c415aSEtienne Carriere return; 473430c415aSEtienne Carriere 474430c415aSEtienne Carriere res = clk_enable(bank->clock); 475430c415aSEtienne Carriere if (res) 476430c415aSEtienne Carriere panic(); 477430c415aSEtienne Carriere 478430c415aSEtienne Carriere cidcfgr = io_read32(bank->base + GPIO_CIDCFGR(pin)); 479430c415aSEtienne Carriere 480430c415aSEtienne Carriere if (stm32_rif_semaphore_enabled_and_ok(cidcfgr, RIF_CID1) && 481430c415aSEtienne Carriere semaphore_current_cid(bank, pin) == RIF_CID1) { 482430c415aSEtienne Carriere res = stm32_rif_release_semaphore(bank->base + GPIO_SEMCR(pin), 483430c415aSEtienne Carriere GPIO_MAX_CID_SUPPORTED); 484430c415aSEtienne Carriere if (res) { 485430c415aSEtienne Carriere EMSG("Failed to release GPIO %c%u semaphore", 486430c415aSEtienne Carriere bank->bank_id + 'A', pin); 487430c415aSEtienne Carriere panic(); 488430c415aSEtienne Carriere } 489430c415aSEtienne Carriere } 490430c415aSEtienne Carriere 491430c415aSEtienne Carriere clk_disable(bank->clock); 492430c415aSEtienne Carriere } 493430c415aSEtienne Carriere #else 494*4675225eSEtienne Carriere static bool pin_is_accessible(struct stm32_gpio_bank *bank __unused, 495*4675225eSEtienne Carriere unsigned int pin __unused) 496*4675225eSEtienne Carriere { 497*4675225eSEtienne Carriere return true; 498*4675225eSEtienne Carriere } 499*4675225eSEtienne Carriere 500430c415aSEtienne Carriere static TEE_Result 501430c415aSEtienne Carriere acquire_rif_semaphore_if_needed(struct stm32_gpio_bank *bank __unused, 502430c415aSEtienne Carriere unsigned int pin __unused) 503430c415aSEtienne Carriere { 504430c415aSEtienne Carriere return TEE_SUCCESS; 505430c415aSEtienne Carriere } 506430c415aSEtienne Carriere 507430c415aSEtienne Carriere static void 508430c415aSEtienne Carriere release_rif_semaphore_if_acquired(struct stm32_gpio_bank *bank __unused, 509430c415aSEtienne Carriere unsigned int pin __unused) 510430c415aSEtienne Carriere { 511430c415aSEtienne Carriere } 512430c415aSEtienne Carriere #endif /*CFG_STM32_RIF*/ 513430c415aSEtienne Carriere 514*4675225eSEtienne Carriere static bool pin_is_secure(struct stm32_gpio_bank *bank, unsigned int pin) 515*4675225eSEtienne Carriere { 516*4675225eSEtienne Carriere bool secure = false; 517*4675225eSEtienne Carriere 518*4675225eSEtienne Carriere if (bank->rif_cfg || bank->sec_support) { 519*4675225eSEtienne Carriere if (clk_enable(bank->clock)) 520*4675225eSEtienne Carriere panic(); 521*4675225eSEtienne Carriere 522*4675225eSEtienne Carriere secure = io_read32(bank->base + GPIO_SECR_OFFSET) & BIT(pin); 523*4675225eSEtienne Carriere 524*4675225eSEtienne Carriere clk_disable(bank->clock); 525*4675225eSEtienne Carriere } 526*4675225eSEtienne Carriere 527*4675225eSEtienne Carriere return secure; 528*4675225eSEtienne Carriere } 529*4675225eSEtienne Carriere 530077d486eSEtienne Carriere /* Save to output @cfg the current GPIO (@bank_id/@pin) configuration */ 531bfc43b68SGatien Chevallier static void get_gpio_cfg(uint32_t bank_id, uint32_t pin, struct gpio_cfg *cfg) 532077d486eSEtienne Carriere { 533077d486eSEtienne Carriere struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id); 534077d486eSEtienne Carriere 535077d486eSEtienne Carriere if (clk_enable(bank->clock)) 536077d486eSEtienne Carriere panic(); 5374b5e93edSEtienne Carriere 5384b5e93edSEtienne Carriere /* 5394b5e93edSEtienne Carriere * Save GPIO configuration bits spread over the few bank registers. 5404b5e93edSEtienne Carriere * 1bit fields are accessed at bit position being the pin index. 5414b5e93edSEtienne Carriere * 2bit fields are accessed at bit position being twice the pin index. 5424b5e93edSEtienne Carriere * 4bit fields are accessed at bit position being fourth the pin index 5434b5e93edSEtienne Carriere * but accessed from 2 32bit registers at incremental addresses. 5444b5e93edSEtienne Carriere */ 545077d486eSEtienne Carriere cfg->mode = (io_read32(bank->base + GPIO_MODER_OFFSET) >> (pin << 1)) & 5464b5e93edSEtienne Carriere GPIO_MODE_MASK; 5474b5e93edSEtienne Carriere 548077d486eSEtienne Carriere cfg->otype = (io_read32(bank->base + GPIO_OTYPER_OFFSET) >> pin) & 1; 5494b5e93edSEtienne Carriere 550077d486eSEtienne Carriere cfg->ospeed = (io_read32(bank->base + GPIO_OSPEEDR_OFFSET) >> 551077d486eSEtienne Carriere (pin << 1)) & GPIO_OSPEED_MASK; 5524b5e93edSEtienne Carriere 553077d486eSEtienne Carriere cfg->pupd = (io_read32(bank->base + GPIO_PUPDR_OFFSET) >> (pin << 1)) & 5544b5e93edSEtienne Carriere GPIO_PUPD_PULL_MASK; 5554b5e93edSEtienne Carriere 556077d486eSEtienne Carriere cfg->od = (io_read32(bank->base + GPIO_ODR_OFFSET) >> (pin << 1)) & 1; 5574b5e93edSEtienne Carriere 5584b5e93edSEtienne Carriere if (pin < GPIO_ALT_LOWER_LIMIT) 559077d486eSEtienne Carriere cfg->af = (io_read32(bank->base + GPIO_AFRL_OFFSET) >> 560077d486eSEtienne Carriere (pin << 2)) & GPIO_ALTERNATE_MASK; 5614b5e93edSEtienne Carriere else 562077d486eSEtienne Carriere cfg->af = (io_read32(bank->base + GPIO_AFRH_OFFSET) >> 5634b5e93edSEtienne Carriere ((pin - GPIO_ALT_LOWER_LIMIT) << 2)) & 5644b5e93edSEtienne Carriere GPIO_ALTERNATE_MASK; 5654b5e93edSEtienne Carriere 566077d486eSEtienne Carriere clk_disable(bank->clock); 5674b5e93edSEtienne Carriere } 5684b5e93edSEtienne Carriere 5694b5e93edSEtienne Carriere /* Apply GPIO (@bank/@pin) configuration described by @cfg */ 570077d486eSEtienne Carriere static void set_gpio_cfg(uint32_t bank_id, uint32_t pin, struct gpio_cfg *cfg) 5714b5e93edSEtienne Carriere { 572077d486eSEtienne Carriere struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id); 57398dfcedaSEtienne Carriere uint32_t exceptions = 0; 5744b5e93edSEtienne Carriere 575077d486eSEtienne Carriere if (clk_enable(bank->clock)) 576077d486eSEtienne Carriere panic(); 57798dfcedaSEtienne Carriere exceptions = cpu_spin_lock_xsave(&gpio_lock); 5784b5e93edSEtienne Carriere 5794b5e93edSEtienne Carriere /* Load GPIO MODE value, 2bit value shifted by twice the pin number */ 580077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_MODER_OFFSET, 581bed4582fSEtienne Carriere SHIFT_U32(GPIO_MODE_MASK, pin << 1), 582bed4582fSEtienne Carriere SHIFT_U32(cfg->mode, pin << 1)); 5834b5e93edSEtienne Carriere 5844b5e93edSEtienne Carriere /* Load GPIO Output TYPE value, 1bit shifted by pin number value */ 585077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_OTYPER_OFFSET, BIT(pin), 586bed4582fSEtienne Carriere SHIFT_U32(cfg->otype, pin)); 5874b5e93edSEtienne Carriere 5884b5e93edSEtienne Carriere /* Load GPIO Output Speed confguration, 2bit value */ 589077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_OSPEEDR_OFFSET, 590bed4582fSEtienne Carriere SHIFT_U32(GPIO_OSPEED_MASK, pin << 1), 591bed4582fSEtienne Carriere SHIFT_U32(cfg->ospeed, pin << 1)); 5924b5e93edSEtienne Carriere 5934b5e93edSEtienne Carriere /* Load GPIO pull configuration, 2bit value */ 594077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_PUPDR_OFFSET, BIT(pin), 595bed4582fSEtienne Carriere SHIFT_U32(cfg->pupd, pin << 1)); 5964b5e93edSEtienne Carriere 5974b5e93edSEtienne Carriere /* Load pin mux Alternate Function configuration, 4bit value */ 5984b5e93edSEtienne Carriere if (pin < GPIO_ALT_LOWER_LIMIT) { 599077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_AFRL_OFFSET, 600bed4582fSEtienne Carriere SHIFT_U32(GPIO_ALTERNATE_MASK, pin << 2), 601bed4582fSEtienne Carriere SHIFT_U32(cfg->af, pin << 2)); 6024b5e93edSEtienne Carriere } else { 6034b5e93edSEtienne Carriere size_t shift = (pin - GPIO_ALT_LOWER_LIMIT) << 2; 6044b5e93edSEtienne Carriere 605077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_AFRH_OFFSET, 606bed4582fSEtienne Carriere SHIFT_U32(GPIO_ALTERNATE_MASK, shift), 607bed4582fSEtienne Carriere SHIFT_U32(cfg->af, shift)); 6084b5e93edSEtienne Carriere } 6094b5e93edSEtienne Carriere 6104b5e93edSEtienne Carriere /* Load GPIO Output direction confuguration, 1bit */ 611077d486eSEtienne Carriere io_clrsetbits32(bank->base + GPIO_ODR_OFFSET, BIT(pin), cfg->od << pin); 6124b5e93edSEtienne Carriere 613c4cab2bbSEtienne Carriere cpu_spin_unlock_xrestore(&gpio_lock, exceptions); 61498dfcedaSEtienne Carriere clk_disable(bank->clock); 6154b5e93edSEtienne Carriere } 6164b5e93edSEtienne Carriere 6174b5e93edSEtienne Carriere /* Count pins described in the DT node and get related data if possible */ 618b38386fbSEtienne Carriere static int get_pinctrl_from_fdt(const void *fdt, int node, 6194b5e93edSEtienne Carriere struct stm32_pinctrl *pinctrl, size_t count) 6204b5e93edSEtienne Carriere { 621b38386fbSEtienne Carriere const fdt32_t *cuint = NULL; 622b38386fbSEtienne Carriere const fdt32_t *slewrate = NULL; 62310bcbd6cSEtienne Carriere int len = 0; 62410bcbd6cSEtienne Carriere uint32_t i = 0; 6254b5e93edSEtienne Carriere uint32_t speed = GPIO_OSPEED_LOW; 6264b5e93edSEtienne Carriere uint32_t pull = GPIO_PUPD_NO_PULL; 6274b5e93edSEtienne Carriere size_t found = 0; 6284b5e93edSEtienne Carriere 6294b5e93edSEtienne Carriere cuint = fdt_getprop(fdt, node, "pinmux", &len); 6304b5e93edSEtienne Carriere if (!cuint) 6314b5e93edSEtienne Carriere return -FDT_ERR_NOTFOUND; 6324b5e93edSEtienne Carriere 6334b5e93edSEtienne Carriere slewrate = fdt_getprop(fdt, node, "slew-rate", NULL); 6344b5e93edSEtienne Carriere if (slewrate) 6354b5e93edSEtienne Carriere speed = fdt32_to_cpu(*slewrate); 6364b5e93edSEtienne Carriere 6374b5e93edSEtienne Carriere if (fdt_getprop(fdt, node, "bias-pull-up", NULL)) 6384b5e93edSEtienne Carriere pull = GPIO_PUPD_PULL_UP; 6394b5e93edSEtienne Carriere if (fdt_getprop(fdt, node, "bias-pull-down", NULL)) 6404b5e93edSEtienne Carriere pull = GPIO_PUPD_PULL_DOWN; 6414b5e93edSEtienne Carriere 6424b5e93edSEtienne Carriere for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 64310bcbd6cSEtienne Carriere uint32_t pincfg = 0; 64410bcbd6cSEtienne Carriere uint32_t bank = 0; 64510bcbd6cSEtienne Carriere uint32_t pin = 0; 64610bcbd6cSEtienne Carriere uint32_t mode = 0; 6474b5e93edSEtienne Carriere uint32_t alternate = 0; 648322cf9e3SEtienne Carriere uint32_t odata = 0; 6494b5e93edSEtienne Carriere bool opendrain = false; 6504b5e93edSEtienne Carriere 6514b5e93edSEtienne Carriere pincfg = fdt32_to_cpu(*cuint); 6524b5e93edSEtienne Carriere cuint++; 6534b5e93edSEtienne Carriere 6544b5e93edSEtienne Carriere bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT; 6554b5e93edSEtienne Carriere 6564b5e93edSEtienne Carriere pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT; 6574b5e93edSEtienne Carriere 6584b5e93edSEtienne Carriere mode = pincfg & DT_GPIO_MODE_MASK; 6594b5e93edSEtienne Carriere 6604b5e93edSEtienne Carriere switch (mode) { 6614b5e93edSEtienne Carriere case 0: 6624b5e93edSEtienne Carriere mode = GPIO_MODE_INPUT; 6634b5e93edSEtienne Carriere break; 6644b5e93edSEtienne Carriere case 1: 6654b5e93edSEtienne Carriere case 2: 6664b5e93edSEtienne Carriere case 3: 6674b5e93edSEtienne Carriere case 4: 6684b5e93edSEtienne Carriere case 5: 6694b5e93edSEtienne Carriere case 6: 6704b5e93edSEtienne Carriere case 7: 6714b5e93edSEtienne Carriere case 8: 6724b5e93edSEtienne Carriere case 9: 6734b5e93edSEtienne Carriere case 10: 6744b5e93edSEtienne Carriere case 11: 6754b5e93edSEtienne Carriere case 12: 6764b5e93edSEtienne Carriere case 13: 6774b5e93edSEtienne Carriere case 14: 6784b5e93edSEtienne Carriere case 15: 6794b5e93edSEtienne Carriere case 16: 6804b5e93edSEtienne Carriere alternate = mode - 1U; 6814b5e93edSEtienne Carriere mode = GPIO_MODE_ALTERNATE; 6824b5e93edSEtienne Carriere break; 6834b5e93edSEtienne Carriere case 17: 6844b5e93edSEtienne Carriere mode = GPIO_MODE_ANALOG; 6854b5e93edSEtienne Carriere break; 6864b5e93edSEtienne Carriere default: 6874b5e93edSEtienne Carriere mode = GPIO_MODE_OUTPUT; 6884b5e93edSEtienne Carriere break; 6894b5e93edSEtienne Carriere } 6904b5e93edSEtienne Carriere 6914b5e93edSEtienne Carriere if (fdt_getprop(fdt, node, "drive-open-drain", NULL)) 6924b5e93edSEtienne Carriere opendrain = true; 6934b5e93edSEtienne Carriere 694322cf9e3SEtienne Carriere if (fdt_getprop(fdt, node, "output-high", NULL) && 695322cf9e3SEtienne Carriere mode == GPIO_MODE_INPUT) { 696322cf9e3SEtienne Carriere mode = GPIO_MODE_OUTPUT; 697322cf9e3SEtienne Carriere odata = 1; 698322cf9e3SEtienne Carriere } 699322cf9e3SEtienne Carriere 700322cf9e3SEtienne Carriere if (fdt_getprop(fdt, node, "output-low", NULL) && 701322cf9e3SEtienne Carriere mode == GPIO_MODE_INPUT) { 702322cf9e3SEtienne Carriere mode = GPIO_MODE_OUTPUT; 703322cf9e3SEtienne Carriere odata = 0; 704322cf9e3SEtienne Carriere } 705322cf9e3SEtienne Carriere 7064b5e93edSEtienne Carriere if (found < count) { 7074b5e93edSEtienne Carriere struct stm32_pinctrl *ref = &pinctrl[found]; 7084b5e93edSEtienne Carriere 7094b5e93edSEtienne Carriere ref->bank = (uint8_t)bank; 7104b5e93edSEtienne Carriere ref->pin = (uint8_t)pin; 711b38386fbSEtienne Carriere ref->cfg.mode = mode; 712b38386fbSEtienne Carriere if (opendrain) 713b38386fbSEtienne Carriere ref->cfg.otype = GPIO_OTYPE_OPEN_DRAIN; 714b38386fbSEtienne Carriere else 715b38386fbSEtienne Carriere ref->cfg.otype = GPIO_OTYPE_PUSH_PULL; 716b38386fbSEtienne Carriere ref->cfg.ospeed = speed; 717b38386fbSEtienne Carriere ref->cfg.pupd = pull; 718b38386fbSEtienne Carriere ref->cfg.od = odata; 719b38386fbSEtienne Carriere ref->cfg.af = alternate; 7204b5e93edSEtienne Carriere } 7214b5e93edSEtienne Carriere 7224b5e93edSEtienne Carriere found++; 7234b5e93edSEtienne Carriere } 7244b5e93edSEtienne Carriere 7254b5e93edSEtienne Carriere return (int)found; 7264b5e93edSEtienne Carriere } 7274b5e93edSEtienne Carriere 728bfc43b68SGatien Chevallier static TEE_Result consumed_gpios_pm(enum pm_op op, 729bfc43b68SGatien Chevallier unsigned int pm_hint __unused, 730bfc43b68SGatien Chevallier const struct pm_callback_handle *pm_hdl) 731bfc43b68SGatien Chevallier { 732bfc43b68SGatien Chevallier struct stm32_gpio_pm_state *handle = pm_hdl->handle; 733bfc43b68SGatien Chevallier unsigned int bank_id = handle->gpio_pinctrl.bank; 734bfc43b68SGatien Chevallier unsigned int pin = handle->gpio_pinctrl.pin; 735bfc43b68SGatien Chevallier struct gpio_chip *chip = &stm32_gpio_get_bank(bank_id)->gpio_chip; 736bfc43b68SGatien Chevallier 737bfc43b68SGatien Chevallier if (op == PM_OP_RESUME) { 738bfc43b68SGatien Chevallier set_gpio_cfg(bank_id, pin, &handle->gpio_pinctrl.cfg); 739bfc43b68SGatien Chevallier if (handle->gpio_pinctrl.cfg.mode == GPIO_MODE_OUTPUT) 740bfc43b68SGatien Chevallier stm32_gpio_set_level(chip, pin, handle->level); 741bfc43b68SGatien Chevallier } else { 742bfc43b68SGatien Chevallier get_gpio_cfg(bank_id, pin, &handle->gpio_pinctrl.cfg); 743bfc43b68SGatien Chevallier if (handle->gpio_pinctrl.cfg.mode == GPIO_MODE_OUTPUT) 744bfc43b68SGatien Chevallier handle->level = stm32_gpio_get_level(chip, pin); 745bfc43b68SGatien Chevallier } 746bfc43b68SGatien Chevallier 747bfc43b68SGatien Chevallier return TEE_SUCCESS; 748bfc43b68SGatien Chevallier } 749bfc43b68SGatien Chevallier DECLARE_KEEP_PAGER(consumed_gpios_pm); 750bfc43b68SGatien Chevallier 751b357d34fSEtienne Carriere static TEE_Result stm32_gpio_get_dt(struct dt_pargs *pargs, void *data, 752b357d34fSEtienne Carriere struct gpio **out_gpio) 753420a32c5SEtienne Carriere { 754b357d34fSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 755430c415aSEtienne Carriere const char *consumer_name __maybe_unused = NULL; 7567761b658SEtienne Carriere struct stm32_gpio_pm_state *reg_state = NULL; 757bfc43b68SGatien Chevallier struct stm32_gpio_pm_state *state = NULL; 758420a32c5SEtienne Carriere struct stm32_gpio_bank *bank = data; 759420a32c5SEtienne Carriere struct gpio *gpio = NULL; 760420a32c5SEtienne Carriere unsigned int shift_1b = 0; 761420a32c5SEtienne Carriere unsigned int shift_2b = 0; 762*4675225eSEtienne Carriere bool gpio_secure = true; 763420a32c5SEtienne Carriere uint32_t exceptions = 0; 764420a32c5SEtienne Carriere uint32_t otype = 0; 765420a32c5SEtienne Carriere uint32_t pupd = 0; 766420a32c5SEtienne Carriere uint32_t mode = 0; 767420a32c5SEtienne Carriere 768430c415aSEtienne Carriere consumer_name = fdt_get_name(pargs->fdt, pargs->consumer_node, 769430c415aSEtienne Carriere NULL); 770430c415aSEtienne Carriere 771b357d34fSEtienne Carriere res = gpio_dt_alloc_pin(pargs, &gpio); 772b357d34fSEtienne Carriere if (res) 773b357d34fSEtienne Carriere return res; 774420a32c5SEtienne Carriere 775420a32c5SEtienne Carriere if (gpio->pin >= bank->ngpios) { 776420a32c5SEtienne Carriere DMSG("Invalid GPIO reference"); 777420a32c5SEtienne Carriere free(gpio); 778b357d34fSEtienne Carriere return TEE_ERROR_GENERIC; 779420a32c5SEtienne Carriere } 780420a32c5SEtienne Carriere 781*4675225eSEtienne Carriere if (gpio->dt_flags & GPIO_STM32_NSEC) 782*4675225eSEtienne Carriere gpio_secure = false; 783*4675225eSEtienne Carriere 784bfc43b68SGatien Chevallier state = calloc(1, sizeof(*state)); 785bfc43b68SGatien Chevallier if (!state) { 786bfc43b68SGatien Chevallier free(gpio); 787bfc43b68SGatien Chevallier return TEE_ERROR_OUT_OF_MEMORY; 788bfc43b68SGatien Chevallier } 789bfc43b68SGatien Chevallier 7907761b658SEtienne Carriere SLIST_FOREACH(reg_state, &consumed_gpios_head, link) { 7917761b658SEtienne Carriere if (reg_state->gpio_pinctrl.bank == bank->bank_id && 7927761b658SEtienne Carriere reg_state->gpio_pinctrl.pin == gpio->pin) { 7937761b658SEtienne Carriere EMSG("node %s: GPIO %c%u is used by another device", 794*4675225eSEtienne Carriere consumer_name, bank->bank_id + 'A', gpio->pin); 7957761b658SEtienne Carriere free(state); 7967761b658SEtienne Carriere free(gpio); 7977761b658SEtienne Carriere return TEE_ERROR_GENERIC; 7987761b658SEtienne Carriere } 7997761b658SEtienne Carriere } 8007761b658SEtienne Carriere 801*4675225eSEtienne Carriere if (!pin_is_accessible(bank, gpio->pin)) { 802*4675225eSEtienne Carriere EMSG("node %s requests pin on GPIO %c%u which access is denied", 803*4675225eSEtienne Carriere consumer_name, bank->bank_id + 'A', gpio->pin); 804*4675225eSEtienne Carriere panic(); 805*4675225eSEtienne Carriere } 806*4675225eSEtienne Carriere 807430c415aSEtienne Carriere res = acquire_rif_semaphore_if_needed(bank, gpio->pin); 808430c415aSEtienne Carriere if (res) { 809430c415aSEtienne Carriere EMSG("Failed to acquire GPIO %c%u semaphore for node %s", 810430c415aSEtienne Carriere bank->bank_id + 'A', gpio->pin, consumer_name); 811430c415aSEtienne Carriere return res; 812430c415aSEtienne Carriere } 813430c415aSEtienne Carriere 814*4675225eSEtienne Carriere if (gpio_secure && !(bank->rif_cfg || bank->sec_support)) { 815*4675225eSEtienne Carriere EMSG("node %s requests secure GPIO %c%u that cannot be secured", 816*4675225eSEtienne Carriere consumer_name, bank->bank_id + 'A', gpio->pin); 817*4675225eSEtienne Carriere panic(); 818*4675225eSEtienne Carriere } 819*4675225eSEtienne Carriere 820*4675225eSEtienne Carriere if (gpio_secure != pin_is_secure(bank, gpio->pin)) { 821*4675225eSEtienne Carriere IMSG("WARNING: node %s requests %s GPIO %c%u but pin is %s. Check st,protreg in GPIO bank node %s", 822*4675225eSEtienne Carriere consumer_name, gpio_secure ? "secure" : "non-secure", 823*4675225eSEtienne Carriere bank->bank_id + 'A', gpio->pin, 824*4675225eSEtienne Carriere pin_is_secure(bank, gpio->pin) ? "secure" : "non-secure", 825*4675225eSEtienne Carriere fdt_get_name(pargs->fdt, pargs->phandle_node, NULL)); 826*4675225eSEtienne Carriere if (!IS_ENABLED(CFG_INSECURE)) 827*4675225eSEtienne Carriere panic(); 828*4675225eSEtienne Carriere } 829*4675225eSEtienne Carriere 830bfc43b68SGatien Chevallier state->gpio_pinctrl.pin = gpio->pin; 831bfc43b68SGatien Chevallier state->gpio_pinctrl.bank = bank->bank_id; 832bfc43b68SGatien Chevallier SLIST_INSERT_HEAD(&consumed_gpios_head, state, link); 833bfc43b68SGatien Chevallier 834bfc43b68SGatien Chevallier register_pm_driver_cb(consumed_gpios_pm, state, "stm32-gpio-state"); 835bfc43b68SGatien Chevallier 836420a32c5SEtienne Carriere shift_1b = gpio->pin; 837420a32c5SEtienne Carriere shift_2b = SHIFT_U32(gpio->pin, 1); 838420a32c5SEtienne Carriere 839420a32c5SEtienne Carriere if (gpio->dt_flags & GPIO_PULL_UP) 840420a32c5SEtienne Carriere pupd = GPIO_PUPD_PULL_UP; 841420a32c5SEtienne Carriere else if (gpio->dt_flags & GPIO_PULL_DOWN) 842420a32c5SEtienne Carriere pupd = GPIO_PUPD_PULL_DOWN; 843420a32c5SEtienne Carriere else 844420a32c5SEtienne Carriere pupd = GPIO_PUPD_NO_PULL; 845420a32c5SEtienne Carriere 846420a32c5SEtienne Carriere if (gpio->dt_flags & GPIO_LINE_OPEN_DRAIN) 847420a32c5SEtienne Carriere otype = GPIO_OTYPE_OPEN_DRAIN; 848420a32c5SEtienne Carriere else 849420a32c5SEtienne Carriere otype = GPIO_OTYPE_PUSH_PULL; 850420a32c5SEtienne Carriere 851420a32c5SEtienne Carriere if (clk_enable(bank->clock)) 852420a32c5SEtienne Carriere panic(); 853420a32c5SEtienne Carriere exceptions = cpu_spin_lock_xsave(&gpio_lock); 854420a32c5SEtienne Carriere 855420a32c5SEtienne Carriere io_clrsetbits32(bank->base + GPIO_MODER_OFFSET, 856420a32c5SEtienne Carriere SHIFT_U32(GPIO_MODE_MASK, shift_2b), 857420a32c5SEtienne Carriere SHIFT_U32(mode, shift_2b)); 858420a32c5SEtienne Carriere 859420a32c5SEtienne Carriere io_clrsetbits32(bank->base + GPIO_OTYPER_OFFSET, 860420a32c5SEtienne Carriere SHIFT_U32(GPIO_OTYPE_OPEN_DRAIN, shift_1b), 861420a32c5SEtienne Carriere SHIFT_U32(otype, shift_1b)); 862420a32c5SEtienne Carriere 863420a32c5SEtienne Carriere io_clrsetbits32(bank->base + GPIO_PUPDR_OFFSET, 864420a32c5SEtienne Carriere SHIFT_U32(GPIO_PUPD_PULL_MASK, shift_2b), 865420a32c5SEtienne Carriere SHIFT_U32(pupd, shift_2b)); 866420a32c5SEtienne Carriere 867420a32c5SEtienne Carriere cpu_spin_unlock_xrestore(&gpio_lock, exceptions); 868420a32c5SEtienne Carriere clk_disable(bank->clock); 869420a32c5SEtienne Carriere 870420a32c5SEtienne Carriere gpio->chip = &bank->gpio_chip; 871420a32c5SEtienne Carriere 872b357d34fSEtienne Carriere *out_gpio = gpio; 873420a32c5SEtienne Carriere 874b357d34fSEtienne Carriere return TEE_SUCCESS; 875420a32c5SEtienne Carriere } 876420a32c5SEtienne Carriere 8779818a481SEtienne Carriere /* Get bank ID from bank node property st,bank-name or panic on failure */ 8789818a481SEtienne Carriere static unsigned int dt_get_bank_id(const void *fdt, int node) 8799818a481SEtienne Carriere { 8809818a481SEtienne Carriere const int dt_name_len = strlen(DT_GPIO_BANK_NAME0); 8819818a481SEtienne Carriere const fdt32_t *cuint = NULL; 8829818a481SEtienne Carriere int len = 0; 8839818a481SEtienne Carriere 8849818a481SEtienne Carriere /* Parse "st,bank-name" to get its id (eg: GPIOA -> 0) */ 8859818a481SEtienne Carriere cuint = fdt_getprop(fdt, node, "st,bank-name", &len); 8869818a481SEtienne Carriere if (!cuint || (len != dt_name_len + 1)) 8879818a481SEtienne Carriere panic("Missing/wrong st,bank-name property"); 8889818a481SEtienne Carriere 8899818a481SEtienne Carriere if (strncmp((const char *)cuint, DT_GPIO_BANK_NAME0, dt_name_len - 1) || 8909818a481SEtienne Carriere strcmp((const char *)cuint, DT_GPIO_BANK_NAME0) < 0) 8919818a481SEtienne Carriere panic("Wrong st,bank-name property"); 8929818a481SEtienne Carriere 8939818a481SEtienne Carriere return (unsigned int)strcmp((const char *)cuint, DT_GPIO_BANK_NAME0); 8949818a481SEtienne Carriere } 8959818a481SEtienne Carriere 8969818a481SEtienne Carriere /* 8979818a481SEtienne Carriere * Return whether or not the GPIO bank related to a DT node is already 8989818a481SEtienne Carriere * registered in the GPIO bank link. 8999818a481SEtienne Carriere */ 9009818a481SEtienne Carriere static bool bank_is_registered(const void *fdt, int node) 9019818a481SEtienne Carriere { 9029818a481SEtienne Carriere unsigned int bank_id = dt_get_bank_id(fdt, node); 9039818a481SEtienne Carriere struct stm32_gpio_bank *bank = NULL; 9049818a481SEtienne Carriere 9059818a481SEtienne Carriere STAILQ_FOREACH(bank, &bank_list, link) 9069818a481SEtienne Carriere if (bank->bank_id == bank_id) 9079818a481SEtienne Carriere return true; 9089818a481SEtienne Carriere 9099818a481SEtienne Carriere return false; 9109818a481SEtienne Carriere } 9119818a481SEtienne Carriere 9129def1fb7SGatien Chevallier #ifdef CFG_STM32_RIF 913a72f07daSEtienne Carriere static TEE_Result handle_available_semaphores(struct stm32_gpio_bank *bank, 914a72f07daSEtienne Carriere uint32_t gpios_mask) 915bd03c8c3SGatien Chevallier { 916bd03c8c3SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 917bd03c8c3SGatien Chevallier uint32_t cidcfgr = 0; 918bd03c8c3SGatien Chevallier unsigned int i = 0; 919bd03c8c3SGatien Chevallier 9209def1fb7SGatien Chevallier for (i = 0 ; i < bank->ngpios; i++) { 921a72f07daSEtienne Carriere if (!(BIT(i) & gpios_mask)) 9229def1fb7SGatien Chevallier continue; 9239def1fb7SGatien Chevallier 9249def1fb7SGatien Chevallier cidcfgr = io_read32(bank->base + GPIO_CIDCFGR(i)); 9259def1fb7SGatien Chevallier 9269def1fb7SGatien Chevallier if (!stm32_rif_semaphore_enabled_and_ok(cidcfgr, RIF_CID1)) 9279def1fb7SGatien Chevallier continue; 9289def1fb7SGatien Chevallier 9299def1fb7SGatien Chevallier if (!(io_read32(bank->base + GPIO_SECR_OFFSET) & BIT(i))) { 9309def1fb7SGatien Chevallier res = stm32_rif_release_semaphore(bank->base + 9319def1fb7SGatien Chevallier GPIO_SEMCR(i), 9329def1fb7SGatien Chevallier MAX_CID_SUPPORTED); 9339def1fb7SGatien Chevallier if (res) { 9349def1fb7SGatien Chevallier EMSG("Cannot release semaphore for resource %u", 9359def1fb7SGatien Chevallier i); 9369def1fb7SGatien Chevallier return res; 9379def1fb7SGatien Chevallier } 9389def1fb7SGatien Chevallier } else { 9399def1fb7SGatien Chevallier res = stm32_rif_acquire_semaphore(bank->base + 9409def1fb7SGatien Chevallier GPIO_SEMCR(i), 9419def1fb7SGatien Chevallier MAX_CID_SUPPORTED); 9429def1fb7SGatien Chevallier if (res) { 9439def1fb7SGatien Chevallier EMSG("Cannot acquire semaphore for resource %u", 9449def1fb7SGatien Chevallier i); 9459def1fb7SGatien Chevallier return res; 9469def1fb7SGatien Chevallier } 9479def1fb7SGatien Chevallier } 9489def1fb7SGatien Chevallier } 9499def1fb7SGatien Chevallier 9509def1fb7SGatien Chevallier return TEE_SUCCESS; 9519def1fb7SGatien Chevallier } 9529def1fb7SGatien Chevallier 953a72f07daSEtienne Carriere static TEE_Result apply_rif_config(struct stm32_gpio_bank *bank, 954a72f07daSEtienne Carriere uint32_t gpios_mask) 9559def1fb7SGatien Chevallier { 9569def1fb7SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 9579def1fb7SGatien Chevallier unsigned int i = 0; 9589def1fb7SGatien Chevallier 959bd03c8c3SGatien Chevallier if (!bank->rif_cfg) 960bd03c8c3SGatien Chevallier return TEE_SUCCESS; 961bd03c8c3SGatien Chevallier 962bd03c8c3SGatien Chevallier if (clk_enable(bank->clock)) 963bd03c8c3SGatien Chevallier panic(); 964bd03c8c3SGatien Chevallier 9659def1fb7SGatien Chevallier if (bank->is_tdcid) { 966bd03c8c3SGatien Chevallier for (i = 0; i < bank->ngpios; i++) { 967a72f07daSEtienne Carriere if (!(BIT(i) & gpios_mask)) 968bd03c8c3SGatien Chevallier continue; 969bd03c8c3SGatien Chevallier 970bd03c8c3SGatien Chevallier /* 9719def1fb7SGatien Chevallier * When TDCID, OP-TEE should be the one to set the CID 9729def1fb7SGatien Chevallier * filtering configuration. Clearing previous 9739def1fb7SGatien Chevallier * configuration prevents undesired events during the 9749def1fb7SGatien Chevallier * only legitimate configuration. 975bd03c8c3SGatien Chevallier */ 976bd03c8c3SGatien Chevallier io_clrbits32(bank->base + GPIO_CIDCFGR(i), 977bd03c8c3SGatien Chevallier GPIO_CIDCFGR_CONF_MASK); 978bd03c8c3SGatien Chevallier } 9799def1fb7SGatien Chevallier } else { 980a72f07daSEtienne Carriere res = handle_available_semaphores(bank, gpios_mask); 9819def1fb7SGatien Chevallier if (res) 9829def1fb7SGatien Chevallier panic(); 983bd03c8c3SGatien Chevallier } 984bd03c8c3SGatien Chevallier 985bd03c8c3SGatien Chevallier /* Security and privilege RIF configuration */ 986a72f07daSEtienne Carriere io_mask32(bank->base + GPIO_PRIVCFGR_OFFSET, 987a72f07daSEtienne Carriere bank->rif_cfg->priv_conf[0], gpios_mask); 988a72f07daSEtienne Carriere io_mask32(bank->base + GPIO_SECR_OFFSET, 989a72f07daSEtienne Carriere bank->rif_cfg->sec_conf[0], gpios_mask); 990bd03c8c3SGatien Chevallier 991bd03c8c3SGatien Chevallier if (!bank->is_tdcid) { 992bd03c8c3SGatien Chevallier res = TEE_SUCCESS; 993bd03c8c3SGatien Chevallier goto out; 994bd03c8c3SGatien Chevallier } 995bd03c8c3SGatien Chevallier 996bd03c8c3SGatien Chevallier for (i = 0; i < bank->ngpios; i++) { 997a72f07daSEtienne Carriere if (!(BIT(i) & gpios_mask)) 998bd03c8c3SGatien Chevallier continue; 999bd03c8c3SGatien Chevallier 1000bd03c8c3SGatien Chevallier io_clrsetbits32(bank->base + GPIO_CIDCFGR(i), 1001bd03c8c3SGatien Chevallier GPIO_CIDCFGR_CONF_MASK, 1002bd03c8c3SGatien Chevallier bank->rif_cfg->cid_confs[i]); 1003bd03c8c3SGatien Chevallier } 1004bd03c8c3SGatien Chevallier 1005bd03c8c3SGatien Chevallier /* 1006bd03c8c3SGatien Chevallier * Lock RIF configuration if configured. This cannot be undone until 1007bd03c8c3SGatien Chevallier * next reset. 1008bd03c8c3SGatien Chevallier */ 1009bd03c8c3SGatien Chevallier io_setbits32(bank->base + GPIO_RCFGLOCKR_OFFSET, 1010bd03c8c3SGatien Chevallier bank->rif_cfg->lock_conf[0]); 1011bd03c8c3SGatien Chevallier 1012a72f07daSEtienne Carriere res = handle_available_semaphores(bank, gpios_mask); 10139def1fb7SGatien Chevallier if (res) 10149def1fb7SGatien Chevallier panic(); 10159def1fb7SGatien Chevallier 10169def1fb7SGatien Chevallier out: 1017bd03c8c3SGatien Chevallier if (IS_ENABLED(CFG_TEE_CORE_DEBUG)) { 1018bd03c8c3SGatien Chevallier /* Check that RIF config are applied, panic otherwise */ 1019bd03c8c3SGatien Chevallier if ((io_read32(bank->base + GPIO_PRIVCFGR_OFFSET) & 1020a72f07daSEtienne Carriere gpios_mask) != 1021a72f07daSEtienne Carriere (bank->rif_cfg->priv_conf[0] & gpios_mask)) { 1022bd03c8c3SGatien Chevallier EMSG("GPIO bank%c priv conf is incorrect", 1023bd03c8c3SGatien Chevallier 'A' + bank->bank_id); 1024bd03c8c3SGatien Chevallier panic(); 1025bd03c8c3SGatien Chevallier } 1026bd03c8c3SGatien Chevallier 1027a72f07daSEtienne Carriere if ((io_read32(bank->base + GPIO_SECR_OFFSET) & gpios_mask) != 1028a72f07daSEtienne Carriere (bank->rif_cfg->sec_conf[0] & gpios_mask)) { 1029bd03c8c3SGatien Chevallier EMSG("GPIO bank %c sec conf is incorrect", 1030bd03c8c3SGatien Chevallier 'A' + bank->bank_id); 1031bd03c8c3SGatien Chevallier panic(); 1032bd03c8c3SGatien Chevallier } 1033bd03c8c3SGatien Chevallier } 1034bd03c8c3SGatien Chevallier 1035bd03c8c3SGatien Chevallier clk_disable(bank->clock); 1036bd03c8c3SGatien Chevallier 1037bd03c8c3SGatien Chevallier return res; 1038bd03c8c3SGatien Chevallier } 10399def1fb7SGatien Chevallier #else /* CFG_STM32_RIF */ 1040a72f07daSEtienne Carriere static TEE_Result apply_rif_config(struct stm32_gpio_bank *bank __unused, 1041a72f07daSEtienne Carriere uint32_t gpios_mask __unused) 10429def1fb7SGatien Chevallier { 10439def1fb7SGatien Chevallier return TEE_SUCCESS; 10449def1fb7SGatien Chevallier } 10459def1fb7SGatien Chevallier #endif /* CFG_STM32_RIF */ 1046bd03c8c3SGatien Chevallier 1047a650c9cbSEtienne Carriere /* Forward reference to stm32_gpio_set_conf_sec() defined below */ 1048a650c9cbSEtienne Carriere static void stm32_gpio_set_conf_sec(struct stm32_gpio_bank *bank); 1049a650c9cbSEtienne Carriere 1050a650c9cbSEtienne Carriere static TEE_Result stm32_gpio_fw_configure(struct firewall_query *firewall) 1051a650c9cbSEtienne Carriere { 1052a650c9cbSEtienne Carriere struct stm32_gpio_bank *bank = firewall->ctrl->priv; 1053a650c9cbSEtienne Carriere uint32_t firewall_arg = 0; 1054a650c9cbSEtienne Carriere uint32_t gpios_mask = 0; 1055a650c9cbSEtienne Carriere bool secure = true; 1056a650c9cbSEtienne Carriere 1057a650c9cbSEtienne Carriere assert(bank->sec_support); 1058a650c9cbSEtienne Carriere 1059a650c9cbSEtienne Carriere if (firewall->arg_count != 1) 1060a650c9cbSEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 1061a650c9cbSEtienne Carriere 1062a650c9cbSEtienne Carriere firewall_arg = firewall->args[0]; 1063a650c9cbSEtienne Carriere 1064a650c9cbSEtienne Carriere if (bank->rif_cfg) { 1065a650c9cbSEtienne Carriere gpios_mask = BIT(RIF_CHANNEL_ID(firewall_arg)); 1066a650c9cbSEtienne Carriere 1067a650c9cbSEtienne Carriere /* We're about to change a specific GPIO config */ 1068a650c9cbSEtienne Carriere bank->rif_cfg->access_mask[0] |= gpios_mask; 1069a650c9cbSEtienne Carriere 1070a650c9cbSEtienne Carriere /* 1071a650c9cbSEtienne Carriere * Update bank RIF config with firewall configuration data 1072a650c9cbSEtienne Carriere * and apply it. 1073a650c9cbSEtienne Carriere */ 1074a650c9cbSEtienne Carriere stm32_rif_parse_cfg(firewall_arg, bank->rif_cfg, 1075a650c9cbSEtienne Carriere bank->ngpios); 1076a650c9cbSEtienne Carriere return apply_rif_config(bank, gpios_mask); 1077a650c9cbSEtienne Carriere } 1078a650c9cbSEtienne Carriere 1079a650c9cbSEtienne Carriere /* 1080a650c9cbSEtienne Carriere * Non RIF GPIO banks use a single cell as a bit mask (bits 0 to 15) 1081a650c9cbSEtienne Carriere * to define the a group of GPIO pins (one or several) to configure 1082a650c9cbSEtienne Carriere * for that bank, and GPIO_STM32_NSEC bit flag to set if these pins 1083a650c9cbSEtienne Carriere * are non-secure (flag set) or non-secure (flag cleared). 1084a650c9cbSEtienne Carriere */ 1085a650c9cbSEtienne Carriere gpios_mask = firewall_arg & GENMASK_32(15, 0); 1086a650c9cbSEtienne Carriere 1087a650c9cbSEtienne Carriere secure = !(firewall_arg & GPIO_STM32_NSEC); 1088a650c9cbSEtienne Carriere 1089a650c9cbSEtienne Carriere if (gpios_mask & ~GENMASK_32(bank->ngpios, 0)) { 1090a650c9cbSEtienne Carriere EMSG("Invalid bitmask %#"PRIx32" for GPIO bank %c", 1091a650c9cbSEtienne Carriere gpios_mask, 'A' + bank->bank_id); 1092a650c9cbSEtienne Carriere return TEE_ERROR_GENERIC; 1093a650c9cbSEtienne Carriere } 1094a650c9cbSEtienne Carriere 1095a650c9cbSEtienne Carriere /* Update bank secure register configuration data and apply it */ 1096a650c9cbSEtienne Carriere if (secure) 1097a650c9cbSEtienne Carriere bank->seccfgr |= gpios_mask; 1098a650c9cbSEtienne Carriere else 1099a650c9cbSEtienne Carriere bank->seccfgr &= ~gpios_mask; 1100a650c9cbSEtienne Carriere 1101a650c9cbSEtienne Carriere stm32_gpio_set_conf_sec(bank); 1102a650c9cbSEtienne Carriere 1103a650c9cbSEtienne Carriere return TEE_SUCCESS; 1104a650c9cbSEtienne Carriere } 1105a650c9cbSEtienne Carriere 1106a650c9cbSEtienne Carriere static const struct firewall_controller_ops stm32_gpio_firewall_ops = { 1107a650c9cbSEtienne Carriere .set_conf = stm32_gpio_fw_configure, 1108a650c9cbSEtienne Carriere }; 1109a650c9cbSEtienne Carriere 1110bfc43b68SGatien Chevallier static void stm32_gpio_save_rif_config(struct stm32_gpio_bank *bank) 1111bfc43b68SGatien Chevallier { 1112bfc43b68SGatien Chevallier size_t i = 0; 1113bfc43b68SGatien Chevallier 1114bfc43b68SGatien Chevallier for (i = 0; i < bank->ngpios; i++) 1115bfc43b68SGatien Chevallier bank->rif_cfg->cid_confs[i] = io_read32(bank->base + 1116bfc43b68SGatien Chevallier GPIO_CIDCFGR(i)); 1117bfc43b68SGatien Chevallier 1118bfc43b68SGatien Chevallier bank->rif_cfg->priv_conf[0] = io_read32(bank->base + 1119bfc43b68SGatien Chevallier GPIO_PRIVCFGR_OFFSET); 1120bfc43b68SGatien Chevallier bank->rif_cfg->sec_conf[0] = io_read32(bank->base + 1121bfc43b68SGatien Chevallier GPIO_SECR_OFFSET); 1122bfc43b68SGatien Chevallier bank->rif_cfg->lock_conf[0] = io_read32(bank->base + 1123bfc43b68SGatien Chevallier GPIO_RCFGLOCKR_OFFSET); 1124bfc43b68SGatien Chevallier } 1125bfc43b68SGatien Chevallier 1126bd03c8c3SGatien Chevallier static void stm32_parse_gpio_rif_conf(struct stm32_gpio_bank *bank, 1127bd03c8c3SGatien Chevallier const void *fdt, int node) 1128bd03c8c3SGatien Chevallier { 1129bd03c8c3SGatien Chevallier unsigned int i = 0; 1130bd03c8c3SGatien Chevallier unsigned int nb_rif_conf = 0; 1131bd03c8c3SGatien Chevallier int lenp = 0; 1132bd03c8c3SGatien Chevallier const fdt32_t *cuint = NULL; 1133bd03c8c3SGatien Chevallier 1134bd03c8c3SGatien Chevallier cuint = fdt_getprop(fdt, node, "st,protreg", &lenp); 1135bd03c8c3SGatien Chevallier if (!cuint) { 1136bd03c8c3SGatien Chevallier DMSG("No RIF configuration available"); 1137bd03c8c3SGatien Chevallier return; 1138bd03c8c3SGatien Chevallier } 1139bd03c8c3SGatien Chevallier 1140bd03c8c3SGatien Chevallier bank->rif_cfg = calloc(1, sizeof(*bank->rif_cfg)); 1141bd03c8c3SGatien Chevallier if (!bank->rif_cfg) 1142bd03c8c3SGatien Chevallier panic(); 1143bd03c8c3SGatien Chevallier 1144bd03c8c3SGatien Chevallier bank->rif_cfg->sec_conf = calloc(1, sizeof(uint32_t)); 1145bd03c8c3SGatien Chevallier if (!bank->rif_cfg->sec_conf) 1146bd03c8c3SGatien Chevallier panic(); 1147bd03c8c3SGatien Chevallier 1148bd03c8c3SGatien Chevallier nb_rif_conf = (unsigned int)(lenp / sizeof(uint32_t)); 1149bd03c8c3SGatien Chevallier assert(nb_rif_conf <= bank->ngpios); 1150bd03c8c3SGatien Chevallier 1151bd03c8c3SGatien Chevallier bank->rif_cfg->cid_confs = calloc(bank->ngpios, sizeof(uint32_t)); 1152bd03c8c3SGatien Chevallier bank->rif_cfg->priv_conf = calloc(1, sizeof(uint32_t)); 1153bd03c8c3SGatien Chevallier bank->rif_cfg->lock_conf = calloc(1, sizeof(uint32_t)); 1154bd03c8c3SGatien Chevallier bank->rif_cfg->access_mask = calloc(1, sizeof(uint32_t)); 1155bd03c8c3SGatien Chevallier if (!bank->rif_cfg->cid_confs || !bank->rif_cfg->access_mask || 1156bd03c8c3SGatien Chevallier !bank->rif_cfg->priv_conf || !bank->rif_cfg->lock_conf) 1157bd03c8c3SGatien Chevallier panic("Missing memory capacity for GPIOS RIF configuration"); 1158bd03c8c3SGatien Chevallier 1159bd03c8c3SGatien Chevallier for (i = 0; i < nb_rif_conf; i++) 1160bd03c8c3SGatien Chevallier stm32_rif_parse_cfg(fdt32_to_cpu(cuint[i]), bank->rif_cfg, 1161646ad62bSGatien Chevallier bank->ngpios); 1162bd03c8c3SGatien Chevallier } 1163bd03c8c3SGatien Chevallier 11649818a481SEtienne Carriere /* Get GPIO bank information from the DT */ 11659818a481SEtienne Carriere static TEE_Result dt_stm32_gpio_bank(const void *fdt, int node, 1166e569f6adSEtienne Carriere const void *compat_data, 11679818a481SEtienne Carriere int range_offset, 11689818a481SEtienne Carriere struct stm32_gpio_bank **out_bank) 11699818a481SEtienne Carriere { 1170e569f6adSEtienne Carriere const struct bank_compat *compat = compat_data; 11719818a481SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 11729818a481SEtienne Carriere struct stm32_gpio_bank *bank = NULL; 11739818a481SEtienne Carriere const fdt32_t *cuint = NULL; 11749818a481SEtienne Carriere struct io_pa_va pa_va = { }; 11759818a481SEtienne Carriere struct clk *clk = NULL; 11769818a481SEtienne Carriere size_t blen = 0; 11779818a481SEtienne Carriere paddr_t pa = 0; 11789818a481SEtienne Carriere int len = 0; 11799818a481SEtienne Carriere int i = 0; 11809818a481SEtienne Carriere 11819818a481SEtienne Carriere assert(out_bank); 11829818a481SEtienne Carriere 11839818a481SEtienne Carriere /* Probe deferrable devices first */ 11849818a481SEtienne Carriere res = clk_dt_get_by_index(fdt, node, 0, &clk); 11859818a481SEtienne Carriere if (res) 11869818a481SEtienne Carriere return res; 11879818a481SEtienne Carriere 11889818a481SEtienne Carriere bank = calloc(1, sizeof(*bank)); 11899818a481SEtienne Carriere if (!bank) 11909818a481SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 11919818a481SEtienne Carriere 1192bd03c8c3SGatien Chevallier if (compat->secure_extended) { 1193bd03c8c3SGatien Chevallier res = stm32_rifsc_check_tdcid(&bank->is_tdcid); 1194bd03c8c3SGatien Chevallier if (res) { 1195bd03c8c3SGatien Chevallier free(bank); 1196bd03c8c3SGatien Chevallier return res; 1197bd03c8c3SGatien Chevallier } 1198bd03c8c3SGatien Chevallier } 1199bd03c8c3SGatien Chevallier 12009818a481SEtienne Carriere /* 12019818a481SEtienne Carriere * Do not rely *only* on the "reg" property to get the address, 12029818a481SEtienne Carriere * but consider also the "ranges" translation property 12039818a481SEtienne Carriere */ 12046a0116edSEtienne Carriere if (fdt_reg_info(fdt, node, &pa, &blen)) 12056a0116edSEtienne Carriere panic("missing reg or reg size property"); 12069818a481SEtienne Carriere 12079818a481SEtienne Carriere pa_va.pa = pa + range_offset; 12089818a481SEtienne Carriere 12099818a481SEtienne Carriere DMSG("Bank name %s", fdt_get_name(fdt, node, NULL)); 12109818a481SEtienne Carriere bank->bank_id = dt_get_bank_id(fdt, node); 12119818a481SEtienne Carriere bank->clock = clk; 1212420a32c5SEtienne Carriere bank->gpio_chip.ops = &stm32_gpio_ops; 1213b4893304SGatien Chevallier bank->sec_support = compat->secure_control; 12149818a481SEtienne Carriere 12159818a481SEtienne Carriere /* Parse gpio-ranges with its 4 parameters */ 12169818a481SEtienne Carriere cuint = fdt_getprop(fdt, node, "gpio-ranges", &len); 12179818a481SEtienne Carriere len /= sizeof(*cuint); 12189818a481SEtienne Carriere if (len % 4) 12199818a481SEtienne Carriere panic("wrong gpio-ranges syntax"); 12209818a481SEtienne Carriere 12219818a481SEtienne Carriere /* Get the last defined gpio line (offset + nb of pins) */ 12229818a481SEtienne Carriere for (i = 0; i < len / 4; i++) { 12239818a481SEtienne Carriere bank->ngpios = MAX(bank->ngpios, 12249818a481SEtienne Carriere (unsigned int)(fdt32_to_cpu(*(cuint + 1)) + 12259818a481SEtienne Carriere fdt32_to_cpu(*(cuint + 3)))); 12269818a481SEtienne Carriere cuint += 4; 12279818a481SEtienne Carriere } 12289818a481SEtienne Carriere 1229bd03c8c3SGatien Chevallier if (compat->secure_extended) { 1230bd03c8c3SGatien Chevallier /* RIF configuration */ 1231bd03c8c3SGatien Chevallier bank->base = io_pa_or_va_secure(&pa_va, blen); 1232bd03c8c3SGatien Chevallier 1233bd03c8c3SGatien Chevallier stm32_parse_gpio_rif_conf(bank, fdt, node); 1234bd03c8c3SGatien Chevallier } else if (bank->sec_support) { 1235bd03c8c3SGatien Chevallier /* Secure configuration */ 1236bd03c8c3SGatien Chevallier bank->base = io_pa_or_va_secure(&pa_va, blen); 1237bd03c8c3SGatien Chevallier cuint = fdt_getprop(fdt, node, "st,protreg", NULL); 1238bd03c8c3SGatien Chevallier if (cuint) 1239bd03c8c3SGatien Chevallier bank->seccfgr = fdt32_to_cpu(*cuint); 1240bd03c8c3SGatien Chevallier else 1241bd03c8c3SGatien Chevallier DMSG("GPIO bank %c assigned to non-secure", 1242bd03c8c3SGatien Chevallier bank->bank_id + 'A'); 1243bd03c8c3SGatien Chevallier } else { 1244bd03c8c3SGatien Chevallier bank->base = io_pa_or_va_nsec(&pa_va, blen); 1245bd03c8c3SGatien Chevallier } 1246bd03c8c3SGatien Chevallier 1247e569f6adSEtienne Carriere if (compat->gpioz) 1248e569f6adSEtienne Carriere stm32mp_register_gpioz_pin_count(bank->ngpios); 1249e569f6adSEtienne Carriere 12509818a481SEtienne Carriere *out_bank = bank; 1251bd03c8c3SGatien Chevallier 12529818a481SEtienne Carriere return TEE_SUCCESS; 12539818a481SEtienne Carriere } 12549818a481SEtienne Carriere 1255a650c9cbSEtienne Carriere static TEE_Result stm32_gpio_firewall_register(const void *fdt, int node, 1256a650c9cbSEtienne Carriere struct stm32_gpio_bank *bank) 1257a650c9cbSEtienne Carriere { 1258a650c9cbSEtienne Carriere struct firewall_controller *controller = NULL; 1259a650c9cbSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 1260a650c9cbSEtienne Carriere char bank_name[] = "gpio-bank-X"; 1261a650c9cbSEtienne Carriere char *name = NULL; 1262a650c9cbSEtienne Carriere 1263a650c9cbSEtienne Carriere if (!IS_ENABLED(CFG_DRIVERS_FIREWALL) || 1264a650c9cbSEtienne Carriere !bank->sec_support) 1265a650c9cbSEtienne Carriere return TEE_SUCCESS; 1266a650c9cbSEtienne Carriere 1267a650c9cbSEtienne Carriere controller = calloc(1, sizeof(*controller)); 1268a650c9cbSEtienne Carriere if (!controller) 1269a650c9cbSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 1270a650c9cbSEtienne Carriere 1271a650c9cbSEtienne Carriere bank_name[sizeof(bank_name) - 2] = 'A' + bank->bank_id; 1272a650c9cbSEtienne Carriere name = strdup(bank_name); 1273a650c9cbSEtienne Carriere 1274a650c9cbSEtienne Carriere controller->name = name; 1275a650c9cbSEtienne Carriere controller->priv = bank; 1276a650c9cbSEtienne Carriere controller->ops = &stm32_gpio_firewall_ops; 1277a650c9cbSEtienne Carriere 1278a650c9cbSEtienne Carriere if (!controller->name) 1279a650c9cbSEtienne Carriere EMSG("Warning: out of memory to store bank name"); 1280a650c9cbSEtienne Carriere 1281a650c9cbSEtienne Carriere res = firewall_dt_controller_register(fdt, node, controller); 1282a650c9cbSEtienne Carriere if (res) { 1283a650c9cbSEtienne Carriere free(name); 1284a650c9cbSEtienne Carriere free(controller); 1285a650c9cbSEtienne Carriere } 1286a650c9cbSEtienne Carriere 1287a650c9cbSEtienne Carriere return res; 1288a650c9cbSEtienne Carriere } 1289a650c9cbSEtienne Carriere 1290bd03c8c3SGatien Chevallier /* Parse a pinctrl node to register the GPIO banks it describes */ 12910e0435e2SEtienne Carriere static TEE_Result dt_stm32_gpio_pinctrl(const void *fdt, int node, 12929818a481SEtienne Carriere const void *compat_data) 12939818a481SEtienne Carriere { 12949818a481SEtienne Carriere TEE_Result res = TEE_SUCCESS; 12959818a481SEtienne Carriere const fdt32_t *cuint = NULL; 12969818a481SEtienne Carriere int range_offs = 0; 12979818a481SEtienne Carriere int b_node = 0; 12989818a481SEtienne Carriere int len = 0; 12999818a481SEtienne Carriere 13009818a481SEtienne Carriere /* Read the ranges property (for regs memory translation) */ 13019818a481SEtienne Carriere cuint = fdt_getprop(fdt, node, "ranges", &len); 13029818a481SEtienne Carriere if (!cuint) 13039818a481SEtienne Carriere panic("missing ranges property"); 13049818a481SEtienne Carriere 13059818a481SEtienne Carriere len /= sizeof(*cuint); 13069818a481SEtienne Carriere if (len == 3) 13079818a481SEtienne Carriere range_offs = fdt32_to_cpu(*(cuint + 1)) - fdt32_to_cpu(*cuint); 13089818a481SEtienne Carriere 13099818a481SEtienne Carriere fdt_for_each_subnode(b_node, fdt, node) { 13109818a481SEtienne Carriere cuint = fdt_getprop(fdt, b_node, "gpio-controller", &len); 13119818a481SEtienne Carriere if (cuint) { 13129818a481SEtienne Carriere /* 13139818a481SEtienne Carriere * We found a property "gpio-controller" in the node: 13149818a481SEtienne Carriere * the node is a GPIO bank description, add it to the 13159818a481SEtienne Carriere * bank list. 13169818a481SEtienne Carriere */ 13179818a481SEtienne Carriere struct stm32_gpio_bank *bank = NULL; 13189818a481SEtienne Carriere 13199818a481SEtienne Carriere if (fdt_get_status(fdt, b_node) == DT_STATUS_DISABLED || 13209818a481SEtienne Carriere bank_is_registered(fdt, b_node)) 13219818a481SEtienne Carriere continue; 13229818a481SEtienne Carriere 13239818a481SEtienne Carriere res = dt_stm32_gpio_bank(fdt, b_node, compat_data, 13249818a481SEtienne Carriere range_offs, &bank); 13259818a481SEtienne Carriere if (res) 13269818a481SEtienne Carriere return res; 13279818a481SEtienne Carriere 1328420a32c5SEtienne Carriere /* Registering a provider should not defer probe */ 1329420a32c5SEtienne Carriere res = gpio_register_provider(fdt, b_node, 1330420a32c5SEtienne Carriere stm32_gpio_get_dt, bank); 1331420a32c5SEtienne Carriere if (res) 1332420a32c5SEtienne Carriere panic(); 1333420a32c5SEtienne Carriere 1334a650c9cbSEtienne Carriere res = stm32_gpio_firewall_register(fdt, b_node, bank); 1335a650c9cbSEtienne Carriere if (res) 1336a650c9cbSEtienne Carriere panic(); 1337a650c9cbSEtienne Carriere 13389818a481SEtienne Carriere STAILQ_INSERT_TAIL(&bank_list, bank, link); 13399818a481SEtienne Carriere } else { 13409818a481SEtienne Carriere if (len != -FDT_ERR_NOTFOUND) 13419818a481SEtienne Carriere panic(); 13429818a481SEtienne Carriere } 13439818a481SEtienne Carriere } 13449818a481SEtienne Carriere 13459818a481SEtienne Carriere return TEE_SUCCESS; 13469818a481SEtienne Carriere } 13479818a481SEtienne Carriere 1348077d486eSEtienne Carriere void stm32_gpio_set_secure_cfg(unsigned int bank_id, unsigned int pin, 1349077d486eSEtienne Carriere bool secure) 13504b5e93edSEtienne Carriere { 1351077d486eSEtienne Carriere struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id); 135298dfcedaSEtienne Carriere uint32_t exceptions = 0; 13534b5e93edSEtienne Carriere 1354077d486eSEtienne Carriere if (clk_enable(bank->clock)) 1355077d486eSEtienne Carriere panic(); 135698dfcedaSEtienne Carriere exceptions = cpu_spin_lock_xsave(&gpio_lock); 13574b5e93edSEtienne Carriere 13584b5e93edSEtienne Carriere if (secure) 1359077d486eSEtienne Carriere io_setbits32(bank->base + GPIO_SECR_OFFSET, BIT(pin)); 13604b5e93edSEtienne Carriere else 1361077d486eSEtienne Carriere io_clrbits32(bank->base + GPIO_SECR_OFFSET, BIT(pin)); 13624b5e93edSEtienne Carriere 1363c4cab2bbSEtienne Carriere cpu_spin_unlock_xrestore(&gpio_lock, exceptions); 136498dfcedaSEtienne Carriere clk_disable(bank->clock); 13654b5e93edSEtienne Carriere } 13660e0435e2SEtienne Carriere 1367b38386fbSEtienne Carriere #ifdef CFG_DRIVERS_PINCTRL 1368b38386fbSEtienne Carriere static TEE_Result stm32_pinctrl_conf_apply(struct pinconf *conf) 1369b38386fbSEtienne Carriere { 1370b38386fbSEtienne Carriere struct stm32_pinctrl_array *ref = conf->priv; 1371b38386fbSEtienne Carriere struct stm32_pinctrl *p = ref->pinctrl; 1372430c415aSEtienne Carriere struct stm32_gpio_bank *bank = NULL; 1373430c415aSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 1374b38386fbSEtienne Carriere size_t pin_count = ref->count; 1375b38386fbSEtienne Carriere size_t n = 0; 1376430c415aSEtienne Carriere bool error = false; 1377430c415aSEtienne Carriere 1378430c415aSEtienne Carriere for (n = 0; n < pin_count; n++) { 1379430c415aSEtienne Carriere bank = stm32_gpio_get_bank(p[n].bank); 1380430c415aSEtienne Carriere res = acquire_rif_semaphore_if_needed(bank, p[n].pin); 1381430c415aSEtienne Carriere if (res) { 1382430c415aSEtienne Carriere EMSG("Failed to acquire GPIO %c%u semaphore", 1383430c415aSEtienne Carriere bank->bank_id + 'A', p[n].pin); 1384430c415aSEtienne Carriere error = true; 1385430c415aSEtienne Carriere } 1386430c415aSEtienne Carriere } 1387430c415aSEtienne Carriere 1388430c415aSEtienne Carriere if (error) { 1389430c415aSEtienne Carriere for (n = 0; n < pin_count; n++) { 1390430c415aSEtienne Carriere bank = stm32_gpio_get_bank(p[n].bank); 1391430c415aSEtienne Carriere release_rif_semaphore_if_acquired(bank, p[n].pin); 1392430c415aSEtienne Carriere } 1393430c415aSEtienne Carriere 1394430c415aSEtienne Carriere return TEE_ERROR_SECURITY; 1395430c415aSEtienne Carriere } 1396b38386fbSEtienne Carriere 1397b38386fbSEtienne Carriere for (n = 0; n < pin_count; n++) 1398b38386fbSEtienne Carriere set_gpio_cfg(p[n].bank, p[n].pin, &p[n].cfg); 1399b38386fbSEtienne Carriere 1400b38386fbSEtienne Carriere return TEE_SUCCESS; 1401b38386fbSEtienne Carriere } 1402b38386fbSEtienne Carriere 1403b38386fbSEtienne Carriere static void stm32_pinctrl_conf_free(struct pinconf *conf) 1404b38386fbSEtienne Carriere { 1405b38386fbSEtienne Carriere free(conf); 1406b38386fbSEtienne Carriere } 1407b38386fbSEtienne Carriere 1408b38386fbSEtienne Carriere static const struct pinctrl_ops stm32_pinctrl_ops = { 1409b38386fbSEtienne Carriere .conf_apply = stm32_pinctrl_conf_apply, 1410b38386fbSEtienne Carriere .conf_free = stm32_pinctrl_conf_free, 1411b38386fbSEtienne Carriere }; 1412b38386fbSEtienne Carriere 1413b38386fbSEtienne Carriere DECLARE_KEEP_PAGER(stm32_pinctrl_ops); 1414b38386fbSEtienne Carriere 141570ac0db5SEtienne Carriere void stm32_gpio_pinctrl_bank_pin(struct pinctrl_state *pinctrl, 141670ac0db5SEtienne Carriere unsigned int *bank, unsigned int *pin, 141770ac0db5SEtienne Carriere unsigned int *count) 141870ac0db5SEtienne Carriere { 141970ac0db5SEtienne Carriere size_t conf_index = 0; 142070ac0db5SEtienne Carriere size_t pin_count = 0; 142170ac0db5SEtienne Carriere size_t n = 0; 142270ac0db5SEtienne Carriere 142370ac0db5SEtienne Carriere assert(count); 142470ac0db5SEtienne Carriere if (!pinctrl) 142570ac0db5SEtienne Carriere goto out; 142670ac0db5SEtienne Carriere 142770ac0db5SEtienne Carriere for (conf_index = 0; conf_index < pinctrl->conf_count; conf_index++) { 142870ac0db5SEtienne Carriere struct pinconf *pinconf = pinctrl->confs[conf_index]; 142970ac0db5SEtienne Carriere struct stm32_pinctrl_array *ref = pinconf->priv; 143070ac0db5SEtienne Carriere 143170ac0db5SEtienne Carriere /* Consider only the stm32_gpio pins */ 143270ac0db5SEtienne Carriere if (pinconf->ops != &stm32_pinctrl_ops) 143370ac0db5SEtienne Carriere continue; 143470ac0db5SEtienne Carriere 143570ac0db5SEtienne Carriere if (bank || pin) { 143670ac0db5SEtienne Carriere for (n = 0; n < ref->count; n++) { 143770ac0db5SEtienne Carriere if (bank && pin_count < *count) 143870ac0db5SEtienne Carriere bank[pin_count] = ref->pinctrl[n].bank; 143970ac0db5SEtienne Carriere if (pin && pin_count < *count) 144070ac0db5SEtienne Carriere pin[pin_count] = ref->pinctrl[n].pin; 144170ac0db5SEtienne Carriere pin_count++; 144270ac0db5SEtienne Carriere } 144370ac0db5SEtienne Carriere } else { 144470ac0db5SEtienne Carriere pin_count += ref->count; 144570ac0db5SEtienne Carriere } 144670ac0db5SEtienne Carriere } 144770ac0db5SEtienne Carriere 144870ac0db5SEtienne Carriere out: 144970ac0db5SEtienne Carriere *count = pin_count; 145070ac0db5SEtienne Carriere } 145170ac0db5SEtienne Carriere 14527f823a77SEtienne Carriere void stm32_pinctrl_set_secure_cfg(struct pinctrl_state *pinctrl, bool secure) 14537f823a77SEtienne Carriere { 14547f823a77SEtienne Carriere size_t conf_index = 0; 14557f823a77SEtienne Carriere 14567f823a77SEtienne Carriere if (!pinctrl) 14577f823a77SEtienne Carriere return; 14587f823a77SEtienne Carriere 14597f823a77SEtienne Carriere for (conf_index = 0; conf_index < pinctrl->conf_count; conf_index++) { 14607f823a77SEtienne Carriere struct pinconf *pinconf = pinctrl->confs[conf_index]; 14617f823a77SEtienne Carriere struct stm32_pinctrl_array *ref = pinconf->priv; 14627f823a77SEtienne Carriere struct stm32_pinctrl *pc = NULL; 14637f823a77SEtienne Carriere size_t n = 0; 14647f823a77SEtienne Carriere 14657f823a77SEtienne Carriere for (n = 0; n < ref->count; n++) { 14667f823a77SEtienne Carriere if (pinconf->ops != &stm32_pinctrl_ops) 14677f823a77SEtienne Carriere continue; 14687f823a77SEtienne Carriere 14697f823a77SEtienne Carriere pc = ref->pinctrl + n; 14707f823a77SEtienne Carriere stm32_gpio_set_secure_cfg(pc->bank, pc->pin, secure); 14717f823a77SEtienne Carriere } 14727f823a77SEtienne Carriere } 14737f823a77SEtienne Carriere } 14747f823a77SEtienne Carriere 1475b38386fbSEtienne Carriere /* Allocate and return a pinctrl configuration from a DT reference */ 1476b38386fbSEtienne Carriere static TEE_Result stm32_pinctrl_dt_get(struct dt_pargs *pargs, 1477b38386fbSEtienne Carriere void *data __unused, 1478b38386fbSEtienne Carriere struct pinconf **out_pinconf) 1479b38386fbSEtienne Carriere { 1480b38386fbSEtienne Carriere struct conf { 1481b38386fbSEtienne Carriere struct pinconf pinconf; 1482b38386fbSEtienne Carriere struct stm32_pinctrl_array array_ref; 1483b38386fbSEtienne Carriere } *loc_conf = NULL; 1484b38386fbSEtienne Carriere struct stm32_pinctrl *pinctrl = NULL; 1485b38386fbSEtienne Carriere struct pinconf *pinconf = NULL; 1486b38386fbSEtienne Carriere const void *fdt = NULL; 1487b38386fbSEtienne Carriere size_t pin_count = 0; 1488b38386fbSEtienne Carriere int pinctrl_node = 0; 1489b38386fbSEtienne Carriere int pinmux_node = 0; 1490b38386fbSEtienne Carriere int count = 0; 1491b38386fbSEtienne Carriere 1492b38386fbSEtienne Carriere pinctrl_node = pargs->phandle_node; 1493b38386fbSEtienne Carriere fdt = pargs->fdt; 1494b38386fbSEtienne Carriere assert(fdt && pinctrl_node); 1495b38386fbSEtienne Carriere 1496b38386fbSEtienne Carriere fdt_for_each_subnode(pinmux_node, fdt, pinctrl_node) { 1497b38386fbSEtienne Carriere if (fdt_getprop(fdt, pinmux_node, "pinmux", &count)) 1498b38386fbSEtienne Carriere pin_count += (size_t)count / sizeof(uint32_t); 1499b38386fbSEtienne Carriere else if (count != -FDT_ERR_NOTFOUND) 1500b38386fbSEtienne Carriere panic(); 1501b38386fbSEtienne Carriere } 1502b38386fbSEtienne Carriere 1503b38386fbSEtienne Carriere loc_conf = calloc(1, sizeof(*loc_conf) + sizeof(*pinctrl) * pin_count); 1504b38386fbSEtienne Carriere if (!loc_conf) 1505b38386fbSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 1506b38386fbSEtienne Carriere 1507b38386fbSEtienne Carriere pinconf = &loc_conf->pinconf; 1508b38386fbSEtienne Carriere pinconf->ops = &stm32_pinctrl_ops; 1509b38386fbSEtienne Carriere pinconf->priv = &loc_conf->array_ref; 1510b38386fbSEtienne Carriere 1511b38386fbSEtienne Carriere loc_conf->array_ref.count = pin_count; 1512b38386fbSEtienne Carriere pinctrl = loc_conf->array_ref.pinctrl; 1513b38386fbSEtienne Carriere 1514b38386fbSEtienne Carriere count = 0; 1515b38386fbSEtienne Carriere fdt_for_each_subnode(pinmux_node, fdt, pinctrl_node) { 1516b38386fbSEtienne Carriere int found = 0; 1517b38386fbSEtienne Carriere 1518b38386fbSEtienne Carriere found = get_pinctrl_from_fdt(fdt, pinmux_node, pinctrl + count, 1519b38386fbSEtienne Carriere pin_count - count); 1520b38386fbSEtienne Carriere if (found <= 0 && found > ((int)pin_count - count)) { 1521b38386fbSEtienne Carriere /* We can't recover from an error here so let's panic */ 1522b38386fbSEtienne Carriere panic(); 1523b38386fbSEtienne Carriere } 1524b38386fbSEtienne Carriere 1525b38386fbSEtienne Carriere count += found; 1526b38386fbSEtienne Carriere } 1527b38386fbSEtienne Carriere 1528b38386fbSEtienne Carriere *out_pinconf = pinconf; 1529b38386fbSEtienne Carriere 1530b38386fbSEtienne Carriere return TEE_SUCCESS; 1531b38386fbSEtienne Carriere } 1532b38386fbSEtienne Carriere #endif /*CFG_DRIVERS_PINCTRL*/ 1533b38386fbSEtienne Carriere 1534bfc43b68SGatien Chevallier static void stm32_gpio_get_conf_sec(struct stm32_gpio_bank *bank) 1535bfc43b68SGatien Chevallier { 1536bfc43b68SGatien Chevallier if (bank->sec_support) { 1537bfc43b68SGatien Chevallier clk_enable(bank->clock); 1538bfc43b68SGatien Chevallier bank->seccfgr = io_read32(bank->base + GPIO_SECR_OFFSET); 1539bfc43b68SGatien Chevallier clk_disable(bank->clock); 1540bfc43b68SGatien Chevallier } 1541bfc43b68SGatien Chevallier } 1542bfc43b68SGatien Chevallier 1543bd03c8c3SGatien Chevallier static void stm32_gpio_set_conf_sec(struct stm32_gpio_bank *bank) 1544bd03c8c3SGatien Chevallier { 1545bd03c8c3SGatien Chevallier if (bank->sec_support) { 1546bd03c8c3SGatien Chevallier clk_enable(bank->clock); 1547bd03c8c3SGatien Chevallier io_write32(bank->base + GPIO_SECR_OFFSET, bank->seccfgr); 1548bd03c8c3SGatien Chevallier clk_disable(bank->clock); 1549bd03c8c3SGatien Chevallier } 1550bd03c8c3SGatien Chevallier } 1551bd03c8c3SGatien Chevallier 1552bfc43b68SGatien Chevallier static TEE_Result stm32_gpio_sec_config_resume(void) 1553bfc43b68SGatien Chevallier { 1554bfc43b68SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 1555bfc43b68SGatien Chevallier struct stm32_gpio_bank *bank = NULL; 1556bfc43b68SGatien Chevallier 1557bfc43b68SGatien Chevallier STAILQ_FOREACH(bank, &bank_list, link) { 1558bfc43b68SGatien Chevallier if (bank->rif_cfg) { 1559bfc43b68SGatien Chevallier if (!bank->is_tdcid) 1560bfc43b68SGatien Chevallier continue; 1561bfc43b68SGatien Chevallier 1562bfc43b68SGatien Chevallier bank->rif_cfg->access_mask[0] = GENMASK_32(bank->ngpios, 1563bfc43b68SGatien Chevallier 0); 1564bfc43b68SGatien Chevallier 1565a72f07daSEtienne Carriere res = apply_rif_config(bank, 1566a72f07daSEtienne Carriere bank->rif_cfg->access_mask[0]); 1567bfc43b68SGatien Chevallier if (res) { 1568bfc43b68SGatien Chevallier EMSG("Failed to set GPIO bank %c RIF config", 1569bfc43b68SGatien Chevallier 'A' + bank->bank_id); 1570bfc43b68SGatien Chevallier return res; 1571bfc43b68SGatien Chevallier } 1572bfc43b68SGatien Chevallier } else { 1573bfc43b68SGatien Chevallier stm32_gpio_set_conf_sec(bank); 1574bfc43b68SGatien Chevallier } 1575bfc43b68SGatien Chevallier } 1576bfc43b68SGatien Chevallier 1577bfc43b68SGatien Chevallier return TEE_SUCCESS; 1578bfc43b68SGatien Chevallier } 1579bfc43b68SGatien Chevallier 1580bfc43b68SGatien Chevallier static TEE_Result stm32_gpio_sec_config_suspend(void) 1581bfc43b68SGatien Chevallier { 1582bfc43b68SGatien Chevallier struct stm32_gpio_bank *bank = NULL; 1583bfc43b68SGatien Chevallier 1584bfc43b68SGatien Chevallier STAILQ_FOREACH(bank, &bank_list, link) { 1585bfc43b68SGatien Chevallier if (bank->rif_cfg) { 1586bfc43b68SGatien Chevallier if (bank->is_tdcid) 1587bfc43b68SGatien Chevallier stm32_gpio_save_rif_config(bank); 1588bfc43b68SGatien Chevallier } else { 1589bfc43b68SGatien Chevallier stm32_gpio_get_conf_sec(bank); 1590bfc43b68SGatien Chevallier } 1591bfc43b68SGatien Chevallier } 1592bfc43b68SGatien Chevallier 1593bfc43b68SGatien Chevallier return TEE_SUCCESS; 1594bfc43b68SGatien Chevallier } 1595bfc43b68SGatien Chevallier 1596bfc43b68SGatien Chevallier static TEE_Result 1597bfc43b68SGatien Chevallier stm32_gpio_sec_config_pm(enum pm_op op, unsigned int pm_hint, 1598bfc43b68SGatien Chevallier const struct pm_callback_handle *hdl __unused) 1599bfc43b68SGatien Chevallier { 1600bfc43b68SGatien Chevallier TEE_Result ret = TEE_ERROR_GENERIC; 1601bfc43b68SGatien Chevallier 1602bfc43b68SGatien Chevallier if (!PM_HINT_IS_STATE(pm_hint, CONTEXT)) 1603bfc43b68SGatien Chevallier return TEE_SUCCESS; 1604bfc43b68SGatien Chevallier 1605bfc43b68SGatien Chevallier if (op == PM_OP_RESUME) 1606bfc43b68SGatien Chevallier ret = stm32_gpio_sec_config_resume(); 1607bfc43b68SGatien Chevallier else 1608bfc43b68SGatien Chevallier ret = stm32_gpio_sec_config_suspend(); 1609bfc43b68SGatien Chevallier 1610bfc43b68SGatien Chevallier return ret; 1611bfc43b68SGatien Chevallier } 1612bfc43b68SGatien Chevallier DECLARE_KEEP_PAGER(stm32_gpio_sec_config_pm); 1613bfc43b68SGatien Chevallier 1614bd03c8c3SGatien Chevallier /* 1615bd03c8c3SGatien Chevallier * Several pinctrl nodes can be probed. Their bank will be put in the unique 1616bd03c8c3SGatien Chevallier * bank_list. To avoid multiple configuration set for a bank when looping 1617bd03c8c3SGatien Chevallier * over each bank in the bank list, ready is set to true when a bank is 1618bd03c8c3SGatien Chevallier * configured. Therefore, during other bank probes, the configuration won't 1619bd03c8c3SGatien Chevallier * be set again. 1620bd03c8c3SGatien Chevallier */ 1621bd03c8c3SGatien Chevallier static TEE_Result apply_sec_cfg(void) 1622bd03c8c3SGatien Chevallier { 1623bd03c8c3SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 1624bd03c8c3SGatien Chevallier struct stm32_gpio_bank *bank = NULL; 1625430c415aSEtienne Carriere unsigned int pin = 0; 1626bd03c8c3SGatien Chevallier 1627bd03c8c3SGatien Chevallier STAILQ_FOREACH(bank, &bank_list, link) { 1628bd03c8c3SGatien Chevallier if (bank->ready) 1629bd03c8c3SGatien Chevallier continue; 1630bd03c8c3SGatien Chevallier 1631bd03c8c3SGatien Chevallier if (bank->rif_cfg) { 1632a72f07daSEtienne Carriere res = apply_rif_config(bank, 1633a72f07daSEtienne Carriere bank->rif_cfg->access_mask[0]); 1634bd03c8c3SGatien Chevallier if (res) { 1635bd03c8c3SGatien Chevallier EMSG("Failed to set GPIO bank %c RIF config", 1636bd03c8c3SGatien Chevallier 'A' + bank->bank_id); 1637bd03c8c3SGatien Chevallier STAILQ_REMOVE(&bank_list, bank, stm32_gpio_bank, 1638bd03c8c3SGatien Chevallier link); 16399def1fb7SGatien Chevallier free(bank); 1640bd03c8c3SGatien Chevallier return res; 1641bd03c8c3SGatien Chevallier } 1642430c415aSEtienne Carriere 1643430c415aSEtienne Carriere /* 1644430c415aSEtienne Carriere * Semaphores for pinctrl and GPIO are taken when 1645430c415aSEtienne Carriere * these are used (pinctrl state applied, GPIO 1646430c415aSEtienne Carriere * consumed) or when an explicit firewall configuration 1647430c415aSEtienne Carriere * is requested through the firewall framework. 1648430c415aSEtienne Carriere * Therefore release here the taken semaphores. 1649430c415aSEtienne Carriere */ 1650430c415aSEtienne Carriere for (pin = 0; pin < bank->ngpios; pin++) 1651430c415aSEtienne Carriere release_rif_semaphore_if_acquired(bank, pin); 1652430c415aSEtienne Carriere 1653bd03c8c3SGatien Chevallier } else { 1654bd03c8c3SGatien Chevallier stm32_gpio_set_conf_sec(bank); 1655bd03c8c3SGatien Chevallier } 1656bd03c8c3SGatien Chevallier 1657bd03c8c3SGatien Chevallier bank->ready = true; 1658bd03c8c3SGatien Chevallier } 1659bd03c8c3SGatien Chevallier 1660bd03c8c3SGatien Chevallier return TEE_SUCCESS; 1661bd03c8c3SGatien Chevallier } 1662bd03c8c3SGatien Chevallier 16630e0435e2SEtienne Carriere static TEE_Result stm32_pinctrl_probe(const void *fdt, int node, 16640e0435e2SEtienne Carriere const void *compat_data) 16650e0435e2SEtienne Carriere { 1666bfc43b68SGatien Chevallier static bool pm_register; 1667b38386fbSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 1668b38386fbSEtienne Carriere 16690e0435e2SEtienne Carriere /* Register GPIO banks described in this pin control node */ 1670b38386fbSEtienne Carriere res = dt_stm32_gpio_pinctrl(fdt, node, compat_data); 1671b38386fbSEtienne Carriere if (res) 1672b38386fbSEtienne Carriere return res; 1673b38386fbSEtienne Carriere 1674bd03c8c3SGatien Chevallier if (STAILQ_EMPTY(&bank_list)) 1675bd03c8c3SGatien Chevallier DMSG("no gpio bank for that driver"); 1676bd03c8c3SGatien Chevallier else if (apply_sec_cfg()) 1677bd03c8c3SGatien Chevallier panic(); 1678bd03c8c3SGatien Chevallier 1679bfc43b68SGatien Chevallier if (!pm_register) { 1680bfc43b68SGatien Chevallier /* 1681bfc43b68SGatien Chevallier * Register to PM once for all probed banks to restore 1682bfc43b68SGatien Chevallier * their secure configuration. 1683bfc43b68SGatien Chevallier */ 1684bfc43b68SGatien Chevallier register_pm_driver_cb(stm32_gpio_sec_config_pm, NULL, 1685bfc43b68SGatien Chevallier "stm32-gpio-secure-config"); 1686bfc43b68SGatien Chevallier pm_register = true; 1687bfc43b68SGatien Chevallier } 1688bfc43b68SGatien Chevallier 1689b38386fbSEtienne Carriere #ifdef CFG_DRIVERS_PINCTRL 1690b38386fbSEtienne Carriere res = pinctrl_register_provider(fdt, node, stm32_pinctrl_dt_get, 1691b38386fbSEtienne Carriere (void *)compat_data); 1692b38386fbSEtienne Carriere if (res) 1693bd03c8c3SGatien Chevallier panic(); 1694b38386fbSEtienne Carriere #endif 1695b38386fbSEtienne Carriere 1696b38386fbSEtienne Carriere return TEE_SUCCESS; 16970e0435e2SEtienne Carriere } 16980e0435e2SEtienne Carriere 16990e0435e2SEtienne Carriere static const struct dt_device_match stm32_pinctrl_match_table[] = { 1700e569f6adSEtienne Carriere { 1701e569f6adSEtienne Carriere .compatible = "st,stm32mp135-pinctrl", 1702b4893304SGatien Chevallier .compat_data = &(struct bank_compat){ 1703b4893304SGatien Chevallier .secure_control = true, 1704bd03c8c3SGatien Chevallier .secure_extended = false, 1705b4893304SGatien Chevallier }, 1706e569f6adSEtienne Carriere }, 1707e569f6adSEtienne Carriere { 1708e569f6adSEtienne Carriere .compatible = "st,stm32mp157-pinctrl", 1709b4893304SGatien Chevallier .compat_data = &(struct bank_compat){ 1710b4893304SGatien Chevallier .secure_control = false, 1711bd03c8c3SGatien Chevallier .secure_extended = false, 1712b4893304SGatien Chevallier }, 1713e569f6adSEtienne Carriere }, 1714e569f6adSEtienne Carriere { 1715e569f6adSEtienne Carriere .compatible = "st,stm32mp157-z-pinctrl", 1716b4893304SGatien Chevallier .compat_data = &(struct bank_compat){ 1717b4893304SGatien Chevallier .gpioz = true, 1718b4893304SGatien Chevallier .secure_control = true, 1719bd03c8c3SGatien Chevallier .secure_extended = false, 1720bd03c8c3SGatien Chevallier }, 1721bd03c8c3SGatien Chevallier }, 1722bd03c8c3SGatien Chevallier { 1723bd03c8c3SGatien Chevallier .compatible = "st,stm32mp257-pinctrl", 1724bd03c8c3SGatien Chevallier .compat_data = &(struct bank_compat){ 1725bd03c8c3SGatien Chevallier .secure_control = true, 1726bd03c8c3SGatien Chevallier .secure_extended = true, 1727bd03c8c3SGatien Chevallier }, 1728bd03c8c3SGatien Chevallier }, 1729bd03c8c3SGatien Chevallier { 1730bd03c8c3SGatien Chevallier .compatible = "st,stm32mp257-z-pinctrl", 1731bd03c8c3SGatien Chevallier .compat_data = &(struct bank_compat){ 1732bd03c8c3SGatien Chevallier .gpioz = true, 1733bd03c8c3SGatien Chevallier .secure_control = true, 1734bd03c8c3SGatien Chevallier .secure_extended = true, 1735b4893304SGatien Chevallier }, 1736e569f6adSEtienne Carriere }, 17370e0435e2SEtienne Carriere { } 17380e0435e2SEtienne Carriere }; 17390e0435e2SEtienne Carriere 17400e0435e2SEtienne Carriere DEFINE_DT_DRIVER(stm32_pinctrl_dt_driver) = { 17410e0435e2SEtienne Carriere .name = "stm32_gpio-pinctrl", 17420e0435e2SEtienne Carriere .type = DT_DRIVER_PINCTRL, 17430e0435e2SEtienne Carriere .match_table = stm32_pinctrl_match_table, 17440e0435e2SEtienne Carriere .probe = stm32_pinctrl_probe, 17450e0435e2SEtienne Carriere }; 1746