[Armadillo:09056] Re: A-4x0 にSPIでSDカードスロットを増設

Yasuhisa Nakamura email@hidden
2013年 7月 21日 (日) 00:49:21 JST


中村です。

こんなんところでブロックして(ループして)処理していいのか
わかりませんが、こうやったら動いた、とうパッチです。

まずは、起動時のメッセージ。
...
CSPI: mxc_spi-0 probed
CSPI: mxc_spi-2 probed     (今はSPI3は使ってませんが...)
...
mmc_spi spi1.0: ASSUMING SPI bus stays unshared!
mmc_spi spi1.0: ASSUMING 3.2-3.4 V slot power
mmc_spi spi1.0: SD/MMC host mmc1, no DMA
...
mmc_spi_get_cd: return 1         ★
mmc_spi_setpower: maskval = 21   ★
...
mmc_spi_get_ro: return 1         ★
mmc_spi_get_ro: return 1         ★
mmc1: new SD card on SPI
mmcblk0: mmc1:0000 SD02G 1921024KiB (ro)
 mmcblk0: p1

★は下のパッチでprintk()しているところです。
2GBのSDカードでテストしてます。

同じカードをArmadillo-460本体のSDカードスロットに装着したときは、
次のようなメッセージが表示されました。
mmc0: new high speed SD card at address 41f1
mmcblk0: mmc0:41f1 SD02G 1921024KiB
 mmcblk0: p1

以下、パッチです。

要求バイト数が8バイト(MXC_SPI_FIFO_DEPTH)を超えるときには8バイトで
終わってしまうところを、8バイトを超えるときにはループするように
修正しています。(修正内容を読みやすくするためにgoto使ってます)

--- drivers/spi/mxc_spi.c-orig  2012-07-26 21:30:37.000000000 +0900
+++ drivers/spi/mxc_spi.c       2013-07-20 21:07:14.000000000 +0900
@@ -489,12 +489,15 @@
        mxc_spi->transfer.rx_buf = t->rx_buf;
        mxc_spi->transfer.len = min_t(u32, t->len, MXC_SPI_FIFO_DEPTH);
        mxc_spi->transfer.count = 0;
-       INIT_COMPLETION(mxc_spi->xfer_done);

        spi_enable_interrupt(mxc_spi, MXC_CSPIINT_RREN);

+       i = 0;
+loop:
+       INIT_COMPLETION(mxc_spi->xfer_done);
+
        /* Load up TX FIFO */
-       for (i = 0; i < mxc_spi->transfer.len; i++) {
+       for (; i < mxc_spi->transfer.len; i++) {
                tx_tmp = mxc_spi->transfer.tx_get(mxc_spi, i);
                spi_put_tx_data(mxc_spi->base, tx_tmp);
        }
@@ -503,6 +506,13 @@

        wait_for_completion(&mxc_spi->xfer_done);

+       if (mxc_spi->transfer.count < t->len) {
+               mxc_spi->transfer.len += min_t(u32,
+                                       t->len - mxc_spi->transfer.count,
+                                       MXC_SPI_FIFO_DEPTH);
+               goto loop;
+       }
+
        spi_disable_interrupt(mxc_spi, MXC_CSPIINT_RREN);

        return mxc_spi->transfer.count;


struct spi_board_infoの設定は、こんな感じです。
read-only検知、カード検知、電源制御は関数を用意しなくても
動くようですが、この後の作業のためにダミーを入れてみました。
max_speed_hzは10MHzとしていますが16MHzまではOKなはずです。

--- arch/arm/mach-mx25/armadillo400.c-orig      2012-07-26 21:30:37.000000000 +0900
+++ arch/arm/mach-mx25/armadillo400.c   2013-07-20 21:38:12.000000000 +0900
@@ -26,6 +26,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c-gpio.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
 #include <linux/pwm.h>
 #include <linux/pwm_backlight.h>
 #include <linux/fsl_devices.h>
@@ -710,7 +711,41 @@
        .chipselect_inactive = gpio_spi1_cs_inactive,
 };

+/* とりあえず、常に真を返す */
+static int mmc_spi_get_ro(struct device *dev)
+{
+printk(KERN_INFO "mmc_spi_get_ro: return 1\n");
+       return 1;
+}
+
+/* とりあえず、常に真を返す */
+static int mmc_spi_get_cd(struct device *dev)
+{
+printk(KERN_INFO "mmc_spi_get_cd: return 1\n");
+       return 1;
+}
+
+/* とりあえず、何もしない */
+static void mmc_spi_setpower(struct device *dev, unsigned int maskval)
+{
+printk(KERN_INFO "mmc_spi_setpower: maskval = %d\n", maskval);
+}
+
+static struct mmc_spi_platform_data mmc_spi_info = {
+       .get_ro = mmc_spi_get_ro,
+       .get_cd = mmc_spi_get_cd,
+       .setpower = mmc_spi_setpower,
+};
+
 static struct spi_board_info armadillo400_spi1_board_info[] __initdata = {
+       {
+               .modalias = "mmc_spi",
+               .platform_data  = &mmc_spi_info,
+               .max_speed_hz = 10000000,
+               .bus_num = 1,
+               .chip_select = 0,
+               .mode = SPI_MODE_0,
+       },
 };
 #endif


[Armadillo:08970]で書いた、mmc_spi.hの修正です。

--- include/linux/spi/mmc_spi.h-orig    2012-07-26 21:30:37.000000000 +0900
+++ include/linux/spi/mmc_spi.h 2013-07-09 14:19:03.000000000 +0900
@@ -23,6 +23,15 @@
        /* sense switch on sd cards */
        int (*get_ro)(struct device *);

+       /*
+        * If board does not use CD interrupts, driver can optimize polling
+        * using this function.
+        */
+       int (*get_cd)(struct device *);
+
+       /* Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */
+       unsigned long caps;
+
        /* how long to debounce card detect, in msecs */
        u16 detect_delay;


最後に、GPIOによるCS(SS)信号の制御のバグ修正。

--- arch/arm/mach-mx25/armadillo400_gpio.c-orig 2012-07-26 21:30:37.000000000 +0900
+++ arch/arm/mach-mx25/armadillo400_gpio.c      2013-07-20 22:25:30.000000000 +0900
@@ -1480,7 +1480,7 @@

 void gpio_spi1_cs_active(int cspi_mode, int chipselect)
 {
-       int __maybe_unused val = !(cspi_mode & SPI_CPOL);
+       int __maybe_unused val = !!(cspi_mode & SPI_CS_HIGH);

        switch (chipselect) {
        case 0:
@@ -1504,7 +1504,7 @@

 void gpio_spi1_cs_inactive(int cspi_mode, int chipselect)
 {
-       int __maybe_unused val = !!(cspi_mode & SPI_CPOL);
+       int __maybe_unused val = !(cspi_mode & SPI_CS_HIGH);

        switch (chipselect) {
        case 0:
@@ -1528,7 +1528,7 @@

 void gpio_spi3_cs_active(int cspi_mode, int chipselect)
 {
-       int __maybe_unused val = !(cspi_mode & SPI_CPOL);
+       int __maybe_unused val = !!(cspi_mode & SPI_CS_HIGH);

        switch (chipselect) {
        case 0:
@@ -1558,7 +1558,7 @@

 void gpio_spi3_cs_inactive(int cspi_mode, int chipselect)
 {
-       int __maybe_unused val = !!(cspi_mode & SPI_CPOL);
+       int __maybe_unused val = !(cspi_mode & SPI_CS_HIGH);

        switch (chipselect) {
        case 0:

常にCSがアクティブLOWでいいなら、valとして0か1固定でもいいのです
が(SPI_CPOLを見ているのは間違い)、SPIでSDカードを使うときには、
SPI_CS_HIGHフラグを調べる必要があります。
SDカードをSPIモードで初期化するとききだけ、cspi_modeのSPI_CS_HIGHが
立ちます(CS=HIGHのままreadbytes()でクロックをたくさん出してます)。

これをやっているのが、driver/mmc/host/mmc_spi.cの次の部分です。

static void mmc_spi_initsequence(struct mmc_spi_host *host)
{
        ...
        /*
         * Do a burst with chipselect active-high.  We need to do this to
         * meet the requirement of 74 clock cycles with both chipselect
         * and CMD (MOSI) high before CMD0 ... after the card has been
         * powered up to Vdd(min), and so is ready to take commands.
        (途中省略)
         */
        host->spi->mode |= SPI_CS_HIGH;
        if (spi_setup(host->spi) != 0) {
              エラー処理
        } else {
                mmc_spi_readbytes(host, 18);
                host->spi->mode &= ~SPI_CS_HIGH;
                if (spi_setup(host->spi) != 0) {
                      エラー処理
                }
        }
}

-- 
なかむら




armadillo メーリングリストの案内