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

Amit Tomar atomar25opensource at gmail.com
Mon Dec 14 16:02:38 CET 2020


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.


> +             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.

>
> > +                     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.

>
> Thanks,
>
-Amit


More information about the U-Boot mailing list