Accessing external bus in kernel space on an ARM based board - linux

I'm trying to write an LCD display driver on an ARM based board.
The LCD controller is plugged on the external memory bus.
So I try to convert the physical address of registers of the controller to the virtual one.
I use the following pieces of code to do that :
#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);
}
}
I get the following message :
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
Note that this method works for internal peripherals (such as timers).
So in some cases, phys_to_virt works.
I think that no page is allocated at the address 0x50000000. How can I allocate a page at this specific address ?
I found functions like kmap but it seems to be very complicated and I don't know how to use it.

The best way to access memory-mapped peripherals is with the kernel's ioremap and friends.
First, declare that you want to use a specific region of memory for your peripheral:
struct resource *res = request_mem_region(0x50000000, region_size, "at91");
When you unload your driver, you will want to free that memory region.
release_mem_region(0x50000000, region_size);
Now, you can remap the I/O region before use.
void *ptr = ioremap(0x50000000, region_size);
If you want to prevent caching of these registers, use ioremap_nocache instead. You can also only remap a subregion of your device's memory space if you're only using that part.
Now that you have the iomapped region, you can do I/O on that memory.
iowrite8(value, (char *)ptr + reg);
unsigned int val = ioread8((char *)ptr + reg);
Once you're done reading from and writing to that region of memory, you can unmap it.
iounmap(ptr);
I hope this helps. I would recommend reading (or at least using as a reference) Linux Device Drivers, 3rd Edition, which can be read online for free.

Related

u-boot hard fault error after ram initialization

I have ported U-boot on my Waveshare coreH7 stm32h743 board. I have used stm32h743-disco files and device trees as a template for porting. my onboard SDRAM is IS42S16400J that is 8MBytes. I have calculate the parameters of my sdram and I put them in my board device tree file as shown as below:
/*
* Memory configuration from sdram datasheet IS42S32800G-6BLI
* firsct bank is bank#0
* second bank is bank#1
*/
bank2: bank#1 {
st,sdram-control = /bits/ 8 <NO_COL_8
NO_ROW_12
MWIDTH_16
BANKS_4
CAS_3
SDCLK_2
RD_BURST_EN
RD_PIPE_DL_0>;
st,sdram-timing = /bits/ 8 <TMRD_1
TXSR_1
TRAS_1
TRC_6
TRP_2
TWR_1
TRCD_1>;
st,sdram-refcount = <300>;
};
also, I have been configured the rcc values to feed the dram with 100MHz.
but when uboot starts initialization, it goes to hard fault interrupt.
this is the log:
lib/fdtdec.c:fdtdec_setup_mem_size_base_fdt() fdtdec_setup_mem_size_base_fdt: Initial DRAM size 2000000
include/initcall.h:initcall_run_list() initcall: 08008a89
common/board_f.c:setup_dest_addr() Monitor len: 00039F80
common/board_f.c:setup_dest_addr() Ram size: 02000000
common/board_f.c:setup_dest_addr() Ram top: D2000000
include/initcall.h:initcall_run_list() initcall: 08008665
include/initcall.h:initcall_run_list() initcall: 0800117d
arch/arm/lib/cache.c:arm_reserve_mmu() TLB table from d1ff0000 to d1ff4000
include/initcall.h:initcall_run_list() initcall: 080088c3
include/initcall.h:initcall_run_list() initcall: 080088c7
include/initcall.h:initcall_run_list() initcall: 080086b1
common/board_f.c:reserve_uboot() Reserving 231k for U-Boot at: d1fb6000
include/initcall.h:initcall_run_list() initcall: 080088ed
common/board_f.c:reserve_malloc() Reserving 1032k for malloc() at: d1eb4000
include/initcall.h:initcall_run_list() initcall: 08008821
Hard fault
pc : 0800087e lr : 00000000 xPSR : 21000000
r12 : d1eb3ff0 r3 : 00000000 r2 : 00000010
r1 : 00000000 r0 : d1eb3fb0
Resetting CPU ...
what is the problem? is ram initialized unsuccessfully? why? maybe wrong parameters? how Can I know that ram is initialized successfully?
this is normal info log of u-boot:
U-Boot 2020.07-00610-g610e1487c8-dirty (Aug 04 2020 - 00:34:13 +0430)
Model: Waveshare STM32H743i-Coreh7 board
DRAM: Hard fault
pc : 0800087e lr : 00000000 xPSR : 21000000
r12 : d1eb3ff0 r3 : 00000000 r2 : 00000010
r1 : 00000000 r0 : d1eb3fb0
Resetting CPU ...
your ram size is wrong. as you mentioned, the actual ram size is 8MB that is 0x7A1200 in hexadecimal number. but in U-boot log is "Initial DRAM size 2000000". you should change it in the device tree of your board.
memory {
device_type = "memory";
reg = <0xd0000000 0x7A1200>;
};

How to debug mapbox crash

I have a redundant crash with mapbox library com.mapbox.mapboxsdk:mapbox-android-sdk:6.1.1
On the logcat I got this stack:
pid: 4960, tid: 4960, name: [...] >>> [...] <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
r0 00000000 r1 6b9faf17 r2 0000010f r3 00000001
r4 00000000 r5 b4d96a80 r6 00000003 r7 bef0db50
r8 bef0dae0 r9 b4db6500 sl bef0dbb0 fp b4db6500
ip b4cfc94c sp bef0da88 lr b499dc29 pc 9fdc9ebc cpsr 60060030
backtrace:
#00 pc 00095ebc /data/app/[...]/lib/arm/libmapbox-gl.so
#01 pc 00097545 /data/app/[...]/lib/arm/libmapbox-gl.so
#02 pc 00097587 /data/app/[...]/lib/arm/libmapbox-gl.so
#03 pc 000eaa29 /system/lib/libart.so (art_quick_generic_jni_trampoline+40)
#04 pc 000e6331 /system/lib/libart.so (art_quick_invoke_stub_internal+64)
#05 pc 004028a5 /system/lib/libart.so (art_quick_invoke_stub+188)
#06 pc 007fccdc [stack]`
And trying to get a clear line number for libmapbox-gl.so with ndk-stack, I only got that:
Build fingerprint: 'google/hammerhead/hammerhead:6.0.1/M4B30Z/3437181:user/release-keys'
pid: 4960, tid: 4960, name: [...] >>> [...] <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
Stack frame #00 pc 00095ebc /data/app/[...]/lib/arm/libmapbox-gl.so: Routine ??
??:0
Stack frame #01 pc 00097545 /data/app/[...]/lib/arm/libmapbox-gl.so: Routine ??
??:0
Stack frame #02 pc 00097587 /data/app/[...]/lib/arm/libmapbox-gl.so: Routine ??
??:0
Stack frame #03 pc 000eaa29 /system/lib/libart.so (art_quick_generic_jni_trampoline+40)
Stack frame #04 pc 000e6331 /system/lib/libart.so (art_quick_invoke_stub_internal+64)
Stack frame #05 pc 004028a5 /system/lib/libart.so (art_quick_invoke_stub+188)
Stack frame #06 pc 007fccdc [stack]
Here is my ndk-stack command: $ANDROID_LIB_PATH/sdk/ndk-bundle/ndk-stack -sym $PROJECT_PATH/app/build/intermediates/transforms/mergeJniLibs/servertest/release/0/lib/x86_64/ -dump logcat.txt
How to get the line number where the crash happened?

If I have only one PCIe EP and only one function, Do I need “interrupt-map” function in device tree (DTS)?

I’m using legacy PCIe Interrupt mechanism(Virtual INTx), As per my PCIe controller user guide, In legacy Interrupt mechanism Assert or Deassert Virtual INTx messages will be received from the EP device and according to that PCIe Core it will generate or clear interrupt signal respectively.
If I have only one EP and only one function, Do I need “interrupt-map” function in device tree (DTS)?
As per my understanding,
PCIe EP will send Assert_INTA message
PCIe RP decode this message and send Interrupt to Interrupt generating pin INTA_OUT
In design INTA_OUT is directly connected to Interrupt controller Input pin
In Linux kernel this IRQ will invoke PCIe RP ISR and also Endpoint functions ISR
Am I correct in my understanding ?
pci#0xfb000000 {
interrupts = <1 1>; //PCIe RP INTA  IRQ1, Only this info is enough ??
interrupt-parent = <&pic>;
/*Do we need follow info also ??? */
#interrupt-cells = <0x1>;
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = < 0x0 0x0 0x0 0x1 &pcie_intc 0x1
0x0 0x0 0x0 0x2 &pcie_intc 0x2
0x0 0x0 0x0 0x3 &pcie_intc 0x3
0x0 0x0 0x0 0x4 &pcie_intc 0x4 >;
}

How come Linux kernel interferes the execution of RISC-V custom0 instruction on Zedboard?

dummy_rocc is a naive built-in RoCC accelerator example in RISCV tools, where several custom0 instructions are defined. After setup dummy_rocc (either on Spike ISA simulator or on Rocket-FPGA, differently), we use dummy_rocc_test -- a user program testcase to verify the correctness of the dummy_rocc accelerator. We have two ways to run dummy_rocc_test, either on pk (proxy kernel) or on Linux.
I once setup dummy_rocc on Spike ISA simulator, the dummy_rocc_test worked well either on pk or on Linux.
Now I replace Spike with Rocket-FPGA on Zedboard. While the execution on pk succeeds:
root#zynq:~# ./fesvr-zynq pk /nfs/copy_to_rootfs/work/dummy_rocc_test
begin
after asm code
load x into accumulator 2 (funct=0)
read it back into z (funct=1) to verify it
accumulate 456 into it (funct=3)
verify it
do it all again, but initialize acc2 via memory this time (funct=2)
do it all again, but initialize acc2 via memory this time (funct=2)
do it all again, but initialize acc2 via memory this time (funct=2)
success!
the execution on Linux fails:
./fesvr-zynq +disk=/nfs/root.bin bbl /nfs/fpga-zynq/zedboard/fpga-images-zedboard/riscv/vmlinux
..................................Booting RISC-V Linux.........................................
/ # ./work/dummy_rocc_test
begin
after asm code
[ 0.400000] dummy_rocc_test[23]: unhandled signal 4 code 0x30001 at 0x0000000000800500 in ]
[ 0.400000] CPU: 0 PID: 23 Comm: dummy_rocc_test Not tainted 3.14.33-g043bb5d #1
[ 0.400000] task: ffffffff8fa3f500 ti: ffffffff8fb76000 task.ti: ffffffff8fb76000
[ 0.400000] sepc: 0000000000800500 ra : 00000000008004fc sp : 0000003fff943c70
[ 0.400000] gp : 0000000000882198 tp : 0000000000884700 t0 : 0000000000000000
[ 0.400000] t1 : 000000000080adc8 t2 : 8101010101010100 s0 : 0000003fff943ca0
[ 0.400000] s1 : 0000000000800d5c a0 : 000000000000000f a1 : 0000002000002000
[ 0.400000] a2 : 000000000000000f a3 : 000000000085cee8 a4 : 0000000000000001
[ 0.400000] a5 : 000000000000007b a6 : 0000000000000008 a7 : 0000000000000040
[ 0.400000] s2 : 0000000000000000 s3 : 00000000008a2668 s4 : 00000000008d8d98
[ 0.400000] s5 : 00000000008d7770 s6 : 0000000000000008 s7 : 00000000008d6000
[ 0.400000] s8 : 00000000008d8d60 s9 : 0000000000000000 s10: 00000000008a32b8
[ 0.400000] s11: ffffffffffffffff t3 : 000000000000000b t4 : 000000006ffffdff
[ 0.400000] t5 : 000000000000000a t6 : 000000006ffffeff
[ 0.400000] sstatus: 8000000000003008 sbadaddr: 0000000000800500 scause: 0000000000000002
Illegal instruction
A screenshot shows that the "signal 4" is caused by a custom0 instruction.
readelf screenshot of dummy_rocc_test
So my problem is "How come Linux kernel interferes the execution of RISC-V custom0 instruction on Zedboard? "
The source code of dummy_rocc_test is provided as reference:
// The following is a RISC-V program to test the functionality of the
// dummy RoCC accelerator.
// Compile with riscv64-unknown-elf-gcc dummy_rocc_test.c
// Run with spike --extension=dummy_rocc pk a.out
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
int main() {
printf("begin\n");
uint64_t x = 123, y = 456, z = 0;
// load x into accumulator 2 (funct=0)
// asm code
asm volatile ("addi a1, a1, 2");
/// printf again
printf("after asm code\n");
asm volatile ("custom0 x0, %0, 2, 0" : : "r"(x));
printf("load x into accumulator 2 (funct=0)\n");
// read it back into z (funct=1) to verify it
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
printf("read it back into z (funct=1) to verify it\n");
assert(z == x);
// accumulate 456 into it (funct=3)
asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));
printf("accumulate 456 into it (funct=3)\n");
// verify it
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
printf("verify it\n");
assert(z == x+y);
// do it all again, but initialize acc2 via memory this time (funct=2)
asm volatile ("custom0 x0, %0, 2, 2" : : "r"(&x));
printf("do it all again, but initialize acc2 via memory this time (funct=2)\n");
asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));
printf("do it all again, but initialize acc2 via memory this time (funct=2)\n");
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
printf("do it all again, but initialize acc2 via memory this time (funct=2)\n");
assert(z == x+y);
printf("success!\n");
}
"Illegal instruction" means your processor threw an illegal instruction exception.
Since custom0 is not going to be something Linux will know how to execute in software (since it's something that's customizable!), Linux will panic and throw the error that you saw.
The question I have for you is "Did you implement the custom0 instruction in the processor? Is it enabled? Did the program execute your custom0 instruction properly and return the correct answer when you used the proxy-kernel?"

Why can't I register edge triggered interrupts in Linux 2.6.26?

First time poster, so please excuse any stupidity.
I'm working on porting a custom CPLD driver on a linux kernel built for an MPC83xx from 2.6.22 to 2.6.26, and am receiving an unexpected Oops. The driver works fine for the .22 kernel, but the .26 kernel chokes on my call to request_irq. Does anybody know why there was a change in behavior, or better yet, what I need to do to resolve it?
I've tracked down the source of the Oops to a call to kernel/irq/manage.c, where desc->chip->enable(irq) is called in setup_irq(), and it looks like the function pointer to enable is being cleared in a call to ipic_set_irq_type() in arch/powerpc/sysdev/ipic.c. Unfortunately, I have no clue why.
I've included both the Oops and a sample kernel modules that replicates the issue.
Oops -
Unable to handle kernel paging request for instruction fetch
Faulting instruction address: 0x00000000
Oops: Kernel access of bad area, sig: 11 [#1]
PREEMPT SCPA-G2
Modules linked in: cpld(+)
NIP: 00000000 LR: c004b930 CTR: 00000000
REGS: df8b5df0 TRAP: 0400 Not tainted (2.6.26-twacs-100.0.0)
MSR: 20001032 <ME,IR,DR> CR: 24022422 XER: 20000000
TASK = dfbcfc00[488] 'insmod' THREAD: df8b4000
GPR00: 00000000 df8b5ea0 dfbcfc00 00000017 00000001 00000001 00000000 c02d1fb4
GPR08: 00002268 00000000 00000000 00000000 44022484 10073f68 1ffcb000 007ffeb0
GPR16: 00000000 00000000 00800000 00000000 bffff7f0 00000000 1006e3dc 00000000
GPR24: 00000002 00000000 00000000 00009032 df9d04c0 00000017 df8b4000 c02d40e4
NIP [00000000] 0x0
LR [c004b930] setup_irq+0x404/0x430
Call Trace:
[df8b5ea0] [c004b8ec] setup_irq+0x3c0/0x430 (unreliable)
[df8b5ed0] [c004bbd8] request_irq+0xe0/0x130
[df8b5f00] [e1078054] cpld_init+0x54/0xd0 [cpld]
[df8b5f10] [c0048ba0] sys_init_module+0x14c/0x1d8
[df8b5f40] [c0010008] ret_from_syscall+0x0/0x38
--- Exception: c01 at 0xff27bb0
LR = 0x10019ca8
Instruction dump:
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Kernel panic - not syncing: Fatal exception
Module -
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
static unsigned int cpld_virq = NO_IRQ;
unsigned value = 0xdeadbeef;
static irqreturn_t cpld_isr(int irq, void *dev_id) {
return IRQ_HANDLED;
}
void __exit cpld_cleanup(void) {
free_irq(cpld_interrupt, &value);
irq_dispose_mapping(cpld_virq);
return;
}
int __init cpld_init(void) {
int retval;
unsigned long cpld_interrupt = 23;
cpld_virq = irq_create_mapping(NULL, cpld_interrupt);
if (cpld_virq == NO_IRQ) {
return -EBUSY;
}
retval = request_irq(cpld_virq, cpld_isr,
IRQF_DISABLED | IRQF_SHARED | IRQF_TRIGGER_FALLING,
"CPLD", &value);
if (retval) {
irq_dispose_mapping(cpld_virq);
return retval;
}
return 0;
}
module_init(cpld_init);
module_exit(cpld_cleanup);
MODULE_LICENSE("Dual BSD/GPL");
Thanks for the help. I've been beating my head on this for several days now, and am open to any suggestions.
My copy of the 2.6.26 kernel's ipic_set_irq_type() doesn't do anything with the enable() pointer. It does, however, have the following comments that were not in 2.6.22:
/* ipic only supports low assertion and high-to-low change senses
*/
and
/* ipic supports only edge mode on external interrupts */
It looks like what you were doing on 2.6.22 was not supported by the hardware.
Aaah. Sounds like JayM was close, though not for the reason he thought. I just saw a patch go across the linuxppc-dev mailing list stating that edge supported interrupts were broken.
http://lkml.org/lkml/2010/5/3/363
This may not be the root cause, but is indicative of a problem I'm not going to fix.

Resources