View Single Post
Old 10-04-2017, 12:18 AM   #14
varu
Member
varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'varu knows the difference between 'who' and 'whom'
 
Posts: 12
Karma: 10000
Join Date: Sep 2017
Device: Kindle PW2
The display temperature is retrieved as follows in max77696-adc.c:

Spoiler:
Code:
/********* ADC Source external interfaces *******************************/
/* Display Panel NTC interface */
static void adc_disp_gettemp_work(struct work_struct *work)
{
    u16 data = 0x0;

    max77696_adc_read(MAX77696_ADC_CH_AIN0, &data);

    /* Remove ADC grnd ref noise in boards with analog grnd island */
    if(lab126_board_rev_greater(BOARD_ID_WARIO_2) ||
	lab126_board_is(BOARD_ID_ICEWINE_WARIO_512) ||
	lab126_board_is(BOARD_ID_ICEWINE_WFO_WARIO_512) ||
        lab126_board_rev_greater(BOARD_ID_ICEWINE_WARIO_P5) ||
	lab126_board_rev_greater(BOARD_ID_ICEWINE_WFO_WARIO_P5) ||
	lab126_board_rev_greater(BOARD_ID_PINOT_WFO_EVT1) ||
	lab126_board_rev_greater(BOARD_ID_PINOT_WFO_2GB_EVT1) ||
	lab126_board_is(BOARD_ID_PINOT_2GB) ||
	lab126_board_is(BOARD_ID_PINOT) ||
	lab126_board_is(BOARD_ID_MUSCAT_WAN) ||
	lab126_board_is(BOARD_ID_MUSCAT_WFO) ||
	lab126_board_is(BOARD_ID_MUSCAT_32G_WFO) ||
	lab126_board_is(BOARD_ID_BOURBON_WFO) ||
        lab126_board_is(BOARD_ID_BOURBON_WFO_PREEVT2) ||
	lab126_board_is(BOARD_ID_WHISKY_WFO) ||
	lab126_board_is(BOARD_ID_WHISKY_WAN) ||
	lab126_board_is(BOARD_ID_WOODY)) { 
            u16 noise = 0x0;
            max77696_adc_read(MAX77696_ADC_CH_AIN2, &noise);
            /* diff to compensate */
            data = abs(data - noise);
    }

    /*this is the only place display_temp gets updated, so no mutex needed! */
    display_temp_c = lkup_temp_data(data);

    schedule_delayed_work(&adc_disp_temp_work, msecs_to_jiffies(DISP_ADC_TEMP_THRESHOLD));
    return;
}

So MAX77696_ADC_CH_AIN2 is the specific sensor we want to look at. The kernel exposes this sensor & all others on the MAX77696 at /sys/devices/platform/imx-i2c.0/i2c-0/0-003c/max77696-adc.0/

Running cat /sys/devices/platform/imx-i2c.0/i2c-0/0-003c/max77696-adc.0/ain2 I get...

Code:
max77696-adc max77696-adc.0: adc conversion timed out
What's interesting is, this ADC conversion fails for every single exposed sensor!

The code for the ADC conversion is in max77696-adc.c & looks like this:

Spoiler:
Code:
static int max77696_adc_channel_convert (struct max77696_adc *me,
    u8 channel, u16 *data)
{
    u8 adc_busy, adc_ch, tmp;
    u16 val;
    unsigned long timeout;
    int rc;

    adc_ch = max77696_adc_channels[channel].physical_channel;

    /* Enable ADC by setting the ADC Enable (ADCEN) bit to 1
     * which in turn forces the ADC reference on
     * even if ADCREFEN bit is set to 0.
     */
    rc = max77696_adc_reg_set_bit(me, ADCCNTL, ADCEN, 1);
    if (unlikely(rc)) {
        dev_err(me->dev, "ADCCNTL write error [%d]\n", rc);
        goto out;
    }

    /* Setup ADC channel for conversion */
    if (likely(max77696_adc_channels[channel].setup)) {
        rc = max77696_adc_channels[channel].setup(me, channel);
        if (unlikely(rc)) {
	    dev_err(me->dev, "failed to setup channel %u [%d]\n", channel, rc);
	    goto out;
	}
    }

    /* Initiate ADC conversion sequence
     * by setting the ADC Start Conversion (ADCCONV) to 1.
     */
    rc = max77696_adc_reg_set_bit(me, ADCCNTL, ADCCONV, 1);
    if (unlikely(rc)) {
        dev_err(me->dev, "ADCCNTL write error [%d]\n", rc);
        goto out;
    }

    /* Check availability of ADC val by inspecting the ADCCONV bit.
     * This bit is automatically cleared to 0
     * when an ADC conversion sequence has completed.
     */

    timeout = jiffies + msecs_to_jiffies(ADC_CONVERSION_TIME_OUT);

    do {
        if (unlikely(time_after(jiffies, timeout))) {
            dev_err(me->dev, "adc conversion timed out\n");
            rc = - -ETIMEDOUT;
            goto out;
        }
        msleep(1);
        max77696_adc_reg_get_bit(me, ADCCNTL, ADCCONV, &adc_busy);
    } while (likely(adc_busy));

    /* Read ADC conversion result. */
    rc = max77696_adc_reg_set_bit(me, ADCCHSEL, ADCCH, adc_ch);
    if (unlikely(rc)) {
        dev_err(me->dev, "ADCCHSEL write failed [%d]\n", rc);
        goto out;
    }
    
    max77696_read(me->i2c, 0x2A, &tmp);

    rc = max77696_adc_reg_read(me, ADCDATAH, &tmp);
    if (unlikely(rc)) {
        dev_err(me->dev, "ADCDATAH read failed [%d]\n", rc);
        goto out;
    }

    val = (tmp << 8);

    rc = max77696_adc_reg_read(me, ADCDATAL, &tmp);
    if (unlikely(rc)) {
        dev_err(me->dev, "ADCDATAL read failed [%d]\n", rc);
        goto out;
    }
    
    val = (val | tmp);
    
    *data = val;

    /* Release ADC channel after conversion */
    if (likely(max77696_adc_channels[channel].release)) {
        max77696_adc_channels[channel].release(me, channel);
    }

out:
    /* Disable the ADC Block and Reference when not in use
     * to save power. */
    max77696_adc_reg_write(me, ADCCNTL, 0);

    return rc;
}


The salient bit is as follows (with comments on what I think it does - I'm by no means a programmer). The 10msec value is drawn from earlier in the file:

#define ADC_CONVERSION_TIME_OUT 10 /* in milli-seconds */

Code:
/* Check availability of ADC val by inspecting the ADCCONV bit.
 * This bit is automatically cleared to 0
 * when an ADC conversion sequence has completed.
 */
 
timeout = jiffies + msecs_to_jiffies(ADC_CONVERSION_TIME_OUT); // time when this function is ran plus 10msec (in jiffies)

do {
	if (unlikely(time_after(jiffies, timeout))) { // if time when this is checked is *after* timeout, this check has taken too long
		dev_err(me->dev, "adc conversion timed out\n");
		rc = - -ETIMEDOUT;
		goto out;
	}
	msleep(1); // sleep for 1ms
	max77696_adc_reg_get_bit(me, ADCCNTL, ADCCONV, &adc_busy);
} while (likely(adc_busy));
To further probe into why all ADC conversions fail I'd have to poll the appropriate registers & see the bit values. This may be useful; however, this code works properly on other PW2s and, given the ADC timeout on every sensor, it's safe to say that either the chip's gone bad or there is some other board-level defect - neither are avenues worth pursuing for me, particularly given the lack of a datasheet on the MAX77696 or board schematics for the PW2.

As the display temperature check is all I'm concerned about, the next step is to rebuild the kernel with a modified max77696-battery.c where either wario_battery_check_disabled is hardcoded to 1, or where the temp_delta < BATT_ID_CHECK_TEMP_DELTA check is removed from the OR chain. That said, such a task is somewhat unexplored territory for me (bit of a different story than just compiling one on x86 from what I've read). If anyone reading can assist or think of a better approach, I welcome any and all suggestions!

Last edited by varu; 10-04-2017 at 01:34 AM.
varu is offline   Reply With Quote