eLBC增强型控制器使用总结+PowerPC P2040 reboot命令无效原因分析

eLBC 增强型本地总线控制器,NOTE !!!总线控制器,从名字可以获取到一个关键的信息,“总线控制器”,可以猜测什么样的总线控制器是增强型控制器,具备多个控制器的功能?或者可以多用兼容不同接口,看一下官方描述

上面说到,有以下功能,通用的片选控制器,NAND flash控制器,还有一个用户可编程的控制器UPM,接下来看一下各自的特点

GPCM

兼容SRAM EPROM NOR FLASH EEPROM和其他外围设备

第二个的意思是可以用来启动系统

最小三个时钟周期访问外围设备

 

两个字节写使能信号,输出使能信号,外部访问终止信号

FCM(NAND FLASH 控制器):

兼容大页和小页并行NAND FLASH EEPROM

和GPCM一样,可以用来引导系统启动

ECC校验

Boot 片选 支持8bit位宽的设备

可编程指令和数据传输队列支持8个,大概是这个意思

通用命令和地址寄存器支持专有的Flash接口

块写锁定确保系统安全和完整

UPM

每一个UPM可以设置支持64,128,256,512Kb深度的DRAM

支持8bit和16bit的设备

页模式支持突发的连续传输

。。。。。。。略

上边大概的意思就是访问一个地址时,也就是访问memory时,访问的地址在哪个片选上,则该片选有效,该片选的控制器(GPCM,FCM,UPM这些控制器)就会产生相应的信号去发起数据传输。

信号描述:

太多了,包括写保护等等,看手册吧,这里把这个东东拿出来关照一下,或许调试的时候会用到,这里可以看到,总共有四个片选,该字段每一位表示一个片选。

寄存器:

BR ,OR?是个什么东西?(GPCM

划重点:标黄的了解一下

下边看BR寄存器到底搞了个什么东东

BA:基地址,只有17位,这里提到了OR寄存器里的AM字段,后边观察一下和它有什么联系

上边的字段是对位宽,写保护,以及对GPCM,FCM,UPM的选择,最后一位是设置该片选是否有效,这是个重点,稍微注意一下。

下边看一下OR

上边说到,bank0可以用来引导系统,OR0的复位值和其他的不同,地址掩码字段(AM)和BR的BA一样,总共17位,剩余的描述,大概意思就是AM可以设置外部设备可以访问的地址范围,最小32Kb,最大4Gb,来人,上图

AM:地址掩码,到底是个什么东西,答案是设置Memory Bank Size

下边的一些字段都和时序有关系,不同的设备,时序不同,具体怎么设置,只有研究手册咯

其他寄存器:

特别注意一下eLBC_LBCR这个寄存器,曾经踩过坑,主要包括对一些信号极性的设置,

外挂bpi flash的时候遇到这个信号的问题,导致flash读写不对,时序没对上

BR,OR配置的一个例子:

eLBC相关的寄存器在哪里初始化的呢?

答案是:uboot阶段,uboot引导系统,自然要初始化。

上代码:

void init_early_memctl_regs(void)

{

        uint init_br1 = 1;

#ifdef CONFIG_SYS_FSL_ERRATUM_ELBC_A001

        /* Set the local bus monitor timeout value to the maximum */

        clrsetbits_be32(&(LBC_BASE_ADDR)->lbcr, LBCR_BMT|LBCR_BMTPS, 0xf);

#endif

 

#ifdef CONFIG_MPC85xx

        /* if cs1 is already set via debugger, leave cs0/cs1 alone */

        if (get_lbc_br(1) & BR_V)

                init_br1 = 0;

#endif

 

        /*

         * Map banks 0 (and maybe 1) to the FLASH banks 0 (and 1) at

         * preliminary addresses - these have to be modified later

         * when FLASH size has been determined

         */

#if defined(CONFIG_SYS_OR0_REMAP)

        set_lbc_or(0, CONFIG_SYS_OR0_REMAP);

#endif

#if defined(CONFIG_SYS_OR1_REMAP)

        set_lbc_or(1, CONFIG_SYS_OR1_REMAP);

#endif

        /* now restrict to preliminary range */

        if (init_br1) {

#if defined(CONFIG_SYS_BR0_PRELIM) && defined(CONFIG_SYS_OR0_PRELIM)

                set_lbc_br(0, CONFIG_SYS_BR0_PRELIM);

                set_lbc_or(0, CONFIG_SYS_OR0_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM)

                set_lbc_or(1, CONFIG_SYS_OR1_PRELIM);

                set_lbc_br(1, CONFIG_SYS_BR1_PRELIM);

#endif

        }

 

#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM)

        set_lbc_or(2, CONFIG_SYS_OR2_PRELIM);

        set_lbc_br(2, CONFIG_SYS_BR2_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM)

        set_lbc_or(3, CONFIG_SYS_OR3_PRELIM);

        set_lbc_br(3, CONFIG_SYS_BR3_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM)

        set_lbc_or(4, CONFIG_SYS_OR4_PRELIM);

        set_lbc_br(4, CONFIG_SYS_BR4_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM)

        set_lbc_or(5, CONFIG_SYS_OR5_PRELIM);

        set_lbc_br(5, CONFIG_SYS_BR5_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM)

        set_lbc_or(6, CONFIG_SYS_OR6_PRELIM);

        set_lbc_br(6, CONFIG_SYS_BR6_PRELIM);

#endif

 

#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM)

        set_lbc_or(7, CONFIG_SYS_OR7_PRELIM);

        set_lbc_br(7, CONFIG_SYS_BR7_PRELIM);

#endif

}

可以看到上边这个函数初始化了BR和OR,这里有8个bank,函数使用的相关宏,在include/configs/P2041RDB.h头文件里定义,比如CS1的配置(修改过)

/*for nor flash*/

#define CONFIG_SYS_BR1_PRELIM  0xe0001001 /* CS1 -> NOR FLASH */

#define CONFIG_SYS_OR1_PRELIM  0xf8000f85 /* FLASH MAX SIZE:128M */

上边这个例子中可以看到,CS1的起始地址是0xE0000000开始的,从BR的低17位0xe000 + 4b0001 可以看出,CS1的基地址为0XE0000000,OR的低17位0xf8000对应上边关于OR描述那部分中的表可以看出,地址范围为128M,再看一下CS0的配置,BR:0xe8001001,OR:0xf8000f85,实际上除了基地址不一样以外,其他地方是一样的,uboot的默认配置,CS1是NAND FLASH,而这里由于需要,配置为NOR FLASH,也就是说,如果分析官方的代码,实际上CS1并不是上边的配置。那么这个地方的0XE0000000这个地址以及0XE8000000从何而来呢,先看一下代码(include/configs/P2041RDB.h,该头文件内容大多是地址的定义,默认的环境变量等),解释一下为什么是0XE8000000,

/*

 * This board doesn't have a promjet connector.

 * However, it uses commone corenet board LAW and TLB.

 * It is necessary to use the same start address with proper offset.

 */

FLASH的基地址,为什么是这个地址?

#define CONFIG_SYS_FLASH_BASE           0xe0000000

#ifdef CONFIG_PHYS_64BIT

#define CONFIG_SYS_FLASH_BASE_PHYS      0xfe0000000ull

#else

#define CONFIG_SYS_FLASH_BASE_PHYS      CONFIG_SYS_FLASH_BASE

#endif

这里是CS0 BR和OR的配置,可以看到,这里在FLASH的基地址上加了一个偏移量0x8000000,为什么要加这个偏移量?

#define CONFIG_SYS_FLASH_BR_PRELIM \

                (BR_PHYS_ADDR((CONFIG_SYS_FLASH_BASE_PHYS + 0x8000000)) | \

                BR_PS_16 | BR_V)

#define CONFIG_SYS_FLASH_OR_PRELIM \

                ((0xf8000ff7 & ~OR_GPCM_SCY & ~OR_GPCM_EHTR) \

                 | OR_GPCM_SCY_8 | OR_GPCM_EHTR_CLEAR)

CPLD,在官方的设计上,这个是控制系统复位的,以及其他的一些功能,在系统启动后,如果不断电,使用reboot命令重启,CPLD起着关键的作用,这里有要谈及另外一个故事了,《关于reboot命令无法重启问题》,p2041这个东西有点神奇,系统接收到reboot命令后,会将复位控制寄存器bit30设置为1,如下,

设置为1后干什么呢,解释如下:

意思就是,会驱动RESET_REQ这个引脚去产生一个复位信号,这个信号给谁,当然是这里的CPLD了,如此,实际上自己定制板卡,或许不会采用官方的设计去使用CPLD,如果采用430类似的方案实现,则需要自己去处理这个复位信号,比如430接收到该信号后,根据P2040的复位时序在HRESET_B或者PORESET_B输出该时序进行复位,uboot代码里有部分代码会读取CPLD的版本号,会用到eLBC,使用该片选需要注意这一点,CPLD的片选使用的是CS3,到此,reboot的故事讲完了。

#define CONFIG_FSL_CPLD

#define CPLD_BASE               0xffdf0000      /* CPLD registers */

#ifdef CONFIG_PHYS_64BIT

#define CPLD_BASE_PHYS          0xfffdf0000ull

#else

#define CPLD_BASE_PHYS          CPLD_BASE

#endif

 

CS3 片选3,实际上OR没有改动,BR改动了一下,除了基地址变化,其余配置和CS0 ,CS1保持一致。具体配置看如何使用。

#if 1

#define CONFIG_SYS_BR3_PRELIM   0xffdf1001

#define CONFIG_SYS_OR3_PRELIM   0xffffeff7      /* 32KB but only 4k mapped */

#else

/*defalut config*/

#define CONFIG_SYS_BR3_PRELIM   (BR_PHYS_ADDR(CPLD_BASE_PHYS) | BR_PS_8 | BR_V)

#define CONFIG_SYS_OR3_PRELIM   0xffffeff7      /* 32KB but only 4k mapped */

#endif

 

#define PIXIS_LBMAP_SWITCH      7

#define PIXIS_LBMAP_MASK        0xf0

#define PIXIS_LBMAP_SHIFT       4

#define PIXIS_LBMAP_ALTBANK     0x40

 

#define CONFIG_SYS_FLASH_QUIET_TEST

#define CONFIG_FLASH_SHOW_PROGRESS      45 /* count down from 45/5: 9..1 */

 

#define CONFIG_SYS_MAX_FLASH_BANKS      1               /* number of banks */

#define CONFIG_SYS_MAX_FLASH_SECT       1024            /* sectors per device */

#define CONFIG_SYS_FLASH_ERASE_TOUT     60000           /* Erase Timeout (ms) */

#define CONFIG_SYS_FLASH_WRITE_TOUT     500             /* Write Timeout (ms) */

 

#define CONFIG_SYS_MONITOR_BASE         CONFIG_SYS_TEXT_BASE

 

#if defined(CONFIG_RAMBOOT_PBL)

#define CONFIG_SYS_RAMBOOT

#endif

 

#define CONFIG_NAND_FSL_ELBC

/* Nand Flash */

#ifdef CONFIG_NAND_FSL_ELBC

#define CONFIG_SYS_NAND_BASE            0xffa00000

#ifdef CONFIG_PHYS_64BIT

#define CONFIG_SYS_NAND_BASE_PHYS       0xfffa00000ull

#else

#define CONFIG_SYS_NAND_BASE_PHYS       CONFIG_SYS_NAND_BASE

#endif

 

#define CONFIG_SYS_NAND_BASE_LIST     {CONFIG_SYS_NAND_BASE}

#define CONFIG_SYS_MAX_NAND_DEVICE      1

#define CONFIG_CMD_NAND

#define CONFIG_SYS_NAND_BLOCK_SIZE    (128 * 1024)

 

/* NAND flash config */

#define CONFIG_SYS_NAND_BR_PRELIM  (BR_PHYS_ADDR(CONFIG_SYS_NAND_BASE_PHYS) \

                               | (2<<BR_DECC_SHIFT)    /* Use HW ECC */ \

                               | BR_PS_8               /* Port Size = 8 bit */ \

                               | BR_MS_FCM             /* MSEL = FCM */ \

                               | BR_V)                 /* valid */

 

#define CONFIG_SYS_NAND_OR_PRELIM  (0xFFFC0000        /* length 256K */ \

                               | OR_FCM_PGS            /* Large Page*/ \

                               | OR_FCM_CSCT \

                               | OR_FCM_CST \

                               | OR_FCM_CHT \

                               | OR_FCM_SCY_1 \

                               | OR_FCM_TRLX \

                               | OR_FCM_EHTR)

上边从代码层面解释了为什么CS0的基地址是0XE8000000,但仍旧没看到官方的说明,下边摘自《QORIQ-SDK-2.0-IC-REV0 1703.pdf》。

可以看到eLBC的地址范围是0xfe0000000 – 0xfefffffff,这里可以清楚eLBC的地址范围了,但为什么CS0的基地址为加上0x8000000的偏移量呢,从上边头文件的宏定义中可以看到,CS0基地址为0XE8000000,CS1基地址为0XE0000000,猜想,是不是CS0和CS1交换基地址也可以呢,官方为什么将CS0的基地址设置为0XE8000000呢?

Powerpc的开发避免不了CodeWarrior,在CodeWarrior安装路径下搜索.tcl文件,该文件用于初始化CPU,地址配置,内存初始化等,其中也包括eLBC,这个只是在烧写的时候用

以P2041为例,

猜想,如果这里改为0XE0000000,再将uboot里CS0的基地址改为0XE0000000,或许也是一样的,但目前没有依据,只是猜测。现在有两个地方告诉我们CS0的基地址是0XE8000000,但没有解释为什么是这个值。这里顺便先将启动相关文件的一些存放地址说明一下,

RCW:放在flash的前128K的位置,

UBOOT:放在flash的最后768K,

UBOOT ENV:紧挨着UBOOT,放在UBOOT后的128K,

FMAN:紧挨着UBOOT ENV,128K

以上,偏移位置都是固定不可变的,上边的基地址为什么是0XE800000不得而知,关于uboot的地址,和p2040上电启动第一条执行的指令地址有关系,芯片手册有说明,如下,

p2040上电第一条执行的指令,放在0X0FFFFFFFC这个地址,那么这个地址放的什么指令呢?打开arch/powerpc/cpu/mpc85xx/ u-boot.lds,如下:

/*

 * Copyright 2007-2009, 2011 Freescale Semiconductor, Inc.

 *

 * SPDX-License-Identifier:      GPL-2.0+

 */

 

#include "config.h"

 

#ifdef CONFIG_RESET_VECTOR_ADDRESS

#define RESET_VECTOR_ADDRESS CONFIG_RESET_VECTOR_ADDRESS

#else

#define RESET_VECTOR_ADDRESS 0xfffffffc

#endif

 

#ifndef CONFIG_SYS_MONITOR_LEN

#define CONFIG_SYS_MONITOR_LEN     0x80000

#endif

 

OUTPUT_ARCH(powerpc)

ENTRY(_start_e500)

 

PHDRS

{

  text PT_LOAD;

  bss PT_LOAD;

}

resetvec.S:

         .section .resetvec,"ax"

         b _start_e500

这里有一条跳转指令,uboot的开始。

回过神来继续查找为什么是0XE8000000这个基地址,然而找了整个芯片手册也没找到任何线索,从芯片手册看来,并没有任何地方指定CS0的基地址必须得是0XE8000000。

结论:

FLASH的基地址为什么是0XE0000000,因为eLBC的地址范围是从0XE0000000开始的256M地址空间。

CS0为什么要加0x8000000的偏移?未知

。。。。。。。。。。。

等等,再看一下p2040的启动,上电第一条指令地址放在哪儿?放在0X0_FFFF_FFFC,想一想,这第一条指令显然应该是uboot的第一条指令,uboot放在哪里,肯定是flash,flash挂载哪里?当然是eLBC,eLBC的地址范围是什么,如下,当然是0xf_e0000000 – 0xf_efffffff,对吧,flash多大容量?128M,那么128*1024*1024 换算成16进制是多少?当然是0X8000000,而显然0X0_FFFF_FFFC这个地址应该对应在eLBC的后半部,对吧,所以CS0 的基地址应该为0xefffffff – 0x8000000,所以正好是0XE8000000,至此,水落石出。(上述分析是CS0接的是NOR FLASH的情况下,仅供参考)

目前CS0的基地址确定好了,其他片选的基地址呢?

片选地址:

CS0 :0XE8000000(NOR FLASH)或者 0XFA000000(NAND FLASH)

CS1:0XE0000000(可用,已验证) 或者 0XF8200000(NAND FLASH,未验证)

CS2:0X?????????

CS3:0XFFDF0000(可用,已验证)

看来片选基地址应该看具体怎么使用而定,如下图,

在描述.tcl文件时,文件里的CS1的基地址如下,基地址为0XF8200000,这个地址又从何而来呢?

接下来看看内核里的eLBC干了什么,内核里的eLBC代码在arch/powerpc/sysdev/fsl_lbc.c里,看一下代码:

首先是注册

static int __init fsl_lbc_init(void)

{

#ifdef CONFIG_SUSPEND

         register_syscore_ops(&lbc_syscore_pm_ops);

#endif

         return platform_driver_register(&fsl_lbc_ctrl_driver);

}

static struct platform_driver fsl_lbc_ctrl_driver = {

         .driver = {

                   .name = "fsl-lbc",

                   .of_match_table = fsl_lbc_match,

         },

         .probe = fsl_lbc_ctrl_probe,

};

然后看一下probe干了什么

static int fsl_lbc_ctrl_probe(struct platform_device *dev)

{

         int ret;

 

         if (!dev->dev.of_node) {

                   dev_err(&dev->dev, "Device OF-Node is NULL");

                   return -EFAULT;

         }

 

         fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);

         if (!fsl_lbc_ctrl_dev)

                   return -ENOMEM;

 

         dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);

 

         spin_lock_init(&fsl_lbc_ctrl_dev->lock);

         init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);

         /*获取设备树里的资源*/

         fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);

         if (!fsl_lbc_ctrl_dev->regs) {

                   dev_err(&dev->dev, "failed to get memory region\n");

                   ret = -ENODEV;

                   goto err;

         }

         /*获取中断*/

         fsl_lbc_ctrl_dev->irq[0] = irq_of_parse_and_map(dev->dev.of_node, 0);

         if (!fsl_lbc_ctrl_dev->irq[0]) {

                   dev_err(&dev->dev, "failed to get irq resource\n");

                   ret = -ENODEV;

                   goto err;

         }

 

         fsl_lbc_ctrl_dev->dev = &dev->dev;

         /*lbc的初始化*/

         ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node);

         if (ret < 0)

                   goto err;

 

         ret = request_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_irq, 0,

                                     "fsl-lbc", fsl_lbc_ctrl_dev);

         if (ret != 0) {

                   dev_err(&dev->dev, "failed to install irq (%d)\n",

                            fsl_lbc_ctrl_dev->irq[0]);

                   ret = fsl_lbc_ctrl_dev->irq[0];

                   goto err;

         }

 

         fsl_lbc_ctrl_dev->irq[1] = irq_of_parse_and_map(dev->dev.of_node, 1);

         if (fsl_lbc_ctrl_dev->irq[1]) {

                   ret = request_irq(fsl_lbc_ctrl_dev->irq[1], fsl_lbc_ctrl_irq,

                                     IRQF_SHARED, "fsl-lbc-err", fsl_lbc_ctrl_dev);

                   if (ret) {

                            dev_err(&dev->dev, "failed to install irq (%d)\n",

                                               fsl_lbc_ctrl_dev->irq[1]);

                            ret = fsl_lbc_ctrl_dev->irq[1];

                            goto err1;

                   }

         }

 

         /* Enable interrupts for any detected events */

         out_be32(&fsl_lbc_ctrl_dev->regs->lteir, LTEIR_ENABLE);

 

         return 0;

 

err1:

         free_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_dev);

err:

         iounmap(fsl_lbc_ctrl_dev->regs);

         kfree(fsl_lbc_ctrl_dev);

         fsl_lbc_ctrl_dev = NULL;

         return ret;

}

没干太多事情,上边主要的就是fsl_lbc_ctrl_dev,看一下这个是个什么东西,代码里的定义如下:

struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;

EXPORT_SYMBOL(fsl_lbc_ctrl_dev);

这里EXPORT出来了,其他地方可以使用,也就是说你的驱动里边可以获取到这个变量,去设置你想改变的BR 和 OR,后边分析一个例子,看一下fsl_lbc_ctrl这个结构体里有什么东西

结构体定义如下:

struct fsl_lbc_ctrl {

         /* device info */

         struct device                       *dev;

         /*重要的成员,指向eLBC控制器基地址的指针*/

         struct fsl_lbc_regs __iomem   *regs;

         int                                irq[2];

         wait_queue_head_t                  irq_wait;

         spinlock_t                            lock;

         void                              *nand;

 

         /* status read from LTESR by irq handler */

         unsigned int                       irq_status;

 

#ifdef CONFIG_SUSPEND

         /* save regs when system go to deep-sleep */

         struct fsl_lbc_regs             *saved_regs;

#endif

};

在内核里找到了两个例子,分别是drivers/uio/uio_fsl_elbc_gpcm.c和

drivers/mtd/nand/fsl_elbc_nand.c,下边看一下它们的probe函数里都干了什么,先看uio_fsl_elbc_gpcm.c

static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)

{

         struct device_node *node = pdev->dev.of_node;

         struct fsl_elbc_gpcm *priv;

         struct uio_info *info;

         char *uio_name = NULL;

         struct resource res;

         unsigned int irq;

         u32 reg_br_cur;

         u32 reg_or_cur;

         u32 reg_br_new;

         u32 reg_or_new;

         int ret;

         这里就直接使用了之前fsl_lbc.c里EXPORT出来的变量

         if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)

                   return -ENODEV;

 

         /* allocate private data */

         priv = kzalloc(sizeof(*priv), GFP_KERNEL);

         if (!priv)

                   return -ENOMEM;

         priv->dev = &pdev->dev;

         priv->lbc = fsl_lbc_ctrl_dev->regs;

 

         /* get device tree data */

         ret = get_of_data(priv, node, &res, &reg_br_new, &reg_or_new,

                              &irq, &uio_name);

         if (ret)

                   goto out_err0;

 

         /* allocate UIO structure */

         info = kzalloc(sizeof(*info), GFP_KERNEL);

         if (!info) {

                   ret = -ENOMEM;

                   goto out_err0;

         }

         下边的代码就是获取BR,OR,然后根据需要设置新的值

         /* get current BR/OR values */

         reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br);

         reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or);

 

         /* if bank already configured, make sure it matches */

         if ((reg_br_cur & BR_V)) {

                   if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||

                       (reg_br_cur & reg_or_cur & BR_BA)

                        != fsl_lbc_addr(res.start)) {

                            dev_err(priv->dev,

                                     "bank in use by another peripheral\n");

                            ret = -ENODEV;

                            goto out_err1;

                   }

 

                   /* warn if behavior settings changing */

                   if ((reg_br_cur & ~(BR_BA | BR_V)) !=

                       (reg_br_new & ~(BR_BA | BR_V))) {

                            dev_warn(priv->dev,

                                      "modifying BR settings: 0x%08x -> 0x%08x",

                                      reg_br_cur, reg_br_new);

                   }

                   if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) {

                            dev_warn(priv->dev,

                                      "modifying OR settings: 0x%08x -> 0x%08x",

                                      reg_or_cur, reg_or_new);

                   }

         }

 

         /* configure the bank (force base address and GPCM) */

         reg_br_new &= ~(BR_BA | BR_MSEL);

         reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V;

         out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new);

         out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new);

 

         /* map the memory resource */

         info->mem[0].internal_addr = ioremap(res.start, resource_size(&res));

         if (!info->mem[0].internal_addr) {

                   dev_err(priv->dev, "failed to map chip region\n");

                   ret = -ENODEV;

                   goto out_err1;

         }

 

         /* set all UIO data */

         if (node->name)

                   info->mem[0].name = kstrdup(node->name, GFP_KERNEL);

         info->mem[0].addr = res.start;

         info->mem[0].size = resource_size(&res);

         info->mem[0].memtype = UIO_MEM_PHYS;

         info->priv = priv;

         info->name = uio_name;

         info->version = "0.0.1";

         if (irq != NO_IRQ) {

                   if (priv->irq_handler) {

                            info->irq = irq;

                            info->irq_flags = IRQF_SHARED;

                            info->handler = priv->irq_handler;

                   } else {

                            irq = NO_IRQ;

                            dev_warn(priv->dev, "ignoring irq, no handler\n");

                   }

         }

 

         if (priv->init)

                   priv->init(info);

 

         /* register UIO device */

         if (uio_register_device(priv->dev, info) != 0) {

                   dev_err(priv->dev, "UIO registration failed\n");

                   ret = -ENODEV;

                   goto out_err2;

         }

 

         /* store private data */

         platform_set_drvdata(pdev, info);

 

         /* create sysfs files */

         ret = device_create_file(priv->dev, &dev_attr_reg_br);

         if (ret)

                   goto out_err3;

         ret = device_create_file(priv->dev, &dev_attr_reg_or);

         if (ret)

                   goto out_err4;

 

         dev_info(priv->dev,

                    "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",

                    priv->name, (unsigned long long)res.start, priv->bank,

                    irq != NO_IRQ ? irq : -1);

 

         return 0;

out_err4:

         device_remove_file(priv->dev, &dev_attr_reg_br);

out_err3:

         platform_set_drvdata(pdev, NULL);

         uio_unregister_device(info);

out_err2:

         if (priv->shutdown)

                   priv->shutdown(info, true);

         iounmap(info->mem[0].internal_addr);

out_err1:

         kfree(info->mem[0].name);

         kfree(info);

out_err0:

         kfree(uio_name);

         kfree(priv);

         return ret;

}

上面的代码主要就是获取自己BR,OR的值,然后根据需要设置新的值进去,实际上eLBC在fsl_lbc.c初始化过后,其他的驱动仅需要设置BR,OR就可以了,下面看fsl_elbc_nand.c的probe,

static int fsl_elbc_nand_probe(struct platform_device *pdev)

{

         struct fsl_lbc_regs __iomem *lbc;

         struct fsl_elbc_mtd *priv;

         struct resource res;

         struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;

         static const char *part_probe_types[]

                   = { "cmdlinepart", "RedBoot", "ofpart", NULL };

         int ret;

         int bank;

         struct device *dev;

         struct device_node *node = pdev->dev.of_node;

         struct mtd_part_parser_data ppdata;

 

         ppdata.of_node = pdev->dev.of_node;

         if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)

                   return -ENODEV;

         获取eLBC控制器的基地址指针

         lbc = fsl_lbc_ctrl_dev->regs;

         dev = fsl_lbc_ctrl_dev->dev;

 

         /* get, allocate and map the memory resource */

         ret = of_address_to_resource(node, 0, &res);

         if (ret) {

                   dev_err(dev, "failed to get resource\n");

                   return ret;

         }

         下边这段代码主要是比较每个片选的BR的BA,也就是基地址,是否是自己的基地址,以及是不是FCM,实际上也就是判断已经初始化的BR和OR里有没有与自己匹配的,如果没有,说明没设置对。

         /* find which chip select it is connected to */

         for (bank = 0; bank < MAX_BANKS; bank++)

                   if ((in_be32(&lbc->bank[bank].br) & BR_V) &&

                       (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&

                       (in_be32(&lbc->bank[bank].br) &

                        in_be32(&lbc->bank[bank].or) & BR_BA)

                        == fsl_lbc_addr(res.start))

                            break;

 

         if (bank >= MAX_BANKS) {

                   dev_err(dev, "address did not match any chip selects\n");

                   return -ENODEV;

         }

 

         priv = kzalloc(sizeof(*priv), GFP_KERNEL);

         if (!priv)

                   return -ENOMEM;

 

         mutex_lock(&fsl_elbc_nand_mutex);

         if (!fsl_lbc_ctrl_dev->nand) {

                   elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);

                   if (!elbc_fcm_ctrl) {

                            mutex_unlock(&fsl_elbc_nand_mutex);

                            ret = -ENOMEM;

                            goto err;

                   }

                   elbc_fcm_ctrl->counter++;

 

                   spin_lock_init(&elbc_fcm_ctrl->controller.lock);

                   init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);

                   fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;

         } else {

                   elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;

         }

         mutex_unlock(&fsl_elbc_nand_mutex);

 

         elbc_fcm_ctrl->chips[bank] = priv;

         priv->bank = bank;

         priv->ctrl = fsl_lbc_ctrl_dev;

         priv->dev = &pdev->dev;

         dev_set_drvdata(priv->dev, priv);

 

         priv->vbase = ioremap(res.start, resource_size(&res));

         if (!priv->vbase) {

                   dev_err(dev, "failed to map chip region\n");

                   ret = -ENOMEM;

                   goto err;

         }

 

         priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);

         if (!priv->mtd.name) {

                   ret = -ENOMEM;

                   goto err;

         }

 

         ret = fsl_elbc_chip_init(priv);

         if (ret)

                   goto err;

 

         ret = nand_scan_ident(&priv->mtd, 1, NULL);

         if (ret)

                   goto err;

 

         ret = fsl_elbc_chip_init_tail(&priv->mtd);

         if (ret)

                   goto err;

 

         ret = nand_scan_tail(&priv->mtd);

         if (ret)

                   goto err;

 

         /* First look for RedBoot table or partitions on the command

          * line, these take precedence over device tree information */

         mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata,

                                       NULL, 0);

 

         printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",

                (unsigned long long)res.start, priv->bank);

         return 0;

 

err:

         fsl_elbc_chip_remove(priv);

         return ret;

}

内核代码到此就over了

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页