1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Texas Instruments System Control Interface (TI SCI) clock driver 4 * 5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ 6 * Andreas Dannenberg <dannenberg@ti.com> 7 * 8 * Loosely based on Linux kernel sci-clk.c... 9 */ 10 11 #include <common.h> 12 #include <dm.h> 13 #include <errno.h> 14 #include <clk-uclass.h> 15 #include <linux/soc/ti/ti_sci_protocol.h> 16 17 /** 18 * struct ti_sci_clk_data - clock controller information structure 19 * @sci: TI SCI handle used for communication with system controller 20 */ 21 struct ti_sci_clk_data { 22 const struct ti_sci_handle *sci; 23 }; 24 25 static int ti_sci_clk_probe(struct udevice *dev) 26 { 27 struct ti_sci_clk_data *data = dev_get_priv(dev); 28 29 debug("%s(dev=%p)\n", __func__, dev); 30 31 if (!data) 32 return -ENOMEM; 33 34 /* Store handle for communication with the system controller */ 35 data->sci = ti_sci_get_handle(dev); 36 if (IS_ERR(data->sci)) 37 return PTR_ERR(data->sci); 38 39 return 0; 40 } 41 42 static int ti_sci_clk_of_xlate(struct clk *clk, 43 struct ofnode_phandle_args *args) 44 { 45 debug("%s(clk=%p, args_count=%d)\n", __func__, clk, args->args_count); 46 47 if (args->args_count != 2) { 48 debug("Invalid args_count: %d\n", args->args_count); 49 return -EINVAL; 50 } 51 52 /* 53 * On TI SCI-based devices, the clock provider id field is used as a 54 * device ID, and the data field is used as the associated sub-ID. 55 */ 56 clk->id = args->args[0]; 57 clk->data = args->args[1]; 58 59 return 0; 60 } 61 62 static int ti_sci_clk_request(struct clk *clk) 63 { 64 debug("%s(clk=%p)\n", __func__, clk); 65 return 0; 66 } 67 68 static int ti_sci_clk_free(struct clk *clk) 69 { 70 debug("%s(clk=%p)\n", __func__, clk); 71 return 0; 72 } 73 74 static ulong ti_sci_clk_get_rate(struct clk *clk) 75 { 76 struct ti_sci_clk_data *data = dev_get_priv(clk->dev); 77 const struct ti_sci_handle *sci = data->sci; 78 const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; 79 u64 current_freq; 80 int ret; 81 82 debug("%s(clk=%p)\n", __func__, clk); 83 84 ret = cops->get_freq(sci, clk->id, clk->data, ¤t_freq); 85 if (ret) { 86 dev_err(clk->dev, "%s: get_freq failed (%d)\n", __func__, ret); 87 return ret; 88 } 89 90 debug("%s(current_freq=%llu)\n", __func__, current_freq); 91 92 return current_freq; 93 } 94 95 static ulong ti_sci_clk_set_rate(struct clk *clk, ulong rate) 96 { 97 struct ti_sci_clk_data *data = dev_get_priv(clk->dev); 98 const struct ti_sci_handle *sci = data->sci; 99 const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; 100 int ret; 101 102 debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate); 103 104 /* Ask for exact frequency by using same value for min/target/max */ 105 ret = cops->set_freq(sci, clk->id, clk->data, rate, rate, rate); 106 if (ret) 107 dev_err(clk->dev, "%s: set_freq failed (%d)\n", __func__, ret); 108 109 return ret; 110 } 111 112 static int ti_sci_clk_set_parent(struct clk *clk, struct clk *parent) 113 { 114 struct ti_sci_clk_data *data = dev_get_priv(clk->dev); 115 const struct ti_sci_handle *sci = data->sci; 116 const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; 117 u8 num_parents; 118 u8 parent_cid; 119 int ret; 120 121 debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent); 122 123 /* Make sure the clock parent is valid for a given device ID */ 124 if (clk->id != parent->id) 125 return -EINVAL; 126 127 /* Make sure clock has parents that can be set */ 128 ret = cops->get_num_parents(sci, clk->id, clk->data, &num_parents); 129 if (ret) { 130 dev_err(clk->dev, "%s: get_num_parents failed (%d)\n", 131 __func__, ret); 132 return ret; 133 } 134 if (num_parents < 2) { 135 dev_err(clk->dev, "%s: clock has no settable parents!\n", 136 __func__); 137 return -EINVAL; 138 } 139 140 /* Make sure parent clock ID is valid */ 141 parent_cid = parent->data - clk->data - 1; 142 if (parent_cid >= num_parents) { 143 dev_err(clk->dev, "%s: invalid parent clock!\n", __func__); 144 return -EINVAL; 145 } 146 147 /* Ready to proceed to configure the new clock parent */ 148 ret = cops->set_parent(sci, clk->id, clk->data, parent->data); 149 if (ret) 150 dev_err(clk->dev, "%s: set_parent failed (%d)\n", __func__, 151 ret); 152 153 return ret; 154 } 155 156 static int ti_sci_clk_enable(struct clk *clk) 157 { 158 struct ti_sci_clk_data *data = dev_get_priv(clk->dev); 159 const struct ti_sci_handle *sci = data->sci; 160 const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; 161 int ret; 162 163 debug("%s(clk=%p)\n", __func__, clk); 164 165 /* 166 * Allow the System Controller to automatically manage the state of 167 * this clock. If the device is enabled, then the clock is enabled. 168 */ 169 ret = cops->put_clock(sci, clk->id, clk->data); 170 if (ret) 171 dev_err(clk->dev, "%s: put_clock failed (%d)\n", __func__, ret); 172 173 return ret; 174 } 175 176 static int ti_sci_clk_disable(struct clk *clk) 177 { 178 struct ti_sci_clk_data *data = dev_get_priv(clk->dev); 179 const struct ti_sci_handle *sci = data->sci; 180 const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; 181 int ret; 182 183 debug("%s(clk=%p)\n", __func__, clk); 184 185 /* Unconditionally disable clock, regardless of state of the device */ 186 ret = cops->idle_clock(sci, clk->id, clk->data); 187 if (ret) 188 dev_err(clk->dev, "%s: idle_clock failed (%d)\n", __func__, 189 ret); 190 191 return ret; 192 } 193 194 static const struct udevice_id ti_sci_clk_of_match[] = { 195 { .compatible = "ti,k2g-sci-clk" }, 196 { /* sentinel */ }, 197 }; 198 199 static struct clk_ops ti_sci_clk_ops = { 200 .of_xlate = ti_sci_clk_of_xlate, 201 .request = ti_sci_clk_request, 202 .free = ti_sci_clk_free, 203 .get_rate = ti_sci_clk_get_rate, 204 .set_rate = ti_sci_clk_set_rate, 205 .set_parent = ti_sci_clk_set_parent, 206 .enable = ti_sci_clk_enable, 207 .disable = ti_sci_clk_disable, 208 }; 209 210 U_BOOT_DRIVER(ti_sci_clk) = { 211 .name = "ti-sci-clk", 212 .id = UCLASS_CLK, 213 .of_match = ti_sci_clk_of_match, 214 .probe = ti_sci_clk_probe, 215 .priv_auto_alloc_size = sizeof(struct ti_sci_clk_data), 216 .ops = &ti_sci_clk_ops, 217 }; 218