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.

[参考译文] SK-AM62:Linux 上 eQEP 的监控事件

Guru**** 2378660 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1500510/sk-am62-monitoring-event-of-eqep-on-linux

器件型号:SK-AM62

工具/软件:

您好、

我们正在使用 am62x 和 Processor SDK 09.02.01.10开发器件。

是否有办法监测 eQEP 发出的事件(例如 COUNTER_EVENT_OVERFLOW、COUNTER_EVENT_UNDERFLOW)?

根据下一页、这可以通过使用 COUNTER_ADD_WATCH_IOCTL 来实现。

https://docs.kernel.org/driver-api/generic-counter.html#userspace

当我尝试在 linux-ti-staging 中使用 tools/counter/counter_example.c 获取溢出事件时、内核无法读取该事件并显示以下消息。

"无法处理虚拟地址0000000000000010处的内核 NULL 指针解除引用"

如果您有任何建议或想法、请告诉我。

附加了 counter_example.c 的源代码。

// SPDX-License-Identifier: GPL-2.0-only
/* Counter - example userspace application
 *
 * The userspace application opens /dev/counter0, configures the
 * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
 * 1 count, and prints out the data as it becomes available on the
 * character device node.
 *
 * Copyright (C) 2021 William Breathitt Gray
 */
#include <errno.h>
#include <fcntl.h>
#include <linux/counter.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <poll.h>

static struct counter_watch watches[1] = {
	{
		/* Component data: Count 0 count */
		.component.type = COUNTER_COMPONENT_COUNT,
		.component.scope = COUNTER_SCOPE_COUNT,
		.component.parent = 0,
		/* Event type: overflow */
		.event = COUNTER_EVENT_OVERFLOW,
		/* Device event channel 0 */
		.channel = 0,
	},
};

int main(void)
{
	int fd;
	int ret;
	int i;
	struct counter_event event_data[1];

	fd = open("/dev/counter0", O_RDWR);
	if (fd == -1) {
		perror("Unable to open /dev/counter0");
		return 1;
	}
	for (i = 0; i < 1; i++) {
		ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i);
		if (ret == -1) {
			fprintf(stderr, "Error adding watches[%d]: %s\n", i,
				strerror(errno));
			return 1;
		}
	}
	ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);

	if (ret == -1) {
		perror("Error enabling events");
		return 1;
	}
	struct pollfd fds = {
            .fd = fd,
            .events = POLLIN,
        };
	for (;;) {
		printf("polling\n");
		int ret = poll(&fds, 1, -1);
		if (ret >= 1) {

			printf("read start\n");
			ret = read(fd, event_data, sizeof(event_data));
			printf("read end\n");
			if (ret == -1) {
				perror("Failed to read event data");
				return 1;
			}

			if (ret != sizeof(event_data)) {
				fprintf(stderr, "Failed to read event data\n");
				return -EIO;
			}

			printf("Timestamp 0: %llu\tCount 0: %llu\n"
			       "Error Message 0: %s\n",
			       event_data[0].timestamp, event_data[0].value,
			       strerror(event_data[0].status));
		} else {
			perror("poll()");
			close(fd);
			return -1;
		}
	}

	return 0;
}

此致、

Takayuki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你好、 Takayuki-San、

    首先要设定期望、我们在编写自定义用户空间代码时所能提供的支持将受到限制。

    ti-eQEP 驱动程序似乎提供了配置 COUNTER_EVENT_OVERFLOW 和 COUNTER_EVENT_UNDERFLOW 事件 的选项:
    drivers/counter/ti-eQEP-c

    static int ti_eqep_events_configure(struct counter_device *counter)
    {
            struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter);
            struct counter_event_node *event_node;
            u32 qeint = 0;
    
            list_for_each_entry(event_node, &counter->events_list, l) {
                    switch (event_node->event) {
                    case COUNTER_EVENT_OVERFLOW:
                            qeint |= QEINT_PCO;
                            break;
                    case COUNTER_EVENT_UNDERFLOW:
                            qeint |= QEINT_PCU;
                            break;
                    case COUNTER_EVENT_DIRECTION_CHANGE:
                            qeint |= QEINT_QDC;
                            break;
                    case COUNTER_EVENT_TIMEOUT:
                            qeint |= QEINT_UTO;
                            break;
                    }
            }
    
            return regmap_write_bits(priv->regmap16, QEINT, qeint, ~0);
    }
    

    我可以看到一些 sysfs 条目使用上溢和下溢的位置:
    https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-counter

    但我无法就如何实施基于通用计数器 API 的事件提供指导。

    我假设您已经找到此页面、但未来的读者可以在此处找到有关 eQEP 驱动程序的 TI 文档:
    https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/09_02_01_10/exports/docs/linux/Foundational_Components、Kernel_Drivers Kernel/Kernel/EQEP.html

    此致、

    Nick

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Nick-San、
    感谢您的答复。
    为了监视该事件、我要检查是否调用了 eQEP 驱动程序中设置的中断。
    当发生溢出时、中断函数似乎没有被调用。
    下面是检查方法。  
    1.配置设备树
    2.将 printk 插入 eQEP 驱动程序的 ti_eQEP_IRQ_HANDLER 中。 编译和部署
    3.连接连接器 EXP_GPIO0_41和 EQEP0_A
    4.在高和低之间切换 GPIO
    5.确认/sys/bus/counter/devices/counter0/count0/count 已递增。
    6.重复上述步骤并确认当 count 达到/sys/bus/counter/devices/counter0/count0/ceiling.的值时、它将返回0
    当它达到6时、我在 printk 中输入的字符不会被打印。
    我正在连接设备树设置、因此如果缺少任何设置、请告知我。
     
    &main_pmx0 {
            main_eqep0_pins_default: main-eqep0-pins-default {
                    pinctrl-single,pins = <
                            AM62X_IOPAD(0x0194, PIN_INPUT, 8) /* (B19) MCASP0_AXR3.EQEP0_A */
                            AM62X_IOPAD(0x0198, PIN_INPUT, 8) /* (A19) MCASP0_AXR2.EQEP0_B */
                            AM62X_IOPAD(0x01a0, PIN_INPUT, 8) /* (E18) MCASP0_AXR0.EQEP0_I */
                            AM62X_IOPAD(0x019c, PIN_INPUT, 8) /* (B18) MCASP0_AXR1.EQEP0_S */
                    >;
            };
    
            main_gpio0_pins_default: main-gpio0-pins-default {
                    pinctrl-single,pins = <
                            AM62X_IOPAD(0x00a8, PIN_OUTPUT, 7) /* (J18) GPMC0_CSn0.GPIO0_41 */
                    >;
            };
    
    &eqep0 {
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <&main_eqep0_pins_default>;
    };
    
    &main_gpio0 {
            pinctrl-names = "default";
            pinctrl-0 = <&main_gpio0_pins_default>;
    };
    此致、
    Takayuki
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你好、Takayuki-San、

    看起来驱动程序正在写入正确的寄存器地址。

    我不知道这是否是从驱动程序调用.events_configure 的正确方法:
    RET = ioctl (fd、counter_add_watch_IOCTL、watches + I);

    请在您期望设置 QEINT 寄存器后使用 devmem2等工具来检查该寄存器的值。
    我预计从 0x0023200000到偏移量0x30

    此致、

    Nick

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Nick-San、

    感谢您的答复。

    我可以使用 devmem2设置 QEINT 来调用中断函数。

    但是,COUNTER_Push_event()函数中会出现 NULL 指针解除引用错误。

    有没有办法解决这个问题?

    此致、

    Takayuki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你好、Takayuki-San、

    我花了几天时间再看看这个。

    为了进行确认、您没有看到用户空间代码设置的 QEINT 寄存器值、对吧?

    何时发生 NULL 指针解除引用错误? 我可以查看终端输出吗?

    此致、

    Nick

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Nick-San、

    以下是重现此问题的步骤。

    运行以下命令:

    echo 461 > /sys/class/gpio/export
    echo out > /sys/class/gpio/gpio461/direction
    echo 0 > /sys/class/gpio/gpio461/value
    
    echo increase > /sys/bus/counter/devices/counter0/count0/function
    echo 2 > /sys/bus/counter/devices/counter0/count0/ceiling
    echo 1 > /sys/bus/counter/devices/counter0/count0/enable

    使用 devmem2设置 QEINT 的 PCO。

    root@am62xx-evm:~# devmem2 0x0023200030 l 0x00000040
    /dev/mem opened.
    Memory mapped at address 0xffff96f67000.
    Read at address 0x23200030 (0xffff96f67030): 0x0000000000000000
    Write at address 0x23200030 (0xffff96f67030): 0x0000000000000040, readback 0x0000000000000040

    使用以下命令在高电平和低电平之间切换 GPIO 将生成溢出事件。

    此时、控制台上将显示内核错误。

    echo 1 > /sys/class/gpio/gpio461/value
    echo 0 > /sys/class/gpio/gpio461/value
    echo 1 > /sys/class/gpio/gpio461/value

    如果在内核驱动程序中添加了设置 QEINT 的进程、则会发生相同的错误。

    此致、

    Takayuki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Nick-San、

    我找到了解决问题的方法。  以下补丁将修复该错误。

    diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
    index b33e94f82c01..50436e22fbf4 100644
    --- a/drivers/counter/ti-eqep.c
    +++ b/drivers/counter/ti-eqep.c
    @@ -765,8 +765,8 @@ static struct counter_comp ti_eqep_device_ext[] = {
     
     static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id)
     {
    -	struct ti_eqep_cnt *priv = dev_id;
    -	struct counter_device *counter = &priv->counter;
    +	struct counter_device *counter = dev_id;
    +	struct ti_eqep_cnt *priv = counter_priv(counter);
     	u32 qflg;
     	u32 qclr = 0;
     
    @@ -861,7 +861,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
     		return irq;
     
     	err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler,
    -					IRQF_ONESHOT, dev_name(dev), priv);
    +					IRQF_ONESHOT, dev_name(dev), counter);
     	if (err < 0)
     		return err;
     
    

    这样我们就可以获取计数器事件。

    您能否确认此补丁的有效性?

    此致、

    Takayuki