Blame view

drivers/regulator/userspace-consumer.c 4.62 KB
1d98cccf7   Mike Rapoport   regulator: add us...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * userspace-consumer.c
   *
   * Copyright 2009 CompuLab, Ltd.
   *
   * Author: Mike Rapoport <mike@compulab.co.il>
   *
   * Based of virtual consumer driver:
   *   Copyright 2008 Wolfson Microelectronics PLC.
   *   Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation; either version 2 of the
   * License, or (at your option) any later version.
   *
   */
  
  #include <linux/err.h>
  #include <linux/mutex.h>
65602c32e   Paul Gortmaker   regulator: Add mo...
21
  #include <linux/module.h>
1d98cccf7   Mike Rapoport   regulator: add us...
22
23
24
  #include <linux/platform_device.h>
  #include <linux/regulator/consumer.h>
  #include <linux/regulator/userspace-consumer.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
1d98cccf7   Mike Rapoport   regulator: add us...
26
27
28
29
30
31
32
33
34
35
  
  struct userspace_consumer_data {
  	const char *name;
  
  	struct mutex lock;
  	bool enabled;
  
  	int num_supplies;
  	struct regulator_bulk_data *supplies;
  };
7c314991d   Liam Girdwood   regulator: build ...
36
  static ssize_t reg_show_name(struct device *dev,
1d98cccf7   Mike Rapoport   regulator: add us...
37
38
39
40
41
42
43
  			  struct device_attribute *attr, char *buf)
  {
  	struct userspace_consumer_data *data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%s
  ", data->name);
  }
7c314991d   Liam Girdwood   regulator: build ...
44
  static ssize_t reg_show_state(struct device *dev,
1d98cccf7   Mike Rapoport   regulator: add us...
45
46
47
48
49
50
51
52
53
54
55
  			  struct device_attribute *attr, char *buf)
  {
  	struct userspace_consumer_data *data = dev_get_drvdata(dev);
  
  	if (data->enabled)
  		return sprintf(buf, "enabled
  ");
  
  	return sprintf(buf, "disabled
  ");
  }
7c314991d   Liam Girdwood   regulator: build ...
56
  static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr,
1d98cccf7   Mike Rapoport   regulator: add us...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  			 const char *buf, size_t count)
  {
  	struct userspace_consumer_data *data = dev_get_drvdata(dev);
  	bool enabled;
  	int ret;
  
  	/*
  	 * sysfs_streq() doesn't need the 
  's, but we add them so the strings
  	 * will be shared with show_state(), above.
  	 */
  	if (sysfs_streq(buf, "enabled
  ") || sysfs_streq(buf, "1"))
  		enabled = true;
  	else if (sysfs_streq(buf, "disabled
  ") || sysfs_streq(buf, "0"))
  		enabled = false;
  	else {
  		dev_err(dev, "Configuring invalid mode
  ");
  		return count;
  	}
  
  	mutex_lock(&data->lock);
  	if (enabled != data->enabled) {
  		if (enabled)
  			ret = regulator_bulk_enable(data->num_supplies,
  						    data->supplies);
  		else
  			ret = regulator_bulk_disable(data->num_supplies,
  						     data->supplies);
  
  		if (ret == 0)
  			data->enabled = enabled;
  		else
  			dev_err(dev, "Failed to configure state: %d
  ", ret);
  	}
  	mutex_unlock(&data->lock);
  
  	return count;
  }
7c314991d   Liam Girdwood   regulator: build ...
99
100
  static DEVICE_ATTR(name, 0444, reg_show_name, NULL);
  static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state);
1d98cccf7   Mike Rapoport   regulator: add us...
101

e9d62698e   Felipe Balbi   regulator: usersp...
102
103
104
105
106
107
108
109
  static struct attribute *attributes[] = {
  	&dev_attr_name.attr,
  	&dev_attr_state.attr,
  	NULL,
  };
  
  static const struct attribute_group attr_group = {
  	.attrs	= attributes,
1d98cccf7   Mike Rapoport   regulator: add us...
110
111
112
113
114
115
  };
  
  static int regulator_userspace_consumer_probe(struct platform_device *pdev)
  {
  	struct regulator_userspace_consumer_data *pdata;
  	struct userspace_consumer_data *drvdata;
e9d62698e   Felipe Balbi   regulator: usersp...
116
  	int ret;
1d98cccf7   Mike Rapoport   regulator: add us...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  
  	pdata = pdev->dev.platform_data;
  	if (!pdata)
  		return -EINVAL;
  
  	drvdata = kzalloc(sizeof(struct userspace_consumer_data), GFP_KERNEL);
  	if (drvdata == NULL)
  		return -ENOMEM;
  
  	drvdata->name = pdata->name;
  	drvdata->num_supplies = pdata->num_supplies;
  	drvdata->supplies = pdata->supplies;
  
  	mutex_init(&drvdata->lock);
  
  	ret = regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
  				 drvdata->supplies);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to get supplies: %d
  ", ret);
  		goto err_alloc_supplies;
  	}
e9d62698e   Felipe Balbi   regulator: usersp...
139
140
141
  	ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
  	if (ret != 0)
  		goto err_create_attrs;
1d98cccf7   Mike Rapoport   regulator: add us...
142

e9d62698e   Felipe Balbi   regulator: usersp...
143
  	if (pdata->init_on) {
1d98cccf7   Mike Rapoport   regulator: add us...
144
145
  		ret = regulator_bulk_enable(drvdata->num_supplies,
  					    drvdata->supplies);
e9d62698e   Felipe Balbi   regulator: usersp...
146
147
148
149
150
151
  		if (ret) {
  			dev_err(&pdev->dev,
  				"Failed to set initial state: %d
  ", ret);
  			goto err_enable;
  		}
1d98cccf7   Mike Rapoport   regulator: add us...
152
  	}
e9d62698e   Felipe Balbi   regulator: usersp...
153
  	drvdata->enabled = pdata->init_on;
1d98cccf7   Mike Rapoport   regulator: add us...
154
155
156
  	platform_set_drvdata(pdev, drvdata);
  
  	return 0;
e9d62698e   Felipe Balbi   regulator: usersp...
157
158
  err_enable:
  	sysfs_remove_group(&pdev->dev.kobj, &attr_group);
1d98cccf7   Mike Rapoport   regulator: add us...
159

e9d62698e   Felipe Balbi   regulator: usersp...
160
  err_create_attrs:
1d98cccf7   Mike Rapoport   regulator: add us...
161
162
163
164
165
166
167
168
169
170
  	regulator_bulk_free(drvdata->num_supplies, drvdata->supplies);
  
  err_alloc_supplies:
  	kfree(drvdata);
  	return ret;
  }
  
  static int regulator_userspace_consumer_remove(struct platform_device *pdev)
  {
  	struct userspace_consumer_data *data = platform_get_drvdata(pdev);
1d98cccf7   Mike Rapoport   regulator: add us...
171

e9d62698e   Felipe Balbi   regulator: usersp...
172
  	sysfs_remove_group(&pdev->dev.kobj, &attr_group);
1d98cccf7   Mike Rapoport   regulator: add us...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  
  	if (data->enabled)
  		regulator_bulk_disable(data->num_supplies, data->supplies);
  
  	regulator_bulk_free(data->num_supplies, data->supplies);
  	kfree(data);
  
  	return 0;
  }
  
  static struct platform_driver regulator_userspace_consumer_driver = {
  	.probe		= regulator_userspace_consumer_probe,
  	.remove		= regulator_userspace_consumer_remove,
  	.driver		= {
  		.name		= "reg-userspace-consumer",
  	},
  };
005d610f2   Axel Lin   regulator: Conver...
190
  module_platform_driver(regulator_userspace_consumer_driver);
1d98cccf7   Mike Rapoport   regulator: add us...
191
192
193
194
  
  MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
  MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
  MODULE_LICENSE("GPL");