1 /*
2 * Copyright (c) 2023 Antmicro <www.antmicro.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #ifndef ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_
8 #define ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_
9
10 #include <zephyr/device.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/pm/policy.h>
14
15 #include "uart_pl011_registers.h"
16 #include <am_mcu_apollo.h>
17
18 #define PWRCTRL_MAX_WAIT_US 5
19
pl011_ambiq_enable_clk(const struct device * dev)20 static inline void pl011_ambiq_enable_clk(const struct device *dev)
21 {
22 get_uart(dev)->cr |= PL011_CR_AMBIQ_CLKEN;
23 }
24
pl011_ambiq_clk_set(const struct device * dev,uint32_t clk)25 static inline int pl011_ambiq_clk_set(const struct device *dev, uint32_t clk)
26 {
27 uint8_t clksel;
28
29 switch (clk) {
30 case 3000000:
31 clksel = PL011_CR_AMBIQ_CLKSEL_3MHZ;
32 break;
33 case 6000000:
34 clksel = PL011_CR_AMBIQ_CLKSEL_6MHZ;
35 break;
36 case 12000000:
37 clksel = PL011_CR_AMBIQ_CLKSEL_12MHZ;
38 break;
39 case 24000000:
40 clksel = PL011_CR_AMBIQ_CLKSEL_24MHZ;
41 break;
42 default:
43 return -EINVAL;
44 }
45
46 get_uart(dev)->cr |= FIELD_PREP(PL011_CR_AMBIQ_CLKSEL, clksel);
47 return 0;
48 }
49
clk_enable_ambiq_uart(const struct device * dev,uint32_t clk)50 static inline int clk_enable_ambiq_uart(const struct device *dev, uint32_t clk)
51 {
52 pl011_ambiq_enable_clk(dev);
53 return pl011_ambiq_clk_set(dev, clk);
54 }
55
56 #ifdef CONFIG_PM_DEVICE
57
58 /* Register status record.
59 * The register status will be preserved to this variable before entering sleep mode,
60 * and they will be restored after wake up.
61 */
62 typedef struct {
63 bool bValid;
64 uint32_t regILPR;
65 uint32_t regIBRD;
66 uint32_t regFBRD;
67 uint32_t regLCRH;
68 uint32_t regCR;
69 uint32_t regIFLS;
70 uint32_t regIER;
71 } uart_register_state_t;
72 static uart_register_state_t sRegState[AM_REG_UART_NUM_MODULES];
73
uart_ambiq_pm_action(const struct device * dev,enum pm_device_action action)74 static int uart_ambiq_pm_action(const struct device *dev, enum pm_device_action action)
75 {
76 int key;
77
78 /*Uart module number*/
79 uint32_t ui32Module = ((uint32_t)get_uart(dev) == UART0_BASE) ? 0 : 1;
80
81 /*Uart Power module*/
82 am_hal_pwrctrl_periph_e eUARTPowerModule =
83 ((am_hal_pwrctrl_periph_e)(AM_HAL_PWRCTRL_PERIPH_UART0 + ui32Module));
84
85 /*Uart register status*/
86 uart_register_state_t *pRegisterStatus = &sRegState[ui32Module];
87
88 /* Decode the requested power state and update UART operation accordingly.*/
89 switch (action) {
90
91 /* Turn on the UART. */
92 case PM_DEVICE_ACTION_RESUME:
93
94 /* Make sure we don't try to restore an invalid state.*/
95 if (!pRegisterStatus->bValid) {
96 return -EPERM;
97 }
98
99 /*The resume and suspend actions may be executed back-to-back,
100 * so we add a busy wait here for stabilization.
101 */
102 k_busy_wait(100);
103
104 /* Enable power control.*/
105 am_hal_pwrctrl_periph_enable(eUARTPowerModule);
106
107 /* Restore UART registers*/
108 key = irq_lock();
109
110 UARTn(ui32Module)->ILPR = pRegisterStatus->regILPR;
111 UARTn(ui32Module)->IBRD = pRegisterStatus->regIBRD;
112 UARTn(ui32Module)->FBRD = pRegisterStatus->regFBRD;
113 UARTn(ui32Module)->LCRH = pRegisterStatus->regLCRH;
114 UARTn(ui32Module)->CR = pRegisterStatus->regCR;
115 UARTn(ui32Module)->IFLS = pRegisterStatus->regIFLS;
116 UARTn(ui32Module)->IER = pRegisterStatus->regIER;
117 pRegisterStatus->bValid = false;
118
119 irq_unlock(key);
120
121 return 0;
122 case PM_DEVICE_ACTION_SUSPEND:
123
124 while ((get_uart(dev)->fr & PL011_FR_BUSY) != 0)
125 ;
126
127 /* Preserve UART registers*/
128 key = irq_lock();
129
130 pRegisterStatus->regILPR = UARTn(ui32Module)->ILPR;
131 pRegisterStatus->regIBRD = UARTn(ui32Module)->IBRD;
132 pRegisterStatus->regFBRD = UARTn(ui32Module)->FBRD;
133 pRegisterStatus->regLCRH = UARTn(ui32Module)->LCRH;
134 pRegisterStatus->regCR = UARTn(ui32Module)->CR;
135 pRegisterStatus->regIFLS = UARTn(ui32Module)->IFLS;
136 pRegisterStatus->regIER = UARTn(ui32Module)->IER;
137 pRegisterStatus->bValid = true;
138
139 irq_unlock(key);
140
141 /* Clear all interrupts before sleeping as having a pending UART
142 * interrupt burns power.
143 */
144 UARTn(ui32Module)->IEC = 0xFFFFFFFF;
145
146 /* If the user is going to sleep, certain bits of the CR register
147 * need to be 0 to be low power and have the UART shut off.
148 * Since the user either wishes to retain state which takes place
149 * above or the user does not wish to retain state, it is acceptable
150 * to set the entire CR register to 0.
151 */
152 UARTn(ui32Module)->CR = 0;
153
154 /* Disable power control.*/
155 am_hal_pwrctrl_periph_disable(eUARTPowerModule);
156 return 0;
157 default:
158 return -ENOTSUP;
159 }
160 }
161 #endif /* CONFIG_PM_DEVICE */
162
163 /* Problem: writes to power configure register takes some time to take effective.
164 * Solution: Check device's power status to ensure that register has taken effective.
165 * Note: busy wait is not allowed to use here due to UART is initiated before timer starts.
166 */
167 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
168 #define DEVPWRSTATUS_OFFSET 0x10
169 #define HCPA_MASK 0x4
170 #define AMBIQ_UART_DEFINE(n) \
171 PM_DEVICE_DT_INST_DEFINE(n, uart_ambiq_pm_action); \
172 static int pwr_on_ambiq_uart_##n(void) \
173 { \
174 uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \
175 DT_INST_PHA(n, ambiq_pwrcfg, offset); \
176 uint32_t pwr_status_addr = addr + DEVPWRSTATUS_OFFSET; \
177 sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr); \
178 while (!(sys_read32(pwr_status_addr) & HCPA_MASK)) { \
179 }; \
180 return 0; \
181 } \
182 static inline int clk_enable_ambiq_uart_##n(const struct device *dev, uint32_t clk) \
183 { \
184 return clk_enable_ambiq_uart(dev, clk); \
185 }
186 #else
187 #define DEVPWRSTATUS_OFFSET 0x4
188 #define AMBIQ_UART_DEFINE(n) \
189 PM_DEVICE_DT_INST_DEFINE(n, uart_ambiq_pm_action); \
190 static int pwr_on_ambiq_uart_##n(void) \
191 { \
192 uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \
193 DT_INST_PHA(n, ambiq_pwrcfg, offset); \
194 uint32_t pwr_status_addr = addr + DEVPWRSTATUS_OFFSET; \
195 sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr); \
196 while ((sys_read32(pwr_status_addr) & DT_INST_PHA(n, ambiq_pwrcfg, mask)) != \
197 DT_INST_PHA(n, ambiq_pwrcfg, mask)) { \
198 }; \
199 return 0; \
200 } \
201 static inline int clk_enable_ambiq_uart_##n(const struct device *dev, uint32_t clk) \
202 { \
203 return clk_enable_ambiq_uart(dev, clk); \
204 }
205 #endif
206
207 #endif /* ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_ */
208