/*
* lm3533-als.c -- LM3533 Ambient Light Sensor driver
*
* Copyright (C) 2011-2012 Texas Instruments
*
* Author: Johan Hovold <jhovold@gmail.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/atomic.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mfd/lm3533.h>
#define LM3533_ALS_RESISTOR_MIN 1
#define LM3533_ALS_RESISTOR_MAX 127
#define LM3533_ALS_CHANNEL_CURRENT_MAX 2
#define LM3533_ALS_THRESH_MAX 3
#define LM3533_ALS_ZONE_MAX 4
#define LM3533_REG_ALS_RESISTOR_SELECT 0x30
#define LM3533_REG_ALS_CONF 0x31
#define LM3533_REG_ALS_ZONE_INFO 0x34
#define LM3533_REG_ALS_READ_ADC_RAW 0x37
#define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38
#define LM3533_REG_ALS_BOUNDARY_BASE 0x50
#define LM3533_REG_ALS_TARGET_BASE 0x60
#define LM3533_ALS_ENABLE_MASK 0x01
#define LM3533_ALS_INPUT_MODE_MASK 0x02
#define LM3533_ALS_INT_ENABLE_MASK 0x01
#define LM3533_ALS_ZONE_SHIFT 2
#define LM3533_ALS_ZONE_MASK 0x1c
#define LM3533_ALS_FLAG_INT_ENABLED 1
struct lm3533_als {
struct lm3533 *lm3533;
struct platform_device *pdev;
unsigned long flags;
int irq;
atomic_t zone;
struct mutex thresh_mutex;
};
static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average,
int *adc)
{
struct lm3533_als *als = iio_priv(indio_dev);
u8 reg;
u8 val;
int ret;
if (average)
reg = LM3533_REG_ALS_READ_ADC_AVERAGE;
else
reg = LM3533_REG_ALS_READ_ADC_RAW;
ret = lm3533_read(als->lm3533, reg, &val);
if (ret) {
dev_err(&indio_dev->dev, "failed to read adc\n");
return ret;
}
*adc = val;
return 0;
}
static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
{
struct lm3533_als *als = iio_priv(indio_dev);
u8 val;
int ret;
ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
if (ret) {
dev_err(&indio_dev->dev, "failed to read zone\n");
return ret;
}
val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT;
*zone = min_t(u8, val, LM3533_ALS_ZONE_MAX);
return 0;
}
static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
{
struct lm3533_als *als = iio_priv(indio_dev);
int ret;
if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) {
*zone = atomic_read(&als->zone);
} else {
ret = _lm3533_als_get_zone(indio_dev, zone);
if (ret)
return ret;
}
return 0;
}
/*
* channel output channel 0..2
* zone zone 0..4
*/
static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone)
{
return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone;
}
static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel,
unsigned zone, u8 *val)
{
struct lm3533_als *als = iio_priv(indio_dev);
u8 reg;
int ret;
if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
return -EINVAL;
if (zone > LM3533_ALS_ZONE_MAX)
return -EINVAL;
reg = lm3533_als_get_target_reg(channel, zone);
ret = lm3533_read(als->lm3533, reg, val);
if (ret)
dev_err(&indio_dev->dev, "failed to get target current\n");
return ret;
}
static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel,
unsigned zone, u8 val)
{
struct lm3533_als *als = iio_priv(indio_dev);
u8 reg;
int ret;
if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
return -EINVAL;
if (zone > LM3533_ALS_ZONE_MAX)
return -EINVAL;
reg = lm3533_als_get_target_reg(channel, zone);
ret = lm3533_write(als->lm3533, reg, val);
if (ret)
dev_err(&indio_dev->dev, "failed to set target current\n");
return ret;
}
static int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel,
int *val)
{
u8 zone;
u8 target;
int ret;
ret = lm3533_als_get_zone(indio_dev, &zone);
if (ret)
return ret;
ret = lm3533_als_get_target(indio_dev, channel, zone, &target);
if (ret)
return ret;
*val