Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_sys_io_baremetal_stm32f429 / sys_io_baremetal.cc
1 // Copyright 2019 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include <cinttypes>
16
17 #include "pw_preprocessor/compiler.h"
18 #include "pw_sys_io/sys_io.h"
19
20 namespace {
21
22 // Default core clock. This is technically not a constant, but since this app
23 // doesn't change the system clock a constant will suffice.
24 constexpr uint32_t kSystemCoreClock = 16000000;
25
26 // Base address for everything peripheral-related on the STM32F4xx.
27 constexpr uint32_t kPeripheralBaseAddr = 0x40000000u;
28 // Base address for everything AHB1-related on the STM32F4xx.
29 constexpr uint32_t kAhb1PeripheralBase = kPeripheralBaseAddr + 0x00020000U;
30 // Base address for everything APB2-related on the STM32F4xx.
31 constexpr uint32_t kApb2PeripheralBase = kPeripheralBaseAddr + 0x00010000U;
32
33 // Reset/clock configuration block (RCC).
34 // `reserved` fields are unimplemented features, and are present to ensure
35 // proper alignment of registers that are in use.
36 PW_PACKED(struct) RccBlock {
37   uint32_t reserved1[12];
38   uint32_t ahb1_config;
39   uint32_t reserved2[4];
40   uint32_t apb2_config;
41 };
42
43 // Mask for ahb1_config (AHB1ENR) to enable the "A" GPIO pins.
44 constexpr uint32_t kGpioAEnable = 0x1u;
45
46 // Mask for apb2_config (APB2ENR) to enable USART1.
47 constexpr uint32_t kUsart1Enable = 0x1u << 4;
48
49 // GPIO register block definition.
50 PW_PACKED(struct) GpioBlock {
51   uint32_t modes;
52   uint32_t out_type;
53   uint32_t out_speed;
54   uint32_t pull_up_down;
55   uint32_t input_data;
56   uint32_t output_data;
57   uint32_t gpio_bit_set;
58   uint32_t port_config_lock;
59   uint32_t alt_low;
60   uint32_t alt_high;
61 };
62
63 // Constants related to GPIO mode register masks.
64 constexpr uint32_t kGpio9PortModePos = 18;
65 constexpr uint32_t kGpio10PortModePos = 20;
66 constexpr uint32_t kGpioPortModeAlternate = 2;
67
68 // Constants related to GPIO port speed register masks.
69 constexpr uint32_t kGpio9PortSpeedPos = 18;
70 constexpr uint32_t kGpio10PortSpeedPos = 20;
71 constexpr uint32_t kGpioSpeedVeryHigh = 3;
72
73 // Constants related to GPIO pull up/down resistor type masks.
74 constexpr uint32_t kGpio9PullTypePos = 18;
75 constexpr uint32_t kGpio10PullTypePos = 20;
76 constexpr uint32_t kPullTypePullUp = 1;
77
78 // Constants related to GPIO port speed register masks.
79 constexpr uint32_t kGpio9AltModeHighPos = 4;
80 constexpr uint32_t kGpio10AltModeHighPos = 8;
81
82 // Alternate function for pins A9 and A10 that enable USART1.
83 constexpr uint8_t kGpioAlternateFunctionUsart1 = 0x07u;
84
85 // USART status flags.
86 constexpr uint32_t kTxRegisterEmpty = 0x1u << 7;
87
88 // USART configuration flags for config1 register.
89 // Note: a large number of configuration flags have been omitted as they default
90 // to sane values and we don't need to change them.
91 constexpr uint32_t kReceiveEnable = 0x1 << 2;
92 constexpr uint32_t kTransmitEnable = 0x1 << 3;
93 constexpr uint32_t kReadDataReady = 0x1u << 5;
94 constexpr uint32_t kEnableUsart = 0x1 << 13;
95
96 // Layout of memory mapped registers for USART blocks.
97 PW_PACKED(struct) UsartBlock {
98   uint32_t status;
99   // Only the lower 8 bits are valid. Read for RX data, write to TX data.
100   uint32_t data_register;
101   uint32_t baud_rate;
102   uint32_t config1;
103   uint32_t config2;
104   uint32_t config3;
105   uint32_t config4;
106 };
107
108 // Sets the UART baud register using the peripheral clock and target baud rate.
109 // These calculations are specific to the default oversample by 16 mode.
110 // TODO(amontanez): Document magic calculations in full UART implementation.
111 uint32_t CalcBaudRegister(uint32_t clock, uint32_t target_baud) {
112   uint32_t div_fac = (clock * 25) / (4 * target_baud);
113   uint32_t mantissa = div_fac / 100;
114   uint32_t fraction = ((div_fac - mantissa * 100) * 16 + 50) / 100;
115
116   return (mantissa << 4) + (fraction & 0xFFu);
117 }
118
119 // Declare a reference to the memory mapped RCC block.
120 volatile RccBlock& platform_rcc =
121     *reinterpret_cast<volatile RccBlock*>(kAhb1PeripheralBase + 0x3800U);
122
123 // Declare a reference to the 'A' GPIO memory mapped block.
124 volatile GpioBlock& gpio_a =
125     *reinterpret_cast<volatile GpioBlock*>(kAhb1PeripheralBase + 0x0000U);
126
127 // Declare a reference to the memory mapped block for USART1.
128 volatile UsartBlock& usart1 =
129     *reinterpret_cast<volatile UsartBlock*>(kApb2PeripheralBase + 0x1000U);
130
131 }  // namespace
132
133 extern "C" void pw_sys_io_Init() {
134   // Enable 'A' GIPO clocks.
135   platform_rcc.ahb1_config |= kGpioAEnable;
136
137   // Enable Uart TX pin.
138   // Output type defaults to push-pull (rather than open/drain).
139   gpio_a.modes |= kGpioPortModeAlternate << kGpio9PortModePos;
140   gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio9PortSpeedPos;
141   gpio_a.pull_up_down |= kPullTypePullUp << kGpio9PullTypePos;
142   gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio9AltModeHighPos;
143
144   // Enable Uart RX pin.
145   // Output type defaults to push-pull (rather than open/drain).
146   gpio_a.modes |= kGpioPortModeAlternate << kGpio10PortModePos;
147   gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio10PortSpeedPos;
148   gpio_a.pull_up_down |= kPullTypePullUp << kGpio10PullTypePos;
149   gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio10AltModeHighPos;
150
151   // Initialize USART1. Initialized to 8N1 at the specified baud rate.
152   platform_rcc.apb2_config |= kUsart1Enable;
153
154   // Warning: Normally the baud rate register calculation is based off
155   // peripheral 2 clock. For this code, the peripheral clock defaults to
156   // the system core clock so it can be used directly.
157   usart1.baud_rate = CalcBaudRegister(kSystemCoreClock, /*target_baud=*/115200);
158
159   usart1.config1 = kEnableUsart | kReceiveEnable | kTransmitEnable;
160 }
161
162 namespace pw::sys_io {
163
164 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
165 // extremely inefficient as it requires the target to burn CPU cycles polling to
166 // see if a byte is ready yet.
167 Status ReadByte(std::byte* dest) {
168   while (true) {
169     if (TryReadByte(dest).ok()) {
170       return OkStatus();
171     }
172   }
173 }
174
175 // Wait for a byte to read on USART1. This blocks until a byte is read. This is
176 // extremely inefficient as it requires the target to burn CPU cycles polling to
177 // see if a byte is ready yet.
178 Status TryReadByte(std::byte* dest) {
179   if (!(usart1.status & kReadDataReady)) {
180     return Status::Unavailable();
181   }
182   *dest = static_cast<std::byte>(usart1.data_register);
183   return OkStatus();
184 }
185
186 // Send a byte over USART1. Since this blocks on every byte, it's rather
187 // inefficient. At the default baud rate of 115200, one byte blocks the CPU for
188 // ~87 micro seconds. This means it takes only 10 bytes to block the CPU for
189 // 1ms!
190 Status WriteByte(std::byte b) {
191   // Wait for TX buffer to be empty. When the buffer is empty, we can write
192   // a value to be dumped out of UART.
193   while (!(usart1.status & kTxRegisterEmpty)) {
194   }
195   usart1.data_register = static_cast<uint32_t>(b);
196   return OkStatus();
197 }
198
199 // Writes a string using pw::sys_io, and add newline characters at the end.
200 StatusWithSize WriteLine(const std::string_view& s) {
201   size_t chars_written = 0;
202   StatusWithSize result = WriteBytes(std::as_bytes(std::span(s)));
203   if (!result.ok()) {
204     return result;
205   }
206   chars_written += result.size();
207
208   // Write trailing newline.
209   result = WriteBytes(std::as_bytes(std::span("\r\n", 2)));
210   chars_written += result.size();
211
212   return StatusWithSize(result.status(), chars_written);
213 }
214
215 }  // namespace pw::sys_io