[PATCH] hwmon: add GP2A002 proximity/ambient sensor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Minkyu,

Adding linux-input ML, as driver is using linux input subsystem.

On Fri, Jun 26, 2009 at 2:09 PM, Minkyu Kang<mk7.kang at samsung.com> wrote:
> The GP2A002 is a I2C interfaced Sensor for Proximity/Ambient
> This patch adds support the GP2A002 sensor
>
> Signed-off-by: Minkyu Kang <mk7.kang at samsung.com>
> Signed-off-by: Suchang Woo <suchang.woo at samsung.com>
> ---
> ?drivers/hwmon/Kconfig ? ?| ? ?7 +
> ?drivers/hwmon/Makefile ? | ? ?1 +
> ?drivers/hwmon/gp2ap002.c | ?471 ++++++++++++++++++++++++++++++++++++++++++++++
> ?include/linux/gp2ap002.h | ? 21 ++
> ?4 files changed, 500 insertions(+), 0 deletions(-)
> ?create mode 100644 drivers/hwmon/gp2ap002.c
> ?create mode 100644 include/linux/gp2ap002.h
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 2d50166..cc4e200 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1017,6 +1017,13 @@ config SENSORS_APPLESMC
> ? ? ? ? ?Say Y here if you have an applicable laptop and want to experience
> ? ? ? ? ?the awesome power of applesmc.
>
> +config SENSORS_GP2AP002
> + ? ? ? tristate "GP2AP002 series Proximity/Ambient Sensor"
> + ? ? ? depends on HWMON && I2C
> + ? ? ? help
> + ? ? ? ? This driver provides support for Proximity/Ambient Sensor.
> +
> +
> ?config HWMON_DEBUG_CHIP
> ? ? ? ?bool "Hardware Monitoring Chip debugging messages"
> ? ? ? ?default n
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index b793dce..5dd1671 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -46,6 +46,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) ?+= fscpos.o
> ?obj-$(CONFIG_SENSORS_G760A) ? ?+= g760a.o
> ?obj-$(CONFIG_SENSORS_GL518SM) ?+= gl518sm.o
> ?obj-$(CONFIG_SENSORS_GL520SM) ?+= gl520sm.o
> +obj-$(CONFIG_SENSORS_GP2AP002) += gp2ap002.o
> ?obj-$(CONFIG_SENSORS_ULTRA45) ?+= ultra45_env.o
> ?obj-$(CONFIG_SENSORS_HDAPS) ? ?+= hdaps.o
> ?obj-$(CONFIG_SENSORS_I5K_AMB) ?+= i5k_amb.o
> diff --git a/drivers/hwmon/gp2ap002.c b/drivers/hwmon/gp2ap002.c
> new file mode 100644
> index 0000000..6861584
> --- /dev/null
> +++ b/drivers/hwmon/gp2ap002.c
> @@ -0,0 +1,471 @@
> +/*
> + * ?gp2ap002.c - Proximity/Ambient light sensor
> + *
> + * ?Copyright (C) 2009 Samsung Electronics
> + * ?Minkyu Kang <mk7.kang at samsung.com>
> + * ?Suchang Woo <suchang.woo at samsung.com>
> + * ?Kyungmin Park <kyungmin.park at samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/workqueue.h>
> +#include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/input.h>
> +#include <linux/gp2ap002.h>
> +
> +#define GP2AP002_PROX ? ? ? ? ?0x00 ? ?/* Read Only */
> +#define GP2AP002_GAIN ? ? ? ? ?0x01
> +#define GP2AP002_HYS ? ? ? ? ? 0x02
> +#define GP2AP002_CYCLE ? ? ? ? 0x03
> +#define GP2AP002_OPMOD ? ? ? ? 0x04
> +#define GP2AP002_CON ? ? ? ? ? 0x06
> +
> +#define PROX_VO_NO_DETECT ? ? ?(0 << 0)
> +#define PROX_VO_DETECT ? ? ? ? (1 << 0)
> +
> +#define GAIN_LED0_SMALL ? ? ? ? ? ? ? ?(0 << 3)
> +#define GAIN_LED0_LARGE ? ? ? ? ? ? ? ?(1 << 3)
> +
> +#define HYS_HYSD ? ? ? ? ? ? ? (1 << 7)
> +#define HYS_HYSC1 ? ? ? ? ? ? ?(1 << 6)
> +#define HYS_HYSC0 ? ? ? ? ? ? ?(1 << 5)
> +#define HYS_HYSF3 ? ? ? ? ? ? ?(1 << 3)
> +#define HYS_HYSF2 ? ? ? ? ? ? ?(1 << 2)
> +#define HYS_HYSF1 ? ? ? ? ? ? ?(1 << 1)
> +#define HYS_HYSF0 ? ? ? ? ? ? ?(1 << 0)
> +
> +#define OPMOD_SSD_SHUTDOWN ? ? (0 << 0)
> +#define OPMOD_SSD_OPERATING ? ?(1 << 0)
> +#define OPMOD_VCON_NORMAL ? ? ?(0 << 1)
> +#define OPMOD_VCON_IRQ ? ? ? ? (1 << 1)
> +
> +#define CON_OCON1 ? ? ? ? ? ? ?(1 << 4)
> +#define CON_OCON0 ? ? ? ? ? ? ?(1 << 3)
> +
> +#define GP2AP002_MAX_LUX ? ? ? 10
> +
> +static int lux_table[GP2AP002_MAX_LUX] = {
> + ? ? ? 1, 165, 288, 497, 869, 1532, 2692, 4692, 8280, 100000,
> +};
> +
> +struct gp2ap002_chip {
> + ? ? ? struct i2c_client ? ? ? *client;
> + ? ? ? struct device ? ? ? ? ? *dev;
> + ? ? ? struct input_dev ? ? ? ?*idev;
> + ? ? ? struct work_struct ? ? ?work;
> + ? ? ? struct mutex ? ? ? ? ? ?lock;
> +
> + ? ? ? void (*power_enable)(int onoff);
> + ? ? ? int (*get_adc)(void);
> + ? ? ? int vo_gpio;
> +
> + ? ? ? /* Proximity */
> + ? ? ? int enable;
> + ? ? ? int mode;
> + ? ? ? int vo;
> + ? ? ? /* Ambient Light */
> + ? ? ? int adc;
> + ? ? ? int level;
> +};
> +
> +static int gp2ap002_write_reg(struct i2c_client *client, int reg, u8 value)
> +{
> + ? ? ? int ret;
> +
> + ? ? ? ret = i2c_smbus_write_byte_data(client, reg, value);
> +
> + ? ? ? if (ret < 0)
> + ? ? ? ? ? ? ? dev_err(&client->dev, "%s: err %d\n", __func__, ret);
> +
> + ? ? ? return ret;
> +}
> +
> +static int gp2ap002_read_reg(struct i2c_client *client, int reg)
> +{
> + ? ? ? int ret;
> +
> + ? ? ? ret = i2c_smbus_read_byte_data(client, reg);
> +
> + ? ? ? if (ret < 0)
> + ? ? ? ? ? ? ? dev_err(&client->dev, "%s: err %d\n", __func__, ret);
> +
> + ? ? ? return ret;
> +}
> +
> +static void gp2ap002_get_vo(struct gp2ap002_chip *chip)
> +{
> + ? ? ? if (chip->mode == OPMOD_VCON_IRQ) {
> + ? ? ? ? ? ? ? chip->vo = gp2ap002_read_reg(chip->client,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GP2AP002_PROX) & PROX_VO_DETECT;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? chip->vo = !gpio_get_value(chip->vo_gpio);
> + ? ? ? }
> +}
> +
> +static void gp2ap002_get_adc(struct gp2ap002_chip *chip)
> +{
> + ? ? ? if (chip->get_adc)
> + ? ? ? ? ? ? ? chip->adc = chip->get_adc();
> +}
> +
> +static void gp2ap002_get_level(struct gp2ap002_chip *chip)
> +{
> + ? ? ? int i;
> +
> + ? ? ? gp2ap002_get_adc(chip);
> +
> + ? ? ? for (i = 0; i < GP2AP002_MAX_LUX; i++) {
> + ? ? ? ? ? ? ? if (lux_table[i] > chip->adc) {
> + ? ? ? ? ? ? ? ? ? ? ? chip->level = i;
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +}
> +
> +static void gp2ap002_set_mode(struct gp2ap002_chip *chip, int enable)
> +{
> + ? ? ? if (enable == chip->enable)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? chip->enable = enable;
> + ? ? ? chip->vo = 0;
> +
> + ? ? ? gp2ap002_write_reg(chip->client, GP2AP002_OPMOD, OPMOD_SSD_SHUTDOWN);
> +
> + ? ? ? if (enable) {
> + ? ? ? ? ? ? ? gp2ap002_write_reg(chip->client, GP2AP002_OPMOD,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OPMOD_SSD_OPERATING | chip->mode);
> + ? ? ? ? ? ? ? gp2ap002_get_vo(chip);
> + ? ? ? }
> +}
> +
> +static void gp2ap002_set_enable(struct gp2ap002_chip *chip, const char *buf)
> +{
> + ? ? ? if (!strncmp(buf, "1", 1))
> + ? ? ? ? ? ? ? gp2ap002_set_mode(chip, 1);
> + ? ? ? else if (!strncmp(buf, "0", 1))
> + ? ? ? ? ? ? ? gp2ap002_set_mode(chip, 0);
> +}
> +
> +static void gp2ap002_update_data(struct gp2ap002_chip *chip)
> +{
> + ? ? ? gp2ap002_get_vo(chip);
> + ? ? ? enable_irq(chip->client->irq);
> +}
> +
> +static void gp2ap002_work(struct work_struct *work)
> +{
> + ? ? ? struct gp2ap002_chip *chip = container_of(work,
> + ? ? ? ? ? ? ? ? ? ? ? struct gp2ap002_chip, work);
> +
> + ? ? ? mutex_lock(&chip->lock);
> +
> + ? ? ? gp2ap002_update_data(chip);
> +
> + ? ? ? input_report_abs(chip->idev, ABS_DISTANCE, chip->vo);
> + ? ? ? input_sync(chip->idev);
> +
> + ? ? ? mutex_unlock(&chip->lock);
> +}
> +
> +static irqreturn_t gp2ap002_irq(int irq, void *data)
> +{
> + ? ? ? struct gp2ap002_chip *chip = data;
> +
> + ? ? ? if (!work_pending(&chip->work)) {
> + ? ? ? ? ? ? ? disable_irq_nosync(irq);
> + ? ? ? ? ? ? ? schedule_work(&chip->work);
> + ? ? ? } else {
> + ? ? ? ? ? ? ? dev_err(&chip->client->dev, "work pending\n");
> + ? ? ? }
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +#define GP2AP002_OUTPUT(name, field) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
> +static ssize_t gp2ap002_show_##name(struct device *dev, ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? ? ? ? ? struct device_attribute *attr, char *buf) ? ? ? ? ? ? ? \
> +{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? struct gp2ap002_chip *chip = dev_get_drvdata(dev); ? ? ? ? ? ? ?\
> + ? ? ? gp2ap002_get_##name(chip); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? return sprintf(buf, "%d\n", chip->field); ? ? ? ? ? ? ? ? ? ? ? \
> +} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO, gp2ap002_show_##name, NULL, 0);
> +
> +#define GP2AP002_INPUT(name, field) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> +static ssize_t gp2ap002_store_##name(struct device *dev, ? ? ? ? ? ? ? \
> + ? ? ? struct device_attribute *attr, const char *buf, size_t count) ? \
> +{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? struct gp2ap002_chip *chip = dev_get_drvdata(dev); ? ? ? ? ? ? ?\
> + ? ? ? if (!count) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
> + ? ? ? ? ? ? ? return -EINVAL; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
> + ? ? ? gp2ap002_set_##name(chip, buf); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
> + ? ? ? return count; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
> +} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> +static ssize_t gp2ap002_show_##name(struct device *dev, ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? ? ? ? ? struct device_attribute *attr, char *buf) ? ? ? ? ? ? ? \
> +{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> + ? ? ? struct gp2ap002_chip *chip = dev_get_drvdata(dev); ? ? ? ? ? ? ?\
> + ? ? ? return sprintf(buf, "%d\n", chip->field); ? ? ? ? ? ? ? ? ? ? ? \
> +} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, ? ? ? ? ? ? ? ? ? ? \
> + ? ? ? ? ? ? ? gp2ap002_show_##name, gp2ap002_store_##name, 0);
> +
> +GP2AP002_OUTPUT(vo, vo);
> +GP2AP002_INPUT(enable, enable);
> +GP2AP002_OUTPUT(adc, adc);
> +GP2AP002_OUTPUT(level, level);
> +
> +static struct attribute *proximity_attributes[] = {
> + ? ? ? &sensor_dev_attr_vo.dev_attr.attr,
> + ? ? ? &sensor_dev_attr_enable.dev_attr.attr,
> + ? ? ? NULL
> +};
> +
> +static struct attribute *ambient_attributes[] = {
> + ? ? ? &sensor_dev_attr_adc.dev_attr.attr,
> + ? ? ? &sensor_dev_attr_level.dev_attr.attr,
> + ? ? ? NULL
> +};
> +
> +static const struct attribute_group proximity_group = {
> + ? ? ? .name = "proximity",
> + ? ? ? .attrs = proximity_attributes,
> +};
> +
> +static const struct attribute_group ambient_group = {
> + ? ? ? .name = "ambient",
> + ? ? ? .attrs = ambient_attributes,
> +};
> +
> +static void gp2ap002_unregister_input_device(struct gp2ap002_chip *chip)
> +{
> + ? ? ? struct i2c_client *client = chip->client;
> +
> + ? ? ? if (client->irq > 0)
> + ? ? ? ? ? ? ? free_irq(client->irq, chip);
> +
> + ? ? ? input_unregister_device(chip->idev);
> + ? ? ? input_free_device(chip->idev);

Please see comment below.

> +}
> +
> +static int gp2ap002_register_input_device(struct gp2ap002_chip *chip)
> +{
> + ? ? ? struct i2c_client *client = chip->client;
> + ? ? ? struct input_dev *idev;
> + ? ? ? int ret;
> +
> + ? ? ? idev = chip->idev = input_allocate_device();
> + ? ? ? if (!idev) {
> + ? ? ? ? ? ? ? dev_err(&client->dev, "allocating input device is failed\n");
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto error_alloc;
> + ? ? ? }
> +
> + ? ? ? idev->name = "GP2AP002 OpticalSensor";

maybe idev->phys also good to add.

> + ? ? ? idev->id.bustype = BUS_I2C;
> + ? ? ? idev->dev.parent = &client->dev;
> + ? ? ? idev->evbit[0] = BIT_MASK(EV_ABS);
> +
> + ? ? ? input_set_abs_params(idev, ABS_DISTANCE, 0, 1, 0, 0);
> +
> + ? ? ? input_set_drvdata(idev, chip);
> +
> + ? ? ? ret = input_register_device(idev);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&client->dev, "registering input device is failed\n");
> + ? ? ? ? ? ? ? goto error_reg;
> + ? ? ? }
> +
> + ? ? ? if (client->irq > 0) {
> + ? ? ? ? ? ? ? unsigned long irq_flag = IRQF_DISABLED;
> +
> + ? ? ? ? ? ? ? if (chip->mode == OPMOD_VCON_IRQ)
> + ? ? ? ? ? ? ? ? ? ? ? irq_flag |= IRQF_TRIGGER_FALLING;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? irq_flag |= IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
> +
> + ? ? ? ? ? ? ? ret = request_irq(client->irq, gp2ap002_irq, irq_flag,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "GP2AP002 OpticalSensor", chip);
> + ? ? ? ? ? ? ? if (ret) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&client->dev, "can't get IRQ %d, ret %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? client->irq, ret);
> + ? ? ? ? ? ? ? ? ? ? ? goto error_irq;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? return 0;
> +
> +error_irq:
> + ? ? ? input_unregister_device(idev);
> +error_reg:
> + ? ? ? input_free_device(idev);

Please put idev=NULL after input_unregister_device call, as idev is
ref-counted and input_free_device is not required after
input_unregister_device.

> +error_alloc:
> + ? ? ? return ret;
> +}
> +
> +
> +static int gp2ap002_probe(struct i2c_client *client,
> + ? ? ? ? ? ? ? ? ? ? ? ? const struct i2c_device_id *id)
> +{


__devinit.

> + ? ? ? struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + ? ? ? struct gp2ap002_chip *chip;
> + ? ? ? struct gp2ap002_platform_data *pdata;
> + ? ? ? int ret;
> +
> + ? ? ? if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
> + ? ? ? ? ? ? ? return -EIO;
> +
> + ? ? ? chip = kzalloc(sizeof(struct gp2ap002_chip), GFP_KERNEL);
> + ? ? ? if (!chip)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? pdata = client->dev.platform_data;
> +
> + ? ? ? chip->client = client;
> + ? ? ? i2c_set_clientdata(client, chip);
> +
> + ? ? ? chip->dev = hwmon_device_register(&client->dev);
> + ? ? ? if (IS_ERR(chip->dev)) {
> + ? ? ? ? ? ? ? dev_err(&client->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Registering to hwmon device is failed\n");
> + ? ? ? ? ? ? ? ret = PTR_ERR(chip->dev);
> + ? ? ? ? ? ? ? goto error_hwmon;
> + ? ? ? }
> +
> + ? ? ? ret = sysfs_create_group(&client->dev.kobj, &proximity_group);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&client->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Creating proximity attribute group failed\n");
> + ? ? ? ? ? ? ? goto error_sysfs1;
> + ? ? ? }
> +
> + ? ? ? ret = sysfs_create_group(&client->dev.kobj, &ambient_group);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&client->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Creating light attribute group failed\n");
> + ? ? ? ? ? ? ? goto error_sysfs2;
> + ? ? ? }
> +
> + ? ? ? ret = gp2ap002_register_input_device(chip);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&client->dev, "Registering input device is failed\n");
> + ? ? ? ? ? ? ? goto error_input;
> + ? ? ? }
> +
> + ? ? ? chip->power_enable ? ? ?= pdata->power_enable;
> + ? ? ? chip->get_adc ? ? ? ? ? = pdata->get_adc;
> + ? ? ? chip->vo_gpio ? ? ? ? ? = pdata->vo_gpio;
> + ? ? ? chip->mode ? ? ? ? ? ? ?= pdata->prox_mode;
> +
> + ? ? ? INIT_WORK(&chip->work, gp2ap002_work);
> + ? ? ? mutex_init(&chip->lock);
> +
> + ? ? ? if (chip->power_enable)
> + ? ? ? ? ? ? ? chip->power_enable(0);
> +
> + ? ? ? gp2ap002_set_mode(chip, pdata->enable);
> +
> + ? ? ? return 0;
> +
> +error_input:
> + ? ? ? sysfs_remove_group(&client->dev.kobj, &ambient_group);
> +error_sysfs2:
> + ? ? ? sysfs_remove_group(&client->dev.kobj, &proximity_group);
> +error_sysfs1:
> + ? ? ? hwmon_device_unregister(chip->dev);
> +error_hwmon:
> + ? ? ? i2c_set_clientdata(client, NULL);
> + ? ? ? kfree(chip);
> + ? ? ? return ret;
> +}
> +
> +static int __exit gp2ap002_remove(struct i2c_client *client)
> +{
> + ? ? ? struct gp2ap002_chip *chip = i2c_get_clientdata(client);
> +
> + ? ? ? gp2ap002_unregister_input_device(chip);
> + ? ? ? hwmon_device_unregister(chip->dev);
> +
> + ? ? ? sysfs_remove_group(&client->dev.kobj, &proximity_group);
> + ? ? ? sysfs_remove_group(&client->dev.kobj, &ambient_group);
> +
> + ? ? ? i2c_set_clientdata(client, NULL);
> +
> + ? ? ? if (chip->power_enable)
> + ? ? ? ? ? ? ? chip->power_enable(0);
> +
> + ? ? ? kfree(chip);
> + ? ? ? return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +
> +static int gp2ap002_suspend(struct i2c_client *client, pm_message_t mesg)
> +{
> + ? ? ? struct gp2ap002_chip *chip = i2c_get_clientdata(client);
> + ? ? ? gp2ap002_set_mode(chip, 0);
> +

cancel_work_sync ?

> + ? ? ? return 0;
> +}
> +
> +static int gp2ap002_resume(struct i2c_client *client)
> +{
> + ? ? ? struct gp2ap002_chip *chip = i2c_get_clientdata(client);
> + ? ? ? gp2ap002_set_mode(chip, 1);
> +
> + ? ? ? return 0;
> +}
> +
> +#else
> +
> +#define gp2ap002_suspend NULL
> +#define gp2ap002_resume NULL
> +
> +#endif /* CONFIG_PM */
> +
> +static const struct i2c_device_id gp2ap002_id[] = {
> + ? ? ? { "GP2AP002", 0 },
> + ? ? ? { }
> +};
> +MODULE_DEVICE_TABLE(i2c, gp2ap002_id);
> +
> +static struct i2c_driver gp2ap002_i2c_driver = {
> + ? ? ? .driver = {
> + ? ? ? ? ? ? ? .name ? = "GP2AP002",
> + ? ? ? },
> + ? ? ? .probe ? ? ? ? ?= gp2ap002_probe,
> + ? ? ? .remove ? ? ? ? = __exit_p(gp2ap002_remove),
> + ? ? ? .suspend ? ? ? ?= gp2ap002_suspend,
> + ? ? ? .resume ? ? ? ? = gp2ap002_resume,
> + ? ? ? .id_table ? ? ? = gp2ap002_id,
> +};
> +
> +static int __init gp2ap002_init(void)
> +{
> + ? ? ? return i2c_add_driver(&gp2ap002_i2c_driver);
> +}
> +module_init(gp2ap002_init);
> +
> +static void __exit gp2ap002_exit(void)
> +{
> + ? ? ? i2c_del_driver(&gp2ap002_i2c_driver);
> +}
> +module_exit(gp2ap002_exit);
> +
> +MODULE_AUTHOR("Minkyu Kang <mk7.kang at samsung.com>");
> +MODULE_DESCRIPTION("GP2AP002 Proximity/Ambient Light Sensor driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/gp2ap002.h b/include/linux/gp2ap002.h
> new file mode 100644
> index 0000000..b6a950b
> --- /dev/null
> +++ b/include/linux/gp2ap002.h
> @@ -0,0 +1,21 @@
> +/*
> + * ?Copyright (C) 2009 Samsung Electronics
> + * ?Minkyu Kang <mk7.kang at samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __GP2AP002_H_
> +#define __GP2AP002_H_
> +
> +struct gp2ap002_platform_data {
> + ? ? ? int (*get_adc)(void);
> + ? ? ? void (*power_enable)(int);
> + ? ? ? int vo_gpio;
> + ? ? ? int prox_mode;
> + ? ? ? int enable;
> +};
> +
> +#endif
> --
> 1.5.4.3
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>



-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni



[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux