1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2015 Atmel Corporation, 4 * Nicolas Ferre <nicolas.ferre@atmel.com> 5 * Copyright (c) 2021, Microchip 6 */ 7 8 #include <drivers/atmel_shdwc.h> 9 #include <drivers/sam/at91_ddr.h> 10 #include <drivers/pm/sam/atmel_pm.h> 11 #include <io.h> 12 #include <kernel/dt.h> 13 #include <kernel/dt_driver.h> 14 #include <kernel/thread.h> 15 #include <libfdt.h> 16 #include <matrix.h> 17 #include <platform_config.h> 18 #include <stdbool.h> 19 #include <tee_api_defines.h> 20 #include <tee_api_types.h> 21 #include <trace.h> 22 #include <types_ext.h> 23 #include <util.h> 24 25 #include "at91_clk.h" 26 27 #define SHDW_WK_PIN(reg, cfg) ((reg) & \ 28 AT91_SHDW_WKUPIS((cfg)->wkup_pin_input)) 29 #define SHDW_RTCWK(reg, cfg) (((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1) 30 #define SHDW_RTTWK(reg, cfg) (((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1) 31 #define SHDW_RTCWKEN(cfg) BIT((cfg)->mr_rtcwk_shift) 32 #define SHDW_RTTWKEN(cfg) BIT((cfg)->mr_rttwk_shift) 33 34 #define SLOW_CLK_FREQ 32768ULL 35 #define DBC_PERIOD_US(x) DIV_ROUND_UP((1000000ULL * (x)), SLOW_CLK_FREQ) 36 37 /* 38 * @type_offset offset of Memory Device Register 39 * @type_mask mask of Memory Device Type in Memory Device Register 40 * @compatible the compatible string in the device tree 41 */ 42 struct ddrc_reg_config { 43 uint32_t type_offset; 44 uint32_t type_mask; 45 const char *compatible; 46 }; 47 48 /* 49 * @shdwc_always_secure Is peripheral SHDWC always secured? 50 * @ddrc DDR controller configurations 51 */ 52 struct shdwc_compat { 53 bool shdwc_always_secure; 54 struct ddrc_reg_config ddrc; 55 }; 56 57 static vaddr_t shdwc_base; 58 static vaddr_t mpddrc_base; 59 60 bool atmel_shdwc_available(void) 61 { 62 return shdwc_base != 0; 63 } 64 65 void __noreturn atmel_shdwc_shutdown(void) 66 { 67 vaddr_t pmc_base = at91_pmc_get_base(); 68 69 /* 70 * Mask exception before entering assembly which does not expect to be 71 * interrupted. 72 */ 73 thread_mask_exceptions(THREAD_EXCP_ALL); 74 75 __atmel_shdwc_shutdown(mpddrc_base, shdwc_base, pmc_base); 76 77 /* We are going to shutdown the CPU so we will never hit this loop */ 78 while (true) 79 ; 80 } 81 82 static const unsigned long long sdwc_dbc_period[] = { 83 0, 3, 32, 512, 4096, 32768, 84 }; 85 86 static uint32_t at91_shdwc_debouncer_value(uint32_t in_period_us) 87 { 88 int i = 0; 89 int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1; 90 uint64_t period_us = 0; 91 uint64_t max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]); 92 93 if (in_period_us > max_period_us) { 94 DMSG("debouncer period %"PRIu32" too big, using %"PRIu64" us", 95 in_period_us, max_period_us); 96 return max_idx; 97 } 98 99 for (i = max_idx - 1; i > 0; i--) { 100 period_us = DBC_PERIOD_US(sdwc_dbc_period[i]); 101 if (in_period_us > period_us) 102 break; 103 } 104 105 return i + 1; 106 } 107 108 static uint32_t at91_shdwc_get_wakeup_input(const void *fdt, int np) 109 { 110 const uint32_t *prop = NULL; 111 uint32_t wk_input_mask = 0; 112 uint32_t wuir = 0; 113 uint32_t wk_input = 0; 114 int child = 0; 115 int len = 0; 116 117 fdt_for_each_subnode(child, fdt, np) { 118 prop = fdt_getprop(fdt, child, "reg", &len); 119 if (!prop || len != sizeof(uint32_t)) { 120 DMSG("reg property is missing for node %s", 121 fdt_get_name(fdt, child, NULL)); 122 continue; 123 } 124 wk_input = fdt32_to_cpu(*prop); 125 wk_input_mask = BIT32(wk_input); 126 if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) { 127 DMSG("wake-up input %"PRId32" out of bounds ignore", 128 wk_input); 129 continue; 130 } 131 wuir |= wk_input_mask; 132 133 if (fdt_getprop(fdt, child, "atmel,wakeup-active-high", NULL)) 134 wuir |= AT91_SHDW_WKUPT(wk_input); 135 } 136 137 return wuir; 138 } 139 140 static void at91_shdwc_dt_configure(const void *fdt, int np) 141 { 142 const uint32_t *prop = NULL; 143 uint32_t mode = 0; 144 uint32_t tmp = 0; 145 uint32_t input = 0; 146 int len = 0; 147 148 prop = fdt_getprop(fdt, np, "debounce-delay-us", &len); 149 if (prop && len == sizeof(uint32_t)) { 150 tmp = fdt32_to_cpu(*prop); 151 mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(tmp)); 152 } 153 154 if (fdt_getprop(fdt, np, "atmel,wakeup-rtc-timer", &len)) 155 mode |= AT91_SHDW_RTCWKEN; 156 157 if (fdt_getprop(fdt, np, "atmel,wakeup-rtt-timer", &len)) 158 mode |= AT91_SHDW_RTTWKEN; 159 160 io_write32(shdwc_base + AT91_SHDW_MR, mode); 161 162 input = at91_shdwc_get_wakeup_input(fdt, np); 163 io_write32(shdwc_base + AT91_SHDW_WUIR, input); 164 } 165 166 static TEE_Result atmel_shdwc_probe(const void *fdt, int node, 167 const void *compat_data) 168 { 169 int ddr_node = 0; 170 size_t size = 0; 171 uint32_t ddr = AT91_DDRSDRC_MD_LPDDR2; 172 struct shdwc_compat *compat = (struct shdwc_compat *)compat_data; 173 174 /* 175 * Assembly code relies on the fact that there is only one CPU to avoid 176 * any other one to invalidate TLB/I-Cache. 177 */ 178 COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1); 179 180 if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC) 181 return TEE_ERROR_BAD_PARAMETERS; 182 183 if (!compat->shdwc_always_secure) 184 matrix_configure_periph_secure(AT91C_ID_SYS); 185 186 if (dt_map_dev(fdt, node, &shdwc_base, &size, DT_MAP_AUTO) < 0) 187 return TEE_ERROR_GENERIC; 188 189 ddr_node = fdt_node_offset_by_compatible(fdt, -1, 190 compat->ddrc.compatible); 191 if (ddr_node < 0) 192 return TEE_ERROR_GENERIC; 193 194 if (dt_map_dev(fdt, ddr_node, &mpddrc_base, &size, DT_MAP_AUTO) < 0) 195 return TEE_ERROR_GENERIC; 196 197 if (!compat->ddrc.type_mask) { 198 ddr = io_read32(mpddrc_base + compat->ddrc.type_offset); 199 ddr &= compat->ddrc.type_mask; 200 if (ddr != AT91_DDRSDRC_MD_LPDDR2 && 201 ddr != AT91_DDRSDRC_MD_LPDDR3) 202 mpddrc_base = 0; 203 } else { 204 /* 205 * Set the base to 0 as the code of DRAM controller for power 206 * down is not implemented yet. 207 */ 208 mpddrc_base = 0; 209 } 210 211 at91_shdwc_dt_configure(fdt, node); 212 213 return sam_pm_init(fdt, shdwc_base); 214 } 215 216 static const struct shdwc_compat sama5d2_compat = { 217 .shdwc_always_secure = false, 218 .ddrc = { 219 .type_offset = AT91_DDRSDRC_MDR, 220 .type_mask = AT91_DDRSDRC_MD, 221 .compatible = "atmel,sama5d3-ddramc", 222 } 223 }; 224 225 static const struct shdwc_compat sama7g5_compat = { 226 .shdwc_always_secure = true, 227 .ddrc = { 228 .compatible = "microchip,sama7g5-uddrc", 229 } 230 }; 231 232 static const struct dt_device_match atmel_shdwc_match_table[] = { 233 { 234 .compatible = "atmel,sama5d2-shdwc", 235 .compat_data = &sama5d2_compat 236 }, 237 { 238 .compatible = "microchip,sama7g5-shdwc", 239 .compat_data = &sama7g5_compat, 240 }, 241 { } 242 }; 243 244 DEFINE_DT_DRIVER(atmel_shdwc_dt_driver) = { 245 .name = "atmel_shdwc", 246 .type = DT_DRIVER_NOTYPE, 247 .match_table = atmel_shdwc_match_table, 248 .probe = atmel_shdwc_probe, 249 }; 250