Search code examples
clinux-kernelkernel-modulegpioioremap

Using writel to write 4 bit to ioremap-ed memory address


I'm new to kernel programming, and now trying to write some values to a 32-bit GPIO register in a device driver. The I/O is ioremap()-ed to a memory address. The problem is, I don't know how writel()/writeb()/writew() writes bits to the addresses.

The vendor documents says the register is on 0xE5200000. The bits I have to write to is the [0:3] bits and leave the remaining 28 bits (the [4:31] bits) as zeros.

This is part of the code in the device driver I've written so far:

#define TCON_ADDR 0xE250000 // The address as provided by the vendor
static void *TIMER_CON_ADDR;
// I have to map and write to the address when the device is opened
static int on_dev_open(struct inode *inode, struct file *file) {
    unsigned int data;
    TIMER_CON_ADDR = ioremap(TCON_ADDR, 4); // Map the [0:4] bits to TIMER_CON_ADDR
    data = 4; // 0100 in binary
    writel(data, TIMER_CON_ADDR); // Write 0100 to TIMER_CON_ADDR
    return 0;
}

The above code might just be outright gibberish to you all, but I am not familiar with write(l|w|b) and ioremap().

So my questions are:

  1. Did I map the [0:4] bits to TIMER_CON_ADDR correctly?
  2. If not, how do I map them correctly?
  3. After I have correctly mapped the 4 bits, how do I use any of the write(1|w|b) functions to write bits (0100) to TIMER_CON_ADDR in the correct order?
  4. What does write(l|w|b) do under the hood to write bits?
  5. Is there any information I've missed / got wrong?

Thanks for all your help in advance.


Solution

    1. Did I map the [0:4] bits to TIMER_CON_ADDR correctly?

    no, you write 32bits, writel write 4 bytes, 4 * 8 = 32 bits

    1. If not, how do I map them correctly?

    No way to map 4 bits, minimum 8 bits = 1 bytes, but if you work with 32bit register you need map 32 bits = 4 bytes. Also do not forget check and handle errors.

    1. After I have correctly mapped the 4 bits, how do I use any of the write(1|w|b) functions to write bits (0100) to TIMER_CON_ADDR in the correct order?

    you need use readl, kernel full of examples, just run grep inside drivers subdirectory of linux kernel source tree. General idea read/write:

    u32 reg = readl(TIMER_CON_ADDR);
    reg &= ~0xfu;
    reg |= 4;
    writel(reg, TIMER_CON_ADDR);
    
    1. What does write(l|w|b) do under the hood to write bits?

    look at source code, it just simple C functions, like:

    static inline void __raw_writel(u32 value, volatile void __iomem *addr)
    {
        *(volatile u32 __force *)addr = value;
    }
    

    the main idea is telling to compiler that it should not remove your memory reading/writing

    1. Is there any information I've missed / got wrong?

    read source code of similar drivers, it is already contains almost all solutions for such simple drivers.