linux内核的水很深,分享一下关于__raw_writel()出错的问题

来源:互联网 发布:知乎回答数最多的问题 编辑:程序博客网 时间:2024/05/22 12:31
先上一个测试代码来说明一下:
#include <linux/kernel.h>#include <linux/module.h>#include <linux/gpio.h>#include <linux/io.h>#define at91_pwm_read(reg)                  __raw_readl(reg)   #define at91_pwm_write(reg, val)            __raw_writel((val), reg)#define PWMC_BASE 0xfffb8000#define PWM_MR_OFF              0x00000000    ///< PWM Mode Register offset.//#define PWM_MR     __phys_to_pfn(PWMC_BASE + PWM_MR_OFF) ///< PWM Mode Register.  //这里是关键#define PWM_MR     (PWMC_BASE + PWM_MR_OFF) ///< PWM Mode Register.static int __init my_led_init(void){unsigned long value;value = at91_pwm_read(PWM_MR);printk("0xfffb8000 = %x", value);at91_pwm_write(PWM_MR, 0xA2); value = at91_pwm_read(PWM_MR);printk("0xfffb8000 = %x", value);at91_set_gpio_output(AT91_PIN_PD15, 0);at91_set_gpio_value(AT91_PIN_PD15, 0);    return 0;}static void __exit my_led_exit(void){   //at91_set_gpio_output(AT91_PIN_PD15, 0);at91_set_gpio_value(AT91_PIN_PD15, 1);    return;}module_init(my_led_init)module_exit(my_led_exit)MODULE_LICENSE("GPL");


其实这就是一个简单的加载和卸载驱动的模块,因为我昨天做的pwm的驱动下载到开发板的时候总是报错,在代码里面找了很久都没有找到原因,

今天早上我把模块单独拿出来试试,最后确定是__raw_writel和__raw_readl的原因, 开始以为是我定义的地址出错了.后来调试了很久还是没有找到.上一个出错的信息;

<1>Unable to handle kernel paging request at virtual address fffb8000pgd = c6c14000[fffb8000] *pgd=705a5031, *pte=00000000, *ppte=00000000Internal error: Oops: 17 [#1]Modules linked in: myled(+) [last unloaded: myled]CPU: 0    Not tainted  (2.6.30 #28)PC is at my_led_init+0x10/0x58 [myled]LR is at do_one_initcall+0x4c/0x17cpc : [<bf00f010>]    lr : [<c01222d4>]    psr: 60000013sp : c6c2df08  ip : 00000000  fp : bec68c94r10: 00000000  r9 : c6c2c000  r8 : c0483140r7 : bf00f000  r6 : 001e5060  r5 : bf00c01c  r4 : fffb8fffr3 : 00000000  r2 : c6c2c000  r1 : 00000001  r0 : bf00f000Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment userControl: 0005317f  Table: 76c14000  DAC: 00000015Process insmod (pid: 450, stack limit = 0xc6c2c268)Stack: (0xc6c2df08 to 0xc6c2e000)df00:                   00000000 00000ae4 bf00c320 c01222d4 c888e2ce c79cf420 df20: c888ea10 00000016 00000018 c888e3d0 bf00c32c c888e768 c6c14000 00000000 df40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 df60: 00000ae4 bf00c320 001e5060 00000ae4 bf00c320 001e5060 00000000 c0122ea8 df80: 00000000 c0157f24 c7815740 ffffffff 001e44c0 00000ae4 bec68db1 000002ec dfa0: 00000080 c0122d00 00000ae4 bec68db1 001e5060 00000ae4 001e5050 bec68db1 dfc0: 00000ae4 bec68db1 000002ec 00000080 00000000 00000002 00000000 bec68c94 dfe0: bec68ab8 bec68aa8 0002cdbc 00009300 20000010 001e5060 00000000 00000000 [<bf00f010>] (my_led_init+0x10/0x58 [myled]) from [<c01222d4>] (do_one_initcall+0x4c/0x17c)[<c01222d4>] (do_one_initcall+0x4c/0x17c) from [<c0157f24>] (sys_init_module+0x90/0x194)[<c0157f24>] (sys_init_module+0x90/0x194) from [<c0122d00>] (ret_fast_syscall+0x0/0x2c)Code: e92d4030 e59f5048 e3e04a47 e24dd004 (e5141fff) ---[ end trace 55d98dffd0e5be81 ]---Segmentation fault

要感谢Crack-Punk
http://blog.chinaunix.net/uid-27039867-id-3236478.html
前人植树,后人乘凉.把贵先生的内容转载到空间里面.

移植路上真是荆棘满路,时不时让你疯狂一两天,却无可奈何...        网上已有很多相关的移植教程,第一步,当然是照着做。问题是我的Linux是2.6.19.2,与前些版本相比(据查,2.6.17版还在),在移植过程中,我发现有两个变化。一是 include/asm-arm/irq.h 里已删除一些irq操作的预定义,如:extern void disable_irq(unsigned int);extern void enable_irq(unsigned int);int set_irq_type(unsigned int irq, unsigned int type);而网上的那个cs8900.c驱动比较老,与Linux-2.6.19.2带的cs89x0相差很大(据说这个驱动也没有使用新的驱动模型,不知道,这里应该与dm9000比较一下)。导致出现很多错误,不能编译 cs8900.c ,一天就这样在郁闷中过去...      旧的不去,新的不来,干脆就直接用cs89x0.c ^_^ google到一个 pathch-cs89x0 ,哪个开心啊...可惜这个patch是针对2.6.14,自动patch失败,手动补齐后再添一些头文件、修改一些小错误,终于编译通过。可是,狂跳几下后又得郁闷了,出现了内核编程常见错误oops:cs89x0:cs89x0_probe(0x0)Unable to handle kernel paging request at virtual address f400030apgd = c0004000[f400030a] *pgd=00000000Internal error: Oops: 5 [#1]Modules linked in:CPU: 0PC is at readword+0x1c/0x2cLR is at cs89x0_probe1+0xec/0x874pc : []    lr : []    Not taintedsp : c032feb0  ip : c032fec0  fp : c032febcr10: f4000300  r9 : 00000000  r8 : 00000000r7 : c0f3b800  r6 : c0f3ba60  r5 : c0f3b800  r4 : f4000300r3 : f400030a  r2 : 00000000  r1 : f400030a  r0 : f4000300Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment kernelControl: 717FTable: 30004000  DAC: 00000017Process swapper (pid: 1, stack limit = 0xc032e250)Stack: (0xc032feb0 to 0xc0330000)fea0:                                     c032ff4c c032fec0 c001bec8 c0139fa8fec0: ffffffff c0f3b803 0000000a c032ff4c ffffffff 00000002 c0f3b800 fffffffffee0: c032ff30 c032fef0 c00fa694 c00f9e88 0000000a ffffffff ffffffff 00000002ff00: 3f0c47ff 00000000 c0f3b800 00000000 00000000 00000000 00000000 c0021290ff20: c032ff3c c002619c c0f3b800 00000000 00000000 00000000 00000000 c0021290ff40: c032ff6c c032ff50 c001c6e0 c001bdec c0026164 00000001 00000000 c00217a4ff60: c032ff88 c032ff70 c001bb04 c001c660 00000001 00000000 c032e000 c032ffa0ff80: c032ff8c c001bbd0 c001bad0 00000000 c0021208 c032fff4 c032ffa4 c0027100ffa0: c001bb48 00000001 c0027e24 c003caf4 00000000 00000000 c0027058 c0043128ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000ffe0: 00000000 00000000 00000000 c032fff8 c0043128 c0027068 0a00003e e5dc3001Backtrace:[] (readword+0x0/0x2c) from [] (cs89x0_probe1+0xec/0x874)[] (cs89x0_probe1+0x0/0x874) from [] (cs89x0_probe+0x90/0x1)[] (cs89x0_probe+0x0/0x108) from [] (probe_list2+0x44/0x78) r7 = C00217A4  r6 = 00000000  r5 = 00000001  r4 = C0026164[] (probe_list2+0x0/0x78) from [] (net_olddevs_init+0x98/0x) r6 = C032E000  r5 = 00000000  r4 = 00000001[] (net_olddevs_init+0x0/0xdc) from [] (init+0xa8/0x274) r5 = C0021208  r4 = 00000000[] (init+0x0/0x274) from [] (do_exit+0x0/0x7d8)Code: e0801001 e3510201 e1a03001 328334f2 (e1d300b0) <0>Kernel panic - not syncing: Attempted to kill init!      前两天才在《Linux内核设计与实现》里看到的常见错误,想不到这么快就被我遇上...这里就是新版Linux第二个变化引起的错误。尝试更改映射的虚地址,无效。就在准备回去冲凉的瞬间,google里又出现一个好东西 ARM Linux Kernel Porting ,有原理图,有cs8900的实地址为0x19000000的解析(大致就是A24脚的高低电平控制cs8900开关,故+1000000),可惜是韩文,里面的文字不知说什么,但错误现象一样,根据里面的代码再修改cs89x0一遍(这个文件真的很乱了^_^)。十分可惜,还是不能在我睡前干一件好事...      早上精神好啊,又经过一番搜索,终于发现问题,原来实地址与虚地址的映射结构 smdk2410_iodesc 里有所变化。第二个参数从原来的 unsigned long physical 变为 unsigned long pfn 。而从 smdk2410_iodesc 传入的SMDK2410_PA_CS8900A_BASE 仍然是一个 physical 值,因此出现 Unable to handle kernel paging request 错误。这里很感谢http://bibu.blogchina.com/bibu/4914641.html的作者,根据提示,将实地址作一个小小变化就Ok了 ^_^#ifndef __ASM_ARCH_SMDK2410_H#define __ASM_ARCH_SMDK2410_H#define SMDK2410_PA_CS8900A_BASE       __phys_to_pfn(S3C2410_CS3 + 0x01000000) /* nGCS3 +0x01000000 */#define SMDK2410_VA_CS8900A_BASE       S3C2410_ADDR(0x04000000)  /* 0xF4000000 */#endif /* __ASM_ARCH_SMDK2410_H */虚地址改多少无所谓,如0xE0000000,只要不和其它设备冲突就行。      下面总结一下整个移植过程:1. include/asm-arm/arch-s3c2410/map.h 添加: /* CS8900 */ #define S3C24XX_VA_CS8900   S3C2410_ADDR(0x04000000) #define S3C2410_PA_CS8900     __phys_to_pfn(0x19000000) #define S3C24XX_SZ_CS8900    SZ_1M #define S3C24XX_PA_CS8900    S3C2410_PA_CS89002. arch/arm/mach-s3c2410/mach-smdk2410.c 修改static struct map_desc smdk2410_iodesc[] __initdata={        {S3C24XX_VA_CS8900IO, S3C2410_PA_CS8900, S3C24XX_SZ_CS8900, S3C24XX_SZ_CS8900, MT_DEVICE}};3. include/asm-arm/irq.h 添加int set_irq_type(unsigned int irq, unsigned int type);4.  drivers/net/cs89x0.c下面是我的 diff--- ../linux2/linux-2.6.19.2/drivers/net/cs89x0.c 2007-01-11 03:10:37.000000000 +0800+++ drivers/net/cs89x0.c 2007-02-04 21:16:42.000000000 +0800@@ -96,6 +96,9 @@   Dmitry Pervushin  : dpervushin@ru.mvista.com                     : PNX010X platform support +  Christian Pell    : chripell@gmail.com+                    : SMDK2410 platform support, fixed bug with signed ioaddr+ */  /* Always include 'config.h' first in case the user wants to turn on@@ -194,6 +197,27 @@ #define CIRRUS_DEFAULT_IRQ VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */ static unsigned int netcard_portlist[] __initdata = {CIRRUS_DEFAULT_BASE, 0}; static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0};++#elif defined(CONFIG_ARCH_S3C2410) //Added weibing+#include <linux/irq.h>+#include <asm/irq.h>+#include <asm/arch/irqs.h>+#include <asm/arch/regs-mem.h>+#include <asm/arch/regs-gpio.h>+#include <asm/arch/smdk2410-map.h>++static unsigned int netcard_portlist[] __initdata = {S3C24XX_VA_CS8900+0x300, 0};+static unsigned int cs8900_irq_map[] = {IRQ_EINT9, 0, 0, 0};+/*+#ifdef request_region+#undef request_region+#endif+#ifdef release_region+#undef release_region+#endif+#define request_region(a,s,n) request_mem_region(a,s,n)+#define release_region(a,s) release_mem_region(a,s)+*/ #else static unsigned int netcard_portlist[] __initdata =    { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};@@ -246,7 +270,7 @@  /* Index to functions, as function prototypes. */ -static int cs89x0_probe1(struct net_device *dev, int ioaddr, int modular);+static int cs89x0_probe1(struct net_device *dev, unsigned int ioaddr, int modular); static int net_open(struct net_device *dev); static int net_send_packet(struct sk_buff *skb, struct net_device *dev); static irqreturn_t net_interrupt(int irq, void *dev_id);@@ -324,6 +348,10 @@  io = dev->base_addr;  irq = dev->irq; +#ifdef CONFIG_ARCH_S3C2410 //Added weibing+ __raw_writel((__raw_readl(S3C2410_GPGCON)&~(0x3<<2))|(0x2<<2),S3C2410_GPGCON);+ __raw_writel((__raw_readl(S3C2410_EXTINT1)&~(0x7<<4))|(0x4<<4),S3C2410_EXTINT1);+#endif  if (net_debug)   printk("cs89x0:cs89x0_probe(0x%x)\n", io); @@ -386,6 +414,18 @@ {  outw(value, base_addr + (portno << 1)); }+#elif defined(CONFIG_ARCH_S3C2410) //Added weibing+static u16+readword(unsigned long base_addr, int portno)+{+ return __raw_readw(base_addr+portno);+}++static void +writeword(unsigned long base_addr, int portno,u16 value) +{ + __raw_writew(value,base_addr+portno); +}  #else static u16 readword(unsigned long base_addr, int portno)@@ -507,7 +547,7 @@  */  static int __init-cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)+cs89x0_probe1(struct net_device *dev, unsigned int ioaddr, int modular) {  struct net_local *lp = netdev_priv(dev);  static unsigned version_printed;@@ -638,7 +678,18 @@     the driver will always do *something* instead of complain that     adapter_cnf is 0. */ -#ifdef CONFIG_SH_HICOSH4+#if defined CONFIG_ARCH_S3C2410 + lp->force=FORCE_RJ45; + lp->auto_neg_cnf=IMM_BIT; ++ dev->dev_addr[0]=0x09; /*setMACaddress*/ + dev->dev_addr[1]=0x90; + dev->dev_addr[2]=0x99; + dev->dev_addr[3]=0x09; + dev->dev_addr[4]=0x90; + dev->dev_addr[5]=0x99; ++#elif defined CONFIG_SH_HICOSH4  if (1) {   /* For the HiCO.SH4 board, things are different: we don't      have EEPROM, but there is some data in flash, so we go@@ -1036,7 +1087,7 @@ { #if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)  struct net_local *lp = netdev_priv(dev);- int ioaddr = dev->base_addr;+ unsigned int ioaddr = dev->base_addr; #endif  int reset_start_time; @@ -1278,7 +1329,7 @@  int i;  int ret; -#if !defined(CONFIG_SH_HICOSH4) && !defined(CONFIG_ARCH_PNX010X) /* uses irq#1, so this won't work */+#if !defined(CONFIG_SH_HICOSH4) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_ARCH_S3C2410) /* uses irq#1, so this won't work */  if (dev->irq < 2) {   /* Allow interrupts to be generated by the chip */ /* Cirrus' release had this: */@@ -1309,7 +1360,7 @@  else #endif  {-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X)+#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_ARCH_S3C2410)   if (((1 << dev->irq) & lp->irq_map) == 0) {    printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",                                dev->name, dev->irq, lp->irq_map);@@ -1324,6 +1375,9 @@   writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); #endif   write_irq(dev, lp->chip_type, dev->irq);+#if defined(CONFIG_ARCH_S3C2410)+  set_irq_type(dev->irq, IRQT_RISING);+#endif   ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev);   if (ret) {    if (net_debug)@@ -1394,7 +1448,7 @@  case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break;         default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);         }-#ifdef CONFIG_ARCH_PNX010X+#if defined(CONFIG_ARCH_PNX0105) || defined(CONFIG_ARCH_S3C2410)  result = A_CNF_10B_T; #endif         if (!result) {@@ -1577,7 +1631,8 @@ {  struct net_device *dev = dev_id;  struct net_local *lp;- int ioaddr, status;+ unsigned int ioaddr;+ int status;   int handled = 0;   ioaddr = dev->base_addr;@@ -1683,7 +1738,7 @@  struct sk_buff *skb;  int status, length; - int ioaddr = dev->base_addr;+ unsigned int ioaddr = dev->base_addr;  status = readword(ioaddr, RX_FRAME_PORT);  length = readword(ioaddr, RX_FRAME_PORT);5.重新编译,重新启动,重新happy...好了,现在可以挂NFS了,玩玩去......... ......现在又有一不像问题的问题,网卡工作正常,但启动是提示申请 io 注册失败,request_region(0xf4000300, 0x10) failed 。cs89x0:cs89x0_probe(0x0)cs89x0.c: v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton eth0: cs8900 rev K found at 0xf4000300cs89x0: Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command lecs89x0 media RJ-45, IRQ 53, programmed I/O, MAC 09:90:99:09:90:99cs89x0_probe1() successfulcs89x0:cs89x0_probe(0x0)cs89x0: request_region(0xf4000300, 0x10) failedcs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP更改虚地址也无效,追踪一下,在 kernel/resource.c 里出现问题。不知为什么资源冲突了,我看网上移植cs8900到2.4版也有这个问题...不理,先向下跳,回过头来在处理它...443                 for (;;) {444                         struct resource *conflict;445 446                         conflict = __request_resource(parent, res);447                         if (!conflict)448                                 break;449 if (conflict != parent) {450                                 parent = conflict;451                                 if (!(conflict->flags & IORESOURCE_BUSY))452                                         continue;453                         }454 455                         /* Uhhuh, that didn't work out.. */456                         kfree(res);457                         res = NULL;458                         break;459                 }

把内存地址里面的
#define PWM_MR     __phys_to_pfn(PWMC_BASE + PWM_MR_OFF)

就可以编译通过了.内核水很深,大家多多讨论.


原创粉丝点击