#define AT91SAM9X5_BASE_ADC 0xf804c000 #define AT91SAM9260_BASE_ADC 0xfffe0000 #define ADC_SHTIME_DEFAULT 0x05 #define ADC_STARTUP_DEFAULT 0x04 #define ADC_PRESCALE_DEFAULT 0x3F #define ADC_MODE_DEFAULT \ ((ADC_SHTIME_DEFAULT & 0x0F) << 24) | \ ((ADC_STARTUP_DEFAULT & 0x1F) << 16) | \ ((ADC_PRESCALE_DEFAULT & 0x3F) << 8) #define ADC_CR_OFFSET 0x00 #define ADC_MR_OFFSET 0x04 #define ADC_CHER_OFFSET 0x10 #define ADC_CHDR_OFFSET 0x14 #define ADC_CHSR_OFFSET 0x18 #define ADC_SR_OFFSET 0x1C #define ADC_LDCR_OFFSET 0x20 #define ADC_IER_OFFSET 0x14 #define ADC_IDR_OFFSET 0x28 #define ADC_IMR_OFFSET 0x2C #define ADC_CDR0_OFFSET 0x30 #define ADC_CDR1_OFFSET 0x34 #define ADC_CDR2_OFFSET 0x38 #define ADC_CDR3_OFFSET 0x3C void __iomem *adc_base; struct clk *adc_clk; #define ADC_CONVERT_RESET(base) writel(0x01, (base) + ADC_CR_OFFSET) #define ADC_CONVERT_START(base) writel(0x02, (base) + ADC_CR_OFFSET) static ssize_t mts_attr_show_adc(struct device *dev, struct device_attribute *attr, char *buf) { int offset; u32 value; u32 chan_mask; if (!DEVICE_CAPA(id_eeprom.capa, CAPA_ADC)) { log_debug("ADC not available"); return -ENODEV; } if (!strcmp(attr->attr.name, "adc0")) { offset = ADC_CDR0_OFFSET; chan_mask = 0x01; } else if (!strcmp(attr->attr.name, "adc1")) { offset = ADC_CDR1_OFFSET; chan_mask = 0x02; } else if (!strcmp(attr->attr.name, "adc2")) { offset = ADC_CDR2_OFFSET; chan_mask = 0x04; } else if (!strcmp(attr->attr.name, "adc3")) { offset = ADC_CDR3_OFFSET; chan_mask = 0x08; } else { log_notice("adc attr does not exist"); return -ENOENT; } mutex_lock(&mts_io_mutex); // disable all channels and enable the one we want writel(0x0F, adc_base + ADC_CHDR_OFFSET); writel(chan_mask, adc_base + ADC_CHER_OFFSET); ADC_CONVERT_START(adc_base); // wait for conversion to complete (EOC bit set) value = 0; while (value != chan_mask) { value = readl(adc_base + ADC_SR_OFFSET) & chan_mask; log_debug("ADC_SR EOC [%X]", value); } // read result value = readl(adc_base + offset); mutex_unlock(&mts_io_mutex); return sprintf(buf, "%lu\n", (unsigned long) value); } static DEVICE_ATTR_RO_MTS(dev_attr_adc0, "adc0", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc1, "adc1", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc2, "adc2", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc3, "adc3", mts_attr_show_adc); static int mts_io_board_adc_probe(struct platform_device *pdev) { int err; adc_clk = clk_get(&pdev->dev, "adc_clk"); if (adc_clk) { err = clk_prepare_enable(adc_clk); if (err) { log_error("clock failed to prepare+enable: %d\n", err); return err; } #ifdef CONFIG_SOC_AT91SAM9X5 adc_base = ioremap(AT91SAM9X5_BASE_ADC, SZ_16K); #else adc_base = ioremap(AT91SAM9260_BASE_ADC, SZ_16K); #endif if (!adc_base) { log_error("ioremap failed.\n"); return -ENOMEM; } ADC_CONVERT_RESET(adc_base); writel(ADC_MODE_DEFAULT, adc_base + ADC_MR_OFFSET); writel(0x000F0F0F, adc_base + ADC_IDR_OFFSET); writel(0x0F, adc_base + ADC_CHDR_OFFSET); } return 0; } static int mts_io_board_adc_remove(struct platform_device *pdev) { if (adc_clk) { clk_disable_unprepare(adc_clk); } return 0; }