rockbox/utils/hwstub/stub/stmp/target.c
Amaury Pouly 287be81c16 hwstub: use a more reasonable hclk frequency
The old code would set CPU to 64MHz and HCLK to 9MHz but that's too low for
many things like usb and gpmi. So change HCLK to ~32MHZ.

Change-Id: I6459f25900e42603333cebccb7b0ed26c59640ad
2013-11-18 21:44:06 +00:00

294 lines
8.4 KiB
C

/***************************************************************************
* __________ __ ___.
* 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 "stddef.h"
#include "target.h"
#include "system.h"
#include "logf.h"
#define __REG_SET(reg) (*((volatile uint32_t *)(&reg + 1)))
#define __REG_CLR(reg) (*((volatile uint32_t *)(&reg + 2)))
#define __REG_TOG(reg) (*((volatile uint32_t *)(&reg + 3)))
#define __BLOCK_SFTRST (1 << 31)
#define __BLOCK_CLKGATE (1 << 30)
#define __XTRACT(reg, field) ((reg & reg##__##field##_BM) >> reg##__##field##_BP)
#define __XTRACT_EX(val, field) (((val) & field##_BM) >> field##_BP)
#define __FIELD_SET(reg, field, val) reg = (reg & ~reg##__##field##_BM) | (val << reg##__##field##_BP)
/**
*
* Global
*
*/
enum stmp_family_t
{
UNKNOWN,
STMP3600,
STMP3700,
STMP3770,
STMP3780
};
static enum stmp_family_t g_stmp_family = UNKNOWN;
static int g_atexit = HWSTUB_ATEXIT_OFF;
/**
*
* Power
*
*/
#define HW_POWER_BASE 0x80044000
void power_off(void)
{
switch(g_stmp_family)
{
case STMP3600:
*(volatile uint32_t *)(HW_POWER_BASE + 0xc0) = 0x3e770014;
break;
case STMP3700:
case STMP3770:
*(volatile uint32_t *)(HW_POWER_BASE + 0xe0) = 0x3e770001;
break;
case STMP3780:
*(volatile uint32_t *)(HW_POWER_BASE + 0x100) = 0x3e770001;
break;
default:
break;
}
}
/**
*
* Clkctrl
*
*/
#define HW_CLKCTRL_BASE 0x80040000
#define HW_CLKCTRL_PLLCTRL0 (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x0))
#define HW_CLKCTRL_PLLCTRL0__BYPASS (1 << 17) /* STMP3600 only */
#define HW_CLKCTRL_PLLCTRL0__POWER (1 << 16)
#define HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS (1 << 18)
#define HW_CLKCTRL_PLLCTRL1 (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x10))
#define HW_CLKCTRL_PLLCTRL1__LOCK (1 << 31)
/* STMP3600 only */
#define HW_CLKCTRL_CPUCLKCTRL (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x20))
#define HW_CLKCTRL_CPUCLKCTRL__DIV_BP 0
#define HW_CLKCTRL_CPUCLKCTRL__DIV_BM 0x3ff
#define HW_CLKCTRL_CPUCLKCTRL__WAIT_PLL_LOCK (1 << 30)
/* STMP3600 */
#define HW_CLKCTRL_HBUSCLKCTRL (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x30))
/* STMP3600 only */
#define HW_CLKCTRL_XBUSCLKCTRL (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x40))
#define HW_CLKCTRL_XBUSCLKCTRL__DIV_BP 0
#define HW_CLKCTRL_XBUSCLKCTRL__DIV_BM 0x3ff
/* STMP3600 only */
#define HW_CLKCTRL_UTMICLKCTRL (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x70))
#define HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK30M_GATE (1 << 30)
#define HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK120M_GATE (1 << 31)
void clkctrl_reset(void)
{
switch(g_stmp_family)
{
case STMP3600:
*(volatile uint32_t *)(HW_POWER_BASE + 0xc0) = 0x3e770002;
break;
case STMP3700:
case STMP3770:
*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0xf0) = 0x1;
break;
case STMP3780:
*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x120) = 0x1;
break;
default:
break;
}
}
/**
*
* Digctl
*
*/
/* Digital control */
#define HW_DIGCTL_BASE 0x8001C000
#define HW_DIGCTL_CTRL (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0))
#define HW_DIGCTL_CTRL__USB_CLKGATE (1 << 2)
/* STMP3700+ */
#define HW_DIGCTL_MICROSECONDS (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0xC0))
/* STMP3600 */
#define HW_DIGCTL_MICROSECONDS2 (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0xB0))
#define HW_DIGCTL_CHIPID (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0x310))
#define HW_DIGCTL_CHIPID__PRODUCT_CODE_BP 16
#define HW_DIGCTL_CHIPID__PRODUCT_CODE_BM 0xffff0000
#define HW_DIGCTL_CHIPID__REVISION_BP 0
#define HW_DIGCTL_CHIPID__REVISION_BM 0xff
#define HZ 1000000
/**
*
* USB PHY
*
*/
/* USB Phy */
#define HW_USBPHY_BASE 0x8007C000
#define HW_USBPHY_PWD (*(volatile uint32_t *)(HW_USBPHY_BASE + 0))
#define HW_USBPHY_CTRL (*(volatile uint32_t *)(HW_USBPHY_BASE + 0x30))
void target_init(void)
{
/* detect family */
uint16_t product_code = __XTRACT(HW_DIGCTL_CHIPID, PRODUCT_CODE);
if(product_code >= 0x3600 && product_code < 0x3700)
{
logf("identified STMP3600 family\n");
g_stmp_family = STMP3600;
}
else if(product_code == 0x3700)
{
logf("identified STMP3700 family\n");
g_stmp_family = STMP3700;
}
else if(product_code == 0x37b0)
{
logf("identified STMP3770 family\n");
g_stmp_family = STMP3770;
}
else if(product_code == 0x3780)
{
logf("identified STMP3780 family\n");
g_stmp_family = STMP3780;
}
else
logf("cannot identify family: 0x%x\n", product_code);
if(g_stmp_family == STMP3600)
{
/* CPU clock is always derived from PLL, if we switch to PLL, cpu will
* run at 480 MHz unprepared ! That's bad so prepare to run at slow sleed
* (1.2MHz) for a safe transition */
HW_CLKCTRL_CPUCLKCTRL = HW_CLKCTRL_CPUCLKCTRL__WAIT_PLL_LOCK | 400;
/* We need to ensure that XBUS < HBUS but HBUS will be 1.2 MHz after the
* switch so lower XBUS too */
HW_CLKCTRL_XBUSCLKCTRL = 20;
/* Power PLL */
__REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
HW_CLKCTRL_PLLCTRL0 = (HW_CLKCTRL_PLLCTRL0 & ~0x3ff) | 480;
/* Wait lock */
while(!(HW_CLKCTRL_PLLCTRL1 & HW_CLKCTRL_PLLCTRL1__LOCK));
/* Switch to PLL source */
__REG_CLR(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__BYPASS;
/* Get back XBUS = 24 MHz and CPU = HBUS = 64MHz */
HW_CLKCTRL_CPUCLKCTRL = 7;
HW_CLKCTRL_HBUSCLKCTRL = 2;
HW_CLKCTRL_XBUSCLKCTRL = 1;
__REG_CLR(HW_CLKCTRL_UTMICLKCTRL) = HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK120M_GATE;
__REG_CLR(HW_CLKCTRL_UTMICLKCTRL) = HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK30M_GATE;
}
else
__REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
/* enable USB PHY PLL */
__REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS;
/* power up USB PHY */
__REG_CLR(HW_USBPHY_CTRL) = __BLOCK_CLKGATE | __BLOCK_SFTRST;
HW_USBPHY_PWD = 0;
/* enable USB controller */
__REG_CLR(HW_DIGCTL_CTRL) = HW_DIGCTL_CTRL__USB_CLKGATE;
}
static struct usb_resp_info_stmp_t g_stmp;
static struct usb_resp_info_target_t g_target =
{
.id = HWSTUB_TARGET_STMP,
.name = "STMP3600 / STMP3700 / STMP3780 (i.MX233)"
};
int target_get_info(int info, void **buffer)
{
if(info == HWSTUB_INFO_STMP)
{
g_stmp.chipid = __XTRACT(HW_DIGCTL_CHIPID, PRODUCT_CODE);
g_stmp.rev = __XTRACT(HW_DIGCTL_CHIPID, REVISION);
g_stmp.is_supported = g_stmp_family != 0;
*buffer = &g_stmp;
return sizeof(g_stmp);
}
else if(info == HWSTUB_INFO_TARGET)
{
*buffer = &g_target;
return sizeof(g_target);
}
else
return -1;
}
int target_atexit(int method)
{
g_atexit = method;
return 0;
}
void target_exit(void)
{
switch(g_atexit)
{
case HWSTUB_ATEXIT_OFF:
power_off();
// fallthrough in case of return
case HWSTUB_ATEXIT_REBOOT:
clkctrl_reset();
// fallthrough in case of return
case HWSTUB_ATEXIT_NOP:
default:
return;
}
}
void target_udelay(int us)
{
volatile uint32_t *reg = g_stmp_family == STMP3600 ? &HW_DIGCTL_MICROSECONDS2 : &HW_DIGCTL_MICROSECONDS;
uint32_t cur = *reg;
uint32_t end = cur + us;
if(cur < end)
while(*reg < end) {}
else
while(*reg >= cur) {}
}
void target_mdelay(int ms)
{
return target_udelay(ms * 1000);
}