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