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