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:
[0:4]
bits to TIMER_CON_ADDR correctly?write(1|w|b)
functions to write bits (0100
) to TIMER_CON_ADDR in the correct order?write(l|w|b)
do under the hood to write bits?Thanks for all your help in advance.
- Did I map the
[0:4]
bits to TIMER_CON_ADDR correctly?
no, you write 32bits, writel
write 4 bytes, 4 * 8 = 32 bits
- 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.
- 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);
- 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
- 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.