1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * R-Car X5H Clock Pulse Generator
4 *
5 * Copyright (C) 2026 Glider bv
6 */
7
8 #include <linux/clk.h>
9 #include <linux/clk-provider.h>
10 #include <linux/dev_printk.h>
11 #include <linux/device-id/of.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16
17 #include <dt-bindings/clock/renesas,r8a78000-cpg.h>
18
19 struct clk_map {
20 int dt_id; /* DT binding clock ID or -1 sentinel */
21 u32 fw_id; /* FIXED_CLK() ID */
22 };
23
24 enum fixed_clk {
25 FIXED_CLK_66M,
26 FIXED_CLK_266M,
27 NUM_FIXED_CLKS
28 };
29
30 static const unsigned long fixed_clk_rates[NUM_FIXED_CLKS] = {
31 [FIXED_CLK_66M] = 66666000,
32 [FIXED_CLK_266M] = 266660000,
33 };
34
35 #define FIXED_CLK(rate) FIXED_CLK_ ## rate
36
37 /**
38 * struct r8a78000_cpg_priv - Clock Pulse Generator Private Data
39 *
40 * @dev: CPG device
41 * @map: Mapping from DT clock IDs to fixed-rate clocks
42 * @fixed_hws: Fixed rate clocks
43 */
44 struct r8a78000_cpg_priv {
45 struct device *dev;
46 const struct clk_map *map;
47 struct clk_hw *fixed_hws[NUM_FIXED_CLKS];
48 };
49
clk_map_find(const struct clk_map * map,u32 id)50 static const struct clk_map *clk_map_find(const struct clk_map *map, u32 id)
51 {
52 if (!map)
53 return NULL;
54
55 for (; map->dt_id >= 0; map++) {
56 if (map->dt_id == id)
57 return map;
58 }
59
60 return NULL;
61 }
62
r8a78000_clk_get(struct of_phandle_args * spec,void * data)63 static struct clk_hw *r8a78000_clk_get(struct of_phandle_args *spec,
64 void *data)
65 {
66 struct r8a78000_cpg_priv *priv = data;
67 struct device *dev = priv->dev;
68 const struct clk_map *map;
69 struct clk_hw *hw;
70 u32 id;
71
72 if (spec->args_count != 1)
73 return ERR_PTR(-EINVAL);
74
75 id = spec->args[0];
76
77 map = clk_map_find(priv->map, id);
78 if (!map) {
79 dev_err(dev, "Unknown clock %u\n", id);
80 return ERR_PTR(-ENOENT);
81 }
82
83 dev_dbg(dev, "Mapping DT clock %u to fixed clock %u\n", id, map->fw_id);
84
85 hw = priv->fixed_hws[map->fw_id];
86
87 dev_dbg(dev, "clock %u is %s at %lu Hz\n", id, clk_hw_get_name(hw),
88 clk_hw_get_rate(hw));
89
90 return hw;
91 }
92
register_fixed_clks(struct r8a78000_cpg_priv * priv)93 static int register_fixed_clks(struct r8a78000_cpg_priv *priv)
94 {
95 struct device *dev = priv->dev;
96 unsigned long rate;
97 struct clk_hw *hw;
98 const char *name;
99
100 for (unsigned int i = 0; i < ARRAY_SIZE(fixed_clk_rates); i++) {
101 rate = fixed_clk_rates[i];
102 name = devm_kasprintf(dev, GFP_KERNEL, "cpg-%lu", rate);
103 if (!name)
104 return -ENOMEM;
105
106 hw = devm_clk_hw_register_fixed_rate(dev, name, NULL, 0, rate);
107 if (IS_ERR(hw))
108 return PTR_ERR(hw);
109
110 priv->fixed_hws[i] = hw;
111 }
112
113 return 0;
114 }
115
r8a78000_cpg_probe(struct platform_device * pdev)116 static int r8a78000_cpg_probe(struct platform_device *pdev)
117 {
118 struct device *dev = &pdev->dev;
119 struct r8a78000_cpg_priv *priv;
120 const struct clk_map *map;
121 int ret;
122
123 map = of_device_get_match_data(dev);
124 if (!map)
125 return -ENODEV;
126
127 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
128 if (!priv)
129 return -ENOMEM;
130
131 priv->dev = dev;
132 priv->map = map;
133
134 ret = register_fixed_clks(priv);
135 if (ret)
136 return ret;
137
138 return devm_of_clk_add_hw_provider(dev, r8a78000_clk_get, priv);
139 }
140
141 static const struct clk_map r8a78000_cpg_default[] = {
142 { R8A78000_CPG_SGASYNCD4_PERW_BUS, FIXED_CLK(266M) },
143 { R8A78000_CPG_SGASYNCD16_PERW_BUS, FIXED_CLK(66M) },
144 { -1 }
145 };
146
147 static const struct of_device_id r8a78000_cpg_match[] = {
148 {
149 .compatible = "renesas,r8a78000-cpg",
150 .data = &r8a78000_cpg_default,
151 },
152 { /* sentinel */ }
153 };
154
155 static struct platform_driver r8a78000_cpg_driver = {
156 .probe = r8a78000_cpg_probe,
157 .driver = {
158 .name = "r8a78000-cpg",
159 .of_match_table = r8a78000_cpg_match,
160 .suppress_bind_attrs = true,
161 },
162 };
163
164 builtin_platform_driver(r8a78000_cpg_driver)
165
166 MODULE_DESCRIPTION("R-Car X5H CPG Driver");
167