Forum: Mikrocontroller und Digitale Elektronik TC Treiber Linux AT91


von Hummel (Gast)


Lesenswert?

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 ?

von Imon (Gast)


Lesenswert?

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

von Hummel (Gast)


Lesenswert?

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?

von Imon (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.