|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * System Control and Power Interface (SCMI) Protocol based clock driver | 
|  | * | 
|  | * Copyright (C) 2018-2024 ARM Ltd. | 
|  | */ | 
|  |  | 
|  | #include <linux/bits.h> | 
|  | #include <linux/clk-provider.h> | 
|  | #include <linux/device.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/scmi_protocol.h> | 
|  | #include <asm/div64.h> | 
|  |  | 
|  | #define NOT_ATOMIC	false | 
|  | #define ATOMIC		true | 
|  |  | 
|  | enum scmi_clk_feats { | 
|  | SCMI_CLK_ATOMIC_SUPPORTED, | 
|  | SCMI_CLK_STATE_CTRL_SUPPORTED, | 
|  | SCMI_CLK_RATE_CTRL_SUPPORTED, | 
|  | SCMI_CLK_PARENT_CTRL_SUPPORTED, | 
|  | SCMI_CLK_DUTY_CYCLE_SUPPORTED, | 
|  | SCMI_CLK_FEATS_COUNT | 
|  | }; | 
|  |  | 
|  | #define SCMI_MAX_CLK_OPS	BIT(SCMI_CLK_FEATS_COUNT) | 
|  |  | 
|  | static const struct scmi_clk_proto_ops *scmi_proto_clk_ops; | 
|  |  | 
|  | struct scmi_clk { | 
|  | u32 id; | 
|  | struct device *dev; | 
|  | struct clk_hw hw; | 
|  | const struct scmi_clock_info *info; | 
|  | const struct scmi_protocol_handle *ph; | 
|  | struct clk_parent_data *parent_data; | 
|  | }; | 
|  |  | 
|  | #define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) | 
|  |  | 
|  | static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | int ret; | 
|  | u64 rate; | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | ret = scmi_proto_clk_ops->rate_get(clk->ph, clk->id, &rate); | 
|  | if (ret) | 
|  | return 0; | 
|  | return rate; | 
|  | } | 
|  |  | 
|  | static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long *parent_rate) | 
|  | { | 
|  | u64 fmin, fmax, ftmp; | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | /* | 
|  | * We can't figure out what rate it will be, so just return the | 
|  | * rate back to the caller. scmi_clk_recalc_rate() will be called | 
|  | * after the rate is set and we'll know what rate the clock is | 
|  | * running at then. | 
|  | */ | 
|  | if (clk->info->rate_discrete) | 
|  | return rate; | 
|  |  | 
|  | fmin = clk->info->range.min_rate; | 
|  | fmax = clk->info->range.max_rate; | 
|  | if (rate <= fmin) | 
|  | return fmin; | 
|  | else if (rate >= fmax) | 
|  | return fmax; | 
|  |  | 
|  | ftmp = rate - fmin; | 
|  | ftmp += clk->info->range.step_size - 1; /* to round up */ | 
|  | do_div(ftmp, clk->info->range.step_size); | 
|  |  | 
|  | return ftmp * clk->info->range.step_size + fmin; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate); | 
|  | } | 
|  |  | 
|  | static int scmi_clk_set_parent(struct clk_hw *hw, u8 parent_index) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | return scmi_proto_clk_ops->parent_set(clk->ph, clk->id, parent_index); | 
|  | } | 
|  |  | 
|  | static u8 scmi_clk_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  | u32 parent_id, p_idx; | 
|  | int ret; | 
|  |  | 
|  | ret = scmi_proto_clk_ops->parent_get(clk->ph, clk->id, &parent_id); | 
|  | if (ret) | 
|  | return 0; | 
|  |  | 
|  | for (p_idx = 0; p_idx < clk->info->num_parents; p_idx++) { | 
|  | if (clk->parent_data[p_idx].index == parent_id) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (p_idx == clk->info->num_parents) | 
|  | return 0; | 
|  |  | 
|  | return p_idx; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) | 
|  | { | 
|  | /* | 
|  | * Suppose all the requested rates are supported, and let firmware | 
|  | * to handle the left work. | 
|  | */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | return scmi_proto_clk_ops->enable(clk->ph, clk->id, NOT_ATOMIC); | 
|  | } | 
|  |  | 
|  | static void scmi_clk_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | scmi_proto_clk_ops->disable(clk->ph, clk->id, NOT_ATOMIC); | 
|  | } | 
|  |  | 
|  | static int scmi_clk_atomic_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | return scmi_proto_clk_ops->enable(clk->ph, clk->id, ATOMIC); | 
|  | } | 
|  |  | 
|  | static void scmi_clk_atomic_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | scmi_proto_clk_ops->disable(clk->ph, clk->id, ATOMIC); | 
|  | } | 
|  |  | 
|  | static int __scmi_clk_is_enabled(struct clk_hw *hw, bool atomic) | 
|  | { | 
|  | int ret; | 
|  | bool enabled = false; | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | ret = scmi_proto_clk_ops->state_get(clk->ph, clk->id, &enabled, atomic); | 
|  | if (ret) | 
|  | dev_warn(clk->dev, | 
|  | "Failed to get state for clock ID %d\n", clk->id); | 
|  |  | 
|  | return !!enabled; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_atomic_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | return __scmi_clk_is_enabled(hw, ATOMIC); | 
|  | } | 
|  |  | 
|  | static int scmi_clk_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | return __scmi_clk_is_enabled(hw, NOT_ATOMIC); | 
|  | } | 
|  |  | 
|  | static int scmi_clk_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) | 
|  | { | 
|  | int ret; | 
|  | u32 val; | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | ret = scmi_proto_clk_ops->config_oem_get(clk->ph, clk->id, | 
|  | SCMI_CLOCK_CFG_DUTY_CYCLE, | 
|  | &val, NULL, false); | 
|  | if (!ret) { | 
|  | duty->num = val; | 
|  | duty->den = 100; | 
|  | } else { | 
|  | dev_warn(clk->dev, | 
|  | "Failed to get duty cycle for clock ID %d\n", clk->id); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) | 
|  | { | 
|  | int ret; | 
|  | u32 val; | 
|  | struct scmi_clk *clk = to_scmi_clk(hw); | 
|  |  | 
|  | /* SCMI OEM Duty Cycle is expressed as a percentage */ | 
|  | val = (duty->num * 100) / duty->den; | 
|  | ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id, | 
|  | SCMI_CLOCK_CFG_DUTY_CYCLE, | 
|  | val, false); | 
|  | if (ret) | 
|  | dev_warn(clk->dev, | 
|  | "Failed to set duty cycle(%u/%u) for clock ID %d\n", | 
|  | duty->num, duty->den, clk->id); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, | 
|  | const struct clk_ops *scmi_ops) | 
|  | { | 
|  | int ret; | 
|  | unsigned long min_rate, max_rate; | 
|  |  | 
|  | struct clk_init_data init = { | 
|  | .flags = CLK_GET_RATE_NOCACHE, | 
|  | .num_parents = sclk->info->num_parents, | 
|  | .ops = scmi_ops, | 
|  | .name = sclk->info->name, | 
|  | .parent_data = sclk->parent_data, | 
|  | }; | 
|  |  | 
|  | sclk->hw.init = &init; | 
|  | ret = devm_clk_hw_register(dev, &sclk->hw); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | if (sclk->info->rate_discrete) { | 
|  | int num_rates = sclk->info->list.num_rates; | 
|  |  | 
|  | if (num_rates <= 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | min_rate = sclk->info->list.rates[0]; | 
|  | max_rate = sclk->info->list.rates[num_rates - 1]; | 
|  | } else { | 
|  | min_rate = sclk->info->range.min_rate; | 
|  | max_rate = sclk->info->range.max_rate; | 
|  | } | 
|  |  | 
|  | clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * scmi_clk_ops_alloc() - Alloc and configure clock operations | 
|  | * @dev: A device reference for devres | 
|  | * @feats_key: A bitmap representing the desired clk_ops capabilities | 
|  | * | 
|  | * Allocate and configure a proper set of clock operations depending on the | 
|  | * specifically required SCMI clock features. | 
|  | * | 
|  | * Return: A pointer to the allocated and configured clk_ops on success, | 
|  | *	   or NULL on allocation failure. | 
|  | */ | 
|  | static const struct clk_ops * | 
|  | scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) | 
|  | { | 
|  | struct clk_ops *ops; | 
|  |  | 
|  | ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); | 
|  | if (!ops) | 
|  | return NULL; | 
|  | /* | 
|  | * We can provide enable/disable/is_enabled atomic callbacks only if the | 
|  | * underlying SCMI transport for an SCMI instance is configured to | 
|  | * handle SCMI commands in an atomic manner. | 
|  | * | 
|  | * When no SCMI atomic transport support is available we instead provide | 
|  | * only the prepare/unprepare API, as allowed by the clock framework | 
|  | * when atomic calls are not available. | 
|  | */ | 
|  | if (feats_key & BIT(SCMI_CLK_STATE_CTRL_SUPPORTED)) { | 
|  | if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) { | 
|  | ops->enable = scmi_clk_atomic_enable; | 
|  | ops->disable = scmi_clk_atomic_disable; | 
|  | } else { | 
|  | ops->prepare = scmi_clk_enable; | 
|  | ops->unprepare = scmi_clk_disable; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) | 
|  | ops->is_enabled = scmi_clk_atomic_is_enabled; | 
|  | else | 
|  | ops->is_prepared = scmi_clk_is_enabled; | 
|  |  | 
|  | /* Rate ops */ | 
|  | ops->recalc_rate = scmi_clk_recalc_rate; | 
|  | ops->round_rate = scmi_clk_round_rate; | 
|  | ops->determine_rate = scmi_clk_determine_rate; | 
|  | if (feats_key & BIT(SCMI_CLK_RATE_CTRL_SUPPORTED)) | 
|  | ops->set_rate = scmi_clk_set_rate; | 
|  |  | 
|  | /* Parent ops */ | 
|  | ops->get_parent = scmi_clk_get_parent; | 
|  | if (feats_key & BIT(SCMI_CLK_PARENT_CTRL_SUPPORTED)) | 
|  | ops->set_parent = scmi_clk_set_parent; | 
|  |  | 
|  | /* Duty cycle */ | 
|  | if (feats_key & BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED)) { | 
|  | ops->get_duty_cycle = scmi_clk_get_duty_cycle; | 
|  | ops->set_duty_cycle = scmi_clk_set_duty_cycle; | 
|  | } | 
|  |  | 
|  | return ops; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * scmi_clk_ops_select() - Select a proper set of clock operations | 
|  | * @sclk: A reference to an SCMI clock descriptor | 
|  | * @atomic_capable: A flag to indicate if atomic mode is supported by the | 
|  | *		    transport | 
|  | * @atomic_threshold_us: Platform atomic threshold value in microseconds: | 
|  | *			 clk_ops are atomic when clock enable latency is less | 
|  | *			 than this threshold | 
|  | * @clk_ops_db: A reference to the array used as a database to store all the | 
|  | *		created clock operations combinations. | 
|  | * @db_size: Maximum number of entries held by @clk_ops_db | 
|  | * | 
|  | * After having built a bitmap descriptor to represent the set of features | 
|  | * needed by this SCMI clock, at first use it to lookup into the set of | 
|  | * previously allocated clk_ops to check if a suitable combination of clock | 
|  | * operations was already created; when no match is found allocate a brand new | 
|  | * set of clk_ops satisfying the required combination of features and save it | 
|  | * for future references. | 
|  | * | 
|  | * In this way only one set of clk_ops is ever created for each different | 
|  | * combination that is effectively needed by a driver instance. | 
|  | * | 
|  | * Return: A pointer to the allocated and configured clk_ops on success, or | 
|  | *	   NULL otherwise. | 
|  | */ | 
|  | static const struct clk_ops * | 
|  | scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, | 
|  | unsigned int atomic_threshold_us, | 
|  | const struct clk_ops **clk_ops_db, size_t db_size) | 
|  | { | 
|  | const struct scmi_clock_info *ci = sclk->info; | 
|  | unsigned int feats_key = 0; | 
|  | const struct clk_ops *ops; | 
|  |  | 
|  | /* | 
|  | * Note that when transport is atomic but SCMI protocol did not | 
|  | * specify (or support) an enable_latency associated with a | 
|  | * clock, we default to use atomic operations mode. | 
|  | */ | 
|  | if (atomic_capable && ci->enable_latency <= atomic_threshold_us) | 
|  | feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED); | 
|  |  | 
|  | if (!ci->state_ctrl_forbidden) | 
|  | feats_key |= BIT(SCMI_CLK_STATE_CTRL_SUPPORTED); | 
|  |  | 
|  | if (!ci->rate_ctrl_forbidden) | 
|  | feats_key |= BIT(SCMI_CLK_RATE_CTRL_SUPPORTED); | 
|  |  | 
|  | if (!ci->parent_ctrl_forbidden) | 
|  | feats_key |= BIT(SCMI_CLK_PARENT_CTRL_SUPPORTED); | 
|  |  | 
|  | if (ci->extended_config) | 
|  | feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED); | 
|  |  | 
|  | if (WARN_ON(feats_key >= db_size)) | 
|  | return NULL; | 
|  |  | 
|  | /* Lookup previously allocated ops */ | 
|  | ops = clk_ops_db[feats_key]; | 
|  | if (ops) | 
|  | return ops; | 
|  |  | 
|  | /* Did not find a pre-allocated clock_ops */ | 
|  | ops = scmi_clk_ops_alloc(sclk->dev, feats_key); | 
|  | if (!ops) | 
|  | return NULL; | 
|  |  | 
|  | /* Store new ops combinations */ | 
|  | clk_ops_db[feats_key] = ops; | 
|  |  | 
|  | return ops; | 
|  | } | 
|  |  | 
|  | static int scmi_clocks_probe(struct scmi_device *sdev) | 
|  | { | 
|  | int idx, count, err; | 
|  | unsigned int atomic_threshold_us; | 
|  | bool transport_is_atomic; | 
|  | struct clk_hw **hws; | 
|  | struct clk_hw_onecell_data *clk_data; | 
|  | struct device *dev = &sdev->dev; | 
|  | struct device_node *np = dev->of_node; | 
|  | const struct scmi_handle *handle = sdev->handle; | 
|  | struct scmi_protocol_handle *ph; | 
|  | const struct clk_ops *scmi_clk_ops_db[SCMI_MAX_CLK_OPS] = {}; | 
|  |  | 
|  | if (!handle) | 
|  | return -ENODEV; | 
|  |  | 
|  | scmi_proto_clk_ops = | 
|  | handle->devm_protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph); | 
|  | if (IS_ERR(scmi_proto_clk_ops)) | 
|  | return PTR_ERR(scmi_proto_clk_ops); | 
|  |  | 
|  | count = scmi_proto_clk_ops->count_get(ph); | 
|  | if (count < 0) { | 
|  | dev_err(dev, "%pOFn: invalid clock output count\n", np); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count), | 
|  | GFP_KERNEL); | 
|  | if (!clk_data) | 
|  | return -ENOMEM; | 
|  |  | 
|  | clk_data->num = count; | 
|  | hws = clk_data->hws; | 
|  |  | 
|  | transport_is_atomic = handle->is_transport_atomic(handle, | 
|  | &atomic_threshold_us); | 
|  |  | 
|  | for (idx = 0; idx < count; idx++) { | 
|  | struct scmi_clk *sclk; | 
|  | const struct clk_ops *scmi_ops; | 
|  |  | 
|  | sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL); | 
|  | if (!sclk) | 
|  | return -ENOMEM; | 
|  |  | 
|  | sclk->info = scmi_proto_clk_ops->info_get(ph, idx); | 
|  | if (!sclk->info) { | 
|  | dev_dbg(dev, "invalid clock info for idx %d\n", idx); | 
|  | devm_kfree(dev, sclk); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | sclk->id = idx; | 
|  | sclk->ph = ph; | 
|  | sclk->dev = dev; | 
|  |  | 
|  | /* | 
|  | * Note that the scmi_clk_ops_db is on the stack, not global, | 
|  | * because it cannot be shared between mulitple probe-sequences | 
|  | * to avoid sharing the devm_ allocated clk_ops between multiple | 
|  | * SCMI clk driver instances. | 
|  | */ | 
|  | scmi_ops = scmi_clk_ops_select(sclk, transport_is_atomic, | 
|  | atomic_threshold_us, | 
|  | scmi_clk_ops_db, | 
|  | ARRAY_SIZE(scmi_clk_ops_db)); | 
|  | if (!scmi_ops) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Initialize clock parent data. */ | 
|  | if (sclk->info->num_parents > 0) { | 
|  | sclk->parent_data = devm_kcalloc(dev, sclk->info->num_parents, | 
|  | sizeof(*sclk->parent_data), GFP_KERNEL); | 
|  | if (!sclk->parent_data) | 
|  | return -ENOMEM; | 
|  |  | 
|  | for (int i = 0; i < sclk->info->num_parents; i++) { | 
|  | sclk->parent_data[i].index = sclk->info->parents[i]; | 
|  | sclk->parent_data[i].hw = hws[sclk->info->parents[i]]; | 
|  | } | 
|  | } | 
|  |  | 
|  | err = scmi_clk_ops_init(dev, sclk, scmi_ops); | 
|  | if (err) { | 
|  | dev_err(dev, "failed to register clock %d\n", idx); | 
|  | devm_kfree(dev, sclk->parent_data); | 
|  | devm_kfree(dev, sclk); | 
|  | hws[idx] = NULL; | 
|  | } else { | 
|  | dev_dbg(dev, "Registered clock:%s%s\n", | 
|  | sclk->info->name, | 
|  | scmi_ops->enable ? " (atomic ops)" : ""); | 
|  | hws[idx] = &sclk->hw; | 
|  | } | 
|  | } | 
|  |  | 
|  | return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, | 
|  | clk_data); | 
|  | } | 
|  |  | 
|  | static const struct scmi_device_id scmi_id_table[] = { | 
|  | { SCMI_PROTOCOL_CLOCK, "clocks" }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(scmi, scmi_id_table); | 
|  |  | 
|  | static struct scmi_driver scmi_clocks_driver = { | 
|  | .name = "scmi-clocks", | 
|  | .probe = scmi_clocks_probe, | 
|  | .id_table = scmi_id_table, | 
|  | }; | 
|  | module_scmi_driver(scmi_clocks_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); | 
|  | MODULE_DESCRIPTION("ARM SCMI clock driver"); | 
|  | MODULE_LICENSE("GPL v2"); |