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,structtimespec*tp)=(unsignedlong*)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.