Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek tsl2550.c

  Interaktion und
PortierbarkeitC
 

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  tsl2550.c - Linux kernel modules for ambient light sensor
 *
 *  Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
 *  Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
 */


#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>

#define TSL2550_DRV_NAME "tsl2550"
#define DRIVER_VERSION  "1.2"

/*
 * Defines
 */


#define TSL2550_POWER_DOWN  0x00
#define TSL2550_POWER_UP  0x03
#define TSL2550_STANDARD_RANGE  0x18
#define TSL2550_EXTENDED_RANGE  0x1d
#define TSL2550_READ_ADC0  0x43
#define TSL2550_READ_ADC1  0x83

/*
 * Structs
 */


struct tsl2550_data {
 struct i2c_client *client;
 struct mutex update_lock;

 unsigned int power_state:1;
 unsigned int operating_mode:1;
};

/*
 * Global data
 */


static const u8 TSL2550_MODE_RANGE[2] = {
 TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
};

/*
 * Management functions
 */


static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
{
 struct tsl2550_data *data = i2c_get_clientdata(client);

 int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);

 data->operating_mode = mode;

 return ret;
}

static int tsl2550_set_power_state(struct i2c_client *client, int state)
{
 struct tsl2550_data *data = i2c_get_clientdata(client);
 int ret;

 if (state == 0)
  ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
 else {
  ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);

  /* On power up we should reset operating mode also... */
  tsl2550_set_operating_mode(client, data->operating_mode);
 }

 data->power_state = state;

 return ret;
}

static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
{
 int ret;

 ret = i2c_smbus_read_byte_data(client, cmd);
 if (ret < 0)
  return ret;
 if (!(ret & 0x80))
  return -EAGAIN;
 return ret & 0x7f; /* remove the "valid" bit */
}

/*
 * LUX calculation
 */


#define TSL2550_MAX_LUX  1846

static const u8 ratio_lut[] = {
 100100100100100100100100,
 1001001001001001009999,
 9999999999999999,
 9999999898989898,
 9898979797979796,
 9696969595959494,
 9393939292919190,
 8989888787868584,
 8382818079787775,
 7473716968666462,
 6058565452494744,
 4241404039393838,
 3737373636363535,
 3535343434343333,
 3333323232323231,
 3131313130303030,
 30,
};

static const u16 count_lut[] = {
 01234567,
 89101112131415,
 1618202224262830,
 3234363840424446,
 4953576165697377,
 8185899397101105109,
 115123131139147155163171,
 179187195203211219227235,
 247263279295311327343359,
 375391407423439455471487,
 511543575607639671703735,
 767799831863895927959991,
 10391103116712311295135914231487,
 15511615167917431807187119351999,
 20952223235124792607273528632991,
 31193247337535033631375938874015,
};

/*
 * This function is described into Taos TSL2550 Designer's Notebook
 * pages 2, 3.
 */

static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
{
 unsigned int lux;

 /* Look up count from channel values */
 u16 c0 = count_lut[ch0];
 u16 c1 = count_lut[ch1];

 /* Avoid division by 0 and count 1 cannot be greater than count 0 */
 if (c1 <= c0)
  if (c0) {
   /*
 * Calculate ratio.
 * Note: the "128" is a scaling factor
 */

   u8 r = c1 * 128 / c0;

   /* Calculate LUX */
   lux = ((c0 - c1) * ratio_lut[r]) / 256;
  } else
   lux = 0;
 else
  return 0;

 /* LUX range check */
 return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
}

/*
 * SysFS support
 */


static ssize_t tsl2550_show_power_state(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));

 return sprintf(buf, "%u\n", data->power_state);
}

static ssize_t tsl2550_store_power_state(struct device *dev,
  struct device_attribute *attr, const char *buf, size_t count)
{
 struct i2c_client *client = to_i2c_client(dev);
 struct tsl2550_data *data = i2c_get_clientdata(client);
 unsigned long val;
 int ret;

 if (kstrtoul(buf, 10, &val) || val > 1)
  return -EINVAL;

 mutex_lock(&data->update_lock);
 ret = tsl2550_set_power_state(client, val);
 mutex_unlock(&data->update_lock);

 if (ret < 0)
  return ret;

 return count;
}

static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
     tsl2550_show_power_state, tsl2550_store_power_state);

static ssize_t tsl2550_show_operating_mode(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));

 return sprintf(buf, "%u\n", data->operating_mode);
}

static ssize_t tsl2550_store_operating_mode(struct device *dev,
  struct device_attribute *attr, const char *buf, size_t count)
{
 struct i2c_client *client = to_i2c_client(dev);
 struct tsl2550_data *data = i2c_get_clientdata(client);
 unsigned long val;
 int ret;

 if (kstrtoul(buf, 10, &val) || val > 1)
  return -EINVAL;

 if (data->power_state == 0)
  return -EBUSY;

 mutex_lock(&data->update_lock);
 ret = tsl2550_set_operating_mode(client, val);
 mutex_unlock(&data->update_lock);

 if (ret < 0)
  return ret;

 return count;
}

static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
     tsl2550_show_operating_mode, tsl2550_store_operating_mode);

static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
{
 struct tsl2550_data *data = i2c_get_clientdata(client);
 u8 ch0, ch1;
 int ret;

 ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
 if (ret < 0)
  return ret;
 ch0 = ret;

 ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
 if (ret < 0)
  return ret;
 ch1 = ret;

 /* Do the job */
 ret = tsl2550_calculate_lux(ch0, ch1);
 if (ret < 0)
  return ret;
 if (data->operating_mode == 1)
  ret *= 5;

 return sprintf(buf, "%d\n", ret);
}

static ssize_t tsl2550_show_lux1_input(struct device *dev,
   struct device_attribute *attr, char *buf)
{
 struct i2c_client *client = to_i2c_client(dev);
 struct tsl2550_data *data = i2c_get_clientdata(client);
 int ret;

 /* No LUX data if not operational */
 if (!data->power_state)
  return -EBUSY;

 mutex_lock(&data->update_lock);
 ret = __tsl2550_show_lux(client, buf);
 mutex_unlock(&data->update_lock);

 return ret;
}

static DEVICE_ATTR(lux1_input, S_IRUGO,
     tsl2550_show_lux1_input, NULL);

static struct attribute *tsl2550_attributes[] = {
 &dev_attr_power_state.attr,
 &dev_attr_operating_mode.attr,
 &dev_attr_lux1_input.attr,
 NULL
};

static const struct attribute_group tsl2550_attr_group = {
 .attrs = tsl2550_attributes,
};

/*
 * Initialization function
 */


static int tsl2550_init_client(struct i2c_client *client)
{
 struct tsl2550_data *data = i2c_get_clientdata(client);
 int err;

 /*
 * Probe the chip. To do so we try to power up the device and then to
 * read back the 0x03 code
 */

 err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
 if (err < 0)
  return err;
 if (err != TSL2550_POWER_UP)
  return -ENODEV;
 data->power_state = 1;

 /* Set the default operating mode */
 err = i2c_smbus_write_byte(client,
       TSL2550_MODE_RANGE[data->operating_mode]);
 if (err < 0)
  return err;

 return 0;
}

/*
 * I2C init/probing/exit functions
 */


static struct i2c_driver tsl2550_driver;
static int tsl2550_probe(struct i2c_client *client)
{
 struct i2c_adapter *adapter = client->adapter;
 struct tsl2550_data *data;
 int *opmode, err = 0;

 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
         | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
  err = -EIO;
  goto exit;
 }

 data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
 if (!data) {
  err = -ENOMEM;
  goto exit;
 }
 data->client = client;
 i2c_set_clientdata(client, data);

 /* Check platform data */
 opmode = client->dev.platform_data;
 if (opmode) {
  if (*opmode < 0 || *opmode > 1) {
   dev_err(&client->dev, "invalid operating_mode (%d)\n",
     *opmode);
   err = -EINVAL;
   goto exit_kfree;
  }
  data->operating_mode = *opmode;
 } else
  data->operating_mode = 0/* default mode is standard */
 dev_info(&client->dev, "%s operating mode\n",
   data->operating_mode ? "extended" : "standard");

 mutex_init(&data->update_lock);

 /* Initialize the TSL2550 chip */
 err = tsl2550_init_client(client);
 if (err)
  goto exit_kfree;

 /* Register sysfs hooks */
 err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
 if (err)
  goto exit_kfree;

 dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);

 return 0;

exit_kfree:
 kfree(data);
exit:
 return err;
}

static void tsl2550_remove(struct i2c_client *client)
{
 sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);

 /* Power down the device */
 tsl2550_set_power_state(client, 0);

 kfree(i2c_get_clientdata(client));
}

#ifdef CONFIG_PM_SLEEP

static int tsl2550_suspend(struct device *dev)
{
 return tsl2550_set_power_state(to_i2c_client(dev), 0);
}

static int tsl2550_resume(struct device *dev)
{
 return tsl2550_set_power_state(to_i2c_client(dev), 1);
}

static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume);
#define TSL2550_PM_OPS (&tsl2550_pm_ops)

#else

#define TSL2550_PM_OPS NULL

#endif /* CONFIG_PM_SLEEP */

static const struct i2c_device_id tsl2550_id[] = {
 { "tsl2550" },
 { }
};
MODULE_DEVICE_TABLE(i2c, tsl2550_id);

static const struct of_device_id tsl2550_of_match[] = {
 { .compatible = "taos,tsl2550" },
 { }
};
MODULE_DEVICE_TABLE(of, tsl2550_of_match);

static struct i2c_driver tsl2550_driver = {
 .driver = {
  .name = TSL2550_DRV_NAME,
  .of_match_table = tsl2550_of_match,
  .pm = TSL2550_PM_OPS,
 },
 .probe = tsl2550_probe,
 .remove = tsl2550_remove,
 .id_table = tsl2550_id,
};

module_i2c_driver(tsl2550_driver);

MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);

Messung V0.5 in Prozent
C=97 H=96 G=96

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.14Bemerkung:  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-07) ¤

*Bot Zugriff






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=141584
#Domains=752002