iio: exyno-adc: use syscon for PMU register access
[firefly-linux-kernel-4.4.55.git] / drivers / iio / adc / exynos_adc.c
index 62631a778b1f5cdc31f68b05423c13083965474f..fe0317745436e3b93955d486a86998f1da99ed80 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 /* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
 #define ADC_V1_CON(x)          ((x) + 0x00)
@@ -47,6 +49,9 @@
 #define ADC_V1_INTCLR(x)       ((x) + 0x18)
 #define ADC_V1_MUX(x)          ((x) + 0x1c)
 
+/* S3C2410 ADC registers definitions */
+#define ADC_S3C2410_MUX(x)     ((x) + 0x18)
+
 /* Future ADC_V2 registers definitions */
 #define ADC_V2_CON1(x)         ((x) + 0x00)
 #define ADC_V2_CON2(x)         ((x) + 0x04)
@@ -63,6 +68,8 @@
 
 /* Bit definitions for S3C2410 ADC */
 #define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
+#define ADC_S3C2410_DATX_MASK  0x3FF
+#define ADC_S3C2416_CON_RES_SEL        (1u << 3)
 
 /* Bit definitions for ADC_V2 */
 #define ADC_V2_CON1_SOFT_RESET (1u << 2)
 
 /* Bit definitions common for ADC_V1 and ADC_V2 */
 #define ADC_CON_EN_START       (1u << 0)
+#define ADC_CON_EN_START_MASK  (0x3 << 0)
 #define ADC_DATX_MASK          0xFFF
 
 #define EXYNOS_ADC_TIMEOUT     (msecs_to_jiffies(100))
 
+#define EXYNOS_ADCV1_PHY_OFFSET        0x0718
+#define EXYNOS_ADCV2_PHY_OFFSET        0x0720
+
 struct exynos_adc {
        struct exynos_adc_data  *data;
        struct device           *dev;
        void __iomem            *regs;
-       void __iomem            *enable_reg;
+       struct regmap           *pmu_map;
        struct clk              *clk;
        struct clk              *sclk;
        unsigned int            irq;
@@ -103,6 +114,9 @@ struct exynos_adc {
 struct exynos_adc_data {
        int num_channels;
        bool needs_sclk;
+       bool needs_adc_phy;
+       int phy_offset;
+       u32 mask;
 
        void (*init_hw)(struct exynos_adc *info);
        void (*exit_hw)(struct exynos_adc *info);
@@ -174,7 +188,8 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
 {
        u32 con1;
 
-       writel(1, info->enable_reg);
+       if (info->data->needs_adc_phy)
+               regmap_write(info->pmu_map, info->data->phy_offset, 1);
 
        /* set default prescaler values and Enable prescaler */
        con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
@@ -188,7 +203,8 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
 {
        u32 con;
 
-       writel(0, info->enable_reg);
+       if (info->data->needs_adc_phy)
+               regmap_write(info->pmu_map, info->data->phy_offset, 0);
 
        con = readl(ADC_V1_CON(info->regs));
        con |= ADC_V1_CON_STANDBY;
@@ -213,6 +229,9 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info,
 
 static const struct exynos_adc_data exynos_adc_v1_data = {
        .num_channels   = MAX_ADC_V1_CHANNELS,
+       .mask           = ADC_DATX_MASK,        /* 12 bit ADC resolution */
+       .needs_adc_phy  = true,
+       .phy_offset     = EXYNOS_ADCV1_PHY_OFFSET,
 
        .init_hw        = exynos_adc_v1_init_hw,
        .exit_hw        = exynos_adc_v1_exit_hw,
@@ -220,6 +239,53 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
        .start_conv     = exynos_adc_v1_start_conv,
 };
 
+static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
+                                         unsigned long addr)
+{
+       u32 con1;
+
+       /* Enable 12 bit ADC resolution */
+       con1 = readl(ADC_V1_CON(info->regs));
+       con1 |= ADC_S3C2416_CON_RES_SEL;
+       writel(con1, ADC_V1_CON(info->regs));
+
+       /* Select channel for S3C2416 */
+       writel(addr, ADC_S3C2410_MUX(info->regs));
+
+       con1 = readl(ADC_V1_CON(info->regs));
+       writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static struct exynos_adc_data const exynos_adc_s3c2416_data = {
+       .num_channels   = MAX_ADC_V1_CHANNELS,
+       .mask           = ADC_DATX_MASK,        /* 12 bit ADC resolution */
+
+       .init_hw        = exynos_adc_v1_init_hw,
+       .exit_hw        = exynos_adc_v1_exit_hw,
+       .start_conv     = exynos_adc_s3c2416_start_conv,
+};
+
+static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
+                                         unsigned long addr)
+{
+       u32 con1;
+
+       /* Select channel for S3C2433 */
+       writel(addr, ADC_S3C2410_MUX(info->regs));
+
+       con1 = readl(ADC_V1_CON(info->regs));
+       writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static struct exynos_adc_data const exynos_adc_s3c2443_data = {
+       .num_channels   = MAX_ADC_V1_CHANNELS,
+       .mask           = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
+
+       .init_hw        = exynos_adc_v1_init_hw,
+       .exit_hw        = exynos_adc_v1_exit_hw,
+       .start_conv     = exynos_adc_s3c2443_start_conv,
+};
+
 static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
                                          unsigned long addr)
 {
@@ -231,8 +297,18 @@ static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
        writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
 }
 
+static struct exynos_adc_data const exynos_adc_s3c24xx_data = {
+       .num_channels   = MAX_ADC_V1_CHANNELS,
+       .mask           = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
+
+       .init_hw        = exynos_adc_v1_init_hw,
+       .exit_hw        = exynos_adc_v1_exit_hw,
+       .start_conv     = exynos_adc_s3c64xx_start_conv,
+};
+
 static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
        .num_channels   = MAX_ADC_V1_CHANNELS,
+       .mask           = ADC_DATX_MASK,        /* 12 bit ADC resolution */
 
        .init_hw        = exynos_adc_v1_init_hw,
        .exit_hw        = exynos_adc_v1_exit_hw,
@@ -244,7 +320,8 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info)
 {
        u32 con1, con2;
 
-       writel(1, info->enable_reg);
+       if (info->data->needs_adc_phy)
+               regmap_write(info->pmu_map, info->data->phy_offset, 1);
 
        con1 = ADC_V2_CON1_SOFT_RESET;
        writel(con1, ADC_V2_CON1(info->regs));
@@ -261,7 +338,8 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
 {
        u32 con;
 
-       writel(0, info->enable_reg);
+       if (info->data->needs_adc_phy)
+               regmap_write(info->pmu_map, info->data->phy_offset, 0);
 
        con = readl(ADC_V2_CON1(info->regs));
        con &= ~ADC_CON_EN_START;
@@ -289,6 +367,9 @@ static void exynos_adc_v2_start_conv(struct exynos_adc *info,
 
 static const struct exynos_adc_data exynos_adc_v2_data = {
        .num_channels   = MAX_ADC_V2_CHANNELS,
+       .mask           = ADC_DATX_MASK, /* 12 bit ADC resolution */
+       .needs_adc_phy  = true,
+       .phy_offset     = EXYNOS_ADCV2_PHY_OFFSET,
 
        .init_hw        = exynos_adc_v2_init_hw,
        .exit_hw        = exynos_adc_v2_exit_hw,
@@ -298,7 +379,10 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
 
 static const struct exynos_adc_data exynos3250_adc_data = {
        .num_channels   = MAX_EXYNOS3250_ADC_CHANNELS,
+       .mask           = ADC_DATX_MASK, /* 12 bit ADC resolution */
        .needs_sclk     = true,
+       .needs_adc_phy  = true,
+       .phy_offset     = EXYNOS_ADCV1_PHY_OFFSET,
 
        .init_hw        = exynos_adc_v2_init_hw,
        .exit_hw        = exynos_adc_v2_exit_hw,
@@ -308,6 +392,18 @@ static const struct exynos_adc_data exynos3250_adc_data = {
 
 static const struct of_device_id exynos_adc_match[] = {
        {
+               .compatible = "samsung,s3c2410-adc",
+               .data = &exynos_adc_s3c24xx_data,
+       }, {
+               .compatible = "samsung,s3c2416-adc",
+               .data = &exynos_adc_s3c2416_data,
+       }, {
+               .compatible = "samsung,s3c2440-adc",
+               .data = &exynos_adc_s3c24xx_data,
+       }, {
+               .compatible = "samsung,s3c2443-adc",
+               .data = &exynos_adc_s3c2443_data,
+       }, {
                .compatible = "samsung,s3c6410-adc",
                .data = &exynos_adc_s3c64xx_data,
        }, {
@@ -373,9 +469,10 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
 static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
 {
        struct exynos_adc *info = (struct exynos_adc *)dev_id;
+       u32 mask = info->data->mask;
 
        /* Read value */
-       info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK;
+       info->value = readl(ADC_V1_DATX(info->regs)) & mask;
 
        /* clear irq */
        if (info->data->clear_irq)
@@ -468,10 +565,16 @@ static int exynos_adc_probe(struct platform_device *pdev)
        if (IS_ERR(info->regs))
                return PTR_ERR(info->regs);
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(info->enable_reg))
-               return PTR_ERR(info->enable_reg);
+
+       if (info->data->needs_adc_phy) {
+               info->pmu_map = syscon_regmap_lookup_by_phandle(
+                                       pdev->dev.of_node,
+                                       "samsung,syscon-phandle");
+               if (IS_ERR(info->pmu_map)) {
+                       dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
+                       return PTR_ERR(info->pmu_map);
+               }
+       }
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
@@ -632,7 +735,6 @@ static struct platform_driver exynos_adc_driver = {
        .remove         = exynos_adc_remove,
        .driver         = {
                .name   = "exynos-adc",
-               .owner  = THIS_MODULE,
                .of_match_table = exynos_adc_match,
                .pm     = &exynos_adc_pm_ops,
        },