[PATCH 3/4] misc: Add Microchip MCP342x support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
---
 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@xxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux