blob: af980e5eb2d9cc392cbf10770fe8fe867588af57 [file] [log] [blame]
Haojian Zhuang910bd8e2016-01-27 13:22:43 +08001/*
2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Haojian Zhuang910bd8e2016-01-27 13:22:43 +08005 *
6 * ARM PL061 GPIO Driver.
7 * Reference to ARM DDI 0190B document.
8 *
9 */
10
11#include <assert.h>
12#include <cassert.h>
13#include <debug.h>
14#include <errno.h>
15#include <gpio.h>
16#include <mmio.h>
17#include <pl061_gpio.h>
Masahiro Yamada73dfd2e2016-12-05 14:28:59 +090018#include <utils.h>
Haojian Zhuang910bd8e2016-01-27 13:22:43 +080019
20#if !PLAT_PL061_MAX_GPIOS
21# define PLAT_PL061_MAX_GPIOS 32
22#endif /* PLAT_PL061_MAX_GPIOS */
23
24CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios);
25
26#define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \
27 (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061)
28
29#define PL061_GPIO_DIR 0x400
30
31#define GPIOS_PER_PL061 8
Haojian Zhuang910bd8e2016-01-27 13:22:43 +080032
33static int pl061_get_direction(int gpio);
34static void pl061_set_direction(int gpio, int direction);
35static int pl061_get_value(int gpio);
36static void pl061_set_value(int gpio, int value);
37
38static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES];
39
40static const gpio_ops_t pl061_gpio_ops = {
41 .get_direction = pl061_get_direction,
42 .set_direction = pl061_set_direction,
43 .get_value = pl061_get_value,
44 .set_value = pl061_set_value,
45};
46
47static int pl061_get_direction(int gpio)
48{
49 uintptr_t base_addr;
50 unsigned int data, offset;
51
52 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
53
54 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
55 offset = gpio % GPIOS_PER_PL061;
56 data = mmio_read_8(base_addr + PL061_GPIO_DIR);
57 if (data & BIT(offset))
58 return GPIO_DIR_OUT;
59 return GPIO_DIR_IN;
60}
61
62static void pl061_set_direction(int gpio, int direction)
63{
64 uintptr_t base_addr;
65 unsigned int data, offset;
66
67 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
68
69 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
70 offset = gpio % GPIOS_PER_PL061;
71 if (direction == GPIO_DIR_OUT) {
72 data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset);
73 mmio_write_8(base_addr + PL061_GPIO_DIR, data);
74 } else {
75 data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset);
76 mmio_write_8(base_addr + PL061_GPIO_DIR, data);
77 }
78}
79
80/*
81 * The offset of GPIODATA register is 0.
82 * The values read from GPIODATA are determined for each bit, by the mask bit
83 * derived from the address used to access the data register, PADDR[9:2].
84 * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA
85 * to be read, and bits that are 0 in the address mask cause the corresponding
86 * bits in GPIODATA to be read as 0, regardless of their value.
87 */
88static int pl061_get_value(int gpio)
89{
90 uintptr_t base_addr;
91 unsigned int offset;
92
93 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
94
95 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
96 offset = gpio % GPIOS_PER_PL061;
97 if (mmio_read_8(base_addr + BIT(offset + 2)))
98 return GPIO_LEVEL_HIGH;
99 return GPIO_LEVEL_LOW;
100}
101
102/*
103 * In order to write GPIODATA, the corresponding bits in the mask, resulting
104 * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values
105 * remain unchanged by the write.
106 */
107static void pl061_set_value(int gpio, int value)
108{
109 uintptr_t base_addr;
110 int offset;
111
112 assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS));
113
114 base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061];
115 offset = gpio % GPIOS_PER_PL061;
116 if (value == GPIO_LEVEL_HIGH)
117 mmio_write_8(base_addr + BIT(offset + 2), BIT(offset));
118 else
119 mmio_write_8(base_addr + BIT(offset + 2), 0);
120}
121
122
123/*
124 * Register the PL061 GPIO controller with a base address and the offset
125 * of start pin in this GPIO controller.
126 * This function is called after pl061_gpio_ops_init().
127 */
128void pl061_gpio_register(uintptr_t base_addr, int gpio_dev)
129{
130 assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES));
131
132 pl061_reg_base[gpio_dev] = base_addr;
133}
134
135/*
136 * Initialize PL061 GPIO controller with the total GPIO numbers in SoC.
137 */
138void pl061_gpio_init(void)
139{
140 gpio_init(&pl061_gpio_ops);
141}