Software APIs
dif_gpio.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include "gpio_regs.h" // Generated.
8
9static_assert(kDifGpioNumPins <= 32,
10 "This implementation assumes that the number of pins is less "
11 "than or equal to 32");
12
13/**
14 * Gives the mask that corresponds to the given bit index.
15 *
16 * @param index Bit index in a 32-bit register.
17 */
18static uint32_t index_to_mask(uint32_t index) { return 1u << index; }
19
20/**
21 * Perform a masked write to a GPIO register.
22 *
23 * The GPIO device provides masked bit-level atomic writes to its DIRECT_OUT
24 * and DIRECT_OE registers. This allows software to modify half of the bits
25 * at a time without requiring a read-modify-write. Note that depending on the
26 * value of the `mask`, this function may perform two writes.
27 *
28 * For instance, DIRECT_OUT's lower and upper halves can be modified by
29 * MASKED_OUT_LOWER and MASKED_OUT_UPPER, respectively. Upper half of
30 * MASKED_OUT_LOWER is the mask that determines which bits in the lower half of
31 * DIRECT_OUT will be modified, while lower half of MASKED_OUT_LOWER determines
32 * their values. MASKED_OUT_UPPER behaves in the same way for modifying the
33 * upper half of DIRECT_OUT.
34 *
35 * @param gpio GPIO instance.
36 * @param reg_lower_offset Offset of the masked access register that corresponds
37 * to the lower half of the actual register.
38 * @param reg_upper_offset Offset of the masked access register that corresponds
39 * to the upper half of the actual register.
40 * @param mask Mask that identifies the bits to write to.
41 * @param val Value to write.
42 */
44static dif_result_t gpio_masked_write(const dif_gpio_t *gpio,
45 ptrdiff_t reg_lower_offset,
46 ptrdiff_t reg_upper_offset, uint32_t mask,
47 uint32_t val) {
48 if (gpio == NULL) {
49 return kDifBadArg;
50 }
51
52 const uint32_t mask_lower_half = mask & 0x0000FFFFu;
53 const uint32_t mask_upper_half = mask & 0xFFFF0000u;
54 if (mask_lower_half != 0) {
55 mmio_region_write32(gpio->base_addr, reg_lower_offset,
56 (mask_lower_half << 16) | (val & 0x0000FFFFu));
57 }
58 if (mask_upper_half != 0) {
59 mmio_region_write32(gpio->base_addr, reg_upper_offset,
60 mask_upper_half | ((val & 0xFFFF0000u) >> 16));
61 }
62
63 return kDifOk;
64}
65
66/**
67 * Perform a masked write to a single bit of a GPIO register.
68 *
69 * The GPIO device provides masked bit-level atomic writes to its DIRECT_OUT
70 * and DIRECT_OE registers. This allows software to modify half of the bits
71 * at a time without requiring a read-modify-write. This function is guaranteed
72 * to perform only one write since it never needs to access both halves of a
73 * register.
74 *
75 * See also `gpio_masked_write()`.
76 *
77 * @param gpio GPIO instance.
78 * @param reg_lower_offset Offset of the masked access register that corresponds
79 * to the lower half of the actual register.
80 * @param reg_upper_offset Offset of the masked access register that corresponds
81 * to the upper half of the actual register.
82 * @param index Zero-based index of the bit to write to.
83 * @param val Value to write.
84 */
86static dif_result_t gpio_masked_bit_write(const dif_gpio_t *gpio,
87 ptrdiff_t reg_lower_offset,
88 ptrdiff_t reg_upper_offset,
89 uint32_t index, bool val) {
90 if (gpio == NULL || index >= kDifGpioNumPins) {
91 return kDifBadArg;
92 }
93
94 // Write to reg_lower_offset if the bit is in the lower half, write to
95 // reg_upper_offset otherwise.
96 const ptrdiff_t offset = (index < 16) ? reg_lower_offset : reg_upper_offset;
97 // Since masked access writes to half of a register, index mod 16 gives the
98 // index of the bit in the half-word mask.
99 const uint32_t mask = index_to_mask(index % 16);
100 mmio_region_write32(gpio->base_addr, offset,
101 (mask << 16) | (val ? mask : 0u));
102
103 return kDifOk;
104}
105
106dif_result_t dif_gpio_reset(const dif_gpio_t *gpio) {
107 if (gpio == NULL) {
108 return kDifBadArg;
109 }
110
111 // We don't need to write to `GPIO_MASKED_OE_*` and `GPIO_MASKED_OUT_*`
112 // since we directly reset `GPIO_DIRECT_OE` and `GPIO_DIRECT_OUT` below.
113 mmio_region_write32(gpio->base_addr, GPIO_INTR_ENABLE_REG_OFFSET, 0);
114 mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OE_REG_OFFSET, 0);
115 mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OUT_REG_OFFSET, 0);
116 mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
117 mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
118 mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, 0);
119 mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, 0);
120 mmio_region_write32(gpio->base_addr, GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, 0);
121 // Also clear all pending interrupts
122 mmio_region_write32(gpio->base_addr, GPIO_INTR_STATE_REG_OFFSET, 0xFFFFFFFFu);
123
124 return kDifOk;
125}
126
127dif_result_t dif_gpio_irq_set_trigger(const dif_gpio_t *gpio,
128 dif_gpio_mask_t mask,
129 dif_gpio_irq_trigger_t trigger) {
130 if (gpio == NULL) {
131 return kDifBadArg;
132 }
133
134 // Disable all interrupt triggers for the given mask.
135 mmio_region_nonatomic_clear_mask32(
136 gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
137 mmio_region_nonatomic_clear_mask32(
138 gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
139 mmio_region_nonatomic_clear_mask32(
140 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
141 mmio_region_nonatomic_clear_mask32(
142 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
143
144 switch (trigger) {
146 mmio_region_nonatomic_set_mask32(
147 gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
148 break;
150 mmio_region_nonatomic_set_mask32(
151 gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
152 break;
154 mmio_region_nonatomic_set_mask32(
155 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
156 break;
158 mmio_region_nonatomic_set_mask32(
159 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
160 break;
162 mmio_region_nonatomic_set_mask32(
163 gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
164 mmio_region_nonatomic_set_mask32(
165 gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
166 break;
168 mmio_region_nonatomic_set_mask32(
169 gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
170 mmio_region_nonatomic_set_mask32(
171 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
172 break;
174 mmio_region_nonatomic_set_mask32(
175 gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
176 mmio_region_nonatomic_set_mask32(
177 gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
178 break;
179 default:
180 return kDifError;
181 }
182
183 return kDifOk;
184}
185
186dif_result_t dif_gpio_read_all(const dif_gpio_t *gpio,
187 dif_gpio_state_t *state) {
188 if (gpio == NULL || state == NULL) {
189 return kDifBadArg;
190 }
191
192 *state = mmio_region_read32(gpio->base_addr, GPIO_DATA_IN_REG_OFFSET);
193
194 return kDifOk;
195}
196
197dif_result_t dif_gpio_read(const dif_gpio_t *gpio, dif_gpio_pin_t pin,
198 bool *state) {
199 if (gpio == NULL || state == NULL || pin >= kDifGpioNumPins) {
200 return kDifBadArg;
201 }
202
203 *state = mmio_region_get_bit32(gpio->base_addr, GPIO_DATA_IN_REG_OFFSET, pin);
204
205 return kDifOk;
206}
207
208dif_result_t dif_gpio_write_all(const dif_gpio_t *gpio,
209 dif_gpio_state_t state) {
210 if (gpio == NULL) {
211 return kDifBadArg;
212 }
213
214 mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OUT_REG_OFFSET, state);
215
216 return kDifOk;
217}
218
219dif_result_t dif_gpio_write(const dif_gpio_t *gpio, dif_gpio_pin_t pin,
220 bool state) {
221 return gpio_masked_bit_write(gpio, GPIO_MASKED_OUT_LOWER_REG_OFFSET,
222 GPIO_MASKED_OUT_UPPER_REG_OFFSET, pin, state);
223}
224
225dif_result_t dif_gpio_write_masked(const dif_gpio_t *gpio, dif_gpio_mask_t mask,
226 dif_gpio_state_t state) {
227 return gpio_masked_write(gpio, GPIO_MASKED_OUT_LOWER_REG_OFFSET,
228 GPIO_MASKED_OUT_UPPER_REG_OFFSET, mask, state);
229}
230
231dif_result_t dif_gpio_output_set_enabled_all(const dif_gpio_t *gpio,
232 dif_gpio_state_t state) {
233 if (gpio == NULL) {
234 return kDifBadArg;
235 }
236
237 mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OE_REG_OFFSET, state);
238
239 return kDifOk;
240}
241
242dif_result_t dif_gpio_output_set_enabled(const dif_gpio_t *gpio,
243 dif_gpio_pin_t pin,
244 dif_toggle_t state) {
245 return gpio_masked_bit_write(gpio, GPIO_MASKED_OE_LOWER_REG_OFFSET,
246 GPIO_MASKED_OE_UPPER_REG_OFFSET, pin, state);
247}
248
249dif_result_t dif_gpio_output_set_enabled_masked(const dif_gpio_t *gpio,
250 dif_gpio_mask_t mask,
251 dif_gpio_state_t state) {
252 return gpio_masked_write(gpio, GPIO_MASKED_OE_LOWER_REG_OFFSET,
253 GPIO_MASKED_OE_UPPER_REG_OFFSET, mask, state);
254}
255
256dif_result_t dif_gpio_input_noise_filter_set_enabled(const dif_gpio_t *gpio,
257 dif_gpio_mask_t mask,
258 dif_toggle_t state) {
259 if (gpio == NULL) {
260 return kDifBadArg;
261 }
262
263 switch (state) {
265 mmio_region_nonatomic_set_mask32(
266 gpio->base_addr, GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, mask, 0);
267 break;
269 mmio_region_nonatomic_clear_mask32(
270 gpio->base_addr, GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, mask, 0);
271 break;
272 default:
273 return kDifBadArg;
274 }
275
276 return kDifOk;
277}