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:
Tom Rini
2021-07-09 21:08:52 -04:00
42 changed files with 737 additions and 136 deletions

View File

@@ -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 */

View File

@@ -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;
}