11fe98f82SClément Léger // SPDX-License-Identifier: BSD-2-Clause
21fe98f82SClément Léger /*
31fe98f82SClément Léger * Copyright 2022 Microchip
41fe98f82SClément Léger *
51fe98f82SClément Léger * Driver for AT91 PIOBU
61fe98f82SClément Léger */
71fe98f82SClément Léger
81fe98f82SClément Léger #include <assert.h>
91fe98f82SClément Léger #include <drivers/atmel_rtc.h>
106dcd18c8SClément Léger #include <drivers/gpio.h>
111fe98f82SClément Léger #include <dt-bindings/gpio/atmel,piobu.h>
121fe98f82SClément Léger #include <io.h>
131fe98f82SClément Léger #include <kernel/boot.h>
141fe98f82SClément Léger #include <kernel/dt.h>
159e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
1633a0c835SEtienne Carriere #include <kernel/interrupt.h>
171fe98f82SClément Léger #include <kernel/pm.h>
181fe98f82SClément Léger #include <libfdt.h>
191fe98f82SClément Léger #include <mm/core_memprot.h>
201fe98f82SClément Léger
21*4b17205bSTony Han #define SECUMOD_MAX_PINS (piobu_device->compat->max_pins)
221fe98f82SClément Léger #define SECUMOD_PIN_MASK (BIT(SECUMOD_MAX_PINS) - 1)
231fe98f82SClément Léger #define SECUMOD_PIN_SHIFT 16
241fe98f82SClément Léger #define SECUMOD_PIN_VAL(pin) BIT(SECUMOD_PIN_SHIFT + (pin))
251fe98f82SClément Léger
261fe98f82SClément Léger #define DT_GPIO_CELLS 2
271fe98f82SClément Léger
281fe98f82SClément Léger #define SECUMOD_CR 0x0
291fe98f82SClément Léger #define SECUMOD_CR_KEY_SHIFT 16
301fe98f82SClément Léger #define SECUMOD_CR_KEY SHIFT_U32(0x89CA, SECUMOD_CR_KEY_SHIFT)
311fe98f82SClément Léger #define SECUMOD_CR_BACKUP BIT(0)
321fe98f82SClément Léger #define SECUMOD_CR_NORMAL BIT(1)
331fe98f82SClément Léger
341fe98f82SClément Léger #define SECUMOD_SR 0x8
351fe98f82SClément Léger #define SECUMOD_SR_JTAG BIT(3)
361fe98f82SClément Léger #define SECUMOD_SR_TST_PIN BIT(2)
371fe98f82SClément Léger
381fe98f82SClément Léger #define SECUMOD_SCR 0x10
391fe98f82SClément Léger
401fe98f82SClément Léger #define SECUMOD_PIOBU(x) (0x18 + (x) * 0x4)
411fe98f82SClément Léger #define SECUMOD_PIOBU_AFV_MASK GENMASK_32(3, 0)
421fe98f82SClément Léger #define SECUMOD_PIOBU_RFV_SHIFT 4
431fe98f82SClément Léger #define SECUMOD_PIOBU_OUTPUT BIT(8)
441fe98f82SClément Léger #define SECUMOD_PIOBU_SOD BIT(9)
451fe98f82SClément Léger #define SECUMOD_PIOBU_PDS BIT(10)
461fe98f82SClément Léger #define SECUMOD_PIOBU_PULLUP_SHIFT 12
471fe98f82SClément Léger #define SECUMOD_PIOBU_SWITCH_SHIFT 15
481fe98f82SClément Léger
491fe98f82SClément Léger #define SECUMOD_JTAGCR_FNTRST 0x1
501fe98f82SClément Léger
51*4b17205bSTony Han /*
52*4b17205bSTony Han * PIOBU instance data
53*4b17205bSTony Han * @compat - Reference to compat data passed at driver initialization
54*4b17205bSTony Han */
55*4b17205bSTony Han struct piobu_instance {
56*4b17205bSTony Han struct piobu_compat *compat;
57*4b17205bSTony Han };
58*4b17205bSTony Han
59*4b17205bSTony Han /*
60*4b17205bSTony Han * @max_pins the number of the tamper I/Os
61*4b17205bSTony Han * @of_jtagcr offset of SECUMOD JTAG Protection Control Register
62*4b17205bSTony Han * @of_bmpr offset of SECUMOD Backup Mode Protection Register
63*4b17205bSTony Han * @of_nmpr offset of SECUMOD Normal Mode Protection Register
64*4b17205bSTony Han * @of_niepr offset of SECUMOD Normal Interrupt Enable Protection Register
65*4b17205bSTony Han * @of_nidpr offset of SECUMOD Normal Interrupt Disable Protection Register
66*4b17205bSTony Han * @of_nimpr offset of SECUMOD Normal Interrupt Mask Protection Register
67*4b17205bSTony Han * @of_wkpr offset of SECUMOD Wake-up Register
68*4b17205bSTony Han */
69*4b17205bSTony Han struct piobu_compat {
70*4b17205bSTony Han uint8_t max_pins;
71*4b17205bSTony Han uint8_t of_jtagcr;
72*4b17205bSTony Han uint8_t of_bmpr;
73*4b17205bSTony Han uint8_t of_nmpr;
74*4b17205bSTony Han uint8_t of_niepr;
75*4b17205bSTony Han uint8_t of_nidpr;
76*4b17205bSTony Han uint8_t of_nimpr;
77*4b17205bSTony Han uint8_t of_wkpr;
78*4b17205bSTony Han };
79*4b17205bSTony Han
80*4b17205bSTony Han /* Expects at most a single instance */
81*4b17205bSTony Han static struct piobu_instance *piobu_device;
821fe98f82SClément Léger
831fe98f82SClément Léger static vaddr_t secumod_base;
841fe98f82SClément Léger static uint32_t gpio_protected;
851fe98f82SClément Léger static struct gpio_chip secumod_chip;
861fe98f82SClément Léger
871fe98f82SClément Léger /*
881fe98f82SClément Léger * Get value from GPIO controller
891fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
901fe98f82SClément Léger * gpio_pin: pin from which value needs to be read
911fe98f82SClément Léger * Return target GPIO pin level.
921fe98f82SClément Léger */
secumod_gpio_get_value(struct gpio_chip * chip __unused,unsigned int gpio_pin)938bc270b1SClément Léger static enum gpio_level secumod_gpio_get_value(struct gpio_chip *chip __unused,
941fe98f82SClément Léger unsigned int gpio_pin)
951fe98f82SClément Léger {
961fe98f82SClément Léger vaddr_t piobu_addr = 0;
971fe98f82SClément Léger uint32_t piobu = 0;
981fe98f82SClément Léger
991fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
1001fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
1011fe98f82SClément Léger
1021fe98f82SClément Léger piobu_addr = secumod_base + SECUMOD_PIOBU(gpio_pin);
1031fe98f82SClément Léger piobu = io_read32(piobu_addr);
1041fe98f82SClément Léger
1051fe98f82SClément Léger if (piobu & SECUMOD_PIOBU_PDS)
1061fe98f82SClément Léger return GPIO_LEVEL_HIGH;
1071fe98f82SClément Léger else
1081fe98f82SClément Léger return GPIO_LEVEL_LOW;
1091fe98f82SClément Léger }
1101fe98f82SClément Léger
1111fe98f82SClément Léger /*
1121fe98f82SClément Léger * Set value for GPIO controller
1131fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
1141fe98f82SClément Léger * gpio_pin: pin to which value needs to be written
1151fe98f82SClément Léger * value: Level state for the target pin
1161fe98f82SClément Léger */
secumod_gpio_set_value(struct gpio_chip * chip __unused,unsigned int gpio_pin,enum gpio_level value)1178bc270b1SClément Léger static void secumod_gpio_set_value(struct gpio_chip *chip __unused,
1181fe98f82SClément Léger unsigned int gpio_pin, enum gpio_level value)
1191fe98f82SClément Léger {
1201fe98f82SClément Léger vaddr_t piobu_addr = 0;
1211fe98f82SClément Léger
1221fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
1231fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
1241fe98f82SClément Léger
1251fe98f82SClément Léger piobu_addr = secumod_base + SECUMOD_PIOBU(gpio_pin);
1261fe98f82SClément Léger
1271fe98f82SClément Léger if (value == GPIO_LEVEL_HIGH)
1281fe98f82SClément Léger io_setbits32(piobu_addr, SECUMOD_PIOBU_SOD);
1291fe98f82SClément Léger else
1301fe98f82SClément Léger io_clrbits32(piobu_addr, SECUMOD_PIOBU_SOD);
1311fe98f82SClément Léger }
1321fe98f82SClément Léger
1331fe98f82SClément Léger /*
1341fe98f82SClément Léger * Get direction from GPIO controller
1351fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
1361fe98f82SClément Léger * gpio_pin: pin from which direction needs to be read
1371fe98f82SClément Léger */
secumod_gpio_get_direction(struct gpio_chip * chip __unused,unsigned int gpio_pin)1388bc270b1SClément Léger static enum gpio_dir secumod_gpio_get_direction(struct gpio_chip *chip __unused,
1391fe98f82SClément Léger unsigned int gpio_pin)
1401fe98f82SClément Léger {
1411fe98f82SClément Léger vaddr_t piobu_addr = 0;
1421fe98f82SClément Léger uint32_t piobu = 0;
1431fe98f82SClément Léger
1441fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
1451fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
1461fe98f82SClément Léger
1471fe98f82SClément Léger piobu_addr = secumod_base + SECUMOD_PIOBU(gpio_pin);
1481fe98f82SClément Léger piobu = io_read32(piobu_addr);
1491fe98f82SClément Léger
1501fe98f82SClément Léger if (piobu & SECUMOD_PIOBU_OUTPUT)
1511fe98f82SClément Léger return GPIO_DIR_OUT;
1521fe98f82SClément Léger else
1531fe98f82SClément Léger return GPIO_DIR_IN;
1541fe98f82SClément Léger }
1551fe98f82SClément Léger
1561fe98f82SClément Léger /*
1571fe98f82SClément Léger * Set direction for GPIO controller
1581fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
1591fe98f82SClément Léger * gpio_pin: pin on which direction needs to be set
1601fe98f82SClément Léger * direction: direction which needs to be set on pin
1611fe98f82SClément Léger */
secumod_gpio_set_direction(struct gpio_chip * chip __unused,unsigned int gpio_pin,enum gpio_dir direction)1628bc270b1SClément Léger static void secumod_gpio_set_direction(struct gpio_chip *chip __unused,
1638bc270b1SClément Léger unsigned int gpio_pin,
1648bc270b1SClément Léger enum gpio_dir direction)
1651fe98f82SClément Léger {
1661fe98f82SClément Léger vaddr_t piobu_addr = 0;
1671fe98f82SClément Léger
1681fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
1691fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
1701fe98f82SClément Léger
1711fe98f82SClément Léger piobu_addr = secumod_base + SECUMOD_PIOBU(gpio_pin);
1721fe98f82SClément Léger
1731fe98f82SClément Léger if (direction == GPIO_DIR_OUT)
1741fe98f82SClément Léger io_setbits32(piobu_addr, SECUMOD_PIOBU_OUTPUT);
1751fe98f82SClément Léger else
1761fe98f82SClément Léger io_clrbits32(piobu_addr, SECUMOD_PIOBU_OUTPUT);
1771fe98f82SClément Léger }
1781fe98f82SClément Léger
1791fe98f82SClément Léger /*
1801fe98f82SClément Léger * Get interrupt from GPIO controller
1811fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
1821fe98f82SClément Léger * gpio_pin: pin from which interrupt value needs to be read
1831fe98f82SClément Léger */
1848bc270b1SClément Léger static enum gpio_interrupt
secumod_gpio_get_interrupt(struct gpio_chip * chip __unused,unsigned int gpio_pin)1858bc270b1SClément Léger secumod_gpio_get_interrupt(struct gpio_chip *chip __unused,
1861fe98f82SClément Léger unsigned int gpio_pin)
1871fe98f82SClément Léger {
188*4b17205bSTony Han vaddr_t nimpr_addr = secumod_base + piobu_device->compat->of_nimpr;
1891fe98f82SClément Léger uint32_t data = 0;
1901fe98f82SClément Léger
1911fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
1921fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
1931fe98f82SClément Léger
1941fe98f82SClément Léger data = io_read32(nimpr_addr);
1951fe98f82SClément Léger
1961fe98f82SClément Léger if (data & SECUMOD_PIN_VAL(gpio_pin))
1971fe98f82SClément Léger return GPIO_INTERRUPT_ENABLE;
1981fe98f82SClément Léger else
1991fe98f82SClément Léger return GPIO_INTERRUPT_DISABLE;
2001fe98f82SClément Léger }
2011fe98f82SClément Léger
2021fe98f82SClément Léger /*
2031fe98f82SClément Léger * Set interrupt event for GPIO controller
2041fe98f82SClément Léger * chip: pointer to GPIO controller chip instance
2051fe98f82SClément Léger * gpio_pin: pin on which interrupt value needs to be set
2061fe98f82SClément Léger * interrupt: interrupt value which needs to be set on pin
2071fe98f82SClément Léger */
secumod_gpio_set_interrupt(struct gpio_chip * chip __unused,unsigned int gpio_pin,enum gpio_interrupt interrupt)2088bc270b1SClément Léger static void secumod_gpio_set_interrupt(struct gpio_chip *chip __unused,
2091fe98f82SClément Léger unsigned int gpio_pin,
2101fe98f82SClément Léger enum gpio_interrupt interrupt)
2111fe98f82SClément Léger {
212*4b17205bSTony Han vaddr_t niepr_addr = secumod_base + piobu_device->compat->of_niepr;
2131fe98f82SClément Léger
2141fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS &&
2151fe98f82SClément Léger !(gpio_protected & BIT32(gpio_pin)));
2161fe98f82SClément Léger
2171fe98f82SClément Léger if (interrupt == GPIO_INTERRUPT_ENABLE)
2181fe98f82SClément Léger io_setbits32(niepr_addr, SECUMOD_PIN_VAL(gpio_pin));
2191fe98f82SClément Léger else
2201fe98f82SClément Léger io_clrbits32(niepr_addr, SECUMOD_PIN_VAL(gpio_pin));
2211fe98f82SClément Léger }
2221fe98f82SClément Léger
2231fe98f82SClément Léger static const struct gpio_ops atmel_piobu_ops = {
2248bc270b1SClément Léger .get_direction = secumod_gpio_get_direction,
2258bc270b1SClément Léger .set_direction = secumod_gpio_set_direction,
2268bc270b1SClément Léger .get_value = secumod_gpio_get_value,
2278bc270b1SClément Léger .set_value = secumod_gpio_set_value,
2288bc270b1SClément Léger .get_interrupt = secumod_gpio_get_interrupt,
2298bc270b1SClément Léger .set_interrupt = secumod_gpio_set_interrupt,
2301fe98f82SClément Léger };
2311fe98f82SClément Léger
secumod_dt_get(struct dt_pargs * pargs,void * data,struct gpio ** out_gpio)232b357d34fSEtienne Carriere static TEE_Result secumod_dt_get(struct dt_pargs *pargs, void *data,
233b357d34fSEtienne Carriere struct gpio **out_gpio)
234b786cc03SClément Léger {
235b357d34fSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
236b786cc03SClément Léger struct gpio *gpio = NULL;
237b786cc03SClément Léger struct gpio_chip *chip = data;
238b786cc03SClément Léger
239b357d34fSEtienne Carriere res = gpio_dt_alloc_pin(pargs, &gpio);
240b357d34fSEtienne Carriere if (res)
241b357d34fSEtienne Carriere return res;
242b786cc03SClément Léger
243b786cc03SClément Léger if (gpio_protected & BIT32(gpio->pin)) {
244b786cc03SClément Léger free(gpio);
245b357d34fSEtienne Carriere return TEE_ERROR_GENERIC;
246b786cc03SClément Léger }
247b786cc03SClément Léger
248b786cc03SClément Léger gpio->chip = chip;
249b357d34fSEtienne Carriere *out_gpio = gpio;
250b786cc03SClément Léger
251b357d34fSEtienne Carriere return TEE_SUCCESS;
252b786cc03SClément Léger }
253b786cc03SClément Léger
secumod_it_handler(struct itr_handler * handler __unused)2541fe98f82SClément Léger static enum itr_return secumod_it_handler(struct itr_handler *handler __unused)
2551fe98f82SClément Léger {
2561fe98f82SClément Léger int i = 0;
2571fe98f82SClément Léger struct optee_rtc_time tm = { };
2581fe98f82SClément Léger TEE_Result res = TEE_ERROR_GENERIC;
2591fe98f82SClément Léger uint32_t sr = io_read32(secumod_base + SECUMOD_SR);
2601fe98f82SClément Léger
2611fe98f82SClément Léger for (i = 0; i < SECUMOD_MAX_PINS; i++) {
2621fe98f82SClément Léger if (sr & SECUMOD_PIN_VAL(i))
2631fe98f82SClément Léger EMSG("Detected tampering on pin %d", i);
2641fe98f82SClément Léger }
2651fe98f82SClément Léger
2661fe98f82SClément Léger if (sr & SECUMOD_SR_JTAG)
2671fe98f82SClément Léger EMSG("JTAG tampering attempt");
2681fe98f82SClément Léger
2691fe98f82SClément Léger if (sr & SECUMOD_SR_TST_PIN)
2701fe98f82SClément Léger EMSG("Test pin tampering attempt");
2711fe98f82SClément Léger
2721fe98f82SClément Léger res = atmel_rtc_get_tamper_timestamp(&tm);
2731fe98f82SClément Léger if (!res) {
2741fe98f82SClément Léger EMSG("Date of tampering: %02"PRIu32"/%02"PRIu32"/%02"PRIu32"",
2751fe98f82SClément Léger tm.tm_mday, tm.tm_mon, tm.tm_year);
2761fe98f82SClément Léger EMSG("Time of tampering: %02"PRIu32":%02"PRIu32":%02"PRIu32"",
2771fe98f82SClément Léger tm.tm_hour, tm.tm_min, tm.tm_sec);
2781fe98f82SClément Léger }
2791fe98f82SClément Léger
2801fe98f82SClément Léger io_write32(secumod_base + SECUMOD_SCR,
2811fe98f82SClément Léger SECUMOD_PIN_MASK << SECUMOD_PIN_SHIFT);
2821fe98f82SClément Léger
2831fe98f82SClément Léger panic("Tampering detected, system halted");
2841fe98f82SClément Léger
2851fe98f82SClément Léger return ITRR_HANDLED;
2861fe98f82SClément Léger }
2871fe98f82SClément Léger
2881fe98f82SClément Léger static struct itr_handler secumod_itr_handler = {
2891fe98f82SClément Léger .it = AT91C_ID_SECUMOD,
2901fe98f82SClément Léger .handler = secumod_it_handler,
2911fe98f82SClément Léger };
2921fe98f82SClément Léger
secumod_interrupt_init(void)2931fe98f82SClément Léger static void secumod_interrupt_init(void)
2941fe98f82SClément Léger {
29557fec118SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
29657fec118SEtienne Carriere
29757fec118SEtienne Carriere secumod_itr_handler.chip = interrupt_get_main_chip();
29857fec118SEtienne Carriere
29957fec118SEtienne Carriere res = interrupt_add_configure_handler(&secumod_itr_handler,
30057fec118SEtienne Carriere IRQ_TYPE_LEVEL_HIGH, 7);
30157fec118SEtienne Carriere if (res)
30257fec118SEtienne Carriere panic();
30357fec118SEtienne Carriere
30457fec118SEtienne Carriere interrupt_enable(secumod_itr_handler.chip, secumod_itr_handler.it);
3051fe98f82SClément Léger }
3061fe98f82SClément Léger
secumod_cfg_input_pio(uint8_t gpio_pin,uint32_t config)3071fe98f82SClément Léger static void secumod_cfg_input_pio(uint8_t gpio_pin, uint32_t config)
3081fe98f82SClément Léger {
3091fe98f82SClément Léger vaddr_t piobu_addr = 0;
3101fe98f82SClément Léger uint8_t afv = 0;
3111fe98f82SClément Léger uint8_t rfv = 0;
3121fe98f82SClément Léger uint8_t pull_mode = PIOBU_PIN_PULL_NONE;
3131fe98f82SClément Léger uint8_t def_level = PIOBU_PIN_DEF_LEVEL_LOW;
3141fe98f82SClément Léger
3151fe98f82SClément Léger assert(gpio_pin < SECUMOD_MAX_PINS);
3161fe98f82SClément Léger
3171fe98f82SClément Léger piobu_addr = secumod_base + SECUMOD_PIOBU(gpio_pin);
3181fe98f82SClément Léger
3191fe98f82SClément Léger /* Set GPIO as input */
3201fe98f82SClément Léger io_clrbits32(piobu_addr, SECUMOD_PIOBU_OUTPUT);
3211fe98f82SClément Léger
3221fe98f82SClément Léger afv = PIOBU_PIN_AFV(config);
3231fe98f82SClément Léger rfv = PIOBU_PIN_RFV(config);
3241fe98f82SClément Léger pull_mode = PIOBU_PIN_PULL_MODE(config);
3251fe98f82SClément Léger def_level = PIOBU_PIN_DEF_LEVEL(config);
3261fe98f82SClément Léger
3271fe98f82SClément Léger io_write32(piobu_addr, afv | rfv << SECUMOD_PIOBU_RFV_SHIFT |
3281fe98f82SClément Léger pull_mode << SECUMOD_PIOBU_PULLUP_SHIFT |
3291fe98f82SClément Léger def_level << SECUMOD_PIOBU_SWITCH_SHIFT);
3301fe98f82SClément Léger
3311fe98f82SClément Léger /* Enable Tampering Interrupt */
3328bc270b1SClément Léger secumod_gpio_set_interrupt(&secumod_chip, gpio_pin,
3338bc270b1SClément Léger GPIO_INTERRUPT_ENABLE);
3341fe98f82SClément Léger
3351fe98f82SClément Léger /* Enable Intrusion Detection */
336*4b17205bSTony Han io_setbits32(secumod_base + piobu_device->compat->of_nmpr,
337*4b17205bSTony Han SECUMOD_PIN_VAL(gpio_pin));
3381fe98f82SClément Léger
3391fe98f82SClément Léger /* Enable Wakeup */
3401fe98f82SClément Léger if (PIOBU_PIN_WAKEUP(config))
341*4b17205bSTony Han io_setbits32(secumod_base + piobu_device->compat->of_wkpr,
3421fe98f82SClément Léger SECUMOD_PIN_VAL(gpio_pin));
3431fe98f82SClément Léger
3441fe98f82SClément Léger gpio_protected |= BIT32(gpio_pin);
3451fe98f82SClément Léger }
3461fe98f82SClément Léger
secumod_hw_init(const void * fdt,int node)3471fe98f82SClément Léger static void secumod_hw_init(const void *fdt, int node)
3481fe98f82SClément Léger {
3491fe98f82SClément Léger int i = 0;
3501fe98f82SClément Léger int len = 0;
3511fe98f82SClément Léger uint8_t gpio_pin = 0;
3521fe98f82SClément Léger uint32_t config = 0;
3531fe98f82SClément Léger const uint32_t *prop = NULL;
3541fe98f82SClément Léger
3551fe98f82SClément Léger /* Disable JTAG Reset and Debug */
356*4b17205bSTony Han io_write32(secumod_base + piobu_device->compat->of_jtagcr,
357*4b17205bSTony Han SECUMOD_JTAGCR_FNTRST);
3581fe98f82SClément Léger
3591fe98f82SClément Léger /* Switch IOs to normal mode */
3601fe98f82SClément Léger io_write32(secumod_base + SECUMOD_CR, SECUMOD_CR_KEY |
3611fe98f82SClément Léger SECUMOD_CR_NORMAL);
3621fe98f82SClément Léger
3631fe98f82SClément Léger /* Clear all detection intrusion in normal mode */
364*4b17205bSTony Han io_write32(secumod_base + piobu_device->compat->of_nmpr, 0);
3651fe98f82SClément Léger
3661fe98f82SClément Léger /* Clear Alarms */
3671fe98f82SClément Léger io_write32(secumod_base + SECUMOD_SCR,
3681fe98f82SClément Léger SECUMOD_PIN_MASK << SECUMOD_PIN_SHIFT);
3691fe98f82SClément Léger
3701fe98f82SClément Léger /* Configure each IOs */
3711fe98f82SClément Léger prop = fdt_getprop(fdt, node, "gpios", &len);
3721fe98f82SClément Léger if (!prop)
3731fe98f82SClément Léger return;
3741fe98f82SClément Léger
3751fe98f82SClément Léger len /= sizeof(uint32_t);
3761fe98f82SClément Léger for (i = 0; i < len; i += DT_GPIO_CELLS) {
3771fe98f82SClément Léger gpio_pin = fdt32_to_cpu(prop[i]);
3781fe98f82SClément Léger config = fdt32_to_cpu(prop[i + 1]);
3791fe98f82SClément Léger
3801fe98f82SClément Léger secumod_cfg_input_pio(gpio_pin, config);
3811fe98f82SClément Léger }
3821fe98f82SClément Léger }
3831fe98f82SClément Léger
3841fe98f82SClément Léger #ifdef CFG_PM_ARM32
piobu_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl __unused)3851fe98f82SClément Léger static TEE_Result piobu_pm(enum pm_op op, uint32_t pm_hint __unused,
3861fe98f82SClément Léger const struct pm_callback_handle *hdl __unused)
3871fe98f82SClément Léger {
3881fe98f82SClément Léger switch (op) {
3891fe98f82SClément Léger case PM_OP_RESUME:
3901fe98f82SClément Léger io_write32(secumod_base + SECUMOD_CR, SECUMOD_CR_KEY |
3911fe98f82SClément Léger SECUMOD_CR_NORMAL);
3921fe98f82SClément Léger break;
3931fe98f82SClément Léger case PM_OP_SUSPEND:
3941fe98f82SClément Léger /* Enter backup mode before suspending */
3951fe98f82SClément Léger io_write32(secumod_base + SECUMOD_CR, SECUMOD_CR_KEY |
3961fe98f82SClément Léger SECUMOD_CR_BACKUP);
3971fe98f82SClément Léger break;
3981fe98f82SClément Léger default:
3991fe98f82SClément Léger panic("Invalid PM operation");
4001fe98f82SClément Léger }
4011fe98f82SClément Léger
4021fe98f82SClément Léger return TEE_SUCCESS;
4031fe98f82SClément Léger }
4041fe98f82SClément Léger
piobu_register_pm(void)4051fe98f82SClément Léger static void piobu_register_pm(void)
4061fe98f82SClément Léger {
4071fe98f82SClément Léger register_pm_driver_cb(piobu_pm, NULL, "piobu");
4081fe98f82SClément Léger }
4091fe98f82SClément Léger #else
piobu_register_pm(void)4101fe98f82SClément Léger static void piobu_register_pm(void) {}
4111fe98f82SClément Léger #endif
4121fe98f82SClément Léger
atmel_secumod_probe(const void * fdt,int node,const void * compat_data)4131fe98f82SClément Léger static TEE_Result atmel_secumod_probe(const void *fdt, int node,
414*4b17205bSTony Han const void *compat_data)
4151fe98f82SClément Léger {
4161fe98f82SClément Léger size_t size = 0;
4171fe98f82SClément Léger
4181fe98f82SClément Léger if (secumod_base)
4191fe98f82SClément Léger return TEE_ERROR_GENERIC;
4201fe98f82SClément Léger
421*4b17205bSTony Han piobu_device = calloc(1, sizeof(*piobu_device));
422*4b17205bSTony Han if (!piobu_device)
423*4b17205bSTony Han return TEE_ERROR_OUT_OF_MEMORY;
424*4b17205bSTony Han
425*4b17205bSTony Han piobu_device->compat = (struct piobu_compat *)compat_data;
426*4b17205bSTony Han
427a5d5bbc8SVesa Jääskeläinen if (dt_map_dev(fdt, node, &secumod_base, &size, DT_MAP_AUTO) < 0)
4281fe98f82SClément Léger return TEE_ERROR_GENERIC;
4291fe98f82SClément Léger
4301fe98f82SClément Léger secumod_hw_init(fdt, node);
4311fe98f82SClément Léger secumod_interrupt_init();
4321fe98f82SClément Léger
4331fe98f82SClément Léger secumod_chip.ops = &atmel_piobu_ops;
4341fe98f82SClément Léger
4351fe98f82SClément Léger piobu_register_pm();
4361fe98f82SClément Léger
437b786cc03SClément Léger assert(gpio_ops_is_valid(&atmel_piobu_ops));
438b786cc03SClément Léger
439b786cc03SClément Léger return gpio_register_provider(fdt, node, secumod_dt_get, &secumod_chip);
4401fe98f82SClément Léger }
4411fe98f82SClément Léger
442*4b17205bSTony Han static const struct piobu_compat sama5d2_compat = {
443*4b17205bSTony Han .max_pins = 8,
444*4b17205bSTony Han .of_jtagcr = 0x68,
445*4b17205bSTony Han .of_bmpr = 0x7C,
446*4b17205bSTony Han .of_nmpr = 0x80,
447*4b17205bSTony Han .of_niepr = 0x84,
448*4b17205bSTony Han .of_nidpr = 0x88,
449*4b17205bSTony Han .of_nimpr = 0x8C,
450*4b17205bSTony Han .of_wkpr = 0x90,
451*4b17205bSTony Han };
452*4b17205bSTony Han
453*4b17205bSTony Han static const struct piobu_compat sama7g54_compat = {
454*4b17205bSTony Han .max_pins = 4,
455*4b17205bSTony Han .of_jtagcr = 0x70,
456*4b17205bSTony Han .of_bmpr = 0x84,
457*4b17205bSTony Han .of_nmpr = 0x88,
458*4b17205bSTony Han .of_niepr = 0x8C,
459*4b17205bSTony Han .of_nidpr = 0x90,
460*4b17205bSTony Han .of_nimpr = 0x94,
461*4b17205bSTony Han .of_wkpr = 0x98,
462*4b17205bSTony Han };
463*4b17205bSTony Han
4641fe98f82SClément Léger static const struct dt_device_match atmel_secumod_match_table[] = {
465*4b17205bSTony Han {
466*4b17205bSTony Han .compatible = "atmel,sama5d2-secumod",
467*4b17205bSTony Han .compat_data = &sama5d2_compat,
468*4b17205bSTony Han },
469*4b17205bSTony Han {
470*4b17205bSTony Han .compatible = "microchip,sama7g5-secumod",
471*4b17205bSTony Han .compat_data = &sama7g54_compat,
472*4b17205bSTony Han },
4731fe98f82SClément Léger { }
4741fe98f82SClément Léger };
4751fe98f82SClément Léger
4761fe98f82SClément Léger DEFINE_DT_DRIVER(atmel_secumod_dt_driver) = {
4771fe98f82SClément Léger .name = "atmel_secumod",
4781fe98f82SClément Léger .type = DT_DRIVER_NOTYPE,
4791fe98f82SClément Léger .match_table = atmel_secumod_match_table,
4801fe98f82SClément Léger .probe = atmel_secumod_probe,
4811fe98f82SClément Léger };
482