提交 e64ebde1 编写于 作者: T Tom Rini

Merge tag 'mmc-2019-11-5' of https://gitlab.denx.de/u-boot/custodians/u-boot-mmc

- fsl_esdhc driver cleanup
- fsl_esdhc_imx driver improvement and compatible string update
...@@ -25,13 +25,6 @@ ...@@ -25,13 +25,6 @@
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
#define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \
IRQSTATEN_CINT | \
IRQSTATEN_CTOE | IRQSTATEN_CCE | IRQSTATEN_CEBE | \
IRQSTATEN_CIE | IRQSTATEN_DTOE | IRQSTATEN_DCE | \
IRQSTATEN_DEBE | IRQSTATEN_BRR | IRQSTATEN_BWR | \
IRQSTATEN_DINT)
struct fsl_esdhc { struct fsl_esdhc {
uint dsaddr; /* SDMA system address register */ uint dsaddr; /* SDMA system address register */
uint blkattr; /* Block attributes register */ uint blkattr; /* Block attributes register */
...@@ -82,8 +75,6 @@ struct fsl_esdhc_plat { ...@@ -82,8 +75,6 @@ struct fsl_esdhc_plat {
* @mmc: mmc * @mmc: mmc
* Following is used when Driver Model is enabled for MMC * Following is used when Driver Model is enabled for MMC
* @dev: pointer for the device * @dev: pointer for the device
* @non_removable: 0: removable; 1: non-removable
* @wp_enable: 1: enable checking wp; 0: no check
* @cd_gpio: gpio for card detection * @cd_gpio: gpio for card detection
* @wp_gpio: gpio for write protection * @wp_gpio: gpio for write protection
*/ */
...@@ -92,13 +83,10 @@ struct fsl_esdhc_priv { ...@@ -92,13 +83,10 @@ struct fsl_esdhc_priv {
unsigned int sdhc_clk; unsigned int sdhc_clk;
struct clk per_clk; struct clk per_clk;
unsigned int clock; unsigned int clock;
unsigned int bus_width;
#if !CONFIG_IS_ENABLED(DM_MMC) #if !CONFIG_IS_ENABLED(DM_MMC)
struct mmc *mmc; struct mmc *mmc;
#endif #endif
struct udevice *dev; struct udevice *dev;
int non_removable;
int wp_enable;
}; };
/* Return the XFERTYP flags for a given command and data packet */ /* Return the XFERTYP flags for a given command and data packet */
...@@ -241,12 +229,10 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, ...@@ -241,12 +229,10 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc,
#endif #endif
if (wml_value > WML_WR_WML_MAX) if (wml_value > WML_WR_WML_MAX)
wml_value = WML_WR_WML_MAX_VAL; wml_value = WML_WR_WML_MAX_VAL;
if (priv->wp_enable) {
if ((esdhc_read32(&regs->prsstat) & if (!(esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL)) {
PRSSTAT_WPSPL) == 0) { printf("Can not write to locked SD card.\n");
printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return -EINVAL;
return -ETIMEDOUT;
}
} }
esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK, esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK,
...@@ -636,236 +622,42 @@ static int esdhc_getcd_common(struct fsl_esdhc_priv *priv) ...@@ -636,236 +622,42 @@ static int esdhc_getcd_common(struct fsl_esdhc_priv *priv)
if (CONFIG_ESDHC_DETECT_QUIRK) if (CONFIG_ESDHC_DETECT_QUIRK)
return 1; return 1;
#endif #endif
#if CONFIG_IS_ENABLED(DM_MMC)
if (priv->non_removable)
return 1;
#endif
while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout) while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
udelay(1000); udelay(1000);
return timeout > 0; return timeout > 0;
} }
static int esdhc_reset(struct fsl_esdhc *regs) static void fsl_esdhc_get_cfg_common(struct fsl_esdhc_priv *priv,
{ struct mmc_config *cfg)
ulong start;
/* reset the controller */
esdhc_setbits32(&regs->sysctl, SYSCTL_RSTA);
/* hardware clears the bit when it is done */
start = get_timer(0);
while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA)) {
if (get_timer(start) > 100) {
printf("MMC/SD: Reset never completed.\n");
return -ETIMEDOUT;
}
}
return 0;
}
#if !CONFIG_IS_ENABLED(DM_MMC)
static int esdhc_getcd(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_getcd_common(priv);
}
static int esdhc_init(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_init_common(priv, mmc);
}
static int esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_send_cmd_common(priv, mmc, cmd, data);
}
static int esdhc_set_ios(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_set_ios_common(priv, mmc);
}
static const struct mmc_ops esdhc_ops = {
.getcd = esdhc_getcd,
.init = esdhc_init,
.send_cmd = esdhc_send_cmd,
.set_ios = esdhc_set_ios,
};
#endif
static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
struct fsl_esdhc_plat *plat)
{ {
struct mmc_config *cfg; struct fsl_esdhc *regs = priv->esdhc_regs;
struct fsl_esdhc *regs; u32 caps;
u32 caps, voltage_caps;
int ret;
if (!priv)
return -EINVAL;
regs = priv->esdhc_regs;
/* First reset the eSDHC controller */
ret = esdhc_reset(regs);
if (ret)
return ret;
esdhc_setbits32(&regs->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN |
SYSCTL_IPGEN | SYSCTL_CKEN);
writel(SDHCI_IRQ_EN_BITS, &regs->irqstaten);
cfg = &plat->cfg;
#ifndef CONFIG_DM_MMC
memset(cfg, '\0', sizeof(*cfg));
#endif
voltage_caps = 0;
caps = esdhc_read32(&regs->hostcapblt); caps = esdhc_read32(&regs->hostcapblt);
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135 #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135
caps = caps & ~(ESDHC_HOSTCAPBLT_SRS | caps &= ~(HOSTCAPBLT_SRS | HOSTCAPBLT_VS18 | HOSTCAPBLT_VS30);
ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30);
#endif #endif
/* T4240 host controller capabilities register should have VS33 bit */
#ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33 #ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33
caps = caps | ESDHC_HOSTCAPBLT_VS33; caps |= HOSTCAPBLT_VS33;
#endif #endif
if (caps & HOSTCAPBLT_VS18)
if (caps & ESDHC_HOSTCAPBLT_VS18) cfg->voltages |= MMC_VDD_165_195;
voltage_caps |= MMC_VDD_165_195; if (caps & HOSTCAPBLT_VS30)
if (caps & ESDHC_HOSTCAPBLT_VS30) cfg->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
voltage_caps |= MMC_VDD_29_30 | MMC_VDD_30_31; if (caps & HOSTCAPBLT_VS33)
if (caps & ESDHC_HOSTCAPBLT_VS33) cfg->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
cfg->name = "FSL_SDHC"; cfg->name = "FSL_SDHC";
#if !CONFIG_IS_ENABLED(DM_MMC)
cfg->ops = &esdhc_ops;
#endif
#ifdef CONFIG_SYS_SD_VOLTAGE
cfg->voltages = CONFIG_SYS_SD_VOLTAGE;
#else
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
#endif
if ((cfg->voltages & voltage_caps) == 0) {
printf("voltage not supported by controller\n");
return -1;
}
if (priv->bus_width == 8)
cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
else if (priv->bus_width == 4)
cfg->host_caps = MMC_MODE_4BIT;
cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
if (priv->bus_width > 0) {
if (priv->bus_width < 8)
cfg->host_caps &= ~MMC_MODE_8BIT;
if (priv->bus_width < 4)
cfg->host_caps &= ~MMC_MODE_4BIT;
}
if (caps & ESDHC_HOSTCAPBLT_HSS) if (caps & HOSTCAPBLT_HSS)
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
cfg->host_caps &= ~MMC_MODE_8BIT;
#endif
cfg->f_min = 400000; cfg->f_min = 400000;
cfg->f_max = min(priv->sdhc_clk, (u32)200000000); cfg->f_max = min(priv->sdhc_clk, (u32)200000000);
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
return 0;
} }
#if !CONFIG_IS_ENABLED(DM_MMC)
static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg,
struct fsl_esdhc_priv *priv)
{
if (!cfg || !priv)
return -EINVAL;
priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base);
priv->bus_width = cfg->max_bus_width;
priv->sdhc_clk = cfg->sdhc_clk;
priv->wp_enable = cfg->wp_enable;
return 0;
};
int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
{
struct fsl_esdhc_plat *plat;
struct fsl_esdhc_priv *priv;
struct mmc *mmc;
int ret;
if (!cfg)
return -EINVAL;
priv = calloc(sizeof(struct fsl_esdhc_priv), 1);
if (!priv)
return -ENOMEM;
plat = calloc(sizeof(struct fsl_esdhc_plat), 1);
if (!plat) {
free(priv);
return -ENOMEM;
}
ret = fsl_esdhc_cfg_to_priv(cfg, priv);
if (ret) {
debug("%s xlate failure\n", __func__);
free(plat);
free(priv);
return ret;
}
ret = fsl_esdhc_init(priv, plat);
if (ret) {
debug("%s init failure\n", __func__);
free(plat);
free(priv);
return ret;
}
mmc = mmc_create(&plat->cfg, priv);
if (!mmc)
return -EIO;
priv->mmc = mmc;
return 0;
}
int fsl_esdhc_mmc_init(bd_t *bis)
{
struct fsl_esdhc_cfg *cfg;
cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1);
cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR;
cfg->sdhc_clk = gd->arch.sdhc_clk;
return fsl_esdhc_initialize(bis, cfg);
}
#endif
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
void mmc_adapter_card_type_ident(void) void mmc_adapter_card_type_ident(void)
{ {
...@@ -939,7 +731,106 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) ...@@ -939,7 +731,106 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd)
} }
#endif #endif
#if CONFIG_IS_ENABLED(DM_MMC) #if !CONFIG_IS_ENABLED(DM_MMC)
static int esdhc_getcd(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_getcd_common(priv);
}
static int esdhc_init(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_init_common(priv, mmc);
}
static int esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_send_cmd_common(priv, mmc, cmd, data);
}
static int esdhc_set_ios(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = mmc->priv;
return esdhc_set_ios_common(priv, mmc);
}
static const struct mmc_ops esdhc_ops = {
.getcd = esdhc_getcd,
.init = esdhc_init,
.send_cmd = esdhc_send_cmd,
.set_ios = esdhc_set_ios,
};
int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
{
struct fsl_esdhc_plat *plat;
struct fsl_esdhc_priv *priv;
struct mmc_config *mmc_cfg;
struct mmc *mmc;
if (!cfg)
return -EINVAL;
priv = calloc(sizeof(struct fsl_esdhc_priv), 1);
if (!priv)
return -ENOMEM;
plat = calloc(sizeof(struct fsl_esdhc_plat), 1);
if (!plat) {
free(priv);
return -ENOMEM;
}
priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base);
priv->sdhc_clk = cfg->sdhc_clk;
mmc_cfg = &plat->cfg;
if (cfg->max_bus_width == 8) {
mmc_cfg->host_caps |= MMC_MODE_1BIT | MMC_MODE_4BIT |
MMC_MODE_8BIT;
} else if (cfg->max_bus_width == 4) {
mmc_cfg->host_caps |= MMC_MODE_1BIT | MMC_MODE_4BIT;
} else if (cfg->max_bus_width == 1) {
mmc_cfg->host_caps |= MMC_MODE_1BIT;
} else {
mmc_cfg->host_caps |= MMC_MODE_1BIT | MMC_MODE_4BIT |
MMC_MODE_8BIT;
printf("No max bus width provided. Assume 8-bit supported.\n");
}
#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
mmc_cfg->host_caps &= ~MMC_MODE_8BIT;
#endif
mmc_cfg->ops = &esdhc_ops;
fsl_esdhc_get_cfg_common(priv, mmc_cfg);
mmc = mmc_create(mmc_cfg, priv);
if (!mmc)
return -EIO;
priv->mmc = mmc;
return 0;
}
int fsl_esdhc_mmc_init(bd_t *bis)
{
struct fsl_esdhc_cfg *cfg;
cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1);
cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR;
cfg->sdhc_clk = gd->arch.sdhc_clk;
return fsl_esdhc_initialize(bis, cfg);
}
#else /* DM_MMC */
#ifndef CONFIG_PPC #ifndef CONFIG_PPC
#include <asm/arch/clock.h> #include <asm/arch/clock.h>
#endif #endif
...@@ -949,7 +840,6 @@ static int fsl_esdhc_probe(struct udevice *dev) ...@@ -949,7 +840,6 @@ static int fsl_esdhc_probe(struct udevice *dev)
struct fsl_esdhc_plat *plat = dev_get_platdata(dev); struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc_priv *priv = dev_get_priv(dev);
fdt_addr_t addr; fdt_addr_t addr;
unsigned int val;
struct mmc *mmc; struct mmc *mmc;
int ret; int ret;
...@@ -963,22 +853,6 @@ static int fsl_esdhc_probe(struct udevice *dev) ...@@ -963,22 +853,6 @@ static int fsl_esdhc_probe(struct udevice *dev)
#endif #endif
priv->dev = dev; priv->dev = dev;
val = dev_read_u32_default(dev, "bus-width", -1);
if (val == 8)
priv->bus_width = 8;
else if (val == 4)
priv->bus_width = 4;
else
priv->bus_width = 1;
if (dev_read_bool(dev, "non-removable")) {
priv->non_removable = 1;
} else {
priv->non_removable = 0;
}
priv->wp_enable = 1;
if (IS_ENABLED(CONFIG_CLK)) { if (IS_ENABLED(CONFIG_CLK)) {
/* Assigned clock already set clock */ /* Assigned clock already set clock */
ret = clk_get_by_name(dev, "per", &priv->per_clk); ret = clk_get_by_name(dev, "per", &priv->per_clk);
...@@ -1005,11 +879,7 @@ static int fsl_esdhc_probe(struct udevice *dev) ...@@ -1005,11 +879,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
} }
} }
ret = fsl_esdhc_init(priv, plat); fsl_esdhc_get_cfg_common(priv, &plat->cfg);
if (ret) {
dev_err(dev, "fsl_esdhc_init failure\n");
return ret;
}
mmc_of_parse(dev, &plat->cfg); mmc_of_parse(dev, &plat->cfg);
...@@ -1024,8 +894,12 @@ static int fsl_esdhc_probe(struct udevice *dev) ...@@ -1024,8 +894,12 @@ static int fsl_esdhc_probe(struct udevice *dev)
static int fsl_esdhc_get_cd(struct udevice *dev) static int fsl_esdhc_get_cd(struct udevice *dev)
{ {
struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc_priv *priv = dev_get_priv(dev);
if (plat->cfg.host_caps & MMC_CAP_NONREMOVABLE)
return 1;
return esdhc_getcd_common(priv); return esdhc_getcd_common(priv);
} }
......
...@@ -627,9 +627,6 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) ...@@ -627,9 +627,6 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock)
int sdhc_clk = priv->sdhc_clk; int sdhc_clk = priv->sdhc_clk;
uint clk; uint clk;
if (clock < mmc->cfg->f_min)
clock = mmc->cfg->f_min;
while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256) while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256)
pre_div *= 2; pre_div *= 2;
...@@ -958,6 +955,7 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) ...@@ -958,6 +955,7 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
{ {
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
int ret __maybe_unused; int ret __maybe_unused;
u32 clock;
#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
/* Select to use peripheral clock */ /* Select to use peripheral clock */
...@@ -966,8 +964,12 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) ...@@ -966,8 +964,12 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
esdhc_clock_control(priv, true); esdhc_clock_control(priv, true);
#endif #endif
/* Set the clock speed */ /* Set the clock speed */
if (priv->clock != mmc->clock) clock = mmc->clock;
set_sysctl(priv, mmc, mmc->clock); if (clock < mmc->cfg->f_min)
clock = mmc->cfg->f_min;
if (priv->clock != clock)
set_sysctl(priv, mmc, clock);
#ifdef MMC_SUPPORTS_TUNING #ifdef MMC_SUPPORTS_TUNING
if (mmc->clk_disable) { if (mmc->clk_disable) {
...@@ -1645,6 +1647,9 @@ static const struct udevice_id fsl_esdhc_ids[] = { ...@@ -1645,6 +1647,9 @@ static const struct udevice_id fsl_esdhc_ids[] = {
{ .compatible = "fsl,imx7d-usdhc", .data = (ulong)&usdhc_imx7d_data,}, { .compatible = "fsl,imx7d-usdhc", .data = (ulong)&usdhc_imx7d_data,},
{ .compatible = "fsl,imx7ulp-usdhc", }, { .compatible = "fsl,imx7ulp-usdhc", },
{ .compatible = "fsl,imx8qm-usdhc", .data = (ulong)&usdhc_imx8qm_data,}, { .compatible = "fsl,imx8qm-usdhc", .data = (ulong)&usdhc_imx8qm_data,},
{ .compatible = "fsl,imx8mm-usdhc", .data = (ulong)&usdhc_imx8qm_data,},
{ .compatible = "fsl,imx8mn-usdhc", .data = (ulong)&usdhc_imx8qm_data,},
{ .compatible = "fsl,imx8mq-usdhc", .data = (ulong)&usdhc_imx8qm_data,},
{ .compatible = "fsl,esdhc", }, { .compatible = "fsl,esdhc", },
{ /* sentinel */ } { /* sentinel */ }
}; };
......
...@@ -156,18 +156,18 @@ ...@@ -156,18 +156,18 @@
#define BLKATTR_SIZE(x) (x & 0x1fff) #define BLKATTR_SIZE(x) (x & 0x1fff)
#define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */ #define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */
#define ESDHC_HOSTCAPBLT_VS18 0x04000000 /* Host controller capabilities register */
#define ESDHC_HOSTCAPBLT_VS30 0x02000000 #define HOSTCAPBLT_VS18 0x04000000
#define ESDHC_HOSTCAPBLT_VS33 0x01000000 #define HOSTCAPBLT_VS30 0x02000000
#define ESDHC_HOSTCAPBLT_SRS 0x00800000 #define HOSTCAPBLT_VS33 0x01000000
#define ESDHC_HOSTCAPBLT_DMAS 0x00400000 #define HOSTCAPBLT_SRS 0x00800000
#define ESDHC_HOSTCAPBLT_HSS 0x00200000 #define HOSTCAPBLT_DMAS 0x00400000
#define HOSTCAPBLT_HSS 0x00200000
struct fsl_esdhc_cfg { struct fsl_esdhc_cfg {
phys_addr_t esdhc_base; phys_addr_t esdhc_base;
u32 sdhc_clk; u32 sdhc_clk;
u8 max_bus_width; u8 max_bus_width;
int wp_enable;
int vs18_enable; /* Use 1.8V if set to 1 */ int vs18_enable; /* Use 1.8V if set to 1 */
struct mmc_config cfg; struct mmc_config cfg;
}; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册