xref: /optee_os/core/drivers/stm32_gpio.c (revision 98dfceda3c6e045765735fe78f828e6d45b8e167)
14b5e93edSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
24b5e93edSEtienne Carriere /*
39818a481SEtienne Carriere  * Copyright (c) 2017-2023, STMicroelectronics
44b5e93edSEtienne Carriere  *
54b5e93edSEtienne Carriere  * STM32 GPIO driver is used as pin controller for stm32mp SoCs.
64b5e93edSEtienne Carriere  * The driver API is defined in header file stm32_gpio.h.
74b5e93edSEtienne Carriere  */
84b5e93edSEtienne Carriere 
94b5e93edSEtienne Carriere #include <assert.h>
1097391ffbSEtienne Carriere #include <drivers/clk.h>
1197391ffbSEtienne Carriere #include <drivers/clk_dt.h>
124b5e93edSEtienne Carriere #include <drivers/stm32_gpio.h>
134b5e93edSEtienne Carriere #include <io.h>
144b5e93edSEtienne Carriere #include <kernel/dt.h>
1565401337SJens Wiklander #include <kernel/boot.h>
164b5e93edSEtienne Carriere #include <kernel/panic.h>
174b5e93edSEtienne Carriere #include <kernel/spinlock.h>
18a2fc83d1SJerome Forissier #include <libfdt.h>
194b5e93edSEtienne Carriere #include <mm/core_memprot.h>
204b5e93edSEtienne Carriere #include <stdbool.h>
214b5e93edSEtienne Carriere #include <stm32_util.h>
229818a481SEtienne Carriere #include <sys/queue.h>
234b5e93edSEtienne Carriere #include <trace.h>
244b5e93edSEtienne Carriere #include <util.h>
254b5e93edSEtienne Carriere 
264b5e93edSEtienne Carriere #define GPIO_PIN_MAX		15
274b5e93edSEtienne Carriere 
284b5e93edSEtienne Carriere #define GPIO_MODER_OFFSET	0x00
294b5e93edSEtienne Carriere #define GPIO_OTYPER_OFFSET	0x04
304b5e93edSEtienne Carriere #define GPIO_OSPEEDR_OFFSET	0x08
314b5e93edSEtienne Carriere #define GPIO_PUPDR_OFFSET	0x0c
324b5e93edSEtienne Carriere #define GPIO_IDR_OFFSET		0x10
334b5e93edSEtienne Carriere #define GPIO_ODR_OFFSET		0x14
344b5e93edSEtienne Carriere #define GPIO_BSRR_OFFSET	0x18
354b5e93edSEtienne Carriere #define GPIO_AFRL_OFFSET	0x20
364b5e93edSEtienne Carriere #define GPIO_AFRH_OFFSET	0x24
374b5e93edSEtienne Carriere #define GPIO_SECR_OFFSET	0x30
384b5e93edSEtienne Carriere 
394b5e93edSEtienne Carriere #define GPIO_ALT_LOWER_LIMIT	0x8
404b5e93edSEtienne Carriere 
414b5e93edSEtienne Carriere #define GPIO_MODE_MASK		GENMASK_32(1, 0)
424b5e93edSEtienne Carriere #define GPIO_OSPEED_MASK	GENMASK_32(1, 0)
434b5e93edSEtienne Carriere #define GPIO_PUPD_PULL_MASK	GENMASK_32(1, 0)
44729093d5SLionel Debieve #define GPIO_ALTERNATE_MASK	GENMASK_32(3, 0)
454b5e93edSEtienne Carriere 
464b5e93edSEtienne Carriere #define DT_GPIO_BANK_SHIFT	12
474b5e93edSEtienne Carriere #define DT_GPIO_BANK_MASK	GENMASK_32(16, 12)
484b5e93edSEtienne Carriere #define DT_GPIO_PIN_SHIFT	8
494b5e93edSEtienne Carriere #define DT_GPIO_PIN_MASK	GENMASK_32(11, 8)
504b5e93edSEtienne Carriere #define DT_GPIO_MODE_MASK	GENMASK_32(7, 0)
514b5e93edSEtienne Carriere 
529818a481SEtienne Carriere #define DT_GPIO_BANK_NAME0	"GPIOA"
539818a481SEtienne Carriere 
549818a481SEtienne Carriere /**
559818a481SEtienne Carriere  * struct stm32_gpio_bank - GPIO bank instance
569818a481SEtienne Carriere  *
579818a481SEtienne Carriere  * @base: base address of the GPIO controller registers.
589818a481SEtienne Carriere  * @clock: clock identifier.
599818a481SEtienne Carriere  * @ngpios: number of GPIOs.
609818a481SEtienne Carriere  * @bank_id: Id of the bank.
619818a481SEtienne Carriere  * @lock: lock protecting the GPIO bank access.
629818a481SEtienne Carriere  * @sec_support: True if bank supports pin security protection, otherwise false
639818a481SEtienne Carriere  * @seccfgr: Secure configuration register value.
649818a481SEtienne Carriere  * @link: Link in bank list
659818a481SEtienne Carriere  */
669818a481SEtienne Carriere struct stm32_gpio_bank {
679818a481SEtienne Carriere 	vaddr_t base;
689818a481SEtienne Carriere 	struct clk *clock;
699818a481SEtienne Carriere 	unsigned int ngpios;
709818a481SEtienne Carriere 	unsigned int bank_id;
719818a481SEtienne Carriere 	unsigned int lock;
729818a481SEtienne Carriere 	STAILQ_ENTRY(stm32_gpio_bank) link;
739818a481SEtienne Carriere };
749818a481SEtienne Carriere 
754b5e93edSEtienne Carriere static unsigned int gpio_lock;
764b5e93edSEtienne Carriere 
779818a481SEtienne Carriere static STAILQ_HEAD(, stm32_gpio_bank) bank_list =
789818a481SEtienne Carriere 		STAILQ_HEAD_INITIALIZER(bank_list);
799818a481SEtienne Carriere 
80077d486eSEtienne Carriere static struct stm32_gpio_bank *stm32_gpio_get_bank(unsigned int bank_id)
814b5e93edSEtienne Carriere {
82077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = NULL;
834b5e93edSEtienne Carriere 
84077d486eSEtienne Carriere 	STAILQ_FOREACH(bank, &bank_list, link)
85077d486eSEtienne Carriere 		if (bank_id == bank->bank_id)
86077d486eSEtienne Carriere 			return bank;
87077d486eSEtienne Carriere 
88077d486eSEtienne Carriere 	panic();
89077d486eSEtienne Carriere }
90077d486eSEtienne Carriere 
91077d486eSEtienne Carriere /* Save to output @cfg the current GPIO (@bank_id/@pin) configuration */
92077d486eSEtienne Carriere static void get_gpio_cfg(uint32_t bank_id, uint32_t pin, struct gpio_cfg *cfg)
93077d486eSEtienne Carriere {
94077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
95077d486eSEtienne Carriere 
96077d486eSEtienne Carriere 	if (clk_enable(bank->clock))
97077d486eSEtienne Carriere 		panic();
984b5e93edSEtienne Carriere 
994b5e93edSEtienne Carriere 	/*
1004b5e93edSEtienne Carriere 	 * Save GPIO configuration bits spread over the few bank registers.
1014b5e93edSEtienne Carriere 	 * 1bit fields are accessed at bit position being the pin index.
1024b5e93edSEtienne Carriere 	 * 2bit fields are accessed at bit position being twice the pin index.
1034b5e93edSEtienne Carriere 	 * 4bit fields are accessed at bit position being fourth the pin index
1044b5e93edSEtienne Carriere 	 * but accessed from 2 32bit registers at incremental addresses.
1054b5e93edSEtienne Carriere 	 */
106077d486eSEtienne Carriere 	cfg->mode = (io_read32(bank->base + GPIO_MODER_OFFSET) >> (pin << 1)) &
1074b5e93edSEtienne Carriere 		    GPIO_MODE_MASK;
1084b5e93edSEtienne Carriere 
109077d486eSEtienne Carriere 	cfg->otype = (io_read32(bank->base + GPIO_OTYPER_OFFSET) >> pin) & 1;
1104b5e93edSEtienne Carriere 
111077d486eSEtienne Carriere 	cfg->ospeed = (io_read32(bank->base +  GPIO_OSPEEDR_OFFSET) >>
112077d486eSEtienne Carriere 		       (pin << 1)) & GPIO_OSPEED_MASK;
1134b5e93edSEtienne Carriere 
114077d486eSEtienne Carriere 	cfg->pupd = (io_read32(bank->base +  GPIO_PUPDR_OFFSET) >> (pin << 1)) &
1154b5e93edSEtienne Carriere 		    GPIO_PUPD_PULL_MASK;
1164b5e93edSEtienne Carriere 
117077d486eSEtienne Carriere 	cfg->od = (io_read32(bank->base + GPIO_ODR_OFFSET) >> (pin << 1)) & 1;
1184b5e93edSEtienne Carriere 
1194b5e93edSEtienne Carriere 	if (pin < GPIO_ALT_LOWER_LIMIT)
120077d486eSEtienne Carriere 		cfg->af = (io_read32(bank->base + GPIO_AFRL_OFFSET) >>
121077d486eSEtienne Carriere 			   (pin << 2)) & GPIO_ALTERNATE_MASK;
1224b5e93edSEtienne Carriere 	else
123077d486eSEtienne Carriere 		cfg->af = (io_read32(bank->base + GPIO_AFRH_OFFSET) >>
1244b5e93edSEtienne Carriere 			   ((pin - GPIO_ALT_LOWER_LIMIT) << 2)) &
1254b5e93edSEtienne Carriere 			  GPIO_ALTERNATE_MASK;
1264b5e93edSEtienne Carriere 
127077d486eSEtienne Carriere 	clk_disable(bank->clock);
1284b5e93edSEtienne Carriere }
1294b5e93edSEtienne Carriere 
1304b5e93edSEtienne Carriere /* Apply GPIO (@bank/@pin) configuration described by @cfg */
131077d486eSEtienne Carriere static void set_gpio_cfg(uint32_t bank_id, uint32_t pin, struct gpio_cfg *cfg)
1324b5e93edSEtienne Carriere {
133077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
134*98dfcedaSEtienne Carriere 	uint32_t exceptions = 0;
1354b5e93edSEtienne Carriere 
136077d486eSEtienne Carriere 	if (clk_enable(bank->clock))
137077d486eSEtienne Carriere 		panic();
138*98dfcedaSEtienne Carriere 	exceptions = cpu_spin_lock_xsave(&gpio_lock);
1394b5e93edSEtienne Carriere 
1404b5e93edSEtienne Carriere 	/* Load GPIO MODE value, 2bit value shifted by twice the pin number */
141077d486eSEtienne Carriere 	io_clrsetbits32(bank->base + GPIO_MODER_OFFSET,
142bed4582fSEtienne Carriere 			SHIFT_U32(GPIO_MODE_MASK, pin << 1),
143bed4582fSEtienne Carriere 			SHIFT_U32(cfg->mode, pin << 1));
1444b5e93edSEtienne Carriere 
1454b5e93edSEtienne Carriere 	/* Load GPIO Output TYPE value, 1bit shifted by pin number value */
146077d486eSEtienne Carriere 	io_clrsetbits32(bank->base + GPIO_OTYPER_OFFSET, BIT(pin),
147bed4582fSEtienne Carriere 			SHIFT_U32(cfg->otype, pin));
1484b5e93edSEtienne Carriere 
1494b5e93edSEtienne Carriere 	/* Load GPIO Output Speed confguration, 2bit value */
150077d486eSEtienne Carriere 	io_clrsetbits32(bank->base + GPIO_OSPEEDR_OFFSET,
151bed4582fSEtienne Carriere 			SHIFT_U32(GPIO_OSPEED_MASK, pin << 1),
152bed4582fSEtienne Carriere 			SHIFT_U32(cfg->ospeed, pin << 1));
1534b5e93edSEtienne Carriere 
1544b5e93edSEtienne Carriere 	/* Load GPIO pull configuration, 2bit value */
155077d486eSEtienne Carriere 	io_clrsetbits32(bank->base + GPIO_PUPDR_OFFSET, BIT(pin),
156bed4582fSEtienne Carriere 			SHIFT_U32(cfg->pupd, pin << 1));
1574b5e93edSEtienne Carriere 
1584b5e93edSEtienne Carriere 	/* Load pin mux Alternate Function configuration, 4bit value */
1594b5e93edSEtienne Carriere 	if (pin < GPIO_ALT_LOWER_LIMIT) {
160077d486eSEtienne Carriere 		io_clrsetbits32(bank->base + GPIO_AFRL_OFFSET,
161bed4582fSEtienne Carriere 				SHIFT_U32(GPIO_ALTERNATE_MASK, pin << 2),
162bed4582fSEtienne Carriere 				SHIFT_U32(cfg->af, pin << 2));
1634b5e93edSEtienne Carriere 	} else {
1644b5e93edSEtienne Carriere 		size_t shift = (pin - GPIO_ALT_LOWER_LIMIT) << 2;
1654b5e93edSEtienne Carriere 
166077d486eSEtienne Carriere 		io_clrsetbits32(bank->base + GPIO_AFRH_OFFSET,
167bed4582fSEtienne Carriere 				SHIFT_U32(GPIO_ALTERNATE_MASK, shift),
168bed4582fSEtienne Carriere 				SHIFT_U32(cfg->af, shift));
1694b5e93edSEtienne Carriere 	}
1704b5e93edSEtienne Carriere 
1714b5e93edSEtienne Carriere 	/* Load GPIO Output direction confuguration, 1bit */
172077d486eSEtienne Carriere 	io_clrsetbits32(bank->base + GPIO_ODR_OFFSET, BIT(pin), cfg->od << pin);
1734b5e93edSEtienne Carriere 
174c4cab2bbSEtienne Carriere 	cpu_spin_unlock_xrestore(&gpio_lock, exceptions);
175*98dfcedaSEtienne Carriere 	clk_disable(bank->clock);
1764b5e93edSEtienne Carriere }
1774b5e93edSEtienne Carriere 
1784b5e93edSEtienne Carriere void stm32_pinctrl_load_active_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
1794b5e93edSEtienne Carriere {
18010bcbd6cSEtienne Carriere 	size_t n = 0;
1814b5e93edSEtienne Carriere 
1824b5e93edSEtienne Carriere 	for (n = 0; n < cnt; n++)
1834b5e93edSEtienne Carriere 		set_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
1844b5e93edSEtienne Carriere 			     &pinctrl[n].active_cfg);
1854b5e93edSEtienne Carriere }
1864b5e93edSEtienne Carriere 
1874b5e93edSEtienne Carriere void stm32_pinctrl_load_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
1884b5e93edSEtienne Carriere {
18910bcbd6cSEtienne Carriere 	size_t n = 0;
1904b5e93edSEtienne Carriere 
1914b5e93edSEtienne Carriere 	for (n = 0; n < cnt; n++)
1924b5e93edSEtienne Carriere 		set_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
1934b5e93edSEtienne Carriere 			     &pinctrl[n].standby_cfg);
1944b5e93edSEtienne Carriere }
1954b5e93edSEtienne Carriere 
1964b5e93edSEtienne Carriere void stm32_pinctrl_store_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
1974b5e93edSEtienne Carriere {
19810bcbd6cSEtienne Carriere 	size_t n = 0;
1994b5e93edSEtienne Carriere 
2004b5e93edSEtienne Carriere 	for (n = 0; n < cnt; n++)
2014b5e93edSEtienne Carriere 		get_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
2024b5e93edSEtienne Carriere 			     &pinctrl[n].standby_cfg);
2034b5e93edSEtienne Carriere }
2044b5e93edSEtienne Carriere 
20542f193b6SEtienne Carriere /* Panic if GPIO bank information from platform do not match DTB description */
2064b5e93edSEtienne Carriere static void ckeck_gpio_bank(void *fdt, uint32_t bank, int pinctrl_node)
2074b5e93edSEtienne Carriere {
20810bcbd6cSEtienne Carriere 	int pinctrl_subnode = 0;
2094b5e93edSEtienne Carriere 
2104b5e93edSEtienne Carriere 	fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) {
21110bcbd6cSEtienne Carriere 		const fdt32_t *cuint = NULL;
2124b5e93edSEtienne Carriere 
2134b5e93edSEtienne Carriere 		if (fdt_getprop(fdt, pinctrl_subnode,
2144b5e93edSEtienne Carriere 				"gpio-controller", NULL) == NULL)
2154b5e93edSEtienne Carriere 			continue;
2164b5e93edSEtienne Carriere 
2174b5e93edSEtienne Carriere 		/* Check bank register offset matches platform assumptions */
2184b5e93edSEtienne Carriere 		cuint = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL);
2194b5e93edSEtienne Carriere 		if (fdt32_to_cpu(*cuint) != stm32_get_gpio_bank_offset(bank))
220563f6249SEtienne Carriere 			continue;
2214b5e93edSEtienne Carriere 
2224b5e93edSEtienne Carriere 		/* Check controller is enabled */
223f354a5d8SGatien Chevallier 		if (fdt_get_status(fdt, pinctrl_subnode) == DT_STATUS_DISABLED)
2244b5e93edSEtienne Carriere 			panic();
2254b5e93edSEtienne Carriere 
2264b5e93edSEtienne Carriere 		return;
2274b5e93edSEtienne Carriere 	}
2284b5e93edSEtienne Carriere 
2294b5e93edSEtienne Carriere 	panic();
2304b5e93edSEtienne Carriere }
2314b5e93edSEtienne Carriere 
2324b5e93edSEtienne Carriere /* Count pins described in the DT node and get related data if possible */
2334b5e93edSEtienne Carriere static int get_pinctrl_from_fdt(void *fdt, int node,
2344b5e93edSEtienne Carriere 				struct stm32_pinctrl *pinctrl, size_t count)
2354b5e93edSEtienne Carriere {
2364b5e93edSEtienne Carriere 	const fdt32_t *cuint, *slewrate;
23710bcbd6cSEtienne Carriere 	int len = 0;
23810bcbd6cSEtienne Carriere 	int pinctrl_node = 0;
23910bcbd6cSEtienne Carriere 	uint32_t i = 0;
2404b5e93edSEtienne Carriere 	uint32_t speed = GPIO_OSPEED_LOW;
2414b5e93edSEtienne Carriere 	uint32_t pull = GPIO_PUPD_NO_PULL;
2424b5e93edSEtienne Carriere 	size_t found = 0;
2434b5e93edSEtienne Carriere 
2444b5e93edSEtienne Carriere 	cuint = fdt_getprop(fdt, node, "pinmux", &len);
2454b5e93edSEtienne Carriere 	if (!cuint)
2464b5e93edSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
2474b5e93edSEtienne Carriere 
2484b5e93edSEtienne Carriere 	pinctrl_node = fdt_parent_offset(fdt, fdt_parent_offset(fdt, node));
2494b5e93edSEtienne Carriere 	if (pinctrl_node < 0)
2504b5e93edSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
2514b5e93edSEtienne Carriere 
2524b5e93edSEtienne Carriere 	slewrate = fdt_getprop(fdt, node, "slew-rate", NULL);
2534b5e93edSEtienne Carriere 	if (slewrate)
2544b5e93edSEtienne Carriere 		speed = fdt32_to_cpu(*slewrate);
2554b5e93edSEtienne Carriere 
2564b5e93edSEtienne Carriere 	if (fdt_getprop(fdt, node, "bias-pull-up", NULL))
2574b5e93edSEtienne Carriere 		pull = GPIO_PUPD_PULL_UP;
2584b5e93edSEtienne Carriere 	if (fdt_getprop(fdt, node, "bias-pull-down", NULL))
2594b5e93edSEtienne Carriere 		pull = GPIO_PUPD_PULL_DOWN;
2604b5e93edSEtienne Carriere 
2614b5e93edSEtienne Carriere 	for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
26210bcbd6cSEtienne Carriere 		uint32_t pincfg = 0;
26310bcbd6cSEtienne Carriere 		uint32_t bank = 0;
26410bcbd6cSEtienne Carriere 		uint32_t pin = 0;
26510bcbd6cSEtienne Carriere 		uint32_t mode = 0;
2664b5e93edSEtienne Carriere 		uint32_t alternate = 0;
267322cf9e3SEtienne Carriere 		uint32_t odata = 0;
2684b5e93edSEtienne Carriere 		bool opendrain = false;
2694b5e93edSEtienne Carriere 
2704b5e93edSEtienne Carriere 		pincfg = fdt32_to_cpu(*cuint);
2714b5e93edSEtienne Carriere 		cuint++;
2724b5e93edSEtienne Carriere 
2734b5e93edSEtienne Carriere 		bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT;
2744b5e93edSEtienne Carriere 
2754b5e93edSEtienne Carriere 		pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT;
2764b5e93edSEtienne Carriere 
2774b5e93edSEtienne Carriere 		mode = pincfg & DT_GPIO_MODE_MASK;
2784b5e93edSEtienne Carriere 
2794b5e93edSEtienne Carriere 		switch (mode) {
2804b5e93edSEtienne Carriere 		case 0:
2814b5e93edSEtienne Carriere 			mode = GPIO_MODE_INPUT;
2824b5e93edSEtienne Carriere 			break;
2834b5e93edSEtienne Carriere 		case 1:
2844b5e93edSEtienne Carriere 		case 2:
2854b5e93edSEtienne Carriere 		case 3:
2864b5e93edSEtienne Carriere 		case 4:
2874b5e93edSEtienne Carriere 		case 5:
2884b5e93edSEtienne Carriere 		case 6:
2894b5e93edSEtienne Carriere 		case 7:
2904b5e93edSEtienne Carriere 		case 8:
2914b5e93edSEtienne Carriere 		case 9:
2924b5e93edSEtienne Carriere 		case 10:
2934b5e93edSEtienne Carriere 		case 11:
2944b5e93edSEtienne Carriere 		case 12:
2954b5e93edSEtienne Carriere 		case 13:
2964b5e93edSEtienne Carriere 		case 14:
2974b5e93edSEtienne Carriere 		case 15:
2984b5e93edSEtienne Carriere 		case 16:
2994b5e93edSEtienne Carriere 			alternate = mode - 1U;
3004b5e93edSEtienne Carriere 			mode = GPIO_MODE_ALTERNATE;
3014b5e93edSEtienne Carriere 			break;
3024b5e93edSEtienne Carriere 		case 17:
3034b5e93edSEtienne Carriere 			mode = GPIO_MODE_ANALOG;
3044b5e93edSEtienne Carriere 			break;
3054b5e93edSEtienne Carriere 		default:
3064b5e93edSEtienne Carriere 			mode = GPIO_MODE_OUTPUT;
3074b5e93edSEtienne Carriere 			break;
3084b5e93edSEtienne Carriere 		}
3094b5e93edSEtienne Carriere 
3104b5e93edSEtienne Carriere 		if (fdt_getprop(fdt, node, "drive-open-drain", NULL))
3114b5e93edSEtienne Carriere 			opendrain = true;
3124b5e93edSEtienne Carriere 
313322cf9e3SEtienne Carriere 		if (fdt_getprop(fdt, node, "output-high", NULL) &&
314322cf9e3SEtienne Carriere 		    mode == GPIO_MODE_INPUT) {
315322cf9e3SEtienne Carriere 			mode = GPIO_MODE_OUTPUT;
316322cf9e3SEtienne Carriere 			odata = 1;
317322cf9e3SEtienne Carriere 		}
318322cf9e3SEtienne Carriere 
319322cf9e3SEtienne Carriere 		if (fdt_getprop(fdt, node, "output-low", NULL) &&
320322cf9e3SEtienne Carriere 		    mode == GPIO_MODE_INPUT) {
321322cf9e3SEtienne Carriere 			mode = GPIO_MODE_OUTPUT;
322322cf9e3SEtienne Carriere 			odata = 0;
323322cf9e3SEtienne Carriere 		}
324322cf9e3SEtienne Carriere 
3254b5e93edSEtienne Carriere 		/* Check GPIO bank clock/base address against platform */
3264b5e93edSEtienne Carriere 		ckeck_gpio_bank(fdt, bank, pinctrl_node);
3274b5e93edSEtienne Carriere 
3284b5e93edSEtienne Carriere 		if (found < count) {
3294b5e93edSEtienne Carriere 			struct stm32_pinctrl *ref = &pinctrl[found];
3304b5e93edSEtienne Carriere 
3314b5e93edSEtienne Carriere 			ref->bank = (uint8_t)bank;
3324b5e93edSEtienne Carriere 			ref->pin = (uint8_t)pin;
3334b5e93edSEtienne Carriere 			ref->active_cfg.mode = mode;
3344b5e93edSEtienne Carriere 			ref->active_cfg.otype = opendrain ? 1 : 0;
3354b5e93edSEtienne Carriere 			ref->active_cfg.ospeed = speed;
3364b5e93edSEtienne Carriere 			ref->active_cfg.pupd = pull;
337322cf9e3SEtienne Carriere 			ref->active_cfg.od = odata;
3384b5e93edSEtienne Carriere 			ref->active_cfg.af = alternate;
3394b5e93edSEtienne Carriere 			/* Default to analog mode for standby state */
3404b5e93edSEtienne Carriere 			ref->standby_cfg.mode = GPIO_MODE_ANALOG;
3414b5e93edSEtienne Carriere 			ref->standby_cfg.pupd = GPIO_PUPD_NO_PULL;
3424b5e93edSEtienne Carriere 		}
3434b5e93edSEtienne Carriere 
3444b5e93edSEtienne Carriere 		found++;
3454b5e93edSEtienne Carriere 	}
3464b5e93edSEtienne Carriere 
3474b5e93edSEtienne Carriere 	return (int)found;
3484b5e93edSEtienne Carriere }
3494b5e93edSEtienne Carriere 
3509818a481SEtienne Carriere /* Get bank ID from bank node property st,bank-name or panic on failure */
3519818a481SEtienne Carriere static unsigned int dt_get_bank_id(const void *fdt, int node)
3529818a481SEtienne Carriere {
3539818a481SEtienne Carriere 	const int dt_name_len = strlen(DT_GPIO_BANK_NAME0);
3549818a481SEtienne Carriere 	const fdt32_t *cuint = NULL;
3559818a481SEtienne Carriere 	int len = 0;
3569818a481SEtienne Carriere 
3579818a481SEtienne Carriere 	/* Parse "st,bank-name" to get its id (eg: GPIOA -> 0) */
3589818a481SEtienne Carriere 	cuint = fdt_getprop(fdt, node, "st,bank-name", &len);
3599818a481SEtienne Carriere 	if (!cuint || (len != dt_name_len + 1))
3609818a481SEtienne Carriere 		panic("Missing/wrong st,bank-name property");
3619818a481SEtienne Carriere 
3629818a481SEtienne Carriere 	if (strncmp((const char *)cuint, DT_GPIO_BANK_NAME0, dt_name_len - 1) ||
3639818a481SEtienne Carriere 	    strcmp((const char *)cuint, DT_GPIO_BANK_NAME0) < 0)
3649818a481SEtienne Carriere 		panic("Wrong st,bank-name property");
3659818a481SEtienne Carriere 
3669818a481SEtienne Carriere 	return (unsigned int)strcmp((const char *)cuint, DT_GPIO_BANK_NAME0);
3679818a481SEtienne Carriere }
3689818a481SEtienne Carriere 
3699818a481SEtienne Carriere /*
3709818a481SEtienne Carriere  * Return whether or not the GPIO bank related to a DT node is already
3719818a481SEtienne Carriere  * registered in the GPIO bank link.
3729818a481SEtienne Carriere  */
3739818a481SEtienne Carriere static bool bank_is_registered(const void *fdt, int node)
3749818a481SEtienne Carriere {
3759818a481SEtienne Carriere 	unsigned int bank_id = dt_get_bank_id(fdt, node);
3769818a481SEtienne Carriere 	struct stm32_gpio_bank *bank = NULL;
3779818a481SEtienne Carriere 
3789818a481SEtienne Carriere 	STAILQ_FOREACH(bank, &bank_list, link)
3799818a481SEtienne Carriere 		if (bank->bank_id == bank_id)
3809818a481SEtienne Carriere 			return true;
3819818a481SEtienne Carriere 
3829818a481SEtienne Carriere 	return false;
3839818a481SEtienne Carriere }
3849818a481SEtienne Carriere 
3859818a481SEtienne Carriere /* Get GPIO bank information from the DT */
3869818a481SEtienne Carriere static TEE_Result dt_stm32_gpio_bank(const void *fdt, int node,
3879818a481SEtienne Carriere 				     const void *compat_data __unused,
3889818a481SEtienne Carriere 				     int range_offset,
3899818a481SEtienne Carriere 				     struct stm32_gpio_bank **out_bank)
3909818a481SEtienne Carriere {
3919818a481SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3929818a481SEtienne Carriere 	struct stm32_gpio_bank *bank = NULL;
3939818a481SEtienne Carriere 	const fdt32_t *cuint = NULL;
3949818a481SEtienne Carriere 	struct io_pa_va pa_va = { };
3959818a481SEtienne Carriere 	struct clk *clk = NULL;
3969818a481SEtienne Carriere 	size_t blen = 0;
3979818a481SEtienne Carriere 	paddr_t pa = 0;
3989818a481SEtienne Carriere 	int len = 0;
3999818a481SEtienne Carriere 	int i = 0;
4009818a481SEtienne Carriere 
4019818a481SEtienne Carriere 	assert(out_bank);
4029818a481SEtienne Carriere 
4039818a481SEtienne Carriere 	/* Probe deferrable devices first */
4049818a481SEtienne Carriere 	res = clk_dt_get_by_index(fdt, node, 0, &clk);
4059818a481SEtienne Carriere 	if (res)
4069818a481SEtienne Carriere 		return res;
4079818a481SEtienne Carriere 
4089818a481SEtienne Carriere 	bank = calloc(1, sizeof(*bank));
4099818a481SEtienne Carriere 	if (!bank)
4109818a481SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
4119818a481SEtienne Carriere 
4129818a481SEtienne Carriere 	/*
4139818a481SEtienne Carriere 	 * Do not rely *only* on the "reg" property to get the address,
4149818a481SEtienne Carriere 	 * but consider also the "ranges" translation property
4159818a481SEtienne Carriere 	 */
4169818a481SEtienne Carriere 	pa = fdt_reg_base_address(fdt, node);
4179818a481SEtienne Carriere 	if (pa == DT_INFO_INVALID_REG)
4189818a481SEtienne Carriere 		panic("missing reg property");
4199818a481SEtienne Carriere 
4209818a481SEtienne Carriere 	pa_va.pa = pa + range_offset;
4219818a481SEtienne Carriere 
4229818a481SEtienne Carriere 	blen = fdt_reg_size(fdt, node);
4239818a481SEtienne Carriere 	if (blen == DT_INFO_INVALID_REG_SIZE)
4249818a481SEtienne Carriere 		panic("missing reg size property");
4259818a481SEtienne Carriere 
4269818a481SEtienne Carriere 	DMSG("Bank name %s", fdt_get_name(fdt, node, NULL));
4279818a481SEtienne Carriere 	bank->base = io_pa_or_va_secure(&pa_va, blen);
4289818a481SEtienne Carriere 	bank->bank_id = dt_get_bank_id(fdt, node);
4299818a481SEtienne Carriere 	bank->clock = clk;
4309818a481SEtienne Carriere 
4319818a481SEtienne Carriere 	/* Parse gpio-ranges with its 4 parameters */
4329818a481SEtienne Carriere 	cuint = fdt_getprop(fdt, node, "gpio-ranges", &len);
4339818a481SEtienne Carriere 	len /= sizeof(*cuint);
4349818a481SEtienne Carriere 	if (len % 4)
4359818a481SEtienne Carriere 		panic("wrong gpio-ranges syntax");
4369818a481SEtienne Carriere 
4379818a481SEtienne Carriere 	/* Get the last defined gpio line (offset + nb of pins) */
4389818a481SEtienne Carriere 	for (i = 0; i < len / 4; i++) {
4399818a481SEtienne Carriere 		bank->ngpios = MAX(bank->ngpios,
4409818a481SEtienne Carriere 				   (unsigned int)(fdt32_to_cpu(*(cuint + 1)) +
4419818a481SEtienne Carriere 						  fdt32_to_cpu(*(cuint + 3))));
4429818a481SEtienne Carriere 		cuint += 4;
4439818a481SEtienne Carriere 	}
4449818a481SEtienne Carriere 
4459818a481SEtienne Carriere 	*out_bank = bank;
4469818a481SEtienne Carriere 	return TEE_SUCCESS;
4479818a481SEtienne Carriere }
4489818a481SEtienne Carriere 
4499818a481SEtienne Carriere /* Parse a pinctrl node to register the GPIO banks it describes */
4500e0435e2SEtienne Carriere static TEE_Result dt_stm32_gpio_pinctrl(const void *fdt, int node,
4519818a481SEtienne Carriere 					const void *compat_data)
4529818a481SEtienne Carriere {
4539818a481SEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
4549818a481SEtienne Carriere 	const fdt32_t *cuint = NULL;
4559818a481SEtienne Carriere 	int range_offs = 0;
4569818a481SEtienne Carriere 	int b_node = 0;
4579818a481SEtienne Carriere 	int len = 0;
4589818a481SEtienne Carriere 
4599818a481SEtienne Carriere 	/* Read the ranges property (for regs memory translation) */
4609818a481SEtienne Carriere 	cuint = fdt_getprop(fdt, node, "ranges", &len);
4619818a481SEtienne Carriere 	if (!cuint)
4629818a481SEtienne Carriere 		panic("missing ranges property");
4639818a481SEtienne Carriere 
4649818a481SEtienne Carriere 	len /= sizeof(*cuint);
4659818a481SEtienne Carriere 	if (len == 3)
4669818a481SEtienne Carriere 		range_offs = fdt32_to_cpu(*(cuint + 1)) - fdt32_to_cpu(*cuint);
4679818a481SEtienne Carriere 
4689818a481SEtienne Carriere 	fdt_for_each_subnode(b_node, fdt, node) {
4699818a481SEtienne Carriere 		cuint = fdt_getprop(fdt, b_node, "gpio-controller", &len);
4709818a481SEtienne Carriere 		if (cuint) {
4719818a481SEtienne Carriere 			/*
4729818a481SEtienne Carriere 			 * We found a property "gpio-controller" in the node:
4739818a481SEtienne Carriere 			 * the node is a GPIO bank description, add it to the
4749818a481SEtienne Carriere 			 * bank list.
4759818a481SEtienne Carriere 			 */
4769818a481SEtienne Carriere 			struct stm32_gpio_bank *bank = NULL;
4779818a481SEtienne Carriere 
4789818a481SEtienne Carriere 			if (fdt_get_status(fdt, b_node) == DT_STATUS_DISABLED ||
4799818a481SEtienne Carriere 			    bank_is_registered(fdt, b_node))
4809818a481SEtienne Carriere 				continue;
4819818a481SEtienne Carriere 
4829818a481SEtienne Carriere 			res = dt_stm32_gpio_bank(fdt, b_node, compat_data,
4839818a481SEtienne Carriere 						 range_offs, &bank);
4849818a481SEtienne Carriere 			if (res)
4859818a481SEtienne Carriere 				return res;
4869818a481SEtienne Carriere 
4879818a481SEtienne Carriere 			STAILQ_INSERT_TAIL(&bank_list, bank, link);
4889818a481SEtienne Carriere 		} else {
4899818a481SEtienne Carriere 			if (len != -FDT_ERR_NOTFOUND)
4909818a481SEtienne Carriere 				panic();
4919818a481SEtienne Carriere 		}
4929818a481SEtienne Carriere 	}
4939818a481SEtienne Carriere 
4949818a481SEtienne Carriere 	return TEE_SUCCESS;
4959818a481SEtienne Carriere }
4969818a481SEtienne Carriere 
4974b5e93edSEtienne Carriere int stm32_pinctrl_fdt_get_pinctrl(void *fdt, int device_node,
4984b5e93edSEtienne Carriere 				  struct stm32_pinctrl *pinctrl, size_t count)
4994b5e93edSEtienne Carriere {
50010bcbd6cSEtienne Carriere 	const fdt32_t *cuint = NULL;
50110bcbd6cSEtienne Carriere 	int lenp = 0;
50210bcbd6cSEtienne Carriere 	int i = 0;
5034b5e93edSEtienne Carriere 	size_t found = 0;
5044b5e93edSEtienne Carriere 
5054b5e93edSEtienne Carriere 	cuint = fdt_getprop(fdt, device_node, "pinctrl-0", &lenp);
5064b5e93edSEtienne Carriere 	if (!cuint)
5074b5e93edSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
5084b5e93edSEtienne Carriere 
5094b5e93edSEtienne Carriere 	for (i = 0; i < (lenp / 4); i++) {
51010bcbd6cSEtienne Carriere 		int node = 0;
51110bcbd6cSEtienne Carriere 		int subnode = 0;
5124b5e93edSEtienne Carriere 
5134b5e93edSEtienne Carriere 		node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint));
5144b5e93edSEtienne Carriere 		if (node < 0)
5154b5e93edSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
5164b5e93edSEtienne Carriere 
5174b5e93edSEtienne Carriere 		fdt_for_each_subnode(subnode, fdt, node) {
51810bcbd6cSEtienne Carriere 			size_t n = 0;
51910bcbd6cSEtienne Carriere 			int rc = 0;
5204b5e93edSEtienne Carriere 
5214b5e93edSEtienne Carriere 			if (count > found)
5224b5e93edSEtienne Carriere 				n = count - found;
5234b5e93edSEtienne Carriere 			else
5244b5e93edSEtienne Carriere 				n = 0;
5254b5e93edSEtienne Carriere 
5264b5e93edSEtienne Carriere 			rc = get_pinctrl_from_fdt(fdt, subnode,
5274b5e93edSEtienne Carriere 						  &pinctrl[found], n);
5284b5e93edSEtienne Carriere 			if (rc < 0)
5294b5e93edSEtienne Carriere 				return rc;
5304b5e93edSEtienne Carriere 
5314b5e93edSEtienne Carriere 			found += (size_t)rc;
5324b5e93edSEtienne Carriere 		}
5334b5e93edSEtienne Carriere 
5344b5e93edSEtienne Carriere 		cuint++;
5354b5e93edSEtienne Carriere 	}
5364b5e93edSEtienne Carriere 
5374b5e93edSEtienne Carriere 	return (int)found;
5384b5e93edSEtienne Carriere }
539a3104caaSEtienne Carriere 
540a3104caaSEtienne Carriere int stm32_get_gpio_count(void *fdt, int pinctrl_node, unsigned int bank)
541a3104caaSEtienne Carriere {
542a3104caaSEtienne Carriere 	int node = 0;
543a3104caaSEtienne Carriere 	const fdt32_t *cuint = NULL;
544a3104caaSEtienne Carriere 
545a3104caaSEtienne Carriere 	fdt_for_each_subnode(node, fdt, pinctrl_node) {
546a3104caaSEtienne Carriere 		if (!fdt_getprop(fdt, node, "gpio-controller", NULL))
547a3104caaSEtienne Carriere 			continue;
548a3104caaSEtienne Carriere 
549a3104caaSEtienne Carriere 		cuint = fdt_getprop(fdt, node, "reg", NULL);
550a3104caaSEtienne Carriere 		if (!cuint)
551a3104caaSEtienne Carriere 			continue;
552a3104caaSEtienne Carriere 
553a3104caaSEtienne Carriere 		if (fdt32_to_cpu(*cuint) != stm32_get_gpio_bank_offset(bank))
554a3104caaSEtienne Carriere 			continue;
555a3104caaSEtienne Carriere 
556a3104caaSEtienne Carriere 		cuint = fdt_getprop(fdt, node, "ngpios", NULL);
557a3104caaSEtienne Carriere 		if (!cuint)
558a3104caaSEtienne Carriere 			panic();
559a3104caaSEtienne Carriere 
560a3104caaSEtienne Carriere 		return (int)fdt32_to_cpu(*cuint);
561a3104caaSEtienne Carriere 	}
562a3104caaSEtienne Carriere 
563a3104caaSEtienne Carriere 	return -1;
564a3104caaSEtienne Carriere }
5654b5e93edSEtienne Carriere 
566077d486eSEtienne Carriere static __maybe_unused bool valid_gpio_config(unsigned int bank_id,
5674b5e93edSEtienne Carriere 					     unsigned int pin, bool input)
5684b5e93edSEtienne Carriere {
569077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
570077d486eSEtienne Carriere 	uint32_t mode = (io_read32(bank->base + GPIO_MODER_OFFSET) >>
571077d486eSEtienne Carriere 			 (pin << 1)) & GPIO_MODE_MASK;
5724b5e93edSEtienne Carriere 
5734b5e93edSEtienne Carriere 	if (pin > GPIO_PIN_MAX)
5744b5e93edSEtienne Carriere 		return false;
5754b5e93edSEtienne Carriere 
5764b5e93edSEtienne Carriere 	if (input)
5774b5e93edSEtienne Carriere 		return mode == GPIO_MODE_INPUT;
5784b5e93edSEtienne Carriere 	else
5794b5e93edSEtienne Carriere 		return mode == GPIO_MODE_OUTPUT;
5804b5e93edSEtienne Carriere }
5814b5e93edSEtienne Carriere 
582077d486eSEtienne Carriere int stm32_gpio_get_input_level(unsigned int bank_id, unsigned int pin)
5834b5e93edSEtienne Carriere {
584077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
5854b5e93edSEtienne Carriere 	int rc = 0;
5864b5e93edSEtienne Carriere 
587077d486eSEtienne Carriere 	if (clk_enable(bank->clock))
588077d486eSEtienne Carriere 		panic();
5894b5e93edSEtienne Carriere 
590077d486eSEtienne Carriere 	assert(valid_gpio_config(bank_id, pin, true));
5916fdc9662SLoïc Bauer 
592077d486eSEtienne Carriere 	if (io_read32(bank->base + GPIO_IDR_OFFSET) == BIT(pin))
5934b5e93edSEtienne Carriere 		rc = 1;
5944b5e93edSEtienne Carriere 
595077d486eSEtienne Carriere 	clk_disable(bank->clock);
5964b5e93edSEtienne Carriere 
5974b5e93edSEtienne Carriere 	return rc;
5984b5e93edSEtienne Carriere }
5994b5e93edSEtienne Carriere 
600077d486eSEtienne Carriere void stm32_gpio_set_output_level(unsigned int bank_id, unsigned int pin,
601077d486eSEtienne Carriere 				 int level)
6024b5e93edSEtienne Carriere {
603077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
6044b5e93edSEtienne Carriere 
605077d486eSEtienne Carriere 	if (clk_enable(bank->clock))
606077d486eSEtienne Carriere 		panic();
6074b5e93edSEtienne Carriere 
608077d486eSEtienne Carriere 	assert(valid_gpio_config(bank_id, pin, false));
6096fdc9662SLoïc Bauer 
6104b5e93edSEtienne Carriere 	if (level)
611077d486eSEtienne Carriere 		io_write32(bank->base + GPIO_BSRR_OFFSET, BIT(pin));
6124b5e93edSEtienne Carriere 	else
613077d486eSEtienne Carriere 		io_write32(bank->base + GPIO_BSRR_OFFSET, BIT(pin + 16));
6144b5e93edSEtienne Carriere 
615077d486eSEtienne Carriere 	clk_disable(bank->clock);
6164b5e93edSEtienne Carriere }
6174b5e93edSEtienne Carriere 
618077d486eSEtienne Carriere void stm32_gpio_set_secure_cfg(unsigned int bank_id, unsigned int pin,
619077d486eSEtienne Carriere 			       bool secure)
6204b5e93edSEtienne Carriere {
621077d486eSEtienne Carriere 	struct stm32_gpio_bank *bank = stm32_gpio_get_bank(bank_id);
622*98dfcedaSEtienne Carriere 	uint32_t exceptions = 0;
6234b5e93edSEtienne Carriere 
624077d486eSEtienne Carriere 	if (clk_enable(bank->clock))
625077d486eSEtienne Carriere 		panic();
626*98dfcedaSEtienne Carriere 	exceptions = cpu_spin_lock_xsave(&gpio_lock);
6274b5e93edSEtienne Carriere 
6284b5e93edSEtienne Carriere 	if (secure)
629077d486eSEtienne Carriere 		io_setbits32(bank->base + GPIO_SECR_OFFSET, BIT(pin));
6304b5e93edSEtienne Carriere 	else
631077d486eSEtienne Carriere 		io_clrbits32(bank->base + GPIO_SECR_OFFSET, BIT(pin));
6324b5e93edSEtienne Carriere 
633c4cab2bbSEtienne Carriere 	cpu_spin_unlock_xrestore(&gpio_lock, exceptions);
634*98dfcedaSEtienne Carriere 	clk_disable(bank->clock);
6354b5e93edSEtienne Carriere }
6360e0435e2SEtienne Carriere 
6370e0435e2SEtienne Carriere static TEE_Result stm32_pinctrl_probe(const void *fdt, int node,
6380e0435e2SEtienne Carriere 				      const void *compat_data)
6390e0435e2SEtienne Carriere {
6400e0435e2SEtienne Carriere 	/* Register GPIO banks described in this pin control node */
6410e0435e2SEtienne Carriere 	return dt_stm32_gpio_pinctrl(fdt, node, compat_data);
6420e0435e2SEtienne Carriere }
6430e0435e2SEtienne Carriere 
6440e0435e2SEtienne Carriere static const struct dt_device_match stm32_pinctrl_match_table[] = {
6450e0435e2SEtienne Carriere 	{ .compatible = "st,stm32mp135-pinctrl" },
6460e0435e2SEtienne Carriere 	{ .compatible = "st,stm32mp157-pinctrl" },
6470e0435e2SEtienne Carriere 	{ .compatible = "st,stm32mp157-z-pinctrl" },
6480e0435e2SEtienne Carriere 	{ }
6490e0435e2SEtienne Carriere };
6500e0435e2SEtienne Carriere 
6510e0435e2SEtienne Carriere DEFINE_DT_DRIVER(stm32_pinctrl_dt_driver) = {
6520e0435e2SEtienne Carriere 	.name = "stm32_gpio-pinctrl",
6530e0435e2SEtienne Carriere 	.type = DT_DRIVER_PINCTRL,
6540e0435e2SEtienne Carriere 	.match_table = stm32_pinctrl_match_table,
6550e0435e2SEtienne Carriere 	.probe = stm32_pinctrl_probe,
6560e0435e2SEtienne Carriere };
657