/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2009 by Bertrik Sikken
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "config.h"

#include "inttypes.h"
#include "s5l87xx.h"
#include "adc.h"
#include "adc-target.h"
#include "pmu-target.h"
#include "kernel.h"


/* MS_TO_TICKS converts a milisecond time period into the
 * corresponding amount of ticks. If the time period cannot
 * be accurately measured in ticks it will round up.
 */
#define MS_PER_HZ (1000/HZ)
#define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ)

static const struct pmu_adc_channel adc_channels[] =
{
    [ADC_BATTERY] =
    {
        .name = "Battery",
        .adcc1 = PCF5063X_ADCC1_MUX_BATSNS_SUBTR
               | PCF5063X_ADCC1_AVERAGE_4
               | PCF5063X_ADCC1_RES_10BIT,
    },

    [ADC_USBDATA] =
    {
        .name = "USB D+/D-",
        .adcc1 = PCF5063X_ADCC1_MUX_ADCIN2_RES
               | PCF5063X_ADCC1_AVERAGE_16
               | PCF5063X_ADCC1_RES_10BIT,
        .adcc3 = PCF5063X_ADCC3_RES_DIV_THREE,
    },

    [ADC_ACCESSORY] =
    {
        .name = "Accessory",
        .adcc1 = PCF5063X_ADCC1_MUX_ADCIN1
               | PCF5063X_ADCC1_AVERAGE_16
               | PCF5063X_ADCC1_RES_10BIT,
        .adcc2 = PCF5063X_ADCC2_RATIO_ADCIN1
               | PCF5063X_ADCC2_RATIOSETTL_10US,
        .adcc3 = PCF5063X_ADCC3_ACCSW_EN,
        .bias_dly = MS_TO_TICKS(50),
    },
};

const char *adc_name(int channel)
{
    return adc_channels[channel].name;
}

unsigned short adc_read_millivolts(int channel)
{
    const struct pmu_adc_channel *ch = &adc_channels[channel];
    return pmu_adc_raw2mv(ch, pmu_read_adc(ch));
}

/* Returns battery voltage [millivolts] */
unsigned int adc_read_battery_voltage(void)
{
    return adc_read_millivolts(ADC_BATTERY);
}

/* Returns USB D+/D- voltage from ADC [millivolts] */
unsigned int adc_read_usbdata_voltage(bool dp)
{
    unsigned int mvolts;
    int gpio = dp ? 0xb0300 : 0xb0200; /* select D+/D- */
    GPIOCMD = gpio | 0xf; /* route to ADCIN2 */
    mvolts = adc_read_millivolts(ADC_USBDATA);
    GPIOCMD = gpio | 0xe; /* unroute */
    return mvolts;
}

/* Returns resistor connected to "Accessory identify" pin [ohms] */
#define IAP_DEVICE_RESISTOR 100000  /* ohms */
int adc_read_accessory_resistor(void)
{
    int raw = pmu_read_adc(&adc_channels[ADC_ACCESSORY]);
    return (1023-raw) ? raw * IAP_DEVICE_RESISTOR / (1023-raw)
                      : -1 /* open circuit */;
}


/* API functions */
unsigned short adc_read(int channel)
{
    return pmu_read_adc(&adc_channels[channel]);
}

void adc_init(void)
{
}
