xref: /rk3399_ARM-atf/drivers/arm/pl061/pl061_gpio.c (revision 0ab3f9a741afcded4ddd4cf4da8a2bd2d5da8551)
1*0ab3f9a7SHaojian Zhuang /*
2*0ab3f9a7SHaojian Zhuang  * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3*0ab3f9a7SHaojian Zhuang  *
4*0ab3f9a7SHaojian Zhuang  * Redistribution and use in source and binary forms, with or without
5*0ab3f9a7SHaojian Zhuang  * modification, are permitted provided that the following conditions are met:
6*0ab3f9a7SHaojian Zhuang  *
7*0ab3f9a7SHaojian Zhuang  * Redistributions of source code must retain the above copyright notice, this
8*0ab3f9a7SHaojian Zhuang  * list of conditions and the following disclaimer.
9*0ab3f9a7SHaojian Zhuang  *
10*0ab3f9a7SHaojian Zhuang  * Redistributions in binary form must reproduce the above copyright notice,
11*0ab3f9a7SHaojian Zhuang  * this list of conditions and the following disclaimer in the documentation
12*0ab3f9a7SHaojian Zhuang  * and/or other materials provided with the distribution.
13*0ab3f9a7SHaojian Zhuang  *
14*0ab3f9a7SHaojian Zhuang  * Neither the name of ARM nor the names of its contributors may be used
15*0ab3f9a7SHaojian Zhuang  * to endorse or promote products derived from this software without specific
16*0ab3f9a7SHaojian Zhuang  * prior written permission.
17*0ab3f9a7SHaojian Zhuang  *
18*0ab3f9a7SHaojian Zhuang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*0ab3f9a7SHaojian Zhuang  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*0ab3f9a7SHaojian Zhuang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*0ab3f9a7SHaojian Zhuang  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22*0ab3f9a7SHaojian Zhuang  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*0ab3f9a7SHaojian Zhuang  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*0ab3f9a7SHaojian Zhuang  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*0ab3f9a7SHaojian Zhuang  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*0ab3f9a7SHaojian Zhuang  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*0ab3f9a7SHaojian Zhuang  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*0ab3f9a7SHaojian Zhuang  * POSSIBILITY OF SUCH DAMAGE.
29*0ab3f9a7SHaojian Zhuang  *
30*0ab3f9a7SHaojian Zhuang  * ARM PL061 GPIO Driver.
31*0ab3f9a7SHaojian Zhuang  * Reference to ARM DDI 0190B document.
32*0ab3f9a7SHaojian Zhuang  *
33*0ab3f9a7SHaojian Zhuang  */
34*0ab3f9a7SHaojian Zhuang 
35*0ab3f9a7SHaojian Zhuang #include <assert.h>
36*0ab3f9a7SHaojian Zhuang #include <cassert.h>
37*0ab3f9a7SHaojian Zhuang #include <debug.h>
38*0ab3f9a7SHaojian Zhuang #include <errno.h>
39*0ab3f9a7SHaojian Zhuang #include <gpio.h>
40*0ab3f9a7SHaojian Zhuang #include <mmio.h>
41*0ab3f9a7SHaojian Zhuang #include <pl061_gpio.h>
42*0ab3f9a7SHaojian Zhuang 
43*0ab3f9a7SHaojian Zhuang #if !PLAT_PL061_MAX_GPIOS
44*0ab3f9a7SHaojian Zhuang # define PLAT_PL061_MAX_GPIOS	32
45*0ab3f9a7SHaojian Zhuang #endif	/* PLAT_PL061_MAX_GPIOS */
46*0ab3f9a7SHaojian Zhuang 
47*0ab3f9a7SHaojian Zhuang CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios);
48*0ab3f9a7SHaojian Zhuang 
49*0ab3f9a7SHaojian Zhuang #define MAX_GPIO_DEVICES	((PLAT_PL061_MAX_GPIOS +		\
50*0ab3f9a7SHaojian Zhuang 				 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
51*0ab3f9a7SHaojian Zhuang 
52*0ab3f9a7SHaojian Zhuang #define PL061_GPIO_DIR		0x400
53*0ab3f9a7SHaojian Zhuang 
54*0ab3f9a7SHaojian Zhuang #define GPIOS_PER_PL061		8
55*0ab3f9a7SHaojian Zhuang #define BIT(nr)			(1UL << (nr))
56*0ab3f9a7SHaojian Zhuang 
57*0ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio);
58*0ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction);
59*0ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio);
60*0ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value);
61*0ab3f9a7SHaojian Zhuang 
62*0ab3f9a7SHaojian Zhuang static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES];
63*0ab3f9a7SHaojian Zhuang 
64*0ab3f9a7SHaojian Zhuang static const gpio_ops_t pl061_gpio_ops = {
65*0ab3f9a7SHaojian Zhuang 	.get_direction	= pl061_get_direction,
66*0ab3f9a7SHaojian Zhuang 	.set_direction	= pl061_set_direction,
67*0ab3f9a7SHaojian Zhuang 	.get_value	= pl061_get_value,
68*0ab3f9a7SHaojian Zhuang 	.set_value	= pl061_set_value,
69*0ab3f9a7SHaojian Zhuang };
70*0ab3f9a7SHaojian Zhuang 
71*0ab3f9a7SHaojian Zhuang static int pl061_get_direction(int gpio)
72*0ab3f9a7SHaojian Zhuang {
73*0ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
74*0ab3f9a7SHaojian Zhuang 	unsigned int data, offset;
75*0ab3f9a7SHaojian Zhuang 
76*0ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
77*0ab3f9a7SHaojian Zhuang 
78*0ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
79*0ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
80*0ab3f9a7SHaojian Zhuang 	data = mmio_read_8(base_addr + PL061_GPIO_DIR);
81*0ab3f9a7SHaojian Zhuang 	if (data & BIT(offset))
82*0ab3f9a7SHaojian Zhuang 		return GPIO_DIR_OUT;
83*0ab3f9a7SHaojian Zhuang 	return GPIO_DIR_IN;
84*0ab3f9a7SHaojian Zhuang }
85*0ab3f9a7SHaojian Zhuang 
86*0ab3f9a7SHaojian Zhuang static void pl061_set_direction(int gpio, int direction)
87*0ab3f9a7SHaojian Zhuang {
88*0ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
89*0ab3f9a7SHaojian Zhuang 	unsigned int data, offset;
90*0ab3f9a7SHaojian Zhuang 
91*0ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
92*0ab3f9a7SHaojian Zhuang 
93*0ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
94*0ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
95*0ab3f9a7SHaojian Zhuang 	if (direction == GPIO_DIR_OUT) {
96*0ab3f9a7SHaojian Zhuang 		data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset);
97*0ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + PL061_GPIO_DIR, data);
98*0ab3f9a7SHaojian Zhuang 	} else {
99*0ab3f9a7SHaojian Zhuang 		data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset);
100*0ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + PL061_GPIO_DIR, data);
101*0ab3f9a7SHaojian Zhuang 	}
102*0ab3f9a7SHaojian Zhuang }
103*0ab3f9a7SHaojian Zhuang 
104*0ab3f9a7SHaojian Zhuang /*
105*0ab3f9a7SHaojian Zhuang  * The offset of GPIODATA register is 0.
106*0ab3f9a7SHaojian Zhuang  * The values read from GPIODATA are determined for each bit, by the mask bit
107*0ab3f9a7SHaojian Zhuang  * derived from the address used to access the data register, PADDR[9:2].
108*0ab3f9a7SHaojian Zhuang  * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
109*0ab3f9a7SHaojian Zhuang  * to be read, and bits that are 0 in the address mask cause the corresponding
110*0ab3f9a7SHaojian Zhuang  * bits in GPIODATA to be read as 0, regardless of their value.
111*0ab3f9a7SHaojian Zhuang  */
112*0ab3f9a7SHaojian Zhuang static int pl061_get_value(int gpio)
113*0ab3f9a7SHaojian Zhuang {
114*0ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
115*0ab3f9a7SHaojian Zhuang 	unsigned int offset;
116*0ab3f9a7SHaojian Zhuang 
117*0ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
118*0ab3f9a7SHaojian Zhuang 
119*0ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
120*0ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
121*0ab3f9a7SHaojian Zhuang 	if (mmio_read_8(base_addr + BIT(offset + 2)))
122*0ab3f9a7SHaojian Zhuang 		return GPIO_LEVEL_HIGH;
123*0ab3f9a7SHaojian Zhuang 	return GPIO_LEVEL_LOW;
124*0ab3f9a7SHaojian Zhuang }
125*0ab3f9a7SHaojian Zhuang 
126*0ab3f9a7SHaojian Zhuang /*
127*0ab3f9a7SHaojian Zhuang  * In order to write GPIODATA, the corresponding bits in the mask, resulting
128*0ab3f9a7SHaojian Zhuang  * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
129*0ab3f9a7SHaojian Zhuang  * remain unchanged by the write.
130*0ab3f9a7SHaojian Zhuang  */
131*0ab3f9a7SHaojian Zhuang static void pl061_set_value(int gpio, int value)
132*0ab3f9a7SHaojian Zhuang {
133*0ab3f9a7SHaojian Zhuang 	uintptr_t base_addr;
134*0ab3f9a7SHaojian Zhuang 	int offset;
135*0ab3f9a7SHaojian Zhuang 
136*0ab3f9a7SHaojian Zhuang 	assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
137*0ab3f9a7SHaojian Zhuang 
138*0ab3f9a7SHaojian Zhuang 	base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
139*0ab3f9a7SHaojian Zhuang 	offset = gpio % GPIOS_PER_PL061;
140*0ab3f9a7SHaojian Zhuang 	if (value == GPIO_LEVEL_HIGH)
141*0ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + BIT(offset + 2), BIT(offset));
142*0ab3f9a7SHaojian Zhuang 	else
143*0ab3f9a7SHaojian Zhuang 		mmio_write_8(base_addr + BIT(offset + 2), 0);
144*0ab3f9a7SHaojian Zhuang }
145*0ab3f9a7SHaojian Zhuang 
146*0ab3f9a7SHaojian Zhuang 
147*0ab3f9a7SHaojian Zhuang /*
148*0ab3f9a7SHaojian Zhuang  * Register the PL061 GPIO controller with a base address and the offset
149*0ab3f9a7SHaojian Zhuang  * of start pin in this GPIO controller.
150*0ab3f9a7SHaojian Zhuang  * This function is called after pl061_gpio_ops_init().
151*0ab3f9a7SHaojian Zhuang  */
152*0ab3f9a7SHaojian Zhuang void pl061_gpio_register(uintptr_t base_addr, int gpio_dev)
153*0ab3f9a7SHaojian Zhuang {
154*0ab3f9a7SHaojian Zhuang 	assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES));
155*0ab3f9a7SHaojian Zhuang 
156*0ab3f9a7SHaojian Zhuang 	pl061_reg_base[gpio_dev] = base_addr;
157*0ab3f9a7SHaojian Zhuang }
158*0ab3f9a7SHaojian Zhuang 
159*0ab3f9a7SHaojian Zhuang /*
160*0ab3f9a7SHaojian Zhuang  * Initialize PL061 GPIO controller with the total GPIO numbers in SoC.
161*0ab3f9a7SHaojian Zhuang  */
162*0ab3f9a7SHaojian Zhuang void pl061_gpio_init(void)
163*0ab3f9a7SHaojian Zhuang {
164*0ab3f9a7SHaojian Zhuang 	gpio_init(&pl061_gpio_ops);
165*0ab3f9a7SHaojian Zhuang }
166