1 /*
2  * Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 /** @file
7  * @brief Atmel SAM4L MCU family Power Management (PM) module
8  * HAL driver.
9  */
10 
11 #include <soc.h>
12 #include <sys/__assert.h>
13 #include <sys/util.h>
14 
15 /**
16  * SAM4L define peripheral-ids out of order.  This maps peripheral-id group
17  * to right register index.
18  */
19 static const uint32_t bridge_peripheral_ids[] = {
20 	2,	/* PBA GRP */
21 	3,	/* PBB GRP */
22 	4,	/* PBC GRP */
23 	5,	/* PBD GRP */
24 	1,	/* HSB GRP */
25 	0,	/* CPU GRP */
26 };
27 
28 static const uint32_t bridge_peripheral_instances[] = {
29 	1,	/* CPU MASK Instances */
30 	10,	/* HSB MASK Instances */
31 	24,	/* PBA MASK Instances */
32 	7,	/* PBB MASK Instances */
33 	5,	/* PBC MASK Instances */
34 	6,	/* PBD MASK Instances */
35 };
36 
soc_pmc_peripheral_enable(uint32_t id)37 void soc_pmc_peripheral_enable(uint32_t id)
38 {
39 	uint32_t bus_grp = id >> 5;
40 	uint32_t per_idx = id & 0x1F;
41 	uint32_t bus_id;
42 	uint32_t mask;
43 
44 	if (bus_grp >= 6) {
45 		return;
46 	}
47 
48 	bus_id = bridge_peripheral_ids[bus_grp];
49 
50 	if (per_idx >= bridge_peripheral_instances[bus_id]) {
51 		return;
52 	}
53 
54 	mask		= *(&PM->CPUMASK + bus_id);
55 	mask		|= (1U << per_idx);
56 	PM->UNLOCK	= PM_UNLOCK_KEY(0xAAu) |
57 			  PM_UNLOCK_ADDR(((uint32_t)&PM->CPUMASK -
58 					  (uint32_t)PM) +
59 					  (4 * bus_id));
60 	*(&PM->CPUMASK + bus_id) = mask;
61 }
62 
soc_pmc_peripheral_disable(uint32_t id)63 void soc_pmc_peripheral_disable(uint32_t id)
64 {
65 	uint32_t bus_grp = id >> 5;
66 	uint32_t per_idx = id & 0x1F;
67 	uint32_t bus_id;
68 	uint32_t mask;
69 
70 	if (bus_grp >= 6) {
71 		return;
72 	}
73 
74 	bus_id = bridge_peripheral_ids[bus_grp];
75 
76 	if (per_idx >= bridge_peripheral_instances[bus_id]) {
77 		return;
78 	}
79 
80 	mask		= *(&PM->CPUMASK + bus_id);
81 	mask		&= ~(1U << per_idx);
82 	PM->UNLOCK	= PM_UNLOCK_KEY(0xAAu) |
83 			  PM_UNLOCK_ADDR(((uint32_t)&PM->CPUMASK -
84 					  (uint32_t)PM) +
85 					  (4 * bus_id));
86 	*(&PM->CPUMASK + bus_id) = mask;
87 }
88 
soc_pmc_peripheral_is_enabled(uint32_t id)89 uint32_t soc_pmc_peripheral_is_enabled(uint32_t id)
90 {
91 	uint32_t bus_grp = id >> 5;
92 	uint32_t per_idx = id & 0x1F;
93 	uint32_t bus_id;
94 	uint32_t mask;
95 
96 	if (bus_grp >= 6) {
97 		return 0;
98 	}
99 
100 	bus_id = bridge_peripheral_ids[bus_grp];
101 
102 	if (per_idx >= bridge_peripheral_instances[bus_id]) {
103 		return 0;
104 	}
105 
106 	mask = *(&PM->CPUMASK + bus_id);
107 
108 	return ((mask & (1U << per_idx)) > 0);
109 }
110 
soc_pm_enable_pba_divmask(uint32_t mask)111 void soc_pm_enable_pba_divmask(uint32_t mask)
112 {
113 	uint32_t temp_mask;
114 
115 	temp_mask	= PM->PBADIVMASK;
116 	temp_mask	|= mask;
117 
118 	PM->UNLOCK	= PM_UNLOCK_KEY(0xAAu) |
119 			  PM_UNLOCK_ADDR((uint32_t)&PM->PBADIVMASK -
120 					 (uint32_t)PM);
121 	PM->PBADIVMASK = temp_mask;
122 }
123