xref: /rk3399_ARM-atf/drivers/arm/pl061/pl061_gpio.c (revision 152c8c11b7943c83ae07c9a51dc9433f85d85306)
10ab3f9a7SHaojian Zhuang /*
20ab3f9a7SHaojian Zhuang  * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
30ab3f9a7SHaojian Zhuang  *
40ab3f9a7SHaojian Zhuang  * Redistribution and use in source and binary forms, with or without
50ab3f9a7SHaojian Zhuang  * modification, are permitted provided that the following conditions are met:
60ab3f9a7SHaojian Zhuang  *
70ab3f9a7SHaojian Zhuang  * Redistributions of source code must retain the above copyright notice, this
80ab3f9a7SHaojian Zhuang  * list of conditions and the following disclaimer.
90ab3f9a7SHaojian Zhuang  *
100ab3f9a7SHaojian Zhuang  * Redistributions in binary form must reproduce the above copyright notice,
110ab3f9a7SHaojian Zhuang  * this list of conditions and the following disclaimer in the documentation
120ab3f9a7SHaojian Zhuang  * and/or other materials provided with the distribution.
130ab3f9a7SHaojian Zhuang  *
140ab3f9a7SHaojian Zhuang  * Neither the name of ARM nor the names of its contributors may be used
150ab3f9a7SHaojian Zhuang  * to endorse or promote products derived from this software without specific
160ab3f9a7SHaojian Zhuang  * prior written permission.
170ab3f9a7SHaojian Zhuang  *
180ab3f9a7SHaojian Zhuang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
190ab3f9a7SHaojian Zhuang  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
200ab3f9a7SHaojian Zhuang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
210ab3f9a7SHaojian Zhuang  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
220ab3f9a7SHaojian Zhuang  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
230ab3f9a7SHaojian Zhuang  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
240ab3f9a7SHaojian Zhuang  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
250ab3f9a7SHaojian Zhuang  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
260ab3f9a7SHaojian Zhuang  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
270ab3f9a7SHaojian Zhuang  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
280ab3f9a7SHaojian Zhuang  * POSSIBILITY OF SUCH DAMAGE.
290ab3f9a7SHaojian Zhuang  *
300ab3f9a7SHaojian Zhuang  * ARM PL061 GPIO Driver.
310ab3f9a7SHaojian Zhuang  * Reference to ARM DDI 0190B document.
320ab3f9a7SHaojian Zhuang  *
330ab3f9a7SHaojian Zhuang  */
340ab3f9a7SHaojian Zhuang 
350ab3f9a7SHaojian Zhuang #include <assert.h>
360ab3f9a7SHaojian Zhuang #include <cassert.h>
370ab3f9a7SHaojian Zhuang #include <debug.h>
380ab3f9a7SHaojian Zhuang #include <errno.h>
390ab3f9a7SHaojian Zhuang #include <gpio.h>
400ab3f9a7SHaojian Zhuang #include <mmio.h>
410ab3f9a7SHaojian Zhuang #include <pl061_gpio.h>
42*152c8c11SMasahiro Yamada #include <utils.h>
430ab3f9a7SHaojian Zhuang 
440ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS
450ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS	32
460ab3f9a7SHaojian Zhuang #endif	/* PLAT_PL061_MAX_GPIOS */
470ab3f9a7SHaojian Zhuang 
480ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios);
490ab3f9a7SHaojian Zhuang 
500ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES	((PLAT_PL061_MAX_GPIOS +		\
510ab3f9a7SHaojian Zhuang 				 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
520ab3f9a7SHaojian Zhuang 
530ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR		0x400
540ab3f9a7SHaojian Zhuang 
550ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061		8
560ab3f9a7SHaojian Zhuang 
570ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio);
580ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction);
590ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio);
600ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value);
610ab3f9a7SHaojian Zhuang 
620ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES];
630ab3f9a7SHaojian Zhuang 
640ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = {
650ab3f9a7SHaojian Zhuang 	.get_direction	= pl061_get_direction,
660ab3f9a7SHaojian Zhuang 	.set_direction	= pl061_set_direction,
670ab3f9a7SHaojian Zhuang 	.get_value	= pl061_get_value,
680ab3f9a7SHaojian Zhuang 	.set_value	= pl061_set_value,
690ab3f9a7SHaojian Zhuang };
700ab3f9a7SHaojian Zhuang 
710ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio)
720ab3f9a7SHaojian Zhuang {
730ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
740ab3f9a7SHaojian Zhuang 	unsigned int data, offset;
750ab3f9a7SHaojian Zhuang 
760ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
770ab3f9a7SHaojian Zhuang 
780ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
790ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
800ab3f9a7SHaojian Zhuang 	data = mmio_read_8(base_addr + PL061_GPIO_DIR);
810ab3f9a7SHaojian Zhuang 	if (data & BIT(offset))
820ab3f9a7SHaojian Zhuang 		return GPIO_DIR_OUT;
830ab3f9a7SHaojian Zhuang 	return GPIO_DIR_IN;
840ab3f9a7SHaojian Zhuang }
850ab3f9a7SHaojian Zhuang 
860ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction)
870ab3f9a7SHaojian Zhuang {
880ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
890ab3f9a7SHaojian Zhuang 	unsigned int data, offset;
900ab3f9a7SHaojian Zhuang 
910ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
920ab3f9a7SHaojian Zhuang 
930ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
940ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
950ab3f9a7SHaojian Zhuang 	if (direction == GPIO_DIR_OUT) {
960ab3f9a7SHaojian Zhuang 		data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset);
970ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + PL061_GPIO_DIR, data);
980ab3f9a7SHaojian Zhuang 	} else {
990ab3f9a7SHaojian Zhuang 		data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset);
1000ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + PL061_GPIO_DIR, data);
1010ab3f9a7SHaojian Zhuang 	}
1020ab3f9a7SHaojian Zhuang }
1030ab3f9a7SHaojian Zhuang 
1040ab3f9a7SHaojian Zhuang /*
1050ab3f9a7SHaojian Zhuang  * The offset of GPIODATA register is 0.
1060ab3f9a7SHaojian Zhuang  * The values read from GPIODATA are determined for each bit, by the mask bit
1070ab3f9a7SHaojian Zhuang  * derived from the address used to access the data register, PADDR[9:2].
1080ab3f9a7SHaojian Zhuang  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
1090ab3f9a7SHaojian Zhuang  * to be read, and bits that are 0 in the address mask cause the corresponding
1100ab3f9a7SHaojian Zhuang  * bits in GPIODATA to be read as 0, regardless of their value.
1110ab3f9a7SHaojian Zhuang  */
1120ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio)
1130ab3f9a7SHaojian Zhuang {
1140ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
1150ab3f9a7SHaojian Zhuang 	unsigned int offset;
1160ab3f9a7SHaojian Zhuang 
1170ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
1180ab3f9a7SHaojian Zhuang 
1190ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
1200ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
1210ab3f9a7SHaojian Zhuang 	if (mmio_read_8(base_addr + BIT(offset + 2)))
1220ab3f9a7SHaojian Zhuang 		return GPIO_LEVEL_HIGH;
1230ab3f9a7SHaojian Zhuang 	return GPIO_LEVEL_LOW;
1240ab3f9a7SHaojian Zhuang }
1250ab3f9a7SHaojian Zhuang 
1260ab3f9a7SHaojian Zhuang /*
1270ab3f9a7SHaojian Zhuang  * In order to write GPIODATA, the corresponding bits in the mask, resulting
1280ab3f9a7SHaojian Zhuang  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
1290ab3f9a7SHaojian Zhuang  * remain unchanged by the write.
1300ab3f9a7SHaojian Zhuang  */
1310ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value)
1320ab3f9a7SHaojian Zhuang {
1330ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
1340ab3f9a7SHaojian Zhuang 	int offset;
1350ab3f9a7SHaojian Zhuang 
1360ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
1370ab3f9a7SHaojian Zhuang 
1380ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
1390ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
1400ab3f9a7SHaojian Zhuang 	if (value == GPIO_LEVEL_HIGH)
1410ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + BIT(offset + 2), BIT(offset));
1420ab3f9a7SHaojian Zhuang 	else
1430ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + BIT(offset + 2), 0);
1440ab3f9a7SHaojian Zhuang }
1450ab3f9a7SHaojian Zhuang 
1460ab3f9a7SHaojian Zhuang 
1470ab3f9a7SHaojian Zhuang /*
1480ab3f9a7SHaojian Zhuang  * Register the PL061 GPIO controller with a base address and the offset
1490ab3f9a7SHaojian Zhuang  * of start pin in this GPIO controller.
1500ab3f9a7SHaojian Zhuang  * This function is called after pl061_gpio_ops_init().
1510ab3f9a7SHaojian Zhuang  */
1520ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev)
1530ab3f9a7SHaojian Zhuang {
1540ab3f9a7SHaojian Zhuang 	assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES));
1550ab3f9a7SHaojian Zhuang 
1560ab3f9a7SHaojian Zhuang 	pl061_reg_base[gpio_dev] = base_addr;
1570ab3f9a7SHaojian Zhuang }
1580ab3f9a7SHaojian Zhuang 
1590ab3f9a7SHaojian Zhuang /*
1600ab3f9a7SHaojian Zhuang  * Initialize PL061 GPIO controller with the total GPIO numbers in SoC.
1610ab3f9a7SHaojian Zhuang  */
1620ab3f9a7SHaojian Zhuang void pl061_gpio_init(void)
1630ab3f9a7SHaojian Zhuang {
1640ab3f9a7SHaojian Zhuang 	gpio_init(&pl061_gpio_ops);
1650ab3f9a7SHaojian Zhuang }
166