Contributors: 11
Author Tokens Token Proportion Commits Commit Proportion
Samu Onkalo 5949 97.46% 1 7.69%
Jingoo Han 102 1.67% 2 15.38%
Himangi Saraogi 23 0.38% 1 7.69%
Dan Carpenter 18 0.29% 1 7.69%
Geliang Tang 4 0.07% 1 7.69%
Thomas Gleixner 2 0.03% 1 7.69%
Uwe Kleine-König 2 0.03% 2 15.38%
Arvind Yadav 1 0.02% 1 7.69%
Wolfram Sang 1 0.02% 1 7.69%
Axel Lin 1 0.02% 1 7.69%
Rafael J. Wysocki 1 0.02% 1 7.69%
Total 6104 13


// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file is part of the ROHM BH1770GLC / OSRAM SFH7770 sensor driver.
 * Chip is combined proximity and ambient light sensor.
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/platform_data/bh1770glc.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/slab.h>

#define BH1770_ALS_CONTROL	0x80 /* ALS operation mode control */
#define BH1770_PS_CONTROL	0x81 /* PS operation mode control */
#define BH1770_I_LED		0x82 /* active LED and LED1, LED2 current */
#define BH1770_I_LED3		0x83 /* LED3 current setting */
#define BH1770_ALS_PS_MEAS	0x84 /* Forced mode trigger */
#define BH1770_PS_MEAS_RATE	0x85 /* PS meas. rate at stand alone mode */
#define BH1770_ALS_MEAS_RATE	0x86 /* ALS meas. rate at stand alone mode */
#define BH1770_PART_ID		0x8a /* Part number and revision ID */
#define BH1770_MANUFACT_ID	0x8b /* Manufacturerer ID */
#define BH1770_ALS_DATA_0	0x8c /* ALS DATA low byte */
#define BH1770_ALS_DATA_1	0x8d /* ALS DATA high byte */
#define BH1770_ALS_PS_STATUS	0x8e /* Measurement data and int status */
#define BH1770_PS_DATA_LED1	0x8f /* PS data from LED1 */
#define BH1770_PS_DATA_LED2	0x90 /* PS data from LED2 */
#define BH1770_PS_DATA_LED3	0x91 /* PS data from LED3 */
#define BH1770_INTERRUPT	0x92 /* Interrupt setting */
#define BH1770_PS_TH_LED1	0x93 /* PS interrupt threshold for LED1 */
#define BH1770_PS_TH_LED2	0x94 /* PS interrupt threshold for LED2 */
#define BH1770_PS_TH_LED3	0x95 /* PS interrupt threshold for LED3 */
#define BH1770_ALS_TH_UP_0	0x96 /* ALS upper threshold low byte */
#define BH1770_ALS_TH_UP_1	0x97 /* ALS upper threshold high byte */
#define BH1770_ALS_TH_LOW_0	0x98 /* ALS lower threshold low byte */
#define BH1770_ALS_TH_LOW_1	0x99 /* ALS lower threshold high byte */

/* MANUFACT_ID */
#define BH1770_MANUFACT_ROHM	0x01
#define BH1770_MANUFACT_OSRAM	0x03

/* PART_ID */
#define BH1770_PART		0x90
#define BH1770_PART_MASK	0xf0
#define BH1770_REV_MASK		0x0f
#define BH1770_REV_SHIFT	0
#define BH1770_REV_0		0x00
#define BH1770_REV_1		0x01

/* Operating modes for both */
#define BH1770_STANDBY		0x00
#define BH1770_FORCED		0x02
#define BH1770_STANDALONE	0x03
#define BH1770_SWRESET		(0x01 << 2)

#define BH1770_PS_TRIG_MEAS	(1 << 0)
#define BH1770_ALS_TRIG_MEAS	(1 << 1)

/* Interrupt control */
#define BH1770_INT_OUTPUT_MODE	(1 << 3) /* 0 = latched */
#define BH1770_INT_POLARITY	(1 << 2) /* 1 = active high */
#define BH1770_INT_ALS_ENA	(1 << 1)
#define BH1770_INT_PS_ENA	(1 << 0)

/* Interrupt status */
#define BH1770_INT_LED1_DATA	(1 << 0)
#define BH1770_INT_LED1_INT	(1 << 1)
#define BH1770_INT_LED2_DATA	(1 << 2)
#define BH1770_INT_LED2_INT	(1 << 3)
#define BH1770_INT_LED3_DATA	(1 << 4)
#define BH1770_INT_LED3_INT	(1 << 5)
#define BH1770_INT_LEDS_INT	((1 << 1) | (1 << 3) | (1 << 5))
#define BH1770_INT_ALS_DATA	(1 << 6)
#define BH1770_INT_ALS_INT	(1 << 7)

/* Led channels */
#define BH1770_LED1		0x00

#define BH1770_DISABLE		0
#define BH1770_ENABLE		1
#define BH1770_PROX_CHANNELS	1

#define BH1770_LUX_DEFAULT_RATE	1 /* Index to lux rate table */
#define BH1770_PROX_DEFAULT_RATE 1 /* Direct HW value =~ 50Hz */
#define BH1770_PROX_DEF_RATE_THRESH 6 /* Direct HW value =~ 5 Hz */
#define BH1770_STARTUP_DELAY	50
#define BH1770_RESET_TIME	10
#define BH1770_TIMEOUT		2100 /* Timeout in 2.1 seconds */

#define BH1770_LUX_RANGE	65535
#define BH1770_PROX_RANGE	255
#define BH1770_COEF_SCALER	1024
#define BH1770_CALIB_SCALER	8192
#define BH1770_LUX_NEUTRAL_CALIB_VALUE (1 * BH1770_CALIB_SCALER)
#define BH1770_LUX_DEF_THRES	1000
#define BH1770_PROX_DEF_THRES	70
#define BH1770_PROX_DEF_ABS_THRES   100
#define BH1770_DEFAULT_PERSISTENCE  10
#define BH1770_PROX_MAX_PERSISTENCE 50
#define BH1770_LUX_GA_SCALE	16384
#define BH1770_LUX_CF_SCALE	2048 /* CF ChipFactor */
#define BH1770_NEUTRAL_CF	BH1770_LUX_CF_SCALE
#define BH1770_LUX_CORR_SCALE	4096

#define PROX_ABOVE_THRESHOLD	1
#define PROX_BELOW_THRESHOLD	0

#define PROX_IGNORE_LUX_LIMIT	500

struct bh1770_chip {
	struct bh1770_platform_data	*pdata;
	char				chipname[10];
	u8				revision;
	struct i2c_client		*client;
	struct regulator_bulk_data	regs[2];
	struct mutex			mutex; /* avoid parallel access */
	wait_queue_head_t		wait;

	bool			int_mode_prox;
	bool			int_mode_lux;
	struct delayed_work	prox_work;
	u32	lux_cf; /* Chip specific factor */
	u32	lux_ga;
	u32	lux_calib;
	int	lux_rate_index;
	u32	lux_corr;
	u16	lux_data_raw;
	u16	lux_threshold_hi;
	u16	lux_threshold_lo;
	u16	lux_thres_hi_onchip;
	u16	lux_thres_lo_onchip;
	bool	lux_wait_result;

	int	prox_enable_count;
	u16	prox_coef;
	u16	prox_const;
	int	prox_rate;
	int	prox_rate_threshold;
	u8	prox_persistence;
	u8	prox_persistence_counter;
	u8	prox_data;
	u8	prox_threshold;
	u8	prox_threshold_hw;
	bool	prox_force_update;
	u8	prox_abs_thres;
	u8	prox_led;
};

static const char reg_vcc[] = "Vcc";
static const char reg_vleds[] = "Vleds";

/*
 * Supported stand alone rates in ms from chip data sheet
 * {10, 20, 30, 40, 70, 100, 200, 500, 1000, 2000};
 */
static const s16 prox_rates_hz[] = {100, 50, 33, 25, 14, 10, 5, 2};
static const s16 prox_rates_ms[] = {10, 20, 30, 40, 70, 100, 200, 500};

/*
 * Supported stand alone rates in ms from chip data sheet
 * {100, 200, 500, 1000, 2000};
 */
static const s16 lux_rates_hz[] = {10, 5, 2, 1, 0};

/*
 * interrupt control functions are called while keeping chip->mutex
 * excluding module probe / remove
 */
static inline int bh1770_lux_interrupt_control(struct bh1770_chip *chip,
					int lux)
{
	chip->int_mode_lux = lux;
	/* Set interrupt modes, interrupt active low, latched */
	return i2c_smbus_write_byte_data(chip->client,
					BH1770_INTERRUPT,
					(lux << 1) | chip->int_mode_prox);
}

static inline int bh1770_prox_interrupt_control(struct bh1770_chip *chip,
					int ps)
{
	chip->int_mode_prox = ps;
	return i2c_smbus_write_byte_data(chip->client,
					BH1770_INTERRUPT,
					(chip->int_mode_lux << 1) | (ps << 0));
}

/* chip->mutex is always kept here */
static int bh1770_lux_rate(struct bh1770_chip *chip, int rate_index)
{
	/* sysfs may call this when the chip is powered off */
	if (pm_runtime_suspended(&chip->client->dev))
		return 0;

	/* Proper proximity response needs fastest lux rate (100ms) */
	if (chip->prox_enable_count)
		rate_index = 0;

	return i2c_smbus_write_byte_data(chip->client,
					BH1770_ALS_MEAS_RATE,
					rate_index);
}

static int bh1770_prox_rate(struct bh1770_chip *chip, int mode)
{
	int rate;

	rate = (mode == PROX_ABOVE_THRESHOLD) ?
		chip->prox_rate_threshold : chip->prox_rate;

	return i2c_smbus_write_byte_data(chip->client,
					BH1770_PS_MEAS_RATE,
					rate);
}

/* InfraredLED is controlled by the chip during proximity scanning */
static inline int bh1770_led_cfg(struct bh1770_chip *chip)
{
	/* LED cfg, current for leds 1 and 2 */
	return i2c_smbus_write_byte_data(chip->client,
					BH1770_I_LED,
					(BH1770_LED1 << 6) |
					(BH1770_LED_5mA << 3) |
					chip->prox_led);
}

/*
 * Following two functions converts raw ps values from HW to normalized
 * values. Purpose is to compensate differences between different sensor
 * versions and variants so that result means about the same between
 * versions.
 */
static inline u8 bh1770_psraw_to_adjusted(struct bh1770_chip *chip, u8 psraw)
{
	u16 adjusted;
	adjusted = (u16)(((u32)(psraw + chip->prox_const) * chip->prox_coef) /
		BH1770_COEF_SCALER);
	if (adjusted > BH1770_PROX_RANGE)
		adjusted = BH1770_PROX_RANGE;
	return adjusted;
}

static inline u8 bh1770_psadjusted_to_raw(struct bh1770_chip *chip, u8 ps)
{
	u16 raw;

	raw = (((u32)ps * BH1770_COEF_SCALER) / chip->prox_coef);
	if (raw > chip->prox_const)
		raw = raw - chip->prox_const;
	else
		raw = 0;
	return raw;
}

/*
 * Following two functions converts raw lux values from HW to normalized
 * values. Purpose is to compensate differences between different sensor
 * versions and variants so that result means about the same between
 * versions. Chip->mutex is kept when this is called.
 */
static int bh1770_prox_set_threshold(struct bh1770_chip *chip)
{
	u8 tmp = 0;

	/* sysfs may call this when the chip is powered off */
	if (pm_runtime_suspended(&chip->client->dev))
		return 0;

	tmp = bh1770_psadjusted_to_raw(chip, chip->prox_threshold);
	chip->prox_threshold_hw = tmp;

	return	i2c_smbus_write_byte_data(chip->client, BH1770_PS_TH_LED1,
					tmp);
}

static inline u16 bh1770_lux_raw_to_adjusted(struct bh1770_chip *chip, u16 raw)
{
	u32 lux;
	lux = ((u32)raw * chip->lux_corr) / BH1770_LUX_CORR_SCALE;
	return min(lux, (u32)BH1770_LUX_RANGE);
}

static inline u16 bh1770_lux_adjusted_to_raw(struct bh1770_chip *chip,
					u16 adjusted)
{
	return (u32)adjusted * BH1770_LUX_CORR_SCALE / chip->lux_corr;
}

/* chip->mutex is kept when this is called */
static int bh1770_lux_update_thresholds(struct bh1770_chip *chip,
					u16 threshold_hi, u16 threshold_lo)
{
	u8 data[4];
	int ret;

	/* sysfs may call this when the chip is powered off */
	if (pm_runtime_suspended(&chip->client->dev))
		return 0;

	/*
	 * Compensate threshold values with the correction factors if not
	 * set to minimum or maximum.
	 * Min & max values disables interrupts.
	 */
	if (threshold_hi != BH1770_LUX_RANGE && threshold_hi != 0)
		threshold_hi = bh1770_lux_adjusted_to_raw(chip, threshold_hi);

	if (threshold_lo != BH1770_LUX_RANGE && threshold_lo != 0)
		threshold_lo = bh1770_lux_adjusted_to_raw(chip, threshold_lo);

	if (chip->lux_thres_hi_onchip == threshold_hi &&
	    chip->lux_thres_lo_onchip == threshold_lo)
		return 0;

	chip->lux_thres_hi_onchip = threshold_hi;
	chip->lux_thres_lo_onchip = threshold_lo;

	data[0] = threshold_hi;
	data[1] = threshold_hi >> 8;
	data[2] = threshold_lo;
	data[3] = threshold_lo >> 8;

	ret = i2c_smbus_write_i2c_block_data(chip->client,
					BH1770_ALS_TH_UP_0,
					ARRAY_SIZE(data),
					data);
	return ret;
}

static int bh1770_lux_get_result(struct bh1770_chip *chip)
{
	u16 data;
	int ret;

	ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_0);
	if (ret < 0)
		return ret;

	data = ret & 0xff;
	ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_1);
	if (ret < 0)
		return ret;

	chip->lux_data_raw = data | ((ret & 0xff) << 8);

	return 0;
}

/* Calculate correction value which contains chip and device specific parts */
static u32 bh1770_get_corr_value(struct bh1770_chip *chip)
{
	u32 tmp;
	/* Impact of glass attenuation correction */
	tmp = (BH1770_LUX_CORR_SCALE * chip->lux_ga) / BH1770_LUX_GA_SCALE;
	/* Impact of chip factor correction */
	tmp = (tmp * chip->lux_cf) / BH1770_LUX_CF_SCALE;
	/* Impact of Device specific calibration correction */
	tmp = (tmp * chip->lux_calib) / BH1770_CALIB_SCALER;
	return tmp;
}

static int bh1770_lux_read_result(struct bh1770_chip *chip)
{
	bh1770_lux_get_result(chip);
	return bh1770_lux_raw_to_adjusted(chip, chip->lux_data_raw);
}

/*
 * Chip on / off functions are called while keeping mutex except probe
 * or remove phase
 */
static int bh1770_chip_on(struct bh1770_chip *chip)
{
	int ret = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
					chip->regs);
	if (ret < 0)
		return ret;

	usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2);

	/* Reset the chip */
	i2c_smbus_write_byte_data(chip->client, BH1770_ALS_CONTROL,
				BH1770_SWRESET);
	usleep_range(BH1770_RESET_TIME, BH1770_RESET_TIME * 2);

	/*
	 * ALS is started always since proximity needs als results
	 * for realibility estimation.
	 * Let's assume dark until the first ALS measurement is ready.
	 */
	chip->lux_data_raw = 0;
	chip->prox_data = 0;
	ret = i2c_smbus_write_byte_data(chip->client,
					BH1770_ALS_CONTROL, BH1770_STANDALONE);

	/* Assume reset defaults */
	chip->lux_thres_hi_onchip = BH1770_LUX_RANGE;
	chip->lux_thres_lo_onchip = 0;

	return ret;
}

static void bh1770_chip_off(struct bh1770_chip *chip)
{
	i2c_smbus_write_byte_data(chip->client,
					BH1770_INTERRUPT, BH1770_DISABLE);
	i2c_smbus_write_byte_data(chip->client,
				BH1770_ALS_CONTROL, BH1770_STANDBY);
	i2c_smbus_write_byte_data(chip->client,
				BH1770_PS_CONTROL, BH1770_STANDBY);
	regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
}

/* chip->mutex is kept when this is called */
static int bh1770_prox_mode_control(struct bh1770_chip *chip)
{
	if (chip->prox_enable_count) {
		chip->prox_force_update = true; /* Force immediate update */

		bh1770_lux_rate(chip, chip->lux_rate_index);
		bh1770_prox_set_threshold(chip);
		bh1770_led_cfg(chip);
		bh1770_prox_rate(chip, PROX_BELOW_THRESHOLD);
		bh1770_prox_interrupt_control(chip, BH1770_ENABLE);
		i2c_smbus_write_byte_data(chip->client,
					BH1770_PS_CONTROL, BH1770_STANDALONE);
	} else {
		chip->prox_data = 0;
		bh1770_lux_rate(chip, chip->lux_rate_index);
		bh1770_prox_interrupt_control(chip, BH1770_DISABLE);
		i2c_smbus_write_byte_data(chip->client,
					BH1770_PS_CONTROL, BH1770_STANDBY);
	}
	return 0;
}

/* chip->mutex is kept when this is called */
static int bh1770_prox_read_result(struct bh1770_chip *chip)
{
	int ret;
	bool above;
	u8 mode;

	ret = i2c_smbus_read_byte_data(chip->client, BH1770_PS_DATA_LED1);
	if (ret < 0)
		goto out;

	if (ret > chip->prox_threshold_hw)
		above = true;
	else
		above = false;

	/*
	 * when ALS levels goes above limit, proximity result may be
	 * false proximity. Thus ignore the result. With real proximity
	 * there is a shadow causing low als levels.
	 */
	if (chip->lux_data_raw > PROX_IGNORE_LUX_LIMIT)
		ret = 0;

	chip->prox_data = bh1770_psraw_to_adjusted(chip, ret);

	/* Strong proximity level or force mode requires immediate response */
	if (chip->prox_data >= chip->prox_abs_thres ||
	    chip->prox_force_update)
		chip->prox_persistence_counter = chip->prox_persistence;

	chip->prox_force_update = false;

	/* Persistence filttering to reduce false proximity events */
	if (likely(above)) {
		if (chip->prox_persistence_counter < chip->prox_persistence) {
			chip->prox_persistence_counter++;
			ret = -ENODATA;
		} else {
			mode = PROX_ABOVE_THRESHOLD;
			ret = 0;
		}
	} else {
		chip->prox_persistence_counter = 0;
		mode = PROX_BELOW_THRESHOLD;
		chip->prox_data = 0;
		ret = 0;
	}

	/* Set proximity detection rate based on above or below value */
	if (ret == 0) {
		bh1770_prox_rate(chip, mode);
		sysfs_notify(&chip->client->dev.kobj, NULL, "prox0_raw");
	}
out:
	return ret;
}

static int bh1770_detect(struct bh1770_chip *chip)
{
	struct i2c_client *client = chip->client;
	s32 ret;
	u8 manu, part;

	ret = i2c_smbus_read_byte_data(client, BH1770_MANUFACT_ID);
	if (ret < 0)
		goto error;
	manu = (u8)ret;

	ret = i2c_smbus_read_byte_data(client, BH1770_PART_ID);
	if (ret < 0)
		goto error;
	part = (u8)ret;

	chip->revision = (part & BH1770_REV_MASK) >> BH1770_REV_SHIFT;
	chip->prox_coef = BH1770_COEF_SCALER;
	chip->prox_const = 0;
	chip->lux_cf = BH1770_NEUTRAL_CF;

	if ((manu == BH1770_MANUFACT_ROHM) &&
	    ((part & BH1770_PART_MASK) == BH1770_PART)) {
		snprintf(chip->chipname, sizeof(chip->chipname), "BH1770GLC");
		return 0;
	}

	if ((manu == BH1770_MANUFACT_OSRAM) &&
	    ((part & BH1770_PART_MASK) == BH1770_PART)) {
		snprintf(chip->chipname, sizeof(chip->chipname), "SFH7770");
		/* Values selected by comparing different versions */
		chip->prox_coef = 819; /* 0.8 * BH1770_COEF_SCALER */
		chip->prox_const = 40;
		return 0;
	}

	ret = -ENODEV;
error:
	dev_dbg(&client->dev, "BH1770 or SFH7770 not found\n");

	return ret;
}

/*
 * This work is re-scheduled at every proximity interrupt.
 * If this work is running, it means that there hasn't been any
 * proximity interrupt in time. Situation is handled as no-proximity.
 * It would be nice to have low-threshold interrupt or interrupt
 * when measurement and hi-threshold are both 0. But neither of those exists.
 * This is a workaroud for missing HW feature.
 */

static void bh1770_prox_work(struct work_struct *work)
{
	struct bh1770_chip *chip =
		container_of(work, struct bh1770_chip, prox_work.work);

	mutex_lock(&chip->mutex);
	bh1770_prox_read_result(chip);
	mutex_unlock(&chip->mutex);
}

/* This is threaded irq handler */
static irqreturn_t bh1770_irq(int irq, void *data)
{
	struct bh1770_chip *chip = data;
	int status;
	int rate = 0;

	mutex_lock(&chip->mutex);
	status = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_PS_STATUS);

	/* Acknowledge interrupt by reading this register */
	i2c_smbus_read_byte_data(chip->client, BH1770_INTERRUPT);

	/*
	 * Check if there is fresh data available for als.
	 * If this is the very first data, update thresholds after that.
	 */
	if (status & BH1770_INT_ALS_DATA) {
		bh1770_lux_get_result(chip);
		if (unlikely(chip->lux_wait_result)) {
			chip->lux_wait_result = false;
			wake_up(&chip->wait);
			bh1770_lux_update_thresholds(chip,
						chip->lux_threshold_hi,
						chip->lux_threshold_lo);
		}
	}

	/* Disable interrupt logic to guarantee acknowledgement */
	i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT,
				  (0 << 1) | (0 << 0));

	if ((status & BH1770_INT_ALS_INT))
		sysfs_notify(&chip->client->dev.kobj, NULL, "lux0_input");

	if (chip->int_mode_prox && (status & BH1770_INT_LEDS_INT)) {
		rate = prox_rates_ms[chip->prox_rate_threshold];
		bh1770_prox_read_result(chip);
	}

	/* Re-enable interrupt logic */
	i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT,
				  (chip->int_mode_lux << 1) |
				  (chip->int_mode_prox << 0));
	mutex_unlock(&chip->mutex);

	/*
	 * Can't cancel work while keeping mutex since the work uses the
	 * same mutex.
	 */
	if (rate) {
		/*
		 * Simulate missing no-proximity interrupt 50ms after the
		 * next expected interrupt time.
		 */
		cancel_delayed_work_sync(&chip->prox_work);
		schedule_delayed_work(&chip->prox_work,
				msecs_to_jiffies(rate + 50));
	}
	return IRQ_HANDLED;
}

static ssize_t bh1770_power_state_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long value;
	ssize_t ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	mutex_lock(&chip->mutex);
	if (value) {
		pm_runtime_get_sync(dev);

		ret = bh1770_lux_rate(chip, chip->lux_rate_index);
		if (ret < 0) {
			pm_runtime_put(dev);
			goto leave;
		}

		ret = bh1770_lux_interrupt_control(chip, BH1770_ENABLE);
		if (ret < 0) {
			pm_runtime_put(dev);
			goto leave;
		}

		/* This causes interrupt after the next measurement cycle */
		bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES,
					BH1770_LUX_DEF_THRES);
		/* Inform that we are waiting for a result from ALS */
		chip->lux_wait_result = true;
		bh1770_prox_mode_control(chip);
	} else if (!pm_runtime_suspended(dev)) {
		pm_runtime_put(dev);
	}
	ret = count;
leave:
	mutex_unlock(&chip->mutex);
	return ret;
}

static ssize_t bh1770_power_state_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", !pm_runtime_suspended(dev));
}

static ssize_t bh1770_lux_result_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	ssize_t ret;
	long timeout;

	if (pm_runtime_suspended(dev))
		return -EIO; /* Chip is not enabled at all */

	timeout = wait_event_interruptible_timeout(chip->wait,
					!chip->lux_wait_result,
					msecs_to_jiffies(BH1770_TIMEOUT));
	if (!timeout)
		return -EIO;

	mutex_lock(&chip->mutex);
	ret = sprintf(buf, "%d\n", bh1770_lux_read_result(chip));
	mutex_unlock(&chip->mutex);

	return ret;
}

static ssize_t bh1770_lux_range_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", BH1770_LUX_RANGE);
}

static ssize_t bh1770_prox_enable_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	mutex_lock(&chip->mutex);
	/* Assume no proximity. Sensor will tell real state soon */
	if (!chip->prox_enable_count)
		chip->prox_data = 0;

	if (value)
		chip->prox_enable_count++;
	else if (chip->prox_enable_count > 0)
		chip->prox_enable_count--;
	else
		goto leave;

	/* Run control only when chip is powered on */
	if (!pm_runtime_suspended(dev))
		bh1770_prox_mode_control(chip);
leave:
	mutex_unlock(&chip->mutex);
	return count;
}

static ssize_t bh1770_prox_enable_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	ssize_t len;

	mutex_lock(&chip->mutex);
	len = sprintf(buf, "%d\n", chip->prox_enable_count);
	mutex_unlock(&chip->mutex);
	return len;
}

static ssize_t bh1770_prox_result_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	ssize_t ret;

	mutex_lock(&chip->mutex);
	if (chip->prox_enable_count && !pm_runtime_suspended(dev))
		ret = sprintf(buf, "%d\n", chip->prox_data);
	else
		ret = -EIO;
	mutex_unlock(&chip->mutex);
	return ret;
}

static ssize_t bh1770_prox_range_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", BH1770_PROX_RANGE);
}

static ssize_t bh1770_get_prox_rate_avail(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	int i;
	int pos = 0;
	for (i = 0; i < ARRAY_SIZE(prox_rates_hz); i++)
		pos += sprintf(buf + pos, "%d ", prox_rates_hz[i]);
	sprintf(buf + pos - 1, "\n");
	return pos;
}

static ssize_t bh1770_get_prox_rate_above(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate_threshold]);
}

static ssize_t bh1770_get_prox_rate_below(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate]);
}

static int bh1770_prox_rate_validate(int rate)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(prox_rates_hz) - 1; i++)
		if (rate >= prox_rates_hz[i])
			break;
	return i;
}

static ssize_t bh1770_set_prox_rate_above(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	mutex_lock(&chip->mutex);
	chip->prox_rate_threshold = bh1770_prox_rate_validate(value);
	mutex_unlock(&chip->mutex);
	return count;
}

static ssize_t bh1770_set_prox_rate_below(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	mutex_lock(&chip->mutex);
	chip->prox_rate = bh1770_prox_rate_validate(value);
	mutex_unlock(&chip->mutex);
	return count;
}

static ssize_t bh1770_get_prox_thres(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", chip->prox_threshold);
}

static ssize_t bh1770_set_prox_thres(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	if (value > BH1770_PROX_RANGE)
		return -EINVAL;

	mutex_lock(&chip->mutex);
	chip->prox_threshold = value;
	ret = bh1770_prox_set_threshold(chip);
	mutex_unlock(&chip->mutex);
	if (ret < 0)
		return ret;
	return count;
}

static ssize_t bh1770_prox_persistence_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);

	return sprintf(buf, "%u\n", chip->prox_persistence);
}

static ssize_t bh1770_prox_persistence_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t len)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	if (value > BH1770_PROX_MAX_PERSISTENCE)
		return -EINVAL;

	chip->prox_persistence = value;

	return len;
}

static ssize_t bh1770_prox_abs_thres_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);
	return sprintf(buf, "%u\n", chip->prox_abs_thres);
}

static ssize_t bh1770_prox_abs_thres_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t len)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);
	unsigned long value;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	if (value > BH1770_PROX_RANGE)
		return -EINVAL;

	chip->prox_abs_thres = value;

	return len;
}

static ssize_t bh1770_chip_id_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%s rev %d\n", chip->chipname, chip->revision);
}

static ssize_t bh1770_lux_calib_default_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%u\n", BH1770_CALIB_SCALER);
}

static ssize_t bh1770_lux_calib_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);
	ssize_t len;

	mutex_lock(&chip->mutex);
	len = sprintf(buf, "%u\n", chip->lux_calib);
	mutex_unlock(&chip->mutex);
	return len;
}

static ssize_t bh1770_lux_calib_store(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
{
	struct bh1770_chip *chip = dev_get_drvdata(dev);
	unsigned long value;
	u32 old_calib;
	u32 new_corr;
	int ret;

	ret = kstrtoul(buf, 0, &value);
	if (ret)
		return ret;

	mutex_lock(&chip->mutex);
	old_calib = chip->lux_calib;
	chip->lux_calib = value;
	new_corr = bh1770_get_corr_value(chip);
	if (new_corr == 0) {
		chip->lux_calib = old_calib;
		mutex_unlock(&chip->mutex);
		return -EINVAL;
	}
	chip->lux_corr = new_corr;
	/* Refresh thresholds on HW after changing correction value */
	bh1770_lux_update_thresholds(chip, chip->lux_threshold_hi,
				chip->lux_threshold_lo);

	mutex_unlock(&chip->mutex);

	return len;
}

static ssize_t bh1770_get_lux_rate_avail(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	int i;
	int pos = 0;
	for (i = 0; i < ARRAY_SIZE(lux_rates_hz); i++)
		pos += sprintf(buf + pos, "%d ", lux_rates_hz[i]);
	sprintf(buf + pos - 1, "\n");
	return pos;
}

static ssize_t bh1770_get_lux_rate(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", lux_rates_hz[chip->lux_rate_index]);
}

static ssize_t bh1770_set_lux_rate(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	unsigned long rate_hz;
	int ret, i;

	ret = kstrtoul(buf, 0, &rate_hz);
	if (ret)
		return ret;

	for (i = 0; i < ARRAY_SIZE(lux_rates_hz) - 1; i++)
		if (rate_hz >= lux_rates_hz[i])
			break;

	mutex_lock(&chip->mutex);
	chip->lux_rate_index = i;
	ret = bh1770_lux_rate(chip, i);
	mutex_unlock(&chip->mutex);

	if (ret < 0)
		return ret;

	return count;
}

static ssize_t bh1770_get_lux_thresh_above(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", chip->lux_threshold_hi);
}

static ssize_t bh1770_get_lux_thresh_below(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", chip->lux_threshold_lo);
}

static ssize_t bh1770_set_lux_thresh(struct bh1770_chip *chip, u16 *target,
				const char *buf)
{
	unsigned long thresh;
	int ret;

	ret = kstrtoul(buf, 0, &thresh);
	if (ret)
		return ret;

	if (thresh > BH1770_LUX_RANGE)
		return -EINVAL;

	mutex_lock(&chip->mutex);
	*target = thresh;
	/*
	 * Don't update values in HW if we are still waiting for
	 * first interrupt to come after device handle open call.
	 */
	if (!chip->lux_wait_result)
		ret = bh1770_lux_update_thresholds(chip,
						chip->lux_threshold_hi,
						chip->lux_threshold_lo);
	mutex_unlock(&chip->mutex);
	return ret;

}

static ssize_t bh1770_set_lux_thresh_above(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_hi, buf);
	if (ret < 0)
		return ret;
	return len;
}

static ssize_t bh1770_set_lux_thresh_below(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
{
	struct bh1770_chip *chip =  dev_get_drvdata(dev);
	int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_lo, buf);
	if (ret < 0)
		return ret;
	return len;
}

static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, bh1770_prox_enable_show,
						bh1770_prox_enable_store);
static DEVICE_ATTR(prox0_thresh_above1_value, S_IRUGO | S_IWUSR,
						bh1770_prox_abs_thres_show,
						bh1770_prox_abs_thres_store);
static DEVICE_ATTR(prox0_thresh_above0_value, S_IRUGO | S_IWUSR,
						bh1770_get_prox_thres,
						bh1770_set_prox_thres);
static DEVICE_ATTR(prox0_raw, S_IRUGO, bh1770_prox_result_show, NULL);
static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, bh1770_prox_range_show, NULL);
static DEVICE_ATTR(prox0_thresh_above_count, S_IRUGO | S_IWUSR,
						bh1770_prox_persistence_show,
						bh1770_prox_persistence_store);
static DEVICE_ATTR(prox0_rate_above, S_IRUGO | S_IWUSR,
						bh1770_get_prox_rate_above,
						bh1770_set_prox_rate_above);
static DEVICE_ATTR(prox0_rate_below, S_IRUGO | S_IWUSR,
						bh1770_get_prox_rate_below,
						bh1770_set_prox_rate_below);
static DEVICE_ATTR(prox0_rate_avail, S_IRUGO, bh1770_get_prox_rate_avail, NULL);

static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, bh1770_lux_calib_show,
						bh1770_lux_calib_store);
static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO,
						bh1770_lux_calib_default_show,
						NULL);
static DEVICE_ATTR(lux0_input, S_IRUGO, bh1770_lux_result_show, NULL);
static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, bh1770_lux_range_show, NULL);
static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, bh1770_get_lux_rate,
						bh1770_set_lux_rate);
static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, bh1770_get_lux_rate_avail, NULL);
static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR,
						bh1770_get_lux_thresh_above,
						bh1770_set_lux_thresh_above);
static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR,
						bh1770_get_lux_thresh_below,
						bh1770_set_lux_thresh_below);
static DEVICE_ATTR(chip_id, S_IRUGO, bh1770_chip_id_show, NULL);
static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, bh1770_power_state_show,
						 bh1770_power_state_store);


static struct attribute *sysfs_attrs[] = {
	&dev_attr_lux0_calibscale.attr,
	&dev_attr_lux0_calibscale_default.attr,
	&dev_attr_lux0_input.attr,
	&dev_attr_lux0_sensor_range.attr,
	&dev_attr_lux0_rate.attr,
	&dev_attr_lux0_rate_avail.attr,
	&dev_attr_lux0_thresh_above_value.attr,
	&dev_attr_lux0_thresh_below_value.attr,
	&dev_attr_prox0_raw.attr,
	&dev_attr_prox0_sensor_range.attr,
	&dev_attr_prox0_raw_en.attr,
	&dev_attr_prox0_thresh_above_count.attr,
	&dev_attr_prox0_rate_above.attr,
	&dev_attr_prox0_rate_below.attr,
	&dev_attr_prox0_rate_avail.attr,
	&dev_attr_prox0_thresh_above0_value.attr,
	&dev_attr_prox0_thresh_above1_value.attr,
	&dev_attr_chip_id.attr,
	&dev_attr_power_state.attr,
	NULL
};

static const struct attribute_group bh1770_attribute_group = {
	.attrs = sysfs_attrs
};

static int bh1770_probe(struct i2c_client *client)
{
	struct bh1770_chip *chip;
	int err;

	chip = devm_kzalloc(&client->dev, sizeof *chip, GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	i2c_set_clientdata(client, chip);
	chip->client  = client;

	mutex_init(&chip->mutex);
	init_waitqueue_head(&chip->wait);
	INIT_DELAYED_WORK(&chip->prox_work, bh1770_prox_work);

	if (client->dev.platform_data == NULL) {
		dev_err(&client->dev, "platform data is mandatory\n");
		return -EINVAL;
	}

	chip->pdata		= client->dev.platform_data;
	chip->lux_calib		= BH1770_LUX_NEUTRAL_CALIB_VALUE;
	chip->lux_rate_index	= BH1770_LUX_DEFAULT_RATE;
	chip->lux_threshold_lo	= BH1770_LUX_DEF_THRES;
	chip->lux_threshold_hi	= BH1770_LUX_DEF_THRES;

	if (chip->pdata->glass_attenuation == 0)
		chip->lux_ga = BH1770_NEUTRAL_GA;
	else
		chip->lux_ga = chip->pdata->glass_attenuation;

	chip->prox_threshold	= BH1770_PROX_DEF_THRES;
	chip->prox_led		= chip->pdata->led_def_curr;
	chip->prox_abs_thres	= BH1770_PROX_DEF_ABS_THRES;
	chip->prox_persistence	= BH1770_DEFAULT_PERSISTENCE;
	chip->prox_rate_threshold = BH1770_PROX_DEF_RATE_THRESH;
	chip->prox_rate		= BH1770_PROX_DEFAULT_RATE;
	chip->prox_data		= 0;

	chip->regs[0].supply = reg_vcc;
	chip->regs[1].supply = reg_vleds;

	err = devm_regulator_bulk_get(&client->dev,
				      ARRAY_SIZE(chip->regs), chip->regs);
	if (err < 0) {
		dev_err(&client->dev, "Cannot get regulators\n");
		return err;
	}

	err = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
				chip->regs);
	if (err < 0) {
		dev_err(&client->dev, "Cannot enable regulators\n");
		return err;
	}

	usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2);
	err = bh1770_detect(chip);
	if (err < 0)
		goto fail0;

	/* Start chip */
	bh1770_chip_on(chip);
	pm_runtime_set_active(&client->dev);
	pm_runtime_enable(&client->dev);

	chip->lux_corr = bh1770_get_corr_value(chip);
	if (chip->lux_corr == 0) {
		dev_err(&client->dev, "Improper correction values\n");
		err = -EINVAL;
		goto fail0;
	}

	if (chip->pdata->setup_resources) {
		err = chip->pdata->setup_resources();
		if (err) {
			err = -EINVAL;
			goto fail0;
		}
	}

	err = sysfs_create_group(&chip->client->dev.kobj,
				&bh1770_attribute_group);
	if (err < 0) {
		dev_err(&chip->client->dev, "Sysfs registration failed\n");
		goto fail1;
	}

	/*
	 * Chip needs level triggered interrupt to work. However,
	 * level triggering doesn't work always correctly with power
	 * management. Select both
	 */
	err = request_threaded_irq(client->irq, NULL,
				bh1770_irq,
				IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
				IRQF_TRIGGER_LOW,
				"bh1770", chip);
	if (err) {
		dev_err(&client->dev, "could not get IRQ %d\n",
			client->irq);
		goto fail2;
	}
	regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
	return err;
fail2:
	sysfs_remove_group(&chip->client->dev.kobj,
			&bh1770_attribute_group);
fail1:
	if (chip->pdata->release_resources)
		chip->pdata->release_resources();
fail0:
	regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
	return err;
}

static void bh1770_remove(struct i2c_client *client)
{
	struct bh1770_chip *chip = i2c_get_clientdata(client);

	free_irq(client->irq, chip);

	sysfs_remove_group(&chip->client->dev.kobj,
			&bh1770_attribute_group);

	if (chip->pdata->release_resources)
		chip->pdata->release_resources();

	cancel_delayed_work_sync(&chip->prox_work);

	if (!pm_runtime_suspended(&client->dev))
		bh1770_chip_off(chip);

	pm_runtime_disable(&client->dev);
	pm_runtime_set_suspended(&client->dev);
}

#ifdef CONFIG_PM_SLEEP
static int bh1770_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bh1770_chip *chip = i2c_get_clientdata(client);

	bh1770_chip_off(chip);

	return 0;
}

static int bh1770_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bh1770_chip *chip = i2c_get_clientdata(client);
	int ret = 0;

	bh1770_chip_on(chip);

	if (!pm_runtime_suspended(dev)) {
		/*
		 * If we were enabled at suspend time, it is expected
		 * everything works nice and smoothly
		 */
		ret = bh1770_lux_rate(chip, chip->lux_rate_index);
		ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE);

		/* This causes interrupt after the next measurement cycle */
		bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES,
					BH1770_LUX_DEF_THRES);
		/* Inform that we are waiting for a result from ALS */
		chip->lux_wait_result = true;
		bh1770_prox_mode_control(chip);
	}
	return ret;
}
#endif

#ifdef CONFIG_PM
static int bh1770_runtime_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bh1770_chip *chip = i2c_get_clientdata(client);

	bh1770_chip_off(chip);

	return 0;
}

static int bh1770_runtime_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bh1770_chip *chip = i2c_get_clientdata(client);

	bh1770_chip_on(chip);

	return 0;
}
#endif

static const struct i2c_device_id bh1770_id[] = {
	{"bh1770glc", 0 },
	{"sfh7770", 0 },
	{}
};

MODULE_DEVICE_TABLE(i2c, bh1770_id);

static const struct dev_pm_ops bh1770_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(bh1770_suspend, bh1770_resume)
	SET_RUNTIME_PM_OPS(bh1770_runtime_suspend, bh1770_runtime_resume, NULL)
};

static struct i2c_driver bh1770_driver = {
	.driver	  = {
		.name	= "bh1770glc",
		.pm	= &bh1770_pm_ops,
	},
	.probe    = bh1770_probe,
	.remove	  = bh1770_remove,
	.id_table = bh1770_id,
};

module_i2c_driver(bh1770_driver);

MODULE_DESCRIPTION("BH1770GLC / SFH7770 combined ALS and proximity sensor");
MODULE_AUTHOR("Samu Onkalo, Nokia Corporation");
MODULE_LICENSE("GPL v2");