diff --git a/Documentation/devicetree/bindings/mfd/ingenic,jz4740-adc.txt b/Documentation/devicetree/bindings/mfd/ingenic,jz4740-adc.txt new file mode 100644 index 00000000000000..3112a7f46fe162 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ingenic,jz4740-adc.txt @@ -0,0 +1,22 @@ +* Ingenic JZ4740 ADC + +Required properties: +- compatible: should be "ingenic,jz4740-adc" +- reg: Should contain the address & length of registers +- interrupts: Should specify the interrupt provided by parent. +- clocks: Should contain a single clock specifier for the ADC clock. +- clock-names: Should be "adc". + +Example: + +adc@0x10070000 { + compatible = "ingenic,jz4740-adc"; + reg = <0x10070000 0x30>; + + interrupt-parent = <&intc>; + interrupts = <18>; + + clocks = <&cgu JZ4780_CLK_SADC>; + clock-names = "adc"; +}; + diff --git a/arch/mips/boot/dts/jz4780.dtsi b/arch/mips/boot/dts/jz4780.dtsi index 07e5845aed3204..641bc61ef21679 100644 --- a/arch/mips/boot/dts/jz4780.dtsi +++ b/arch/mips/boot/dts/jz4780.dtsi @@ -524,6 +524,17 @@ }; }; }; + + adc@0x10070000 { + compatible = "ingenic,jz4780-adc"; + reg = <0x10070000 0x30>; + + interrupt-parent = <&intc>; + interrupts = <18>; + + clocks = <&cgu JZ4780_CLK_SADC>; + clock-names = "adc"; + }; }; ahb2 { diff --git a/arch/mips/include/asm/mach-jz4740/irq.h b/arch/mips/include/asm/mach-jz4740/irq.h index 7e4ec21e7fb7fa..483c3f14864a24 100644 --- a/arch/mips/include/asm/mach-jz4740/irq.h +++ b/arch/mips/include/asm/mach-jz4740/irq.h @@ -59,8 +59,9 @@ #define JZ4740_IRQ_INTC_GPIO(x) (JZ4740_IRQ_GPIO0 - (x)) #define JZ4740_IRQ_GPIO(x) (JZ4740_IRQ(NR_INTC_IRQS + 16) + (x)) -#define JZ4740_IRQ_ADC_BASE JZ4740_IRQ(NR_INTC_IRQS + 144) +#define JZ4740_IRQ_ADC_BASE JZ4740_IRQ(256) +#define JZ4740_IRQ_ADC_COUNT 6 -#define NR_IRQS (JZ4740_IRQ_ADC_BASE + 6) +#define NR_IRQS (JZ4740_IRQ_ADC_BASE + JZ4740_IRQ_ADC_COUNT) #endif diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5286d7ce1f9eb7..a2f8a9cbd75736 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -597,7 +597,7 @@ config SENSORS_IT87 config SENSORS_JZ4740 tristate "Ingenic JZ4740 SoC ADC driver" - depends on MACH_JZ4740 && MFD_JZ4740_ADC + depends on (MACH_JZ4740 || MACH_JZ4780) && MFD_JZ4740_ADC help If you say yes here you get support for reading adc values from the ADCIN pin on Ingenic JZ4740 SoC based boards. diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c index 7488e36809c876..7674359b4aa7a9 100644 --- a/drivers/hwmon/jz4740-hwmon.c +++ b/drivers/hwmon/jz4740-hwmon.c @@ -26,6 +26,8 @@ #include #include +#include +#include struct jz4740_hwmon { void __iomem *base; @@ -38,6 +40,7 @@ struct jz4740_hwmon { struct completion read_completion; struct mutex lock; + enum jz4740_adc_version version; }; static ssize_t jz4740_hwmon_show_name(struct device *dev, @@ -59,6 +62,8 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev, { struct jz4740_hwmon *hwmon = dev_get_drvdata(dev); struct completion *completion = &hwmon->read_completion; + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + unsigned int channel = 1; long t; unsigned long val; int ret; @@ -70,6 +75,18 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev, enable_irq(hwmon->irq); hwmon->cell->enable(to_platform_device(dev)); + if (hwmon->version >= JZ_ADC_JZ4780) { + switch(attr->index) { + case 1: channel = JZ_ADC_CONFIG_AUX1_EN; + break; + case 2: channel = JZ_ADC_CONFIG_AUX2_EN; + break; + default: dev_err(dev, "invalid channel attribute"); + } + + jz4740_adc_set_config(dev->parent, JZ_ADC_CONFIG_CMD_MASK , channel); + } + t = wait_for_completion_interruptible_timeout(completion, HZ); if (t > 0) { @@ -89,11 +106,17 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev, } static DEVICE_ATTR(name, S_IRUGO, jz4740_hwmon_show_name, NULL); -static DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL); +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL , 1); +#ifdef CONFIG_MACH_JZ4780 +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL , 2); +#endif static struct attribute *jz4740_hwmon_attributes[] = { &dev_attr_name.attr, - &dev_attr_in0_input.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, +#ifdef CONFIG_MACH_JZ4780 + &sensor_dev_attr_in1_input.dev_attr.attr, +#endif NULL }; @@ -105,6 +128,7 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) { int ret; struct jz4740_hwmon *hwmon; + struct jz4740_adc *adc; struct resource *mem; hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); @@ -150,6 +174,9 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) goto err_remove_file; } + adc = platform_get_drvdata(to_platform_device(pdev->dev.parent)); + hwmon->version = adc->version; + return 0; err_remove_file: diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1456ea70bbc7c5..ae5dcbe0409deb 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -312,7 +312,7 @@ config MFD_JZ4740_ADC bool "Janz JZ4740 ADC core" select MFD_CORE select GENERIC_IRQ_CHIP - depends on MACH_JZ4740 + depends on (MACH_JZ4740 || MACH_JZ4780) help Say yes here if you want support for the ADC unit in the JZ4740 SoC. This driver is necessary for jz4740-battery and jz4740-hwmon driver. diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 7a51c0d0d4f1f5..ae2ad8ad0e2f2a 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -16,6 +16,8 @@ */ #include +#include +#include #include #include #include @@ -39,6 +41,8 @@ #define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10 #define JZ_REG_ADC_BATTERY_BASE 0x1c #define JZ_REG_ADC_HWMON_BASE 0x20 +#define JZ_REG_ADC_CMD 0x24 +#define JZ_REG_ADC_CLKDIV 0x28 #define JZ_ADC_ENABLE_TOUCH BIT(2) #define JZ_ADC_ENABLE_BATTERY BIT(1) @@ -52,19 +56,6 @@ enum { JZ_ADC_IRQ_PENDOWN, }; -struct jz4740_adc { - struct resource *mem; - void __iomem *base; - - int irq; - struct irq_chip_generic *gc; - - struct clk *clk; - atomic_t clk_ref; - - spinlock_t lock; -}; - static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip_generic *gc = irq_desc_get_handler_data(desc); @@ -202,12 +193,19 @@ static const struct mfd_cell jz4740_adc_cells[] = { }, }; +static struct of_device_id jz4740_adc_of_match[] = { + { .compatible = "ingenic,jz4740-adc", .data = (void *)JZ_ADC_JZ4740 }, + { .compatible = "ingenic,jz4780-adc", .data = (void *)JZ_ADC_JZ4780 }, + { }, +}; + static int jz4740_adc_probe(struct platform_device *pdev) { struct irq_chip_generic *gc; struct irq_chip_type *ct; struct jz4740_adc *adc; struct resource *mem_base; + const struct of_device_id *match; int ret; int irq_base; @@ -217,6 +215,9 @@ static int jz4740_adc_probe(struct platform_device *pdev) return -ENOMEM; } + match = of_match_device(of_match_ptr(jz4740_adc_of_match), &pdev->dev); + adc->version = (enum jz4740_adc_version)match->data; + adc->irq = platform_get_irq(pdev, 0); if (adc->irq < 0) { ret = adc->irq; @@ -224,7 +225,7 @@ static int jz4740_adc_probe(struct platform_device *pdev) return ret; } - irq_base = platform_get_irq(pdev, 1); + irq_base = irq_alloc_descs(-1, 0, JZ4740_IRQ_ADC_COUNT, 0); if (irq_base < 0) { dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base); return irq_base; @@ -259,7 +260,9 @@ static int jz4740_adc_probe(struct platform_device *pdev) } spin_lock_init(&adc->lock); - atomic_set(&adc->clk_ref, 0); + + atomic_set(&adc->clk_ref, 1); + clk_prepare_enable(adc->clk); platform_set_drvdata(pdev, adc); @@ -324,6 +327,7 @@ static struct platform_driver jz4740_adc_driver = { .remove = jz4740_adc_remove, .driver = { .name = "jz4740-adc", + .of_match_table = jz4740_adc_of_match, .owner = THIS_MODULE, }, }; diff --git a/include/linux/jz4740-adc.h b/include/linux/jz4740-adc.h index 8184578fbfa456..3e85bbdf546d33 100644 --- a/include/linux/jz4740-adc.h +++ b/include/linux/jz4740-adc.h @@ -23,10 +23,32 @@ int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val); #define JZ_ADC_CONFIG_SAMPLE_NUM_MASK (0x7 << 10) #define JZ_ADC_CONFIG_CLKDIV_MASK (0xf << 5) #define JZ_ADC_CONFIG_BAT_MB BIT(4) +#define JZ_ADC_CONFIG_CMD_MASK 0x3 +#define JZ_ADC_CONFIG_AUX1_EN 0x1 +#define JZ_ADC_CONFIG_AUX2_EN 0x2 #define JZ_ADC_CONFIG_DNUM(dnum) ((dnum) << 16) #define JZ_ADC_CONFIG_XYZ_OFFSET(dnum) ((xyz) << 13) #define JZ_ADC_CONFIG_SAMPLE_NUM(x) ((x) << 10) #define JZ_ADC_CONFIG_CLKDIV(div) ((div) << 5) +enum jz4740_adc_version { + JZ_ADC_JZ4740, + JZ_ADC_JZ4780, +}; + +struct jz4740_adc { + struct resource *mem; + void __iomem *base; + + int irq; + struct irq_chip_generic *gc; + + struct clk *clk; + atomic_t clk_ref; + + spinlock_t lock; + enum jz4740_adc_version version; +}; + #endif