小白自制Linux開發板 七. USB驅動配置

本文章基於
https://whycan.com/t_3087.html
https://whycan.com/t_6021.html
整理

F1c100s芯片支持USB的OTG模式,也就是可以通過更改UsbId拉低或拉高方式定義當前的開發板可以作爲host還是device。

  • usbid 拉高時,開發板作爲外設方式。
  • usbid 拉低時,開發板作爲主機方式。

當然除了使用硬件方式,還可以通過Linux系統直接更改當前USB的模式。

1. 原理圖

在F1c100s中PE2引腳是作爲usbid功能來使用,因爲爲了使用Sunxi-tool 所以我在畫原理圖的時候默認將PE2做了上拉處理。

 這個芯片只有一個usb引腳

 爲了可以引出更多的usb外設,所以這裏使用了FE8.1這個USB Hub芯片,這個芯片最多可以引出4個Usb接口。具體原理圖如下:

 

 2. 設備樹與驅動代碼配置

硬件處理完成,接下來就是軟件部分修改了,打開Linux5.7.1內核源碼

2.1 首先修改設備樹文件arch/arm/boot/dts/suniv-f1c100s.dtsi文件

在soc節點下增加

usb_otg: usb@1c13000 {
    compatible = "allwinner,suniv-musb";
    reg = <0x01c13000 0x0400>;
    clocks = <&ccu CLK_BUS_OTG>;
    resets = <&ccu RST_BUS_OTG>;
    interrupts = <26>;
    interrupt-names = "mc";
    phys = <&usbphy 0>;
    phy-names = "usb";
    extcon = <&usbphy 0>;
    allwinner,sram = <&otg_sram 1>;
    status = "disabled";
};

usbphy: phy@1c13400 {
    compatible = "allwinner,suniv-usb-phy";
    reg = <0x01c13400 0x10>;
    reg-names = "phy_ctrl";
    clocks = <&ccu CLK_USB_PHY0>;
    clock-names = "usb0_phy";
    resets = <&ccu RST_USB_PHY0>;
    reset-names = "usb0_reset";
    #phy-cells = <1>;
    status = "disabled";
};

2.2 修改arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts文件

在文件裏面添加

&otg_sram {
        status = "okay";
};

&usb_otg {
        dr_mode = "host"; /* 三個可選項: otg / host / peripheral  我在這裏指定爲host模式*/
        status = "okay";
};

&usbphy {
        usb0_id_det-gpio = <&pio 4 2 GPIO_ACTIVE_HIGH>; /* PE2 */
        status = "okay";
};

2.3 修改drivers/phy/allwinner/phy-sun4i-usb.c文件

因爲我們在設備樹中定義的驅動是suniv-usb-phy,所以我們需要添加這部分代碼。

enum sun4i_usb_phy_type {
    suniv_phy,  //新增枚舉值
    sun4i_a10_phy,
    sun6i_a31_phy,
    sun8i_a33_phy,
    sun8i_a83t_phy,
    sun8i_h3_phy,
    sun8i_r40_phy,
    sun8i_v3s_phy,
    sun50i_a64_phy,
    sun50i_h6_phy,
};

 

//新增以下結構
static const struct sun4i_usb_phy_cfg suniv_cfg = {
    .num_phys = 1,
    .type = suniv_phy,
    .disc_thresh = 3,
    .phyctl_offset = REG_PHYCTL_A10,
    .dedicated_clocks = true,
};

 

static const struct of_device_id sun4i_usb_phy_of_match[] = {
    { .compatible = "allwinner,suniv-usb-phy", .data = &suniv_cfg }, //新增配置項
    { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
    { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
    { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
    { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
    { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
    { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
    { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
    { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
    { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
    { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
    { .compatible = "allwinner,sun50i-a64-usb-phy",
      .data = &sun50i_a64_cfg},
    { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
    { },
};

2.4 修改drivers/usb/musb/sunxi.c文件

這裏主要處理設備樹中定義的驅動suniv-musb

static int sunxi_musb_probe(struct platform_device *pdev)
{
    struct musb_hdrc_platform_data    pdata;
    struct platform_device_info    pinfo;
    struct sunxi_glue        *glue;
    struct device_node        *np = pdev->dev.of_node;
    int ret;

    if (!np) {
        dev_err(&pdev->dev, "Error no device tree node found\n");
        return -EINVAL;
    }

    glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
    if (!glue)
        return -ENOMEM;

    memset(&pdata, 0, sizeof(pdata));
    switch (usb_get_dr_mode(&pdev->dev)) {
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
    case USB_DR_MODE_HOST:
        pdata.mode = MUSB_HOST;
        glue->phy_mode = PHY_MODE_USB_HOST;
        break;
#endif
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET
    case USB_DR_MODE_PERIPHERAL:
        pdata.mode = MUSB_PERIPHERAL;
        glue->phy_mode = PHY_MODE_USB_DEVICE;
        break;
#endif
#ifdef CONFIG_USB_MUSB_DUAL_ROLE
    case USB_DR_MODE_OTG:
        pdata.mode = MUSB_OTG;
        glue->phy_mode = PHY_MODE_USB_OTG;
        break;
#endif
    default:
        dev_err(&pdev->dev, "Invalid or missing 'dr_mode' property\n");
        return -EINVAL;
    }
    pdata.platform_ops    = &sunxi_musb_ops;
    if (!of_device_is_compatible(np, "allwinner,sun8i-h3-musb"))
        pdata.config = &sunxi_musb_hdrc_config;
    else
        pdata.config = &sunxi_musb_hdrc_config_h3;

    glue->dev = &pdev->dev;
    INIT_WORK(&glue->work, sunxi_musb_work);
    glue->host_nb.notifier_call = sunxi_musb_host_notifier;

    if (of_device_is_compatible(np, "allwinner,sun4i-a10-musb")||
        of_device_is_compatible(np, "allwinner,suniv-musb")){ //新增判斷項代碼
        set_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags);
    }
    if (of_device_is_compatible(np, "allwinner,sun6i-a31-musb"))
        set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);

    if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb") ||
        of_device_is_compatible(np, "allwinner,sun8i-h3-musb") ||
        of_device_is_compatible(np, "allwinner,suniv-musb")) {  //新增判斷項代碼
        set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
        set_bit(SUNXI_MUSB_FL_NO_CONFIGDATA, &glue->flags);
    }

    glue->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(glue->clk)) {
        dev_err(&pdev->dev, "Error getting clock: %ld\n",
            PTR_ERR(glue->clk));
        return PTR_ERR(glue->clk);
    }

    if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) {
        glue->rst = devm_reset_control_get(&pdev->dev, NULL);
        if (IS_ERR(glue->rst)) {
            if (PTR_ERR(glue->rst) == -EPROBE_DEFER)
                return -EPROBE_DEFER;
            dev_err(&pdev->dev, "Error getting reset %ld\n",
                PTR_ERR(glue->rst));
            return PTR_ERR(glue->rst);
        }
    }

    …………

 

static const struct of_device_id sunxi_musb_match[] = {
    { .compatible = "allwinner,suniv-musb", }, //新增代碼
    { .compatible = "allwinner,sun4i-a10-musb", },
    { .compatible = "allwinner,sun6i-a31-musb", },
    { .compatible = "allwinner,sun8i-a33-musb", },
    { .compatible = "allwinner,sun8i-h3-musb", },
    {}
};

2.5 通過menuconfig配置usb相關的選項

進入:Device Drivers - USB support

 

 

  

 

 進行配置,當然可以根據自己實際情況進行開啓與配置。

接下載編譯Linux編譯內核和設備樹,就可以得到內核文件和設備樹文件了。

3. 測試USB鍵盤

這裏我默認設置開發板爲host模式,然後我要通過usb連接鍵盤進行輸入操作:

[   31.653231] cfg80211: failed to load regulatory.db
[   31.696053] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[   31.707915] VFS: Mounted root (ext4 filesystem) on device 179:2.
[   31.726584] devtmpfs: mounted
[   31.736031] Freeing unused kernel memory: 1024K
[   31.746083] Run /sbin/init as init process
[   32.023410] usb 1-1: new high-speed USB device number 2 using musb-hdrc
[   32.215728] hub 1-1:1.0: USB hub found
[   32.221496] hub 1-1:1.0: 4 ports detected

 啓動日誌中我們可以看到usb已經被檢測到,並且檢測到4個端口,但是我們只用到兩個。

插入鍵盤。

拔出鍵盤。

 鍵盤操作開發板:

 如果想要進一步瞭解usb相關的操作,可以藉助usbutils相關組件。

首先安裝usbutils組件

apt-get install usbutils

安裝完成後執行:

lsusb 

就可以查看相關usb設備信息了。

4 通過LINUX方式更改USB屬性

首先更改設備樹,改爲otg模式

&usb_otg {
        dr_mode = "otg"; /* 三個可選項: otg / host / peripheral */
        status = "okay";
};

進入Linux系統,執行,usb將會被設置成爲host模式

echo host > /sys/devices/platform/soc/1c13000.usb/musb-hdrc.1.auto/mode

運行結果如下,此時可以插入鍵盤,就可以使用鍵盤操作Linux了。

# echo host > /sys/devices/platform/soc/1c13000.usb/musb-hdrc.1.auto/mode
# [117.758152] phy phy-1c13400.phy.0: Changing dr_mode to 1
[  118.414817] usb 1-1: new high-speed USB device number 3 using musb-hdrc
[  118.598193] usb-storage 1-1:1.0: USB Mass Storage device detected
[  118.611789] scsi host0: usb-storage 1-1:1.0
[  119.686198] scsi 0:0:0:0: Direct-Access     Mass     Storage Device   1.00 PQ: 0 ANSI: 0 CCS
[  119.703976] sd 0:0:0:0: [sda] 3842048 512-byte logical blocks: (1.97 GB/1.83 GiB)
[  119.725260] sd 0:0:0:0: Attached scsi generic sg0 type 0
[  119.739844] sd 0:0:0:0: [sda] Write Protect is off
[  119.771819] sd 0:0:0:0: [sda] No Caching mode page found
[  119.777288] sd 0:0:0:0: [sda] Assuming drive cache: write through
[  119.801571]  sda: sda1 sda2 sda3
[  119.817224] sd 0:0:0:0: [sda] Attached SCSI removable disk

如果執行如下命令,則進入設備模式

#
##切換到device模式:
# echo peripheral > /sys/devices/platform/soc/1c13000.usb/musb-hdrc.1.auto/mode
[  123.880272] phy phy-1c13400.phy.0: Changing dr_mode to 2
# [  123.890905] usb 1-1: USB disconnect, device number 3

 

 後記

在論壇中用有測試表明,在這個方案配置完USB後,可能無法同時運行兩個控制設備,墨雲沒做相關測試,如果有相關需求的還請注意覈實。謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章