1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * SCMI Power domain driver 4 * 5 * Copyright (C) 2023 Linaro Limited 6 * author: AKASHI Takahiro <takahiro.akashi@linaro.org> 7 */ 8 9#include <dm.h> 10#include <malloc.h> 11#include <power-domain.h> 12#include <power-domain-uclass.h> 13#include <scmi_agent.h> 14#include <scmi_protocols.h> 15#include <dm/device_compat.h> 16 17/** 18 * struct scmi_pwd_properties 19 * @attributes: Power domain attributes 20 * @name: Name of the domain 21 */ 22struct scmi_pwd_properties { 23 u32 attributes; 24 u8 *name; /* not used now */ 25}; 26 27/** 28 * struct scmi_power_domain_priv 29 * @num_pwdoms: Number of power domains 30 * @prop: Pointer to domain's properties 31 * @stats_addr: Address of statistics memory region 32 * @stats_len: Length of statistics memory region 33 */ 34struct scmi_power_domain_priv { 35 int num_pwdoms; 36 struct scmi_pwd_properties *prop; 37 u64 stats_addr; 38 size_t stats_len; 39}; 40 41/** 42 * async_is_supported - check asynchronous transition 43 * @attributes: Power domain attributes 44 * 45 * Determine if the power transition can be done asynchronously. 46 * 47 * Return: true if supported, false if not 48 */ 49static bool async_is_supported(u32 attributes) 50{ 51 if (attributes & SCMI_PWD_ATTR_PSTATE_ASYNC) 52 return true; 53 54 /* TODO: check attributes && SCMI_PWD_ATTR_PSTATE_SYNC */ 55 return false; 56} 57 58/** 59 * scmi_power_domain_on - Enable the power domain 60 * @power_domain: Power domain 61 * 62 * Turn on the power domain. 63 * 64 * Return: 0 on success, error code on failure 65 */ 66static int scmi_power_domain_on(struct power_domain *power_domain) 67{ 68 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); 69 u32 flags, pstate; 70 int ret; 71 72 if (power_domain->id > priv->num_pwdoms) 73 return -EINVAL; 74 75 if (async_is_supported(priv->prop[power_domain->id].attributes)) 76 flags = SCMI_PWD_SET_FLAGS_ASYNC; 77 else 78 flags = 0; 79 80 /* ON */ 81 pstate = 0; 82 83 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, 84 pstate); 85 if (ret) { 86 dev_err(power_domain->dev, "failed to set the state on (%d)\n", 87 ret); 88 return ret; 89 } 90 91 return 0; 92} 93 94/** 95 * scmi_power_domain_off - Disable the power domain 96 * @power_domain: Power domain 97 * 98 * Turn off the power domain. 99 * 100 * Return: 0 on success, error code on failure 101 */ 102static int scmi_power_domain_off(struct power_domain *power_domain) 103{ 104 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); 105 u32 flags, pstate; 106 int ret; 107 108 if (power_domain->id > priv->num_pwdoms) 109 return -EINVAL; 110 111 if (async_is_supported(priv->prop[power_domain->id].attributes)) 112 flags = SCMI_PWD_SET_FLAGS_ASYNC; 113 else 114 flags = 0; 115 116 /* OFF */ 117 pstate = SCMI_PWD_PSTATE_TYPE_LOST; 118 119 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, 120 pstate); 121 if (ret) { 122 dev_err(power_domain->dev, "failed to set the state off (%d)\n", 123 ret); 124 return ret; 125 } 126 127 return 0; 128} 129 130/** 131 * scmi_power_domain_probe - Probe the power domain 132 * @dev: Power domain device 133 * 134 * Probe the power domain and initialize the properties. 135 * 136 * Return: 0 on success, error code on failure 137 */ 138static int scmi_power_domain_probe(struct udevice *dev) 139{ 140 struct scmi_power_domain_priv *priv = dev_get_priv(dev); 141 u32 version; 142 int i, ret; 143 144 ret = devm_scmi_of_get_channel(dev); 145 if (ret) { 146 dev_err(dev, "failed to get channel (%d)\n", ret); 147 return ret; 148 } 149 150 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_POWER_DOMAIN, 151 &version); 152 153 ret = scmi_pwd_protocol_attrs(dev, &priv->num_pwdoms, &priv->stats_addr, 154 &priv->stats_len); 155 if (ret) { 156 dev_err(dev, "failed to get protocol attributes (%d)\n", ret); 157 return ret; 158 } 159 160 priv->prop = calloc(sizeof(*priv->prop), priv->num_pwdoms); 161 if (!priv->prop) 162 return -ENOMEM; 163 164 for (i = 0; i < priv->num_pwdoms; i++) { 165 ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes, 166 &priv->prop[i].name); 167 if (ret) { 168 dev_err(dev, "failed to get attributes pwd:%d (%d)\n", 169 i, ret); 170 for (i--; i >= 0; i--) 171 free(priv->prop[i].name); 172 free(priv->prop); 173 174 return ret; 175 } 176 } 177 178 return 0; 179} 180 181struct power_domain_ops scmi_power_domain_ops = { 182 .on = scmi_power_domain_on, 183 .off = scmi_power_domain_off, 184}; 185 186U_BOOT_DRIVER(scmi_power_domain) = { 187 .name = "scmi_power_domain", 188 .id = UCLASS_POWER_DOMAIN, 189 .ops = &scmi_power_domain_ops, 190 .probe = scmi_power_domain_probe, 191 .priv_auto = sizeof(struct scmi_power_domain_priv), 192}; 193