Re: [RFC PATCH v2 1/1] mmc: sprd: add MMC host driver for Spreadtrum SoC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2015/8/12 19:01, Hongtao Wu wrote:


On Sat, Aug 8, 2015 at 5:20 PM, Shawn Lin <shawn.lin@xxxxxxxxxxxxxx
<mailto:shawn.lin@xxxxxxxxxxxxxx>> wrote:

    On 2015/8/4 21:16, Hongtao Wu wrote:

        This patch adds MMC host driver for Spreadtrum SoC.
        The following coding style may be not meet kernel coding style.
        I am not sure this kind of coding style is better or worse.
        1) A macro that represent some bits of a register is added a
        prefix "__",
              for example:
              #define SDHOST_16_HOST_CTRL_2   0x3E
              #define __TIMING_MODE_SDR12     0x0000
              #define __TIMING_MODE_SDR25     0x0001
              #define __TIMING_MODE_SDR50     0x0002
              I think it is more useful to distinguish a register from a
        bit of this
              register.
        2) A function in order to operate a register is also added a
        prefix "_".
              If the functions(A) call other function(B), we added a
        prefix "__" before B,
              for example:
              static inline void _sdhost_enable_int(struct sdhost_host
        *host, u32 mask)
              {
                  __local_writel(mask, host, SDHOST_32_INT_ST_EN);
                  __local_writel(mask, host, SDHOST_32_INT_SIG_EN);
              }
              I think this make the relationship of the function call
        more explicit.

        Hi Shawn,

        Thanks for your kindly reply.
        According to your suggestion, I modified the following points:
        1) delete some redundant mdelay().
        2) Add error handling in some functions.

    pls add a Series-changes tag to detail the diff between v1 & v2


Thanks for your reply.
I am sorry! I will add the patch changes in the next version.

Changes in v2:
- delete some redundant mdelay()
- Add error handling in some functions


        Signed-off-by: Billows Wu(WuHongtao) <wuht06@xxxxxxxxx
        <mailto:wuht06@xxxxxxxxx>>
        ---


    [...]

        +static void _send_cmd(struct sdhost_host *host, struct
        mmc_command *cmd)
        +{
        +       struct mmc_data *data = cmd->data;
        +       int sg_cnt;
        +       u32 flag = 0;
        +       u16 rsp_type = 0;
        +       int if_has_data = 0;
        +       int if_mult = 0;
        +       int if_read = 0;
        +       int if_dma = 0;
        +       u16 auto_cmd = __ACMD_DIS;
        +
        +       pr_debug("%s(%s)  CMD%d, arg 0x%x, flag 0x%x\n", __func__,
        +              host->device_name, cmd->opcode, cmd->arg,
        cmd->flags);
        +       if (cmd->data)
        +               pr_debug("%s(%s) block size %d, cnt %d\n", __func__,
        +                      host->device_name, cmd->data->blksz,
        cmd->data->blocks);
        +
        +       _sdhost_disable_all_int(host);
        +
        +       if (38 == cmd->opcode) {


    It would be nice to use "MMC_ERASE " instear of "38"


You are right. I will change it.


        +               /* if it is erase command , it's busy time will
        long,
        +                * so we set long timeout value here.
        +                */
        +               mod_timer(&host->timer, jiffies + 10 * HZ);


    how can you get 10*HZ?
    Actually, something should be diff between secure/nosecure
    erase/trim/discard


    mmc_erase_timeout does calculate the busy time yet.
    Might you can get busytime from cmd.busy_timeout!


Actually, we only use nosecure erase in our application program.
Once we received busytime from cmd.busy_timeout, but we encountered a lot of
problems because of off-standard emmc chips . So now we use a longer timeout
value(10*HZ). But we will change this value to max timeout value.


Thanks for clarifying. Yes, some eMMCs with bad FTL design do hold too much busy time while performing GC or programming. So we should not be too "spec" sometime and I also find SDHCI setup timer for 10HZ there to
finish its transfer. That's okay.


        +               _sdhost_writeb(host, __DATA_TIMEOUT_MAX_VAL,
        SDHOST_8_TIMEOUT);
        +       } else {
        +               mod_timer(&host->timer, jiffies + 3 * HZ);


    Ditto.

        +               _sdhost_writeb(host, host->data_timeout_val,
        SDHOST_8_TIMEOUT);
        +       }
        +


    [...]

        +                       _sdhost_writel(host,
        sg_dma_address(data->sg),
        +                                      SDHOST_32_SYS_ADDR);
        +               } else {
        +                       WARN_ON(1);


    Why need dump here?

There is no need to do it. I will delete it.



        +                       flag |= _INT_ERR_ADMA;
        +                       _sdhost_set_dma(host, __32ADMA_MOD);
        +                       _sdhost_set_32_blk_cnt(host, data->blocks);
        +                       _sdhost_writel(host,
        sg_dma_address(data->sg),


    [...]

        +       pr_debug("sdhost %s CMD%d rsp:0x%x intflag:0x%x\n"
        +              "if_mult:0x%x if_read:0x%x auto_cmd:0x%x
        if_dma:0x%x\n",
        +              host->device_name, cmd->opcode, mmc_resp_type(cmd),
        +              flag, if_mult, if_read, auto_cmd, if_dma);
        +


    No warning from checkpatch?

I check it with checkpatch, and I  don't find any waning message.

okay.



        +       _sdhost_set_trans_and_cmd(host, if_mult, if_read,
        auto_cmd, if_mult,
        +                                 if_dma, cmd->opcode,
        if_has_data, rsp_type);
        +}
        +
        +static irqreturn_t _irq(int irq, void *param)
        +{
        +       /* maybe _timeout_func run in one core and _irq run in
        +        * another core, this will panic if access cmd->data
        +        */
        +       if ((!mrq) || (!cmd)) {


    It would be nice if you can use "goto out" here.


You are right. I will change it.



        +               spin_unlock(&host->lock);
        +               return IRQ_NONE;
        +       }
        +       data = cmd->data;
        +
        +       intmask = _sdhost_readl(host, SDHOST_32_INT_ST);
        +       if (!intmask) {


    Ditto.

        +               spin_unlock(&host->lock);
        +               return IRQ_NONE;
        +       }
        +       pr_debug("%s(%s) CMD%d, intmask 0x%x, filter = 0x%x\n",
        __func__,
        +              host->device_name, cmd->opcode, intmask,
        host->int_filter);
        +
        +       /* disable unused interrupt */


    disable or clear ?


Clear the unused interrupt. Sorry, we will change it.


        +       _sdhost_clear_int(host, intmask);
        +       /* just care about the interrupt that we want */
        +       intmask &= host->int_filter;


    It's not a good idea. If you don't care a irq, disable it while probing.


This is our emmc controller speciality.


        +
        +       while (intmask) {
        +               if (_INT_FILTER_ERR & intmask) {
        +                       /* some error happened in command */


    [...]

        +                               _send_cmd(host, mrq->stop);


    Are you sure it's fine to call _send_cmd in irq_handler not half
    bottom? I wonder about the performance.


Actually, Only current command error happended in data token, we use
_send_cmd() to send cmd12 to spop it.
Normally, we don't call _send_cmd in irq_ handler. So I don't think it
will decrease the perfomance.


        +                       } else {
        +                               /* request finish with error, so
        reset it and
        +                                * stop the request
        +                                */
        +                               _sdhost_reset(host, _RST_CMD |
        _RST_DATA);


    [...]

        +
        +static void sdhost_hw_reset(struct mmc_host *mmc)
        +{


    hw_reset means host trigger RST_n io of emmc to let it enter pre-idle
    and init card for the first time or for err recovery if ext_csd
    enable the reset bit. Your controller doesn't have rst pin? Even a
    gpio is okay.


    sdhost_reset_emmc for what?


  Our controller has a reset pin and the shost_reset_emmc() is for this
reset pin.
We don't use this gpio, because it is sensitive. This may reset our
controller out of our control.


Any pull-up configure for this pin?
sdhost_reset_emmc defined but not be used now, you might consider removing it.



        +       struct sdhost_host *host = mmc_priv(mmc);
        +
        +       _runtime_get(host);
        +
        +       /* close LDO and open LDO again. */
        +       _signal_voltage_on_off(host, 0);
        +       if (mmc->supply.vmmc)
        +               mmc_regulator_set_ocr(host->mmc,
        mmc->supply.vmmc, 0);
        +       if (mmc->supply.vmmc)
        +               mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc,
        +                                     host->ios.vdd);
        +
        +       _signal_voltage_on_off(host, 1);
        +       mmiowb();
        +       _runtime_put(host);
        +
        +}
        +
        +static const struct mmc_host_ops sdhost_ops = {
        +       .request = sdhost_request,
        +       .set_ios = sdhost_set_ios,
        +       .get_ro = sdhost_get_ro,
        +       .get_cd = sdhost_get_cd,
        +
        +       .start_signal_voltage_switch = sdhost_set_vqmmc,
        +       .card_busy = sdhost_card_busy,
        +       .hw_reset = sdhost_hw_reset,
        +};


    remove the blank line


Sorry! I will do it.


        +
        +static void get_caps_info(struct sdhost_host *host,


    [...]

        +       }
        +
        +       host->clk = of_clk_get(np, 0);
        +       if (IS_ERR_OR_NULL(host->clk)) {
        +               ret = PTR_ERR(host->clk);
        +               dev_err(&pdev->dev, "can not get clock: %d\n", ret);
        +               goto err;
        +       }
        +
        +       host->clk_parent = of_clk_get(np, 1);
        +       if (IS_ERR_OR_NULL(host->clk_parent)) {
        +               ret = PTR_ERR(host->clk_parent);
        +               dev_err(&pdev->dev, "can not get parent clock:
        %d\n", ret);
        +               goto err;
        +       }
        +

    First, it's hard to understand what are clk and clk_parent. I guess
    that clk is clock_out for emmc devices and clk_parent is for
    controller itself.

    And it isn't a good idea to get them by fixed order.
    host->clk_xxx= devm_clk_get(host->dev, "clk-name-in-dt") might be
    better?


clk_parent  is the parent clock of emmc host controller.  There is
unnecessary set clk_parent here.
We will delete it. We will also get clock from devm_clk_get().


        +       ret = of_property_read_string(np, "sprd,name",
        &host->device_name);


    [...]

        +       ret = of_property_read_u32_array(np, "sprd,delay",
        sdhost_delay, 3);
        +       if (!ret) {
        +               host->write_delay = sdhost_delay[0];
        +               host->read_pos_delay = sdhost_delay[1];
        +               host->read_neg_delay = sdhost_delay[2];
        +       } else
        +               dev_err(&pdev->dev,
        +                       "can not read the property of sprd
        delay\n");
        +


    pls fix coding style issue.


Sorry, I'll do it.


        +       return 0;
        +


    [...]

        +
        +static struct platform_driver sdhost_driver = {
        +       .probe = sdhost_probe,
        +       .shutdown = sdhost_shutdown,
        +       .driver = {
        +                  .owner = THIS_MODULE,
        +                  .pm = &sdhost_dev_pm_ops,
        +                  .name = DRIVER_NAME,
        +                  .of_match_table = of_match_ptr(sdhost_of_match),
        +                  },
        +};
        +


    I think you need sdhost_remove hook to release something.
    Have you test it by bind/unbind your driver repeatly?


You are right, I will add sdhsot_remove and test it.


        +module_platform_driver(sdhost_driver);
        +
        +MODULE_DESCRIPTION("Spreadtrum sdio host controller driver");
        +MODULE_LICENSE("GPL");
        diff --git a/drivers/mmc/host/sprd_sdhost.h
        b/drivers/mmc/host/sprd_sdhost.h
        new file mode 100644
        index 0000000..e921616
        --- /dev/null
        +++ b/drivers/mmc/host/sprd_sdhost.h
        @@ -0,0 +1,591 @@
        +/*


    [...]

        +       char *clk_name;
        +       char *clk_parent_name;


    I can't find where to use it


Yes, it is unnecessary. I'll delete it.


        +       u32 base_clk;
        +


    [...]

        +/* Controller registers */
        +#ifdef SPRD_SDHOST_4_BYTE_ALIGNE
        +static inline void __local_writeb(u8 val, struct sdhost_host *host,
        +                                 u32 reg)
        +{
        +       u32 addr;
        +       u32 value;
        +       u32 ofst;
        +
        +       ofst = (reg & 0x3) << 3;
        +       addr = reg & (~((u32) (0x3)));
        +       value = readl_relaxed((host->ioaddr + addr));
        +       value &= (~(((u32) ((u8) (-1))) << ofst));
        +       value |= (((u32) val) << ofst);
        +       writel_relaxed(value, (host->ioaddr + addr));
        +}
        +


    Can we use writeb/writew/readb/readw instead?


Do you mean there is a barrier in the definition of writex?

The reson we use writex_relaxed base on a discussion of arm community of
2011:
http://comments.gmane.org/gmane.linux.ports.arm.kernel/117626

Please note the reply from Arnd Bergman and Russell King.
"... Originally, writel was intended for PCI buses and similar things
where you hava
to read from the same device in order to actually flush it all the way
down."

"This is the main difference to PIO accessors (outl) that operate on a
special
non-posted memory range that is only visible to a few bus types like PCI or
PCMIA."

On architecture of arm32, the __iowmb() is defined empty loop if
CONFIG_ARM_DMA_MEM_BUFFERABLE is not defined. In this case, maybe writex
and writex_relaxed are the same. Actually, our IO areas are
uncacheableand unbufferble,
so we think cache flush is no need.


Thanks for clarifying. Right, uncacheable & unbufferble IO mapping don't
need barrier to guarantee its access ordering if all instructions emited to the same sub-bus layer from ALU.

On architecture of x86, there is not a barrier in the definition of writex.

What's your opinion about writex and writex_relaxed?


        +static inline void __local_writew(u16 val, struct sdhost_host
        *host,
        +                                 u32 reg)


    [...]

        +static inline u32 _sdhost_calc_div(u32 base_clk, u32 clk)
        +{
        +       u32 N;
        +


    "div" will be better?
    At least avoid using upper case for variable.
    just my drive-by comment :)

You are right. I will change it.
Thanks.


        +       if (base_clk <= clk)
        +               return 0;
        +
        +       N = (u32) (base_clk / clk);
        +       N = (N >> 1);
        +       if (N)
        +               N--;
        +       if ((base_clk / ((N + 1) << 1)) > clk)
        +               N++;
        +       if (__CLK_MAX_DIV < N)
        +               N = __CLK_MAX_DIV;
        +
        +       return N;
        +}
        +
        +static inline void _sdhost_clk_set_and_on(struct sdhost_host *host,
        +                                         u32 div)
        +{
        +       u16 ctrl = 0;
        +
        +       __local_writew(0, host, SDHOST_16_CLK_CTRL);
        +       ctrl |= (u16) (((div & 0x300) >> 2) | ((div & 0xFF) << 8));
        +       ctrl |= __CLK_IN_EN;
        +       __local_writew(ctrl, host, SDHOST_16_CLK_CTRL);
        +       while (!(__CLK_IN_STABLE & __local_readw(host,
        SDHOST_16_CLK_CTRL)))
        +               ;
        +}


    I'm not sure if your clk can still be unready for some reasons(known
    or unknown).
    So how about timeout to break it and cast a dev_err here.
    Further on, do something to recover it?

You are right. We will add timeout to break this dead loop and cast a
dev_err.

    If not the case, just ignore this comment.

        +
        +#define SDHOST_8_TIMEOUT               0x2E
        +#define __DATA_TIMEOUT_MAX_VAL         0xe
        +


    [...]

        +
        +/* spredtrum define it byself */
        +static inline void _sdhost_reset_emmc(struct sdhost_host *host)
        +{
        +       __local_writeb(0, host, SDHOST_8_RST);
        +       mdelay(2);
        +       __local_writeb(_RST_EMMC, host, SDHOST_8_RST);
        +}


    According to JEDEC eMMC spec
    tRstW >= 1us ; RST_n pulse width
    tRSCA >= 200us ; RST_n to Command time
    tRSTH >= 1us ; RST_n high period

    I prefer to add at least 200us after unreset.
    Ignore this comment if you will not use it before sending cmd.


Now we have not use this function, but we will still consider your
suggestion.


        +
        +#define SDHOST_32_INT_ST               0x30
        +#define SDHOST_32_INT_ST_EN            0x34
        +#define SDHOST_32_INT_SIG_EN   0x38
        +#define _INT_CMD_END                   0x00000001
        +#define _INT_TRAN_END                  0x00000002
        +#define _INT_DMA_END                   0x00000008
        +#define _INT_WR_RDY                            0x00000010
        /* not used */
        +#define _INT_RD_RDY                            0x00000020
        /* not used */
        +#define _INT_ERR                               0x00008000
        +#define _INT_ERR_CMD_TIMEOUT   0x00010000
        +#define _INT_ERR_CMD_CRC               0x00020000
        +#define _INT_ERR_CMD_END               0x00040000
        +#define _INT_ERR_CMD_INDEX             0x00080000
        +#define _INT_ERR_DATA_TIMEOUT  0x00100000
        +#define _INT_ERR_DATA_CRC              0x00200000
        +#define _INT_ERR_DATA_END              0x00400000
        +#define _INT_ERR_CUR_LIMIT             0x00800000
        +#define _INT_ERR_ACMD                  0x01000000
        +#define _INT_ERR_ADMA                  0x02000000


    [...]

        +
        +#endif


    BTW, don't you need a documentation to elaborate more about your
    controller or dt-binding?


Yes, We will add dt-binding and mmc node in DT files in next patch.


        --
        1.7.9.5





    --
    Shawn Lin




--
Shawn Lin

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux