Ver Fonte

修改adc驱动,支持多个通道同时采值

huangliang há 2 anos atrás
pai
commit
57bf7635a9
1 ficheiros alterados com 177 adições e 284 exclusões
  1. 177 284
      linux/drivers/iio/adc/ark1668e-adc.c

+ 177 - 284
linux/drivers/iio/adc/ark1668e-adc.c

@@ -1,7 +1,7 @@
 #include <linux/kernel.h>
-#include <linux/module.h> 
-#include <linux/platform_device.h> 
-#include <linux/io.h> 
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
@@ -15,13 +15,8 @@
 #include <linux/clk.h>
 #include <linux/completion.h>
 
-
-enum ADC_MODE{
-	ARK_ADC_MODE_NONE,
-	ARK_ADC_MODE_BATTERY,
-	ARK_ADC_MODE_KEY,
-	ARK_ADC_MODE_END,
-};
+#define ADC_CHANNEL_MAX_NUMS		8
+#define ADC_ARK1668E_CHANNEL_NUM	4
 
 struct ark_adc {
 	struct ark_adc_data	*data;
@@ -31,9 +26,8 @@ struct ark_adc {
 	struct clk			*clk;
 	struct completion	completion;
 	u32					irq;
-	u32 				mode[4];	//adc channel[n] mode(battery, key, ...)
-	u32					value1[4];	//adc channel[n] temp value
-	u32					value2[4];	//adc channel[n] final value
+	bool				pull_down;
+	u32					value[ADC_CHANNEL_MAX_NUMS];
 };
 
 struct ark_adc_data {
@@ -46,40 +40,29 @@ struct ark_adc_data {
 	void (*start_conv)(struct ark_adc *info, unsigned long addr);
 };
 
-#define  BAT_CHANNEL           		1
-#define  TOUCHPAN_CHANNEL      		2
-#define  AUX0_CHANNEL          		3
-#define  AUX1_CHANNEL          		4
-#define  AUX2_CHANNEL          		5
-#define  AUX3_CHANNEL          		6
-
-#define  AUX0_START_INT       		(1<<0)
-#define  AUX0_STOP_INT        		(1<<1)
-#define  AUX0_VALUE_INT       		(1<<2)
-#define  AUX1_START_INT       		(1<<3)
-#define  AUX1_STOP_INT        		(1<<4)
-#define  AUX1_VALUE_INT       		(1<<5)
-#define  AUX2_START_INT       		(1<<6)
-#define  AUX2_STOP_INT        		(1<<7)
-#define  AUX2_VALUE_INT       		(1<<8)
-#define  AUX3_START_INT       		(1<<9)
-#define  AUX3_STOP_INT        		(1<<10)
-#define  AUX3_VALUE_INT       		(1<<11)
-
-#define	AUX_START_INT(ch)			(1 << ((ch)*3 + 0))
-#define	AUX_STOP_INT(ch)       		(1 << ((ch)*3 + 1))
-#define AUX_VALUE_INT(ch)   		(1 << ((ch)*3 + 2))
-
-#define ARK1668_ADC_CHANNEL_MAX_NUMS	4
-#define ARK1668E_ADC_CHANNEL_MAX_NUMS	4
-#define ARKN141_ADC_CHANNEL_MAX_NUMS	2
+#define ADC_CTL0		0x0
+#define ADC_CTL1		0x4
+#define ADC_IMR			0x8
+#define ADC_STA			0xc
+#define ADC_INTER		0x10
+#define ADC_DBNCNT		0x14
+#define ADC_AUX10		0x18
+#define ADC_AUX32		0x1c
+#define ADC_AUX54		0x20
+#define ADC_AUX76		0x24
+
+#define	AUX_START_INT(ch)			(1 << ((ch) * 3 + 0))
+#define	AUX_STOP_INT(ch)       		(1 << ((ch) * 3 + 1))
+#define AUX_VALUE_INT(ch)   		(1 << ((ch) * 3 + 2))
+
+#define	AUX_START_INTMASK(ch)			(1 << ((ch) + 0))
+#define	AUX_STOP_INTMASK(ch)       		(1 << ((ch) + 8))
+#define AUX_VALUE_INTMASK(ch)   		(1 << ((ch) + 16))
+
 #define ARK_ADC_DATX_MASK_12BIT			0xFFF
 #define ARK_ADC_DATX_MASK_10BIT			0x3FF
-#define ARK_BATTERY_VOLTAGE_REF			3300// 3.3V
-#define ARK_ADC_TIMEOUT					(msecs_to_jiffies(100))
-#define ARK_ADC_USE_PULL_UP_REGISTER	//ADC connect pull up register, have interrupe when voltage in 0V ~ 3V
-//#define ARK_ADC_USE_PULL_DOWM_REGISTER	//ADC connect pull dowm register, have interrupt when voltage in 0.3V ~ 3.3V
-
+#define ARK_ADC_VOLTAGE_REF				3300// 3.3V
+#define ARK_ADC_TIMEOUT					(msecs_to_jiffies(20))
 
 
 #define ADC_CHANNEL(_index, _id) {		\
@@ -98,22 +81,60 @@ static const struct iio_chan_spec ark_adc_iio_channels[] = {
 	ADC_CHANNEL(3, "adc3"),
 };
 
+static u32 ark1668e_get_aux_value(struct ark_adc *info, unsigned int ch)
+{
+	u32 val;
+
+	val = readl(info->adc_base + ADC_AUX10 + ch / 2 * 4);
+	if (ch & 1)
+		val = (val >> 16) & 0xfff;
+	else
+		val = val & 0xfff;
+
+	return val;
+}
+
+static void ark1668e_adc_disable_channel(struct ark_adc *info, unsigned int ch)
+{
+	u32 val;
+
+	if (ch >= ADC_CHANNEL_MAX_NUMS)
+		return;
+
+	val = readl(info->adc_base + ADC_CTL0);
+	val &= ~(1 << ch);
+	writel(val, info->adc_base + ADC_CTL0);
+}
+
+static void ark1668e_adc_enable_channel(struct ark_adc *info, unsigned int ch)
+{
+	u32 val;
+
+	if (ch >= ADC_CHANNEL_MAX_NUMS)
+		return;
+
+	val = readl(info->adc_base + ADC_CTL0);
+	val |= 1 << ch;
+	writel(val, info->adc_base + ADC_CTL0);
+}
+
 static int ark_adc_reg_access(struct iio_dev *indio_dev,
 			      unsigned reg, unsigned writeval,
 			      unsigned *readval)
 {
-	//struct ark_adc *info = iio_priv(indio_dev);
-	//printk(KERN_ALERT "### ark_adc_reg_access\n");
+	struct ark_adc *info = iio_priv(indio_dev);
+	u32 val;
 
-	if (!readval)
-		return -EINVAL;
-	
-	//*readval = readl(info->adc_base + reg);
+	if (readval != NULL) {
+		val = readl(info->adc_base + reg);
+		*readval = val;
+	} else {
+		writel(val, info->adc_base + reg);
+	}
 
 	return 0;
 }
 
-
 static int ark_read_raw(struct iio_dev *indio_dev,
 				struct iio_chan_spec const *chan,
 				int *val,
@@ -123,28 +144,25 @@ static int ark_read_raw(struct iio_dev *indio_dev,
 	struct ark_adc *info = iio_priv(indio_dev);
 	unsigned long timeout;
 	int ret = 0;
-	
+
 	mutex_lock(&indio_dev->mlock);
 	reinit_completion(&info->completion);
-	
+
 	/* Select the channel to be used and Trigger conversion */
 	if (info->data->start_conv)
 		info->data->start_conv(info, chan->address);
 
 	timeout = wait_for_completion_timeout(&info->completion, ARK_ADC_TIMEOUT);
-	
-	if (timeout == 0) {
-		dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
-		if (info->data->init_hw)
-			info->data->init_hw(info);
-		ret = -ETIMEDOUT;
-	} else {
-		*val = info->value2[chan->address];
-		*val2 = 0;
-		ret = IIO_VAL_INT;
-	}
+	if (timeout == 0)
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+
+	*val = info->value[chan->address];
+	*val2 = 0;
+	ret = IIO_VAL_INT;
+
+	ark1668e_adc_disable_channel(info, chan->address);
+
 	mutex_unlock(&indio_dev->mlock);
-	//printk(KERN_ALERT "### READ :%d\n", readl(info->adc_base+0x14));
 
 	return ret;
 }
@@ -158,60 +176,63 @@ static void ark1668e_adc_init_hw(struct ark_adc *info)
 {
 	unsigned int val;
 
-	val = readl(info->sys_base+0x74);
-	val |= (1 << 2);
-	writel(val, info->sys_base+0x74);
-
-
-	val = readl(info->adc_base+0x4);
-	val |= (0x3f<<16);
-	writel(val, info->adc_base+0x4);
-
-	val = readl(info->adc_base+0x0);
-	val &=~(1<<31);
-	writel(val, info->adc_base+0x0);
-	val |=1<<26;
-	writel(val, info->adc_base+0x0);
-	val |=0xff;
-	writel(val, info->adc_base+0x0);
-	val |=0xff << 16;
-	writel(val, info->adc_base+0x0);
-
-	val = readl(info->adc_base+0x10);
-	val |= 0xffff;
-	writel(val, info->adc_base+0x10);
-
-	val = readl(info->adc_base+0x08);
-	val &=~0xffffff;
-	writel(val, info->adc_base+0x08);
-
+	//soft reset
+	val = readl(info->sys_base + 0x74);
+	val &= ~(1 << 2);
+	writel(val, info->sys_base + 0x74);
+	udelay(1);
+	val |= 1 << 2;
+	writel(val, info->sys_base + 0x74);
+	udelay(1);
+
+	val = readl(info->adc_base + ADC_CTL1);
+	val |= 0x3f << 16;
+	writel(val, info->adc_base + ADC_CTL1);
+
+	val = readl(info->adc_base + ADC_CTL0);
+	val &= ~(1 << 31);
+	val &= ~0xff;
+	val |= 0xff << 16;
+	writel(val, info->adc_base + ADC_CTL0);
+
+	//set transform interval 10ms
+	val = readl(info->adc_base + ADC_INTER);
+	val &= ~0xffff;;
+	val |= 10000;
+	writel(val, info->adc_base + ADC_INTER);
+
+	//enable interrupt
+	writel(0x0, info->adc_base + ADC_IMR);
 }
 
 static void ark1668e_adc_exit_hw(struct ark_adc *info)
 {
 	//printk(KERN_ALERT "### exit\n");
-	
+
 }
 
 static void ark1668e_adc_start_conv(struct ark_adc *info, unsigned long addr)
 {
-	u32 value = info->value1[addr];
+	int i;
+
+	if (addr >= ADC_CHANNEL_MAX_NUMS)
+		return;
 
-#ifdef ARK_ADC_USE_PULL_UP_REGISTER
-	if((value >= 0) && (value <= 3000))
-		info->value2[addr] = value;
+	if (info->pull_down)
+		info->value[addr] = 0;
 	else
-		info->value2[addr] = ARK_BATTERY_VOLTAGE_REF;
-	info->value1[addr] = ARK_BATTERY_VOLTAGE_REF;
-#else
-	info->value2[addr] = value;
-#endif
-	
-	complete(&info->completion);
+		info->value[addr] = ARK_ADC_VOLTAGE_REF;
+
+	for (i = 0; i < ADC_CHANNEL_MAX_NUMS; i++) {
+		if (i == addr)
+			ark1668e_adc_enable_channel(info, i);
+		else
+			ark1668e_adc_disable_channel(info, i);
+	}
 }
 
 static const struct ark_adc_data ark1668e_adc_data = {
-	.num_channels	= ARK1668E_ADC_CHANNEL_MAX_NUMS,
+	.num_channels	= ADC_ARK1668E_CHANNEL_NUM,
 	.mask			= ARK_ADC_DATX_MASK_12BIT,
 	.init_hw		= ark1668e_adc_init_hw,
 	.exit_hw		= ark1668e_adc_exit_hw,
@@ -231,160 +252,40 @@ MODULE_DEVICE_TABLE(of, ark_adc_match);
 static struct ark_adc_data *ark_adc_get_data(struct platform_device *pdev)
 {
 	const struct of_device_id *match = of_match_node(ark_adc_match, pdev->dev.of_node);
-	
+
 	return (struct ark_adc_data *)match->data;
 }
 
 static irqreturn_t ark_adc_intr_handler(int irq, void *dev_id)
 {
 	struct ark_adc *info = (struct ark_adc *)dev_id;
-	u32 val,range,status;
-	//printk(KERN_ALERT "++++++ark_adc_intr_handler entry\n");
-	
-	status = readl(info->adc_base+0x0c);
-
-	if(status&(1<<23))
-	{
-		val = readl(info->adc_base+0x24);
-		//printk(KERN_ALERT "aux7 value is 0x%x\r\n",(val>>16)&0xfff);
-		val = ~(1<<23);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<22))
-	{
-		val = ~(1<<22);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<21))
-	{
-		val = ~(1<<21);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<20))
-	{
-		val = readl(info->adc_base+0x24);
-		//printk(KERN_ALERT "aux6 value is 0x%x\r\n",(val)&0xfff);
-		val = ~(1<<20);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<19))
-	{
-		val = ~(1<<19);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<18))
-	{
-		val = ~(1<<18);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<17))
-	{
-		val = readl(info->adc_base+0x20);
-		//printk(KERN_ALERT "aux5 value is 0x%x\r\n",(val>>16)&0xfff);
-		val = ~(1<<17);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<16))
-	{
-		val = ~(1<<16);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<15))
-	{
-		val = ~(1<<15);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<14))
-	{
-		val = readl(info->adc_base+0x20);
-		//printk(KERN_ALERT "aux4 value is 0x%x\r\n",(val)&0xfff);
-		val = ~(1<<14);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<13))
-	{
-		val = ~(1<<13);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<12))
-	{
-		val = ~(1<<12);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<11))
-	{
-		val = readl(info->adc_base+0x1c);
-		range = ((info->data->mask==ARK_ADC_DATX_MASK_12BIT) ? 4096 : 1024);
-		info->value1[3] = ((val>>16)&0xfff) * ARK_BATTERY_VOLTAGE_REF / range;
-		//printk(KERN_ALERT "aux3 value = %d \n",(val>>16)&0xfff);
-		val = ~(1<<11);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<10))
-	{
-		val = ~(1<<10);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<9))
-	{
-		val = ~(1<<9);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<8))
-	{
-		val = readl(info->adc_base+0x1c);
-		range = ((info->data->mask==ARK_ADC_DATX_MASK_12BIT) ? 4096 : 1024);
-		info->value1[2] = ((val)&0xfff) * ARK_BATTERY_VOLTAGE_REF / range;
-		val = ~(1<<8);
-		writel(val, info->adc_base+0x0c);
-		
-	}
-	if(status&(1<<7))
-	{
-		val = ~(1<<7);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<6))
-	{
-		val = ~(1<<6);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<5))
-	{
-		val = readl(info->adc_base+0x18);
-		range = ((info->data->mask==ARK_ADC_DATX_MASK_12BIT) ? 4096 : 1024);
-		info->value1[1] = ((val>>16)&0xfff) * ARK_BATTERY_VOLTAGE_REF / range;
-		val = ~(1<<5);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<4))
-	{
-		val = ~(1<<4);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<3))
-	{
-		val = ~(1<<3);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<2))
-	{
-		val = readl(info->adc_base+0x18);
-		range = ((info->data->mask==ARK_ADC_DATX_MASK_12BIT) ? 4096 : 1024);
-		info->value1[0] = ((val)&0xfff) * ARK_BATTERY_VOLTAGE_REF / range;
-		//printk(KERN_ALERT "aux0 value = 0x%x,info->value1[0] = %d\n",val&0xfff,info->value1[0]);
-		val = ~(1<<2);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<1))
-	{
-		val = ~(1<<1);
-		writel(val, info->adc_base+0x0c);
-	}
-	if(status&(1<<0))
-	{
-		val = ~(1<<0);
-		writel(val, info->adc_base+0x0c);
+	u32 val, range, status;
+	int i;
+
+	status = readl(info->adc_base + ADC_STA);
+	writel(0, info->adc_base + ADC_STA);
+
+	//printk(KERN_ALERT "++++++ark_adc_intr_handler status=0x%x\n", status);
+	for (i = 0; i < ADC_CHANNEL_MAX_NUMS; i++) {
+		if (status & AUX_START_INT(i)) {
+			//printk(KERN_ALERT "ch%d start int.\n", i);
+		}
+
+		if (status & AUX_STOP_INT(i)) {
+			//printk(KERN_ALERT "ch%d stop int.\n", i);
+			if (info->pull_down)
+				info->value[i] = 0;
+			else
+				info->value[i] = ARK_ADC_VOLTAGE_REF;
+		}
+
+		if (status & AUX_VALUE_INT(i)) {
+			//printk(KERN_ALERT "ch%d value int.\n", i);
+			val = ark1668e_get_aux_value(info, i);
+			range = (info->data->mask == ARK_ADC_DATX_MASK_12BIT) ? 4096 : 1024;
+			info->value[i] = val * ARK_ADC_VOLTAGE_REF / range;
+			complete(&info->completion);
+		}
 	}
 
 	return IRQ_HANDLED;
@@ -392,18 +293,19 @@ static irqreturn_t ark_adc_intr_handler(int irq, void *dev_id)
 
 static int ark_adc_probe(struct platform_device *pdev)
 {
-	//struct device_node *np = pdev->dev.of_node;
+	struct device_node *np = pdev->dev.of_node;
 	struct iio_dev *indio_dev = NULL;
 	struct ark_adc *info = NULL;
 	struct resource	*res = NULL;
 	int ret = -ENODEV;
+	int i;
 
 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ark_adc));
 	if (!indio_dev) {
 		dev_err(&pdev->dev, "failed allocating iio device\n");
 		return -ENOMEM;
 	}
-	
+
 	info = iio_priv(indio_dev);
 
 	//get data
@@ -415,7 +317,7 @@ static int ark_adc_probe(struct platform_device *pdev)
 
 	//adc resource
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	info->adc_base= devm_ioremap_resource(&pdev->dev, res);
+	info->adc_base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(info->adc_base)) {
 		ret = PTR_ERR(info->adc_base);
 		goto err_resource;
@@ -427,7 +329,7 @@ static int ark_adc_probe(struct platform_device *pdev)
 		ret = PTR_ERR(res);
 		goto err_resource;
     }
-	//info->sys_base = devm_ioremap_resource(&pdev->dev, res);
+
 	info->sys_base = ioremap(res->start, resource_size(res));
 	if (IS_ERR(info->sys_base)) {
 		ret = PTR_ERR(info->sys_base);
@@ -442,7 +344,6 @@ static int ark_adc_probe(struct platform_device *pdev)
 		goto err_irq;
 	}
 
-	//ret = request_irq(info->irq, ark_adc_intr_handler, 0, dev_name(&pdev->dev), info);
     ret = devm_request_irq(
                 &pdev->dev,
                 info->irq,
@@ -456,9 +357,16 @@ static int ark_adc_probe(struct platform_device *pdev)
 		goto err_irq;
 	}
 
+	info->pull_down = of_property_read_bool(np, "pull-down");
+	for (i = 0; i < ADC_CHANNEL_MAX_NUMS; i++) {
+		if (info->pull_down)
+			info->value[i] = 0;
+		else
+			info->value[i] = ARK_ADC_VOLTAGE_REF;
+	}
+
 	info->dev = &pdev->dev;
-	info->value1[0] = info->value1[1] = info->value1[2] = info->value1[3] =ARK_BATTERY_VOLTAGE_REF;
-	
+
 	platform_set_drvdata(pdev, indio_dev);
 	indio_dev->name = dev_name(&pdev->dev);
 	indio_dev->dev.parent = &pdev->dev;
@@ -483,24 +391,16 @@ static int ark_adc_probe(struct platform_device *pdev)
 	if (info->data->init_hw)
 		info->data->init_hw(info);
 
-	printk(KERN_ALERT "++++++ark1668e_adc_probe success\n");
+	//printk(KERN_ALERT "++++++ark1668e_adc_probe success\n");
 	return 0;
 
-//err_iio:
-	iio_device_unregister(indio_dev);
 err_iio_device_register:
-	//free_irq(info->irq, info);
-	//devm_free_irq(&pdev->dev, info->irq, info);
-err_irq: 
-	//if(info->sys_base)
-	//	devm_iounmap(&pdev->dev, info->sys_base);
+err_irq:
+	if(info->sys_base)
+		devm_iounmap(&pdev->dev, info->sys_base);
 err_resource:
-	//if(info->adc_base)
-	//	devm_iounmap(&pdev->dev, info->adc_base);
 err_devm_iio_device_alloc:
-	//if(indio_dev)
-	//	devm_iio_device_free(&pdev->dev, indio_dev);
-	
+
 	return ret;
 }
 
@@ -511,17 +411,10 @@ static int ark_adc_remove(struct platform_device *pdev)
 
 	if(!info)
 		return -ENODEV;
-	
-	//device_for_each_child(&indio_dev->dev, NULL, ark_adc_remove_devices);
+
 	iio_device_unregister(indio_dev);
-	//free_irq(info->irq, info);
-	//devm_free_irq(&pdev->dev, info->irq, info);
-	//if(info->sys_base)
-	//	devm_iounmap(&pdev->dev, info->sys_base);
-	//if(info->adc_base)
-	//	devm_iounmap(&pdev->dev, info->adc_base);
-	//if(indio_dev)
-	//	devm_iio_device_free(&pdev->dev, indio_dev);
+	if(info->sys_base)
+		devm_iounmap(&pdev->dev, info->sys_base);
 
 	return 0;
 }