1 /*
2  * Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT microchip_sam_pmc
8 
9 #include <pmc.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/clock_control/mchp_sam_pmc.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(pmc, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
16 
get_pmc_clk(const struct device * dev,clock_control_subsys_t sys,struct device ** clk)17 static int get_pmc_clk(const struct device *dev, clock_control_subsys_t sys,
18 		       struct device **clk)
19 {
20 	const struct sam_clk_cfg *cfg = (const struct sam_clk_cfg *)sys;
21 	const struct sam_pmc_data *data = dev->data;
22 
23 	if (cfg == NULL || data == NULL) {
24 		LOG_ERR("The PMC config and data can not be NULL.");
25 		return -ENXIO;
26 	}
27 
28 	LOG_DBG("Type: %x, Id: %d", cfg->clock_type, cfg->clock_id);
29 
30 	*clk = sam_pmc_get_clock(cfg, data->pmc);
31 	if (*clk) {
32 		return 0;
33 	}
34 
35 	LOG_ERR("The PMC clock type is not implemented.");
36 
37 	return -ENODEV;
38 }
39 
sam_clock_control_on(const struct device * dev,clock_control_subsys_t sys)40 static int sam_clock_control_on(const struct device *dev,
41 				clock_control_subsys_t sys)
42 {
43 	struct device *clk;
44 	int ret = get_pmc_clk(dev, sys, &clk);
45 
46 	if (!ret) {
47 		return clock_control_on(clk, sys);
48 	}
49 
50 	return ret;
51 }
52 
sam_clock_control_off(const struct device * dev,clock_control_subsys_t sys)53 static int sam_clock_control_off(const struct device *dev,
54 				 clock_control_subsys_t sys)
55 {
56 	struct device *clk;
57 	int ret = get_pmc_clk(dev, sys, &clk);
58 
59 	if (!ret) {
60 		return clock_control_off(clk, sys);
61 	}
62 
63 	return ret;
64 }
65 
sam_clock_control_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)66 static int sam_clock_control_get_rate(const struct device *dev,
67 				      clock_control_subsys_t sys,
68 				      uint32_t *rate)
69 {
70 	struct device *clk;
71 	int ret = get_pmc_clk(dev, sys, &clk);
72 
73 	if (!ret) {
74 		return clock_control_get_rate(clk, sys, rate);
75 	}
76 
77 	return ret;
78 }
79 
80 static enum clock_control_status
sam_clock_control_get_status(const struct device * dev,clock_control_subsys_t sys)81 sam_clock_control_get_status(const struct device *dev,
82 			     clock_control_subsys_t sys)
83 {
84 	struct device *clk;
85 	int ret = get_pmc_clk(dev, sys, &clk);
86 
87 	if (!ret) {
88 		return clock_control_get_status(clk, sys);
89 	}
90 
91 	return ret;
92 }
93 
clock_control_sam_pmc_init(const struct device * dev)94 static int clock_control_sam_pmc_init(const struct device *dev)
95 {
96 	sam_pmc_setup(dev);
97 	return 0;
98 }
99 
100 static DEVICE_API(clock_control, sam_clock_control_api) = {
101 	.on = sam_clock_control_on,
102 	.off = sam_clock_control_off,
103 	.get_rate = sam_clock_control_get_rate,
104 	.get_status = sam_clock_control_get_status,
105 };
106 
107 #define PMC_INIT_CFG_SLCK(n, slck)							\
108 	COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, slck),					\
109 	(										\
110 		.slck = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(n, slck)),		\
111 		.slck##_cfg = {								\
112 			.crystal_osc = DT_INST_CLOCKS_CELL_BY_NAME(n, slck,		\
113 								   clock_crystal_osc),	\
114 		},									\
115 	), ())
116 
117 #define PMC_INIT_CFG_CLK(n, clk)							\
118 	COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, clk),					\
119 	(										\
120 		.clk = DEVICE_DT_GET(DT_CLOCKS_CTLR_BY_NAME(DT_DRV_INST(n), clk)),	\
121 	), ())
122 
123 #define PMC_CFG_DEFN(n)									\
124 	static const struct sam_pmc_cfg pmc##n##_cfg = {				\
125 		.reg = (uint32_t *)DT_INST_REG_ADDR(n),					\
126 		PMC_INIT_CFG_SLCK(n, td_slck)						\
127 		PMC_INIT_CFG_SLCK(n, md_slck)						\
128 		PMC_INIT_CFG_CLK(n, main_xtal)						\
129 	}
130 #define SAM_PMC_DEVICE_INIT(n)								\
131 static struct sam_pmc_data pmc##n##data;						\
132 PMC_CFG_DEFN(n);									\
133 											\
134 DEVICE_DT_INST_DEFINE(n, clock_control_sam_pmc_init, NULL,				\
135 		      &pmc##n##data,							\
136 		      &pmc##n##_cfg,							\
137 		      PRE_KERNEL_1,							\
138 		      CONFIG_CLOCK_CONTROL_INIT_PRIORITY,				\
139 		      &sam_clock_control_api);						\
140 
141 DT_INST_FOREACH_STATUS_OKAY(SAM_PMC_DEVICE_INIT)
142