0

I am working on embedded linux board.

As per our requirement, need to access UART2 through memory map instead of serial character file (/dev/ttymxc1). When I access UART1 through mmap() function, then it is working fine. But, when I am trying to access UART2 through mmap() function, then getting "Bus error". I unable to understand what's happending, because UART1 is working and UART2 is not working. Please find my source code and kindly suggest me anything wrong here.

#include <signal.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <time.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <string.h>

 typedef unsigned int            uint32_t;

 #define READ_REG32(reg)         ( *((volatile uint32_t *) (reg)) )
 #define WRITE_REG32(value,reg)  ( *((volatile uint32_t *) (reg)) = value )

 #define ALLOC_SIZE              (1024)  
 //#define UART1_BASE        0x02020000
 #define UART2_BASE        0x021E8000

 /* Register definitions */
 #define URXD  0x0  /* Receiver Register */
 #define UTXD  0x40 /* Transmitter Register */
 #define UCR1  0x80 /* Control Register 1 */
 #define UCR2  0x84 /* Control Register 2 */
 #define UCR3  0x88 /* Control Register 3 */
 #define UCR4  0x8c /* Control Register 4 */
 #define UFCR  0x90 /* FIFO Control Register */
 #define USR1  0x94 /* Status Register 1 */
 #define USR2  0x98 /* Status Register 2 */
 #define UESC  0x9c /* Escape Character Register */
 #define UTIM  0xa0 /* Escape Timer Register */
 #define UBIR  0xa4 /* BRM Incremental Register */
 #define UBMR  0xa8 /* BRM Modulator Register */
 #define UBRC  0xac /* Baud Rate Count Register */
 #define UTS   0xb4 /* UART Test Register (mx31) */

 /* UART Control Register Bit Fields.*/
 #define  URXD_CHARRDY    (1<<15)
 #define  URXD_ERR        (1<<14)
 #define  URXD_OVRRUN     (1<<13)
 #define  URXD_FRMERR     (1<<12)
 #define  URXD_BRK        (1<<11)
 #define  URXD_PRERR      (1<<10)
 #define  URXD_RX_DATA    (0xFF)
 #define  UCR1_ADEN       (1<<15) /* Auto dectect interrupt */
 #define  UCR1_ADBR       (1<<14) /* Auto detect baud rate */
 #define  UCR1_TRDYEN     (1<<13) /* Transmitter ready interrupt enable */
 #define  UCR1_IDEN       (1<<12) /* Idle condition interrupt */
 #define  UCR1_RRDYEN     (1<<9)     /* Recv ready interrupt enable */
 #define  UCR1_RDMAEN     (1<<8)     /* Recv ready DMA enable */
 #define  UCR1_IREN       (1<<7)     /* Infrared interface enable */
 #define  UCR1_TXMPTYEN   (1<<6)     /* Transimitter empty interrupt enable */
 #define  UCR1_RTSDEN     (1<<5)     /* RTS delta interrupt enable */
 #define  UCR1_SNDBRK     (1<<4)     /* Send break */
 #define  UCR1_TDMAEN     (1<<3)     /* Transmitter ready DMA enable */
 #define  UCR1_UARTCLKEN  (1<<2)     /* UART clock enabled */
 #define  UCR1_DOZE       (1<<1)     /* Doze */
 #define  UCR1_UARTEN     (1<<0)     /* UART enabled */

 #define  UTS_FRCPERR     (1<<13) /* Force parity error */
 #define  UTS_LOOP        (1<<12) /* Loop tx and rx */
 #define  UTS_TXEMPTY     (1<<6)     /* TxFIFO empty */
 #define  UTS_RXEMPTY     (1<<5)     /* RxFIFO empty */
 #define  UTS_TXFULL     (1<<4)     /* TxFIFO full */
 #define  UTS_RXFULL     (1<<3)     /* RxFIFO full */
 #define  UTS_SOFTRST     (1<<0)     /* Software reset */

 void uart_putc(void *base, int ch)
 {
     WRITE_REG32(ch, base + UTXD);
     /* Wait until sent */
     while (!(READ_REG32(base + UTS) & UTS_TXEMPTY));
 }

 static void write_on_uart()  
 {  
         int fd=0;
         void *map_base;

         fd = open("/dev/mem", O_RDWR | O_SYNC);  

         if (fd) {  
             printf("Success to open /dev/mem fd=%08x\n", fd);  
         }  
         else {  
             printf("Fail to open /dev/mem fd=%08x\n", fd);    
         }  

         map_base = mmap(0, ALLOC_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, UART2_BASE);

         if ((int)map_base == -1)
         {
            printf("Fail to create a base address \n");
            return;
          }

          printf("map_base: %p\n", map_base);

         for (int i = 0 ; i < 10; i++)
         uart_putc(map_base,'S');

         close(fd);
         munmap(map_base, ALLOC_SIZE);

 }

 int main(int argc, char **argv)
 {
          write_on_uart();
          return 0;
 }

Getting error:

/dev/mem opened.[ 4243.747456] Unhandled fault: external abort on non-linefetch (0x1008) at 0x76fdd000 [ 4243.756248] pgd = 950c4000 [ 4243.759060] [76fdd000] *pgd=94736835, *pte=021e8703, *ppte=021e8e33

Memory mapped at address 0x76fdd000. Bus error (core dumped)

  • UART1 and UART2 may differ by [pins layout](http://e2e.ti.com/support/microcontrollers/other/f/908/t/351963?UART2-works-normally-but-UART0-UART1-doesn-t) – Agnius Vasiliauskas Jul 03 '19 at 06:30
  • @AgniusVasiliauskas thanks for reply. Yes, I have set pins according to UART1 and UART2. Also, I checked UART2 through "echo test > /dev/ttymxc1", then it is working fiine. But, access UART2 through mmap(), then it still is giving a error. – Pratik Gajera Jul 03 '19 at 06:59
  • Are you sure that for UART2 TX and RX pins are not swapped ? Description of error is almost straight-forward - data can't be read/written to a required register. – Agnius Vasiliauskas Jul 03 '19 at 07:33
  • Yes, I am sure about UART2 pins, there is no issue. I think, this issue is related to memory mapped. When UART2 base address is passed to mmap() function, then it will give virtual base address and based on this virtual base address to access UART2's transmit register (like base + UTXD) for sending data to TX pin. But, error is getting, before reaching data to TX pin. – Pratik Gajera Jul 03 '19 at 08:56
  • In your function **uart_putc()** you have written two expressions of pointer arithmetic using a void pointer. That's bad code. See https://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c – sawdust Jul 03 '19 at 09:20
  • @sawdust thanks for reply. yes agree that need to explicitly type casting when used void pointer and i did, but it still is not working. However, UART1 case, this code is working fine without type casting. Even, compiler is not giving any warning or error. – Pratik Gajera Jul 03 '19 at 09:35
  • *"However, UART1 case, this code is working fine ..."* -- What does that actually mean? Have you verified that characters are transmitted? Basic debugging procedure would be to insert **printf()** s to display all the values involved, e.g. `map_base`, `base + UTXD`, `base + UTS`. Verify the value of UART2_BASE. – sawdust Jul 03 '19 at 20:24
  • Thanks for reply. UART1 case: UART1 is sending data to pc through above code and i verified at PC side, data is successfully received. For UART2, I changed base address of UART2 in code and TX and RX pin. But it is not working. I analysed code flow through printf() also. When control comes at WRITE_REG32(ch, base + UTXD);, then it will give bus error. "Verify the value of UART2_BASE", yes i verified. – Pratik Gajera Jul 04 '19 at 04:23
  • You need to provide a better description of the HW that you're using. You are probably encountering a security mechanism, e.g. Supervisor Protection enforced by the AIPSTZ on some NXP SoCs. – sawdust Jul 05 '19 at 01:25
  • I am using imx6ull board based on toradex and iMX 6 Soc is used in the board. Please find following details: Hardware: col iMX6ULL 512MB WB IT (Iris Carrier Board) U-boot version: 2016.112.8.6+g02735f4 Kernel Version: v4.1.15 – Pratik Gajera Jul 05 '19 at 04:54
  • Based on [this post](https://stackoverflow.com/questions/47569718/linux-register-read-arm-i-mx257-from-userland-devmem-not-working), I assume the issue is related to Supervisor Protection enforced by the AIPSTZ. However for i.MX6 U-Boot should be calling **init_aips()**, which will perform the same initialization as **imx_aips_allow_unprivileged_access()** in Linux kernel. Try accessing the registers in U-Boot to see if that triggers the same fault. – sawdust Jul 06 '19 at 23:18
  • Thanks to all for helping me. Issue has been resolved by enabling clock for UART2. When need to access UART periphearal through mmap(), then enabled clock in kernel source code(drivers/clk/imx/clk-imx6ul.c). – Pratik Gajera Jul 10 '19 at 04:15

0 Answers0