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