Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-sunxi
Aside from the usual fixes and updates one visible change is the MMC update, which fixes some lingering bugs and gives a decent speed increase on some boards (9->19 MB/s on H6, 21->43 MB/s on A64 eMMC). I am keeping an watchful eye on bug reports here, to spot any correctness regressions. Another change is finally the enablement of the first USB host port on many boards without micro-USB (data) sockets, like the Pine64 family. That doubles the number of usable USB ports from 1 to 2 on those boards. Some smaller fixes, 4GB DRAM support (on the H616) and a new board (ZeroPi) conclude this first round of changes. Compile-tested for all 157 sunxi boards, boot-tested on Pine H64, Pine64-LTS, OrangePi Zero 2 and BananaPi M2 Berry. Summary: - DT update for H3/H5/H6 - Enable first USB port on boards without micro-USB - ZeroPi board support - 4GB DRAM support for H616 boards - MMC fixes and speed improvement - some fixes
This commit is contained in:
@@ -23,6 +23,10 @@
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#ifndef CCM_MMC_CTRL_MODE_SEL_NEW
|
||||
#define CCM_MMC_CTRL_MODE_SEL_NEW 0
|
||||
#endif
|
||||
|
||||
struct sunxi_mmc_plat {
|
||||
struct mmc_config cfg;
|
||||
struct mmc mmc;
|
||||
@@ -33,7 +37,6 @@ struct sunxi_mmc_priv {
|
||||
uint32_t *mclkreg;
|
||||
unsigned fatal_err;
|
||||
struct gpio_desc cd_gpio; /* Change Detect GPIO */
|
||||
int cd_inverted; /* Inverted Card Detect */
|
||||
struct sunxi_mmc *reg;
|
||||
struct mmc_config cfg;
|
||||
};
|
||||
@@ -99,24 +102,29 @@ static int mmc_resource_init(int sdc_no)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* All A64 and later MMC controllers feature auto-calibration. This would
|
||||
* normally be detected via the compatible string, but we need something
|
||||
* which works in the SPL as well.
|
||||
*/
|
||||
static bool sunxi_mmc_can_calibrate(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_MACH_SUN50I) ||
|
||||
IS_ENABLED(CONFIG_MACH_SUN50I_H5) ||
|
||||
IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
|
||||
IS_ENABLED(CONFIG_MACH_SUN8I_R40);
|
||||
}
|
||||
|
||||
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
|
||||
{
|
||||
unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
|
||||
bool new_mode = true;
|
||||
bool calibrate = false;
|
||||
bool new_mode = IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE);
|
||||
u32 val = 0;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE))
|
||||
new_mode = false;
|
||||
|
||||
/* A83T support new mode only on eMMC */
|
||||
if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T) && priv->mmc_no != 2)
|
||||
new_mode = false;
|
||||
|
||||
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6)
|
||||
calibrate = true;
|
||||
#endif
|
||||
|
||||
if (hz <= 24000000) {
|
||||
pll = CCM_MMC_CTRL_OSCM24;
|
||||
pll_hz = 24000000;
|
||||
@@ -124,10 +132,14 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
|
||||
#ifdef CONFIG_MACH_SUN9I
|
||||
pll = CCM_MMC_CTRL_PLL_PERIPH0;
|
||||
pll_hz = clock_get_pll4_periph0();
|
||||
#elif defined(CONFIG_SUN50I_GEN_H6)
|
||||
pll = CCM_MMC_CTRL_PLL6X2;
|
||||
pll_hz = clock_get_pll6() * 2;
|
||||
#else
|
||||
/*
|
||||
* SoCs since the A64 (H5, H6, H616) actually use the doubled
|
||||
* rate of PLL6/PERIPH0 as an input clock, but compensate for
|
||||
* that with a fixed post-divider of 2 in the mod clock.
|
||||
* This cancels each other out, so for simplicity we just
|
||||
* pretend it's always PLL6 without a post divider here.
|
||||
*/
|
||||
pll = CCM_MMC_CTRL_PLL6;
|
||||
pll_hz = clock_get_pll6();
|
||||
#endif
|
||||
@@ -156,33 +168,27 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
|
||||
} else if (hz <= 25000000) {
|
||||
oclk_dly = 0;
|
||||
sclk_dly = 5;
|
||||
#ifdef CONFIG_MACH_SUN9I
|
||||
} else if (hz <= 52000000) {
|
||||
oclk_dly = 5;
|
||||
sclk_dly = 4;
|
||||
} else {
|
||||
/* hz > 52000000 */
|
||||
oclk_dly = 2;
|
||||
if (IS_ENABLED(CONFIG_MACH_SUN9I)) {
|
||||
if (hz <= 52000000)
|
||||
oclk_dly = 5;
|
||||
else
|
||||
oclk_dly = 2;
|
||||
} else {
|
||||
if (hz <= 52000000)
|
||||
oclk_dly = 3;
|
||||
else
|
||||
oclk_dly = 1;
|
||||
}
|
||||
sclk_dly = 4;
|
||||
#else
|
||||
} else if (hz <= 52000000) {
|
||||
oclk_dly = 3;
|
||||
sclk_dly = 4;
|
||||
} else {
|
||||
/* hz > 52000000 */
|
||||
oclk_dly = 1;
|
||||
sclk_dly = 4;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (new_mode) {
|
||||
#ifdef CONFIG_MMC_SUNXI_HAS_NEW_MODE
|
||||
#ifdef CONFIG_MMC_SUNXI_HAS_MODE_SWITCH
|
||||
val = CCM_MMC_CTRL_MODE_SEL_NEW;
|
||||
#endif
|
||||
val |= CCM_MMC_CTRL_MODE_SEL_NEW;
|
||||
setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW);
|
||||
#endif
|
||||
} else if (!calibrate) {
|
||||
}
|
||||
|
||||
if (!sunxi_mmc_can_calibrate()) {
|
||||
/*
|
||||
* Use hardcoded delay values if controller doesn't support
|
||||
* calibration
|
||||
@@ -240,14 +246,15 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
|
||||
rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
|
||||
writel(rval, &priv->reg->clkcr);
|
||||
|
||||
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6)
|
||||
#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
|
||||
/* A64 supports calibration of delays on MMC controller and we
|
||||
* have to set delay of zero before starting calibration.
|
||||
* Allwinner BSP driver sets a delay only in the case of
|
||||
* using HS400 which is not supported by mainline U-Boot or
|
||||
* Linux at the moment
|
||||
*/
|
||||
writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl);
|
||||
if (sunxi_mmc_can_calibrate())
|
||||
writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl);
|
||||
#endif
|
||||
|
||||
/* Re-enable Clock */
|
||||
@@ -303,8 +310,9 @@ static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc,
|
||||
SUNXI_MMC_STATUS_FIFO_FULL;
|
||||
unsigned i;
|
||||
unsigned *buff = (unsigned int *)(reading ? data->dest : data->src);
|
||||
unsigned byte_cnt = data->blocksize * data->blocks;
|
||||
unsigned timeout_msecs = byte_cnt >> 8;
|
||||
unsigned word_cnt = (data->blocksize * data->blocks) >> 2;
|
||||
unsigned timeout_msecs = word_cnt >> 6;
|
||||
uint32_t status;
|
||||
unsigned long start;
|
||||
|
||||
if (timeout_msecs < 2000)
|
||||
@@ -315,16 +323,38 @@ static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc,
|
||||
|
||||
start = get_timer(0);
|
||||
|
||||
for (i = 0; i < (byte_cnt >> 2); i++) {
|
||||
while (readl(&priv->reg->status) & status_bit) {
|
||||
for (i = 0; i < word_cnt;) {
|
||||
unsigned int in_fifo;
|
||||
|
||||
while ((status = readl(&priv->reg->status)) & status_bit) {
|
||||
if (get_timer(start) > timeout_msecs)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reading)
|
||||
buff[i] = readl(&priv->reg->fifo);
|
||||
else
|
||||
writel(buff[i], &priv->reg->fifo);
|
||||
/*
|
||||
* For writing we do not easily know the FIFO size, so have
|
||||
* to check the FIFO status after every word written.
|
||||
* TODO: For optimisation we could work out a minimum FIFO
|
||||
* size across all SoCs, and use that together with the current
|
||||
* fill level to write chunks of words.
|
||||
*/
|
||||
if (!reading) {
|
||||
writel(buff[i++], &priv->reg->fifo);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The status register holds the current FIFO level, so we
|
||||
* can be sure to collect as many words from the FIFO
|
||||
* register without checking the status register after every
|
||||
* read. That saves half of the costly MMIO reads, effectively
|
||||
* doubling the read performance.
|
||||
*/
|
||||
for (in_fifo = SUNXI_MMC_STATUS_FIFO_LEVEL(status);
|
||||
in_fifo > 0;
|
||||
in_fifo--)
|
||||
buff[i++] = readl_relaxed(&priv->reg->fifo);
|
||||
dmb();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -521,10 +551,11 @@ struct mmc *sunxi_mmc_init(int sdc_no)
|
||||
|
||||
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
cfg->host_caps = MMC_MODE_4BIT;
|
||||
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_SUN50I_GEN_H6)
|
||||
if (sdc_no == 2)
|
||||
|
||||
if ((IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_MACH_SUN8I) ||
|
||||
IS_ENABLED(CONFIG_SUN50I_GEN_H6)) && (sdc_no == 2))
|
||||
cfg->host_caps = MMC_MODE_8BIT;
|
||||
#endif
|
||||
|
||||
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
|
||||
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
|
||||
|
||||
@@ -580,12 +611,21 @@ static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
||||
|
||||
static int sunxi_mmc_getcd(struct udevice *dev)
|
||||
{
|
||||
struct mmc *mmc = mmc_get_mmc_dev(dev);
|
||||
struct sunxi_mmc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* If polling, assume that the card is always present. */
|
||||
if ((mmc->cfg->host_caps & MMC_CAP_NONREMOVABLE) ||
|
||||
(mmc->cfg->host_caps & MMC_CAP_NEEDS_POLL))
|
||||
return 1;
|
||||
|
||||
if (dm_gpio_is_valid(&priv->cd_gpio)) {
|
||||
int cd_state = dm_gpio_get_value(&priv->cd_gpio);
|
||||
|
||||
return cd_state ^ priv->cd_inverted;
|
||||
if (mmc->cfg->host_caps & MMC_CAP_CD_ACTIVE_HIGH)
|
||||
return !cd_state;
|
||||
else
|
||||
return cd_state;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -617,31 +657,29 @@ static int sunxi_mmc_probe(struct udevice *dev)
|
||||
struct mmc_config *cfg = &plat->cfg;
|
||||
struct ofnode_phandle_args args;
|
||||
u32 *ccu_reg;
|
||||
int bus_width, ret;
|
||||
int ret;
|
||||
|
||||
cfg->name = dev->name;
|
||||
bus_width = dev_read_u32_default(dev, "bus-width", 1);
|
||||
|
||||
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
cfg->host_caps = 0;
|
||||
if (bus_width == 8)
|
||||
cfg->host_caps |= MMC_MODE_8BIT;
|
||||
if (bus_width >= 4)
|
||||
cfg->host_caps |= MMC_MODE_4BIT;
|
||||
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
|
||||
cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
|
||||
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
|
||||
|
||||
cfg->f_min = 400000;
|
||||
cfg->f_max = 52000000;
|
||||
|
||||
priv->reg = (void *)dev_read_addr(dev);
|
||||
ret = mmc_of_parse(dev, cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->reg = dev_read_addr_ptr(dev);
|
||||
|
||||
/* We don't have a sunxi clock driver so find the clock address here */
|
||||
ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0,
|
||||
1, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
ccu_reg = (u32 *)ofnode_get_addr(args.node);
|
||||
ccu_reg = (u32 *)(uintptr_t)ofnode_get_addr(args.node);
|
||||
|
||||
priv->mmc_no = ((uintptr_t)priv->reg - SUNXI_MMC0_BASE) / 0x1000;
|
||||
priv->mclkreg = (void *)ccu_reg + get_mclk_offset() + priv->mmc_no * 4;
|
||||
@@ -659,17 +697,13 @@ static int sunxi_mmc_probe(struct udevice *dev)
|
||||
return ret;
|
||||
|
||||
/* This GPIO is optional */
|
||||
if (!dev_read_bool(dev, "non-removable") &&
|
||||
!gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
|
||||
if (!gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
|
||||
GPIOD_IS_IN)) {
|
||||
int cd_pin = gpio_get_number(&priv->cd_gpio);
|
||||
|
||||
sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
|
||||
}
|
||||
|
||||
/* Check if card detect is inverted */
|
||||
priv->cd_inverted = dev_read_bool(dev, "cd-inverted");
|
||||
|
||||
upriv->mmc = &plat->mmc;
|
||||
|
||||
/* Reset controller */
|
||||
|
||||
@@ -313,9 +313,21 @@ static int sun4i_usb_phy_init(struct phy *phy)
|
||||
data->cfg->disc_thresh, PHY_DISCON_TH_LEN);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_SUNXI
|
||||
/* Needed for HCI and conflicts with MUSB, keep PHY0 on MUSB */
|
||||
if (usb_phy->id != 0)
|
||||
sun4i_usb_phy_passby(phy, true);
|
||||
|
||||
/* Route PHY0 to MUSB to allow USB gadget */
|
||||
if (data->cfg->phy0_dual_route)
|
||||
sun4i_usb_phy0_reroute(data, true);
|
||||
#else
|
||||
sun4i_usb_phy_passby(phy, true);
|
||||
|
||||
sun4i_usb_phy0_reroute(data, true);
|
||||
/* Route PHY0 to HCI to allow USB host */
|
||||
if (data->cfg->phy0_dual_route)
|
||||
sun4i_usb_phy0_reroute(data, false);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user