xref: /linux/drivers/clk/renesas/r8a78000-cpg.c (revision 502d45774af09f1c681c754c4b7cdfb5d7f72fd9)
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