blob: 0156182f91ba7c6e34c75236c110e609d0fa42c6 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/lc823450/lc823450_ipl2.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <debug.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <nvic.h>
#include <unistd.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <nuttx/fs/fs.h>
#ifdef CONFIG_FS_EVFAT
# include <nuttx/fs/mkevfatfs.h>
#endif
#include <nuttx/usb/usbmsc.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/drivers/drivers.h>
#ifdef CONFIG_I2C
# include <nuttx/i2c.h>
#endif
#ifdef CONFIG_LASTKMSG
# include <nuttx/lastkmsg.h>
#endif /* CONFIG_LASTKMSG */
#include <nuttx/usb/usbdev.h>
#include <libgen.h>
#include "arm_internal.h"
#ifdef CONFIG_ADC
# include "lc823450_adc.h"
#endif
#include "lc823450_syscontrol.h"
#include "lc823450_mtd.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define IPL2_DEVPATH "/dev/mtdblock0"
#define IPL2_IMAGE "LC8234xx_17S_start_data.boot_bin"
#define LASTMSG_LOGPATH "/log/lastkmsg"
#define R2A20056BM_ADDR 0x1B
#define R2A20056BM_SCL 375000
#ifdef CONFIG_CHARGER
# define FORCE_USBBOOT_CHARGER
#endif
#pragma GCC optimize ("O0")
/****************************************************************************
* Private Types
****************************************************************************/
static struct
{
uint32_t sig;
uint32_t chunknum;
struct
{
char fname[32];
char csum[32];
uint32_t size;
uint32_t enc;
uint32_t offset;
}
chunk[10];
} upg_image;
/****************************************************************************
* Private Data
****************************************************************************/
static char copybuf[512];
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_USBMSC
static void sysreset(void);
static int set_config(int num, char *buf);
#endif
/****************************************************************************
* Name: blk_read()
****************************************************************************/
static int blk_read(void *buf, int len, const char *path, int offset)
{
void *handle;
int ret;
ret = bchlib_setup(path, true, &handle);
if (ret)
{
return ret;
}
ret = bchlib_read(handle, buf, offset, len);
bchlib_teardown(handle);
return ret;
}
/****************************************************************************
* Name: blk_write()
****************************************************************************/
#ifdef CONFIG_USBMSC
static int blk_write(const void *buf, int len, const char *path, int offset)
{
void *handle;
int ret;
ret = bchlib_setup(path, true, &handle);
if (ret)
{
return ret;
}
ret = bchlib_write(handle, buf, offset, len);
bchlib_teardown(handle);
return ret;
}
#endif
/****************************************************************************
* Name: install_recovery()
****************************************************************************/
static int install_recovery(const char *srcpath)
{
struct file rfile;
int i;
int len;
int rem;
int ret = 0;
void *handle = NULL;
if (bchlib_setup(CONFIG_MTD_RECOVERY_DEVPATH, false, &handle))
{
return -1;
}
ret = file_open(&rfile, srcpath, O_RDONLY | O_CLOEXEC, 0444);
if (file_read(&rfile, &upg_image, sizeof(upg_image)) != sizeof(upg_image))
{
_info("read head");
ret = -EIO;
goto err;
}
#ifdef IMG_SIGNATURE
if (upg_image.sig != IMG_SIGNATURE)
{
_info("image signature mismatch. IPL2=%u, UPG=%u\n",
IMG_SIGNATURE, upg_image.sig);
_info("go normal boot\n");
memset(copybuf, 0, sizeof(copybuf));
snprintf(copybuf, sizeof(copybuf), "normal");
set_config(1, copybuf);
sysreset();
/* NOT REACHED */
}
#endif
for (i = 0; i < upg_image.chunknum; i++)
{
if (!strcmp(basename(upg_image.chunk[i].fname), "recovery"))
{
break;
}
}
if (i == upg_image.chunknum)
{
_info("recovery not found\n");
ret = -ENOENT;
goto err;
}
file_seek(&rfile, upg_image.chunk[i].offset +
((void *)&upg_image.chunk[upg_image.chunknum] - (void *)&upg_image),
SEEK_SET);
rem = upg_image.chunk[i].size;
while (rem > 0)
{
len = file_read(&rfile, copybuf, rem > 512 ? 512 : rem);
if (len < 0)
{
_info("read image");
ret = -EIO;
goto err;
}
bchlib_write(handle, copybuf, upg_image.chunk[i].size - rem, len);
rem -= len;
}
err:
if (handle)
{
bchlib_teardown(handle);
}
file_close(&rfile);
_info("DONE\n");
return ret;
}
/****************************************************************************
* Name: load_kernel()
****************************************************************************/
static void load_kernel(const char *name, const char *devname)
{
int i;
void *tmp = (void *)0x02040000;
blk_read(tmp, 512 * 1024, devname, 0);
/* disable all IRQ */
for (i = LC823450_IRQ_NMI + 1; i < NR_IRQS; i++)
{
up_disable_irq(i);
}
/* clear pending IRQ */
putreg32(0xffffffff, NVIC_IRQ0_31_CLRPEND);
putreg32(0xffffffff, NVIC_IRQ32_63_CLRPEND);
putreg32(0xffffffff, NVIC_IRQ64_95_CLRPEND);
_info("start %s\n", name);
__asm__ __volatile__
(
"ldr sp, [%0, #0]\n" /* set sp */
"ldr pc, [%0, #4]" /* set pc, start nuttx */
: : "r"(tmp)
);
}
/****************************************************************************
* Name: check_diskformat()
****************************************************************************/
#ifdef CONFIG_USBMSC
static int check_diskformat(void)
{
int ret;
#ifdef CONFIG_FS_EVFAT
struct evfat_format_s fmt = EVFAT_FORMAT_INITIALIZER;
/* load MBR */
ret = blk_read(copybuf, sizeof(copybuf), "/dev/mtdblock0p2", 0);
if (ret < 0)
{
return 0;
}
/* If part2 has MBR signature, this eMMC was formatted by PC.
* This means the set is just after writing IPL2.
*/
if (copybuf[510] != 0x55 || copybuf[511] != 0xaa)
{
return 0;
}
ret = mkevfatfs(CONFIG_MTD_CP_DEVPATH, &fmt);
#endif
_info("FORMAT content partition : %d\n", ret);
memset(copybuf, 0, sizeof(copybuf));
ret = blk_write(copybuf, 512, CONFIG_MTD_ETC_DEVPATH, 0);
_info("clear /etc : %d\n", ret);
ret = blk_write(copybuf, 512, CONFIG_MTD_SYSTEM_DEVPATH, 0);
_info("clear /system : %d\n", ret);
ret = blk_write(copybuf, 512, CONFIG_MTD_CACHE_DEVPATH, 0);
_info("clear /cache : %d\n", ret);
return 1;
}
#endif
/****************************************************************************
* Name: check_forceusbboot()
****************************************************************************/
#ifdef CONFIG_ADC
static int check_forceusbboot(void)
{
uint32_t val;
uint32_t val1;
/* enable clock & unreset */
modifyreg32(MCLKCNTAPB, 0, MCLKCNTAPB_ADC_CLKEN);
modifyreg32(MRSTCNTAPB, 0, MRSTCNTAPB_ADC_RSTB);
nxsig_usleep(10000);
/* start ADC0,1 */
putreg32(ADCCTL_ADCNVCK_DIV32 | ADCCTL_ADACT | ADCCTL_ADCHSCN |
1 /* 0,1 ch */, ADCCTL);
putreg32(53, ADCSMPL);
/* wait for adc done */
while ((getreg32(ADCSTS) & ADCSTS_ADCMPL) == 0)
;
val = getreg32(ADC0DT);
val1 = getreg32(ADC1DT);
_info("val = %d, val1 = %d\n", val, val1);
/* disable clock & reset */
modifyreg32(MCLKCNTAPB, MCLKCNTAPB_ADC_CLKEN, 0);
modifyreg32(MRSTCNTAPB, MRSTCNTAPB_ADC_RSTB, 0);
/* check KEY0_AD_D key pressed */
if (val >= (0x3a << 2) && val < (0x57 << 2))
{
return 1;
}
/* check KEY0_AD_B key pressed */
if (val >= (0x0b << 2) && val < (0x20 << 2))
{
return 1;
}
/* check KEY1_AD_B key pressed */
if (val1 >= (0x0b << 2) && val1 < (0x20 << 2))
{
return 1;
}
return 0;
}
#endif
#ifdef CONFIG_USBMSC
/****************************************************************************
* Name: sysreset()
****************************************************************************/
static void sysreset(void)
{
/* workaround to flush eMMC cache */
nxsig_usleep(100000);
up_systemreset();
}
/****************************************************************************
* Name: get_config()
****************************************************************************/
static int get_config(int num, char *buf)
{
int ret;
ret = blk_read(buf, 512, CONFIG_MTD_CONFIG_DEVPATH, num * 512);
return ret;
}
/****************************************************************************
* Name: set_config()
****************************************************************************/
static int set_config(int num, char *buf)
{
int ret;
ret = blk_write(buf, 512, CONFIG_MTD_CONFIG_DEVPATH, num * 512);
return ret;
}
#endif /* CONFIG_USBMSC */
extern volatile int g_update_flag;
/****************************************************************************
* Name: chg_disable()
****************************************************************************/
#ifdef CONFIG_CHARGER
static void chg_disable(void)
{
struct i2c_dev_s *i2c;
int ret;
uint32_t freq;
const uint8_t addr = 0x01;
const uint8_t data = 0x83;
struct i2c_msg_s msg[2] =
{
{
.addr = R2A20056BM_ADDR,
.flags = 0,
.buffer = (uint8_t *)&addr,
.length = 1,
},
{
.addr = R2A20056BM_ADDR,
.flags = I2C_M_NOSTART,
.buffer = (uint8_t *)&data,
.length = 1,
}
};
/* I2C pinmux */
modifyreg32(PMDCNT0, 0x0003c000, 0x00014000);
/* I2C drv : 4mA */
modifyreg32(PTDRVCNT0, 0x0003c000, 0x0003c000);
/* Enable I2C controller */
modifyreg32(MCLKCNTAPB, 0, MCLKCNTAPB_I2C0_CLKEN);
modifyreg32(MRSTCNTAPB, 0, MRSTCNTAPB_I2C0_RSTB);
/* I2C SCL: push pull */
modifyreg32(I2CMODE, 0, I2CMODE0);
/* Disable charge */
i2c = up_i2cinitialize(1);
if (i2c)
{
/* Set slave address */
ret = I2C_SETADDRESS(i2c, R2A20056BM_ADDR, 7);
/* Set frequency */
freq = I2C_SETFREQUENCY(i2c, R2A20056BM_SCL);
/* Charge disable */
if (ret == OK && freq == R2A20056BM_SCL)
{
ret = I2C_TRANSFER(i2c, msg, sizeof(msg) / sizeof(msg[0]));
if (ret != OK)
{
_info("no vbus (%d)\n", ret);
}
else
{
nxsig_usleep(20);
}
}
up_i2cuninitialize(i2c);
}
else
{
_info("Failed to i2c initialize\n");
}
}
#endif
/****************************************************************************
* Name: msc_enable()
****************************************************************************/
#ifdef CONFIG_USBMSC
static int msc_enable(int forced)
{
int ret;
void *handle;
usbmsc_configure(1, &handle);
usbmsc_bindlun(handle, CONFIG_MTD_CP_DEVPATH, 0, 0, 0, false);
usbmsc_exportluns(handle);
#ifdef FORCE_USBBOOT_CHARGER
if (!forced && !usbdev_is_usbcharger())
{
/* If not USBCharger, go normal boot */
usbmsc_uninitialize(handle);
return 0;
}
/* wait for SCSI command */
while (g_update_flag == 0)
{
/* If key released, go normal boot */
if (!forced && !check_forceusbboot())
{
usbmsc_uninitialize(handle);
return 0;
}
nxsig_usleep(10000);
}
#else
/* wait for SCSI command */
while (g_update_flag == 0)
{
nxsig_usleep(10000);
}
#endif
usbmsc_uninitialize(handle);
/* check recovery kernel update */
nx_mount(CONFIG_MTD_CP_DEVPATH, "/mnt/sd0", "evfat", 0, NULL);
nxsig_usleep(10000);
/* recovery kernel install from UPG.img */
ret = install_recovery("/mnt/sd0/UPG.IMG");
if (ret == 0)
{
_info("Install recovery\n");
/* clear old MBR */
memset(copybuf, 0, sizeof(copybuf));
set_config(0, copybuf);
}
/* set bootmode to recovery */
memset(copybuf, 0, sizeof(copybuf));
snprintf(copybuf, sizeof(copybuf), "recovery");
set_config(1, copybuf);
sysreset();
/* not reached */
return 0;
}
#endif
/****************************************************************************
* Name: ipl2_main()
****************************************************************************/
int ipl2_main(int argc, char *argv[])
{
int ret;
UNUSED(ret); /* Not used in all configurations */
#ifdef CONFIG_CHARGER
/* NOTE:
* chg_disable() must be done before CMIC_FWAKE L->H.
* Please refer to PDFW15IS-2494 for more information
*/
chg_disable();
#endif
lc823450_mtd_initialize(0);
#ifdef CONFIG_ADC
ret = check_forceusbboot();
#endif
#ifdef CONFIG_USBMSC
if (ret)
{
msc_enable(0);
}
ret = check_diskformat();
if (ret)
{
msc_enable(1);
}
memset(copybuf, 0, sizeof(copybuf));
get_config(1, copybuf);
/* for "reboot usb" */
if (!strncmp("usb", copybuf, 3))
{
/* remove boot flag for next boot */
memset(copybuf, 0, sizeof(copybuf));
set_config(1, copybuf);
msc_enable(1);
}
#endif
#ifdef CONFIG_LASTKMSG
check_lastkmsg();
#endif /* CONFIG_LASTKMSG */
if (!strncmp("recovery", copybuf, 8))
{
/* check recovery kernel update */
nx_mount(CONFIG_MTD_CP_DEVPATH, "/mnt/sd0", "evfat", 0, NULL);
nxsig_usleep(10000);
/* recovery kernel install from UPG.img */
install_recovery("/mnt/sd0/UPG.IMG");
load_kernel("recovery", CONFIG_MTD_RECOVERY_DEVPATH);
}
else
{
load_kernel("nuttx", CONFIG_MTD_KERNEL_DEVPATH);
}
/* not reached */
return -1;
}