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 
9 static_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  */
18 static 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  */
44 static 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  */
86 static 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 
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 
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.
136  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
138  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
140  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
142  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
143 
144  switch (trigger) {
147  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
148  break;
151  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
152  break;
155  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
156  break;
159  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
160  break;
163  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
165  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
166  break;
169  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
171  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
172  break;
175  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
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 
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 
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 
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 
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 
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 
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 
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 
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 
257  dif_gpio_mask_t mask,
258  dif_toggle_t state) {
259  if (gpio == NULL) {
260  return kDifBadArg;
261  }
262 
263  switch (state) {
264  case kDifToggleEnabled:
266  gpio->base_addr, GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, mask, 0);
267  break;
268  case kDifToggleDisabled:
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 }