Software APIs
dif_gpio.c
1 // Copyright lowRISC contributors.
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 /**
10  * Gives the mask that corresponds to the given bit index.
11  *
12  * @param index Bit index in a 32-bit register.
13  */
14 static uint32_t index_to_mask(uint32_t index) { return 1u << index; }
15 
16 /**
17  * Perform a masked write to a GPIO register.
18  *
19  * The GPIO device provides masked bit-level atomic writes to its DIRECT_OUT
20  * and DIRECT_OE registers. This allows software to modify half of the bits
21  * at a time without requiring a read-modify-write. Note that depending on the
22  * value of the `mask`, this function may perform two writes.
23  *
24  * For instance, DIRECT_OUT's lower and upper halves can be modified by
25  * MASKED_OUT_LOWER and MASKED_OUT_UPPER, respectively. Upper half of
26  * MASKED_OUT_LOWER is the mask that determines which bits in the lower half of
27  * DIRECT_OUT will be modified, while lower half of MASKED_OUT_LOWER determines
28  * their values. MASKED_OUT_UPPER behaves in the same way for modifying the
29  * upper half of DIRECT_OUT.
30  *
31  * @param gpio GPIO instance.
32  * @param reg_lower_offset Offset of the masked access register that corresponds
33  * to the lower half of the actual register.
34  * @param reg_upper_offset Offset of the masked access register that corresponds
35  * to the upper half of the actual register.
36  * @param mask Mask that identifies the bits to write to.
37  * @param val Value to write.
38  */
40 static dif_result_t gpio_masked_write(const dif_gpio_t *gpio,
41  ptrdiff_t reg_lower_offset,
42  ptrdiff_t reg_upper_offset, uint32_t mask,
43  uint32_t val) {
44  if (gpio == NULL) {
45  return kDifBadArg;
46  }
47 
48  const uint32_t mask_lower_half = mask & 0x0000FFFFu;
49  const uint32_t mask_upper_half = mask & 0xFFFF0000u;
50  if (mask_lower_half != 0) {
51  mmio_region_write32(gpio->base_addr, reg_lower_offset,
52  (mask_lower_half << 16) | (val & 0x0000FFFFu));
53  }
54  if (mask_upper_half != 0) {
55  mmio_region_write32(gpio->base_addr, reg_upper_offset,
56  mask_upper_half | ((val & 0xFFFF0000u) >> 16));
57  }
58 
59  return kDifOk;
60 }
61 
62 /**
63  * Perform a masked write to a single bit of a GPIO register.
64  *
65  * The GPIO device provides masked bit-level atomic writes to its DIRECT_OUT
66  * and DIRECT_OE registers. This allows software to modify half of the bits
67  * at a time without requiring a read-modify-write. This function is guaranteed
68  * to perform only one write since it never needs to access both halves of a
69  * register.
70  *
71  * See also `gpio_masked_write()`.
72  *
73  * @param gpio GPIO instance.
74  * @param reg_lower_offset Offset of the masked access register that corresponds
75  * to the lower half of the actual register.
76  * @param reg_upper_offset Offset of the masked access register that corresponds
77  * to the upper half of the actual register.
78  * @param index Zero-based index of the bit to write to.
79  * @param val Value to write.
80  */
82 static dif_result_t gpio_masked_bit_write(const dif_gpio_t *gpio,
83  ptrdiff_t reg_lower_offset,
84  ptrdiff_t reg_upper_offset,
85  uint32_t index, bool val) {
86  if (gpio == NULL) {
87  return kDifBadArg;
88  }
89 
90  // Write to reg_lower_offset if the bit is in the lower half, write to
91  // reg_upper_offset otherwise.
92  const ptrdiff_t offset = (index < 16) ? reg_lower_offset : reg_upper_offset;
93  // Since masked access writes to half of a register, index mod 16 gives the
94  // index of the bit in the half-word mask.
95  const uint32_t mask = index_to_mask(index % 16);
96  mmio_region_write32(gpio->base_addr, offset,
97  (mask << 16) | (val ? mask : 0u));
98 
99  return kDifOk;
100 }
101 
103  if (gpio == NULL) {
104  return kDifBadArg;
105  }
106 
107  // We don't need to write to `GPIO_MASKED_OE_*` and `GPIO_MASKED_OUT_*`
108  // since we directly reset `GPIO_DIRECT_OE` and `GPIO_DIRECT_OUT` below.
109  mmio_region_write32(gpio->base_addr, GPIO_INTR_ENABLE_REG_OFFSET, 0);
110  mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OE_REG_OFFSET, 0);
111  mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OUT_REG_OFFSET, 0);
112  mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
113  mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
114  mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, 0);
115  mmio_region_write32(gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, 0);
116  mmio_region_write32(gpio->base_addr, GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, 0);
117  // Also clear all pending interrupts
118  mmio_region_write32(gpio->base_addr, GPIO_INTR_STATE_REG_OFFSET, 0xFFFFFFFFu);
119 
120  return kDifOk;
121 }
122 
124  dif_gpio_mask_t mask,
125  dif_gpio_irq_trigger_t trigger) {
126  if (gpio == NULL) {
127  return kDifBadArg;
128  }
129 
130  // Disable all interrupt triggers for the given mask.
132  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
134  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
136  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
138  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
139 
140  switch (trigger) {
143  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
144  break;
147  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
148  break;
151  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
152  break;
155  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
156  break;
159  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
161  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
162  break;
165  gpio->base_addr, GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, mask, 0);
167  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, mask, 0);
168  break;
171  gpio->base_addr, GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, mask, 0);
173  gpio->base_addr, GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, mask, 0);
174  break;
175  default:
176  return kDifError;
177  }
178 
179  return kDifOk;
180 }
181 
183  dif_gpio_state_t *state) {
184  if (gpio == NULL || state == NULL) {
185  return kDifBadArg;
186  }
187 
188  *state = mmio_region_read32(gpio->base_addr, GPIO_DATA_IN_REG_OFFSET);
189 
190  return kDifOk;
191 }
192 
194  bool *state) {
195  if (gpio == NULL || state == NULL) {
196  return kDifBadArg;
197  }
198 
199  *state = mmio_region_get_bit32(gpio->base_addr, GPIO_DATA_IN_REG_OFFSET, pin);
200 
201  return kDifOk;
202 }
203 
205  dif_gpio_state_t state) {
206  if (gpio == NULL) {
207  return kDifBadArg;
208  }
209 
210  mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OUT_REG_OFFSET, state);
211 
212  return kDifOk;
213 }
214 
216  bool state) {
217  return gpio_masked_bit_write(gpio, GPIO_MASKED_OUT_LOWER_REG_OFFSET,
218  GPIO_MASKED_OUT_UPPER_REG_OFFSET, pin, state);
219 }
220 
222  dif_gpio_state_t state) {
223  return gpio_masked_write(gpio, GPIO_MASKED_OUT_LOWER_REG_OFFSET,
224  GPIO_MASKED_OUT_UPPER_REG_OFFSET, mask, state);
225 }
226 
228  dif_gpio_state_t state) {
229  if (gpio == NULL) {
230  return kDifBadArg;
231  }
232 
233  mmio_region_write32(gpio->base_addr, GPIO_DIRECT_OE_REG_OFFSET, state);
234 
235  return kDifOk;
236 }
237 
239  dif_gpio_pin_t pin,
240  dif_toggle_t state) {
241  if (gpio == NULL) {
242  return kDifBadArg;
243  }
244 
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 }