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