1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2020-2022 Linaro Limited 4 */ 5 6#define LOG_CATEGORY UCLASS_REGULATOR 7 8#include <common.h> 9#include <dm.h> 10#include <errno.h> 11#include <scmi_agent.h> 12#include <scmi_protocols.h> 13#include <asm/types.h> 14#include <dm/device.h> 15#include <dm/device_compat.h> 16#include <dm/device-internal.h> 17#include <linux/kernel.h> 18#include <power/regulator.h> 19 20/** 21 * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator 22 * @domain_id: ID representing the regulator for the related SCMI agent 23 */ 24struct scmi_regulator_platdata { 25 u32 domain_id; 26}; 27 28static int scmi_voltd_set_enable(struct udevice *dev, bool enable) 29{ 30 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 31 struct scmi_voltd_config_set_in in = { 32 .domain_id = pdata->domain_id, 33 .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, 34 }; 35 struct scmi_voltd_config_set_out out; 36 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, 37 SCMI_VOLTAGE_DOMAIN_CONFIG_SET, 38 in, out); 39 int ret; 40 41 ret = devm_scmi_process_msg(dev, &msg); 42 if (ret) 43 return ret; 44 45 return scmi_to_linux_errno(out.status); 46} 47 48static int scmi_voltd_get_enable(struct udevice *dev) 49{ 50 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 51 struct scmi_voltd_config_get_in in = { 52 .domain_id = pdata->domain_id, 53 }; 54 struct scmi_voltd_config_get_out out; 55 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, 56 SCMI_VOLTAGE_DOMAIN_CONFIG_GET, 57 in, out); 58 int ret; 59 60 ret = devm_scmi_process_msg(dev, &msg); 61 if (ret < 0) 62 return ret; 63 64 ret = scmi_to_linux_errno(out.status); 65 if (ret < 0) 66 return ret; 67 68 return out.config == SCMI_VOLTD_CONFIG_ON; 69} 70 71static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) 72{ 73 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 74 struct scmi_voltd_level_set_in in = { 75 .domain_id = pdata->domain_id, 76 .voltage_level = uV, 77 }; 78 struct scmi_voltd_level_set_out out; 79 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, 80 SCMI_VOLTAGE_DOMAIN_LEVEL_SET, 81 in, out); 82 int ret; 83 84 ret = devm_scmi_process_msg(dev, &msg); 85 if (ret < 0) 86 return ret; 87 88 return scmi_to_linux_errno(out.status); 89} 90 91static int scmi_voltd_get_voltage_level(struct udevice *dev) 92{ 93 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 94 struct scmi_voltd_level_get_in in = { 95 .domain_id = pdata->domain_id, 96 }; 97 struct scmi_voltd_level_get_out out; 98 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, 99 SCMI_VOLTAGE_DOMAIN_LEVEL_GET, 100 in, out); 101 int ret; 102 103 ret = devm_scmi_process_msg(dev, &msg); 104 if (ret < 0) 105 return ret; 106 107 ret = scmi_to_linux_errno(out.status); 108 if (ret < 0) 109 return ret; 110 111 return out.voltage_level; 112} 113 114static int scmi_regulator_of_to_plat(struct udevice *dev) 115{ 116 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 117 fdt_addr_t reg; 118 119 reg = dev_read_addr(dev); 120 if (reg == FDT_ADDR_T_NONE) 121 return -EINVAL; 122 123 pdata->domain_id = (u32)reg; 124 125 return 0; 126} 127 128static int scmi_regulator_probe(struct udevice *dev) 129{ 130 struct scmi_regulator_platdata *pdata = dev_get_plat(dev); 131 struct scmi_voltd_attr_in in = { 0 }; 132 struct scmi_voltd_attr_out out = { 0 }; 133 struct scmi_msg scmi_msg = { 134 .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, 135 .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES, 136 .in_msg = (u8 *)&in, 137 .in_msg_sz = sizeof(in), 138 .out_msg = (u8 *)&out, 139 .out_msg_sz = sizeof(out), 140 }; 141 int ret; 142 143 ret = devm_scmi_of_get_channel(dev); 144 if (ret) 145 return ret; 146 147 /* Check voltage domain is known from SCMI server */ 148 in.domain_id = pdata->domain_id; 149 150 ret = devm_scmi_process_msg(dev, &scmi_msg); 151 if (ret) { 152 dev_err(dev, "Failed to query voltage domain %u: %d\n", 153 pdata->domain_id, ret); 154 return -ENXIO; 155 } 156 157 return 0; 158} 159 160static const struct dm_regulator_ops scmi_voltd_ops = { 161 .get_value = scmi_voltd_get_voltage_level, 162 .set_value = scmi_voltd_set_voltage_level, 163 .get_enable = scmi_voltd_get_enable, 164 .set_enable = scmi_voltd_set_enable, 165}; 166 167U_BOOT_DRIVER(scmi_regulator) = { 168 .name = "scmi_regulator", 169 .id = UCLASS_REGULATOR, 170 .ops = &scmi_voltd_ops, 171 .probe = scmi_regulator_probe, 172 .of_to_plat = scmi_regulator_of_to_plat, 173 .plat_auto = sizeof(struct scmi_regulator_platdata), 174}; 175 176static int scmi_regulator_bind(struct udevice *dev) 177{ 178 struct driver *drv; 179 ofnode node; 180 int ret; 181 182 drv = DM_DRIVER_GET(scmi_regulator); 183 184 ofnode_for_each_subnode(node, dev_ofnode(dev)) { 185 ret = device_bind(dev, drv, ofnode_get_name(node), 186 NULL, node, NULL); 187 if (ret) 188 return ret; 189 } 190 191 return 0; 192} 193 194U_BOOT_DRIVER(scmi_voltage_domain) = { 195 .name = "scmi_voltage_domain", 196 .id = UCLASS_NOP, 197 .bind = scmi_regulator_bind, 198}; 199