Marvell Wi-Fi Driver SDIO card割り込みを読む

Marvell Wi-Fi driverのSDIO card割り込みの処理 についてLinuxカーネルに含まれているソースコードを調べてみました。


# まとめ
* 割り込みが入るとレジスタをまとめてmp_regsに読み出す。
* 各レジスタの使い方についてまとめた。
* host_int_status_regが正なら、割り込みをハンドリングする必要がある
* mwifiex_process_int_status()が割り込みを処理する関数
* コマンド受信割り込み、データ送信割り込み、データ受信割り込みの順で処理する。
* mpaでaggregateの受信が可能なら、まとめて読みむ

# 対象
* Linux kernel 4.6.2
* drivers/net/wireless/marvell/mwiflex

# レジスタ
* 割り込み処理で使うレジスタです。

## mp_regs
* 割り込みでまとめて読み出すレジスタ
* チップごとに読み出すサイズが異なる。max_mp_regsで定義。 87xxは64 88xxは184 89xxは196
* アドレス REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK から長さ max_mp_regs だけ読む

```c
        if (mwifiex_read_data_sync(adapter, card->mp_regs,
                                   card->reg->max_mp_regs,
                                   REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
                mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n");
```

## status_reg0 status_reg1
* firmware stautus 16bit
* mwifiex_sdio_read_fw_status() で読み出す
* ファームウェアダウンロード後の起動確認で使う
* 正常な場合の期待値は 0xfedc

```c
101:#define FIRMWARE_READY_SDIO                         0xfedc
```

## host_int_status_reg
* int status reg
* 割り込みステータス
* DN_LD_CMD_PORT_HOST_INT_STATUS コマンド送信完了
* UP_LD_CMD_PORT_HOST_INT_STATUS コマンド受信
* DN_LD_HOST_INT_STATUS データ送信完了
* UP_LD_HOST_INT_STATUS データ受信

## io_port_0_reg io_port_1_reg io_port_2_reg
* multiport aggregation で使うio_portを示す

## rd_bitmap_l rd_bitmap_u
* 受信バッファのビットマップ
* ビットが1なら受信データあり、0ならデータ無し
* 0ビット目が、mpaでない受信ポート状態
* 1ビット目以降が、mpaの各ポートの状態

## wr_bitmap_l wr_bitmap_u
* 送信バッファのビットマップ
* 1なら送信待ちのデータあり、0ならデータ無し
* 0ビット目以降が、mpaの各ポートの状態

## rd_len_p0_l rd_len_p0_u
* port 0の受信データ長
* port nの受信データ長は、rd_len_p0_l +(n<<1), rd_len_p0_u + (n<<1) のアドレスにある
 
## card_misc_cfg_reg
* その他の設定
* 割り込みの自動再有効の設定で使う

```c
        /* Dnld/Upld ready set to auto reset */
        if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
                mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
                                  reg | AUTO_RE_ENABLE_INT);
        else
                return -1;

        return 0;
```



# SDIO カード割り込みハンドラの登録
* カード割り込みはsdio_claim_irq()でハンドラを登録します。

```
static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
{
        struct sdio_mmc_card *card = adapter->card;
        struct sdio_func *func = card->func;
        int ret;

        sdio_claim_host(func);

        /* Request the SDIO IRQ */
        ret = sdio_claim_irq(func, mwifiex_sdio_interrupt);
```
* 解除は、sdio_release_irq()です。

# SDIO card割り込みハンドラ 1
* SIO deviceからカード割り込みが入ると、sdio_claim_irq()で登録したハンドラが呼ばれます。
* card構造体とadapter構造体のポインタを取得
* adapter->ps_stateはカードのPS状態を表します。割り込みが入ったということは起き上がったと等価なので、PS_SATE_AWAKEを設定
* mwifiex_interrupt_status()
* mwifiex_main_process()

```
/*
 * SDIO interrupt handler.
 *
 * This function reads the interrupt status from firmware and handles
 * the interrupt in current thread (ksdioirqd) right away.
 */
static void
mwifiex_sdio_interrupt(struct sdio_func *func)
{
        struct mwifiex_adapter *adapter;
        struct sdio_mmc_card *card;

        card = sdio_get_drvdata(func);
        if (!card || !card->adapter) {
                pr_err("int: func=%p card=%p adapter=%p\n",
                       func, card, card ? card->adapter : NULL);
                return;
        }
        adapter = card->adapter;

        if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
                adapter->ps_state = PS_STATE_AWAKE;

        mwifiex_interrupt_status(adapter);
        mwifiex_main_process(adapter);
}
```

# SDIO card割り込みハンドラ 2
* mwifiex_interrupt_status()
* 割り込みステータスをアップデート 
 * card->reg->max_mp_regs
 * adapter->int_status
* sdio_iregが正なら、なんらなかの処理すべき割り込みがある。

```c
/*
 * This function reads the interrupt status from card.
 */
static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
{
        struct sdio_mmc_card *card = adapter->card;
        u8 sdio_ireg;
        unsigned long flags;

        if (mwifiex_read_data_sync(adapter, card->mp_regs,
                                   card->reg->max_mp_regs,
                                   REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
                mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n");
                return;
        }

        sdio_ireg = card->mp_regs[card->reg->host_int_status_reg];
        if (sdio_ireg) {
                /*
                 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
                 * For SDIO new mode CMD port interrupts
                 *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
                 *      UP_LD_CMD_PORT_HOST_INT_STATUS
                 * Clear the interrupt status register
                 */
                mwifiex_dbg(adapter, INTR,
                            "int: sdio_ireg = %#x\n", sdio_ireg);
                spin_lock_irqsave(&adapter->int_lock, flags);
                adapter->int_status |= sdio_ireg;
                spin_unlock_irqrestore(&adapter->int_lock, flags);
        }
}
```

# SDIO card割り込みハンドラ 2
* mwifiex_main_process()
* これはドライバのメインプロセスです。
* データ送受信に関わらないところは省略する
* 割り込みが入ったら、adapter->if_ops.process_int_status(adapter);を呼ぶ
* この実体はsdio.cのmwifiex_process_int_status()

```c
/*
 * The main process.
 *
 * This function is the main procedure of the driver and handles various driver
 * operations. It runs in a loop and provides the core functionalities.
 *
 * The main responsibilities of this function are -
 *      - Ensure concurrency control
 *      - Handle pending interrupts and call interrupt handlers
 *      - Wake up the card if required
 *      - Handle command responses and call response handlers
 *      - Handle events and call event handlers
 *      - Execute pending commands
 *      - Transmit pending data packets
 */
int mwifiex_main_process(struct mwifiex_adapter *adapter)
{
    int ret = 0;
    unsigned long flags;

    spin_lock_irqsave(&adapter->main_proc_lock, flags);

    /* Check if already processing */
    if (adapter->mwifiex_processing || adapter->main_locked) {
        adapter->more_task_flag = true;
        spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
        goto exit_main_proc;
    } else {
        adapter->mwifiex_processing = true;
        spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
    }
process_start:
    do {
...
        /* Handle pending interrupt if any */
        if (adapter->int_status) {
            if (adapter->hs_activated)
                mwifiex_process_hs_config(adapter);
            if (adapter->if_ops.process_int_status)
                adapter->if_ops.process_int_status(adapter);
        }
...
      } while (true);

...

exit_main_proc:
    if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING)
        mwifiex_shutdown_drv(adapter);
    return ret;
}
EXPORT_SYMBOL_GPL(mwifiex_main_process);
```

## # SDIO card割り込みハンドラ 3
* mwifiex_process_int_status()の概要
* UP_LD_CMD_PORT_HOST_INT_STATUS コマンド受信割り込み
 *  cmd_rd_len_1とcmd_rd_len_0を読み、受信長を知る
 *  受信長分のskbを確保
 *  CMD53でコマンドを読み出し
 *  mwifiex_decode_rx_packet()でコマンドを解析
* DN_LD_HOST_INT_STATUS データ送信完了割り込み
 * wr_bitmap_l と wr_bitmap_u を読み card->mp_wr_bitmapに上書き
* not generate download ready interrupt の対応
* UP_LD_HOST_INT_STATUS データ受信割り込み
 *  rd_bitmap_l と rd_bitmap_u を読む
 *  mwifiex_get_rd_port(adapter, &port)で読みだすportを知る。
 *  mwifiex_sdio_card_to_host_mp_aggr()データを読み出す。 mpa可能ならmpaで読み出す。
* 終了処理

# 参考
* Marvell Wi-Fi driverのmulti port aggregation を読む http://qiita.com/eggman/items/7c1db4c13551e44433dc