iPod Classic: ADC updates

Add code to read USB D+/D- and accessory ADCs, it is shown in HW
debug menu, might be useful in future for RB and/or the bootloader
to identify external USB chargers.

Change-Id: Ia48ca5e06bb7ddc52bb55abedde6734653ce8dba
This commit is contained in:
Cástor Muñoz 2016-08-12 02:37:45 +02:00
parent a25d0c58aa
commit adbd2969e6
7 changed files with 201 additions and 57 deletions

View File

@ -198,12 +198,12 @@ enum pcf5063X_reg_mbcc2 {
enum pcf5063X_reg_adcc1 {
PCF5063X_ADCC1_ADCSTART = 0x01,
PCF5063X_ADCC1_RES_10BIT = 0x02,
PCF5063X_ADCC1_RES_10BIT = 0x00,
PCF5063X_ADCC1_RES_8BIT = 0x02,
PCF5063X_ADCC1_AVERAGE_NO = 0x00,
PCF5063X_ADCC1_AVERAGE_4 = 0x04,
PCF5063X_ADCC1_AVERAGE_8 = 0x08,
PCF5063X_ADCC1_AVERAGE_16 = 0x0c,
PCF5063X_ADCC1_MUX_BATSNS_RES = 0x00,
PCF5063X_ADCC1_MUX_BATSNS_SUBTR = 0x10,
PCF5063X_ADCC1_MUX_ADCIN2_RES = 0x20,
@ -211,6 +211,7 @@ enum pcf5063X_reg_adcc1 {
PCF5063X_ADCC1_MUX_BATTEMP = 0x60,
PCF5063X_ADCC1_MUX_ADCIN1 = 0x70,
};
#define PCF5063X_ADCC1_RES_MASK 0x02
#define PCF5063X_ADCC1_AVERAGE_MASK 0x0c
#define PCF5063X_ADCC1_ADCMUX_MASK 0xf0
@ -219,9 +220,11 @@ enum pcf5063X_reg_adcc2 {
PCF5063X_ADCC2_RATIO_BATTEMP = 0x01,
PCF5063X_ADCC2_RATIO_ADCIN1 = 0x02,
PCF5063X_ADCC2_RATIO_BOTH = 0x03,
PCF5063X_ADCC2_RATIOSETTL_10US = 0x00,
PCF5063X_ADCC2_RATIOSETTL_100US = 0x04,
};
#define PCF5063X_ADCC2_RATIO_MASK 0x03
#define PCF5063X_ADCC2_RATIOSETTL_MASK 0x04
enum pcf5063X_reg_adcc3 {
PCF5063X_ADCC3_ACCSW_EN = 0x01,
@ -229,6 +232,7 @@ enum pcf5063X_reg_adcc3 {
PCF5063X_ADCC3_RES_DIV_TWO = 0x10,
PCF5063X_ADCC3_RES_DIV_THREE = 0x00,
};
#define PCF5063X_ADCC3_RES_DIV_MASK 0x10
enum pcf5063X_reg_adcs3 {
PCF5063X_ADCS3_REF_NTCSW = 0x00,

View File

@ -19,6 +19,7 @@
*
****************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include "system.h"
#include "config.h"
@ -28,6 +29,7 @@
#include "font.h"
#include "storage.h"
#include "power.h"
#include "adc.h"
#include "pmu-target.h"
#include "pcm-target.h"
#ifdef HAVE_SERIAL
@ -133,12 +135,20 @@ bool dbg_hw_info(void)
_DEBUG_PRINTF("accessory present: %s",
pmu_accessory_present() ? "true" : "false");
#endif
line++;
_DEBUG_PRINTF("ADC:");
_DEBUG_PRINTF("%s: %d mV", adc_name(ADC_BATTERY),
adc_read_battery_voltage());
_DEBUG_PRINTF("%s: %d Ohms", adc_name(ADC_ACCESSORY),
adc_read_accessory_resistor());
_DEBUG_PRINTF("USB D+: %d mV", adc_read_usbdata_voltage(true));
_DEBUG_PRINTF("USB D-: %d mV", adc_read_usbdata_voltage(false));
line++;
extern unsigned long i2c_rd_err, i2c_wr_err;
_DEBUG_PRINTF("i2c rd/wr errors: %lu/%lu", i2c_rd_err, i2c_wr_err);
}
#ifdef UC870X_DEBUG
else if(state==2)
else if(state==(max_states-1))
{
extern struct uartc_port ser_port;
bool opened = !!ser_port.uartc->port_l[ser_port.id];

View File

@ -18,7 +18,7 @@
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "inttypes.h"
@ -28,12 +28,90 @@
#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(channel);
return pmu_read_adc(&adc_channels[channel]);
}
void adc_init(void)
{
}

View File

@ -21,13 +21,23 @@
#ifndef _ADC_TARGET_H_
#define _ADC_TARGET_H_
#define NUM_ADC_CHANNELS 4
#include <stdbool.h>
#include "config.h"
#define ADC_UNKNOWN_0 0
#define ADC_UNKNOWN_1 1
#define ADC_BATTERY 2
#define ADC_UNKNOWN_3 3
enum
{
ADC_BATTERY = 0,
ADC_USBDATA,
ADC_ACCESSORY,
NUM_ADC_CHANNELS
};
#define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */
unsigned short adc_read_millivolts(int channel);
unsigned int adc_read_battery_voltage(void);
unsigned int adc_read_usbdata_voltage(bool dp);
int adc_read_accessory_resistor(void);
const char *adc_name(int channel);
#endif

View File

@ -24,12 +24,11 @@
#include "thread.h"
#include "pmu-target.h"
#include "adc-target.h"
#include "i2c-s5l8702.h"
#include "gpio-s5l8702.h"
static struct mutex pmu_adc_mutex;
int pmu_read_multiple(int address, int count, unsigned char* buffer)
{
return i2c_read(0, 0xe6, address, count, buffer);
@ -54,35 +53,6 @@ int pmu_write(int address, unsigned char val)
return pmu_write_multiple(address, 1, &val);
}
int pmu_read_adc(unsigned int adc)
{
int data = 0;
mutex_lock(&pmu_adc_mutex);
pmu_write(0x54, 5 | (adc << 4));
while ((data & 0x80) == 0)
{
yield();
data = pmu_read(0x57);
}
int value = (pmu_read(0x55) << 2) | (data & 3);
mutex_unlock(&pmu_adc_mutex);
return value;
}
/* millivolts */
int pmu_read_battery_voltage(void)
{
return (pmu_read_adc(1) * 2000 / 1023) + 2250;
}
/* milliamps */
int pmu_read_battery_current(void)
{
//TODO: Figure out how to read the battery current
// return pmu_read_adc(2);
return 0;
}
void pmu_ldo_on_in_standby(unsigned int ldo, int onoff)
{
if (ldo < 4)
@ -142,18 +112,87 @@ void pmu_write_rtc(unsigned char* buffer)
pmu_write_multiple(0x59, 7, buffer);
}
/*
* ADC
*/
#define ADC_FULL_SCALE 2000
#define ADC_FULL_SCALE_VISA 2400
#define ADC_SUBTR_OFFSET 2250
static struct mutex pmu_adc_mutex;
/* converts raw 8/10-bit value to millivolts */
unsigned short pmu_adc_raw2mv(
const struct pmu_adc_channel *ch, unsigned short raw)
{
int full_scale = ADC_FULL_SCALE;
int offset = 0;
switch (ch->adcc1 & PCF5063X_ADCC1_ADCMUX_MASK)
{
case PCF5063X_ADCC1_MUX_BATSNS_RES:
case PCF5063X_ADCC1_MUX_ADCIN2_RES:
full_scale *= ((ch->adcc1 & PCF5063X_ADCC3_RES_DIV_MASK) ==
PCF5063X_ADCC3_RES_DIV_TWO) ? 2 : 3;
break;
case PCF5063X_ADCC1_MUX_BATSNS_SUBTR:
case PCF5063X_ADCC1_MUX_ADCIN2_SUBTR:
offset = ADC_SUBTR_OFFSET;
break;
case PCF5063X_ADCC1_MUX_BATTEMP:
if (ch->adcc2 & PCF5063X_ADCC2_RATIO_BATTEMP)
full_scale = ADC_FULL_SCALE_VISA;
break;
case PCF5063X_ADCC1_MUX_ADCIN1:
if (ch->adcc2 & PCF5063X_ADCC2_RATIO_ADCIN1)
full_scale = ADC_FULL_SCALE_VISA;
break;
}
int nrb = ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) ==
PCF5063X_ADCC1_RES_8BIT) ? 8 : 10;
return (raw * full_scale / ((1<<nrb)-1)) + offset;
}
/* returns raw value, 8 or 10-bit resolution */
unsigned short pmu_read_adc(const struct pmu_adc_channel *ch)
{
mutex_lock(&pmu_adc_mutex);
pmu_write(PCF5063X_REG_ADCC3, ch->adcc3);
if (ch->bias_dly)
sleep(ch->bias_dly);
uint8_t buf[2] = { ch->adcc2, ch->adcc1 | PCF5063X_ADCC1_ADCSTART };
pmu_write_multiple(PCF5063X_REG_ADCC2, 2, buf);
int adcs3 = 0;
while (!(adcs3 & PCF5063X_ADCS3_ADCRDY))
{
yield();
adcs3 = pmu_read(PCF5063X_REG_ADCS3);
}
int raw = pmu_read(PCF5063X_REG_ADCS1);
if ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == PCF5063X_ADCC1_RES_10BIT)
raw = (raw << 2) | (adcs3 & PCF5063X_ADCS3_ADCDAT1L_MASK);
mutex_unlock(&pmu_adc_mutex);
return raw;
}
/*
* eINT
*/
#define Q_EINT 0
static char pmu_thread_stack[DEFAULT_STACK_SIZE/4];
static char pmu_thread_stack[DEFAULT_STACK_SIZE/2];
static struct event_queue pmu_queue;
static unsigned char ints_msk[6];
static void pmu_eint_isr(struct eint_handler*);
static struct eint_handler pmu_eint = {
static struct eint_handler pmu_eint =
{
.gpio_n = GPIO_EINT_PMU,
.type = EIC_INTTYPE_LEVEL,
.level = EIC_INTLEVEL_LOW,
@ -355,10 +394,11 @@ void pmu_preinit(void)
PCF5063X_REG_STBYCTL1, 0x0,
PCF5063X_REG_STBYCTL2, 0x8c,
/* GPIO1,2 = input, GPIO3 = output */
/* GPIO1,2 = input, GPIO3 = output High (NoPower default) */
PCF5063X_REG_GPIOCTL, 0x3,
PCF5063X_REG_GPIO1CFG, 0x0,
PCF5063X_REG_GPIO2CFG, 0x0,
PCF5063X_REG_GPIO3CFG, 0x7,
/* DOWN2 converter (SDRAM): 1800 mV, enabled,
startup current limit = 15mA*0x10 (TBC) */

View File

@ -22,6 +22,7 @@
#ifndef __PMU_TARGET_H__
#define __PMU_TARGET_H__
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
@ -72,14 +73,22 @@ enum pcf50635_reg_gpiostat {
* GPIO3: output, unknown
*/
struct pmu_adc_channel
{
const char *name;
uint8_t adcc1;
uint8_t adcc2;
uint8_t adcc3;
uint8_t bias_dly; /* RB ticks */
};
unsigned char pmu_read(int address);
int pmu_write(int address, unsigned char val);
int pmu_read_multiple(int address, int count, unsigned char* buffer);
int pmu_write_multiple(int address, int count, unsigned char* buffer);
int pmu_read_adc(unsigned int adc);
int pmu_read_battery_voltage(void);
int pmu_read_battery_current(void);
unsigned short pmu_read_adc(const struct pmu_adc_channel *ch);
unsigned short pmu_adc_raw2mv(
const struct pmu_adc_channel *ch, unsigned short raw);
void pmu_init(void);
void pmu_ldo_on_in_standby(unsigned int ldo, int onoff);
void pmu_ldo_set_voltage(unsigned int ldo, unsigned char voltage);

View File

@ -24,6 +24,7 @@
#include "pmu-target.h"
#include "power.h"
#include "audiohw.h"
#include "adc-target.h"
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
{
@ -49,20 +50,12 @@ const unsigned short percent_to_volt_charge[11] =
};
#endif /* CONFIG_CHARGING */
/* ADC should read 0x3ff=6.00V */
#define BATTERY_SCALE_FACTOR 6000
/* full-scale ADC readout (2^10) in millivolt */
/* Returns battery voltage from ADC [millivolts] */
int _battery_voltage(void)
{
int compensation = (10 * (pmu_read_battery_current() - 7)) / 12;
if (charging_state()) return pmu_read_battery_voltage() - compensation;
return pmu_read_battery_voltage() + compensation;
return adc_read_battery_voltage();
}
#ifdef HAVE_ACCESSORY_SUPPLY
void accessory_supply_set(bool enable)
{