xref: /optee_os/core/drivers/clk/sam/at91_usb.c (revision 7bf8a43b1a3900e9fe10c1fe264cbc9831bbe8ce)
1*7bf8a43bSClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
2*7bf8a43bSClément Léger /*
3*7bf8a43bSClément Léger  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4*7bf8a43bSClément Léger  *  Copyright (C) 2021 Microchip
5*7bf8a43bSClément Léger  */
6*7bf8a43bSClément Léger 
7*7bf8a43bSClément Léger #include <io.h>
8*7bf8a43bSClément Léger #include <kernel/delay.h>
9*7bf8a43bSClément Léger #include <kernel/panic.h>
10*7bf8a43bSClément Léger #include <mm/core_memprot.h>
11*7bf8a43bSClément Léger #include <types_ext.h>
12*7bf8a43bSClément Léger 
13*7bf8a43bSClément Léger #include "at91_clk.h"
14*7bf8a43bSClément Léger 
15*7bf8a43bSClément Léger #define SAM9X5_USB_DIV_SHIFT	8
16*7bf8a43bSClément Léger #define SAM9X5_USB_DIV_COUNT	BIT32(4)
17*7bf8a43bSClément Léger #define SAM9X5_USB_MAX_DIV	(SAM9X5_USB_DIV_COUNT - 1)
18*7bf8a43bSClément Léger 
19*7bf8a43bSClément Léger #define SAM9X5_USBS_MASK	BIT(0)
20*7bf8a43bSClément Léger 
21*7bf8a43bSClément Léger struct at91sam9x5_clk_usb {
22*7bf8a43bSClément Léger 	vaddr_t base;
23*7bf8a43bSClément Léger 	uint32_t usbs_mask;
24*7bf8a43bSClément Léger };
25*7bf8a43bSClément Léger 
at91sam9x5_clk_usb_get_rate(struct clk * clk,unsigned long parent_rate)26*7bf8a43bSClément Léger static unsigned long at91sam9x5_clk_usb_get_rate(struct clk *clk,
27*7bf8a43bSClément Léger 						 unsigned long parent_rate)
28*7bf8a43bSClément Léger {
29*7bf8a43bSClément Léger 	struct at91sam9x5_clk_usb *usb = clk->priv;
30*7bf8a43bSClément Léger 	uint8_t usbdiv = 1;
31*7bf8a43bSClément Léger 	unsigned int usbr = io_read32(usb->base + AT91_PMC_USB);
32*7bf8a43bSClément Léger 
33*7bf8a43bSClément Léger 	usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
34*7bf8a43bSClément Léger 
35*7bf8a43bSClément Léger 	return UDIV_ROUND_NEAREST(parent_rate, (usbdiv + 1));
36*7bf8a43bSClément Léger }
37*7bf8a43bSClément Léger 
at91sam9x5_clk_usb_set_parent(struct clk * clk,size_t index)38*7bf8a43bSClément Léger static TEE_Result at91sam9x5_clk_usb_set_parent(struct clk *clk, size_t index)
39*7bf8a43bSClément Léger {
40*7bf8a43bSClément Léger 	struct at91sam9x5_clk_usb *usb = clk->priv;
41*7bf8a43bSClément Léger 
42*7bf8a43bSClément Léger 	if (index >= clk_get_num_parents(clk))
43*7bf8a43bSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
44*7bf8a43bSClément Léger 
45*7bf8a43bSClément Léger 	io_clrsetbits32(usb->base + AT91_PMC_USB, usb->usbs_mask, index);
46*7bf8a43bSClément Léger 
47*7bf8a43bSClément Léger 	return TEE_SUCCESS;
48*7bf8a43bSClément Léger }
49*7bf8a43bSClément Léger 
at91sam9x5_clk_usb_get_parent(struct clk * clk)50*7bf8a43bSClément Léger static size_t at91sam9x5_clk_usb_get_parent(struct clk *clk)
51*7bf8a43bSClément Léger {
52*7bf8a43bSClément Léger 	struct at91sam9x5_clk_usb *usb = clk->priv;
53*7bf8a43bSClément Léger 	unsigned int usbr = io_read32(usb->base + AT91_PMC_USB);
54*7bf8a43bSClément Léger 
55*7bf8a43bSClément Léger 	return usbr & usb->usbs_mask;
56*7bf8a43bSClément Léger }
57*7bf8a43bSClément Léger 
at91sam9x5_clk_usb_set_rate(struct clk * clk,unsigned long rate,unsigned long parent_rate)58*7bf8a43bSClément Léger static TEE_Result at91sam9x5_clk_usb_set_rate(struct clk *clk,
59*7bf8a43bSClément Léger 					      unsigned long rate,
60*7bf8a43bSClément Léger 					      unsigned long parent_rate)
61*7bf8a43bSClément Léger {
62*7bf8a43bSClément Léger 	struct at91sam9x5_clk_usb *usb = clk->priv;
63*7bf8a43bSClément Léger 	unsigned long div = 1;
64*7bf8a43bSClément Léger 
65*7bf8a43bSClément Léger 	if (!rate)
66*7bf8a43bSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
67*7bf8a43bSClément Léger 
68*7bf8a43bSClément Léger 	div = UDIV_ROUND_NEAREST(parent_rate, rate);
69*7bf8a43bSClément Léger 	if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
70*7bf8a43bSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
71*7bf8a43bSClément Léger 
72*7bf8a43bSClément Léger 	io_clrsetbits32(usb->base + AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
73*7bf8a43bSClément Léger 			(div - 1) << SAM9X5_USB_DIV_SHIFT);
74*7bf8a43bSClément Léger 
75*7bf8a43bSClément Léger 	return TEE_SUCCESS;
76*7bf8a43bSClément Léger }
77*7bf8a43bSClément Léger 
78*7bf8a43bSClément Léger static const struct clk_ops at91sam9x5_usb_ops = {
79*7bf8a43bSClément Léger 	.get_rate = at91sam9x5_clk_usb_get_rate,
80*7bf8a43bSClément Léger 	.get_parent = at91sam9x5_clk_usb_get_parent,
81*7bf8a43bSClément Léger 	.set_parent = at91sam9x5_clk_usb_set_parent,
82*7bf8a43bSClément Léger 	.set_rate = at91sam9x5_clk_usb_set_rate,
83*7bf8a43bSClément Léger };
84*7bf8a43bSClément Léger 
85*7bf8a43bSClément Léger static struct clk *
_at91sam9x5_clk_register_usb(struct pmc_data * pmc,const char * name,struct clk ** parents,uint8_t num_parents,uint32_t usbs_mask)86*7bf8a43bSClément Léger _at91sam9x5_clk_register_usb(struct pmc_data *pmc, const char *name,
87*7bf8a43bSClément Léger 			     struct clk **parents, uint8_t num_parents,
88*7bf8a43bSClément Léger 			     uint32_t usbs_mask)
89*7bf8a43bSClément Léger {
90*7bf8a43bSClément Léger 	struct at91sam9x5_clk_usb *usb = NULL;
91*7bf8a43bSClément Léger 	struct clk *clk = NULL;
92*7bf8a43bSClément Léger 
93*7bf8a43bSClément Léger 	clk = clk_alloc(name, &at91sam9x5_usb_ops, parents, num_parents);
94*7bf8a43bSClément Léger 	if (!clk)
95*7bf8a43bSClément Léger 		return NULL;
96*7bf8a43bSClément Léger 
97*7bf8a43bSClément Léger 	usb = calloc(1, sizeof(*usb));
98*7bf8a43bSClément Léger 	if (!usb)
99*7bf8a43bSClément Léger 		return NULL;
100*7bf8a43bSClément Léger 
101*7bf8a43bSClément Léger 	usb->base = pmc->base;
102*7bf8a43bSClément Léger 	usb->usbs_mask = usbs_mask;
103*7bf8a43bSClément Léger 
104*7bf8a43bSClément Léger 	clk->priv = usb;
105*7bf8a43bSClément Léger 	clk->flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
106*7bf8a43bSClément Léger 
107*7bf8a43bSClément Léger 	if (clk_register(clk)) {
108*7bf8a43bSClément Léger 		clk_free(clk);
109*7bf8a43bSClément Léger 		free(usb);
110*7bf8a43bSClément Léger 		return NULL;
111*7bf8a43bSClément Léger 	}
112*7bf8a43bSClément Léger 
113*7bf8a43bSClément Léger 	return clk;
114*7bf8a43bSClément Léger }
115*7bf8a43bSClément Léger 
116*7bf8a43bSClément Léger struct clk *
at91sam9x5_clk_register_usb(struct pmc_data * pmc,const char * name,struct clk ** parents,uint8_t num_parents)117*7bf8a43bSClément Léger at91sam9x5_clk_register_usb(struct pmc_data *pmc, const char *name,
118*7bf8a43bSClément Léger 			    struct clk **parents, uint8_t num_parents)
119*7bf8a43bSClément Léger {
120*7bf8a43bSClément Léger 	return _at91sam9x5_clk_register_usb(pmc, name, parents,
121*7bf8a43bSClément Léger 					    num_parents, SAM9X5_USBS_MASK);
122*7bf8a43bSClément Léger }
123