Software APIs
pinmux.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 
5 #include "sw/device/silicon_creator/lib/drivers/pinmux.h"
6 
12 #include "sw/device/silicon_creator/lib/drivers/otp.h"
13 
14 #include "gpio_regs.h"
16 #include "otp_ctrl_regs.h"
17 #include "pinmux_regs.h"
18 
19 enum {
20  /**
21  * Base address of the pinmux registers.
22  */
24 };
25 
26 /**
27  * A peripheral input and MIO pad to link it to.
28  */
29 typedef struct pinmux_input {
34 
35 /**
36  * An MIO pad and a peripheral output to link it to.
37  */
38 typedef struct pinmux_output {
43 
44 /**
45  * UART RX pin.
46  */
47 static const pinmux_input_t kInputUart0 = {
50  .pad = kTopEarlgreyMuxedPadsIoc3,
51 };
52 
53 /**
54  * UART TX pin.
55  */
56 static const pinmux_output_t kOutputUart0 = {
59  .pad = kTopEarlgreyMuxedPadsIoc4,
60 };
61 
62 /**
63  * SW strap pins.
64  */
65 #define PINMUX_ASSERT_EQ_(a, b) \
66  static_assert((a) == (b), "Unexpected software strap configuration.")
67 
70 PINMUX_ASSERT_EQ_(SW_STRAP_0_PAD, kTopEarlgreyMuxedPadsIoc0);
71 static const pinmux_input_t kInputSwStrap0 = {
72  .periph = SW_STRAP_0_PERIPH,
73  .insel = SW_STRAP_0_INSEL,
74  .pad = SW_STRAP_0_PAD,
75 };
76 
77 PINMUX_ASSERT_EQ_(SW_STRAP_1_PERIPH, kTopEarlgreyPinmuxPeripheralInGpioGpio23);
78 PINMUX_ASSERT_EQ_(SW_STRAP_1_INSEL, kTopEarlgreyPinmuxInselIoc1);
79 PINMUX_ASSERT_EQ_(SW_STRAP_1_PAD, kTopEarlgreyMuxedPadsIoc1);
80 static const pinmux_input_t kInputSwStrap1 = {
81  .periph = SW_STRAP_1_PERIPH,
82  .insel = SW_STRAP_1_INSEL,
83  .pad = SW_STRAP_1_PAD,
84 };
85 
86 PINMUX_ASSERT_EQ_(SW_STRAP_2_PERIPH, kTopEarlgreyPinmuxPeripheralInGpioGpio24);
87 PINMUX_ASSERT_EQ_(SW_STRAP_2_INSEL, kTopEarlgreyPinmuxInselIoc2);
88 PINMUX_ASSERT_EQ_(SW_STRAP_2_PAD, kTopEarlgreyMuxedPadsIoc2);
89 static const pinmux_input_t kInputSwStrap2 = {
90  .periph = SW_STRAP_2_PERIPH,
91  .insel = SW_STRAP_2_INSEL,
92  .pad = SW_STRAP_2_PAD,
93 };
94 
95 /**
96  * Sets the input pad for the specified peripheral input.
97  *
98  * @param input A peripheral input and MIO pad to link it to.
99  */
100 static void configure_input(pinmux_input_t input) {
101  abs_mmio_write32(kBase + PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET +
102  input.periph * sizeof(uint32_t),
103  input.insel);
104 }
105 
106 /**
107  * Enables or disables pull-up/pull-down for the specified pad.
108  *
109  * @param pad A MIO pad.
110  * @param enable Whether the internal pull resistor should be enabled.
111  * @param up Whether the pull resistor should pull up(true) or down(false).
112  */
113 static void enable_pull(top_earlgrey_muxed_pads_t pad, bool enable, bool up) {
114  uint32_t reg = 0;
115  reg = bitfield_bit32_write(reg, PINMUX_MIO_PAD_ATTR_0_PULL_EN_0_BIT, enable);
116  reg = bitfield_bit32_write(reg, PINMUX_MIO_PAD_ATTR_0_PULL_SELECT_0_BIT, up);
117  abs_mmio_write32(
118  kBase + PINMUX_MIO_PAD_ATTR_0_REG_OFFSET + pad * sizeof(uint32_t), reg);
119 }
120 
121 /**
122  * Delay while we wait for pinmux values to stabilize after changing resistor
123  * pull-up/down values.
124  */
125 static void pinmux_prop_delay(void) {
126  // Wait for pull downs to propagate to the physical pads.
127  CSR_WRITE(CSR_REG_MCYCLE, 0);
128  uint32_t mcycle;
129  do {
130  CSR_READ(CSR_REG_MCYCLE, &mcycle);
131  } while (mcycle < PINMUX_PAD_ATTR_PROP_CYCLES);
132 }
133 
134 /**
135  * Read a single strap pin considering weak/strong pull resistors.
136  *
137  * @param input The input pin to read.
138  * @return The value of the pin.
139  */
140 static uint32_t read_strap_pin(pinmux_input_t input) {
141  // First, disable all pull resistors and read the state of the pin.
142  enable_pull(input.pad, false, false);
143  pinmux_prop_delay();
144  uint32_t val =
145  abs_mmio_read32(TOP_EARLGREY_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET);
146  uint32_t state = bitfield_bit32_read(val, input.periph);
147  uint32_t res = state ? 2 : 0;
148 
149  // Then, enable the opposite pull to the observed state.
150  // If the external signal is weak, the internal pull resistor will win; if
151  // the external signal is strong, the external resistor will win.
152  enable_pull(input.pad, true, !state);
153  pinmux_prop_delay();
154 
155  val = abs_mmio_read32(TOP_EARLGREY_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET);
156  state = bitfield_bit32_read(val, input.periph);
157  res += state ? 1 : 0;
158  return res;
159 }
160 
161 /**
162  * Sets the peripheral output for each specified output pad.
163  *
164  * @param output An MIO pad and a peripheral output to link it to.
165  */
166 static void configure_output(pinmux_output_t output) {
167  abs_mmio_write32(
168  kBase + PINMUX_MIO_OUTSEL_0_REG_OFFSET + output.mio * sizeof(uint32_t),
169  output.outsel);
170 }
171 
172 void pinmux_init_uart0_tx(void) { configure_output(kOutputUart0); }
173 
174 void pinmux_init(void) {
175  uint32_t bootstrap_dis =
176  otp_read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_DIS_OFFSET);
177  if (launder32(bootstrap_dis) != kHardenedBoolTrue) {
178  HARDENED_CHECK_NE(bootstrap_dis, kHardenedBoolTrue);
179  // Note: attributes should be configured before the pinmux matrix to avoid
180  // "undesired electrical behavior and/or contention at the pads".
181  enable_pull(kInputSwStrap0.pad, /*enable=*/true, /*up=*/false);
182  enable_pull(kInputSwStrap1.pad, /*enable=*/true, /*up=*/false);
183  enable_pull(kInputSwStrap2.pad, /*enable=*/true, /*up=*/false);
184  // Wait for pull downs to propagate to the physical pads.
185  pinmux_prop_delay();
186 
187  configure_input(kInputSwStrap0);
188  configure_input(kInputSwStrap1);
189  configure_input(kInputSwStrap2);
190  }
191 
192  // Pull the UART_RX line high (idle state for UART). This prevents a
193  // floating UART_RX from incorrectly triggering serial break.
194  enable_pull(kInputUart0.pad, /*enable=*/true, /*up=*/true);
195  configure_input(kInputUart0);
196  configure_output(kOutputUart0);
197 }
198 
199 uint32_t pinmux_read_straps(void) {
200  uint32_t value = 0;
201  value |= read_strap_pin(kInputSwStrap0);
202  value |= read_strap_pin(kInputSwStrap1) << 2;
203  value |= read_strap_pin(kInputSwStrap2) << 4;
204  return value;
205 }