【问题标题】:Using writel to write 4 bit to ioremap-ed memory address使用 writel 将 4 位写入 ioremap-ed 内存地址
【发布时间】:2017-04-02 05:36:36
【问题描述】:

我是内核编程的新手,现在尝试将一些值写入设备驱动程序中的 32 位 GPIO 寄存器。 I/O 是ioremap()-ed 到内存地址。问题是,我不知道writel()/writeb()/writew() 是如何将位写入地址的。

供应商文件说注册在0xE5200000。我必须写入的位是 [0:3] 位,其余 28 位([4:31] 位)为零。

这是我目前编写的设备驱动程序中的部分代码:

#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;
}

上面的代码对你们来说可能是彻头彻尾的胡言乱语,但我不熟悉write(l|w|b)ioremap()

所以我的问题是:

  1. 我是否将[0:4] 位正确映射到TIMER_CON_ADDR?
  2. 如果没有,我该如何正确映射它们?
  3. 正确映射 4 位后,如何使用任何 write(1|w|b) 函数以正确的顺序将位 (0100) 写入 TIMER_CON_ADDR?
  4. write(l|w|b) 在后台做了什么来写入位?
  5. 我是否遗漏/弄错了任何信息?

提前感谢您的所有帮助。

【问题讨论】:

  • 它是现有的驱动程序还是您正在从头开始创建一个?你的代码有很多问题:你错过了地址中的 0,你错过了iounmap() 电话。在驱动程序中,尽管使用相同的writel()iomap()/iounmap() 等,但上述所有操作都以完全不同的方式完成。
  • 我正在为我从中国在线购买的一块板子从头开始创建一个驱动程序,以学习内核编程。事实上,我很难为内核空间函数和宏寻找“标准”文档。不过,我发现我一直在寻找的“文档”在源代码中。仍然没有掌握内核的内部工作原理(例如,在 iomap() 中指定映射地址的大小有什么意义?无论如何都会写一个完整的字节/字/长字等等),但我想我每次我尝试都会越来越近。
  • 最好只为其他硬件寻找类似的驱动程序以获得线索。您正在尝试编程哪种硬件?
  • @0andriy 一个 pwm 调节的蜂鸣器
  • 所以,基本上你需要在drivers/pwm/下查看。我怀疑这正是您所需要的,如果您共享硬件规格(数据表)的 URL 会更好。

标签: c linux-kernel kernel-module gpio ioremap


【解决方案1】:
  1. 我是否将[0:4] 位正确映射到TIMER_CON_ADDR?

不,你写的是 32 位,writel 写的是 4 字节,4 * 8 = 32 位

  1. 如果没有,我该如何正确映射它们?

无法映射 4 位,最少 8 位 = 1 字节,但如果您使用 32 位 注册您需要映射 32 位 = 4 字节。也不要忘记检查和处理 错误。

  1. 正确映射 4 位后,如何使用任何 write(1|w|b) 函数以正确的顺序将位 (0100) 写入 TIMER_CON_ADDR?

你需要使用readl,内核充满了例子,只需在linux内核源代码树的drivers子目录中运行grep。 总体思路读/写:

u32 reg = readl(TIMER_CON_ADDR);
reg &= ~0xfu;
reg |= 4;
writel(reg, TIMER_CON_ADDR);
  1. write(l|w|b) 在后台做了什么来写入位?

查看源代码,它只是简单的 C 函数,例如:

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

主要思想是告诉编译器它不应该删除 你的记忆读/写

  1. 我是否遗漏/弄错了任何信息?

阅读类似驱动的源码,已经包含差不多了 此类简单驱动程序的所有解决方案。

【讨论】:

  • 1.为什么是“不”?如果 IO 应该是 32 位的,writel() 是正确的命令(假设很少字节序)。
  • @0andriy 因为据我了解作者,他想要映射 4 位,而不是 4 字节。
  • 你错了,因为 OP 不知道这些细节。它完全取决于硬件(一些只允许 8 位访问,一些 32 位,一些任何东西)。如果你读到第一句 32-bit 被提及,那么,我假设硬件需要 32-bit 访问器。
  • @0andriy 但是代码中的注释告诉// Map the [0:4] bits to TIMER_CON_ADDR,所以只有问题的作者知道答案。
  • 不,您不能只向寄存器写入 5 位。不可能。仔细阅读我上面的评论。 OP 不明白这些细节。
猜你喜欢
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
  • 2022-11-28
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
  • 2014-11-27
相关资源
最近更新 更多