xref: /optee_os/core/drivers/clk/sam/at91_pmc.c (revision 74fbd27329483af2fdcb5ef01f6d621632ba5232)
15558f7fcSClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
25558f7fcSClément Léger /*
35558f7fcSClément Léger  *  Copyright (C) 2021 Microchip
45558f7fcSClément Léger  */
55558f7fcSClément Léger 
65558f7fcSClément Léger #include <dt-bindings/clock/at91.h>
75e6f824bSClément Léger #include <io.h>
85558f7fcSClément Léger #include <kernel/panic.h>
95e6f824bSClément Léger #include <kernel/pm.h>
105558f7fcSClément Léger #include <malloc.h>
115558f7fcSClément Léger #include <string.h>
125e6f824bSClément Léger #include <trace.h>
135558f7fcSClément Léger #include <types_ext.h>
145558f7fcSClément Léger 
155558f7fcSClément Léger #include "at91_clk.h"
165558f7fcSClément Léger 
175e6f824bSClément Léger #define PMC_MAX_IDS 128
185e6f824bSClément Léger #define PMC_MAX_PCKS 8
195e6f824bSClément Léger 
pmc_clk_get_by_id(struct pmc_clk * clks,unsigned int nclk,unsigned int id)205558f7fcSClément Léger static struct clk *pmc_clk_get_by_id(struct pmc_clk *clks, unsigned int nclk,
215558f7fcSClément Léger 				     unsigned int id)
225558f7fcSClément Léger {
235558f7fcSClément Léger 	unsigned int i = 0;
245558f7fcSClément Léger 
255558f7fcSClément Léger 	for (i = 0; i < nclk; i++) {
265558f7fcSClément Léger 		if (clks[i].clk && clks[i].id == id)
275558f7fcSClément Léger 			return clks[i].clk;
285558f7fcSClément Léger 	}
295558f7fcSClément Léger 
305558f7fcSClément Léger 	return NULL;
315558f7fcSClément Léger }
325558f7fcSClément Léger 
pmc_clk_get_by_name(struct pmc_clk * clks,unsigned int nclk,const char * name)335558f7fcSClément Léger struct clk *pmc_clk_get_by_name(struct pmc_clk *clks, unsigned int nclk,
345558f7fcSClément Léger 				const char *name)
355558f7fcSClément Léger {
365558f7fcSClément Léger 	unsigned int i = 0;
375558f7fcSClément Léger 
385558f7fcSClément Léger 	for (i = 0; i < nclk; i++)
39*74fbd273STony Han 		if (clks[i].clk && strcmp(clks[i].clk->name, name) == 0)
405558f7fcSClément Léger 			return clks[i].clk;
415558f7fcSClément Léger 
425558f7fcSClément Léger 	return NULL;
435558f7fcSClément Léger }
445558f7fcSClément Léger 
pmc_clk_get(struct pmc_data * pmc,unsigned int type,unsigned int idx,struct clk ** clk)455943d3b9SClément Léger TEE_Result pmc_clk_get(struct pmc_data *pmc, unsigned int type,
465943d3b9SClément Léger 		       unsigned int idx, struct clk **clk)
475943d3b9SClément Léger {
485943d3b9SClément Léger 	unsigned int nclk = 0;
495943d3b9SClément Léger 	struct pmc_clk *clks = NULL;
505943d3b9SClément Léger 
515943d3b9SClément Léger 	switch (type) {
525943d3b9SClément Léger 	case PMC_TYPE_CORE:
535943d3b9SClément Léger 		nclk = pmc->ncore;
545943d3b9SClément Léger 		clks = pmc->chws;
555943d3b9SClément Léger 		break;
565943d3b9SClément Léger 	case PMC_TYPE_SYSTEM:
575943d3b9SClément Léger 		nclk = pmc->nsystem;
585943d3b9SClément Léger 		clks = pmc->shws;
595943d3b9SClément Léger 		break;
605943d3b9SClément Léger 	case PMC_TYPE_PERIPHERAL:
615943d3b9SClément Léger 		nclk = pmc->nperiph;
625943d3b9SClément Léger 		clks = pmc->phws;
635943d3b9SClément Léger 		break;
645943d3b9SClément Léger 	case PMC_TYPE_GCK:
655943d3b9SClément Léger 		nclk = pmc->ngck;
665943d3b9SClément Léger 		clks = pmc->ghws;
675943d3b9SClément Léger 		break;
685943d3b9SClément Léger 	case PMC_TYPE_PROGRAMMABLE:
695943d3b9SClément Léger 		nclk = pmc->npck;
705943d3b9SClément Léger 		clks = pmc->pchws;
715943d3b9SClément Léger 		break;
725943d3b9SClément Léger 	default:
735943d3b9SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
745943d3b9SClément Léger 	}
755943d3b9SClément Léger 
765943d3b9SClément Léger 	*clk = pmc_clk_get_by_id(clks, nclk, idx);
775943d3b9SClément Léger 	if (!*clk)
785943d3b9SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
795943d3b9SClément Léger 
805943d3b9SClément Léger 	return TEE_SUCCESS;
815943d3b9SClément Léger }
825943d3b9SClément Léger 
clk_dt_pmc_get(struct dt_pargs * clkspec,void * data,struct clk ** out_clk)83b357d34fSEtienne Carriere TEE_Result clk_dt_pmc_get(struct dt_pargs *clkspec, void *data,
84b357d34fSEtienne Carriere 			  struct clk **out_clk)
855558f7fcSClément Léger {
865558f7fcSClément Léger 	unsigned int type = clkspec->args[0];
875558f7fcSClément Léger 	unsigned int idx = clkspec->args[1];
885558f7fcSClément Léger 	struct pmc_data *pmc_data = data;
895558f7fcSClément Léger 
90b357d34fSEtienne Carriere 	if (clkspec->args_count != 2)
91b357d34fSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
925558f7fcSClément Léger 
93b357d34fSEtienne Carriere 	return pmc_clk_get(pmc_data, type, idx, out_clk);
945558f7fcSClément Léger }
955558f7fcSClément Léger 
pmc_data_allocate(unsigned int ncore,unsigned int nsystem,unsigned int nperiph,unsigned int ngck,unsigned int npck)965558f7fcSClément Léger struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
975558f7fcSClément Léger 				   unsigned int nperiph, unsigned int ngck,
985558f7fcSClément Léger 				   unsigned int npck)
995558f7fcSClément Léger {
1005558f7fcSClément Léger 	unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
1015558f7fcSClément Léger 	unsigned int alloc_size = sizeof(struct pmc_data) +
1025558f7fcSClément Léger 				  num_clks * sizeof(struct pmc_clk);
1035558f7fcSClément Léger 	struct pmc_data *pmc_data = NULL;
1045558f7fcSClément Léger 
1055558f7fcSClément Léger 	pmc_data = calloc(1, alloc_size);
1065558f7fcSClément Léger 	if (!pmc_data)
1075558f7fcSClément Léger 		return NULL;
1085558f7fcSClément Léger 
1095558f7fcSClément Léger 	pmc_data->ncore = ncore;
1105558f7fcSClément Léger 	pmc_data->chws = pmc_data->hwtable;
1115558f7fcSClément Léger 
1125558f7fcSClément Léger 	pmc_data->nsystem = nsystem;
1135558f7fcSClément Léger 	pmc_data->shws = pmc_data->chws + ncore;
1145558f7fcSClément Léger 
1155558f7fcSClément Léger 	pmc_data->nperiph = nperiph;
1165558f7fcSClément Léger 	pmc_data->phws = pmc_data->shws + nsystem;
1175558f7fcSClément Léger 
1185558f7fcSClément Léger 	pmc_data->ngck = ngck;
1195558f7fcSClément Léger 	pmc_data->ghws = pmc_data->phws + nperiph;
1205558f7fcSClément Léger 
1215558f7fcSClément Léger 	pmc_data->npck = npck;
1225558f7fcSClément Léger 	pmc_data->pchws = pmc_data->ghws + ngck;
1235558f7fcSClément Léger 
1245558f7fcSClément Léger 	return pmc_data;
1255558f7fcSClément Léger }
1265e6f824bSClément Léger 
1275e6f824bSClément Léger #ifdef CFG_PM_ARM32
1285e6f824bSClément Léger static uint8_t registered_ids[PMC_MAX_IDS];
1295e6f824bSClément Léger static uint8_t registered_pcks[PMC_MAX_PCKS];
1305e6f824bSClément Léger 
1315e6f824bSClément Léger static struct
1325e6f824bSClément Léger {
1335e6f824bSClément Léger 	uint32_t scsr;
1345e6f824bSClément Léger 	uint32_t pcsr0;
1355e6f824bSClément Léger 	uint32_t uckr;
1365e6f824bSClément Léger 	uint32_t mor;
1375e6f824bSClément Léger 	uint32_t mcfr;
1385e6f824bSClément Léger 	uint32_t pllar;
1395e6f824bSClément Léger 	uint32_t mckr;
1405e6f824bSClément Léger 	uint32_t usb;
1415e6f824bSClément Léger 	uint32_t imr;
1425e6f824bSClément Léger 	uint32_t pcsr1;
1435e6f824bSClément Léger 	uint32_t pcr[PMC_MAX_IDS];
1445e6f824bSClément Léger 	uint32_t audio_pll0;
1455e6f824bSClément Léger 	uint32_t audio_pll1;
1465e6f824bSClément Léger 	uint32_t pckr[PMC_MAX_PCKS];
1475e6f824bSClément Léger } pmc_cache;
1485e6f824bSClément Léger 
1495e6f824bSClément Léger /*
1505e6f824bSClément Léger  * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
1515e6f824bSClément Léger  * without alteration in the table, and 0 is for unused clocks.
1525e6f824bSClément Léger  */
pmc_register_id(uint8_t id)1535e6f824bSClément Léger void pmc_register_id(uint8_t id)
1545e6f824bSClément Léger {
1555e6f824bSClément Léger 	int i = 0;
1565e6f824bSClément Léger 
1575e6f824bSClément Léger 	for (i = 0; i < PMC_MAX_IDS; i++) {
1585e6f824bSClément Léger 		if (registered_ids[i] == 0) {
1595e6f824bSClément Léger 			registered_ids[i] = id;
1605e6f824bSClément Léger 			return;
1615e6f824bSClément Léger 		}
1625e6f824bSClément Léger 		if (registered_ids[i] == id)
1635e6f824bSClément Léger 			return;
1645e6f824bSClément Léger 	}
1655e6f824bSClément Léger 
1665e6f824bSClément Léger 	panic("Invalid clock ID");
1675e6f824bSClément Léger }
1685e6f824bSClément Léger 
1695e6f824bSClément Léger /*
1705e6f824bSClément Léger  * As Programmable Clock 0 is valid on AT91 chips, there is an offset
1715e6f824bSClément Léger  * of 1 between the stored value and the real clock ID.
1725e6f824bSClément Léger  */
pmc_register_pck(uint8_t pck)1735e6f824bSClément Léger void pmc_register_pck(uint8_t pck)
1745e6f824bSClément Léger {
1755e6f824bSClément Léger 	int i = 0;
1765e6f824bSClément Léger 
1775e6f824bSClément Léger 	for (i = 0; i < PMC_MAX_PCKS; i++) {
1785e6f824bSClément Léger 		if (registered_pcks[i] == 0) {
1795e6f824bSClément Léger 			registered_pcks[i] = pck + 1;
1805e6f824bSClément Léger 			return;
1815e6f824bSClément Léger 		}
1825e6f824bSClément Léger 		if (registered_pcks[i] == pck + 1)
1835e6f824bSClément Léger 			return;
1845e6f824bSClément Léger 	}
1855e6f824bSClément Léger 
1865e6f824bSClément Léger 	panic("Invalid clock ID");
1875e6f824bSClément Léger }
1885e6f824bSClément Léger 
pmc_suspend(void)1895e6f824bSClément Léger static void pmc_suspend(void)
1905e6f824bSClément Léger {
1915e6f824bSClément Léger 	int i = 0;
1925e6f824bSClément Léger 	uint8_t num = 0;
1935e6f824bSClément Léger 	vaddr_t pmc_base = at91_pmc_get_base();
1945e6f824bSClément Léger 
1955e6f824bSClément Léger 	pmc_cache.scsr = io_read32(pmc_base + AT91_PMC_SCSR);
1965e6f824bSClément Léger 	pmc_cache.pcsr0 = io_read32(pmc_base + AT91_PMC_PCSR);
1975e6f824bSClément Léger 	pmc_cache.uckr = io_read32(pmc_base + AT91_CKGR_UCKR);
1985e6f824bSClément Léger 	pmc_cache.mor = io_read32(pmc_base + AT91_CKGR_MOR);
1995e6f824bSClément Léger 	pmc_cache.mcfr = io_read32(pmc_base + AT91_CKGR_MCFR);
2005e6f824bSClément Léger 	pmc_cache.pllar = io_read32(pmc_base + AT91_CKGR_PLLAR);
2015e6f824bSClément Léger 	pmc_cache.mckr = io_read32(pmc_base + AT91_PMC_MCKR);
2025e6f824bSClément Léger 	pmc_cache.usb = io_read32(pmc_base + AT91_PMC_USB);
2035e6f824bSClément Léger 	pmc_cache.imr = io_read32(pmc_base + AT91_PMC_IMR);
2045e6f824bSClément Léger 	pmc_cache.pcsr1 = io_read32(pmc_base + AT91_PMC_PCSR1);
2055e6f824bSClément Léger 
2065e6f824bSClément Léger 	for (i = 0; registered_ids[i]; i++) {
2075e6f824bSClément Léger 		io_write32(pmc_base + AT91_PMC_PCR,
2085e6f824bSClément Léger 			   registered_ids[i] & AT91_PMC_PCR_PID_MASK);
2095e6f824bSClément Léger 		pmc_cache.pcr[registered_ids[i]] = io_read32(pmc_base +
2105e6f824bSClément Léger 							     AT91_PMC_PCR);
2115e6f824bSClément Léger 	}
2125e6f824bSClément Léger 	for (i = 0; registered_pcks[i]; i++) {
2135e6f824bSClément Léger 		num = registered_pcks[i] - 1;
2145e6f824bSClément Léger 		pmc_cache.pckr[num] = io_read32(pmc_base + AT91_PMC_PCKR(num));
2155e6f824bSClément Léger 	}
2165e6f824bSClément Léger }
2175e6f824bSClément Léger 
pmc_ready(vaddr_t pmc_base,unsigned int mask)2185e6f824bSClément Léger static bool pmc_ready(vaddr_t pmc_base, unsigned int mask)
2195e6f824bSClément Léger {
2205e6f824bSClément Léger 	uint32_t status = 0;
2215e6f824bSClément Léger 
2225e6f824bSClément Léger 	status = io_read32(pmc_base + AT91_PMC_SR);
2235e6f824bSClément Léger 
2245e6f824bSClément Léger 	return (status & mask) == mask;
2255e6f824bSClément Léger }
2265e6f824bSClément Léger 
pmc_resume(void)2275e6f824bSClément Léger static void pmc_resume(void)
2285e6f824bSClément Léger {
2295e6f824bSClément Léger 	int i = 0;
2305e6f824bSClément Léger 	uint8_t num = 0;
2315e6f824bSClément Léger 	uint32_t tmp = 0;
2325e6f824bSClément Léger 	vaddr_t pmc_base = at91_pmc_get_base();
2335e6f824bSClément Léger 	uint32_t mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
2345e6f824bSClément Léger 
2355e6f824bSClément Léger 	tmp = io_read32(pmc_base + AT91_PMC_MCKR);
2365e6f824bSClément Léger 	if (pmc_cache.mckr != tmp)
2375e6f824bSClément Léger 		panic("MCKR was not configured properly by the previous bootstage");
2385e6f824bSClément Léger 	tmp = io_read32(pmc_base + AT91_CKGR_PLLAR);
2395e6f824bSClément Léger 	if (pmc_cache.pllar != tmp)
2405e6f824bSClément Léger 		panic("PLLAR was not configured properly by the previous bootstage");
2415e6f824bSClément Léger 
2425e6f824bSClément Léger 	io_write32(pmc_base + AT91_PMC_SCER, pmc_cache.scsr);
2435e6f824bSClément Léger 	io_write32(pmc_base + AT91_PMC_PCER, pmc_cache.pcsr0);
2445e6f824bSClément Léger 	io_write32(pmc_base + AT91_CKGR_UCKR, pmc_cache.uckr);
2455e6f824bSClément Léger 	io_write32(pmc_base + AT91_CKGR_MOR, pmc_cache.mor);
2465e6f824bSClément Léger 	io_write32(pmc_base + AT91_CKGR_MCFR, pmc_cache.mcfr);
2475e6f824bSClément Léger 	io_write32(pmc_base + AT91_PMC_USB, pmc_cache.usb);
2485e6f824bSClément Léger 	io_write32(pmc_base + AT91_PMC_IMR, pmc_cache.imr);
2495e6f824bSClément Léger 	io_write32(pmc_base + AT91_PMC_PCER1, pmc_cache.pcsr1);
2505e6f824bSClément Léger 
2515e6f824bSClément Léger 	for (i = 0; registered_ids[i]; i++) {
2525e6f824bSClément Léger 		io_write32(pmc_base + AT91_PMC_PCR,
2535e6f824bSClément Léger 			   pmc_cache.pcr[registered_ids[i]] | AT91_PMC_PCR_CMD);
2545e6f824bSClément Léger 	}
2555e6f824bSClément Léger 	for (i = 0; registered_pcks[i]; i++) {
2565e6f824bSClément Léger 		num = registered_pcks[i] - 1;
2575e6f824bSClément Léger 		io_write32(pmc_base + AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
2585e6f824bSClément Léger 	}
2595e6f824bSClément Léger 
2605e6f824bSClément Léger 	if (pmc_cache.uckr & AT91_PMC_UPLLEN)
2615e6f824bSClément Léger 		mask |= AT91_PMC_LOCKU;
2625e6f824bSClément Léger 
2635e6f824bSClément Léger 	while (!pmc_ready(pmc_base, mask))
2645e6f824bSClément Léger 		;
2655e6f824bSClément Léger }
2665e6f824bSClément Léger 
pmc_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl __unused)2675e6f824bSClément Léger static TEE_Result pmc_pm(enum pm_op op, uint32_t pm_hint __unused,
2685e6f824bSClément Léger 			 const struct pm_callback_handle *hdl __unused)
2695e6f824bSClément Léger {
2705e6f824bSClément Léger 	switch (op) {
2715e6f824bSClément Léger 	case PM_OP_RESUME:
2725e6f824bSClément Léger 		pmc_resume();
2735e6f824bSClément Léger 		break;
2745e6f824bSClément Léger 	case PM_OP_SUSPEND:
2755e6f824bSClément Léger 		pmc_suspend();
2765e6f824bSClément Léger 		break;
2775e6f824bSClément Léger 	default:
2785e6f824bSClément Léger 		panic("Invalid PM operation");
2795e6f824bSClément Léger 	}
2805e6f824bSClément Léger 
2815e6f824bSClément Léger 	return TEE_SUCCESS;
2825e6f824bSClément Léger }
2835e6f824bSClément Léger 
pmc_register_pm(void)2845e6f824bSClément Léger void pmc_register_pm(void)
2855e6f824bSClément Léger {
2865e6f824bSClément Léger 	/*
2875e6f824bSClément Léger 	 * We register the clock as a core service since clocks must be
2885e6f824bSClément Léger 	 * re-enable prior to accessing devices
2895e6f824bSClément Léger 	 */
2905e6f824bSClément Léger 	register_pm_core_service_cb(pmc_pm, NULL, "pmc");
2915e6f824bSClément Léger }
2925e6f824bSClément Léger 
2935e6f824bSClément Léger #endif
294