xref: /linux/drivers/clk/renesas/clk-vbattb.c (revision 9f3a2ba62c7226a6604b8aaeb92b5ff906fa4e6b)
1*3b42450cSClaudiu Beznea // SPDX-License-Identifier: GPL-2.0
2*3b42450cSClaudiu Beznea /*
3*3b42450cSClaudiu Beznea  * VBATTB clock driver
4*3b42450cSClaudiu Beznea  *
5*3b42450cSClaudiu Beznea  * Copyright (C) 2024 Renesas Electronics Corp.
6*3b42450cSClaudiu Beznea  */
7*3b42450cSClaudiu Beznea 
8*3b42450cSClaudiu Beznea #include <linux/cleanup.h>
9*3b42450cSClaudiu Beznea #include <linux/clk-provider.h>
10*3b42450cSClaudiu Beznea #include <linux/device.h>
11*3b42450cSClaudiu Beznea #include <linux/io.h>
12*3b42450cSClaudiu Beznea #include <linux/mod_devicetable.h>
13*3b42450cSClaudiu Beznea #include <linux/of.h>
14*3b42450cSClaudiu Beznea #include <linux/platform_device.h>
15*3b42450cSClaudiu Beznea #include <linux/pm_runtime.h>
16*3b42450cSClaudiu Beznea #include <linux/reset.h>
17*3b42450cSClaudiu Beznea 
18*3b42450cSClaudiu Beznea #include <dt-bindings/clock/renesas,r9a08g045-vbattb.h>
19*3b42450cSClaudiu Beznea 
20*3b42450cSClaudiu Beznea #define VBATTB_BKSCCR			0x1c
21*3b42450cSClaudiu Beznea #define VBATTB_BKSCCR_SOSEL		6
22*3b42450cSClaudiu Beznea #define VBATTB_SOSCCR2			0x24
23*3b42450cSClaudiu Beznea #define VBATTB_SOSCCR2_SOSTP2		0
24*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR			0x30
25*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_OUTEN		16
26*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_XSEL		GENMASK(1, 0)
27*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_XSEL_4_PF		0x0
28*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_XSEL_7_PF		0x1
29*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_XSEL_9_PF		0x2
30*3b42450cSClaudiu Beznea #define VBATTB_XOSCCR_XSEL_12_5_PF	0x3
31*3b42450cSClaudiu Beznea 
32*3b42450cSClaudiu Beznea /**
33*3b42450cSClaudiu Beznea  * struct vbattb_clk - VBATTB clock data structure
34*3b42450cSClaudiu Beznea  * @base: base address
35*3b42450cSClaudiu Beznea  * @lock: lock
36*3b42450cSClaudiu Beznea  */
37*3b42450cSClaudiu Beznea struct vbattb_clk {
38*3b42450cSClaudiu Beznea 	void __iomem *base;
39*3b42450cSClaudiu Beznea 	spinlock_t lock;
40*3b42450cSClaudiu Beznea };
41*3b42450cSClaudiu Beznea 
vbattb_clk_validate_load_capacitance(u32 * reg_lc,u32 of_lc)42*3b42450cSClaudiu Beznea static int vbattb_clk_validate_load_capacitance(u32 *reg_lc, u32 of_lc)
43*3b42450cSClaudiu Beznea {
44*3b42450cSClaudiu Beznea 	switch (of_lc) {
45*3b42450cSClaudiu Beznea 	case 4000:
46*3b42450cSClaudiu Beznea 		*reg_lc = VBATTB_XOSCCR_XSEL_4_PF;
47*3b42450cSClaudiu Beznea 		break;
48*3b42450cSClaudiu Beznea 	case 7000:
49*3b42450cSClaudiu Beznea 		*reg_lc = VBATTB_XOSCCR_XSEL_7_PF;
50*3b42450cSClaudiu Beznea 		break;
51*3b42450cSClaudiu Beznea 	case 9000:
52*3b42450cSClaudiu Beznea 		*reg_lc = VBATTB_XOSCCR_XSEL_9_PF;
53*3b42450cSClaudiu Beznea 		break;
54*3b42450cSClaudiu Beznea 	case 12500:
55*3b42450cSClaudiu Beznea 		*reg_lc = VBATTB_XOSCCR_XSEL_12_5_PF;
56*3b42450cSClaudiu Beznea 		break;
57*3b42450cSClaudiu Beznea 	default:
58*3b42450cSClaudiu Beznea 		return -EINVAL;
59*3b42450cSClaudiu Beznea 	}
60*3b42450cSClaudiu Beznea 
61*3b42450cSClaudiu Beznea 	return 0;
62*3b42450cSClaudiu Beznea }
63*3b42450cSClaudiu Beznea 
vbattb_clk_action(void * data)64*3b42450cSClaudiu Beznea static void vbattb_clk_action(void *data)
65*3b42450cSClaudiu Beznea {
66*3b42450cSClaudiu Beznea 	struct device *dev = data;
67*3b42450cSClaudiu Beznea 	struct reset_control *rstc = dev_get_drvdata(dev);
68*3b42450cSClaudiu Beznea 	int ret;
69*3b42450cSClaudiu Beznea 
70*3b42450cSClaudiu Beznea 	ret = reset_control_assert(rstc);
71*3b42450cSClaudiu Beznea 	if (ret)
72*3b42450cSClaudiu Beznea 		dev_err(dev, "Failed to de-assert reset!");
73*3b42450cSClaudiu Beznea 
74*3b42450cSClaudiu Beznea 	ret = pm_runtime_put_sync(dev);
75*3b42450cSClaudiu Beznea 	if (ret < 0)
76*3b42450cSClaudiu Beznea 		dev_err(dev, "Failed to runtime suspend!");
77*3b42450cSClaudiu Beznea 
78*3b42450cSClaudiu Beznea 	of_clk_del_provider(dev->of_node);
79*3b42450cSClaudiu Beznea }
80*3b42450cSClaudiu Beznea 
vbattb_clk_probe(struct platform_device * pdev)81*3b42450cSClaudiu Beznea static int vbattb_clk_probe(struct platform_device *pdev)
82*3b42450cSClaudiu Beznea {
83*3b42450cSClaudiu Beznea 	struct device_node *np = pdev->dev.of_node;
84*3b42450cSClaudiu Beznea 	struct clk_parent_data parent_data = {};
85*3b42450cSClaudiu Beznea 	struct clk_hw_onecell_data *clk_data;
86*3b42450cSClaudiu Beznea 	const struct clk_hw *parent_hws[2];
87*3b42450cSClaudiu Beznea 	struct device *dev = &pdev->dev;
88*3b42450cSClaudiu Beznea 	struct reset_control *rstc;
89*3b42450cSClaudiu Beznea 	struct vbattb_clk *vbclk;
90*3b42450cSClaudiu Beznea 	u32 of_lc, reg_lc;
91*3b42450cSClaudiu Beznea 	struct clk_hw *hw;
92*3b42450cSClaudiu Beznea 	/* 4 clocks are exported: VBATTB_XC, VBATTB_XBYP, VBATTB_MUX, VBATTB_VBATTCLK. */
93*3b42450cSClaudiu Beznea 	u8 num_clks = 4;
94*3b42450cSClaudiu Beznea 	int ret;
95*3b42450cSClaudiu Beznea 
96*3b42450cSClaudiu Beznea 	/* Default to 4pF as this is not needed if external clock device is connected. */
97*3b42450cSClaudiu Beznea 	of_lc = 4000;
98*3b42450cSClaudiu Beznea 	of_property_read_u32(np, "quartz-load-femtofarads", &of_lc);
99*3b42450cSClaudiu Beznea 
100*3b42450cSClaudiu Beznea 	ret = vbattb_clk_validate_load_capacitance(&reg_lc, of_lc);
101*3b42450cSClaudiu Beznea 	if (ret)
102*3b42450cSClaudiu Beznea 		return ret;
103*3b42450cSClaudiu Beznea 
104*3b42450cSClaudiu Beznea 	vbclk = devm_kzalloc(dev, sizeof(*vbclk), GFP_KERNEL);
105*3b42450cSClaudiu Beznea 	if (!vbclk)
106*3b42450cSClaudiu Beznea 		return -ENOMEM;
107*3b42450cSClaudiu Beznea 
108*3b42450cSClaudiu Beznea 	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_clks), GFP_KERNEL);
109*3b42450cSClaudiu Beznea 	if (!clk_data)
110*3b42450cSClaudiu Beznea 		return -ENOMEM;
111*3b42450cSClaudiu Beznea 	clk_data->num = num_clks;
112*3b42450cSClaudiu Beznea 
113*3b42450cSClaudiu Beznea 	vbclk->base = devm_platform_ioremap_resource(pdev, 0);
114*3b42450cSClaudiu Beznea 	if (IS_ERR(vbclk->base))
115*3b42450cSClaudiu Beznea 		return PTR_ERR(vbclk->base);
116*3b42450cSClaudiu Beznea 
117*3b42450cSClaudiu Beznea 	ret = devm_pm_runtime_enable(dev);
118*3b42450cSClaudiu Beznea 	if (ret)
119*3b42450cSClaudiu Beznea 		return ret;
120*3b42450cSClaudiu Beznea 
121*3b42450cSClaudiu Beznea 	rstc = devm_reset_control_get_shared(dev, NULL);
122*3b42450cSClaudiu Beznea 	if (IS_ERR(rstc))
123*3b42450cSClaudiu Beznea 		return PTR_ERR(rstc);
124*3b42450cSClaudiu Beznea 
125*3b42450cSClaudiu Beznea 	ret = pm_runtime_resume_and_get(dev);
126*3b42450cSClaudiu Beznea 	if (ret)
127*3b42450cSClaudiu Beznea 		return ret;
128*3b42450cSClaudiu Beznea 
129*3b42450cSClaudiu Beznea 	ret = reset_control_deassert(rstc);
130*3b42450cSClaudiu Beznea 	if (ret) {
131*3b42450cSClaudiu Beznea 		pm_runtime_put_sync(dev);
132*3b42450cSClaudiu Beznea 		return ret;
133*3b42450cSClaudiu Beznea 	}
134*3b42450cSClaudiu Beznea 
135*3b42450cSClaudiu Beznea 	dev_set_drvdata(dev, rstc);
136*3b42450cSClaudiu Beznea 	ret = devm_add_action_or_reset(dev, vbattb_clk_action, dev);
137*3b42450cSClaudiu Beznea 	if (ret)
138*3b42450cSClaudiu Beznea 		return ret;
139*3b42450cSClaudiu Beznea 
140*3b42450cSClaudiu Beznea 	spin_lock_init(&vbclk->lock);
141*3b42450cSClaudiu Beznea 
142*3b42450cSClaudiu Beznea 	parent_data.fw_name = "rtx";
143*3b42450cSClaudiu Beznea 	hw = devm_clk_hw_register_gate_parent_data(dev, "xc", &parent_data, 0,
144*3b42450cSClaudiu Beznea 						   vbclk->base + VBATTB_SOSCCR2,
145*3b42450cSClaudiu Beznea 						   VBATTB_SOSCCR2_SOSTP2,
146*3b42450cSClaudiu Beznea 						   CLK_GATE_SET_TO_DISABLE, &vbclk->lock);
147*3b42450cSClaudiu Beznea 	if (IS_ERR(hw))
148*3b42450cSClaudiu Beznea 		return PTR_ERR(hw);
149*3b42450cSClaudiu Beznea 	clk_data->hws[VBATTB_XC] = hw;
150*3b42450cSClaudiu Beznea 
151*3b42450cSClaudiu Beznea 	hw = devm_clk_hw_register_fixed_factor_fwname(dev, np, "xbyp", "rtx", 0, 1, 1);
152*3b42450cSClaudiu Beznea 	if (IS_ERR(hw))
153*3b42450cSClaudiu Beznea 		return PTR_ERR(hw);
154*3b42450cSClaudiu Beznea 	clk_data->hws[VBATTB_XBYP] = hw;
155*3b42450cSClaudiu Beznea 
156*3b42450cSClaudiu Beznea 	parent_hws[0] = clk_data->hws[VBATTB_XC];
157*3b42450cSClaudiu Beznea 	parent_hws[1] = clk_data->hws[VBATTB_XBYP];
158*3b42450cSClaudiu Beznea 	hw = devm_clk_hw_register_mux_parent_hws(dev, "mux", parent_hws, 2, 0,
159*3b42450cSClaudiu Beznea 						 vbclk->base + VBATTB_BKSCCR,
160*3b42450cSClaudiu Beznea 						 VBATTB_BKSCCR_SOSEL,
161*3b42450cSClaudiu Beznea 						 1, 0, &vbclk->lock);
162*3b42450cSClaudiu Beznea 	if (IS_ERR(hw))
163*3b42450cSClaudiu Beznea 		return PTR_ERR(hw);
164*3b42450cSClaudiu Beznea 	clk_data->hws[VBATTB_MUX] = hw;
165*3b42450cSClaudiu Beznea 
166*3b42450cSClaudiu Beznea 	/* Set load capacitance before registering the VBATTCLK clock. */
167*3b42450cSClaudiu Beznea 	scoped_guard(spinlock, &vbclk->lock) {
168*3b42450cSClaudiu Beznea 		u32 val = readl_relaxed(vbclk->base + VBATTB_XOSCCR);
169*3b42450cSClaudiu Beznea 
170*3b42450cSClaudiu Beznea 		val &= ~VBATTB_XOSCCR_XSEL;
171*3b42450cSClaudiu Beznea 		val |= reg_lc;
172*3b42450cSClaudiu Beznea 		writel_relaxed(val, vbclk->base + VBATTB_XOSCCR);
173*3b42450cSClaudiu Beznea 	}
174*3b42450cSClaudiu Beznea 
175*3b42450cSClaudiu Beznea 	/* This feeds the RTC counter clock and it needs to stay on. */
176*3b42450cSClaudiu Beznea 	hw = devm_clk_hw_register_gate_parent_hw(dev, "vbattclk", hw, CLK_IS_CRITICAL,
177*3b42450cSClaudiu Beznea 						 vbclk->base + VBATTB_XOSCCR,
178*3b42450cSClaudiu Beznea 						 VBATTB_XOSCCR_OUTEN, 0,
179*3b42450cSClaudiu Beznea 						 &vbclk->lock);
180*3b42450cSClaudiu Beznea 
181*3b42450cSClaudiu Beznea 	if (IS_ERR(hw))
182*3b42450cSClaudiu Beznea 		return PTR_ERR(hw);
183*3b42450cSClaudiu Beznea 	clk_data->hws[VBATTB_VBATTCLK] = hw;
184*3b42450cSClaudiu Beznea 
185*3b42450cSClaudiu Beznea 	return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
186*3b42450cSClaudiu Beznea }
187*3b42450cSClaudiu Beznea 
188*3b42450cSClaudiu Beznea static const struct of_device_id vbattb_clk_match[] = {
189*3b42450cSClaudiu Beznea 	{ .compatible = "renesas,r9a08g045-vbattb" },
190*3b42450cSClaudiu Beznea 	{ /* sentinel */ }
191*3b42450cSClaudiu Beznea };
192*3b42450cSClaudiu Beznea MODULE_DEVICE_TABLE(of, vbattb_clk_match);
193*3b42450cSClaudiu Beznea 
194*3b42450cSClaudiu Beznea static struct platform_driver vbattb_clk_driver = {
195*3b42450cSClaudiu Beznea 	.driver		= {
196*3b42450cSClaudiu Beznea 		.name	= "renesas-vbattb-clk",
197*3b42450cSClaudiu Beznea 		.of_match_table = vbattb_clk_match,
198*3b42450cSClaudiu Beznea 	},
199*3b42450cSClaudiu Beznea 	.probe = vbattb_clk_probe,
200*3b42450cSClaudiu Beznea };
201*3b42450cSClaudiu Beznea module_platform_driver(vbattb_clk_driver);
202*3b42450cSClaudiu Beznea 
203*3b42450cSClaudiu Beznea MODULE_DESCRIPTION("Renesas VBATTB Clock Driver");
204*3b42450cSClaudiu Beznea MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
205*3b42450cSClaudiu Beznea MODULE_LICENSE("GPL");
206