1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * Copyright 2009 CompuLab, Ltd.
7 * Author: Mike Rapoport <mike@compulab.co.il>
9 * Based of virtual consumer driver:
10 * Copyright 2008 Wolfson Microelectronics PLC.
11 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
14 #include <linux/err.h>
15 #include <linux/mutex.h>
16 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/regulator/consumer.h>
20 #include <linux/regulator/userspace-consumer.h>
21 #include <linux/slab.h>
23 struct userspace_consumer_data
{
31 struct regulator_bulk_data
*supplies
;
34 static ssize_t
name_show(struct device
*dev
,
35 struct device_attribute
*attr
, char *buf
)
37 struct userspace_consumer_data
*data
= dev_get_drvdata(dev
);
39 return sprintf(buf
, "%s\n", data
->name
);
42 static ssize_t
state_show(struct device
*dev
,
43 struct device_attribute
*attr
, char *buf
)
45 struct userspace_consumer_data
*data
= dev_get_drvdata(dev
);
48 return sprintf(buf
, "enabled\n");
50 return sprintf(buf
, "disabled\n");
53 static ssize_t
state_store(struct device
*dev
, struct device_attribute
*attr
,
54 const char *buf
, size_t count
)
56 struct userspace_consumer_data
*data
= dev_get_drvdata(dev
);
61 * sysfs_streq() doesn't need the \n's, but we add them so the strings
62 * will be shared with show_state(), above.
64 if (sysfs_streq(buf
, "enabled\n") || sysfs_streq(buf
, "1"))
66 else if (sysfs_streq(buf
, "disabled\n") || sysfs_streq(buf
, "0"))
69 dev_err(dev
, "Configuring invalid mode\n");
73 mutex_lock(&data
->lock
);
74 if (enabled
!= data
->enabled
) {
76 ret
= regulator_bulk_enable(data
->num_supplies
,
79 ret
= regulator_bulk_disable(data
->num_supplies
,
83 data
->enabled
= enabled
;
85 dev_err(dev
, "Failed to configure state: %d\n", ret
);
87 mutex_unlock(&data
->lock
);
92 static DEVICE_ATTR_RO(name
);
93 static DEVICE_ATTR_RW(state
);
95 static struct attribute
*attributes
[] = {
101 static umode_t
attr_visible(struct kobject
*kobj
, struct attribute
*attr
, int idx
)
103 struct device
*dev
= kobj_to_dev(kobj
);
104 struct userspace_consumer_data
*data
= dev_get_drvdata(dev
);
106 /* If a name hasn't been set, don't bother with the attribute */
107 if (attr
== &dev_attr_name
.attr
&& !data
->name
)
113 static const struct attribute_group attr_group
= {
115 .is_visible
= attr_visible
,
118 static int regulator_userspace_consumer_probe(struct platform_device
*pdev
)
120 struct regulator_userspace_consumer_data tmpdata
;
121 struct regulator_userspace_consumer_data
*pdata
;
122 struct userspace_consumer_data
*drvdata
;
125 pdata
= dev_get_platdata(&pdev
->dev
);
127 if (!pdev
->dev
.of_node
)
131 memset(pdata
, 0, sizeof(*pdata
));
133 pdata
->no_autoswitch
= true;
134 pdata
->num_supplies
= 1;
135 pdata
->supplies
= devm_kzalloc(&pdev
->dev
, sizeof(*pdata
->supplies
), GFP_KERNEL
);
136 if (!pdata
->supplies
)
138 pdata
->supplies
[0].supply
= "vout";
141 if (pdata
->num_supplies
< 1) {
142 dev_err(&pdev
->dev
, "At least one supply required\n");
146 drvdata
= devm_kzalloc(&pdev
->dev
,
147 sizeof(struct userspace_consumer_data
),
152 drvdata
->name
= pdata
->name
;
153 drvdata
->num_supplies
= pdata
->num_supplies
;
154 drvdata
->supplies
= pdata
->supplies
;
155 drvdata
->no_autoswitch
= pdata
->no_autoswitch
;
157 mutex_init(&drvdata
->lock
);
159 ret
= devm_regulator_bulk_get_exclusive(&pdev
->dev
, drvdata
->num_supplies
,
162 return dev_err_probe(&pdev
->dev
, ret
, "Failed to get supplies\n");
164 platform_set_drvdata(pdev
, drvdata
);
166 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &attr_group
);
170 if (pdata
->init_on
&& !pdata
->no_autoswitch
) {
171 ret
= regulator_bulk_enable(drvdata
->num_supplies
,
175 "Failed to set initial state: %d\n", ret
);
180 ret
= regulator_is_enabled(pdata
->supplies
[0].consumer
);
182 dev_err(&pdev
->dev
, "Failed to get regulator status\n");
185 drvdata
->enabled
= !!ret
;
190 sysfs_remove_group(&pdev
->dev
.kobj
, &attr_group
);
195 static void regulator_userspace_consumer_remove(struct platform_device
*pdev
)
197 struct userspace_consumer_data
*data
= platform_get_drvdata(pdev
);
199 sysfs_remove_group(&pdev
->dev
.kobj
, &attr_group
);
201 if (data
->enabled
&& !data
->no_autoswitch
)
202 regulator_bulk_disable(data
->num_supplies
, data
->supplies
);
205 static const struct of_device_id regulator_userspace_consumer_of_match
[] = {
206 { .compatible
= "regulator-output", },
209 MODULE_DEVICE_TABLE(of
, regulator_userspace_consumer_of_match
);
211 static struct platform_driver regulator_userspace_consumer_driver
= {
212 .probe
= regulator_userspace_consumer_probe
,
213 .remove
= regulator_userspace_consumer_remove
,
215 .name
= "reg-userspace-consumer",
216 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
217 .of_match_table
= regulator_userspace_consumer_of_match
,
221 module_platform_driver(regulator_userspace_consumer_driver
);
223 MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
224 MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
225 MODULE_LICENSE("GPL");