root/drivers/misc/bh1770glc.c
// 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) {
                ret = pm_runtime_resume_and_get(dev);
                if (ret < 0)
                        goto leave;

                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 time_left;

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

        time_left = wait_event_interruptible_timeout(chip->wait,
                                                     !chip->lux_wait_result,
                                                     msecs_to_jiffies(BH1770_TIMEOUT));
        if (!time_left)
                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" },
        { "sfh7770" },
        {}
};

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");