Chinaunix首页 | 论坛 | 博客
  • 博客访问: 120336
  • 博文数量: 61
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 230
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-26 11:35
个人简介

实践Linux的理论

文章存档

2015年(1)

2014年(60)

我的朋友

分类:

2014-05-01 20:09:38

针对2.6.13nand注册进platform的跟踪式情景分析



主要分析:

1.struct platform_device s3c_device_nand struct device_driver s3c2440_nand_driver的对应,是靠名字对应的

2. 这里主要分析的是 s3c_device_nand 的这个platform_device的初始化和注册过程,这个过程不在驱动里面,具体的实现过程是在板级支持里面实现了

3. kernel 2.6.13 struct platform_device -------------------- struct device_driver

driver_register(struct device_driver * drv) --------------------driver_unregister(struct device_driver * drv)

paltform_device_register() -------------------- platform_device_unregister()

kernel 2.6.29.6 struct platform_device -------------------- struct platform_driver

platform_driver_register() -------------------- platform_driver_unregister()

platform_device_register() -------------------- platform_deivce_unregister()

4. 针对2.6.13platform_device_register()的源码的分析,在2.6.29.6中已经大变样了

5. 对分区的支持,如何体现分区的,分区如何被注册进内核的

drivers/mtd/nand/s3c2410.c

kernel 2.6.13


1.struct platform_device s3c_device_nand struct device_driver s3c2440_nand_driver的对应,是靠名字对应的


702 static struct device_driver s3c2440_nand_driver = {nand的驱动的结构

703 .name = "s3c2440-nand",

704 .bus = &platform_bus_type,

705 .probe = s3c2440_nand_probe,

706 .remove = s3c2410_nand_remove,

707 };

709 static int __init s3c2410_nand_init(void)

710 {

711 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

712

713 driver_register(&s3c2440_nand_driver);

714 return driver_register(&s3c2410_nand_driver);

715 }

--------------------------------------------------------------------------------------

arch/arm/mach-s3c2410/devs.c 基本上定义了所有的板级支持设备结构

140 /* NAND Controller */

141

142 static struct resource s3c_nand_resource[] = {

143 [0] = {

144 .start = S3C2410_PA_NAND, //0x4E000000

145 .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND, //0x4E000000 + 0x00100000(1MB)

146 .flags = IORESOURCE_MEM, //memory source

147 }

148 };

149

150 struct platform_device s3c_device_nand = {nand的设备是在这里定义的

151 .name = "s3c2410-nand",

152 .id = -1,

153 .num_resources = ARRAY_SIZE(s3c_nand_resource),

154 .resource = s3c_nand_resource,

155 };

156

157 EXPORT_SYMBOL(s3c_device_nand);



arch/arm/mach-s3c2410/s3c2440.c

192 void __init s3c2440_map_io(struct map_desc *mach_desc, int size)

193 {

194 /* register our io-tables */

195

196 iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc));

197 iotable_init(mach_desc, size);

198

199 /* rename any peripherals used differing from the s3c2410 */

200

201 s3c_device_i2c.name = "s3c2440-i2c";设备在这里针对s3c2440重新命名,对应驱动s3c2440_nand_driver

202 s3c_device_nand.name = "s3c2440-nand";

203

204 /* change irq for watchdog */

205

206 s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;

207 s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT;

208 }



2 . 这里主要分析的是 s3c_device_nand 的这个platform_device的初始化和注册过程,这个过程不在驱动里面,具体的实现过程是在板级支持里面实现了


倒着追溯的

arch/arm/mach-s3c2410/cpu.c nand设备最终在这个文件中被注册

208 static int __init s3c_arch_init(void)

209 {

210 int ret;

211

212 // do the correct init for cpu

213

214 if (cpu == NULL)

215 panic("s3c_arch_init: NULL cpu\n");

216

217 ret = (cpu->init)();

218 if (ret != 0)

219 return ret;

220

221 if (board != NULL) {

222 struct platform_device **ptr = board->devices;这里的boardstruct s3c24xx_board,在本文件cpu.c中,是全局变量,他的初始化是在

223 int i; 本文件中的s3c24xx_set_board()函数中完成的,

224

225 for (i = 0; i < board->devices_count; i++, ptr++) {

226 ret = platform_device_register(*ptr);就是在这里完成对各种设备(包括上面提到的s3c_device_nand)的注册的,

227 这是板级的支持,在板级的初始化中完成的

228 if (ret) {

229 printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)- >name, ret, *ptr);

230 }

231 }

232

233 /* mask any error, we may not need all these board

234 * devices */

235 ret = 0;

236 }

237

238 return ret;

239 }

240

241 arch_initcall(s3c_arch_init);



上面用到的board来自这里

arch/arm/mach-s3c2410/cpu.h

52 struct s3c24xx_board {

53 struct platform_device **devices;

54 unsigned int devices_count;

55

56 struct clk **clocks;

57 unsigned int clocks_count;

58 };


arch/arm/mach-s3c2410/cpu.c

127 /* board information */

128

129 static struct s3c24xx_board *board;在文件cup.c中是全局变量,在s3c24xx_set_board()被初始化,在

130 s3c_arch_init()中被注册

131 void s3c24xx_set_board(struct s3c24xx_board *b)

132 {

133 int i;

134

135 board = b; 在这里定义成了全局变量,所以才在s3c_arch_init()中可用

136 这里之所以能对struct s3c24xx_board * board初始化,是因为在文件

137 if (b->clocks_count != 0) { arch/arm/mach-s3c2410/mach-bit2440.c +438 中的 void __init bit2440_map_io(void)

138 struct clk **ptr = b->clocks;; s3c24xx_set_board()的调用,,这个b就是从那里传递过去的,具体见下面

139

140 for (i = b->clocks_count; i > 0; i--, ptr++)

141 s3c24xx_register_clock(*ptr);

142 }

143 }

arch/arm/mach-s3c2410/mach-bit2440.c

389 static struct platform_device *bit2440_devices[] __initdata = {

在这里加载必要的设备,这些设备会在启动时被加载

390 &s3c_device_usb,

391 &s3c_device_lcd,

392 &s3c_device_wdt,

393 &s3c_device_i2c,

394 &s3c_device_iis,

395 &s3c_device_sdi,

396 &s3c_device_usbgadget,

397 &s3c_device_ts,

398 &s3c_device_nand,

399 &s3c_device_sound,

400 &s3c_device_buttons,

401 &s3c_device_rtc,

402 };

404 static struct s3c24xx_board bit2440_board _ _initdata = { 将所有的设备,加入到板级支持里面

405 .devices = bit2440_devices,

406 .devices_count = ARRAY_SIZE(bit2440_devices)

407 };

408

409 void __init bit2440_map_io(void)

410 {

411 u32 val;//yu

412

413 s3c24xx_init_io(bit2440_iodesc, ARRAY_SIZE(bit2440_iodesc));

414

415 #ifdef CONFIG_S3C2440_INCLK12

416 s3c24xx_init_clocks(12000000);

417 #else

418 s3c24xx_init_clocks(16934400);

419 #endif

420

421 //Add by yu

422 val = readl(S3C2410_GPHCON);

423 //printk("old gphcon = %x\n\n\n", val);

424 val &= ~(0xfff << 4);

425 val |= (0xaaa << 4);

426 writel(val, S3C2410_GPHCON);//inialize RxD2 and TxD2

427

428 val = readl(S3C2410_GPHUP);

429 val |= 0x3f << 2;

430 writel(val, S3C2410_GPHUP);

431 val = readl(S3C2410_GPHUP);

432 //printk("new gphup = %x\n", val);

433 ///////////////////////////

434

435 s3c24xx_init_uarts(bit2440_uartcfgs, ARRAY_SIZE(bit2440_uartcfgs));

436 s3c24xx_set_board(&bit2440_board);bit2440_board就是在这里被初始化,然后被s3c24xx_set_board()传到cpu.c 中,进而被

437 s3c_arch_init()完成注册

438 s3c_device_nand.dev.platform_data = &bit_nand_info;

439 }



bit2440_map_io()是在这里最终别调用的

476 MACHINE_START(BIT2440, "QT-2440")

477 .phys_ram = S3C2410_SDRAM_PA,

478 .phys_io = S3C2410_PA_UART,

479 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

480 .boot_params = S3C2410_SDRAM_PA + 0x100,

481

482 .init_irq = bit2440_init_irq,

483 .map_io = bit2440_map_io,

484 .init_machine = bit2440_init,

485 .timer = &s3c24xx_timer,

486 MACHINE_END

通过这个宏最终实现了建立板级的结构struct machine_desc



再往下说,就是linux启动的过程的分析了

内核启动时将运行head.S,再其中将调用.../init/main.c中的start_kernel的函数

asmlinkage void __init start_kernel(void)
{
......
setup_arch(&command_line);
// 1.找到所属的machine_desc,即在arch/arm/mach-s3c2410/qt2410.c中最后定义的一个结构。
// 2.paging_init(&meminfo, mdesc)->devicemaps_init(mdesc)->mdesc->map_io();
// 3.找到该machine_desc定义的初始化函数句柄:
init_arch_irq = mdesc->init_irq;
        system_timer = mdesc->timer;
        init_machine = mdesc->init_machine;
......



==============================================================================

4. 针对2.6.13platform_device_register()的源码的分析,在2.6.29.6中已经大变样了

116 /**

117 * platform_device_register - add a platform-level device

118 * @pdev: platform device we're adding

119 *

120 */

121 int platform_device_register(struct platform_device * pdev)


122 {

123 int i, ret = 0;

124

125 if (!pdev)

126 return -EINVAL;

127 //关联父设备,如果没有父设备,那么就设platform_bus为其父设备

128 if (!pdev->dev.parent)

129 pdev->dev.parent = &platform_bus;

130

131 pdev->dev.bus = &platform_bus_type;

132 //id==-1,只是名字的不同

133 if (pdev->id != -1)

134 snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);

135 else

136 strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

137 //请求分配一些资源

138 for (i = 0; i < pdev->num_resources; i++) {

139 struct resource *p, *r = &pdev->resource[i];

140

141 if (r->name == NULL)

142 r->name = pdev->dev.bus_id;

143

144 p = r->parent;

145 if (!p) {

146 if (r->flags & IORESOURCE_MEM)

147 p = &iomem_resource;

148 else if (r->flags & IORESOURCE_IO)

149 p = &ioport_resource;

150 }

151

152 if (p && request_resource(p, r)) {

153 printk(KERN_ERR

154 "%s: failed to claim resource %d\n",

155 pdev->dev.bus_id, i);

156 ret = -EBUSY;

157 goto failed;

158 }

159 }

160

161 pr_debug("Registering platform device '%s'. Parent at %s\n",

162 pdev->dev.bus_id, pdev->dev.parent->bus_id);

163 //将设备注册进bus中,可以在sys/bus/platform中看到

164 ret = device_register(&pdev->dev);

165 if (ret == 0)

166 return ret;

167

168 failed:

169 while (--i >= 0)

170 if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))

171 release_resource(&pdev->resource[i]);

172 return ret;

173 }















5. 对分区的支持,如何体现分区的,分区设备信息如何被注册进内核的,但没有被注册分区

注册分区的完成是驱动的事情,也就是这个过程虽然完成了分区信息的填充,但在系统中体

现不出来,只能是在驱动调用add_mtd_partions之后才可以看到分区


这里的分区是如何被s3c24xx_nand_probe()中的功能给加载上去的


Nand Flash 设备添加时数据结构包含关系


struct mtd_partition bit_default_nand_part[]

--> struct s3c2410_nand_set bit_nand_set[]

--> struct s3c2410_platform_nand bit_nand_info

--> struct platform_device s3c_device_nand

在该数据结构的 name 字段的初始化值"s3c2410-nand",必须

Nand Flash 设备驱动注册时struct device_driver结构中的

name 字段相同,因为 platfrom bus 是依靠名字来匹配的.

--> struct platform_device *bit_2410_devices[]




arch/arm/mach-s3c2410/mach-bit2440.c 在这里分区被初始化

278 static int chip0_map[] = { 0 }; //add by lili

279 struct mtd_partition bit_default_nand_part[] = { struct mtd_partitio bit_default_nand_part[]

280 [0] = {

281 .name = "bootloader",

282 .offset = 0x00000000,

283 .size = 0x00030000,

284 },

285 [1] = {

286 .name = "kernel",

287 .offset = 0x00030000,

288 .size = 0x00200000,

289 },

290 [2] = {

291 .name = "root",

292 .offset = 0x00230000,

293 .size = 0x03dcc000,

294 }

295 };

296

297 /* the bit has 1 selectable slots for nand-flash, the three

298 * on-board chip areas, as well as the external SmartMedia

299 * slot.

300 *

301 * Note, there is no current hot-plug support for the SmartMedia

302 * socket.

303 */

304

305 static struct s3c2410_nand_set bit_nand_sets[] = { struct s3c2410_nand_set bit_nand_sets[]

分区信息被包含在这个结构体中

306 [0] = {

307 .name = "chip0",

308 .nr_chips = 1,

309 .nr_map = chip0_map,

310 .nr_partitions = ARRAY_SIZE(bit_default_nand_part),

311 .partitions = bit_default_nand_part


312 },

313 };

314

315



316 static struct s3c2410_platform_nand bit_nand_info = { struct s3c2410_platform_nand bit_nand_info

最终被包含在bit_nand_info

317 .tacls = 0,

318 .twrph0 = 30,

319 .twrph1 = 0,

320 .nr_sets = ARRAY_SIZE(bit_nand_sets),

321 .sets = bit_nand_sets,

322 };

arch/arm/mach-s3c2410/devs.c

struct platform_device s3c_device_nand = { struct platform_device s3c_device_nand

.name = "s3c2410-nand"

.id = -1,

.num_resources = ARRAY_SIZE(s3c_nand_resource),

.resource = s3c_nand_resource,

};


也可以这样的

----------------------------------------------

struct platform_device s3c_device_nand = {

.name = "s3c2410-nand",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_nand_resource),

.resource = s3c_nand_resource,

.dev = {

.platform_data = &bit_nand_info

}

};

如果这样的话,那么在bit2440_map_io()中也就不用再此给dev.platform赋值了

-----------------------------------------------



bit_nand_info是在这个函数中被加入到s3c_device_nand 当中的

409 void __init bit2440_map_io(void)

410 {

…...

…...

435 s3c24xx_init_uarts(bit2440_uartcfgs, ARRAY_SIZE(bit2440_uartcfgs));

436 s3c24xx_set_board(&bit2440_board);

437

438 s3c_device_nand.dev.platform_data = &bit_nand_info;

439 } 就是这里,将s3c2410_platform_nand 结构(其中包括mtd_pat分区信息)

放在(void *)dev->platform_data中,而这个dev又包含在s3c_device_nand中,

这样分区信息就被包含在s3c_device_nand 设备中被加入到了板级支持中,

这些过程在板子启动过程中就完成了(驱动加载之前完成的)

加载驱动的时候,调用platform_driver_register(&s3c2440_nand_driver),此时

此时驱动s3c2440_nand_driver会在platform 总线上调用总线的match函数

,依据名字来查找和自己名字相同的设备,这里我们的驱动的名字是

“s3c2440-nand”, 由于我们在板级支持里已经将名字为“s3c2440-nand”的设

s3c_device_nand完成了注册,所以这里能找到。


一但驱动s3c2440_nand_probe找到和他名字向匹配的设备,会马上调用

s3c2440_nand_probe(struct device *dev)-->s3c24xx_nand_probe(sturct device

*dev ,int is_s3c2440),

这里注意probe函数的参数是struct device *dev ,这一点很好理解,如果驱

提前能知道他需要设备一定存在,还用platform总线的match函数匹配干

吗!!嘿嘿

还有一点,就是在设备注册的过程中,你一定还记得在 bit2440_map_io()

中有:s3c_device_nand.dev.platform_data = &bit_nand_info.到这里一切才终

于真想大白,你在probe函数里只需

struct platform_device *pdev = to_platform_device(dev);

//get platform_device(continer_of())

struct s3c2410_platform_nand *plat = to_nand_plat(dev);

//get dev->platform_data

这样你就可以很轻而易举的在驱动中享受设备中已经定义好的资源了



上面是针对2.6.13,版本有点来,到了2.6.29.6中,s3c24xx_nand_probe()的参

书就成了platform_device *dev, 当然这样更好,省的再像老版本中的那样需要

struct platform_device *pdev = to_platform_device(dev);

//get platform_device(continer_of())

才能得到platform_device 这个结构体,嘿嘿,只要明白大概的原理,基本上对新

内核也是不成问题的


----------------------------------------------------------------------------------------------------------------

接下来,s3c_device_nand 是这样被加入到板级支持中的

389 static struct platform_device *bit2440_devices[] __initdata = { struct platform_device *bit2440_devices[]

…....

…....

397 &s3c_device_ts,

398 &s3c_device_nand,

…....

402 };

403

404 static struct s3c24xx_board bit2440_board __initdata = {

405 .devices = bit2440_devices,

406 .devices_count = ARRAY_SIZE(bit2440_devices)

407 };


------------------------------------------------------------------------------------------------------------------

最终在这里通过这个宏,建立板级支持,

476 MACHINE_START(BIT2440, "QT-2440")

477 .phys_ram = S3C2410_SDRAM_PA,

478 .phys_io = S3C2410_PA_UART,

479 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

480 .boot_params = S3C2410_SDRAM_PA + 0x100,

481

482 .init_irq = bit2440_init_irq,

483 .map_io = bit2440_map_io,

484 .init_machine = bit2440_init,

485 .timer = &s3c24xx_timer,

486 MACHINE_END


一旦这样,那么nand的分区信息就体现在设备中了,最终nand驱动在匹配device

的时候,就能知道有几个nand,每个nand是怎样分区的了,最终在s3c24xx_nand_probe()

中,完成每个nand,每个分区的注册





platform_device与驱动的联系



首先你需要为SOC的各个功能部分定义他的一些资源.例如可用于访问的寄存器地址.中断号,DMA什么的。然后将这些资源(resource) 作为 platform dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:
static struct
platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_bl,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_sdi,
&s3c_device_adc,
&s3c_device_nand,
&s3c_device_usbgadget,
&s3c_device_ts,
&s3c_device_buttons,
&s3c_device_rtc,
&s3c_device_spi0,
&s3c_device_timer1,//add by cefanty for battery charging
};
这样你的硬件的信息和资源就会注册到系统中
.

说了半天,这回该说这有什么用了。
你编写的驱动或者移植别人的驱动,一般在驱动里有这样的代码
,例如:
static struct platform_driver s3c2410sdi_driver =
{
.probe          = s3c2410sdi_probe,
.remove         = s3c2410sdi_remove,
.suspend= s3c2410mci_suspend,
.resume= s3c2410mci_resume,
.driver={
.name= "s3c2410-sdi",
.bus    = &platform_bus_type,
.owner= THIS_MODULE,
},
};

看到 .name= "s3c2410-sdi",这条关键的语句没有??,它和我在上面注册的&s3c_device_sdi,里的device的名称是一致的.我这里展开我的s3c_device_sdi,的内容
:
/* SDI */

static struct resource s3c_sdi_resource[] = {
[0] = {
.start = S3C2410_PA_SDI,
.end   = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end   = IRQ_SDI,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 3,
.end  = 3,
.flags = IORESOURCE_DMA,
}
};

struct platform_device s3c_device_sdi = {
.name  = "s3c2410-sdi",
.id  = -1,
.num_resources  = ARRAY_SIZE(s3c_sdi_resource),
.resource  = s3c_sdi_resource,
};

在驱动程序里的init代码大致如下:
static int __init s3c2410sdi_init(void)
{
return platform_driver_register(&s3c2410sdi_driver);
}
platform_driver_register 向系统注册这个驱动程序.而这个函数会在s3c2410sdi_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个 platform_device。如果有注册,那么接着会执行platform_driver probe函数.在这里显然是s3c2410sdi_probe函数  在probe函数里,用的最多和刚才platform_device有关的语句是platform_get_resource,这条语句用于获取 platform_device里的resource资料.例如映射的IO地址,中断等.剩下等得就是ioremap,request_irq等的事情了.

阅读(578) | 评论(0) | 转发(0) |
0

上一篇:nand驱动常识

下一篇:Linux NAND Flash驱动

给主人留下些什么吧!~~