This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to set registers safely from Linux?

Hello everybody.Now I am working on the SEED OMAPL137 evm board.

I met a problem when seting the registers. I wrote a driver for setting registers through ioctl(), PINMUX13  for instance ( the PINMUX13 is related to EMA_D):

static int reg_set(unsigned int reg, unsigned int value)
{
void __iomem *regVirt = ioremap(reg, sizeof(reg));
iowrite32(value, regVirt);
iounmap(regVirt);
return 0;
}

......

static int setreg_ioctl (struct inode *inode,
struct file *filp,
unsigned int cmd,
unsigned long param)
{
reg_set(cmd, param);
return 1;
}

Tests to this driver were correct and the value of chosen register changed. However, after setting the register, the console is crashed and there was no response when pressing any keys. The console occupied UART2 and I was sure UART2 related registers were not changed.

So I wanna to know what is wrong with my code and how to set the registers?

  • Hi David,

    One thing to note here is its not recommended to control PINs while kernel is running. If we are doing this then we might see some random crashes, because we are breaking the functionality of the pin which is being currently in use.

    I have not gone through the corresponding pin details, but I suspect that same pin is used for UART RX/TX. Can you provide the log?

    Regards

    AnilKumar

    Please mark this Forum post as answered via the Verify Answer button below if it helps answer your question.  Thanks!

  •  Thank you, Anil.

    My current object is to process AD converting from GPP side in Linux, based on the demo project "AD_slave_test" from DSP side(SEED_137 evm hardware test). The main difference between coding from DSP side and from GPP side is register configurations, in my opinion. So I wrote the above-mentioned driver to set registers, and rewrote the AD_slave_test. As I have described, the kernel was crashed and I thought the reason was the driver.

    However, now through debugging I found that I might be wrong and the reason was that the program was running inside a while circle.

    The main body of the oringin AD_slave_test code is:
    /* DMA configuration*/
    GPIO_CLR_DATA45 = 0x00000004;//clear the sync signal
    SECR = 0x00004000; //clear the recieve event
    ICR |= 0x00000001; //clear the flag
    EVMOMAPL137_waitusec(1000);

    GPIO_SET_DATA45 = 0x00000004;//set the sync signal high

    SPI_SPIINT = 0 | ( 1 << 16 ); //DMA enable
    EESR = 0x00004000;
    while((IPR & 0x0000001) == 0);//wait for the end of recieve
    SECR = 0x00004000;
    ICR |= 0x00000001;
    GPIO_CLR_DATA45 = 0x00000004;

    My rewritten code is:
    ioctl(fd, GPIO_CLR_DATA45, 0x00000004);//clear the sync signal
    ioctl(fd, SECR, 0x00004000); //clear the recieve event
    ioctl(fd, ICR); //clear the flag
    wait(100);

    ioctl(fd, GPIO_SET_DATA45, 0x00000004);//set the sync signal high

    ioctl(fd, SPI0INT0); //DMA enable
    ioctl(fd, EESR, 0x00004000);
    while((ioctl(fd, IPR) & 0x00000001) == 0);//wait for the end of recieve
    ioctl(fd, SECR, 0x00004000);
    ioctl(fd, ICR);
    ioctl(fd, GPIO_CLR_DATA45, 0x00000004);

    (The ioctl function is:

    static int adc_ioctl (struct inode *inode,struct file *filp, unsigned int cmd, unsigned long param)
    {
             switch(cmd){
                     case ICR:
                               reg_set(ICR, reg_get(ICR)| 1);
                    case IPR:
                               return reg_get(IPR);
                    case SPI0INT0:
                               reg_set(SPI0INT0, reg_get(SPI0INT0)| ( 1 << 16 ));
                   default:
                               reg_set(cmd, param);
             return 0;
    }
    )

    From CCS window I can view the registers and they are correct as configurated. But the code is still running inside the while circle,why does it happen?

    P.S. I also change the suspend source register "SUSPSRC" from DSP to GPP.(Does it matter?)

  • I assume that your ioctl code is Linux kernel code. Not famiiiar with reg_set() and reg_get(). Are they functions that remap physical memory to virtual memory? Linux does not allow direct access to physical memory. I would guess that ioctl(fd, IPR) is not reaching the IPR location. Likely an access violation has occurred as you are accessing unmapped memory.

    You might want to take a look at the "/dev/mem" interface for accessing physical memory from user space. Source is out there on the web. Search for devmem2.

  • Thank you, Norman Wong.

    The ioctl code is surely Linux kernel code. Registers functions work in kernel space and remap physical address to virtual address ,and the code is:

    static int reg_set(unsigned int reg, unsigned int value)
    {
            void __iomem *regVirt = ioremap(reg, sizeof(reg));
            iowrite32(value, regVirt); 
            iounmap(regVirt);
            return 0;
    }

    So in fact I does not acess the physical address directly. In addition, the ioctl(fd, IPR) returns correct value(I also try other registers and view them in CCS)

  • The reg_set() looks good. A bit unconventional as most drivers would ioremap() the controllers memory range once on load and iounmap() once on unload. But it should work.

    Just noticed the ioctl() code might be missing  breaks for each case. Some cases fall through to other cases. Assuming each case is indepedent, the ioctl() would look something like this:

    static int adc_ioctl (struct inode *inode,struct file *filp, unsigned int cmd, unsigned long param)
    { int status = 0;
      switch(cmd){
        case ICR:
          reg_set(ICR, reg_get(ICR)| 1);
          break;
        case IPR:
          status = reg_get(IPR);
          break;
        case SPI0INT0:
          reg_set(SPI0INT0, reg_get(SPI0INT0)| ( 1 << 16 ));
          break;
        default:
          reg_set(cmd, param);
          break;
      }
      return(status);
    }

    The ioctl() function is a bit unconventional as well. Typically cmds are not used as parameters. Should be okay...never quite sure if the kernel inspects the cmd or it will intercept the cmd before calling your ioctl(). Usually cmd encodes a type(magic number), nr(request number) and direction(read or write).

  • Thank you, Norman Wong.

    Oh I really did forget the breaks, although the cases are indepedent. But the results do not change after adding the breaks... 

  •  I  changed the suspend source register "SUSPSRC" from DSP to GPP.Does it matter?

  • Your code looks functionally correct but I would question how it fits in the kernel. I don't think directly accessing DMA and IRQ registers is good for the kernel. The kernel tends to assume nobody else is modifying those registers. Perhaps you should look for an existing driver. Guessing you want a SPI driver. Linux has a driver called spidev. Never used it myself though. Can't say how to use it.

  • OK, now I think I have to search for specific drivers, such GPIO, SPI, EDMA etc. Maybe I should learn more about the Linux and I took the problem too simple. Setting crucial registers, although through iomap(), may be a bad access.

    The last question, can I directly configurate registers from DSP side in DSPBIOS system(as the origin AD_slave_code demo code)? 

  • It is possible to control SPI, EDMA and GPIOs from the DSP side while Linux executed from the ARM. That is one combination that I have tried. I found that I had to configure Linux and DSPBIOs to ignore each others IRQs and EDMA channels. GPIOs seem okay to be shared without extra effort. I never did get it run cleanly. Occassionally, Linux would be unhappy about seeing the DSPs IRQs. Note that on the DSP side, I used the SPI EDMA driver example from PSPBIOS. And DSPLink in between. The example does not modify the registers directly. I am guessing that your AD_slave_code is "Bare Metal" (no OS) code.

  • Hi Norman

    Thank you for your patience and support.

    Best regards

    David