Commit 145b0a5d authored by Chanwoo Choi's avatar Chanwoo Choi Committed by Jonathan Cameron

iio: adc: exynos_adc: Add support for s3c24xx ADC

This patch add support for s3c2410/s3c2416/s3c2440/s3c2443 ADC. The s3c24xx
is alomost same as ADCv1. But, There are a little difference as following:
- ADCMUX register address
- ADCDAT mask (10 bit or 12 bit ADC resolution according to SoC version)
- s3c24xx/s3c64xx has not included ADC_PHY enable register
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 249535d8
...@@ -11,15 +11,25 @@ New driver handles the following ...@@ -11,15 +11,25 @@ New driver handles the following
Required properties: Required properties:
- compatible: Must be "samsung,exynos-adc-v1" - compatible: Must be "samsung,exynos-adc-v1"
for exynos4412/5250 controllers. for exynos4412/5250 and s5pv210 controllers.
Must be "samsung,exynos-adc-v2" for Must be "samsung,exynos-adc-v2" for
future controllers. future controllers.
Must be "samsung,exynos3250-adc" for Must be "samsung,exynos3250-adc" for
controllers compatible with ADC of Exynos3250. controllers compatible with ADC of Exynos3250.
Must be "samsung,s3c2410-adc" for
the ADC in s3c2410 and compatibles
Must be "samsung,s3c2416-adc" for
the ADC in s3c2416 and compatibles
Must be "samsung,s3c2440-adc" for
the ADC in s3c2440 and compatibles
Must be "samsung,s3c2443-adc" for
the ADC in s3c2443 and compatibles
Must be "samsung,s3c6410-adc" for Must be "samsung,s3c6410-adc" for
the ADC in s3c6410 and compatibles the ADC in s3c6410 and compatibles
- reg: Contains ADC register address range (base address and - reg: List of ADC register address range
length) and the address of the phy enable register. - The base address and range of ADC register
- The base address and range of ADC_PHY register (every
SoC except for s3c24xx/s3c64xx ADC)
- interrupts: Contains the interrupt information for the timer. The - interrupts: Contains the interrupt information for the timer. The
format is being dependent on which interrupt controller format is being dependent on which interrupt controller
the Samsung device uses. the Samsung device uses.
......
...@@ -129,7 +129,7 @@ config AT91_ADC ...@@ -129,7 +129,7 @@ config AT91_ADC
config EXYNOS_ADC config EXYNOS_ADC
tristate "Exynos ADC driver support" tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || (OF && COMPILE_TEST) depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
help help
Core support for the ADC block found in the Samsung EXYNOS series Core support for the ADC block found in the Samsung EXYNOS series
of SoCs for drivers such as the touchscreen and hwmon to use to share of SoCs for drivers such as the touchscreen and hwmon to use to share
......
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
#define ADC_V1_INTCLR(x) ((x) + 0x18) #define ADC_V1_INTCLR(x) ((x) + 0x18)
#define ADC_V1_MUX(x) ((x) + 0x1c) #define ADC_V1_MUX(x) ((x) + 0x1c)
/* S3C2410 ADC registers definitions */
#define ADC_S3C2410_MUX(x) ((x) + 0x18)
/* Future ADC_V2 registers definitions */ /* Future ADC_V2 registers definitions */
#define ADC_V2_CON1(x) ((x) + 0x00) #define ADC_V2_CON1(x) ((x) + 0x00)
#define ADC_V2_CON2(x) ((x) + 0x04) #define ADC_V2_CON2(x) ((x) + 0x04)
...@@ -63,6 +66,8 @@ ...@@ -63,6 +66,8 @@
/* Bit definitions for S3C2410 ADC */ /* Bit definitions for S3C2410 ADC */
#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3) #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 */ /* Bit definitions for ADC_V2 */
#define ADC_V2_CON1_SOFT_RESET (1u << 2) #define ADC_V2_CON1_SOFT_RESET (1u << 2)
...@@ -80,6 +85,7 @@ ...@@ -80,6 +85,7 @@
/* Bit definitions common for ADC_V1 and ADC_V2 */ /* Bit definitions common for ADC_V1 and ADC_V2 */
#define ADC_CON_EN_START (1u << 0) #define ADC_CON_EN_START (1u << 0)
#define ADC_CON_EN_START_MASK (0x3 << 0)
#define ADC_DATX_MASK 0xFFF #define ADC_DATX_MASK 0xFFF
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
...@@ -103,6 +109,8 @@ struct exynos_adc { ...@@ -103,6 +109,8 @@ struct exynos_adc {
struct exynos_adc_data { struct exynos_adc_data {
int num_channels; int num_channels;
bool needs_sclk; bool needs_sclk;
bool needs_adc_phy;
u32 mask;
void (*init_hw)(struct exynos_adc *info); void (*init_hw)(struct exynos_adc *info);
void (*exit_hw)(struct exynos_adc *info); void (*exit_hw)(struct exynos_adc *info);
...@@ -174,6 +182,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info) ...@@ -174,6 +182,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
{ {
u32 con1; u32 con1;
if (info->data->needs_adc_phy)
writel(1, info->enable_reg); writel(1, info->enable_reg);
/* set default prescaler values and Enable prescaler */ /* set default prescaler values and Enable prescaler */
...@@ -188,6 +197,7 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info) ...@@ -188,6 +197,7 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
{ {
u32 con; u32 con;
if (info->data->needs_adc_phy)
writel(0, info->enable_reg); writel(0, info->enable_reg);
con = readl(ADC_V1_CON(info->regs)); con = readl(ADC_V1_CON(info->regs));
...@@ -213,6 +223,8 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info, ...@@ -213,6 +223,8 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info,
static const struct exynos_adc_data exynos_adc_v1_data = { static const struct exynos_adc_data exynos_adc_v1_data = {
.num_channels = MAX_ADC_V1_CHANNELS, .num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
.init_hw = exynos_adc_v1_init_hw, .init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw, .exit_hw = exynos_adc_v1_exit_hw,
...@@ -220,6 +232,53 @@ static const struct exynos_adc_data exynos_adc_v1_data = { ...@@ -220,6 +232,53 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
.start_conv = exynos_adc_v1_start_conv, .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, static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
unsigned long addr) unsigned long addr)
{ {
...@@ -231,8 +290,18 @@ static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, ...@@ -231,8 +290,18 @@ static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); 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 = { static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
.num_channels = MAX_ADC_V1_CHANNELS, .num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.init_hw = exynos_adc_v1_init_hw, .init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw, .exit_hw = exynos_adc_v1_exit_hw,
...@@ -244,6 +313,7 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info) ...@@ -244,6 +313,7 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info)
{ {
u32 con1, con2; u32 con1, con2;
if (info->data->needs_adc_phy)
writel(1, info->enable_reg); writel(1, info->enable_reg);
con1 = ADC_V2_CON1_SOFT_RESET; con1 = ADC_V2_CON1_SOFT_RESET;
...@@ -261,6 +331,7 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info) ...@@ -261,6 +331,7 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
{ {
u32 con; u32 con;
if (info->data->needs_adc_phy)
writel(0, info->enable_reg); writel(0, info->enable_reg);
con = readl(ADC_V2_CON1(info->regs)); con = readl(ADC_V2_CON1(info->regs));
...@@ -289,6 +360,8 @@ static void exynos_adc_v2_start_conv(struct exynos_adc *info, ...@@ -289,6 +360,8 @@ static void exynos_adc_v2_start_conv(struct exynos_adc *info,
static const struct exynos_adc_data exynos_adc_v2_data = { static const struct exynos_adc_data exynos_adc_v2_data = {
.num_channels = MAX_ADC_V2_CHANNELS, .num_channels = MAX_ADC_V2_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
.init_hw = exynos_adc_v2_init_hw, .init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw, .exit_hw = exynos_adc_v2_exit_hw,
...@@ -298,7 +371,9 @@ static const struct exynos_adc_data exynos_adc_v2_data = { ...@@ -298,7 +371,9 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
static const struct exynos_adc_data exynos3250_adc_data = { static const struct exynos_adc_data exynos3250_adc_data = {
.num_channels = MAX_EXYNOS3250_ADC_CHANNELS, .num_channels = MAX_EXYNOS3250_ADC_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_sclk = true, .needs_sclk = true,
.needs_adc_phy = true,
.init_hw = exynos_adc_v2_init_hw, .init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw, .exit_hw = exynos_adc_v2_exit_hw,
...@@ -308,6 +383,18 @@ static const struct exynos_adc_data exynos3250_adc_data = { ...@@ -308,6 +383,18 @@ static const struct exynos_adc_data exynos3250_adc_data = {
static const struct of_device_id exynos_adc_match[] = { 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", .compatible = "samsung,s3c6410-adc",
.data = &exynos_adc_s3c64xx_data, .data = &exynos_adc_s3c64xx_data,
}, { }, {
...@@ -373,9 +460,10 @@ static int exynos_read_raw(struct iio_dev *indio_dev, ...@@ -373,9 +460,10 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
static irqreturn_t exynos_adc_isr(int irq, void *dev_id) static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
{ {
struct exynos_adc *info = (struct exynos_adc *)dev_id; struct exynos_adc *info = (struct exynos_adc *)dev_id;
u32 mask = info->data->mask;
/* Read value */ /* Read value */
info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK; info->value = readl(ADC_V1_DATX(info->regs)) & mask;
/* clear irq */ /* clear irq */
if (info->data->clear_irq) if (info->data->clear_irq)
...@@ -468,10 +556,13 @@ static int exynos_adc_probe(struct platform_device *pdev) ...@@ -468,10 +556,13 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (IS_ERR(info->regs)) if (IS_ERR(info->regs))
return PTR_ERR(info->regs); return PTR_ERR(info->regs);
if (info->data->needs_adc_phy) {
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
info->enable_reg = devm_ioremap_resource(&pdev->dev, mem); info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(info->enable_reg)) if (IS_ERR(info->enable_reg))
return PTR_ERR(info->enable_reg); return PTR_ERR(info->enable_reg);
}
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment