Armadilloフォーラム

Armadillo440 SPIでの8バイト以上のデータ転送

s.takagi

2015年3月11日 11時45分

高木と申します。
お世話になっております。

Armadillo-420 SPI通信の転送バイト数について
https://armadillo.atmark-techno.com/forum/armadillo/1178

Armadill-460 SPIの16ビット転送での転送落ち
https://armadillo.atmark-techno.com/forum/armadillo/793

上記2点の質問を参考にパッチを当ててみましたが、ioctlでの8ビット以上の転送がうまく行きません。
手順とソースを下記に記しますので、解決方法がございましたらご教授頂けませんでしょうか?

踏んだ手順を下記の通りです。

1)linux-2.6.26-at23に「0001-mxc_spi-fix-over-8-byte-transfer-error.patch」をあて、カーネルをビルド。

atmark@atde3:~/linux-2.6.26-at23$ patch -p1 < ../0001-mxc_spi-fix-over-8-byte-transfer-error.patch
patching file drivers/spi/mxc_spi.c
 
atmark@atde3:~/linux-2.6.26-at23$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- && gzip -c arch/arm/boot/Image > linux.bin.gz

2)Armadilloに書き込み

atmark@atde3:~/linux-2.6.26-at23$ sudo cp ./linux.bin.gz /var/lib/tftpboot
 
hermit> tftpdl (ArmadilloのIPアドレス) (ATDEのIPアドレス) --kernel=linux.bin.gz

ioctlのソースは下記の通りです。

static int spi_send_image(u8 *ptrImage, int numBytes)
{
    int i;
    u8 *ptr;
    int numBlocks;
    u32 numFullBlocks;                    /* 満載のブロック数 */
    u32    lastBlockSize;                    /* 積み残しブロックのバイト数 */
    struct spi_ioc_transfer *tr = NULL;    /* SPI 転送の構造体 */
 
    for (i = 0; i < numBytes; i++){
        u8 tmp = ptrImage[i];
        ptrImage[i] = ptrImage[i + 1];
        ptrImage[i + 1] = tmp;
    }
 
    numFullBlocks = numBytes / SPI_BLOCKSIZE;
    lastBlockSize = numBytes % SPI_BLOCKSIZE;
    tr = (struct spi_ioc_transfer *)malloc(
        sizeof(struct spi_ioc_transfer) * (numFullBlocks + 1));
 
    //  転送ブロック(満載)を用意
    ptr = ptrImage;
    for (i = 0; i < numFullBlocks; i++) {
        tr[i].tx_buf = (unsigned long)ptr;
        tr[i].rx_buf = (unsigned long)NULL;
        tr[i].len = SPI_BLOCKSIZE;
        tr[i].speed_hz = SPI_SPEED_HZ;
        tr[i].delay_usecs = SPI_DELAY_USECS;
        tr[i].bits_per_word = SPI_BITS;
        tr[i].cs_change = 0;
        ptr += SPI_BLOCKSIZE;
    }
    numBlocks = numFullBlocks;
 
    //  積み残し分の転送ブロックを追加(必要に応じて)
    if (lastBlockSize) {
        i = numFullBlocks;
        tr[i].tx_buf = (unsigned long)ptr;
        tr[i].rx_buf = (unsigned long)NULL;
        tr[i].len = lastBlockSize;
        tr[i].speed_hz = SPI_SPEED_HZ;
        tr[i].delay_usecs = SPI_DELAY_USECS;
        tr[i].bits_per_word = SPI_BITS;
        tr[i].cs_change = 0;
        numBlocks += 1;
    }
 
    //  転送ブロックの送信
    int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(numBlocks), tr);
    free(tr);
    if (ret < 0) {
        perror("error - spi send image");
        return -1;
    }
 
    return 0;
}

以上、どうぞ宜しくお願いします。

コメント

森本と申します。

SPIドライバ内(mxc_spi.c)にて送信できる最大サイズが8バイトと規定され、
それ以上を上位から渡された場合は送信しないようです。

ドライバ側で(無理やり)それに対応したコードを掲載します。ご参考まで。

#define MXC_SPI_FIFO_DEPTH (8)
int mxc_spi_transfer(struct spi_device *spi, struct spi_transfer *t)
{
    struct mxc_spi *mxc_spi;
    int tx_tmp, i;
    int num_fifo_access;
    int bytes_per_word;
 
    mxc_spi = spi_master_get_devdata(spi->master);
 
    /* Init trasnfers state */
    mxc_spi->transfer.tx_buf = t->tx_buf;
    mxc_spi->transfer.rx_buf = t->rx_buf;
    mxc_spi->transfer.len = t->len;
    if (t->bits_per_word <= 8)
        bytes_per_word = 1;
    else if (t->bits_per_word <= 16)
        bytes_per_word = 2;
    else
        bytes_per_word = 4;
    mxc_spi->transfer.bytes_per_word = bytes_per_word;
    mxc_spi->transfer.count = 0;
    INIT_COMPLETION(mxc_spi->xfer_done);
 
    /* Load up TX FIFO */
    num_fifo_access = t->len / bytes_per_word;
    if (t->len % bytes_per_word) {
        pr_debug("%s requested transfer length %d is not aligned with \
        requested bytes_per_word %d \n", __func__, t->len , bytes_per_word);
        return -EINVAL;
    }
 
#if 1   //  MMM
    {
        int fifo_loop;
        int transfer_count = 0;
 
        while(num_fifo_access){
            fifo_loop = num_fifo_access;
            if (fifo_loop > MXC_SPI_FIFO_DEPTH){
                fifo_loop = MXC_SPI_FIFO_DEPTH;
            }
            mxc_spi->transfer.len = fifo_loop * bytes_per_word;
            mxc_spi->transfer.count = 0;
 
            spi_enable_interrupt(mxc_spi, MXC_CSPIINT_RREN);
 
            for (i = 0; i < fifo_loop; ++i) {
                tx_tmp = mxc_spi->transfer.tx_get(mxc_spi, i);
                spi_put_tx_data(mxc_spi->base, tx_tmp);
            }
 
            spi_tx_start(mxc_spi->base);
            wait_for_completion(&mxc_spi->xfer_done);
            spi_disable_interrupt(mxc_spi, MXC_CSPIINT_RREN);
            num_fifo_access -= fifo_loop;
            transfer_count += mxc_spi->transfer.count;
            mxc_spi->transfer.tx_buf += (mxc_spi->transfer.count * bytes_per_word);
            mxc_spi->transfer.rx_buf += (mxc_spi->transfer.rx_buf != NULL) ? (mxc_spi->transfer.count * bytes_per_word) : 0;
        }
        mxc_spi->transfer.count = transfer_count;
    }
 
#else   //  MMM
 
    /* Generate error when num_fifo_access exceeds the FIFO depth */
    if (num_fifo_access > MXC_SPI_FIFO_DEPTH){
        pr_debug("%s requested trasfer exceeds the SPI FIFO size \n",
                __func__);
        return -EINVAL;
    }
 
    spi_enable_interrupt(mxc_spi, MXC_CSPIINT_RREN);
 
    for (i = 0; i < num_fifo_access; i++) {
        tx_tmp = mxc_spi->transfer.tx_get(mxc_spi, i);
        spi_put_tx_data(mxc_spi->base, tx_tmp);
    }
 
    spi_tx_start(mxc_spi->base);
 
    wait_for_completion(&mxc_spi->xfer_done);
 
    spi_disable_interrupt(mxc_spi, MXC_CSPIINT_RREN);
 
#endif  //  MMM
 
    return mxc_spi->transfer.count * bytes_per_word;;
}

中村です。

高木さんのコードをあまりよく読んでいませんが・・・

> SPIドライバ内(mxc_spi.c)にて送信できる最大サイズが8バイトと規定され、
> それ以上を上位から渡された場合は送信しないようです。
>
> ドライバ側で(無理やり)それに対応したコードを掲載します。ご参考まで。

ioctl(spi_fd, SPI_IOC_MESSAGE(numBlocks), tr);
でやるなら、mxc_spi.cに手をいれなくても8バイト以上の送信ができると思います。
ダメなのでしょうか?

mxc_spi.cを修正して8バイト以上の送信ができるようにする方法は、
ML時代に投稿したこれも参考にしてください。
http://lists.atmark-techno.com/pipermail/armadillo/2013-July/009050.html
http://lists.atmark-techno.com/pipermail/armadillo/2013-July/009059.html

--
なかむら

中村です。

> 上記2点の質問を参考にパッチを当ててみましたが、ioctlでの8ビット以上の転送がうまく行きません。

どのように「うまく行かない」のかがわかりませんが・・・
(「8ビット以上」と書いてありますが「8バイト以上」の間違いですよね)
// ついでに・・・
// > Armadill-460 SPIの16ビット転送での転送落ち
// > https://armadillo.atmark-techno.com/forum/armadillo/793
// のパッチファイル
// 0001-mxc_spi-fix-over-8-byte-transfer-error.patch
のファイル名「over-8-byte」は「over-8-bit」の間違い?

>

> static int spi_send_image(u8 *ptrImage, int numBytes)
> {
...
>     for (i = 0; i < numBytes; i++){
>         u8 tmp = ptrImage[i];
>         ptrImage[i] = ptrImage[i + 1];
>         ptrImage[i + 1] = tmp;
>     }
...
> 

このforループの"i++"が問題ということはありませんか?
"i += 2"の間違いとか?(numBytesを偶数として)

--
なかむら