xref: /optee_os/core/drivers/pinctrl/atmel_pio.c (revision 7a6bbd59ef0b1c1578c15db348f6cca9208635e3)
11914ae05SClément Léger // SPDX-License-Identifier: BSD-2-Clause
21914ae05SClément Léger /*
31914ae05SClément Léger  * Copyright 2019 Microchip.
41914ae05SClément Léger  */
51914ae05SClément Léger #include <assert.h>
61914ae05SClément Léger #include <drivers/clk.h>
71914ae05SClément Léger #include <drivers/clk_dt.h>
81914ae05SClément Léger #include <drivers/pinctrl.h>
91914ae05SClément Léger #include <initcall.h>
101914ae05SClément Léger #include <io.h>
111914ae05SClément Léger #include <kernel/dt.h>
121914ae05SClément Léger #include <libfdt.h>
131914ae05SClément Léger #include <malloc.h>
141914ae05SClément Léger #include <matrix.h>
151914ae05SClément Léger #include <platform_config.h>
161914ae05SClément Léger #include <trace.h>
171914ae05SClément Léger #include <util.h>
181914ae05SClément Léger 
191914ae05SClément Léger #define PIO_GROUP_COUNT		4
201914ae05SClément Léger #define PIO_GROUP_OFFSET	0x40
211914ae05SClément Léger #define PIO_REG(reg, group)	((reg) + ((group) * PIO_GROUP_OFFSET))
221914ae05SClément Léger /* Mask register */
231914ae05SClément Léger #define PIO_MSKR(group)		PIO_REG(0x0, (group))
241914ae05SClément Léger /* Configuration register */
251914ae05SClément Léger #define PIO_CFGR(group)		PIO_REG(0x4, (group))
261914ae05SClément Léger #define PIO_CFGR_FUNC		GENMASK(2, 0)
271914ae05SClément Léger #define PIO_CFGR_PUEN		BIT(9)
281914ae05SClément Léger #define PIO_CFGR_PDEN		BIT(10)
291914ae05SClément Léger 
301914ae05SClément Léger /* Non-Secure configuration register */
311914ae05SClément Léger #define PIO_SIONR(group)	PIO_REG(0x30, (group))
321914ae05SClément Léger /* Secure configuration register */
331914ae05SClément Léger #define PIO_SIOSR(group)	PIO_REG(0x34, (group))
341914ae05SClément Léger 
351914ae05SClément Léger #define DT_GET_PIN_NO(val)	((val) & 0xFF)
361914ae05SClément Léger #define DT_GET_FUNC(val)	(((val) >> 16) & 0xF)
371914ae05SClément Léger 
381914ae05SClément Léger struct atmel_pio {
391914ae05SClément Léger 	vaddr_t base;
401914ae05SClément Léger };
411914ae05SClément Léger 
421914ae05SClément Léger struct atmel_pio_pin_conf {
431914ae05SClément Léger 	uint32_t pin_mask;
441914ae05SClément Léger 	uint32_t pin_cfg;
451914ae05SClément Léger 	uint8_t pio_group;
461914ae05SClément Léger 	struct atmel_pio *pio;
471914ae05SClément Léger };
481914ae05SClément Léger 
pio_write(struct atmel_pio * pio,unsigned int offset,uint32_t val)491914ae05SClément Léger static void pio_write(struct atmel_pio *pio, unsigned int offset, uint32_t val)
501914ae05SClément Léger {
511914ae05SClément Léger 	io_write32(pio->base + offset, val);
521914ae05SClément Léger }
531914ae05SClément Léger 
pio_conf_apply(struct pinconf * conf)541914ae05SClément Léger static TEE_Result pio_conf_apply(struct pinconf *conf)
551914ae05SClément Léger {
561914ae05SClément Léger 	struct atmel_pio_pin_conf *pio_conf = conf->priv;
571914ae05SClément Léger 	struct atmel_pio *pio = pio_conf->pio;
581914ae05SClément Léger 
591914ae05SClément Léger 	DMSG("Apply cfg %#" PRIx32 " on group %" PRIu8 ", pins %#" PRIx32,
601914ae05SClément Léger 	     pio_conf->pin_cfg, pio_conf->pio_group, pio_conf->pin_mask);
611914ae05SClément Léger 
621914ae05SClément Léger 	pio_write(pio, PIO_SIOSR(pio_conf->pio_group), pio_conf->pin_mask);
631914ae05SClément Léger 	pio_write(pio, PIO_MSKR(pio_conf->pio_group), pio_conf->pin_mask);
641914ae05SClément Léger 	pio_write(pio, PIO_CFGR(pio_conf->pio_group), pio_conf->pin_cfg);
651914ae05SClément Léger 
661914ae05SClément Léger 	return TEE_SUCCESS;
671914ae05SClément Léger }
681914ae05SClément Léger 
pio_conf_free(struct pinconf * conf)691914ae05SClément Léger static void pio_conf_free(struct pinconf *conf)
701914ae05SClément Léger {
711914ae05SClément Léger 	free(conf);
721914ae05SClément Léger }
731914ae05SClément Léger 
741914ae05SClément Léger static const struct pinctrl_ops pio_pinctrl_ops = {
751914ae05SClément Léger 	.conf_apply = pio_conf_apply,
761914ae05SClément Léger 	.conf_free = pio_conf_free,
771914ae05SClément Léger };
781914ae05SClément Léger 
pio_pinctrl_dt_get(struct dt_pargs * pargs,void * data,struct pinconf ** out_pinconf)79b357d34fSEtienne Carriere static TEE_Result pio_pinctrl_dt_get(struct dt_pargs *pargs, void *data,
80b357d34fSEtienne Carriere 				     struct pinconf **out_pinconf)
811914ae05SClément Léger {
82b357d34fSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
831914ae05SClément Léger 	int i = 0;
841914ae05SClément Léger 	int func = 0;
851914ae05SClément Léger 	int group = 0;
861914ae05SClément Léger 	int pin_no = 0;
871914ae05SClément Léger 	uint32_t cfg = 0;
881914ae05SClément Léger 	int prop_count = 0;
891914ae05SClément Léger 	int pio_group = -1;
901914ae05SClément Léger 	uint32_t pinmux = 0;
911914ae05SClément Léger 	uint32_t pin_mask = 0;
921914ae05SClément Léger 	bitstr_t *cfg_modes = NULL;
931914ae05SClément Léger 	const uint32_t *prop = NULL;
941914ae05SClément Léger 	struct pinconf *pinconf = NULL;
951914ae05SClément Léger 	struct atmel_pio *atmel_pio = data;
961914ae05SClément Léger 	struct atmel_pio_pin_conf *pio_conf = NULL;
971914ae05SClément Léger 
988fd620f7SEtienne Carriere 	prop = fdt_getprop(pargs->fdt, pargs->phandle_node, "pinmux",
998fd620f7SEtienne Carriere 			   &prop_count);
100b357d34fSEtienne Carriere 	if (!prop)
101b357d34fSEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
1021914ae05SClément Léger 
1031914ae05SClément Léger 	prop_count /= sizeof(uint32_t);
1041914ae05SClément Léger 	for (i = 0; i < prop_count; i++) {
1051914ae05SClément Léger 		pinmux = fdt32_to_cpu(prop[i]);
1061914ae05SClément Léger 
1071914ae05SClément Léger 		pin_no = DT_GET_PIN_NO(pinmux);
1081914ae05SClément Léger 		func = DT_GET_FUNC(pinmux);
1091914ae05SClément Léger 
1101914ae05SClément Léger 		group = pin_no / 32;
1111914ae05SClément Léger 		if (pio_group == -1) {
1121914ae05SClément Léger 			pio_group = group;
1131914ae05SClément Léger 		} else {
1141914ae05SClément Léger 			if (group != pio_group) {
1151914ae05SClément Léger 				EMSG("Unexpected group %d vs %d", group,
1161914ae05SClément Léger 				     pio_group);
117b357d34fSEtienne Carriere 				return TEE_ERROR_GENERIC;
1181914ae05SClément Léger 			}
1191914ae05SClément Léger 		}
1201914ae05SClément Léger 
1211914ae05SClément Léger 		pin_mask |= BIT(pin_no % 32);
1221914ae05SClément Léger 	}
1231914ae05SClément Léger 
1241914ae05SClément Léger 	cfg = func;
1251914ae05SClément Léger 
126b357d34fSEtienne Carriere 	res = pinctrl_parse_dt_pin_modes(pargs->fdt, pargs->phandle_node,
1278fd620f7SEtienne Carriere 					 &cfg_modes);
128b357d34fSEtienne Carriere 	if (res)
129b357d34fSEtienne Carriere 		return res;
1301914ae05SClément Léger 
1311914ae05SClément Léger 	for (i = 0; i < PINCTRL_DT_PROP_MAX; i++) {
1321914ae05SClément Léger 		if (!bit_test(cfg_modes, i))
1331914ae05SClément Léger 			continue;
1341914ae05SClément Léger 
1351914ae05SClément Léger 		switch (i) {
1361914ae05SClément Léger 		case PINCTRL_DT_PROP_BIAS_PULL_UP:
1371914ae05SClément Léger 			cfg |= PIO_CFGR_PUEN;
1381914ae05SClément Léger 			cfg &= ~PIO_CFGR_PDEN;
1391914ae05SClément Léger 			break;
1401914ae05SClément Léger 		case PINCTRL_DT_PROP_BIAS_PULL_DOWN:
1411914ae05SClément Léger 			cfg |= PIO_CFGR_PDEN;
1421914ae05SClément Léger 			cfg &= ~PIO_CFGR_PUEN;
1431914ae05SClément Léger 			break;
1441914ae05SClément Léger 		case PINCTRL_DT_PROP_BIAS_DISABLE:
1451914ae05SClément Léger 			break;
1461914ae05SClément Léger 		default:
1471914ae05SClément Léger 			EMSG("Unhandled config %u", i);
1481914ae05SClément Léger 			break;
1491914ae05SClément Léger 		}
1501914ae05SClément Léger 	}
1511914ae05SClément Léger 
1521914ae05SClément Léger 	free(cfg_modes);
1531914ae05SClément Léger 
1541914ae05SClément Léger 	pinconf = calloc(1, sizeof(*pinconf) + sizeof(*pio_conf));
155b357d34fSEtienne Carriere 	if (!pinconf)
156b357d34fSEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
1571914ae05SClément Léger 
1581914ae05SClément Léger 	pio_conf = (struct atmel_pio_pin_conf *)(pinconf + 1);
1591914ae05SClément Léger 
1601914ae05SClément Léger 	pio_conf->pin_mask = pin_mask;
1611914ae05SClément Léger 	pio_conf->pin_cfg = cfg;
1621914ae05SClément Léger 	pio_conf->pio = atmel_pio;
1631914ae05SClément Léger 	pio_conf->pio_group = pio_group;
1641914ae05SClément Léger 	pinconf->priv = pio_conf;
1651914ae05SClément Léger 	pinconf->ops = &pio_pinctrl_ops;
1661914ae05SClément Léger 
167b357d34fSEtienne Carriere 	*out_pinconf = pinconf;
1681914ae05SClément Léger 
169b357d34fSEtienne Carriere 	return TEE_SUCCESS;
1701914ae05SClément Léger }
1711914ae05SClément Léger 
pio_init_hw(struct atmel_pio * pio)1721914ae05SClément Léger static void pio_init_hw(struct atmel_pio *pio)
1731914ae05SClément Léger {
1741914ae05SClément Léger 	int i = 0;
1751914ae05SClément Léger 
1761914ae05SClément Léger 	/* Set all IOs as non-secure */
1771914ae05SClément Léger 	for (i = 0; i < PIO_GROUP_COUNT; i++)
1781914ae05SClément Léger 		pio_write(pio, PIO_SIONR(PIO_GROUP_COUNT), GENMASK_32(31, 0));
1791914ae05SClément Léger }
1801914ae05SClément Léger 
181*7a6bbd59STony Han /* Non-null reference for compat data */
182*7a6bbd59STony Han static const uint8_t has_pioe;
183*7a6bbd59STony Han 
pio_node_probe(const void * fdt,int node,const void * compat_data)1841914ae05SClément Léger static TEE_Result pio_node_probe(const void *fdt, int node,
185*7a6bbd59STony Han 				 const void *compat_data)
1861914ae05SClément Léger {
1871914ae05SClément Léger 	size_t size = 0;
1881914ae05SClément Léger 	struct clk *clk = NULL;
1891914ae05SClément Léger 	struct atmel_pio *pio = NULL;
1901914ae05SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
1911914ae05SClément Léger 
1921914ae05SClément Léger 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
1931914ae05SClément Léger 		return TEE_ERROR_BAD_STATE;
1941914ae05SClément Léger 
1951914ae05SClément Léger 	pio = calloc(1, sizeof(*pio));
1961914ae05SClément Léger 	if (!pio)
1971914ae05SClément Léger 		return TEE_ERROR_OUT_OF_MEMORY;
1981914ae05SClément Léger 
1991914ae05SClément Léger 	res = clk_dt_get_by_index(fdt, node, 0, &clk);
2001914ae05SClément Léger 	if (res)
2011914ae05SClément Léger 		goto free_pio;
2021914ae05SClément Léger 
2031914ae05SClément Léger 	if (dt_map_dev(fdt, node, &pio->base, &size, DT_MAP_AUTO) < 0)
2041914ae05SClément Léger 		goto free_pio;
2051914ae05SClément Léger 
2061914ae05SClément Léger 	res = clk_enable(clk);
2071914ae05SClément Léger 	if (res)
2081914ae05SClément Léger 		goto free_pio;
2091914ae05SClément Léger 
2101914ae05SClément Léger 	matrix_configure_periph_secure(AT91C_ID_PIOA);
2111914ae05SClément Léger 	matrix_configure_periph_secure(AT91C_ID_PIOB);
2121914ae05SClément Léger 	matrix_configure_periph_secure(AT91C_ID_PIOC);
2131914ae05SClément Léger 	matrix_configure_periph_secure(AT91C_ID_PIOD);
2141914ae05SClément Léger 
215*7a6bbd59STony Han 	if (compat_data == &has_pioe)
216*7a6bbd59STony Han 		matrix_configure_periph_secure(AT91C_ID_PIOD + 1);
217*7a6bbd59STony Han 
2181914ae05SClément Léger 	pio_init_hw(pio);
2191914ae05SClément Léger 
2201914ae05SClément Léger 	res = pinctrl_register_provider(fdt, node, pio_pinctrl_dt_get, pio);
2211914ae05SClément Léger 	if (res)
2221914ae05SClément Léger 		goto disable_clock;
2231914ae05SClément Léger 
2241914ae05SClément Léger 	return TEE_SUCCESS;
2251914ae05SClément Léger 
2261914ae05SClément Léger disable_clock:
2271914ae05SClément Léger 	clk_disable(clk);
2281914ae05SClément Léger free_pio:
2291914ae05SClément Léger 	free(pio);
2301914ae05SClément Léger 
2311914ae05SClément Léger 	return res;
2321914ae05SClément Léger }
2331914ae05SClément Léger 
2341914ae05SClément Léger static const struct dt_device_match atmel_pio_match_table[] = {
2351914ae05SClément Léger 	{ .compatible = "atmel,sama5d2-pinctrl" },
236*7a6bbd59STony Han 	{
237*7a6bbd59STony Han 		.compatible = "microchip,sama7g5-pinctrl",
238*7a6bbd59STony Han 		.compat_data = &has_pioe,
239*7a6bbd59STony Han 	},
2401914ae05SClément Léger 	{ }
2411914ae05SClément Léger };
2421914ae05SClément Léger 
2431914ae05SClément Léger DEFINE_DT_DRIVER(atmel_pio_dt_driver) = {
2441914ae05SClément Léger 	.name = "atmel_pio",
2451914ae05SClément Léger 	.type = DT_DRIVER_PINCTRL,
2461914ae05SClément Léger 	.match_table = atmel_pio_match_table,
2471914ae05SClément Léger 	.probe = pio_node_probe,
2481914ae05SClément Léger };
249