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