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