2013-07-07 15:34:04 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 by Amaury Pouly
|
|
|
|
*
|
|
|
|
* 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 "regs-pinctrl.h"
|
|
|
|
#include "regs-power.h"
|
2013-09-26 18:19:30 +00:00
|
|
|
#include "regs-lradc.h"
|
|
|
|
#include "regs-digctl.h"
|
2014-01-21 18:01:34 +00:00
|
|
|
#include "regs-clkctrl.h"
|
2013-07-07 15:34:04 +00:00
|
|
|
|
|
|
|
#define BOOT_ROM_CONTINUE 0 /* continue boot */
|
|
|
|
#define BOOT_ROM_SECTION 1 /* switch to new section *result_id */
|
|
|
|
|
2014-01-21 18:01:34 +00:00
|
|
|
#define BOOT_ARG_CHARGE ('c' | 'h' << 8 | 'r' << 16 | 'g' << 24)
|
|
|
|
|
2013-07-07 15:34:04 +00:00
|
|
|
typedef unsigned long uint32_t;
|
|
|
|
|
|
|
|
// target specific boot context
|
|
|
|
enum context_t
|
|
|
|
{
|
|
|
|
CONTEXT_NORMAL, /* normal boot */
|
|
|
|
CONTEXT_USB, /* USB plugged boot */
|
|
|
|
CONTEXT_RTC, /* RTC wake up boot */
|
|
|
|
};
|
|
|
|
// target specific boot decision
|
|
|
|
enum boot_t
|
|
|
|
{
|
|
|
|
BOOT_STOP, /* power down */
|
|
|
|
BOOT_ROCK, /* boot to Rockbox */
|
|
|
|
BOOT_OF, /* boot to OF */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline int __attribute__((always_inline)) read_gpio(int bank, int pin)
|
|
|
|
{
|
|
|
|
return (HW_PINCTRL_DINn(bank) >> pin) & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((always_inline)) read_pswitch(void)
|
|
|
|
{
|
|
|
|
#if IMX233_SUBTARGET >= 3700
|
|
|
|
return BF_RD(POWER_STS, PSWITCH);
|
|
|
|
#else
|
|
|
|
return BF_RD(DIGCTL_STATUS, PSWITCH);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-09-26 18:19:30 +00:00
|
|
|
/* only works for channels <=7, always divide by 2, never accumulates */
|
|
|
|
static inline void __attribute__((always_inline)) setup_lradc(int src)
|
|
|
|
{
|
|
|
|
BF_CLR(LRADC_CTRL0, SFTRST);
|
|
|
|
BF_CLR(LRADC_CTRL0, CLKGATE);
|
|
|
|
/* don't bother changing the source, we are early enough at boot so that
|
|
|
|
* channel x is mapped to source x */
|
|
|
|
HW_LRADC_CHn_CLR(src) = BM_OR2(LRADC_CHn, NUM_SAMPLES, ACCUMULATE);
|
|
|
|
BF_SETV(LRADC_CTRL2, DIVIDE_BY_TWO, 1 << src);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define BP_LRADC_CTRL1_LRADCx_IRQ(x) (x)
|
|
|
|
#define BM_LRADC_CTRL1_LRADCx_IRQ(x) (1 << (x))
|
|
|
|
|
|
|
|
static inline int __attribute__((always_inline)) read_lradc(int src)
|
|
|
|
{
|
|
|
|
BF_CLR(LRADC_CTRL1, LRADCx_IRQ(src));
|
|
|
|
BF_SETV(LRADC_CTRL0, SCHEDULE, 1 << src);
|
|
|
|
while(!BF_RD(LRADC_CTRL1, LRADCx_IRQ(src)));
|
|
|
|
return BF_RDn(LRADC_CHn, src, VALUE);
|
|
|
|
}
|
|
|
|
|
2013-07-07 15:34:04 +00:00
|
|
|
static inline void __attribute__((noreturn)) power_down()
|
|
|
|
{
|
|
|
|
#ifdef SANSA_FUZEPLUS
|
|
|
|
/* B0P09: this pin seems to be important to shutdown the hardware properly */
|
|
|
|
HW_PINCTRL_MUXSELn_SET(0) = 3 << 18;
|
|
|
|
HW_PINCTRL_DOEn(0) = 1 << 9;
|
|
|
|
HW_PINCTRL_DOUTn(0) = 1 << 9;
|
|
|
|
#endif
|
|
|
|
/* power down */
|
|
|
|
HW_POWER_RESET = BM_OR2(POWER_RESET, UNLOCK, PWD);
|
|
|
|
while(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Boot decision functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(SANSA_FUZEPLUS)
|
|
|
|
static enum boot_t boot_decision(enum context_t context)
|
|
|
|
{
|
|
|
|
/* if volume down is hold, boot to OF */
|
|
|
|
if(!read_gpio(1, 30))
|
|
|
|
return BOOT_OF;
|
|
|
|
/* on normal boot, make sure power button is hold long enough */
|
|
|
|
if(context == CONTEXT_NORMAL)
|
|
|
|
{
|
|
|
|
// monitor PSWITCH
|
|
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < 550000; i++)
|
|
|
|
if(read_pswitch() == 1)
|
|
|
|
count++;
|
|
|
|
if(count < 400000)
|
|
|
|
return BOOT_STOP;
|
|
|
|
}
|
|
|
|
return BOOT_ROCK;
|
|
|
|
}
|
|
|
|
#elif defined(CREATIVE_ZENXFI2)
|
|
|
|
static int boot_decision(int context)
|
|
|
|
{
|
|
|
|
/* We are lacking buttons on the Zen X-Fi2 because on USB, the select button
|
|
|
|
* enters recovery mode ! So we can only use power but power is used to power up
|
|
|
|
* on normal boots and then select is free ! Thus use a non-uniform scheme:
|
|
|
|
* - normal boot/RTC:
|
|
|
|
* - no key: Rockbox
|
|
|
|
* - select: OF
|
|
|
|
* - USB boot:
|
|
|
|
* - no key: Rockbox
|
|
|
|
* - power: OF
|
|
|
|
*/
|
|
|
|
if(context == CONTEXT_USB)
|
|
|
|
return read_pswitch() == 1 ? BOOT_OF : BOOT_ROCK;
|
|
|
|
else
|
|
|
|
return !read_gpio(0, 14) ? BOOT_OF : BOOT_ROCK;
|
|
|
|
}
|
|
|
|
#elif defined(CREATIVE_ZENXFI3)
|
|
|
|
static int boot_decision(int context)
|
|
|
|
{
|
|
|
|
/* if volume down is hold, boot to OF */
|
|
|
|
return !read_gpio(2, 7) ? BOOT_OF : BOOT_ROCK;
|
|
|
|
}
|
2013-09-25 12:30:35 +00:00
|
|
|
#elif defined(SONY_NWZE360) || defined(SONY_NWZE370)
|
2013-09-26 18:19:30 +00:00
|
|
|
static int local_decision(void)
|
|
|
|
{
|
|
|
|
/* read keys and pswitch */
|
|
|
|
int val = read_lradc(0);
|
|
|
|
/* if hold is on, power off
|
|
|
|
* if back is pressed, boot to OF
|
|
|
|
* if play is pressed, boot RB
|
|
|
|
* otherwise power off */
|
|
|
|
if(read_gpio(0, 9) == 0)
|
|
|
|
return BOOT_STOP;
|
|
|
|
if(val >= 1050 && val < 1150)
|
|
|
|
return BOOT_OF;
|
|
|
|
if(val >= 1420 && val < 1520)
|
|
|
|
return BOOT_ROCK;
|
|
|
|
return BOOT_STOP;
|
|
|
|
}
|
|
|
|
|
2013-09-25 12:30:35 +00:00
|
|
|
static int boot_decision(int context)
|
|
|
|
{
|
2013-09-26 18:19:30 +00:00
|
|
|
setup_lradc(0); // setup LRADC channel 0 to read keys
|
|
|
|
HW_PINCTRL_PULLn_SET(0) = 1 << 9; // enable pullup on hold key (B0P09)
|
|
|
|
/* make a decision */
|
|
|
|
int decision = local_decision();
|
|
|
|
/* in USB or alarm context, stick to it */
|
|
|
|
if(context == CONTEXT_USB || context == CONTEXT_RTC)
|
|
|
|
{
|
|
|
|
/* never power down so replace power off decision by rockbox */
|
|
|
|
return decision == BOOT_STOP ? BOOT_ROCK : decision;
|
|
|
|
}
|
|
|
|
/* otherwise start a 1 second timeout. Any decision change
|
|
|
|
* will result in power down */
|
|
|
|
uint32_t tmo = HW_DIGCTL_MICROSECONDS + 1000000;
|
|
|
|
while(HW_DIGCTL_MICROSECONDS < tmo)
|
|
|
|
{
|
|
|
|
int new_dec = local_decision();
|
|
|
|
if(new_dec != decision)
|
|
|
|
return BOOT_STOP;
|
|
|
|
}
|
|
|
|
return decision;
|
2013-09-25 12:30:35 +00:00
|
|
|
}
|
2014-01-05 14:20:41 +00:00
|
|
|
#elif defined(CREATIVE_ZENXFISTYLE)
|
|
|
|
static int boot_decision(int context)
|
|
|
|
{
|
|
|
|
setup_lradc(2); // setup LRADC channel 2 to read keys
|
|
|
|
/* make a decision */
|
|
|
|
int val = read_lradc(2);
|
|
|
|
/* boot to OF if left is hold
|
|
|
|
* NOTE: VDDIO is set to 3.1V initially and the resistor ladder is wired to
|
|
|
|
* VDDIO so these values are not the same as in the main binary which is
|
|
|
|
* calibrated for VDDIO=3.3V */
|
|
|
|
if(val >= 815 && val < 915)
|
|
|
|
return BOOT_OF;
|
|
|
|
return BOOT_ROCK;
|
|
|
|
}
|
2013-07-07 15:34:04 +00:00
|
|
|
#else
|
|
|
|
#warning You should define a target specific boot decision function
|
|
|
|
static int boot_decision(int context)
|
|
|
|
{
|
|
|
|
return BOOT_ROCK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context functions
|
|
|
|
*/
|
|
|
|
static inline enum context_t get_context(void)
|
|
|
|
{
|
|
|
|
#if IMX233_SUBTARGET >= 3780
|
|
|
|
/* On the imx233 it's easy because we know the power up source */
|
|
|
|
unsigned pwrup_src = BF_RD(POWER_STS, PWRUP_SOURCE);
|
|
|
|
if(pwrup_src & (1 << 5))
|
|
|
|
return CONTEXT_USB;
|
|
|
|
else if(pwrup_src & (1 << 4))
|
|
|
|
return CONTEXT_RTC;
|
|
|
|
else
|
|
|
|
return CONTEXT_NORMAL;
|
|
|
|
#else
|
|
|
|
/* On the other targets, we need to poke a few more registers */
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-01-21 18:01:34 +00:00
|
|
|
/**
|
|
|
|
* Charging function
|
|
|
|
*/
|
|
|
|
static inline void do_charge(void)
|
|
|
|
{
|
|
|
|
BF_CLR(LRADC_CTRL0, SFTRST);
|
|
|
|
BF_CLR(LRADC_CTRL0, CLKGATE);
|
|
|
|
BF_WRn(LRADC_DELAYn, 0, TRIGGER_LRADCS, 0x80);
|
|
|
|
BF_WRn(LRADC_DELAYn, 0, TRIGGER_DELAYS, 0x1);
|
|
|
|
BF_WRn(LRADC_DELAYn, 0, DELAY, 200);
|
|
|
|
BF_SETn(LRADC_DELAYn, 0, KICK);
|
|
|
|
BF_SET(LRADC_CONVERSION, AUTOMATIC);
|
|
|
|
BF_WR_V(LRADC_CONVERSION, SCALE_FACTOR, LI_ION);
|
|
|
|
BF_WR(POWER_CHARGE, STOP_ILIMIT, 1);
|
|
|
|
BF_WR(POWER_CHARGE, BATTCHRG_I, 0x10);
|
|
|
|
BF_CLR(POWER_CHARGE, PWD_BATTCHRG);
|
|
|
|
#if IMX233_SUBTARGET >= 3780
|
|
|
|
BF_WR(POWER_DCDC4P2, ENABLE_4P2, 1);
|
|
|
|
BF_CLR(POWER_5VCTRL, PWD_CHARGE_4P2);
|
|
|
|
BF_WR(POWER_5VCTRL, CHARGE_4P2_ILIMIT, 0x10);
|
|
|
|
#endif
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
BF_WR(CLKCTRL_CPU, INTERRUPT_WAIT, 1);
|
|
|
|
asm volatile (
|
|
|
|
"mcr p15, 0, %0, c7, c0, 4 \n" /* Wait for interrupt */
|
|
|
|
"nop\n" /* Datasheet unclear: "The lr sent to handler points here after RTI"*/
|
|
|
|
"nop\n"
|
|
|
|
: : "r"(0)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-07 15:34:04 +00:00
|
|
|
int main(uint32_t arg, uint32_t *result_id)
|
|
|
|
{
|
2014-01-21 18:01:34 +00:00
|
|
|
if(arg == BOOT_ARG_CHARGE)
|
|
|
|
do_charge();
|
2013-07-07 15:34:04 +00:00
|
|
|
switch(boot_decision(get_context()))
|
|
|
|
{
|
|
|
|
case BOOT_ROCK:
|
|
|
|
*result_id = arg;
|
|
|
|
return BOOT_ROM_SECTION;
|
|
|
|
case BOOT_OF:
|
|
|
|
return BOOT_ROM_CONTINUE;
|
|
|
|
case BOOT_STOP:
|
|
|
|
default:
|
|
|
|
power_down();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int __attribute__((section(".start"))) start(uint32_t arg, uint32_t *result_id)
|
|
|
|
{
|
|
|
|
return main(arg, result_id);
|
|
|
|
}
|