2

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.

Chung Lun Yuen
  • 319
  • 4
  • 15
  • Is it existing driver or you are creating one from the scratch? Your code has many problems: you missed 0 in the address, you missed `iounmap()` call. In the drivers all above is done in quite different way, though using same `writel()`, `iomap()`/`iounmap()` and alike. – 0andriy Apr 05 '17 at 22:12
  • I'm creating a driver from scratch, for a board I bought online from China, to learn kernel programming. Indeed, I had hard time looking for "standard" documentations for kernel-space functions and macros. I found out the "documentation" I've been looking for is in the sources after greping through them though. Still haven't grasped kernel's internal workings (e.g. what is the point of specifying the size of mapped address in iomap()? will be writing a whole byte / word / long word anyways, etc, etc..), but I guess I'm getting closer each time I try. – Chung Lun Yuen Apr 06 '17 at 13:33
  • Better to just look for the similar driver for other hardware to get a clue. What kind of hardware you are trying to program? – 0andriy Apr 06 '17 at 17:36
  • @0andriy a pwm-regulated buzzer – Chung Lun Yuen Apr 06 '17 at 18:09
  • So, basically you need to look under *drivers/pwm/*. I doubt that's exactly what you need, would be better if you share URL to hardware specifications (datasheet). – 0andriy Apr 06 '17 at 18:12

1 Answers1

5
  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.

fghj
  • 8,898
  • 4
  • 28
  • 56
  • 1. Why "no"? If IO is supposed to be 32-bit the `writel()` is correct command to do that (assuming little endianess). – 0andriy Apr 05 '17 at 22:09
  • @0andriy Because of, as I understand author, he want map exactly 4 bits, not 4 bytes. – fghj Apr 05 '17 at 22:12
  • You got it wrong because OP doesn't know these details. It purely depends on hardware (some allows only 8-bit accesses, some 32-bit, some anything). If you read first sentence 32-bit is mentioned there, so, I assume the hardware needs 32-bit accessors. – 0andriy Apr 05 '17 at 22:15
  • @0andriy But comment in code tell that `// Map the [0:4] bits to TIMER_CON_ADDR` so only authro of question know answer. – fghj Apr 05 '17 at 22:17
  • No, you can't write only 5 bits to the register. It's impossible. Read my above comment carefully. OP doesn't understand these details. – 0andriy Apr 05 '17 at 22:19
  • @0andriy I know, `2.` explains why. I don't get your point, you tell that author think, but if you not author, how can you do it? – fghj Apr 06 '17 at 07:55
  • Again, my point is that your answer to 1. is wrong. I *assume* that register accesses should be 32-bit based on information in the post. – 0andriy Apr 06 '17 at 08:03
  • Oh thank you helping out. What @user1034749 said was correct - I should look at those char kernel modules. They offer examples on how to interact with hardwares. I should `readl()` from the register, modify the bits to what I want, and write values back to the hardware according to which function - `write(l|b|w)` - I use. – Chung Lun Yuen Apr 06 '17 at 13:19
  • @user1034749 @0andriy I just assumed I was correct in the comment `// Map the [0:4] bits to TIMER_CON_ADDR` to avoid long comments. Didn't thought would cause confusion. Sorry. – Chung Lun Yuen Apr 06 '17 at 13:39
  • 3. `git grep` is much better and faster than simple `grep`. – 0andriy Apr 06 '17 at 17:38
  • @ChungLunYuen, it was clear, but the answer to 1 is wrong here. You did a right thing, you just need as explained to read value, modify bits you would like to modify and write back. Writing of 32-bit register is done exactly like you did, i.e. `writel()`. – 0andriy Apr 06 '17 at 17:39
  • @0andriy I think he's trying to tell me I couldn't just `writel()` 4 bit there by emphasizing `writel()` actually writes 32 bits to the hardware. – Chung Lun Yuen Apr 06 '17 at 18:12