Доступ к внешней шине в пространстве ядра на плате на основе ARM - PullRequest
1 голос
/ 15 декабря 2011

Я пытаюсь написать драйвер ЖК-дисплея на плате на базе ARM.Контроллер ЖКД подключен к шине внешней памяти.Поэтому я пытаюсь преобразовать физический адрес регистров контроллера в виртуальный.

Для этого я использую следующий фрагмент кода:

#define AT91_VA_BASE_CS2    phys_to_virt(0x50000000)

static inline unsigned char at91_CS2_read(unsigned int reg)
{
    void __iomem *CS2_base = (void __iomem *)AT91_VA_BASE_CS2;

    return __raw_readb(CS2_base + reg);
}

static inline void at91_CS2_write(unsigned int reg, unsigned char value)
{
    void __iomem *CS2_base = (void __iomem *)AT91_VA_BASE_CS2;

    __raw_writeb(value, CS2_base + reg);
}

void write_lcd_port (int mode, unsigned char cmd_dat)
{
   while ((read_lcd_port() & 0x03) != 0x03) {
      /* wait while LCD is busy!!! */
   } /* endwhile */


   /* Send Command */
   if (mode == 1)
   {
       at91_CS2_write(4, cmd_dat);
   }
   /* Send Data */
   if (mode == 0)
   {
       at91_CS2_write(0, cmd_dat);
   }
}

Я получаю следующее сообщение:

Unable to handle kernel paging request at virtual address 4f000004
pgd = c39bc000
[4f000004] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: module_complet dm9000 at91_wdt vfat fat jffs2 nls_iso8859_1 nls_cp437 nls_base usb_storage sd_mod sg scsie
CPU: 0
PC is at read_lcd_port+0x1c/0x38 [module_complet]
LR is at 0x1
pc : [<bf0a21b8>]    lr : [<00000001>]    Tainted: P
sp : c380bf1c  ip : 60000093  fp : c380bf2c
r10: 0003a804  r9 : c380a000  r8 : c001de64
r7 : 00000000  r6 : fefff000  r5 : 0000009c  r4 : 00000001
r3 : 4f000000  r2 : 00000000  r1 : 00001438  r0 : bf0a25cc
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: C000717F  Table: 239BC000  DAC: 00000015
Process insmod (pid: 903, stack limit = 0xc380a198)
Stack: (0xc380bf1c to 0xc380c000)
bf00:                                                                00000001
bf20: c380bf44 c380bf30 bf0a21f4 bf0a21ac 00000000 fefa0000 c380bf54 c380bf48
bf40: bf0a2288 bf0a21e4 c380bf64 c380bf58 bf0a246c bf0a2280 c380bf84 c380bf68
bf60: bf0a4058 bf0a2464 40017000 c01c89a0 bf0a2d80 c01c8990 c380bfa4 c380bf88
bf80: c004cd20 bf0a4010 00000003 00000000 0000000c 00000080 00000000 c380bfa8
bfa0: c001dcc0 c004cbc8 00000000 0000000c 00900080 40017000 0000162e 00041050
bfc0: 00000003 00000000 0000000c bea0fde4 bea0fec4 00000002 0003a804 00000000
bfe0: bea0fd10 bea0fd04 0001b290 400d1d20 60000010 00900080 20002031 20002431
Backtrace:
[<bf0a219c>] (read_lcd_port+0x0/0x38 [module_complet]) from [<bf0a21f4>] (write_lcd_port+0x20/0x80 [module_complet])
 r4 = 00000001
[<bf0a21d4>] (write_lcd_port+0x0/0x80 [module_complet]) from [<bf0a2288>] (wr_cmd+0x18/0x1c [module_complet])
 r5 = FEFA0000  r4 = 00000000
[<bf0a2270>] (wr_cmd+0x0/0x1c [module_complet]) from [<bf0a246c>] (lcd_init+0x18/0x80 [module_complet])
[<bf0a2454>] (lcd_init+0x0/0x80 [module_complet]) from [<bf0a4058>] (mon_module_init+0x58/0xcc [module_complet])
[<bf0a4000>] (mon_module_init+0x0/0xcc [module_complet]) from [<c004cd20>] (sys_init_module+0x168/0x2c8)
 r6 = C01C8990  r5 = BF0A2D80  r4 = C01C89A0
[<c004cbb8>] (sys_init_module+0x0/0x2c8) from [<c001dcc0>] (ret_fast_syscall+0x0/0x2c)
 r7 = 00000080  r6 = 0000000C  r5 = 00000000  r4 = 00000003
Code: e59f001c eb3e43c2 e3a0344f e59f0014 (e5d34004)
 Segmentation fault

Обратите внимание, что этот метод работает для внутренних периферийных устройств (таких как таймеры).Так что в некоторых случаях Phys_to_virt работает.Я думаю, что ни одна страница не выделяется по адресу 0x50000000.Как я могу разместить страницу по этому конкретному адресу?Я нашел такие функции, как kmap, но это кажется очень сложным, и я не знаю, как его использовать.

1 Ответ

4 голосов
/ 15 декабря 2011

Лучший способ получить доступ к периферийным устройствам, отображаемым в памяти, - использовать ядро ​​ioremap и его друзей.

Сначала объявите, что вы хотите использовать определенную область памяти для своего периферийного устройства:

struct resource *res = request_mem_region(0x50000000, region_size, "at91");

Когда вы выгрузите свой драйвер, вы захотите освободить эту область памяти.

release_mem_region(0x50000000, region_size);

Теперь вы можете переназначить регион ввода / вывода перед использованием.

void *ptr = ioremap(0x50000000, region_size);

Если вы хотите предотвратить кэширование этих регистров, используйте ioremap_nocache. Вы также можете переназначить субрегион области памяти вашего устройства, только если используете только эту часть.

Теперь, когда у вас есть область iomapped, вы можете выполнять ввод-вывод в этой памяти.

iowrite8(value, (char *)ptr + reg);
unsigned int val = ioread8((char *)ptr + reg);

Как только вы закончите чтение и запись в эту область памяти, вы можете удалить ее из карты.

iounmap(ptr);

Надеюсь, это поможет. Я бы порекомендовал прочитать (или хотя бы использовать в качестве справки) Драйверы устройств Linux, 3-е издание , которые можно читать онлайн бесплатно.

...