Hallo, ich versuche grade einen Treiber (Timer/Counter) für meinen AT91SAM9263 zu schreiben . Als Grundlage habe ich dieses hier benutzt: http://www.ssv-comm.de/forum/viewtopic.php?t=735&sid=5448dd8d0f344210e04c17c741ecefc8 mein Problem ist, dass es in den headern vom AT91 das Macro „AT91_VA_BASE_TCB0“ nicht gibt. Also habe ich versucht dieses durch „AT91_IO_P2V(AT91SAM9263_BASE_TC0)“ zu ersetzen. Dies führ aber zu einem Pagefault nach insmod. # insmod ttreiber.ko init timer AT91SAM9263_BASE_TC1: feffe000 read p: fef7c000 + (0) Unable to handle kernel paging request at virtual address fef7c000 pgd = c3ac4000 [fef7c000] *pgd=20377051, *pte=00000000, *ppte=00000000 Internal error: Oops: 27 [#1] Modules linked in: ttreiber(+) CPU: 0 Not tainted (2.6.28 #2) PC is at ad_irq_init+0x38/0x1c4 [ttreiber] LR is at release_console_sem+0x1a4/0x20c pc : [<bf003038>] lr : [<c0037b2c>] psr: 20000013 sp : c3a05df0 ip : c3a05d20 fp : c3a05e14 r10: 0000b58c r9 : c3a04000 r8 : c0028f64 r7 : 00000000 r6 : fef7cfff r5 : bf000520 r4 : 00000e3b r3 : c02da670 r2 : 00000001 r1 : 00001384 r0 : 0000001a Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 0005317f Table: 23ac4000 DAC: 00000015 Process insmod (pid: 441, stack limit = 0xc3a04268) Stack: (0xc3a05df0 to 0xc3a06000) 5de0: c00bdd78 c39d7d88 c3808a00 00000e3b 5e00: bf000520 bf003000 c3a05f7c c3a05e18 c00282e0 bf003010 c3a05e74 c3a05e28 5e20: c00be5c4 c0229794 c3a05e78 c3a05e40 c3a05e5c c0368180 c02d4204 40000013 5e40: c3a05e6c c3a05e50 c006385c c0061d8c 00000000 c4869000 c38d2c00 00000001 5e60: c3a05e7c c3a05e70 c0063924 c00636b0 c3a05e94 00000001 c4869000 c38d2c00 5e80: 00000001 c4869984 c3a05eb4 c3a05e98 c0077034 c007c268 c3a47d60 00000001 5ea0: bf000520 00000001 c3a05ec4 c3a05eb8 c00770cc c0076f80 c3a05f7c c3a05ec8 5ec0: c0058998 c00770a0 00000000 c3a05f48 c3a05ecc c3a05f34 c4869844 c486995c 5ee0: 00000000 c4869000 c48695cb c396b8c0 c4869d4c 00000012 00000009 c02288d4 5f00: 00000026 00000026 c3a04000 0000005a bf000568 c4869984 bf00052c 00000014 5f20: c48699ac c022bc60 00000000 00000000 00000000 00000000 00000000 00000000 5f40: 00000000 00000e3b bf000520 000a1018 00000000 c0028f64 c3a05f7c c3a05f68 5f60: 00000e3b bf000520 000a1018 00000000 c3a05fa4 c3a05f80 c0058c64 c00282a0 5f80: c3a05fa4 c3a05f90 00000002 000a1018 000a1008 00000080 00000000 c3a05fa8 5fa0: c0028de0 c0058bdc 00000002 000a1018 000a1018 00000e3b 000a1008 00000000 5fc0: 00000002 000a1018 000a1008 00000080 00000052 0000cac4 0000b58c 00000000 5fe0: 40019374 be9adcd0 00019770 40019388 20000010 000a1018 00000000 00000000 Backtrace: [<bf003000>] (ad_irq_init+0x0/0x1c4 [ttreiber]) from [<c00282e0>] (__exception_t ext_end+0x50/0x180) r6:bf003000 r5:bf000520 r4:00000e3b [<c0028290>] (__exception_text_end+0x0/0x180) from [<c0058c64>] (sys_init_module +0x98/0x188) r7:00000000 r6:000a1018 r5:bf000520 r4:00000e3b [<c0058bcc>] (sys_init_module+0x0/0x188) from [<c0028de0>] (ret_fast_syscall+0x0 /0x2c) r7:00000080 r6:000a1008 r5:000a1018 r4:00000002 Code: e59f116c e3a02000 e59f0168 eb489626 (e5164fff) ---[ end trace e517e4c89a8154e9 ]--- Segmentation fault # Wenn ich statt „AT91_VA_BASE_TCB0“ „AT91_VA_BASE_SYS“ benutze was sicher Falsch ist registriert sich das Modul löst aber keine Interruptus aus. Die Frage ist, Warum klappt die Variante mit „AT91_IO_P2V(AT91SAM9263_BASE_TC0)“ nicht ? Wie bekomme ich das richtige Mapping zwischen den Physikalischen und den Virtuellen Adressen hin ?
Hallo, denn Treiber denn du da Referenzierst der ist für eine andere CPU als du hast, daher muss du dir das mal kritisch ansehen um zu entscheiden was du davon nehmen kannst. Außerdem will sich mir der Sinn des Treiber noch nicht so richtig Darstellen und bei Zeilen wie diese hier
1 | |
2 | long (*p_sys_clock_gettime)(clockid_t, struct timespec *tp) = (unsigned long *) 0xc004373c; // function pointer to sys_clock_gettime |
wird mir richtig schlecht, das wird genau auf einer Maschine laufen nämlich der des Entwicklers und auch nur solange dort nie ein Update gemacht wird. Außerdem solltest du nochmal überdenken warum du ein Hardware Timer aus den Userspace ansprechen willst. Das Konzept wirkt erstmal fragwürdig. denn auf dein Linux werden mehre Prozesse laufen, so das du nie sicher sein kannst wann dein Programm das denn Timer ließt, wieder dran kommt denn Spätesten beim aufruf der Funktion read wird der scheduler angewissen neu auszuahndeln welcher Prozess als nächstes laufen darf und somit hast du spätesten hier die genauikeit nicht mehr gegeben die ein Hardware im Vergleich zu einen Software timer hat. Also entweder du handelst etwas mehr von deiner Aufgabe im Kernel ab, oder du nimmst die Standarttimer Funktionen wie udelay und Co oder du hast die Anforderungen an das System, die nur mit denn RT Aufsatz des Linux Kernel (Preempt RT oder Xenomai) erfüllt werden könnnen. Wie denn auch sei, der Beispiel Treiber scheint mir Murks zu sein und nichtmals als gutes Beispiel geeignet. Willst du nicht einfach mal erklären was du vorhast ausser einen Treiber für die Timerinterrups zu schreiben ? Ach ja und für das mapping zwischen Virtuellen und physical Adresses gibt es die Funktionen. virt_to_phys und phys_to_virt
Mein Modul sieht im Moment so aus. #define AT91_TC0 0x00 #define AT91_TC1 0x40 #define AT91_TC2 0x80 //#define AT91_VA_BASE_TCB0 AT91_IO_P2V(AT91SAM9263_BASE_TCB0) #define AT91_ID_TC0 AT91SAM9263_ID_TCB #ifdef MODULE_AUTHOR MODULE_AUTHOR("xxxxx"); MODULE_DESCRIPTION(" Modul T"); MODULE_LICENSE("GPL"); #endif int irq_count=0; /* unload kernel module */ static void __exit ad_irq_cleanup(void) { /* unregister irq handler */ free_irq(AT91_ID_TC0, NULL); //in interrupt.h extern printk(KERN_INFO "ad_irq module removed.\n"); } static inline unsigned long at91_tcb1_read(unsigned int reg) { void __iomem *tcb1_base = (void __iomem *) AT91_IO_P2V( AT91SAM9263_BASE_TC0); printk(KERN_INFO "read p: %p + (%d)\n", tcb1_base, reg); return __raw_readl(tcb1_base + reg); } /* * Write to Timerblock 0 registers. */ static inline void at91_tcb1_write(unsigned int reg, unsigned long value) { void __iomem *tcb1_base = AT91_IO_P2V( AT91SAM9263_BASE_TC0); printk(KERN_INFO "write p: %p + (%d)\n", tcb1_base, reg); __raw_writel(value, tcb1_base + reg); } /* irq timer routine */ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned int status; // read & clear status: status = at91_tcb1_read(AT91_TC0 + AT91_TC_SR); irq_count++; printk(KERN_INFO "IRQ wurde ausgelößt:%d \n",irq_count); if (irq_count > 100 ) irq_count = 0; //ad_irq_count++; return(IRQ_HANDLED); } static int __init ad_irq_init(void) { printk(KERN_INFO "init timer\n"); //disable clock at91_tcb1_write(AT91_TC0 + AT91_TC_CCR, ((at91_tcb1_read(AT91_TC0 + AT91_TC_CCR) | 2) ) ); // disable all Timer Channel 0 interrupts: at91_tcb1_write(AT91_TC0 + AT91_TC_IDR, 0xFFFFFFFF ); // read & clear status: at91_tcb1_read(AT91_TC0 + AT91_TC_SR ); // enable timer clock 5, reset counter and start clock at91_tcb1_write(AT91_TC0 + AT91_TC_CMR, AT91_TC_TIMER_CLOCK4 | AT91_TC_CPCTRG | !AT91_TC_WAVE ); printk(KERN_INFO "CMR: %ld\n", at91_tcb1_read(AT91_TC0 + AT91_TC_CMR) ); at91_tcb1_write(AT91_TC0 + AT91_TC_IER, 0x10 ); if (request_irq(AT91_ID_TC0, (void *)timer_interrupt, 0 ,"Timer_driver", NULL)) { printk(KERN_ERR "ad_irq: irq alrdy claimed!\n"); return -EIO; } // write something to timer register c: at91_tcb1_write( AT91_TC0 + AT91_TC_RC , 0xFBC5 ); // enable & start clock at91_tcb1_write(AT91_TC0 + AT91_TC_CCR , 0x01 ); at91_tcb1_write(AT91_TC0 + AT91_TC_CCR , 0x05 ); // maybe sync is needed ? at91_tcb1_write(AT91_TC0 + AT91_TC_BMR, 1 ); printk(KERN_INFO "ad_irq module installed with irqnr=%d.\n", AT91_ID_TC0); return 0; } //EXPORT_NO_SYMBOLS; module_init(ad_irq_init); module_exit(ad_irq_cleanup); >Willst du nicht einfach mal erklären was du vorhast ausser einen Treiber >für die Timerinterrups zu schreiben ? Im Moment möchte ich einen Treiber schreiben der in der Lage ist Interrupts auszulösen welche unter /proc/interrupts sichtbar werden. Das, dass nicht besonders Sinnvoll ist für ein nicht echtzeitfähiges Linux ist mir erst mal egal. >Wie denn auch sei, der Beispiel Treiber scheint mir Murks zu sein und >nichtmals als gutes Beispiel geeignet. Ich suche jetzt schon seit einigen Tagen nach Beispielen für den AT91, das ist das einzige was ich finden konnte (für tipps wo ich bessere Beispiele find währe ich sehr Dankbar). >Ach ja und für das mapping zwischen Virtuellen und physical Adresses >gibt es die Funktionen. die Definition in der hardware.h für AT91_IO_P2V(x) #define AT91_IO_PHYS_BASE 0xFFF78000 #define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1) #define AT91_IO_VIRT_BASE (0xFF000000 - AT91_IO_SIZE) #else /* * Identity mapping for the non MMU case. */ #define AT91_IO_PHYS_BASE AT91_BASE_SYS #define AT91_IO_VIRT_BASE AT91_IO_PHYS_BASE #endif //#define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1) /* Convert a physical IO address to virtual IO address */ #define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE) und dass, ist doch das Selbe wie bei phys_to_virt nur das die Offsets nicht für den Ram sind oder sehe ich das falsch?
Hummel schrieb: >[Code entfernt] >>Willst du nicht einfach mal erklären was du vorhast ausser einen Treiber >>für die Timerinterrups zu schreiben ? > > Im Moment möchte ich einen Treiber schreiben der in der Lage ist > Interrupts auszulösen welche unter /proc/interrupts sichtbar werden. > Das, dass nicht besonders Sinnvoll ist für ein nicht echtzeitfähiges > Linux ist mir erst mal egal. Ich habe mir das gerade extra nochmal angesehen im Standart Linux 2.6.34 ist der Code schon drin, und zwar unter arch/arm/mach-at91/at91sam926x_time.c der macht genau das was du sucht die Interrupts in /proc/interrupts erhöhen wenn es zum Timerinterrrupt kommt. > >>Wie denn auch sei, der Beispiel Treiber scheint mir Murks zu sein und >>nichtmals als gutes Beispiel geeignet. > > Ich suche jetzt schon seit einigen Tagen nach Beispielen für den AT91, > das ist das einzige was ich finden konnte (für tipps wo ich bessere > Beispiele find währe ich sehr Dankbar). Also die Zentrale Anlaufstelle für die At91 unter Linux ist die Seite www.linux4.sam.org dort wird die Entwicklung von denn at91 Bausteinen unter linux vorangetrieben. allerdings wirst du dort keine Beispiel Treiber finden. Ich denke du solltest dich davon verabschieden direckt bespiele für denn at91 finden zu wollen, da wirst du es wahrscheinlich ziemlich schwer haben. Zum generellen Einstieg in die Kernel Entwicklung Empfehle ich dir das Buch Linux device Drivers, die Webseite kernelnewbies.org und die Verzeichnis Documentation in dein Kernel Source. Ich habe anfangen damit das oben genannte Buch zu lesen, was zugegebenermaßen das entwickeln am PC beschriebt und da nach damit weitergamcht mir die schon Vorhanden Treiber anzusehen und dies an meine Bedürfnisse anzupassen. Das geht recht gut da außer unter arch/ alles Platformunabhänig ist und du somit diese teile für alle CPU Typen die Linux unterschtüzt nutzen kannst. das Ansprechen der Konkreten Hardware ist meisten in ein platform spezifischen teil gekapselt welcher in dein Fall wohl unter /arch/arm/march-at91/board-sam9263ek.c liegt. Am Anfagn wirst du dich wohl für die sachen unter drivers am meisten interessieren, generell gilt, da kannst du gut nach Inspiration suchen ( Ich Liebe Open Soruce), oder vielleicht findest du dort sogar schon ein passenden Treiber, dann must du nur noch den teil in deiner Bord Konfiguration an die Gegebenheiten anpassen. Was meisten daruf hinausläuft nachzusehen wo der Treiber denn du als Quelle deiner Inspration gebraucht hast in Verwendung ist, in dein Fall vorzugsweise unter arch/arm und denn teil übernimmst du dann angepasst in deine board Definitions Datei. An den Timer welchen du nutzen willst ist einmal der Trieber drivers/clocksource/tcb_clksrc.c Beteiligt und als Bord spezifischer teil arch/arm/mach-at91/at91sam926x_time.c > >>Ach ja und für das mapping zwischen Virtuellen und physical Adresses >>gibt es die Funktionen. > > die Definition in der hardware.h für AT91_IO_P2V(x) > #define AT91_IO_PHYS_BASE 0xFFF78000 > #define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1) > #define AT91_IO_VIRT_BASE (0xFF000000 - AT91_IO_SIZE) > #else > /* > * Identity mapping for the non MMU case. > */ > #define AT91_IO_PHYS_BASE AT91_BASE_SYS > #define AT91_IO_VIRT_BASE AT91_IO_PHYS_BASE > #endif > > //#define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1) > > /* Convert a physical IO address to virtual IO address */ > #define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE) > > und dass, ist doch das Selbe wie bei phys_to_virt nur das die Offsets > nicht für den Ram sind oder sehe ich das falsch? Ja und nein beim At91 mag es seind as diese Makros denn gleichen zweck erfüllen und auch gehen würden, aber das geht wieder nur beim At91 die Funktionen die ich dir genannt habe gehen bei jeder CPU, ja sogar wenn es keine MMU gibt, in so einen fall liefern sie dann die gleiche Adresse zurück wie man angegeben hat. Der Linux Kernel ist auf so vielen CPU typen einsatzfähig weil man sich dazu entschieden hat die Treiber in sichten zu bauen es gibt vereinfacht gesagt lowlevel und Highlevel treiber, diese Abstrationsicht macht es möglich das man dnen gleichen Source code nutz um zum beispiel einen Touchscreen einmal an einen ARM zu betreiben und ein anders mal an einen Blackfin ohne denn Touch Treiber anpassen zu müssen. das Konzept würdest du Topetieren wenn du nicht die generischen Funktionen nutz sondern die Makros welche in deinen Fall vielleicht sogar gehen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.