From patchwork Mon Oct 5 22:26:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Marussi X-Patchwork-Id: 11817819 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AB28B139A for ; Mon, 5 Oct 2020 22:28:40 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6EEB42075A for ; Mon, 5 Oct 2020 22:28:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="DkPf/5Ms" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6EEB42075A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=arm.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:MIME-Version:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:References:In-Reply-To:Message-Id:Date:Subject:To: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=Kr6IbfatNHvdyvK498FXRmLJmw3hvuTlulgc9REGTG0=; b=DkPf/5MskoWmH8eW3z5bvSXwsX A/32BNM/bETVq39CGS7ybavRZ1WyyslXSaafjfyFfztPV6qVTT8LO+GMi+Nts4CBM+7Xgei1Lqj+c lwaJRGORqfeaWMvzQz6s/yVMAITEejTcEYXQoIaSSL/MqS9D/kLSjgrw1HO77QEAqTCQqIRxHcHkp ZL9HKrtr6Ux4x5sZbKIKJAlUwBrX2TdKQrkpJHOtxRaYmf57ZEUc1J8Zq47ICI0p0Vh/62hMjoEL7 0vY0AfZf1sl4d/qHChM9tEnEuYXghTf3ZazwNIAGKNz97WyeFXB2cDPMTTt8J0377myeP2sVvji2q pn4uHdRQ==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kPYwV-0007uB-5U; Mon, 05 Oct 2020 22:26:51 +0000 Received: from foss.arm.com ([217.140.110.172]) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kPYwO-0007sJ-Oa for linux-arm-kernel@lists.infradead.org; Mon, 05 Oct 2020 22:26:47 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id CE74C1477; Mon, 5 Oct 2020 15:26:40 -0700 (PDT) Received: from e120937-lin.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id DE8053F70D; Mon, 5 Oct 2020 15:26:38 -0700 (PDT) From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH 3/4] regulator: add SCMI driver Date: Mon, 5 Oct 2020 23:26:22 +0100 Message-Id: <20201005222623.1123-4-cristian.marussi@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201005222623.1123-1-cristian.marussi@arm.com> References: <20201005222623.1123-1-cristian.marussi@arm.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20201005_182645_011715_A545473D X-CRM114-Status: GOOD ( 29.13 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [217.140.110.172 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: robh@kernel.org, satyakim@qti.qualcomm.com, sudeep.holla@arm.com, broonie@kernel.org, cristian.marussi@arm.com, james.quinlan@broadcom.com, Jonathan.Cameron@Huawei.com, etienne.carriere@linaro.org, lukasz.luba@arm.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add a simple regulator based on SCMI Voltage Domain Protocol. Supported operations are: - .enable / .disable / .is_enabled: routed via SCMI Voltage Domain Protocol - .get_voltage / .set_voltage: routed via SCMI Voltage Domain Protocol - .get_voltage_sel/.set_voltage_sel: using regulator framework helpers - .list_voltage/.map_voltage: using regulator frameowrk helpers Signed-off-by: Cristian Marussi ---- v2 --> v3 - fixed init_data constraint parsing - fixes for v5.8 (linear_range.h) v1 --> v2 - fixed commit message content and subject line format - factored out SCMI core specific changes to distinct patch - reworked Kconfig and Makefile to keep proper alphabetic order - fixed SPDX comment style - removed unneeded inline functions - reworked conditionals for legibility - fixed some return paths to properly report SCMI original errors codes - added some more descriptive error messages when fw returns invalid ranges - removed unneeded explicit devm_regulator_unregister from .remove() --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/scmi-regulator.c | 488 +++++++++++++++++++++++++++++ 3 files changed, 498 insertions(+) create mode 100644 drivers/regulator/scmi-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index de17ef7e18f0..6d3a10cb9833 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP and Wolfson Microelectronic Arizona codecs devices. +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OF + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + config REGULATOR_AS3711 tristate "AS3711 PMIC" depends on MFD_AS3711 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d8d3ecf526a8..0532a7393d5d 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o +obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 000000000000..041c6a9dd4df --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage/set_voltage +// get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct scmi_regulator { + u32 id; + const char *name; + struct scmi_device *sdev; + struct regulator_dev *rdev; + struct device_node *of_node; + struct regulator_desc desc; + struct regulator_config conf; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->config_get(handle, sreg->id, + &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sreg->name); + return 0; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage(struct regulator_dev *rdev) +{ + int ret; + u32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, (s32 *)&volt_uV); + if (ret) + return ret; + + return volt_uV; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned int *selector) +{ + int ret; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + /* + * This should have been filtered out by non-negative + * constraints...just in case. + */ + if (min_uV < 0 || max_uV < 0) { + dev_warn(&sreg->sdev->dev, "Negative voltages NOT supported\n"); + return -EINVAL; + } + + ret = handle->voltage_ops->level_set(handle, sreg->id, 0x0, min_uV); + if (ret) + return ret; + + if (selector) + *selector = sreg->desc.ops->map_voltage(rdev, min_uV, min_uV); + + return ret; +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + ret = handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); + if (ret) + return ret; + + return ret; +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage = scmi_reg_get_voltage, + .set_voltage = scmi_reg_set_voltage, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_range_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage = scmi_reg_get_voltage, + .set_voltage = scmi_reg_set_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in triplets by + * the SCMI Voltage Domain protocol support itself. + */ + if (vinfo->num_levels == 3) { + s32 delta_uV; + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + sreg->desc.fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.n_voltages = 1; + sreg->desc.ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + sreg->desc.min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + sreg->desc.linear_min_sel = 0; + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; + sreg->desc.ops = &scmi_reg_linear_ops; + } + } else { + /* Multiple linear mappings. */ + int i, num_ranges, last_max = -1; + struct linear_range *lr; + + num_ranges = vinfo->num_levels / 3; + lr = devm_kcalloc(&sreg->sdev->dev, num_ranges, + sizeof(*lr), GFP_KERNEL); + if (!lr) + return -ENOMEM; + + sreg->desc.n_linear_ranges = num_ranges; + sreg->desc.linear_ranges = lr; + for (i = 0; num_ranges; num_ranges--, i += 3, lr++) { + s32 delta_uV; + + lr->min = + vinfo->levels_uv[i + SCMI_VOLTAGE_SEGMENT_LOW]; + lr->step = + vinfo->levels_uv[i + SCMI_VOLTAGE_SEGMENT_STEP]; + delta_uV = + vinfo->levels_uv[i + SCMI_VOLTAGE_SEGMENT_HIGH] - + lr->min; + if (delta_uV <= 0 || !(delta_uV / lr->step)) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + lr->max_sel = delta_uV / lr->step - 1; + lr->min_sel = last_max + 1; + last_max = lr->max_sel; + } + sreg->desc.n_voltages = last_max + 1; + sreg->desc.ops = &scmi_reg_range_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* Discrete non linear levels are mapped to volt_table */ + sreg->desc.n_voltages = vinfo->num_levels; + if (sreg->desc.n_voltages > 1) { + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; + sreg->desc.ops = &scmi_reg_discrete_ops; + } else { + sreg->desc.fixed_uV = vinfo->levels_uv[0]; + sreg->desc.ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + const struct scmi_handle *handle = sreg->sdev->handle; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + + vinfo = handle->voltage_ops->info_get(handle, sreg->id); + if (!vinfo) + return -ENODEV; + + if (!vinfo->num_levels) + return -EINVAL; + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", + sreg->of_node->full_name); + return -EOPNOTSUPP; + } + + sreg->name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, + "Vscmi.%s", sreg->name); + if (!sreg->name || !sreg->desc.name) + return -ENOMEM; + + sreg->desc.id = sreg->id; + sreg->desc.type = REGULATOR_VOLTAGE; + sreg->desc.owner = THIS_MODULE; + sreg->desc.of_match = sreg->of_node->name; + sreg->desc.regulators_node = "regulators"; + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + if (ret) + return ret; + + sreg->conf.dev = dev; + sreg->conf.driver_data = sreg; + + return 0; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %s\n", + dom, np->full_name); + return -EINVAL; + } + + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, + sizeof(struct scmi_regulator), + GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + /* get hold of good nodes */ + of_node_get(np); + rinfo->sregv[dom]->of_node = np; + dev_info(&sdev->dev, + "Found valid SCMI Regulator -- OF node [%d] -> %s\n", + dom, np->full_name); + + return ret; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + + if (!handle || !handle->voltage_ops) + return -ENODEV; + + num_doms = handle->voltage_ops->num_domains_get(handle); + if (num_doms <= 0) { + dev_err(&sdev->dev, "number of voltage domains invalid\n"); + return num_doms ?: -EINVAL; + } + + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers' array for all possible domains */ + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, + sizeof(rinfo->sregv), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + if (!sreg) + continue; + ret = scmi_regulator_common_init(sreg); + if (ret) + continue; + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, + &sreg->conf); + if (IS_ERR(sreg->rdev)) { + sreg->rdev = NULL; + continue; + } + + dev_info(&sdev->dev, + "Regulator %s registered for domain [%d] %s\n", + sreg->desc.name, sreg->id, sreg->name); + } + + dev_set_drvdata(&sdev->dev, rinfo); + + return 0; +} + +static void scmi_regulator_remove(struct scmi_device *sdev) +{ + int d; + struct scmi_regulator_info *rinfo; + + rinfo = dev_get_drvdata(&sdev->dev); + if (!rinfo) + return; + + for (d = 0; d < rinfo->num_doms; d++) { + if (!rinfo->sregv[d]) + continue; + of_node_put(rinfo->sregv[d]->of_node); + } +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .remove = scmi_regulator_remove, + .id_table = scmi_regulator_id_table, +}; + +module_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi "); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2");