Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.0-rc1 410 lines 11 kB view raw
1/* linux/drivers/hwmon/s3c-hwmon.c 2 * 3 * Copyright (C) 2005, 2008, 2009 Simtec Electronics 4 * http://armlinux.simtec.co.uk/ 5 * Ben Dooks <ben@simtec.co.uk> 6 * 7 * S3C24XX/S3C64XX ADC hwmon support 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21*/ 22 23#include <linux/module.h> 24#include <linux/slab.h> 25#include <linux/delay.h> 26#include <linux/io.h> 27#include <linux/init.h> 28#include <linux/err.h> 29#include <linux/clk.h> 30#include <linux/interrupt.h> 31#include <linux/platform_device.h> 32 33#include <linux/hwmon.h> 34#include <linux/hwmon-sysfs.h> 35 36#include <plat/adc.h> 37#include <plat/hwmon.h> 38 39struct s3c_hwmon_attr { 40 struct sensor_device_attribute in; 41 struct sensor_device_attribute label; 42 char in_name[12]; 43 char label_name[12]; 44}; 45 46/** 47 * struct s3c_hwmon - ADC hwmon client information 48 * @lock: Access lock to serialise the conversions. 49 * @client: The client we registered with the S3C ADC core. 50 * @hwmon_dev: The hwmon device we created. 51 * @attr: The holders for the channel attributes. 52*/ 53struct s3c_hwmon { 54 struct mutex lock; 55 struct s3c_adc_client *client; 56 struct device *hwmon_dev; 57 58 struct s3c_hwmon_attr attrs[8]; 59}; 60 61/** 62 * s3c_hwmon_read_ch - read a value from a given adc channel. 63 * @dev: The device. 64 * @hwmon: Our state. 65 * @channel: The channel we're reading from. 66 * 67 * Read a value from the @channel with the proper locking and sleep until 68 * either the read completes or we timeout awaiting the ADC core to get 69 * back to us. 70 */ 71static int s3c_hwmon_read_ch(struct device *dev, 72 struct s3c_hwmon *hwmon, int channel) 73{ 74 int ret; 75 76 ret = mutex_lock_interruptible(&hwmon->lock); 77 if (ret < 0) 78 return ret; 79 80 dev_dbg(dev, "reading channel %d\n", channel); 81 82 ret = s3c_adc_read(hwmon->client, channel); 83 mutex_unlock(&hwmon->lock); 84 85 return ret; 86} 87 88#ifdef CONFIG_SENSORS_S3C_RAW 89/** 90 * s3c_hwmon_show_raw - show a conversion from the raw channel number. 91 * @dev: The device that the attribute belongs to. 92 * @attr: The attribute being read. 93 * @buf: The result buffer. 94 * 95 * This show deals with the raw attribute, registered for each possible 96 * ADC channel. This does a conversion and returns the raw (un-scaled) 97 * value returned from the hardware. 98 */ 99static ssize_t s3c_hwmon_show_raw(struct device *dev, 100 struct device_attribute *attr, char *buf) 101{ 102 struct s3c_hwmon *adc = platform_get_drvdata(to_platform_device(dev)); 103 struct sensor_device_attribute *sa = to_sensor_dev_attr(attr); 104 int ret; 105 106 ret = s3c_hwmon_read_ch(dev, adc, sa->index); 107 108 return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret); 109} 110 111#define DEF_ADC_ATTR(x) \ 112 static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x) 113 114DEF_ADC_ATTR(0); 115DEF_ADC_ATTR(1); 116DEF_ADC_ATTR(2); 117DEF_ADC_ATTR(3); 118DEF_ADC_ATTR(4); 119DEF_ADC_ATTR(5); 120DEF_ADC_ATTR(6); 121DEF_ADC_ATTR(7); 122 123static struct attribute *s3c_hwmon_attrs[9] = { 124 &sensor_dev_attr_adc0_raw.dev_attr.attr, 125 &sensor_dev_attr_adc1_raw.dev_attr.attr, 126 &sensor_dev_attr_adc2_raw.dev_attr.attr, 127 &sensor_dev_attr_adc3_raw.dev_attr.attr, 128 &sensor_dev_attr_adc4_raw.dev_attr.attr, 129 &sensor_dev_attr_adc5_raw.dev_attr.attr, 130 &sensor_dev_attr_adc6_raw.dev_attr.attr, 131 &sensor_dev_attr_adc7_raw.dev_attr.attr, 132 NULL, 133}; 134 135static struct attribute_group s3c_hwmon_attrgroup = { 136 .attrs = s3c_hwmon_attrs, 137}; 138 139static inline int s3c_hwmon_add_raw(struct device *dev) 140{ 141 return sysfs_create_group(&dev->kobj, &s3c_hwmon_attrgroup); 142} 143 144static inline void s3c_hwmon_remove_raw(struct device *dev) 145{ 146 sysfs_remove_group(&dev->kobj, &s3c_hwmon_attrgroup); 147} 148 149#else 150 151static inline int s3c_hwmon_add_raw(struct device *dev) { return 0; } 152static inline void s3c_hwmon_remove_raw(struct device *dev) { } 153 154#endif /* CONFIG_SENSORS_S3C_RAW */ 155 156/** 157 * s3c_hwmon_ch_show - show value of a given channel 158 * @dev: The device that the attribute belongs to. 159 * @attr: The attribute being read. 160 * @buf: The result buffer. 161 * 162 * Read a value from the ADC and scale it before returning it to the 163 * caller. The scale factor is gained from the channel configuration 164 * passed via the platform data when the device was registered. 165 */ 166static ssize_t s3c_hwmon_ch_show(struct device *dev, 167 struct device_attribute *attr, 168 char *buf) 169{ 170 struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); 171 struct s3c_hwmon *hwmon = platform_get_drvdata(to_platform_device(dev)); 172 struct s3c_hwmon_pdata *pdata = dev->platform_data; 173 struct s3c_hwmon_chcfg *cfg; 174 int ret; 175 176 cfg = pdata->in[sen_attr->index]; 177 178 ret = s3c_hwmon_read_ch(dev, hwmon, sen_attr->index); 179 if (ret < 0) 180 return ret; 181 182 ret *= cfg->mult; 183 ret = DIV_ROUND_CLOSEST(ret, cfg->div); 184 185 return snprintf(buf, PAGE_SIZE, "%d\n", ret); 186} 187 188/** 189 * s3c_hwmon_label_show - show label name of the given channel. 190 * @dev: The device that the attribute belongs to. 191 * @attr: The attribute being read. 192 * @buf: The result buffer. 193 * 194 * Return the label name of a given channel 195 */ 196static ssize_t s3c_hwmon_label_show(struct device *dev, 197 struct device_attribute *attr, 198 char *buf) 199{ 200 struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); 201 struct s3c_hwmon_pdata *pdata = dev->platform_data; 202 struct s3c_hwmon_chcfg *cfg; 203 204 cfg = pdata->in[sen_attr->index]; 205 206 return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name); 207} 208 209/** 210 * s3c_hwmon_create_attr - create hwmon attribute for given channel. 211 * @dev: The device to create the attribute on. 212 * @cfg: The channel configuration passed from the platform data. 213 * @channel: The ADC channel number to process. 214 * 215 * Create the scaled attribute for use with hwmon from the specified 216 * platform data in @pdata. The sysfs entry is handled by the routine 217 * s3c_hwmon_ch_show(). 218 * 219 * The attribute name is taken from the configuration data if present 220 * otherwise the name is taken by concatenating in_ with the channel 221 * number. 222 */ 223static int s3c_hwmon_create_attr(struct device *dev, 224 struct s3c_hwmon_chcfg *cfg, 225 struct s3c_hwmon_attr *attrs, 226 int channel) 227{ 228 struct sensor_device_attribute *attr; 229 int ret; 230 231 snprintf(attrs->in_name, sizeof(attrs->in_name), "in%d_input", channel); 232 233 attr = &attrs->in; 234 attr->index = channel; 235 attr->dev_attr.attr.name = attrs->in_name; 236 attr->dev_attr.attr.mode = S_IRUGO; 237 attr->dev_attr.show = s3c_hwmon_ch_show; 238 239 ret = device_create_file(dev, &attr->dev_attr); 240 if (ret < 0) { 241 dev_err(dev, "failed to create input attribute\n"); 242 return ret; 243 } 244 245 /* if this has a name, add a label */ 246 if (cfg->name) { 247 snprintf(attrs->label_name, sizeof(attrs->label_name), 248 "in%d_label", channel); 249 250 attr = &attrs->label; 251 attr->index = channel; 252 attr->dev_attr.attr.name = attrs->label_name; 253 attr->dev_attr.attr.mode = S_IRUGO; 254 attr->dev_attr.show = s3c_hwmon_label_show; 255 256 ret = device_create_file(dev, &attr->dev_attr); 257 if (ret < 0) { 258 device_remove_file(dev, &attrs->in.dev_attr); 259 dev_err(dev, "failed to create label attribute\n"); 260 } 261 } 262 263 return ret; 264} 265 266static void s3c_hwmon_remove_attr(struct device *dev, 267 struct s3c_hwmon_attr *attrs) 268{ 269 device_remove_file(dev, &attrs->in.dev_attr); 270 device_remove_file(dev, &attrs->label.dev_attr); 271} 272 273/** 274 * s3c_hwmon_probe - device probe entry. 275 * @dev: The device being probed. 276*/ 277static int __devinit s3c_hwmon_probe(struct platform_device *dev) 278{ 279 struct s3c_hwmon_pdata *pdata = dev->dev.platform_data; 280 struct s3c_hwmon *hwmon; 281 int ret = 0; 282 int i; 283 284 if (!pdata) { 285 dev_err(&dev->dev, "no platform data supplied\n"); 286 return -EINVAL; 287 } 288 289 hwmon = kzalloc(sizeof(struct s3c_hwmon), GFP_KERNEL); 290 if (hwmon == NULL) { 291 dev_err(&dev->dev, "no memory\n"); 292 return -ENOMEM; 293 } 294 295 platform_set_drvdata(dev, hwmon); 296 297 mutex_init(&hwmon->lock); 298 299 /* Register with the core ADC driver. */ 300 301 hwmon->client = s3c_adc_register(dev, NULL, NULL, 0); 302 if (IS_ERR(hwmon->client)) { 303 dev_err(&dev->dev, "cannot register adc\n"); 304 ret = PTR_ERR(hwmon->client); 305 goto err_mem; 306 } 307 308 /* add attributes for our adc devices. */ 309 310 ret = s3c_hwmon_add_raw(&dev->dev); 311 if (ret) 312 goto err_registered; 313 314 /* register with the hwmon core */ 315 316 hwmon->hwmon_dev = hwmon_device_register(&dev->dev); 317 if (IS_ERR(hwmon->hwmon_dev)) { 318 dev_err(&dev->dev, "error registering with hwmon\n"); 319 ret = PTR_ERR(hwmon->hwmon_dev); 320 goto err_raw_attribute; 321 } 322 323 for (i = 0; i < ARRAY_SIZE(pdata->in); i++) { 324 struct s3c_hwmon_chcfg *cfg = pdata->in[i]; 325 326 if (!cfg) 327 continue; 328 329 if (cfg->mult >= 0x10000) 330 dev_warn(&dev->dev, 331 "channel %d multiplier too large\n", 332 i); 333 334 if (cfg->div == 0) { 335 dev_err(&dev->dev, "channel %d divider zero\n", i); 336 continue; 337 } 338 339 ret = s3c_hwmon_create_attr(&dev->dev, pdata->in[i], 340 &hwmon->attrs[i], i); 341 if (ret) { 342 dev_err(&dev->dev, 343 "error creating channel %d\n", i); 344 345 for (i--; i >= 0; i--) 346 s3c_hwmon_remove_attr(&dev->dev, 347 &hwmon->attrs[i]); 348 349 goto err_hwmon_register; 350 } 351 } 352 353 return 0; 354 355 err_hwmon_register: 356 hwmon_device_unregister(hwmon->hwmon_dev); 357 358 err_raw_attribute: 359 s3c_hwmon_remove_raw(&dev->dev); 360 361 err_registered: 362 s3c_adc_release(hwmon->client); 363 364 err_mem: 365 kfree(hwmon); 366 return ret; 367} 368 369static int __devexit s3c_hwmon_remove(struct platform_device *dev) 370{ 371 struct s3c_hwmon *hwmon = platform_get_drvdata(dev); 372 int i; 373 374 s3c_hwmon_remove_raw(&dev->dev); 375 376 for (i = 0; i < ARRAY_SIZE(hwmon->attrs); i++) 377 s3c_hwmon_remove_attr(&dev->dev, &hwmon->attrs[i]); 378 379 hwmon_device_unregister(hwmon->hwmon_dev); 380 s3c_adc_release(hwmon->client); 381 382 return 0; 383} 384 385static struct platform_driver s3c_hwmon_driver = { 386 .driver = { 387 .name = "s3c-hwmon", 388 .owner = THIS_MODULE, 389 }, 390 .probe = s3c_hwmon_probe, 391 .remove = __devexit_p(s3c_hwmon_remove), 392}; 393 394static int __init s3c_hwmon_init(void) 395{ 396 return platform_driver_register(&s3c_hwmon_driver); 397} 398 399static void __exit s3c_hwmon_exit(void) 400{ 401 platform_driver_unregister(&s3c_hwmon_driver); 402} 403 404module_init(s3c_hwmon_init); 405module_exit(s3c_hwmon_exit); 406 407MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 408MODULE_DESCRIPTION("S3C ADC HWMon driver"); 409MODULE_LICENSE("GPL v2"); 410MODULE_ALIAS("platform:s3c-hwmon");