From f7c9c149e5c7cde467ceba3b471b6834972e28a3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 16 May 2016 09:46:01 -0700 Subject: aiodev: Add basic LM75 temperature driver Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/aiodev/Kconfig | 6 ++ drivers/aiodev/Makefile | 1 + drivers/aiodev/lm75.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 drivers/aiodev/lm75.c diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig index 185c44e733..0ecf6f4092 100644 --- a/drivers/aiodev/Kconfig +++ b/drivers/aiodev/Kconfig @@ -13,4 +13,10 @@ config IMX_THERMAL Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs. +config LM75 + tristate "LM75 driver" + depends on I2C + help + Support for LM75 and similar devices + endif diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile index 5480a8a94c..c3d2b081a9 100644 --- a/drivers/aiodev/Makefile +++ b/drivers/aiodev/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_AIODEV) += core.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o +obj-$(CONFIG_LM75) += lm75.o diff --git a/drivers/aiodev/lm75.c b/drivers/aiodev/lm75.c new file mode 100644 index 0000000000..b4da5a0f75 --- /dev/null +++ b/drivers/aiodev/lm75.c @@ -0,0 +1,262 @@ +/* + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +/* + * This driver handles the LM75 and compatible digital temperature sensors. + */ + +/* straight from the datasheet */ +#define LM75_TEMP_MIN (-55000) +#define LM75_TEMP_MAX 125000 +#define LM75_SHUTDOWN 0x01 + +enum lm75_type { /* keep sorted in alphabetical order */ + adt75, + ds1775, + ds75, + ds7505, + g751, + lm75, + lm75a, + lm75b, + max6625, + max6626, + mcp980x, + stds75, + tcn75, + tmp100, + tmp101, + tmp105, + tmp112, + tmp175, + tmp275, + tmp75, + tmp75c, +}; + +/* The LM75 registers */ +#define LM75_REG_CONF 0x01 +static const u8 LM75_REG_TEMP[3] = { + 0x00, /* input */ + 0x03, /* max */ + 0x02, /* hyst */ +}; + +/* Each client has this additional data */ +struct lm75_data { + struct i2c_client *client; + struct device_d dev; + u8 resolution; /* In bits, between 9 and 12 */ + struct aiochannel aiochan; + struct aiodevice aiodev; +}; + +static int lm75_read_value(struct lm75_data *data, u8 reg) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_read_byte_data(data->client, reg); + else + return i2c_smbus_read_word_swapped(data->client, reg); +} + +static int lm75_write_value(struct lm75_data *data, u8 reg, u16 value) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_write_byte_data(data->client, reg, value); + else + return i2c_smbus_write_word_swapped(data->client, reg, value); +} + +static long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +static int lm75_get_temp(struct aiochannel *chan, int *val) +{ + struct lm75_data *data = container_of(chan, struct lm75_data, aiochan); + int status; + + status = lm75_read_value(data, LM75_REG_TEMP[0]); + if (status < 0) { + dev_err(&data->client->dev, + "LM75: Failed to read value: reg %d, error %d\n", + LM75_REG_TEMP[0], status); + return status; + } + + *val = lm75_reg_to_mc(status, data->resolution); + + return 0; +} + +static int lm75_probe(struct device_d *dev) +{ + struct lm75_data *data; + int status; + u8 set_mask, clr_mask; + int new, ret; + enum lm75_type kind; + + ret = dev_get_drvdata(dev, (const void **)&kind); + if (ret) + return ret; + + data = xzalloc(sizeof(*data)); + + data->client = to_i2c_client(dev); + + /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. + * Then tweak to be more precise when appropriate. + */ + set_mask = 0; + clr_mask = LM75_SHUTDOWN; /* continuous conversions */ + + switch (kind) { + case adt75: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + break; + case ds1775: + case ds75: + case stds75: + clr_mask |= 3 << 5; + set_mask |= 2 << 5; /* 11-bit mode */ + data->resolution = 11; + break; + case ds7505: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + break; + case g751: + case lm75: + case lm75a: + data->resolution = 9; + break; + case lm75b: + data->resolution = 11; + break; + case max6625: + data->resolution = 9; + break; + case max6626: + data->resolution = 12; + break; + case tcn75: + data->resolution = 9; + break; + case mcp980x: + /* fall through */ + case tmp100: + case tmp101: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + clr_mask |= 1 << 7; /* not one-shot mode */ + break; + case tmp112: + set_mask |= 3 << 5; /* 12-bit mode */ + clr_mask |= 1 << 7; /* not one-shot mode */ + data->resolution = 12; + break; + case tmp105: + case tmp175: + case tmp275: + case tmp75: + set_mask |= 3 << 5; /* 12-bit mode */ + clr_mask |= 1 << 7; /* not one-shot mode */ + data->resolution = 12; + break; + case tmp75c: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + break; + } + + /* configure as specified */ + status = lm75_read_value(data, LM75_REG_CONF); + if (status < 0) { + dev_dbg(dev, "Can't read config? %d\n", status); + return status; + } + + new = status & ~clr_mask; + new |= set_mask; + if (status != new) + lm75_write_value(data, LM75_REG_CONF, new); + + data->aiodev.num_channels = 1; + data->aiodev.hwdev = dev; + data->aiodev.read = lm75_get_temp; + data->aiodev.channels = xmalloc(sizeof(void *)); + data->aiodev.channels[0] = &data->aiochan; + data->aiochan.unit = "mC"; + + ret = aiodevice_register(&data->aiodev); + if (ret) + return ret; + + return 0; +} + +static const struct platform_device_id lm75_ids[] = { + { .name = "adt75", .driver_data = adt75, }, + { .name = "ds1775", .driver_data = ds1775, }, + { .name = "ds75", .driver_data = ds75, }, + { .name = "ds7505", .driver_data = ds7505, }, + { .name = "g751", .driver_data = g751, }, + { .name = "lm75", .driver_data = lm75, }, + { .name = "lm75a", .driver_data = lm75a, }, + { .name = "lm75b", .driver_data = lm75b, }, + { .name = "max6625", .driver_data = max6625, }, + { .name = "max6626", .driver_data = max6626, }, + { .name = "mcp980x", .driver_data = mcp980x, }, + { .name = "stds75", .driver_data = stds75, }, + { .name = "tcn75", .driver_data = tcn75, }, + { .name = "tmp100", .driver_data = tmp100, }, + { .name = "tmp101", .driver_data = tmp101, }, + { .name = "tmp105", .driver_data = tmp105, }, + { .name = "tmp112", .driver_data = tmp112, }, + { .name = "tmp175", .driver_data = tmp175, }, + { .name = "tmp275", .driver_data = tmp275, }, + { .name = "tmp75", .driver_data = tmp75, }, + { .name = "tmp75c", .driver_data = tmp75c, }, + { /* LIST END */ } +}; + +static struct driver_d lm75_driver = { + .name = "lm75", + .probe = lm75_probe, + .id_table = lm75_ids, +}; + +static int lm75_init(void) +{ + i2c_driver_register(&lm75_driver); + return 0; +} + +device_initcall(lm75_init); -- cgit v1.2.3