Skip to content

Commit 892e0dd

Browse files
Marek Vasutbebarino
authored andcommitted
clk: rs9: Add Renesas 9-series PCIe clock generator driver
Add driver for Renesas 9-series PCIe clock generators. This driver is designed to support 9FGV/9DBV/9DMV/9FGL/9DML/9QXL/9SQ series I2C PCIe clock generators, currently the only tested and supported chip is 9FGV0241. The driver is capable of configuring per-chip spread spectrum mode and output amplitude, as well as per-output slew rate. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Michael Turquette <mturquette@baylibre.com> Cc: Rob Herring <robh+dt@kernel.org> Cc: Stephen Boyd <sboyd@kernel.org> Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20220226040723.143705-3-marex@denx.de [sboyd@kernel.org: Use non-underscore API for fixed factor] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent 0c125f8 commit 892e0dd

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

drivers/clk/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,15 @@ config COMMON_CLK_OXNAS
340340
help
341341
Support for the OXNAS SoC Family clocks.
342342

343+
config COMMON_CLK_RS9_PCIE
344+
tristate "Clock driver for Renesas 9-series PCIe clock generators"
345+
depends on I2C
346+
depends on OF
347+
select REGMAP_I2C
348+
help
349+
This driver supports the Renesas 9-series PCIe clock generator
350+
models 9FGV/9DBV/9DMV/9FGL/9DML/9QXL/9SQ.
351+
343352
config COMMON_CLK_VC5
344353
tristate "Clock driver for IDT VersaClock 5,6 devices"
345354
depends on I2C

drivers/clk/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o
6767
obj-$(CONFIG_COMMON_CLK_TPS68470) += clk-tps68470.o
6868
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
6969
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
70+
obj-$(CONFIG_COMMON_CLK_RS9_PCIE) += clk-renesas-pcie.o
7071
obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
7172
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
7273
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o

drivers/clk/clk-renesas-pcie.c

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Driver for Renesas 9-series PCIe clock generator driver
4+
*
5+
* The following series can be supported:
6+
* - 9FGV/9DBV/9DMV/9FGL/9DML/9QXL/9SQ
7+
* Currently supported:
8+
* - 9FGV0241
9+
*
10+
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
11+
*/
12+
13+
#include <linux/clk-provider.h>
14+
#include <linux/i2c.h>
15+
#include <linux/mod_devicetable.h>
16+
#include <linux/module.h>
17+
#include <linux/of.h>
18+
#include <linux/regmap.h>
19+
20+
#define RS9_REG_OE 0x0
21+
#define RS9_REG_OE_DIF_OE(n) BIT((n) + 1)
22+
#define RS9_REG_SS 0x1
23+
#define RS9_REG_SS_AMP_0V6 0x0
24+
#define RS9_REG_SS_AMP_0V7 0x1
25+
#define RS9_REG_SS_AMP_0V8 0x2
26+
#define RS9_REG_SS_AMP_0V9 0x3
27+
#define RS9_REG_SS_AMP_MASK 0x3
28+
#define RS9_REG_SS_SSC_100 0
29+
#define RS9_REG_SS_SSC_M025 (1 << 3)
30+
#define RS9_REG_SS_SSC_M050 (3 << 3)
31+
#define RS9_REG_SS_SSC_MASK (3 << 3)
32+
#define RS9_REG_SS_SSC_LOCK BIT(5)
33+
#define RS9_REG_SR 0x2
34+
#define RS9_REG_SR_2V0_DIF(n) 0
35+
#define RS9_REG_SR_3V0_DIF(n) BIT((n) + 1)
36+
#define RS9_REG_SR_DIF_MASK(n) BIT((n) + 1)
37+
#define RS9_REG_REF 0x3
38+
#define RS9_REG_REF_OE BIT(4)
39+
#define RS9_REG_REF_OD BIT(5)
40+
#define RS9_REG_REF_SR_SLOWEST 0
41+
#define RS9_REG_REF_SR_SLOW (1 << 6)
42+
#define RS9_REG_REF_SR_FAST (2 << 6)
43+
#define RS9_REG_REF_SR_FASTER (3 << 6)
44+
#define RS9_REG_VID 0x5
45+
#define RS9_REG_DID 0x6
46+
#define RS9_REG_BCP 0x7
47+
48+
/* Supported Renesas 9-series models. */
49+
enum rs9_model {
50+
RENESAS_9FGV0241,
51+
};
52+
53+
/* Structure to describe features of a particular 9-series model */
54+
struct rs9_chip_info {
55+
const enum rs9_model model;
56+
unsigned int num_clks;
57+
};
58+
59+
struct rs9_driver_data {
60+
struct i2c_client *client;
61+
struct regmap *regmap;
62+
const struct rs9_chip_info *chip_info;
63+
struct clk *pin_xin;
64+
struct clk_hw *clk_dif[2];
65+
u8 pll_amplitude;
66+
u8 pll_ssc;
67+
u8 clk_dif_sr;
68+
};
69+
70+
/*
71+
* Renesas 9-series i2c regmap
72+
*/
73+
static const struct regmap_range rs9_readable_ranges[] = {
74+
regmap_reg_range(RS9_REG_OE, RS9_REG_REF),
75+
regmap_reg_range(RS9_REG_VID, RS9_REG_BCP),
76+
};
77+
78+
static const struct regmap_access_table rs9_readable_table = {
79+
.yes_ranges = rs9_readable_ranges,
80+
.n_yes_ranges = ARRAY_SIZE(rs9_readable_ranges),
81+
};
82+
83+
static const struct regmap_range rs9_writeable_ranges[] = {
84+
regmap_reg_range(RS9_REG_OE, RS9_REG_REF),
85+
regmap_reg_range(RS9_REG_BCP, RS9_REG_BCP),
86+
};
87+
88+
static const struct regmap_access_table rs9_writeable_table = {
89+
.yes_ranges = rs9_writeable_ranges,
90+
.n_yes_ranges = ARRAY_SIZE(rs9_writeable_ranges),
91+
};
92+
93+
static const struct regmap_config rs9_regmap_config = {
94+
.reg_bits = 8,
95+
.val_bits = 8,
96+
.cache_type = REGCACHE_FLAT,
97+
.max_register = 0x8,
98+
.rd_table = &rs9_readable_table,
99+
.wr_table = &rs9_writeable_table,
100+
};
101+
102+
static int rs9_get_output_config(struct rs9_driver_data *rs9, int idx)
103+
{
104+
struct i2c_client *client = rs9->client;
105+
unsigned char name[5] = "DIF0";
106+
struct device_node *np;
107+
int ret;
108+
u32 sr;
109+
110+
/* Set defaults */
111+
rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
112+
rs9->clk_dif_sr |= RS9_REG_SR_3V0_DIF(idx);
113+
114+
snprintf(name, 5, "DIF%d", idx);
115+
np = of_get_child_by_name(client->dev.of_node, name);
116+
if (!np)
117+
return 0;
118+
119+
/* Output clock slew rate */
120+
ret = of_property_read_u32(np, "renesas,slew-rate", &sr);
121+
of_node_put(np);
122+
if (!ret) {
123+
if (sr == 2000000) { /* 2V/ns */
124+
rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
125+
rs9->clk_dif_sr |= RS9_REG_SR_2V0_DIF(idx);
126+
} else if (sr == 3000000) { /* 3V/ns (default) */
127+
rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
128+
rs9->clk_dif_sr |= RS9_REG_SR_3V0_DIF(idx);
129+
} else
130+
ret = dev_err_probe(&client->dev, -EINVAL,
131+
"Invalid renesas,slew-rate value\n");
132+
}
133+
134+
return ret;
135+
}
136+
137+
static int rs9_get_common_config(struct rs9_driver_data *rs9)
138+
{
139+
struct i2c_client *client = rs9->client;
140+
struct device_node *np = client->dev.of_node;
141+
unsigned int amp, ssc;
142+
int ret;
143+
144+
/* Set defaults */
145+
rs9->pll_amplitude = RS9_REG_SS_AMP_0V7;
146+
rs9->pll_ssc = RS9_REG_SS_SSC_100;
147+
148+
/* Output clock amplitude */
149+
ret = of_property_read_u32(np, "renesas,out-amplitude-microvolt",
150+
&amp);
151+
if (!ret) {
152+
if (amp == 600000) /* 0.6V */
153+
rs9->pll_amplitude = RS9_REG_SS_AMP_0V6;
154+
else if (amp == 700000) /* 0.7V (default) */
155+
rs9->pll_amplitude = RS9_REG_SS_AMP_0V7;
156+
else if (amp == 800000) /* 0.8V */
157+
rs9->pll_amplitude = RS9_REG_SS_AMP_0V8;
158+
else if (amp == 900000) /* 0.9V */
159+
rs9->pll_amplitude = RS9_REG_SS_AMP_0V9;
160+
else
161+
return dev_err_probe(&client->dev, -EINVAL,
162+
"Invalid renesas,out-amplitude-microvolt value\n");
163+
}
164+
165+
/* Output clock spread spectrum */
166+
ret = of_property_read_u32(np, "renesas,out-spread-spectrum", &ssc);
167+
if (!ret) {
168+
if (ssc == 100000) /* 100% ... no spread (default) */
169+
rs9->pll_ssc = RS9_REG_SS_SSC_100;
170+
else if (ssc == 99750) /* -0.25% ... down spread */
171+
rs9->pll_ssc = RS9_REG_SS_SSC_M025;
172+
else if (ssc == 99500) /* -0.50% ... down spread */
173+
rs9->pll_ssc = RS9_REG_SS_SSC_M050;
174+
else
175+
return dev_err_probe(&client->dev, -EINVAL,
176+
"Invalid renesas,out-spread-spectrum value\n");
177+
}
178+
179+
return 0;
180+
}
181+
182+
static void rs9_update_config(struct rs9_driver_data *rs9)
183+
{
184+
int i;
185+
186+
/* If amplitude is non-default, update it. */
187+
if (rs9->pll_amplitude != RS9_REG_SS_AMP_0V7) {
188+
regmap_update_bits(rs9->regmap, RS9_REG_SS, RS9_REG_SS_AMP_MASK,
189+
rs9->pll_amplitude);
190+
}
191+
192+
/* If SSC is non-default, update it. */
193+
if (rs9->pll_ssc != RS9_REG_SS_SSC_100) {
194+
regmap_update_bits(rs9->regmap, RS9_REG_SS, RS9_REG_SS_SSC_MASK,
195+
rs9->pll_ssc);
196+
}
197+
198+
for (i = 0; i < rs9->chip_info->num_clks; i++) {
199+
if (rs9->clk_dif_sr & RS9_REG_SR_3V0_DIF(i))
200+
continue;
201+
202+
regmap_update_bits(rs9->regmap, RS9_REG_SR, RS9_REG_SR_3V0_DIF(i),
203+
rs9->clk_dif_sr & RS9_REG_SR_3V0_DIF(i));
204+
}
205+
}
206+
207+
static struct clk_hw *
208+
rs9_of_clk_get(struct of_phandle_args *clkspec, void *data)
209+
{
210+
struct rs9_driver_data *rs9 = data;
211+
unsigned int idx = clkspec->args[0];
212+
213+
return rs9->clk_dif[idx];
214+
}
215+
216+
static int rs9_probe(struct i2c_client *client, const struct i2c_device_id *id)
217+
{
218+
unsigned char name[5] = "DIF0";
219+
struct rs9_driver_data *rs9;
220+
struct clk_hw *hw;
221+
int i, ret;
222+
223+
rs9 = devm_kzalloc(&client->dev, sizeof(*rs9), GFP_KERNEL);
224+
if (!rs9)
225+
return -ENOMEM;
226+
227+
i2c_set_clientdata(client, rs9);
228+
rs9->client = client;
229+
rs9->chip_info = device_get_match_data(&client->dev);
230+
if (!rs9->chip_info)
231+
return -EINVAL;
232+
233+
/* Fetch common configuration from DT (if specified) */
234+
ret = rs9_get_common_config(rs9);
235+
if (ret)
236+
return ret;
237+
238+
/* Fetch DIFx output configuration from DT (if specified) */
239+
for (i = 0; i < rs9->chip_info->num_clks; i++) {
240+
ret = rs9_get_output_config(rs9, i);
241+
if (ret)
242+
return ret;
243+
}
244+
245+
rs9->regmap = devm_regmap_init_i2c(client, &rs9_regmap_config);
246+
if (IS_ERR(rs9->regmap))
247+
return dev_err_probe(&client->dev, PTR_ERR(rs9->regmap),
248+
"Failed to allocate register map\n");
249+
250+
/* Register clock */
251+
for (i = 0; i < rs9->chip_info->num_clks; i++) {
252+
snprintf(name, 5, "DIF%d", i);
253+
hw = devm_clk_hw_register_fixed_factor_index(&client->dev, name,
254+
0, 0, 4, 1);
255+
if (IS_ERR(hw))
256+
return PTR_ERR(hw);
257+
258+
rs9->clk_dif[i] = hw;
259+
}
260+
261+
ret = devm_of_clk_add_hw_provider(&client->dev, rs9_of_clk_get, rs9);
262+
if (!ret)
263+
rs9_update_config(rs9);
264+
265+
return ret;
266+
}
267+
268+
static int __maybe_unused rs9_suspend(struct device *dev)
269+
{
270+
struct rs9_driver_data *rs9 = dev_get_drvdata(dev);
271+
272+
regcache_cache_only(rs9->regmap, true);
273+
regcache_mark_dirty(rs9->regmap);
274+
275+
return 0;
276+
}
277+
278+
static int __maybe_unused rs9_resume(struct device *dev)
279+
{
280+
struct rs9_driver_data *rs9 = dev_get_drvdata(dev);
281+
int ret;
282+
283+
regcache_cache_only(rs9->regmap, false);
284+
ret = regcache_sync(rs9->regmap);
285+
if (ret)
286+
dev_err(dev, "Failed to restore register map: %d\n", ret);
287+
return ret;
288+
}
289+
290+
static const struct rs9_chip_info renesas_9fgv0241_info = {
291+
.model = RENESAS_9FGV0241,
292+
.num_clks = 2,
293+
};
294+
295+
static const struct i2c_device_id rs9_id[] = {
296+
{ "9fgv0241", .driver_data = RENESAS_9FGV0241 },
297+
{ }
298+
};
299+
MODULE_DEVICE_TABLE(i2c, rs9_id);
300+
301+
static const struct of_device_id clk_rs9_of_match[] = {
302+
{ .compatible = "renesas,9fgv0241", .data = &renesas_9fgv0241_info },
303+
{ }
304+
};
305+
MODULE_DEVICE_TABLE(of, clk_rs9_of_match);
306+
307+
static SIMPLE_DEV_PM_OPS(rs9_pm_ops, rs9_suspend, rs9_resume);
308+
309+
static struct i2c_driver rs9_driver = {
310+
.driver = {
311+
.name = "clk-renesas-pcie-9series",
312+
.pm = &rs9_pm_ops,
313+
.of_match_table = clk_rs9_of_match,
314+
},
315+
.probe = rs9_probe,
316+
.id_table = rs9_id,
317+
};
318+
module_i2c_driver(rs9_driver);
319+
320+
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
321+
MODULE_DESCRIPTION("Renesas 9-series PCIe clock generator driver");
322+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)