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