* [RFC] iodevice support @ 2015-12-08 9:05 Sascha Hauer 2015-12-08 9:05 ` [PATCH 1/4] misc: Add " Sascha Hauer ` (4 more replies) 0 siblings, 5 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-08 9:05 UTC (permalink / raw) To: Barebox List The following is something like a mixture of Linux IIO and hwmon support for the poor. I called it iodevice, but better names are appreciated. A iodevice has multiple channels, each providing a value like for example a temperature or a voltage. For each iodevice we provide device parameters to access the values from the commandline. The C API for a consumer consists of iochannel_get() to get a iochannel, and iochannel_get_value() to actually read a value. As drivers we currently have a LM75 temperature driver and a MCP342x ADC driver. Also there is a PT100 driver which itself is a consumer of another iodevice, a MCP342x in my case. This is more meant as an example since the voltage to temperature conversion function is board specific. Sascha ---------------------------------------------------------------- Sascha Hauer (4): misc: Add iodevice support misc: Add basic LM75 temperature driver misc: Add Microchip MCP342x support misc: Add PT100 temperature sensor support drivers/misc/Kconfig | 18 ++++ drivers/misc/Makefile | 5 + drivers/misc/iodevice.c | 109 ++++++++++++++++++++ drivers/misc/lm75.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/mcp342x.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/pt100.c | 145 +++++++++++++++++++++++++++ include/iodevice.h | 33 ++++++ 7 files changed, 831 insertions(+) create mode 100644 drivers/misc/iodevice.c create mode 100644 drivers/misc/lm75.c create mode 100644 drivers/misc/mcp342x.c create mode 100644 drivers/misc/pt100.c create mode 100644 include/iodevice.h _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/4] misc: Add iodevice support 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer @ 2015-12-08 9:05 ` Sascha Hauer 2015-12-10 20:42 ` Trent Piepho 2015-12-08 9:05 ` [PATCH 2/4] misc: Add basic LM75 temperature driver Sascha Hauer ` (3 subsequent siblings) 4 siblings, 1 reply; 9+ messages in thread From: Sascha Hauer @ 2015-12-08 9:05 UTC (permalink / raw) To: Barebox List This adds a poor copy of the Linux IIO framework. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/iodevice.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ include/iodevice.h | 33 +++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 drivers/misc/iodevice.c create mode 100644 include/iodevice.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7a5b146..250b205 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -19,4 +19,7 @@ config STATE_DRV tristate "state driver" depends on STATE +config IODEVICE + bool "IO device support" + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 487e4b8..127504e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_JTAG) += jtag.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_STATE_DRV) += state.o +obj-$(CONFIG_IODEVICE) += iodevice.o diff --git a/drivers/misc/iodevice.c b/drivers/misc/iodevice.c new file mode 100644 index 0000000..6fdcd42 --- /dev/null +++ b/drivers/misc/iodevice.c @@ -0,0 +1,109 @@ +#include <common.h> +#include <iodevice.h> +#include <linux/list.h> +#include <malloc.h> + +static LIST_HEAD(iodevices); + +struct iochannel *iochannel_get_by_name(const char *name) +{ + struct iodevice *iodev; + int i; + + list_for_each_entry(iodev, &iodevices, list) { + for (i = 0; i < iodev->num_channels; i++) + if (!strcmp(name, iodev->channels[i]->name)) + return iodev->channels[i]; + } + + return ERR_PTR(-ENOENT); +} + +struct iochannel *iochannel_get(struct device_d *dev, int index) +{ + struct of_phandle_args spec; + struct iodevice *iodev; + int ret, chnum = 0; + + if (!dev->device_node) + return ERR_PTR(-EINVAL); + + ret = of_parse_phandle_with_args(dev->device_node, "io-channels", "#io-channel-cells", index, + &spec); + if (ret) + return ERR_PTR(ret); + + list_for_each_entry(iodev, &iodevices, list) { + if (iodev->hwdev->device_node == spec.np) + goto found; + } + + return ERR_PTR(-EPROBE_DEFER); + +found: + if (spec.args_count) + chnum = spec.args[0]; + + if (chnum >= iodev->num_channels) + return ERR_PTR(-EINVAL); + + return iodev->channels[chnum]; +} + +int iochannel_get_value(struct iochannel *iochan, int *value) +{ + struct iodevice *iodev = iochan->iodev; + + return iodev->read(iochan, value); +} + +static int iochannel_param_get_value(struct param_d *p, void *priv) +{ + struct iochannel *iochan = priv; + + return iochannel_get_value(iochan, &iochan->value); +} + +int iodevice_register(struct iodevice *iodev) +{ + const char *devname = NULL; + int i, ret; + + if (iodev->hwdev->device_node) { + devname = of_alias_get(iodev->hwdev->device_node); + iodev->dev.id = DEVICE_ID_SINGLE; + } + + if (!devname) { + devname = "io"; + iodev->dev.id = DEVICE_ID_DYNAMIC; + } + + strcpy(iodev->dev.name, devname); + + iodev->dev.parent = iodev->hwdev; + + ret = register_device(&iodev->dev); + if (ret) + return ret; + + for (i = 0; i < iodev->num_channels; i++) { + struct iochannel *iochan = iodev->channels[i]; + char *name; + + iochan->iodev = iodev; + + name = asprintf("in_value%d_%s", i, iochan->unit); + + dev_add_param_int(&iodev->dev, name, NULL, + iochannel_param_get_value, &iochan->value, "%d", iochan); + + iochan->name = asprintf("%s.%s", devname, name); + + free(name); + } + + list_add_tail(&iodev->list, &iodevices); + + return 0; +} diff --git a/include/iodevice.h b/include/iodevice.h new file mode 100644 index 0000000..e0e571e --- /dev/null +++ b/include/iodevice.h @@ -0,0 +1,33 @@ +#ifndef __IODEVICE_H +#define __IODEVICE_H + +struct iochannel { + char *unit; + struct iodevice *iodev; + + int value; + char *name; +}; + +struct iodevice { + int (*read)(struct iochannel *, int *val); + struct device_d dev; + struct device_d *hwdev; + struct iochannel **channels; + int num_channels; + struct list_head list; +}; + +int iodevice_register(struct iodevice *iodev); + +struct iochannel *iochannel_get(struct device_d *dev, int index); +struct iochannel *iochannel_get_by_name(const char *name); + +int iochannel_get_value(struct iochannel *iochan, int *value); + +static inline const char *iochannel_get_unit(struct iochannel *iochan) +{ + return iochan->unit; +} + +#endif -- 2.6.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/4] misc: Add iodevice support 2015-12-08 9:05 ` [PATCH 1/4] misc: Add " Sascha Hauer @ 2015-12-10 20:42 ` Trent Piepho 2015-12-11 7:37 ` Sascha Hauer 0 siblings, 1 reply; 9+ messages in thread From: Trent Piepho @ 2015-12-10 20:42 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List On Tue, 2015-12-08 at 10:05 +0100, Sascha Hauer wrote: > +int iodevice_register(struct iodevice *iodev) > +{ > + const char *devname = NULL; > + int i, ret; > + > + if (iodev->hwdev->device_node) { > + devname = of_alias_get(iodev->hwdev->device_node); > + iodev->dev.id = DEVICE_ID_SINGLE; > + } > + > + if (!devname) { > + devname = "io"; > + iodev->dev.id = DEVICE_ID_DYNAMIC; > + } > + > + strcpy(iodev->dev.name, devname); > + > + iodev->dev.parent = iodev->hwdev; > + > + ret = register_device(&iodev->dev); Why does every driver/subsystem need to implement its own alias support? Since register_device() has access to (&iodev->dev)->parent->device_node, couldn't it do the alias lookup instead, so that each driver doesn't need to? Then one could write: iodev->dev.parent = iodev->hwdev; strcpy(iodev->dev.name, "io"); iodev->dev.id = DEVICE_ID_DYNAMIC; ret = register_device(&iodev->dev); I guess this doesn't work exactly for net drivers, since they want to do s/ethernet(\d+)/eth\1/ on the alias name. And some drivers create a cdev the alias should be applied to instead of a dev. Though I supposed devfs_create() could do the same thing. _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/4] misc: Add iodevice support 2015-12-10 20:42 ` Trent Piepho @ 2015-12-11 7:37 ` Sascha Hauer 0 siblings, 0 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-11 7:37 UTC (permalink / raw) To: Trent Piepho; +Cc: Barebox List On Thu, Dec 10, 2015 at 08:42:42PM +0000, Trent Piepho wrote: > On Tue, 2015-12-08 at 10:05 +0100, Sascha Hauer wrote: > > +int iodevice_register(struct iodevice *iodev) > > +{ > > + const char *devname = NULL; > > + int i, ret; > > + > > + if (iodev->hwdev->device_node) { > > + devname = of_alias_get(iodev->hwdev->device_node); > > + iodev->dev.id = DEVICE_ID_SINGLE; > > + } > > + > > + if (!devname) { > > + devname = "io"; > > + iodev->dev.id = DEVICE_ID_DYNAMIC; > > + } > > + > > + strcpy(iodev->dev.name, devname); > > + > > + iodev->dev.parent = iodev->hwdev; > > + > > + ret = register_device(&iodev->dev); > > Why does every driver/subsystem need to implement its own alias > support? I think that has historic reasons. When beginning with device tree we didn't want to change the behaviours of all unrelated drivers. Now that we are more familiar with device tree and adopt the aliases approach to more and more drivers you are right: We can push the alias resolving out of the drivers and more to the framework level. > > Since register_device() has access to > (&iodev->dev)->parent->device_node, couldn't it do the alias lookup > instead, so that each driver doesn't need to? > > Then one could write: > > iodev->dev.parent = iodev->hwdev; > strcpy(iodev->dev.name, "io"); > iodev->dev.id = DEVICE_ID_DYNAMIC; > ret = register_device(&iodev->dev); > > I guess this doesn't work exactly for net drivers, since they want to do > s/ethernet(\d+)/eth\1/ on the alias name. The network drivers already do something similar. They pick only the number from the "ethernetx" alias, see net/eth.c: edev->dev.id = of_alias_get_id(edev->dev.parent->device_node, "ethernet"); > > And some drivers create a cdev the alias should be applied to instead of > a dev. Though I supposed devfs_create() could do the same thing. I'm not sure if we should handle aliases in devfs_create(), but at least we should handle it in the subsystem so that the individual drivers no longer have to care about it. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/4] misc: Add basic LM75 temperature driver 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer 2015-12-08 9:05 ` [PATCH 1/4] misc: Add " Sascha Hauer @ 2015-12-08 9:05 ` Sascha Hauer 2015-12-08 9:05 ` [PATCH 3/4] misc: Add Microchip MCP342x support Sascha Hauer ` (2 subsequent siblings) 4 siblings, 0 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-08 9:05 UTC (permalink / raw) To: Barebox List Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/misc/Kconfig | 5 + drivers/misc/Makefile | 1 + drivers/misc/lm75.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/misc/lm75.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 250b205..b95a7f6 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -22,4 +22,9 @@ config STATE_DRV config IODEVICE bool "IO device support" +config LM75 + tristate "LM75 driver" + depends on I2C + depends on IODEVICE + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 127504e..a257dfb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_JTAG) += jtag.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_STATE_DRV) += state.o obj-$(CONFIG_IODEVICE) += iodevice.o +obj-$(CONFIG_LM75) += lm75.o diff --git a/drivers/misc/lm75.c b/drivers/misc/lm75.c new file mode 100644 index 0000000..d9e5580 --- /dev/null +++ b/drivers/misc/lm75.c @@ -0,0 +1,262 @@ +/* + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * + * 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 <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <i2c/i2c.h> +#include <iodevice.h> + +/* + * 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 iochannel iochan; + struct iodevice iodev; +}; + +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 iochannel *iochan, int *val) +{ + struct lm75_data *data = container_of(iochan, struct lm75_data, iochan); + 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->iodev.num_channels = 1; + data->iodev.hwdev = dev; + data->iodev.read = lm75_get_temp; + data->iodev.channels = xmalloc(sizeof(void *)); + data->iodev.channels[0] = &data->iochan; + data->iochan.unit = "mC"; + + ret = iodevice_register(&data->iodev); + 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); \ No newline at end of file -- 2.6.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/4] misc: Add Microchip MCP342x support 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer 2015-12-08 9:05 ` [PATCH 1/4] misc: Add " Sascha Hauer 2015-12-08 9:05 ` [PATCH 2/4] misc: Add basic LM75 temperature driver Sascha Hauer @ 2015-12-08 9:05 ` Sascha Hauer 2015-12-08 9:05 ` [PATCH 4/4] misc: Add PT100 temperature sensor support Sascha Hauer 2015-12-10 6:24 ` [RFC] iodevice support Andrey Smirnov 4 siblings, 0 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-08 9:05 UTC (permalink / raw) To: Barebox List Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/misc/Kconfig | 5 + drivers/misc/Makefile | 2 + drivers/misc/mcp342x.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/misc/mcp342x.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b95a7f6..8d44a5c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -27,4 +27,9 @@ config LM75 depends on I2C depends on IODEVICE +config MCP342X + tristate "Microchip MCP342x ADC driver" + depends on I2C + depends on IODEVICE + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a257dfb..6680334 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_STATE_DRV) += state.o obj-$(CONFIG_IODEVICE) += iodevice.o obj-$(CONFIG_LM75) += lm75.o +obj-$(CONFIG_IODEVICE) += iodevice.o +obj-$(CONFIG_MCP342X) += mcp342x.o diff --git a/drivers/misc/mcp342x.c b/drivers/misc/mcp342x.c new file mode 100644 index 0000000..e498078 --- /dev/null +++ b/drivers/misc/mcp342x.c @@ -0,0 +1,259 @@ +/* + * mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family + * + * Copyright (C) 2013, Angelo Compagnucci + * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com> + * + * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf + * http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf + * + * This driver exports the value of analog input voltage to sysfs, the + * voltage unit is nV. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <common.h> +#include <init.h> +#include <iodevice.h> +#include <malloc.h> +#include <driver.h> +#include <xfuncs.h> +#include <i2c/i2c.h> + +/* Masks */ +#define MCP3422_CHANNEL_MASK 0x60 +#define MCP3422_PGA_MASK 0x03 +#define MCP3422_SRATE_MASK 0x0C +#define MCP3422_SRATE_240 0x0 +#define MCP3422_SRATE_60 0x1 +#define MCP3422_SRATE_15 0x2 +#define MCP3422_SRATE_3 0x3 +#define MCP3422_PGA_1 0 +#define MCP3422_PGA_2 1 +#define MCP3422_PGA_4 2 +#define MCP3422_PGA_8 3 +#define MCP3422_CONT_SAMPLING 0x10 + +#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5) +#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2) + +#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK) +#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK) + +/* Constant msleep times for data acquisitions */ +static const int mcp3422_read_times[4] = { + [MCP3422_SRATE_240] = 1000 / 240, + [MCP3422_SRATE_60] = 1000 / 60, + [MCP3422_SRATE_15] = 1000 / 15, + [MCP3422_SRATE_3] = 1000 / 3 }; + +static const int mcp3422_sample_rate_scales[4] = { + [MCP3422_SRATE_240] = 8000, + [MCP3422_SRATE_60] = 2000, + [MCP3422_SRATE_15] = 500, + [MCP3422_SRATE_3] = 125}; + +static const char *mcp342x_sample_rate_str[] = { + "240", "60", "15", "3" +}; + +static const char *mcp342x_gain_str[] = { + "1", "2", "4", "8" +}; + +/* sample rates to sign extension table */ +static const int mcp3422_sign_extend[4] = { + [MCP3422_SRATE_240] = 11, + [MCP3422_SRATE_60] = 13, + [MCP3422_SRATE_15] = 15, + [MCP3422_SRATE_3] = 17 }; + +struct mcp342x_channel { + int num; + int voltage; + int temp; + struct mcp342x *mcp342x; + struct iochannel iochan; + unsigned int gain; +}; + +struct mcp342x { + struct i2c_client *i2c; + struct iodevice iodev; + unsigned int sps; + u8 id; + u8 config; + u8 pga[4]; + int num_channels; + struct mcp342x_channel *channel[4]; + char *unit; +}; + +static int mcp3422_update_config(struct mcp342x *adc, u8 newconfig) +{ + int ret; + + ret = i2c_master_send(adc->i2c, &newconfig, 1); + if (ret > 0) { + adc->config = newconfig; + ret = 0; + } + + return ret; +} + +static int mcp3422_read(struct mcp342x_channel *channel) +{ + struct mcp342x *adc = channel->mcp342x; + int ret = 0; + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + u8 buf[4] = {0, 0, 0, 0}; + u32 temp; + + if (sample_rate == MCP3422_SRATE_3) { + ret = i2c_master_recv(adc->i2c, buf, 4); + temp = buf[0] << 16 | buf[1] << 8 | buf[2]; + } else { + ret = i2c_master_recv(adc->i2c, buf, 3); + temp = buf[0] << 8 | buf[1]; + } + + channel->voltage = sign_extend32(temp, mcp3422_sign_extend[sample_rate]) * + mcp3422_sample_rate_scales[sample_rate] / (1 << channel->gain) / 8; + + return ret; +} + +static int mcp342x_get_voltage(struct iochannel *iochan, int *value) +{ + struct mcp342x_channel *channel = container_of(iochan, struct mcp342x_channel, iochan); + struct mcp342x *adc = channel->mcp342x; + u8 config; + int ret; + + config = MCP3422_CONT_SAMPLING | + MCP3422_CHANNEL_VALUE(channel->num) | + MCP3422_PGA_VALUE(channel->gain) | + MCP3422_SAMPLE_RATE_VALUE(adc->sps); + + if (config != adc->config) { + ret = mcp3422_update_config(adc, config); + if (ret < 0) + return ret; + + mdelay(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]); + } + + mcp3422_read(channel); + + *value = channel->voltage; + + return 0; +} + +static int mcp342x_probe(struct device_d *dev) +{ + struct mcp342x *adc; + int id , ret, i, num_channels; + + ret = dev_get_drvdata(dev, (const void **)&id); + if (ret) + return ret; + + adc = xzalloc(sizeof(*adc)); + + adc->i2c = to_i2c_client(dev); + adc->id = id; + adc->unit = "uV"; + + switch (id) { + case 1: + num_channels = 1; + break; + case 2: + case 3: + case 6: + case 7: + num_channels = 2; + break; + case 4: + case 8: + num_channels = 4; + break; + default: + return -EINVAL; + } + + adc->num_channels = num_channels; + + adc->sps = MCP3422_SRATE_15; + + adc->iodev.hwdev = dev; + + adc->iodev.channels = xmalloc(sizeof(void *) * num_channels); + adc->iodev.num_channels = num_channels; + adc->iodev.read = mcp342x_get_voltage; + + for (i = 0; i < num_channels; i++) { + struct mcp342x_channel *channel; + + channel = xzalloc(sizeof(*channel)); + adc->iodev.channels[i] = &channel->iochan; + channel->iochan.unit = "uV"; + adc->channel[i] = channel; + channel->num = i; + channel->mcp342x = adc; + } + + ret = iodevice_register(&adc->iodev); + if (ret) + return ret; + + dev_add_param_enum(&adc->iodev.dev, "in_sps", NULL, NULL, &adc->sps, + mcp342x_sample_rate_str, + ARRAY_SIZE(mcp342x_sample_rate_str), NULL); + + for (i = 0; i < num_channels; i++) { + struct mcp342x_channel *channel = adc->channel[i]; + char *name; + + name = asprintf("in_gain%d", i); + dev_add_param_enum(&adc->iodev.dev, name, NULL, NULL, &channel->gain, + mcp342x_gain_str, + ARRAY_SIZE(mcp342x_gain_str), NULL); + free(name); + } + + return 0; +} + +static const struct platform_device_id mcp342x_ids[] = { + { "mcp3421", 1 }, + { "mcp3422", 2 }, + { "mcp3423", 3 }, + { "mcp3424", 4 }, + { "mcp3426", 6 }, + { "mcp3427", 7 }, + { "mcp3428", 8 }, + { } +}; + +static struct driver_d mcp342x_driver = { + .name = "mcp342x", + .probe = mcp342x_probe, + .id_table = mcp342x_ids, +}; + +static int mcp342x_init(void) +{ + i2c_driver_register(&mcp342x_driver); + return 0; +} + +device_initcall(mcp342x_init); -- 2.6.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/4] misc: Add PT100 temperature sensor support 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer ` (2 preceding siblings ...) 2015-12-08 9:05 ` [PATCH 3/4] misc: Add Microchip MCP342x support Sascha Hauer @ 2015-12-08 9:05 ` Sascha Hauer 2015-12-10 6:24 ` [RFC] iodevice support Andrey Smirnov 4 siblings, 0 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-08 9:05 UTC (permalink / raw) To: Barebox List Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/misc/Kconfig | 5 ++ drivers/misc/Makefile | 1 + drivers/misc/pt100.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 drivers/misc/pt100.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8d44a5c..32e8bea 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -32,4 +32,9 @@ config MCP342X depends on I2C depends on IODEVICE + +config PT100 + tristate "PT100 driver" + depends on IODEVICE + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6680334..01b5597 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_IODEVICE) += iodevice.o obj-$(CONFIG_LM75) += lm75.o obj-$(CONFIG_IODEVICE) += iodevice.o obj-$(CONFIG_MCP342X) += mcp342x.o +obj-$(CONFIG_PT100) += pt100.o diff --git a/drivers/misc/pt100.c b/drivers/misc/pt100.c new file mode 100644 index 0000000..f9d8c6a --- /dev/null +++ b/drivers/misc/pt100.c @@ -0,0 +1,145 @@ +#include <common.h> +#include <driver.h> +#include <init.h> +#include <iodevice.h> + +unsigned int pt100[] = { + 18950, 19380, 19820, 20250, 20680, 21110, 21540, 21970, 22400, 22830, /* -200 - -191 */ + 23250, 23680, 24110, 24540, 24970, 25390, 25820, 26240, 26670, 27100, /* -190 - -181 */ + 27520, 27950, 28370, 28800, 29220, 29640, 30070, 30490, 30910, 31340, /* -180 - -171 */ + 31760, 32180, 32600, 33020, 33440, 33860, 34280, 34700, 35120, 35540, /* -170 - -161 */ + 35960, 36380, 36800, 37220, 37640, 38050, 38470, 38890, 39310, 39720, /* -160 - -151 */ + 40140, 40560, 40970, 41390, 41800, 42220, 42630, 43050, 43460, 43880, /* -150 - -141 */ + 44290, 44700, 45120, 45530, 45940, 46360, 46770, 47180, 47590, 48000, /* -140 - -131 */ + 48420, 48830, 49240, 49650, 50060, 50470, 50880, 51290, 51700, 52110, /* -130 - -121 */ + 52520, 52930, 53340, 53750, 54150, 54560, 54970, 55380, 55790, 56190, /* -120 - -111 */ + 56600, 57010, 57410, 57820, 58230, 58630, 59040, 59440, 59850, 60260, /* -110 - -101 */ + 60660, 61070, 61470, 61880, 62280, 62680, 63090, 63490, 63900, 64300, /* -100 - -91 */ + 64700, 65110, 65510, 65910, 66310, 66720, 67120, 67520, 67920, 68330, /* -90 - -81 */ + 68730, 69130, 69530, 69930, 70330, 70730, 71130, 71530, 71930, 72330, /* -80 - -71 */ + 72730, 73130, 73530, 73930, 74330, 74730, 75130, 75530, 75930, 76330, /* -70 - -61 */ + 76730, 77120, 77520, 77920, 78320, 78720, 79110, 79510, 79910, 80310, /* -60 - -51 */ + 80700, 81100, 81500, 81890, 82290, 82690, 83080, 83480, 83870, 84270, /* -50 - -41 */ + 84670, 85060, 85460, 85850, 86250, 86640, 87040, 87430, 87830, 88220, /* -40 - -31 */ + 88620, 89010, 89400, 89800, 90190, 90590, 90980, 91370, 91770, 92160, /* -30 - -21 */ + 92550, 92950, 93340, 93730, 94120, 94520, 94910, 95300, 95690, 96090, /* -20 - -11 */ + 96480, 96870, 97260, 97650, 98040, 98440, 98830, 99220, 99610, 100000, /* -10 - -1 */ + 100000, 100390, 100780, 101170, 101560, 101950, 102340, 102730, 103120, 103510, /* 0 - 9 */ + 103900, 104290, 104680, 105070, 105460, 105850, 106240, 106630, 107020, 107400, /* 10 - 19 */ + 107790, 108180, 108570, 108960, 109350, 109730, 110120, 110510, 110900, 111290, /* 20 - 29 */ + 111670, 112060, 112450, 112830, 113220, 113610, 114000, 114380, 114770, 115150, /* 30 - 39 */ + 115540, 115930, 116310, 116700, 117080, 117470, 117860, 118240, 118630, 119010, /* 40 - 49 */ + 119400, 119780, 120170, 120550, 120940, 121320, 121710, 122090, 122470, 122860, /* 50 - 59 */ + 123240, 123630, 124010, 124390, 124780, 125160, 125540, 125930, 126310, 126690, /* 60 - 69 */ + 127080, 127460, 127840, 128220, 128610, 128990, 129370, 129750, 130130, 130520, /* 70 - 79 */ + 130900, 131280, 131660, 132040, 132420, 132800, 133180, 133570, 133950, 134330, /* 80 - 89 */ + 134710, 135090, 135470, 135850, 136230, 136610, 136990, 137370, 137750, 138130, /* 90 - 99 */ + 138510, 138880, 139260, 139640, 140020, 140400, 140780, 141160, 141540, 141910, /* 100 - 109 */ + 142290, 142670, 143050, 143430, 143800, 144180, 144560, 144940, 145310, 145690, /* 110 - 119 */ + 146070, 146440, 146820, 147200, 147570, 147950, 148330, 148700, 149080, 149460, /* 120 - 129 */ + 149830, 150210, 150580, 150960, 151330, 151710, 152080, 152460, 152830, 153210, /* 130 - 139 */ + 153580, 153960, 154330, 154710, 155080, 155460, 155830, 156200, 156580, 156950, /* 140 - 149 */ + 157330, 157700, 158070, 158450, 158820, 159190, 159560, 159940, 160310, 160680, /* 150 - 159 */ + 161050, 161430, 161800, 162170, 162540, 162910, 163290, 163660, 164030, 164400, /* 160 - 169 */ + 164770, 165140, 165510, 165890, 166260, 166630, 167000, 167370, 167740, 168110, /* 170 - 179 */ + 168480, 168850, 169220, 169590, 169960, 170330, 170700, 171070, 171430, 171800, /* 180 - 189 */ + 172170, 172540, 172910, 173280, 173650, 174020, 174380, 174750, 175120, 175490, /* 190 - 199 */ + 175860, 176220, 176590, 176960, 177330, 177690, 178060, 178430, 178790, 179160, /* 200 - 209 */ + 179530, 179890, 180260, 180630, 180990, 181360, 181720, 182090, 182460, 182820, /* 210 - 219 */ + 183190, 183550, 183920, 184280, 184650, 185010, 185380, 185740, 186110, 186470, /* 220 - 229 */ + 186840, 187200, 187560, 187930, 188290, 188660, 189020, 189380, 189750, 190110, /* 230 - 239 */ + 190470, 190840, 191200, 191560, 191920, 192290, 192650, 193010, 193370, 193740, /* 240 - 249 */ + 194100, 194460, 194820, 195180, 195550, 195910, 196270, 196630, 196990, 197350, /* 250 - 259 */ + 197710, 198070, 198430, 198790, 199150, 199510, 199870, 200230, 200590, 200950, /* 260 - 269 */ + 201310, 201670, 202030, 202390, 202750, 203110, 203470, 203830, 204190, 204550, /* 270 - 279 */ + 204900, 205260, 205620, 205980, 206340, 206700, 207050, 207410, 207770, 208130, /* 280 - 289 */ + 208480, 208840, 209200, 209560, 209910, 210270, 210630, 210980, 211340, 211700, /* 290 - 299 */ + 212050, 212410, 212760, 213120, 213480, 213830, 214190, 214540, 214900, 215250, /* 300 - 309 */ + 215610, 215960, 216320, 216670, 217030, 217380, 217740, 218090, 218440, 218800, /* 310 - 319 */ + 219150, 219510, 219860, 220210, 220570, 220920, 221270, 221630, 221980, 222330, /* 320 - 329 */ + 222680, 223040, 223390, 223740, 224090, 224450, 224800, 225150, 225500, 225850, /* 330 - 339 */ + 226210, 226560, 226910, 227260, 227610, 227960, 228310, 228660, 229020, 229370, /* 340 - 349 */ +}; + +struct pt100_priv { + struct iodevice iodev; + struct iochannel iochan; + struct iochannel *input; +}; + +static int pt100_get_value(struct iochannel *iochan, int *value) +{ + struct pt100_priv *priv = container_of(iochan, struct pt100_priv, iochan); + int U_Rx_uV, Rx_mOhm, i, ret; + + ret = iochannel_get_value(priv->input, &U_Rx_uV); + + if (U_Rx_uV > 460000) { + dev_err(priv->iodev.hwdev, "Input voltage too high. Sensor broken?"); + return -EIO; + } + + if (U_Rx_uV < 5000) { + dev_err(priv->iodev.hwdev, "Input voltage too low. Sensor short circuited?"); + return -EIO; + } + + Rx_mOhm = U_Rx_uV * 7800 / (2048000 - U_Rx_uV) * 1000; + + /* Out of range for PT100. Assume PT1000 */ + if (Rx_mOhm > 229370) + Rx_mOhm /= 10; + + for (i = 0; i < ARRAY_SIZE(pt100) - 1; i++) { + if (Rx_mOhm >= pt100[i] && Rx_mOhm < pt100[i + 1]) { + *value = i - 200; + break; + } + } + + return 0; +} + +static int pt100_probe(struct device_d *dev) +{ + struct pt100_priv *priv; + struct iochannel *iochan; + int ret; + + if (!dev->device_node) + return -EINVAL; + + iochan = iochannel_get(dev, 0); + if (!iochan) + return -ENOENT; + + priv = xzalloc(sizeof(*priv)); + + priv->input = iochan; + + priv->iodev.num_channels = 1; + priv->iodev.hwdev = dev; + priv->iodev.read = pt100_get_value; + priv->iodev.channels = xmalloc(sizeof(void *)); + priv->iodev.channels[0] = &priv->iochan; + priv->iochan.unit = "C"; + + ret = iodevice_register(&priv->iodev); + if (ret) + return ret; + + return 0; +} + +static __maybe_unused struct of_device_id pt100_dt_ids[] = { + { + .compatible = "zuehlke,pt100", + }, +}; + +static struct driver_d pt100_driver = { + .name = "pt100", + .probe = pt100_probe, + .of_compatible = DRV_OF_COMPAT(pt100_dt_ids), +}; +device_platform_driver(pt100_driver); -- 2.6.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC] iodevice support 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer ` (3 preceding siblings ...) 2015-12-08 9:05 ` [PATCH 4/4] misc: Add PT100 temperature sensor support Sascha Hauer @ 2015-12-10 6:24 ` Andrey Smirnov 2015-12-10 10:25 ` Sascha Hauer 4 siblings, 1 reply; 9+ messages in thread From: Andrey Smirnov @ 2015-12-10 6:24 UTC (permalink / raw) To: Sascha Hauer; +Cc: Barebox List On Tue, Dec 8, 2015 at 1:05 AM, Sascha Hauer <s.hauer@pengutronix.de> wrote: > The following is something like a mixture of Linux IIO and hwmon support > for the poor. I called it iodevice, but better names are appreciated. > A iodevice has multiple channels, each providing a value like for > example a temperature or a voltage. For each iodevice we provide device > parameters to access the values from the commandline. The C API for a > consumer consists of iochannel_get() to get a iochannel, and > iochannel_get_value() to actually read a value. > As drivers we currently have a LM75 temperature driver and a MCP342x ADC > driver. Also there is a PT100 driver which itself is a consumer of > another iodevice, a MCP342x in my case. This is more meant as an example > since the voltage to temperature conversion function is board specific. One feature that I would like to suggest/request in this API is ability to give custom names to individual channels via DT. AFAIU, as of right now, that the names are hard coded and assigned automatically by the subsystem. The reason why I am asking is because a fair number of use-cases for Barebox that I see is as a board verification tool and as such it is often used by people who may not be as familiar (if at all) with Barebox, it's code and how "in_value%d_%s" relates to the actual parameter reading they are trying to verify. Thanks, Andrey _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC] iodevice support 2015-12-10 6:24 ` [RFC] iodevice support Andrey Smirnov @ 2015-12-10 10:25 ` Sascha Hauer 0 siblings, 0 replies; 9+ messages in thread From: Sascha Hauer @ 2015-12-10 10:25 UTC (permalink / raw) To: Andrey Smirnov; +Cc: Barebox List On Wed, Dec 09, 2015 at 10:24:37PM -0800, Andrey Smirnov wrote: > On Tue, Dec 8, 2015 at 1:05 AM, Sascha Hauer <s.hauer@pengutronix.de> wrote: > > The following is something like a mixture of Linux IIO and hwmon support > > for the poor. I called it iodevice, but better names are appreciated. > > A iodevice has multiple channels, each providing a value like for > > example a temperature or a voltage. For each iodevice we provide device > > parameters to access the values from the commandline. The C API for a > > consumer consists of iochannel_get() to get a iochannel, and > > iochannel_get_value() to actually read a value. > > As drivers we currently have a LM75 temperature driver and a MCP342x ADC > > driver. Also there is a PT100 driver which itself is a consumer of > > another iodevice, a MCP342x in my case. This is more meant as an example > > since the voltage to temperature conversion function is board specific. > > > One feature that I would like to suggest/request in this API is > ability to give custom names to individual channels via DT. AFAIU, as > of right now, that the names are hard coded and assigned automatically > by the subsystem. > > The reason why I am asking is because a fair number of use-cases for > Barebox that I see is as a board verification tool and as such it is > often used by people who may not be as familiar (if at all) with > Barebox, it's code and how "in_value%d_%s" relates to the actual > parameter reading they are trying to verify. Note the device name is already configurable in device tree using aliases, Adding an foo_temperature = &lm75 will result in the device being named foo_temperature, so echo $foo_temperature.in_value0_mC will give you the temperature. Ok, that alone doesn't solve the problem. We still can only name devices and not individual channels. I'm just thinking about registering a single device named 'sensors' or similar and register all parameters under that device instead of their individual devices. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-12-11 7:37 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-12-08 9:05 [RFC] iodevice support Sascha Hauer 2015-12-08 9:05 ` [PATCH 1/4] misc: Add " Sascha Hauer 2015-12-10 20:42 ` Trent Piepho 2015-12-11 7:37 ` Sascha Hauer 2015-12-08 9:05 ` [PATCH 2/4] misc: Add basic LM75 temperature driver Sascha Hauer 2015-12-08 9:05 ` [PATCH 3/4] misc: Add Microchip MCP342x support Sascha Hauer 2015-12-08 9:05 ` [PATCH 4/4] misc: Add PT100 temperature sensor support Sascha Hauer 2015-12-10 6:24 ` [RFC] iodevice support Andrey Smirnov 2015-12-10 10:25 ` Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox