xref: /openbmc/u-boot/drivers/clk/clk_vexpress_osc.c (revision 5830791d91d1200854ef78fcb32f808c8080f0f0)
1a71e907cSLiviu Dudau // SPDX-License-Identifier: GPL-2.0+
2a71e907cSLiviu Dudau /*
3a71e907cSLiviu Dudau  * Copyright (C) 2018 Arm Ltd
4a71e907cSLiviu Dudau  * Author: Liviu Dudau <liviu.dudau@foss.arm.com>
5a71e907cSLiviu Dudau  *
6a71e907cSLiviu Dudau  */
7a71e907cSLiviu Dudau #define DEBUG
8a71e907cSLiviu Dudau #include <common.h>
9a71e907cSLiviu Dudau #include <clk-uclass.h>
10a71e907cSLiviu Dudau #include <dm.h>
11a71e907cSLiviu Dudau #include <dm/lists.h>
12a71e907cSLiviu Dudau #include <errno.h>
13a71e907cSLiviu Dudau #include <misc.h>
14a71e907cSLiviu Dudau 
15a71e907cSLiviu Dudau #define CLK_FUNCTION		BIT(20)
16a71e907cSLiviu Dudau 
17a71e907cSLiviu Dudau struct vexpress_osc_clk_priv {
18a71e907cSLiviu Dudau 	u8 osc;
19a71e907cSLiviu Dudau 	ulong rate_min;
20a71e907cSLiviu Dudau 	ulong rate_max;
21a71e907cSLiviu Dudau };
22a71e907cSLiviu Dudau 
vexpress_osc_clk_get_rate(struct clk * clk)23a71e907cSLiviu Dudau static ulong vexpress_osc_clk_get_rate(struct clk *clk)
24a71e907cSLiviu Dudau {
25a71e907cSLiviu Dudau 	int err;
26a71e907cSLiviu Dudau 	u32 data;
27a71e907cSLiviu Dudau 	struct udevice *vexpress_cfg = dev_get_parent(clk->dev);
28a71e907cSLiviu Dudau 	struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev);
29a71e907cSLiviu Dudau 
30a71e907cSLiviu Dudau 	data = CLK_FUNCTION | priv->osc;
31a71e907cSLiviu Dudau 	err = misc_read(vexpress_cfg, 0, &data, sizeof(data));
32*8729b1aeSSimon Glass 	if (err < 0)
33a71e907cSLiviu Dudau 		return err;
34a71e907cSLiviu Dudau 
35a71e907cSLiviu Dudau 	return data;
36a71e907cSLiviu Dudau }
37a71e907cSLiviu Dudau 
38a71e907cSLiviu Dudau #ifndef CONFIG_SPL_BUILD
vexpress_osc_clk_set_rate(struct clk * clk,ulong rate)39a71e907cSLiviu Dudau static ulong vexpress_osc_clk_set_rate(struct clk *clk, ulong rate)
40a71e907cSLiviu Dudau {
41a71e907cSLiviu Dudau 	int err;
42a71e907cSLiviu Dudau 	u32 buffer[2];
43a71e907cSLiviu Dudau 	struct udevice *vexpress_cfg = dev_get_parent(clk->dev);
44a71e907cSLiviu Dudau 	struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev);
45a71e907cSLiviu Dudau 
46a71e907cSLiviu Dudau 	if (rate < priv->rate_min || rate > priv->rate_max)
47a71e907cSLiviu Dudau 		return -EINVAL;
48a71e907cSLiviu Dudau 
49a71e907cSLiviu Dudau 	/*
50a71e907cSLiviu Dudau 	 * we are sending the parent the info about the oscillator
51a71e907cSLiviu Dudau 	 * and the value we want to set
52a71e907cSLiviu Dudau 	 */
53a71e907cSLiviu Dudau 	buffer[0] = CLK_FUNCTION | priv->osc;
54a71e907cSLiviu Dudau 	buffer[1] = rate;
55a71e907cSLiviu Dudau 	err = misc_write(vexpress_cfg, 0, buffer, 2 * sizeof(u32));
56*8729b1aeSSimon Glass 	if (err < 0)
57a71e907cSLiviu Dudau 		return err;
58a71e907cSLiviu Dudau 
59a71e907cSLiviu Dudau 	return rate;
60a71e907cSLiviu Dudau }
61a71e907cSLiviu Dudau #endif
62a71e907cSLiviu Dudau 
63a71e907cSLiviu Dudau static struct clk_ops vexpress_osc_clk_ops = {
64a71e907cSLiviu Dudau 	.get_rate = vexpress_osc_clk_get_rate,
65a71e907cSLiviu Dudau #ifndef CONFIG_SPL_BUILD
66a71e907cSLiviu Dudau 	.set_rate = vexpress_osc_clk_set_rate,
67a71e907cSLiviu Dudau #endif
68a71e907cSLiviu Dudau };
69a71e907cSLiviu Dudau 
vexpress_osc_clk_probe(struct udevice * dev)70a71e907cSLiviu Dudau static int vexpress_osc_clk_probe(struct udevice *dev)
71a71e907cSLiviu Dudau {
72a71e907cSLiviu Dudau 	struct vexpress_osc_clk_priv *priv = dev_get_priv(dev);
73a71e907cSLiviu Dudau 	u32 values[2];
74a71e907cSLiviu Dudau 	int err;
75a71e907cSLiviu Dudau 
76a71e907cSLiviu Dudau 	err = dev_read_u32_array(dev, "freq-range", values, 2);
77a71e907cSLiviu Dudau 	if (err)
78a71e907cSLiviu Dudau 		return err;
79a71e907cSLiviu Dudau 	priv->rate_min = values[0];
80a71e907cSLiviu Dudau 	priv->rate_max = values[1];
81a71e907cSLiviu Dudau 
82a71e907cSLiviu Dudau 	err = dev_read_u32_array(dev, "arm,vexpress-sysreg,func", values, 2);
83a71e907cSLiviu Dudau 	if (err)
84a71e907cSLiviu Dudau 		return err;
85a71e907cSLiviu Dudau 
86a71e907cSLiviu Dudau 	if (values[0] != 1) {
87a71e907cSLiviu Dudau 		dev_err(dev, "Invalid VExpress function for clock, must be '1'");
88a71e907cSLiviu Dudau 		return -EINVAL;
89a71e907cSLiviu Dudau 	}
90a71e907cSLiviu Dudau 	priv->osc = values[1];
91a71e907cSLiviu Dudau 	debug("clk \"%s%d\", min freq %luHz, max freq %luHz\n", dev->name,
92a71e907cSLiviu Dudau 	      priv->osc, priv->rate_min, priv->rate_max);
93a71e907cSLiviu Dudau 
94a71e907cSLiviu Dudau 	return 0;
95a71e907cSLiviu Dudau }
96a71e907cSLiviu Dudau 
97a71e907cSLiviu Dudau static const struct udevice_id vexpress_osc_clk_ids[] = {
98a71e907cSLiviu Dudau 	{ .compatible = "arm,vexpress-osc", },
99a71e907cSLiviu Dudau 	{}
100a71e907cSLiviu Dudau };
101a71e907cSLiviu Dudau 
102a71e907cSLiviu Dudau U_BOOT_DRIVER(vexpress_osc_clk) = {
103a71e907cSLiviu Dudau 	.name = "vexpress_osc_clk",
104a71e907cSLiviu Dudau 	.id = UCLASS_CLK,
105a71e907cSLiviu Dudau 	.of_match = vexpress_osc_clk_ids,
106a71e907cSLiviu Dudau 	.ops = &vexpress_osc_clk_ops,
107a71e907cSLiviu Dudau 	.priv_auto_alloc_size = sizeof(struct vexpress_osc_clk_priv),
108a71e907cSLiviu Dudau 	.probe = vexpress_osc_clk_probe,
109a71e907cSLiviu Dudau };
110