A long time (2.6.2x days) I tried to submit a patch to do bus width testing on 
mmc and would appreciate some feedback before I do a formal submission.

Bus width testing is defined in JEDEC Standard No. 84-A441: section A.8.3 
Changing the data bus width

The problem is that it does NOT work in all cases.  Some controllers do not 
handle the CMD19/CMD14 exchange.
(BUSTEST_W/BUSTEST_R)

When this failure happens it is not at all clear what to do....   

We could default to 1 bit mode and just do what we do today and take 4 bit bus 
width -- assuming CAPS are appropriately enabled.

I was thinking of adding a new MMC_CAP saying controller supports bus width 
testing.      Any thoughts ?

<snippet of code from today implementation == based on 2.6.32 but with DDR 
support back ported>

look for mmc_test_bus_width()

My thought was to replace

                        if (err) {
                                printk(KERN_WARNING "%s: switch to bus width %d 
ddr %d "
                                           "failed\n", mmc_hostname(card->host),
                                           1 << bus_width, ddr);
                                err = 0;
                        } else {
                                mmc_set_bus_width_ddr(card->host, bus_width, 
MMC_SDR_MODE);
                                if (mmc_test_bus_width (card, 1<<bus_width))
                                        break;
                        }

with

                        if (err) {
                                printk(KERN_WARNING "%s: switch to bus width %d 
ddr %d "
                                           "failed\n", mmc_hostname(card->host),
                                           1 << bus_width, ddr);
                                err = 0;
                        } else {
                                mmc_set_bus_width_ddr(card->host, bus_width, 
MMC_SDR_MODE);
                                if ((mmc->caps & 
MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST) == 0)
                                        break;
                                if (mmc_test_bus_width (card, 1<<bus_width))
                                        break;
                        }

and let the platform code set MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST



from mmc.c
==========

static int mmc_init_card(struct mmc_host *host, u32 ocr,
        struct mmc_card *oldcard)
{
        struct mmc_card *card;
        int err, ddr = 0;
        u32 cid[4];
        unsigned int max_dtr;
        u32 rocr;

        BUG_ON(!host);
        WARN_ON(!host->claimed);

        /*
         * Since we're changing the OCR value, we seem to
         * need to tell some cards to go back to the idle
         * state.  We wait 1ms to give cards time to
         * respond.
         */
        mmc_go_idle(host);

        /* The extra bit indicates that we support high capacity */
        err = mmc_send_op_cond(host, ocr | MMC_CARD_SECTOR_ADDR, &rocr);
        if (err)
                goto err;

        /*
         * For SPI, enable CRC as appropriate.
         */
        if (mmc_host_is_spi(host)) {
                err = mmc_spi_set_crc(host, use_spi_crc);
                if (err)
                        goto err;
        }

        /*
         * Fetch CID from card.
         */
        if (mmc_host_is_spi(host))
                err = mmc_send_cid(host, cid);
        else
                err = mmc_all_send_cid(host, cid);
        if (err)
                goto err;

        if (oldcard) {
                if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
                        err = -ENOENT;
                        goto err;
                }

                card = oldcard;
        } else {
                /*
                 * Allocate card structure.
                 */
                card = mmc_alloc_card(host, &mmc_type);
                if (IS_ERR(card)) {
                        err = PTR_ERR(card);
                        goto err;
                }

                card->type = MMC_TYPE_MMC;
                card->rca = 1;
                memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
        }

        /*
         * For native busses:  set card RCA and quit open drain mode.
         */
        if (!mmc_host_is_spi(host)) {
                err = mmc_set_relative_addr(card);
                if (err)
                        goto free_card;

                mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
        }

        if (!oldcard) {
                /*
                 * Fetch CSD from card.
                 */
                err = mmc_send_csd(card, card->raw_csd);
                if (err)
                        goto free_card;

                err = mmc_decode_csd(card);
                if (err)
                        goto free_card;
                err = mmc_decode_cid(card);
                if (err)
                        goto free_card;
        }

        /*
         * Select card, as all following commands rely on that.
         */
        if (!mmc_host_is_spi(host)) {
                err = mmc_select_card(card);
                if (err)
                        goto free_card;
        }

#if 0
        if (mmc_card_mmc(card)) {
                /* work around of Micorn 16G emmc */
                /* set bus_width to 1 bit as init state */
                mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                                EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1);

        }
#endif

        if (!oldcard) {
                /*
                 * Fetch and process extended CSD.
                 */
                err = mmc_read_ext_csd(card);
                if (err)
                        goto free_card;

                if (rocr & MMC_CARD_SECTOR_ADDR)
                        mmc_card_set_blockaddr(card);
        }


        /* switch to user partition, emmc may stay in boot partition after boot 
*/
        mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_PART_CONF, 0);

        /*
         * Activate high speed (if supported)
         */
        if ((card->ext_csd.hs_max_dtr != 0) &&
                (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_HS_TIMING, 1);
                if (err && err != -EBADMSG)
                        goto free_card;

                if (err) {
                        printk(KERN_WARNING "%s: switch to highspeed failed\n",
                               mmc_hostname(card->host));
                        err = 0;
                } else {
                        mmc_card_set_highspeed(card);
                        mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
                }
        }

        /*
         * Compute bus speed.
         */
        max_dtr = (unsigned int)-1;

        if (mmc_card_highspeed(card)) {
                if (max_dtr > card->ext_csd.hs_max_dtr)
                        max_dtr = card->ext_csd.hs_max_dtr;
        } else if (max_dtr > card->csd.max_dtr) {
                max_dtr = card->csd.max_dtr;
        }

        mmc_set_clock(host, max_dtr);

        /*
         * Indicate DDR mode (if supported).
         */
        if (mmc_card_highspeed(card)) {
                if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
                        && (host->caps & (MMC_CAP_1_8V_DDR)))
                                ddr = MMC_1_8V_DDR_MODE;
                else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
                        && (host->caps & (MMC_CAP_1_2V_DDR)))
                                ddr = MMC_1_2V_DDR_MODE;
        }

        /*
         * Activate wide bus and DDR (if supported).
         */
        if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
            (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
                unsigned ext_csd_bit, bus_width;
                int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | 
MMC_CAP_4_BIT_DATA);

                do {
                        if (temp_caps & MMC_CAP_8_BIT_DATA) {
                                ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
                                bus_width = MMC_BUS_WIDTH_8;
                        } else {
                                ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
                                bus_width = MMC_BUS_WIDTH_4;
                        }

                        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                                         EXT_CSD_BUS_WIDTH, ext_csd_bit);
                        if (err) {
                                printk(KERN_WARNING "%s: switch to bus width %d 
ddr %d "
                                           "failed\n", mmc_hostname(card->host),
                                           1 << bus_width, ddr);
                                err = 0;
                        } else {
                                mmc_set_bus_width_ddr(card->host, bus_width, 
MMC_SDR_MODE);
                                if (mmc_test_bus_width (card, 1<<bus_width))
                                        break;
                        }
                        
                        if (bus_width == MMC_BUS_WIDTH_8)
                                temp_caps &= ~MMC_CAP_8_BIT_DATA;
                        else
                                temp_caps &= ~MMC_CAP_4_BIT_DATA;

                        if (temp_caps == 0) {
                                ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
                                bus_width = MMC_BUS_WIDTH_1;
                        }
                } while (temp_caps);

                if (temp_caps == 0) {
                        ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
                        bus_width = MMC_BUS_WIDTH_1;
                }
                else if (temp_caps & MMC_CAP_8_BIT_DATA) {
                        if (ddr)
                                ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
                        else
                                ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
                        bus_width = MMC_BUS_WIDTH_8;
                } else {
                        if (ddr)
                                ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
                        else
                                ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
                        bus_width = MMC_BUS_WIDTH_4;
                }

                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                                 EXT_CSD_BUS_WIDTH, ext_csd_bit);

                if (ddr) {
                        mmc_card_set_ddr_mode(card);
                        mmc_set_bus_width_ddr(card->host, bus_width, ddr);
                } else
                        mmc_set_bus_width_ddr(card->host, bus_width, 
MMC_SDR_MODE);

                if (err && err != -EBADMSG)
                        goto free_card;
        }

        if (!oldcard)
                host->card = card;

        return 0;

free_card:
        if (!oldcard)
                mmc_remove_card(card);
err:

        return err;
}


from mmc_ops.c
=============
int mmc_test_bus_width(struct mmc_card *card, int bits)
{
        struct mmc_request mrq;
        struct mmc_command cmd;
        struct mmc_data data;
        struct scatterlist sg;
        int len;
        u8 test_data_write[8];
        u8 test_data_read[64];

        switch (bits) {
        case 8:
                test_data_write[0] = 0x55;
                test_data_write[1] = 0xaa;
                test_data_write[2] = 0x00;
                test_data_write[3] = 0x00;
                test_data_write[4] = 0x00;
                test_data_write[5] = 0x00;
                test_data_write[6] = 0x00;
                test_data_write[7] = 0x00;
                len = 8;
                break;
        case 4:
                test_data_write[0] = 0x5a;
                test_data_write[1] = 0x00;
                test_data_write[2] = 0x00;
                test_data_write[3] = 0x00;
                len = 4;
                break;
        default:
                return 0;
        }

        memset(&mrq, 0, sizeof(struct mmc_request));
        memset(&cmd, 0, sizeof(struct mmc_command));
        memset(&data, 0, sizeof(struct mmc_data));

        cmd.opcode = MMC_BUSTEST_W;
        cmd.arg = 0;
        cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

        data.flags = MMC_DATA_WRITE;
        data.blksz = 64;
        data.blocks = 1;
        data.sg = &sg;
        data.sg_len = 1;

        mrq.cmd = &cmd;
        mrq.data = &data;

        sg_init_one(&sg, &test_data_write, 64);

        /*
         * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a 
timeout
         * of 64 clock cycles.
         */
        data.timeout_ns = 0;
        data.timeout_clks = 64;

        mmc_wait_for_req(card->host, &mrq);

        if (cmd.error || data.error ) {
                printk(KERN_INFO "Failed to send CMD19: %d %d\n", cmd.error, 
data.error);
                return 0;
        }

        /* Now read back */
        memset(&mrq, 0, sizeof(struct mmc_request));
        memset(&cmd, 0, sizeof(struct mmc_command));
        memset(&data, 0, sizeof(struct mmc_data));
        memset (&test_data_read, 0, sizeof(test_data_read));

        cmd.opcode = MMC_BUSTEST_R;
        cmd.arg = 0;
        cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

        data.flags = MMC_DATA_READ;
        data.blksz = sizeof(test_data_read);
        data.blocks = 1;
        data.sg = &sg;
        data.sg_len = 1;

        mrq.cmd = &cmd;
        mrq.data = &data;

        sg_init_one(&sg, &test_data_read, sizeof(test_data_read));

        /*
         * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a 
timeout
         * of 64 clock cycles.
         */
        data.timeout_ns = 0;
        data.timeout_clks = 64;

        mmc_wait_for_req(card->host, &mrq);

        if (cmd.error) {
                printk(KERN_INFO "Failed to send CMD14: %d %d\n", cmd.error, 
data.error);
                return 0;
        }

#if 0
#warning PRINT RESULTS FROM CMD14
        printk (KERN_INFO "%s: Got %02X %02X %02X %02X\n", __FUNCTION__,
                test_data_read[0],
                test_data_read[1],
                test_data_read[2],
                test_data_read[3]);
#endif

        switch (bits) {
        case 8:
                return (test_data_read[0] == 0xaa && test_data_read[1] == 0x55);
        case 4:
                return (test_data_read[0] == 0xa5);
        }
        return 0;
}

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

Reply via email to