Phytec中国的wiki
support@phytec.cn
热线:0755-61802110-803
本文主要讨论在AM335x平台上使用GPMC来连接外部接口,如FPGA、DSP、单片机等外设的设备树配置与如何测试方面的内容。
这里的例子是在NAND版本的核心板基础上实现的,也就是说NAND和FPGA器件处于同一个总线上,因此FPGA这边配置不正确可能会影响到NAND的问题,因此推荐需要检查FPGA的以下项目:
- 检查与核心板连接的引脚的上电状态,不能影响核心板上电后访问NAND
- 检查代码,不能在CS生效前对总线信号进行操作
GPMC基础内容
本文很多参考内容都来自TI的AM335x参考手册TRM,因此请也一定详细阅读 7.1 GPMC这个章节。
根据TI的AM335x TRM Table 7-5,GPMC接口有以下几个工作模式:
模式 | 说明 |
---|---|
非复用地址16位 | 注意此时地址线的A[0]没有使用,从A[1]开始 |
非复用地址8位 | |
复用地址16位 | 注意没有8位复用地址模式 |
16位NAND | 用于NAND |
8位NAND | 用于NAND |
一般连接FPGA使用的是前三个模式。其中16位复用模式使用的引脚数比较少。
以下主要说明在非NAND模式的引脚功能:
引脚 | 说明 |
---|---|
CSxn | 片选信号 |
ADVn_ALE | 地址有效信号,用于锁存地址 |
BE0n_CLE/BE1n | 字节有效 |
CLK | 时钟(同步传输使用,异步不用) |
OE_REn | 输出使能 |
WAITx | 等待信号 |
WEn | 写入使能 |
WPn | 写入保护 |
其中需要注意的是,AM335x的GPMC引脚有部分和显示的LCD接口冲突,因此在即需要GPMC又需要LCD的情况下,有所限制,我们这里之前做的一个例子中,LCD只能使用565模式,GPMC采用16位数据总线非复用模式,只能到11位地址宽度。
设备树配置
在内核中,关于GPMC的文档相对还是挺多的(下面未列出所有):
- https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
- https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/mtd/gpmc-nor.txt
- https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/mtd/mtd-physmap.txt
- https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/net/gpmc-eth.txt
- https://elixir.bootlin.com/linux/latest/source/Documentation/bus-devices/ti-gpmc.txt
其中在这里的应用中最关键的是前三个文档。
以下为我们对设备树的修改:
- 由于默认的LED使用了GPMC的引脚,因此在设备树中关闭了LED的配置。
- 覆盖了nandflash_pins这个引脚配置的节点,增加了两个引脚的配置,一个是clk输出,另一个是cs2。
- 为gpmc节点修改了ranges参数,该参数之前只配置了NAND的cs0范围,这里增加了cs2的内存范围(可选范围为0x0900_0000~0x1FFF_FFFF,还又其他一些限制,请阅读TRM手册)。
- 为gpmc增加了nor子节点,该节点主要说明这个这个子设备是什么类型的设备(即由哪个驱动来管理),内存范围,接口配置,时序配置。
其中,几个重要的子节点说明如下:
gpmc,mux-add-data:该配置写入的是GPMC寄存器的CONFIG1中的MUXADDDATA
- 0:不用复用接口
- 1:AAD复用模式
- 2:AD复用模式
- AAD和AD两种的区别可以在TRM中找到。
- gpmc,device-width / bank-width:该配置写入同上的DEVICESIZE,有8位和16位可选,请注意这里与之前的mux-add-data不能冲突,必须为gpmc支持的模式。
- compatible: 这个节点可以选择 mtd-ram和cfi-flash,我觉得mtd-ram比较好理解,因此就使用的是这个。
- nor: mtd-ram和cfi-flash都属于gpmc-nor这个里面。
diff --git a/arch/arm/boot/dts/am335x-pcm-953.dtsi b/arch/arm/boot/dts/am335x-pcm-953.dtsi index 4b3ef95..1392906 100644 --- a/arch/arm/boot/dts/am335x-pcm-953.dtsi +++ b/arch/arm/boot/dts/am335x-pcm-953.dtsi @@ -43,6 +43,7 @@ compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&user_leds_pins>; + status = "disabled"; green { label = "green:user"; diff --git a/arch/arm/boot/dts/am335x-phycore-rdk.dts b/arch/arm/boot/dts/am335x-phycore-rdk.dts index 12061fb..10c60be 100644 --- a/arch/arm/boot/dts/am335x-phycore-rdk.dts +++ b/arch/arm/boot/dts/am335x-phycore-rdk.dts @@ -25,3 +25,77 @@ status = "okay"; }; + +&am33xx_pinmux { + nandflash_pins: pinmux_nandflash { + pinctrl-single,pins = < + AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad0.gpmc_ad0 */ + AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad1.gpmc_ad1 */ + AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad2.gpmc_ad2 */ + AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad3.gpmc_ad3 */ + AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad4.gpmc_ad4 */ + AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad5.gpmc_ad5 */ + AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad6.gpmc_ad6 */ + AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad7.gpmc_ad7 */ + AM33XX_IOPAD(0x870, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_wait0.gpmc_wait0 */ + AM33XX_IOPAD(0x87c, PIN_OUTPUT | MUX_MODE0) /* gpmc_csn0.gpmc_csn0 */ + AM33XX_IOPAD(0x890, PIN_OUTPUT | MUX_MODE0) /* gpmc_advn_ale.gpmc_advn_ale */ + AM33XX_IOPAD(0x894, PIN_OUTPUT | MUX_MODE0) /* gpmc_oen_ren.gpmc_oen_ren */ + AM33XX_IOPAD(0x898, PIN_OUTPUT | MUX_MODE0) /* gpmc_wen.gpmc_wen */ + AM33XX_IOPAD(0x89c, PIN_OUTPUT | MUX_MODE0) /* gpmc_be0n_cle.gpmc_be0n_cle */ + + AM33XX_IOPAD(0x880, PIN_OUTPUT | MUX_MODE1) /* gpmc_csn1.gpmc_clk_mux1 */ + AM33XX_IOPAD(0x884, PIN_OUTPUT | MUX_MODE0) /* gpmc_csn2.gpmc_csn2 */ + >; + }; +}; + + +&gpmc { + ranges = <0 0 0x08000000 0x1000000>, + <2 0 0x10000000 0x1000000>; + nor@0,0 { + compatible = "mtd-ram"; + linux,mtd-name = "phytec,fpga"; + reg = <2 0 0x1000000>; + gpmc,device-width = <2>; /* 16 bit */ + gpmc,mux-add-data = <2>; /* address-data mode */ + bank-width = <2>; /* 16 bit */ + + gpmc,cs-on-ns = <50>; /* Assertion time */ + gpmc,adv-rd-off-ns = <200>; /* Read deassertion time */ + gpmc,adv-wr-off-ns = <50>; /* Write deassertion time */ + + /* OE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4: */ + gpmc,oe-on-ns = <150>; /* Assertion time */ + gpmc,oe-off-ns = <300>; /* Deassertion time */ + + /* WE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4: */ + gpmc,we-on-ns = <20>; /* Assertion time */ + gpmc,we-off-ns = <20>; /* Deassertion time */ + + /* Access time and cycle time timings (in nanoseconds) corresponding to GPMC_CONFIG5: */ + gpmc,page-burst-access-ns = <20>; /* Multiple access word delay */ + gpmc,access-ns = <250>; /* Start-cycle to first data valid delay */ + gpmc,rd-cycle-ns = <300>; /* Total read cycle time */ + gpmc,wr-cycle-ns = <300>; /* Total write cycle time */ + + gpmc,sync-clk-ps = <0>; /* Minimum clock period for synchronous mode, in picoseconds */ + + gpmc,cs-rd-off-ns = <290> ; /* Read deassertion time */ + gpmc,cs-wr-off-ns = <40>; /* Write deassertion time */ + + /* ADV signal timings (in nanoseconds) corresponding to GPMC_CONFIG3: */ + gpmc,adv-on-ns = <100>; /* Assertion time */ + + gpmc,wait-on-read = "false"; + gpmc,wait-on-write = "false"; + gpmc,sync-read = "false"; + gpmc,sync-write = "false"; + gpmc,burst-read = "false"; + gpmc,burst-write = "false"; + #address-cells = <1>; + #size-cells = <1>; + }; +}; +
下面说明时序的配置,下图来自TRM,是异步单次读取复用地址设备的时序图,也是之前设备树的配置。
可以看到所有的时序都可以通过设备树配置,请注意这里的时序不仅仅是微调,而是可以改变每个事件的时间,从而影响功能,因此要仔细配置,我这里的配置主要是为了让速度慢的逻辑分析仪能看到所有事件的发生。
除了上述的设备树配置,在内核的menuconfig需要增加mtd-ram选项,另外推荐增加下列的调试打印信息(注意内核版本不同可能需要修改):
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index bf0fe01..086e5ff 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -2046,6 +2046,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, "GPMC CS %d end cannot be greater than 0x%x\n", cs, GPMC_MEM_END); } + dev_err(&pdev->dev, "%s: unknown error'\n", + child->name); goto err; } @@ -2083,8 +2085,11 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, } else { ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "bank width failed %s\n", + child->name); goto err; + } } /* Reserve wait pin if it is required and valid */ @@ -2103,8 +2108,11 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); ret = gpmc_cs_program_settings(cs, &gpmc_s); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "gpmc_cs_prog_setting failed:%s\n", + child->name); goto err_cs; + } ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); if (ret) { diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index afb43d5..dd6b85c 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -34,6 +34,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) { struct mtd_info *mtd; + printk("map_ram_probe\n"); /* Check the first byte is RAM */ #if 0 map_write8(map, 0x55, 0); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 3fad359..fabd54a 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -266,6 +266,7 @@ static int of_flash_probe(struct platform_device *dev) * flash will not be exposed directly to the MTD users * (e.g. JFFS2) any more. */ + dev_dbg(&dev->dev, "probe_type:%s\n", probe_type); if (map_indirect) info->list[i].map.phys = NO_XIP;
实测
在启动后,可以用以下指令看到成功分配了内存地址:
root@phycore-am335x-1:~# cat /proc/iomem 08000000-08000003 : /ocp/gpmc@50000000/nand@0,0 10000000-10ffffff : 10000000.nor <--新增的内存空间 40300000-4030ffff : 40300000.ocmcram 44e07000-44e07fff : /ocp/gpio@44e07000 44e09000-44e0afff : /ocp/serial@44e09000 44e0b000-44e0bfff : /ocp/i2c@44e0b000 ...
在内核的message也可以看到没有报错:
root@phycore-am335x-1:~# dmesg | grep gpmc [ 0.187039] omap-gpmc 50000000.gpmc: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_nandflash, deferring probe [ 1.503767] omap-gpmc 50000000.gpmc: GPMC revision 6.0 [ 1.509176] gpmc_mem_init: disabling cs 0 mapped at 0x0-0x1000000 root@phycore-am335x-1:~# dmesg | grep ram [ 0.058942] Security Framework initialized [ 0.950866] Console: switching to colour frame buffer device 100x30 [ 0.964555] tilcdc 4830e000.lcdc: fb0: frame buffer device [ 1.796465] map_ram_probe <---注意这是之前新加入的调试代码
接下来使用devmem2
root@phycore-am335x-1:~# devmem2 0x100055aa h /dev/mem opened. Memory mapped at address 0xb6f05000. Read at address 0x100055AA (0xb6f055aa): 0x0000
此时在逻辑分析仪(采样率16M)那边可以看到:
请注意我这里读取的地址是0x1000_55AA,使用的AD模式,而我只能测到低8位的地址,因此能看到读取的地址是D5,这是因为目前是16位宽度,则地址是按照半字的方式来寻址,则是0x55AA右移1位为0x2AD5.
如果内存未分配成功,则会报类似以下错误:
[ 81.070915] Unhandled fault: external abort on non-linefetch (0x1018) at 0xb6f65000 [ 81.082436] pgd = dc714000 [ 81.085281] [b6f65000] *pgd=9c5b7831, *pte=10000303, *ppte=10000a33 [ 81.095955] audit: type=1701 audit(1531923959.770:2): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=223 comm="devmem2" exe="/usr/bin/devmem2" sig=7
另外有篇博客写的很不错,也请阅读以下:
https://blog.csdn.net/u012010054/article/details/81094704
GPMC可以达到的带宽
根据 https://e2e.ti.com/support/processors/f/791/t/621859 gpmc应该可以达到1Gbps。