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