summaryrefslogtreecommitdiffstats
path: root/drivers/aiodev
diff options
context:
space:
mode:
authorAndrey Gusakov <andrey.gusakov@cogentembedded.com>2018-04-06 19:33:24 +0300
committerSascha Hauer <s.hauer@pengutronix.de>2018-04-10 09:04:26 +0200
commit07329ded178ba6594e575a8d80cfa0448004e331 (patch)
tree58e391cece0af389bcd07191feeb1f2b0825fa45 /drivers/aiodev
parent9b8ed3d83581b2f8cce77442f2ad7eb649b896c3 (diff)
downloadbarebox-07329ded178ba6594e575a8d80cfa0448004e331.tar.gz
barebox-07329ded178ba6594e575a8d80cfa0448004e331.tar.xz
aiodev: mc13xxx: add adc support
Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/aiodev')
-rw-r--r--drivers/aiodev/Kconfig6
-rw-r--r--drivers/aiodev/Makefile1
-rw-r--r--drivers/aiodev/mc13xxx_adc.c234
3 files changed, 241 insertions, 0 deletions
diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig
index 1c5fabe01a..8bad946920 100644
--- a/drivers/aiodev/Kconfig
+++ b/drivers/aiodev/Kconfig
@@ -20,4 +20,10 @@ config LM75
help
Support for LM75 and similar devices
+config MC13XXX_ADC
+ tristate "MC13XXX ADC driver"
+ depends on MFD_MC13XXX
+ help
+ Support for MC13783, MC13892, MC34708 ADC
+
endif
diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile
index c3d2b081a9..1dcf6cdc4b 100644
--- a/drivers/aiodev/Makefile
+++ b/drivers/aiodev/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_AIODEV) += core.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_LM75) += lm75.o
+obj-$(CONFIG_MC13XXX_ADC) += mc13xxx_adc.o
diff --git a/drivers/aiodev/mc13xxx_adc.c b/drivers/aiodev/mc13xxx_adc.c
new file mode 100644
index 0000000000..4e72048169
--- /dev/null
+++ b/drivers/aiodev/mc13xxx_adc.c
@@ -0,0 +1,234 @@
+/*
+ * mc13xxx_adc
+ *
+ * Copyright (c) 2018 Zodiac Inflight Innovation
+ * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+ * Based on the code of analogous driver from Linux:
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <aiodev.h>
+#include <mfd/mc13xxx.h>
+#include <linux/err.h>
+
+#define MC13XXX_ADC0_LICELLCON (1 << 0)
+#define MC13XXX_ADC0_CHRGICON (1 << 1)
+#define MC13XXX_ADC0_BATICON (1 << 2)
+#define MC13XXX_ADC0_BUFEN (1 << 3)
+#define MC13XXX_ADC0_ADIN7SEL_DIE (1 << 4)
+#define MC13XXX_ADC0_ADIN7SEL_UID (2 << 4)
+#define MC13XXX_ADC0_ADREFEN (1 << 10)
+#define MC13XXX_ADC0_TSMOD0 (1 << 12)
+#define MC13XXX_ADC0_TSMOD1 (1 << 13)
+#define MC13XXX_ADC0_TSMOD2 (1 << 14)
+#define MC13XXX_ADC0_ADINC1 (1 << 16)
+#define MC13XXX_ADC0_ADINC2 (1 << 17)
+
+#define MC13XXX_ADC0_TSMOD_MASK (MC13XXX_ADC0_TSMOD0 | \
+ MC13XXX_ADC0_TSMOD1 | \
+ MC13XXX_ADC0_TSMOD2)
+
+#define MC13XXX_ADC0_CONFIG_MASK (MC13XXX_ADC0_TSMOD_MASK | \
+ MC13XXX_ADC0_LICELLCON | \
+ MC13XXX_ADC0_CHRGICON | \
+ MC13XXX_ADC0_BATICON)
+
+#define MC13XXX_ADC1_ADEN (1 << 0)
+#define MC13XXX_ADC1_RAND (1 << 1)
+#define MC13XXX_ADC1_ADSEL (1 << 3)
+#define MC13XXX_ADC1_CHAN0_SHIFT 5
+#define MC13XXX_ADC1_CHAN1_SHIFT 8
+#define MC13XXX_ADC1_ASC (1 << 20)
+#define MC13XXX_ADC1_ADTRIGIGN (1 << 21)
+
+struct mc13xx_adc_data {
+ struct mc13xxx *mc_dev;
+
+ struct aiodevice aiodev;
+ struct aiochannel *aiochan;
+};
+
+static inline struct mc13xx_adc_data *
+to_mc13xx_adc_data(struct aiochannel *chan)
+{
+ return container_of(chan->aiodev, struct mc13xx_adc_data, aiodev);
+}
+
+int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx,
+ unsigned int channel, unsigned int *sample)
+{
+ int i;
+ int timeout = 100;
+ u32 adc0, adc1, old_adc0;
+
+ mc13xxx_reg_read(mc13xxx, MC13783_REG_ADC(0), &old_adc0);
+
+ adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2 | MC13XXX_ADC0_BUFEN;
+ adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
+
+ /* Channels mapped through ADIN7:
+ * 7 - General purpose ADIN7
+ * 16 - UID
+ * 17 - Die temperature */
+ if (channel > 7 && channel < 16) {
+ adc1 |= MC13XXX_ADC1_ADSEL;
+ } else if (channel == 16) {
+ adc0 |= MC13XXX_ADC0_ADIN7SEL_UID;
+ channel = 7;
+ } else if (channel == 17) {
+ adc0 |= MC13XXX_ADC0_ADIN7SEL_DIE;
+ channel = 7;
+ }
+
+ adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
+ adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13XXX_ADC1_RAND;
+
+ mc13xxx_reg_write(mc13xxx, MC13783_REG_ADC(0), adc0);
+ mc13xxx_reg_write(mc13xxx, MC13783_REG_ADC(1), adc1);
+
+ /* wait for completion. ASC will set to zero */
+ do {
+ mc13xxx_reg_read(mc13xxx, MC13783_REG_ADC(1), &adc1);
+ } while ((adc1 & MC13XXX_ADC1_ASC) && (--timeout > 0));
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ for (i = 0; i < 4; ++i) {
+ mc13xxx_reg_read(mc13xxx,
+ MC13783_REG_ADC(2), &sample[i]);
+ }
+
+ return 0;
+}
+
+static int mc13xx_adc_read(struct aiochannel *chan, int *val)
+{
+ int i;
+ int ret;
+ int mc_type;
+ unsigned int sample[4];
+ struct mc13xx_adc_data *mc13xxx_adc;
+ int acc = 0;
+ int index = chan->index;
+
+ mc13xxx_adc = to_mc13xx_adc_data(chan);
+ mc_type = mc13xxx_type(mc13xxx_adc->mc_dev);
+
+ /* add offset for all 8 channel devices becouse t and UID
+ * inputs are mapped to channels 16 and 17 */
+ if ((mc_type != MC13783_TYPE) && (chan->index > 7))
+ index += 8;
+
+ ret = mc13xxx_adc_do_conversion(mc13xxx_adc->mc_dev, index, sample);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 4; i++) {
+ acc += (sample[i] >> 2 & 0x3ff);
+ acc += (sample[i] >> 14 & 0x3ff);
+ }
+ /* div 8 */
+ acc = acc >> 3;
+
+ if (index == 16) {
+ /* UID */
+ if (mc_type == MC13892_TYPE) {
+ /* MC13892 have 1/2 divider
+ * input range is [0, 4.800V] */
+ acc = DIV_ROUND_CLOSEST(acc * 4800, 1024);
+ } else {
+ /* MC13783 have 0.9 divider
+ *input range is [0, 2.555V] */
+ acc = DIV_ROUND_CLOSEST(acc * 2555, 1024);
+ }
+ } else if (index == 17) {
+ /* Die temperature */
+ if (mc_type == MC13892_TYPE) {
+ /* MC13892:
+ * Die Temperature Read Out Code at 25C 680
+ * Temperature change per LSB +0.4244C */
+ acc = DIV_ROUND_CLOSEST(-2635920 + acc * 4244, 10);
+ } else {
+ /* MC13783:
+ * Die Temperature Read Out Code at 25C 282
+ * Temperature change per LSB -1.14C */
+ acc = 346480 - 1140 * acc;
+ }
+ } else {
+ /* GP input
+ * input range is [0, 2.3V], value has 10 bits */
+ acc = DIV_ROUND_CLOSEST(acc * 2300, 1024);
+ }
+
+ *val = acc;
+err:
+ return ret;
+}
+
+int mc13xxx_adc_probe(struct device_d *dev, struct mc13xxx *mc_dev)
+{
+ int i;
+ int ret;
+ int chans;
+ struct mc13xx_adc_data *mc13xxx_adc;
+
+ mc13xxx_adc = xzalloc(sizeof(*mc13xxx_adc));
+
+ if (mc13xxx_type(mc_dev) == MC13783_TYPE) {
+ /* mc13783 has 16 channels */
+ chans = 16 + 2;
+ } else {
+ chans = 8 + 2;
+ }
+
+ mc13xxx_adc->mc_dev = mc_dev;
+ mc13xxx_adc->aiodev.num_channels = chans;
+ mc13xxx_adc->aiochan = xmalloc(mc13xxx_adc->aiodev.num_channels *
+ sizeof(*mc13xxx_adc->aiochan));
+ mc13xxx_adc->aiodev.hwdev = dev;
+ mc13xxx_adc->aiodev.channels =
+ xmalloc(mc13xxx_adc->aiodev.num_channels *
+ sizeof(mc13xxx_adc->aiodev.channels[0]));
+ /* all channels are voltage inputs, expect last one */
+ for (i = 0; i < chans - 1; i++) {
+ mc13xxx_adc->aiodev.channels[i] = &mc13xxx_adc->aiochan[i];
+ mc13xxx_adc->aiochan[i].unit = "mV";
+ }
+ /* temperature input */
+ mc13xxx_adc->aiodev.channels[i] = &mc13xxx_adc->aiochan[i];
+ mc13xxx_adc->aiochan[i].unit = "mC";
+
+ mc13xxx_adc->aiodev.read = mc13xx_adc_read;
+
+ ret = aiodevice_register(&mc13xxx_adc->aiodev);
+ if (!ret)
+ goto done;
+
+ dev_err(dev, "Failed to register AIODEV: %d\n", ret);
+ kfree(mc13xxx_adc);
+done:
+ return ret;
+}