10ab3f9a7SHaojian Zhuang /*
20ab3f9a7SHaojian Zhuang * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
30ab3f9a7SHaojian Zhuang *
482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause
50ab3f9a7SHaojian Zhuang *
60ab3f9a7SHaojian Zhuang * ARM PL061 GPIO Driver.
70ab3f9a7SHaojian Zhuang * Reference to ARM DDI 0190B document.
80ab3f9a7SHaojian Zhuang *
90ab3f9a7SHaojian Zhuang */
100ab3f9a7SHaojian Zhuang
110ab3f9a7SHaojian Zhuang #include <assert.h>
120ab3f9a7SHaojian Zhuang #include <errno.h>
13*09d40e0eSAntonio Nino Diaz
14*09d40e0eSAntonio Nino Diaz #include <common/debug.h>
15*09d40e0eSAntonio Nino Diaz #include <drivers/arm/pl061_gpio.h>
16*09d40e0eSAntonio Nino Diaz #include <drivers/gpio.h>
17*09d40e0eSAntonio Nino Diaz #include <lib/cassert.h>
18*09d40e0eSAntonio Nino Diaz #include <lib/mmio.h>
19*09d40e0eSAntonio Nino Diaz #include <lib/utils.h>
200ab3f9a7SHaojian Zhuang
210ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS
220ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS 32
230ab3f9a7SHaojian Zhuang #endif /* PLAT_PL061_MAX_GPIOS */
240ab3f9a7SHaojian Zhuang
250ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios);
260ab3f9a7SHaojian Zhuang
270ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \
280ab3f9a7SHaojian Zhuang (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
290ab3f9a7SHaojian Zhuang
300ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR 0x400
310ab3f9a7SHaojian Zhuang
320ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061 8
330ab3f9a7SHaojian Zhuang
340ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio);
350ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction);
360ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio);
370ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value);
380ab3f9a7SHaojian Zhuang
390ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES];
400ab3f9a7SHaojian Zhuang
410ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = {
420ab3f9a7SHaojian Zhuang .get_direction = pl061_get_direction,
430ab3f9a7SHaojian Zhuang .set_direction = pl061_set_direction,
440ab3f9a7SHaojian Zhuang .get_value = pl061_get_value,
450ab3f9a7SHaojian Zhuang .set_value = pl061_set_value,
460ab3f9a7SHaojian Zhuang };
470ab3f9a7SHaojian Zhuang
pl061_get_direction(int gpio)480ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio)
490ab3f9a7SHaojian Zhuang {
500ab3f9a7SHaojian Zhuang uintptr_t base_addr;
510ab3f9a7SHaojian Zhuang unsigned int data, offset;
520ab3f9a7SHaojian Zhuang
530ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
540ab3f9a7SHaojian Zhuang
550ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
560ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061;
570ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR);
580ab3f9a7SHaojian Zhuang if (data & BIT(offset))
590ab3f9a7SHaojian Zhuang return GPIO_DIR_OUT;
600ab3f9a7SHaojian Zhuang return GPIO_DIR_IN;
610ab3f9a7SHaojian Zhuang }
620ab3f9a7SHaojian Zhuang
pl061_set_direction(int gpio,int direction)630ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction)
640ab3f9a7SHaojian Zhuang {
650ab3f9a7SHaojian Zhuang uintptr_t base_addr;
660ab3f9a7SHaojian Zhuang unsigned int data, offset;
670ab3f9a7SHaojian Zhuang
680ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
690ab3f9a7SHaojian Zhuang
700ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
710ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061;
720ab3f9a7SHaojian Zhuang if (direction == GPIO_DIR_OUT) {
730ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset);
740ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data);
750ab3f9a7SHaojian Zhuang } else {
760ab3f9a7SHaojian Zhuang data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset);
770ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + PL061_GPIO_DIR, data);
780ab3f9a7SHaojian Zhuang }
790ab3f9a7SHaojian Zhuang }
800ab3f9a7SHaojian Zhuang
810ab3f9a7SHaojian Zhuang /*
820ab3f9a7SHaojian Zhuang * The offset of GPIODATA register is 0.
830ab3f9a7SHaojian Zhuang * The values read from GPIODATA are determined for each bit, by the mask bit
840ab3f9a7SHaojian Zhuang * derived from the address used to access the data register, PADDR[9:2].
850ab3f9a7SHaojian Zhuang * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
860ab3f9a7SHaojian Zhuang * to be read, and bits that are 0 in the address mask cause the corresponding
870ab3f9a7SHaojian Zhuang * bits in GPIODATA to be read as 0, regardless of their value.
880ab3f9a7SHaojian Zhuang */
pl061_get_value(int gpio)890ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio)
900ab3f9a7SHaojian Zhuang {
910ab3f9a7SHaojian Zhuang uintptr_t base_addr;
920ab3f9a7SHaojian Zhuang unsigned int offset;
930ab3f9a7SHaojian Zhuang
940ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
950ab3f9a7SHaojian Zhuang
960ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
970ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061;
980ab3f9a7SHaojian Zhuang if (mmio_read_8(base_addr + BIT(offset + 2)))
990ab3f9a7SHaojian Zhuang return GPIO_LEVEL_HIGH;
1000ab3f9a7SHaojian Zhuang return GPIO_LEVEL_LOW;
1010ab3f9a7SHaojian Zhuang }
1020ab3f9a7SHaojian Zhuang
1030ab3f9a7SHaojian Zhuang /*
1040ab3f9a7SHaojian Zhuang * In order to write GPIODATA, the corresponding bits in the mask, resulting
1050ab3f9a7SHaojian Zhuang * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
1060ab3f9a7SHaojian Zhuang * remain unchanged by the write.
1070ab3f9a7SHaojian Zhuang */
pl061_set_value(int gpio,int value)1080ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value)
1090ab3f9a7SHaojian Zhuang {
1100ab3f9a7SHaojian Zhuang uintptr_t base_addr;
1110ab3f9a7SHaojian Zhuang int offset;
1120ab3f9a7SHaojian Zhuang
1130ab3f9a7SHaojian Zhuang assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
1140ab3f9a7SHaojian Zhuang
1150ab3f9a7SHaojian Zhuang base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
1160ab3f9a7SHaojian Zhuang offset = gpio % GPIOS_PER_PL061;
1170ab3f9a7SHaojian Zhuang if (value == GPIO_LEVEL_HIGH)
1180ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), BIT(offset));
1190ab3f9a7SHaojian Zhuang else
1200ab3f9a7SHaojian Zhuang mmio_write_8(base_addr + BIT(offset + 2), 0);
1210ab3f9a7SHaojian Zhuang }
1220ab3f9a7SHaojian Zhuang
1230ab3f9a7SHaojian Zhuang
1240ab3f9a7SHaojian Zhuang /*
1250ab3f9a7SHaojian Zhuang * Register the PL061 GPIO controller with a base address and the offset
1260ab3f9a7SHaojian Zhuang * of start pin in this GPIO controller.
1270ab3f9a7SHaojian Zhuang * This function is called after pl061_gpio_ops_init().
1280ab3f9a7SHaojian Zhuang */
pl061_gpio_register(uintptr_t base_addr,int gpio_dev)1290ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev)
1300ab3f9a7SHaojian Zhuang {
1310ab3f9a7SHaojian Zhuang assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES));
1320ab3f9a7SHaojian Zhuang
1330ab3f9a7SHaojian Zhuang pl061_reg_base[gpio_dev] = base_addr;
1340ab3f9a7SHaojian Zhuang }
1350ab3f9a7SHaojian Zhuang
1360ab3f9a7SHaojian Zhuang /*
1370ab3f9a7SHaojian Zhuang * Initialize PL061 GPIO controller with the total GPIO numbers in SoC.
1380ab3f9a7SHaojian Zhuang */
pl061_gpio_init(void)1390ab3f9a7SHaojian Zhuang void pl061_gpio_init(void)
1400ab3f9a7SHaojian Zhuang {
1410ab3f9a7SHaojian Zhuang gpio_init(&pl061_gpio_ops);
1420ab3f9a7SHaojian Zhuang }
143