[PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700

André Przywara andre.przywara at arm.com
Mon Dec 14 16:43:36 CET 2020


On 14/12/2020 15:02, Amit Tomar wrote:
> Hi,
> 
> You could pass a pointer to owl_mmc_priv here, which can hold both those
> 
>     values. See below for more more on this.
> 
> Did you mean for argument 3rd and 4th but then in that case how one
> could have differentiated
> between source and destination.
> 
> for instance 3rd and 4th arguments are different for read and write flag.

No, just the first two arguments (that's why I put the comment after
this line).

> 
> 
>     > +             unsigned int src, unsigned int dst,
>     > +             unsigned int len)
>     > +{
>     > +     unsigned int mode = drq;
>     > +     u8 frame_cnt = 0x1;
> 
>     Not needed, just use the 0x1 below directly.
> 
> 
> Ok.
> 
> 
>     > +
>     > +     /* Set Source and Destination adderess mode */
>     > +     mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST |
>     DMA_MODE_DT_DCU |
>     > +                     DMA_MODE_DAM_INC);
>     > +
>     > +     writel(mode, dma_base + DMA_MODE);
>     > +     writel(src, dma_base + DMA_SOURCE);
>     > +     writel(dst, dma_base + DMA_DESTINATION);
>     > +     writel(len, dma_base + DMA_FRAME_LEN);
>     > +     writel(frame_cnt, dma_base + DMA_FRAME_CNT);
>     > +}
>     > +
>     > +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
>     > +             struct mmc_data *data)
>     > +{
>     > +     unsigned int reg, total;
>     > +     uint32_t buf = 0;
>     > +
>     > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +     reg |= OWL_SD_EN_BSEL;
>     > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +     writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
>     > +     writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
>     > +     total = data->blocksize * data->blocks;
>     > +
>     > +     if (data->blocksize < 512)
>     > +             writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
>     > +     else
>     > +             writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
>     > +
>     > +     /* DMA STOP */
>     > +     writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +
>     > +     if (data) {
>     > +             if (data->flags == MMC_DATA_READ) {
> 
>     Shouldn't that be used with '&', since it's one flag among others?
> 
> 
> ,Actually, I wasn't sure about some driver uses == and other one &, even
> here in Mainline
> U-boot.

Well, flags somewhat suggests it's a bitmap, but indeed mmc.c just
assigns a value, and "1" and "2" would work in both ways.

>     > +                     buf = (ulong) (data->dest);
>     > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
>     > +                                     priv->dma_drq,
>     > +                                     (ulong) priv->reg_base +
>     > +                                     OWL_REG_SD_DAT,
>     > +                                     buf, total);
>     > +                     invalidate_dcache_range(buf, buf + total);
>     > +             } else if (data->flags == MMC_DATA_WRITE) {
>     > +                     buf = (ulong) (data->src);
>     > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
>     > +                                     priv->dma_drq,
>     > +                                     buf,
>     > +                                     (ulong) priv->reg_base +
>     > +                                     OWL_REG_SD_DAT,
>     > +                                     total);
>     > +                     flush_dcache_range(buf, buf + total);
>     > +             }
>     > +             /* DMA START */
>     > +             writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +     }
>     > +}
>     > +
>     > +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>     > +             struct mmc_data *data)
>     > +{
>     > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +     unsigned int cmd_rsp_mask, mode, reg;
>     > +     int timeout = DATA_TRANSFER_TIMEOUT;
>     > +
>     > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +     reg |= OWL_SD_ENABLE;
>     > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +     /* setup response */
>     > +     switch (cmd->resp_type) {
>     > +     case MMC_RSP_NONE:
>     > +             break;
>     > +     case MMC_RSP_R1:
>     > +             if (data) {
>     > +                     if (data->flags == MMC_DATA_READ)
>     > +                             mode = OWL_SD_CTL_TM(4);
>     > +                     else
>     > +                             mode = OWL_SD_CTL_TM(5);
>     > +             } else {
>     > +                     mode = OWL_SD_CTL_TM(1);
>     > +             }
>     > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +             break;
>     > +     case MMC_RSP_R1b:
>     > +             mode = OWL_SD_CTL_TM(3);
>     > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +             break;
>     > +     case MMC_RSP_R2:
>     > +             mode = OWL_SD_CTL_TM(2);
>     > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +             break;
>     > +     case MMC_RSP_R3:
>     > +             cmd->cmdarg = 0x40ff8000;
>     > +             mode = OWL_SD_CTL_TM(1);
>     > +             cmd_rsp_mask = OWL_SD_STATE_CLNR;
>     > +             break;
>     > +     default:
>     > +             printf("error: no math command RSP flag %x\n",
>     cmd->cmdarg);
>     > +             return -1;
>     > +     }
>     > +
>     > +     mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
>     > +
>     > +     /* setup command */
>     > +     writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
>     > +     writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
>     > +
>     > +     /* Set LBE to send clk at the end of last read block */
>     > +     if (data)
>     > +             mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
>     > +     else
>     > +             mode |= OWL_SD_CTL_TS;
>     > +
>     > +     if (data)
>     > +             owl_mmc_prepare_data(priv, data);
>     > +
>     > +     /* Start transfer */
>     > +     writel(mode, priv->reg_base + OWL_REG_SD_CTL);
>     > +
>     > +     while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
>     > +                     && timeout--)
>     > +             udelay(20);
>     > +
>     > +     if (!timeout) {
>     > +             printf("error: transferred data timeout\n");
>     > +             return -ETIMEDOUT;
>     > +     }
>     > +
>     > +     if (cmd->resp_type & MMC_RSP_136) {
>     > +             cmd->response[3] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF0);
>     > +             cmd->response[2] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF1);
>     > +             cmd->response[1] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF2);
>     > +             cmd->response[0] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF3);
>     > +     } else {
>     > +             u32 rsp[2];
>     > +
>     > +             rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
>     > +             rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
>     > +             cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
>     > +             cmd->response[1] = rsp[1] >> 8;
>     > +     }
>     > +
>     > +     if (data) {
>     > +             /* DMA STOP */
>     > +             writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +             /* Transmission STOP */
>     > +             while (readl(priv->reg_base + OWL_REG_SD_CTL) &
>     OWL_SD_CTL_TS)
>     > +                     clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
>     > +                                     OWL_SD_CTL_TS);
>     > +     }
>     > +
>     > +     return 0;
>     > +}
>     > +
>     > +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
>     > +{
>     > +     u32 reg;
>     > +
>     > +     reg = readl(priv->reg_base + OWL_REG_SD_CTL);
>     > +     reg &= ~OWL_SD_CTL_DELAY_MSK;
>     > +
>     > +     /* Set RDELAY and WDELAY based on the clock */
>     > +     if (rate <= 1000000) {
>     > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
>     > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
>     > +                     priv->reg_base + OWL_REG_SD_CTL);
>     > +     } else if ((rate > 1000000) && (rate <= 26000000)) {
>     > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
>     > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
>     > +                     priv->reg_base + OWL_REG_SD_CTL);
>     > +     } else if ((rate > 26000000) && (rate <= 52000000)) {
>     > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
>     > +                     OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
>     > +                     priv->reg_base + OWL_REG_SD_CTL);
>     > +     } else {
>     > +             printf("SD clock rate not supported\n");
>     > +     }
>     > +}
>     > +
>     > +static int owl_mmc_set_ios(struct udevice *dev)
>     > +{
>     > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
>     > +     struct mmc *mmc = &plat->mmc;
>     > +     u32 reg, ret;
>     > +
>     > +     if (mmc->clock != priv->clock) {
>     > +             priv->clock = mmc->clock;
>     > +             owl_mmc_clk_set(priv, mmc->clock);
>     > +     }
>     > +
>     > +     ret = clk_set_rate(&priv->clk, mmc->clock);
>     > +     if (IS_ERR_VALUE(ret))
>     > +             return ret;
>     > +
>     > +     ret = clk_enable(&priv->clk);
>     > +     if (ret)
>     > +             return ret;
>     > +
>     > +     /* Set the Bus width */
>     > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +     reg &= ~0x03;
>     > +     if (mmc->bus_width == 8)
>     > +             reg |= OWL_SD_EN_DATAWID(2);
>     > +     else if (mmc->bus_width == 4)
>     > +             reg |= OWL_SD_EN_DATAWID(1);
>     > +
>     > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +     return 0;
>     > +}
>     > +
>     > +static const struct dm_mmc_ops owl_mmc_ops = {
>     > +     .send_cmd       = owl_mmc_send_cmd,
>     > +     .set_ios        = owl_mmc_set_ios,
>     > +};
>     > +
>     > +static int owl_mmc_probe(struct udevice *dev)
>     > +{
>     > +     struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>     > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
>     > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +     struct mmc_config *cfg = &plat->cfg;
>     > +     int bus_width, ret;
>     > +     fdt_addr_t addr;
>     > +
>     > +     cfg->name = dev->name;
>     > +     cfg->voltages = OWL_MMC_OCR;
>     > +     cfg->f_min = 400000;
>     > +     cfg->f_max = 52000000;
>     > +     cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>     > +
>     > +     bus_width = dev_read_u32_default(dev, "bus-width", 1);
>     > +     switch (bus_width) {
>     > +     case 8:
>     > +             cfg->host_caps |= MMC_MODE_8BIT;
>     > +             /* Hosts capable of 8-bit transfers can also do 4
>     bits */
>     > +     case 4:
>     > +             cfg->host_caps |= MMC_MODE_4BIT;
>     > +             break;
>     > +     case 1:
>     > +             break;
>     > +     default:
>     > +             printf("Invalid bus-width value %u\n", bus_width);
>     > +     }
>     > +     cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
>     > +
>     > +     addr = dev_read_addr(dev);
>     > +     if (addr == FDT_ADDR_T_NONE)
>     > +             return -EINVAL;
>     > +
>     > +     priv->reg_base = (void *)addr;
>     > +     priv->dma_drq  = 0x2;
>     > +
>     > +     ret = clk_get_by_index(dev, 0, &priv->clk);
>     > +     if (ret) {
>     > +             debug("clk_get_by_index() failed: %d\n", ret);
>     > +             return ret;
>     > +     }
> 
>     You should read the DMA parameters from the DT here. What you currently
>     hard code (channel 2) only works for the first MMC.
>     It should be fairly straight-forward to follow the phandle and get the
>     base address of the DMA device, and to read the second argument to learn
>     the DRQ number. Then put this into priv and use it in dma_config().
> 
>     Yeah, I initially thought about it but I wasn't sure that This
>     device trigger value is exactly matching to
> 
>    DAM channel number, So I left the idea of having it.

You should be able to verify this in in the Linux code.

Cheers,
Andre


More information about the U-Boot mailing list