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