Files
linux-legacy/drivers/hwmon/isl29003.c
Rob Herring be0524d38f ENGR00117389 Port 5.0.0 release to 2.6.31
This is i.MX BSP 5.0.0 release ported to 2.6.31

Signed-off-by: Rob Herring <r.herring@freescale.com>
Signed-off-by: Alan Tull <r80115@freescale.com>
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
2010-08-10 11:44:41 -05:00

439 lines
8.9 KiB
C

/*
* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file drivers/hwmon/isl29003.c
*
* @brief ISL29003 light sensor Driver
*
* @ingroup
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <mach/hardware.h>
enum isl29003_width {
ISL29003_WIDTH_16 = 0,
ISL29003_WIDTH_12,
ISL29003_WIDTH_8,
ISL29003_WIDTH_4,
};
enum isl29003_gain {
ISL29003_GAIN_1000 = 0,
ISL29003_GAIN_4000,
ISL29003_GAIN_16000,
ISL29003_GAIN_64000,
};
enum isl29003_mode {
ISL29003_MODE_DIODE1 = 0,
ISL29003_MODE_DIODE2,
ISL29003_MODE_DIODE1_2,
};
struct isl29003_param {
enum isl29003_width width;
enum isl29003_gain gain;
enum isl29003_mode mode;
};
/* bit definition for ISL29003_CMD reg */
#define ENABLE 7
#define ADCPD 6
#define TIMEING_MODE 5
#define MODE 2
#define WIDTH 0
/* bit definition for ISL29003_CTRL reg */
#define INT_FLAG 5
#define GAIN 2
#define INT_PERSIST 0
enum isl29003_reg {
ISL29003_CMD = 0,
ISL29003_CTRL,
ISL29003_THRS_HI,
ISL29003_THRS_LO,
ISL29003_LSB_S,
ISL29003_MSB_S,
ISL29003_LSB_T,
ISL29003_MSB_T,
ISL29003_SYNC_IIC = 0x80,
ISL29003_CLAR_INT = 0x40
};
/* default configure for ISL29003 */
#define ISL29003_WIDTH_DEFAULT ISL29003_WIDTH_16
#define ISL29003_GAIN_DEFAULT ISL29003_GAIN_16000
#define ISL29003_MODE_DEFAULT ISL29003_MODE_DIODE1
/* range table for different GAIN settings */
int range[4] = { 973, 3892, 15568, 62272 };
/* width table for different WIDTH settings */
int width[4] = { 16, 1, 256, 16 };
struct isl29003_data {
struct i2c_client *client;
struct device *hwmon_dev;
struct regulator *vdd_reg;
struct isl29003_param param;
int lux_coeff;
unsigned char enable;
};
static struct i2c_client *isl29003_client;
/*!
* This function do the isl29003 register read.
*/
int isl29003_read(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
/*!
* This function do the isl29003 register write.
*/
int isl29003_write(struct i2c_client *client, u8 reg, char value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/*!
* This function do the isl29003 config and enable.
*/
static int isl29003_on(void)
{
unsigned char cmd;
int err = 0;
struct mxc_lightsensor_platform_data *ls_data;
struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
if (data->enable)
goto exit;
ls_data = (struct mxc_lightsensor_platform_data *)
(isl29003_client->dev).platform_data;
/* coeff=range*100k/rext/2^n */
data->lux_coeff = range[data->param.gain] * 100 /
ls_data->rext / width[data->param.width];
if (data->vdd_reg)
regulator_enable(data->vdd_reg);
msleep(100);
cmd = data->param.gain << GAIN;
if (isl29003_write(isl29003_client, ISL29003_CTRL, cmd)) {
err = -ENODEV;
goto exit;
}
cmd = (data->param.width << WIDTH) | (data->param.mode << MODE) |
(1 << ENABLE);
if (isl29003_write(isl29003_client, ISL29003_CMD, cmd)) {
err = -ENODEV;
goto exit;
}
data->enable = 1;
pr_info("isl29003 on\n");
return 0;
exit:
return err;
}
/*!
* This function shut down the isl29003.
*/
static int isl29003_off(void)
{
struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
int cmd;
if (!data->enable)
return 0;
cmd = isl29003_read(isl29003_client, ISL29003_CMD);
if (cmd < 0)
return -ENODEV;
cmd = ((cmd | (1 << ADCPD)) & (~(1 << ENABLE)));
if (isl29003_write(isl29003_client, ISL29003_CMD, (char)cmd))
return -ENODEV;
if (data->vdd_reg)
regulator_disable(data->vdd_reg);
data->enable = 0;
pr_info("isl29003 off\n");
return 0;
}
/*!
* This function read the isl29003 lux registers and convert them to the lux
* value.
*
* @output buffer this param holds the lux value, when =-1, read fail
*
* @return 0
*/
static int isl29003_read_lux(void)
{
int d;
int lux;
struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
d = isl29003_read(isl29003_client, ISL29003_MSB_S);
if (d < 0)
goto err;
lux = d;
d = isl29003_read(isl29003_client, ISL29003_LSB_S);
if (d < 0)
goto err;
lux = (lux << 8) + d;
if (data->param.width < ISL29003_WIDTH_8)
lux = (data->lux_coeff * lux) >> 12;
else
lux = data->lux_coeff * lux;
return lux;
err:
return -1;
}
static ssize_t ls_enable(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
char *endp;
int enable = simple_strtoul(buf, &endp, 0);
size_t size = endp - buf;
if (*endp && isspace(*endp))
size++;
if (size != count)
return -EINVAL;
if (enable == 1) {
if (isl29003_on())
pr_info("device open fail\n");
}
if (enable == 0) {
if (isl29003_off())
pr_info("device powerdown fail\n");
}
return count;
}
static SENSOR_DEVICE_ATTR(enable, S_IWUGO, NULL, ls_enable, 0);
static ssize_t show_lux(struct device *dev, struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", isl29003_read_lux());
}
static SENSOR_DEVICE_ATTR(lux, S_IRUGO, show_lux, NULL, 0);
static int isl29003_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
int err = 0;
struct isl29003_data *data;
struct regulator *vdd_reg;
struct mxc_lightsensor_platform_data *ls_data;
ls_data = (struct mxc_lightsensor_platform_data *)
(client->dev).platform_data;
if (ls_data && ls_data->vdd_reg)
vdd_reg = regulator_get(&client->dev, ls_data->vdd_reg);
else
vdd_reg = NULL;
/* check the existence of the device */
if (vdd_reg)
regulator_enable(vdd_reg);
msleep(100);
if (isl29003_write(client, ISL29003_CMD, 0))
err = -ENODEV;
if (!err)
if (isl29003_read(client, ISL29003_CMD))
err = -ENODEV;
if (vdd_reg)
regulator_disable(vdd_reg);
if (err < 0)
goto exit1;
isl29003_client = client;
data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);
if (data == NULL) {
err = -ENOMEM;
goto exit1;
}
i2c_set_clientdata(client, data);
data->client = client;
data->param.width = ISL29003_WIDTH_DEFAULT;
data->param.gain = ISL29003_GAIN_DEFAULT;
data->param.mode = ISL29003_MODE_DEFAULT;
data->enable = 0;
err = device_create_file(&client->dev,
&sensor_dev_attr_enable.dev_attr);
if (err)
goto exit2;
err = device_create_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
if (err)
goto exit_remove1;
/* Register sysfs hooks */
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove2;
}
data->vdd_reg = vdd_reg;
return 0;
exit_remove2:
device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
exit_remove1:
device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr);
exit2:
kfree(data);
exit1:
if (vdd_reg) {
regulator_put(vdd_reg);
vdd_reg = NULL;
}
isl29003_client = NULL;
return err;
}
static int isl29003_i2c_remove(struct i2c_client *client)
{
struct isl29003_data *data = i2c_get_clientdata(client);
if (data->vdd_reg) {
regulator_put(data->vdd_reg);
data->vdd_reg = NULL;
}
hwmon_device_unregister(data->hwmon_dev);
device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr);
device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
kfree(data);
return 0;
}
static int isl29003_suspend(struct i2c_client *client, pm_message_t message)
{
int cmd;
struct isl29003_data *data = i2c_get_clientdata(client);
if (!data->enable)
goto exit;
cmd = isl29003_read(client, ISL29003_CMD);
if (cmd < 0)
goto err;
cmd = (cmd | (1 << ADCPD));
if (isl29003_write(client, ISL29003_CMD, (char)cmd))
goto err;
exit:
return 0;
err:
return -ENODEV;
}
static int isl29003_resume(struct i2c_client *client)
{
int cmd;
struct isl29003_data *data = i2c_get_clientdata(client);
if (!data->enable)
goto exit;
cmd = isl29003_read(client, ISL29003_CMD);
if (cmd < 0)
goto err;
cmd = (cmd & (~(1 << ADCPD)));
if (isl29003_write(client, ISL29003_CMD, (char)cmd))
goto err;
exit:
return 0;
err:
return -ENODEV;
}
static const struct i2c_device_id isl29003_id[] = {
{"isl29003", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, isl29003_id);
static struct i2c_driver isl29003_driver = {
.driver = {
.name = "isl29003",
},
.probe = isl29003_i2c_probe,
.remove = isl29003_i2c_remove,
.suspend = isl29003_suspend,
.resume = isl29003_resume,
.id_table = isl29003_id,
};
static int __init isl29003_init(void)
{
return i2c_add_driver(&isl29003_driver);;
}
static void __exit isl29003_cleanup(void)
{
i2c_del_driver(&isl29003_driver);
}
module_init(isl29003_init);
module_exit(isl29003_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("ISL29003 light sensor driver");
MODULE_LICENSE("GPL");