前言
根據MT7620的數據手冊,該芯片支持兩個獨立的spi接口,由於驅動不完善等種種原因,一直沒能順利的使用第二個spi接口.近日對mt7620的spi好好研究了一下,終於使能了第二個spi接口,將過程記錄成文.
實現過程
spi驅動的詭異之處
mt7620的spi驅動位於:drivers/spi/spi-rt2880.c,裏面有關spi cs1的代碼非常詭異,因爲7620與5350使用的是同一個spi控制芯片,而該驅動卻區別對待,7620只有一個spi,5350有兩個.
1: static struct rt2880_spi_ops spi_ops[] = {
2: {
3: .init_hw = rt2880_spi_reset,
4: .num_cs = 1,
5: }, {
6: .init_hw = rt5350_spi_reset,
7: .num_cs = 2,
8: },
9: };
10:
11: static const struct of_device_id rt2880_spi_match[] = {
12: { .compatible = "ralink,rt2880-spi", .data = &spi_ops[0]},
13: { .compatible = "ralink,rt5350-spi", .data = &spi_ops[1]},
14: {},
15: };
因此,第一個修改便是,將dts文件中對應的compatible修改爲5350的版本.重新編譯之後,驅動成功加載,然而,問題還遠沒有解決.
cs1沒有輸出信號
在第二個spi接口上連接一個spi flash芯片作爲測試,始終無法probe到芯片.覺得很奇怪,於是用示波器來觀察cs1上的信號,居然沒有任何信號輸出!仔細閱讀7620的編程手冊,發現了一個關鍵的內容:
spi_cs1在系統上電默認狀態下是用作reference clock的!很顯然,該引腳必須設置爲normal spi mode.那麼問題來了:如何設置這個引腳的功能呢?答案就在pinmux驅動中.
pinmux尋蹤
很多嵌入式設備都有管腳複用功能,7620也不例外.pinmux數據在arch/mips/ralink/mt7620.c中定義:
1: static struct rt2880_pmx_func i2c_grp[] = { FUNC("i2c", 0, 1, 2) };
2: static struct rt2880_pmx_func spi_grp[] = { FUNC("spi", 0, 3, 4) };
3: static struct rt2880_pmx_func uartlite_grp[] = { FUNC("uartlite", 0, 15, 2) };
4: static struct rt2880_pmx_func mdio_grp[] = { FUNC("mdio", 0, 22, 2) };
5: static struct rt2880_pmx_func rgmii1_grp[] = { FUNC("rgmii1", 0, 24, 12) };
6: static struct rt2880_pmx_func refclk_grp[] = { FUNC("spi refclk", 0, 37, 3) };
7: static struct rt2880_pmx_func ephy_grp[] = { FUNC("ephy", 0, 40, 5) };
8: static struct rt2880_pmx_func rgmii2_grp[] = { FUNC("rgmii2", 0, 60, 12) };
9: static struct rt2880_pmx_func wled_grp[] = { FUNC("wled", 0, 72, 1) };
10: static struct rt2880_pmx_func pa_grp[] = { FUNC("pa", 0, 18, 4) };
11:
12: static struct rt2880_pmx_group mt7620a_pinmux_data[] = {
13: GRP("i2c", i2c_grp, 1, MT7620_GPIO_MODE_I2C),
14: GRP("uartf", uartf_grp, MT7620_GPIO_MODE_UART0_MASK,
15: MT7620_GPIO_MODE_UART0_SHIFT),
16: GRP("spi", spi_grp, 1, MT7620_GPIO_MODE_SPI),
17: GRP("uartlite", uartlite_grp, 1, MT7620_GPIO_MODE_UART1),
18: GRP_G("wdt", wdt_grp, MT7620_GPIO_MODE_WDT_MASK,
19: MT7620_GPIO_MODE_WDT_GPIO, MT7620_GPIO_MODE_WDT_SHIFT),
20: GRP("mdio", mdio_grp, 1, MT7620_GPIO_MODE_MDIO),
21: GRP("rgmii1", rgmii1_grp, 1, MT7620_GPIO_MODE_RGMII1),
22: GRP("spi refclk", refclk_grp, 1, MT7620_GPIO_MODE_SPI_REF_CLK),
23: GRP_G("pcie", pcie_rst_grp, MT7620_GPIO_MODE_PCIE_MASK,
24: MT7620_GPIO_MODE_PCIE_GPIO, MT7620_GPIO_MODE_PCIE_SHIFT),
25: GRP_G("nd_sd", nd_sd_grp, MT7620_GPIO_MODE_ND_SD_MASK,
26: MT7620_GPIO_MODE_ND_SD_GPIO, MT7620_GPIO_MODE_ND_SD_SHIFT),
27: GRP("rgmii2", rgmii2_grp, 1, MT7620_GPIO_MODE_RGMII2),
28: GRP("wled", wled_grp, 1, MT7620_GPIO_MODE_WLED),
29: GRP("ephy", ephy_grp, 1, MT7620_GPIO_MODE_EPHY),
30: GRP("pa", pa_grp, 1, MT7620_GPIO_MODE_PA),
31: { 0 }
32: };
如何使能這些複用功能,則由dts與pinmux驅動協同完成,爲了能夠仔細分析pinmux實現機理,在drivers/pinctrl/pinctrl-rt2880.c的關鍵函數rt2880_pmx_group_enable中加入診斷代碼:
1: static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev,
2: unsigned func,
3: unsigned group)
4: {
5: struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
6: u32 mode = 0;
7: int i;
8:
9: /* dont allow double use */
10: if (p->groups[group].enabled) {
11: dev_err(p->dev, "%s is already enabled\n", p->groups[group].name);
12: return -EBUSY;
13: }
14:
15: p->groups[group].enabled = 1;
16: p->func[func]->enabled = 1;
17:
18: mode = rt_sysc_r32(SYSC_REG_GPIO_MODE);
19: mode &= ~(p->groups[group].mask << p->groups[group].shift);
20:
21: /* mark the pins as gpio */
22: for (i = 0; i < p->groups[group].func[0].pin_count; i++)
23: p->gpio[p->groups[group].func[0].pins[i]] = 1;
24:
25: /* function 0 is gpio and needs special handling */
26: if (func == 0) {
27: mode |= p->groups[group].gpio << p->groups[group].shift;
28: } else {
29: for (i = 0; i < p->func[func]->pin_count; i++)
30: p->gpio[p->func[func]->pins[i]] = 0;
31: mode |= p->func[func]->value << p->groups[group].shift;
32: }
33: rt_sysc_w32(mode, SYSC_REG_GPIO_MODE);
34:
35: // manfeel, add debug info
36:
37: dev_info(p->dev, "%s(%d),%s(%d)\t\t= %x\n", p->groups[group].name, group, p->func[func]->name, func, mode);
38:
39: return 0;
40: }
在TTL控制檯中,能觀察到這些信息:
1: [ 0.080000] rt2880-pinmux pinctrl.1: ephy(12),gpio(0) = ab11d
2: [ 0.080000] rt2880-pinmux pinctrl.1: wled(11),gpio(0) = ab11d
3: [ 0.090000] rt2880-pinmux pinctrl.1: pa(13),gpio(0) = 1ab11d
4: [ 0.090000] rt2880-pinmux pinctrl.1: wdt(4),gpio(0) = 5ab11d
5: [ 0.100000] rt2880-pinmux pinctrl.1: uartf(1),gpio(0) = 5ab11d
6: [ 0.100000] rt2880-pinmux pinctrl.1: mdio(5),gpio(0) = 5ab11d
7: [ 0.110000] bio: create slab <bio-0> at 0
8: [ 0.120000] rt2880_gpio 10000600.gpio: registering 24 gpios
9: [ 0.120000] rt2880_gpio 10000600.gpio: registering 24 irq handlers
10: [ 0.130000] rt2880_gpio 10000638.gpio: registering 16 gpios
11: [ 0.130000] rt2880_gpio 10000638.gpio: registering 16 irq handlers
12: [ 0.140000] rt2880_gpio 10000660.gpio: registering 32 gpios
13: [ 0.140000] rt2880_gpio 10000660.gpio: registering 32 irq handlers
14: [ 0.150000] rt2880_gpio 10000688.gpio: registering 1 gpios
15: [ 0.150000] rt2880_gpio 10000688.gpio: registering 1 irq handlers
16: ... ...
17: [ 0.160000] rt2880-pinmux pinctrl.1: i2c(0),i2c(1) = 5ab11c
18: [ 0.170000] i2c-ralink 10000900.i2c: loaded
19: ... ...
20: [ 0.410000] rt2880-pinmux pinctrl.1: uartlite(3),uartlite(10) = 5ab11c
21: [ 0.410000] 10000c00.uartlite: ttyS0 at MMIO 0x10000c00 (irq = 20, base_baud = 2500000) is a 16550A
22: ... ...
23: [ 0.470000] rt2880-pinmux pinctrl.1: spi(2),spi(9) = 5ab11c
24: [ 0.500000] m25p80 spi32766.0: found en25p64, expected w25q256
25: [ 0.500000] m25p80 spi32766.0: en25p64 (8192 Kbytes)
結合dts文件,能夠理解得更加透徹(用I2C來分析):
1: i2c@900 {
2: compatible = "link,mt7620a-i2c", "ralink,rt2880-i2c";
3: reg = <0x900 0x100>;
4:
5: resets = <&rstctrl 16>;
6: reset-names = "i2c";
7:
8: #address-cells = <1>;
9: #size-cells = <0>;
10:
11: status = "disabled";
12:
13: pinctrl-names = "default";
14: pinctrl-0 = <&i2c_pins>;
15: };
16: ... ...
17: pinctrl {
18: compatible = "ralink,rt2880-pinmux";
19: pinctrl-names = "default";
20: pinctrl-0 = <&state_default>;
21:
22: i2c_pins: i2c {
23: i2c {
24: ralink,group = "i2c";
25: ralink,function = "i2c";
26: };
27: };
28: };
dts中,每一個設備節點可能需要用到一些複用管腳,該機制通過pinctrl-0來指定,如i2c中的i2c_pins.我們再來看看spi設備節點中的數據:
1: spi@b00 {
2: compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi";
3: reg = <0xb00 0x100>;
4:
5: resets = <&rstctrl 18>;
6: reset-names = "spi";
7:
8: #address-cells = <1>;
9: #size-cells = <1>;
10:
11: status = "disabled";
12:
13: pinctrl-names = "default";
14: pinctrl-0 = <&spi_pins>;
15: };
結合前面的知識,對dts修改如下:
1: spi_pins: spi {
2: spi {
3: ralink,group = "spi";
4: ralink,function = "spi";
5: };
6: /* added by manfeel */
7: cs1 {
8: ralink,group = "spi refclk";
9: ralink,function = "spi refclk";
10: };
11: };
至此,mt7620的第二個spi終於可以使用.
總結
充分利用dts的功能,能夠達到很多意想不到的效果.一句話:dts的強大超乎我們的想象!
最後一點也是最重要的一點:很多7620的開發板在設計的時候,並沒有考慮要使用第二個spi接口,因此對cs1的管腳是做懸浮處理的,如果要正確使用spi的cs1,則需要對該引腳做上拉處理,否則會導致系統出現各種奇怪的問題.