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