#ifndef __INTELMID_ADC_CONTROL_H__ #define __INTELMID_ADC_CONTROL_H_ /* * intelmid_adc_control.h - Intel SST Driver for audio engine * * Copyright (C) 2008-10 Intel Corporation * Authors: R Durgadadoss * Dharageswari R * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Common private ADC declarations for SST */ #define MSIC_ADC1CNTL1 0x1C0 #define MSIC_ADC_ENBL 0x10 #define MSIC_ADC_START 0x08 #define MSIC_ADC1CNTL3 0x1C2 #define MSIC_ADCTHERM_ENBL 0x04 #define MSIC_ADCRRDATA_ENBL 0x05 #define MSIC_STOPBIT_MASK 16 #define MSIC_ADCTHERM_MASK 4 #define ADC_CHANLS_MAX 15 /* Number of ADC channels */ #define ADC_LOOP_MAX (ADC_CHANLS_MAX - 1) /* ADC channel code values */ #define AUDIO_DETECT_CODE 0x06 /* ADC base addresses */ #define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ #define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ /** * configure_adc - enables/disables the ADC for conversion * @val: zero: disables the ADC non-zero:enables the ADC * * Enable/Disable the ADC depending on the argument * * Can sleep */ static inline int configure_adc(int val) { int ret; struct sc_reg_access sc_access = {0,}; sc_access.reg_addr = MSIC_ADC1CNTL1; ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); if (ret) return ret; if (val) /* Enable and start the ADC */ sc_access.value |= (MSIC_ADC_ENBL | MSIC_ADC_START); else /* Just stop the ADC */ sc_access.value &= (~MSIC_ADC_START); sc_access.reg_addr = MSIC_ADC1CNTL1; return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); } /** * reset_stopbit - sets the stop bit to 0 on the given channel * @addr: address of the channel * * Can sleep */ static inline int reset_stopbit(uint16_t addr) { int ret; struct sc_reg_access sc_access = {0,}; sc_access.reg_addr = addr; ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); if (ret) return ret; /* Set the stop bit to zero */ sc_access.reg_addr = addr; sc_access.value = (sc_access.value) & 0xEF; return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); } /** * find_free_channel - finds an empty channel for conversion * * If the ADC is not enabled then start using 0th channel * itself. Otherwise find an empty channel by looking for a * channel in which the stopbit is set to 1. returns the index * of the first free channel if succeeds or an error code. * * Context: can sleep * */ static inline int find_free_channel(void) { int ret; int i; struct sc_reg_access sc_access = {0,}; /* check whether ADC is enabled */ sc_access.reg_addr = MSIC_ADC1CNTL1; ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); if (ret) return ret; if ((sc_access.value & MSIC_ADC_ENBL) == 0) return 0; /* ADC is already enabled; Looking for an empty channel */ for (i = 0; i < ADC_CHANLS_MAX; i++) { sc_access.reg_addr = ADC_CHNL_START_ADDR + i; ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); if (ret) return ret; if (sc_access.value & MSIC_STOPBIT_MASK) { ret = i; break; } } return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; } /** * mid_initialize_adc - initializing the ADC * @dev: our device structure * * Initialize the ADC for reading thermistor values. Can sleep. */ static inline int mid_initialize_adc(void) { int base_addr, chnl_addr; int ret; static int channel_index; struct sc_reg_access sc_access = {0,}; /* Index of the first channel in which the stop bit is set */ channel_index = find_free_channel(); if (channel_index < 0) { pr_err("No free ADC channels"); return channel_index; } base_addr = ADC_CHNL_START_ADDR + channel_index; if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { /* Reset stop bit for channels other than 0 and 12 */ ret = reset_stopbit(base_addr); if (ret) return ret; /* Index of the first free channel */ base_addr++; channel_index++; } /* Since this is the last channel, set the stop bit to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ sc_access.reg_addr = base_addr; sc_access.value = AUDIO_DETECT_CODE | 0x10; ret = sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); if (ret) { pr_err("unable to enable ADC"); return ret; } chnl_addr = ADC_DATA_START_ADDR + 2 * channel_index; pr_debug("mid_initialize : %x", chnl_addr); configure_adc(1); return chnl_addr; } #endif