19a75a7cdSDipen Patel // SPDX-License-Identifier: GPL-2.0 29a75a7cdSDipen Patel /* 39a75a7cdSDipen Patel * Copyright (c) 2021-2022 NVIDIA Corporation 49a75a7cdSDipen Patel * 59a75a7cdSDipen Patel * Author: Dipen Patel <dipenp@nvidia.com> 69a75a7cdSDipen Patel */ 79a75a7cdSDipen Patel 89a75a7cdSDipen Patel #include <linux/err.h> 99a75a7cdSDipen Patel #include <linux/module.h> 109a75a7cdSDipen Patel #include <linux/moduleparam.h> 119a75a7cdSDipen Patel #include <linux/interrupt.h> 129a75a7cdSDipen Patel #include <linux/gpio.h> 139a75a7cdSDipen Patel #include <linux/timer.h> 149a75a7cdSDipen Patel #include <linux/platform_device.h> 159a75a7cdSDipen Patel #include <linux/workqueue.h> 169a75a7cdSDipen Patel #include <linux/hte.h> 179a75a7cdSDipen Patel 189a75a7cdSDipen Patel /* 199a75a7cdSDipen Patel * This sample HTE GPIO test driver demonstrates HTE API usage by enabling 209a75a7cdSDipen Patel * hardware timestamp on gpio_in and specified LIC IRQ lines. 219a75a7cdSDipen Patel * 229a75a7cdSDipen Patel * Note: gpio_out and gpio_in need to be shorted externally in order for this 239a75a7cdSDipen Patel * test driver to work for the GPIO monitoring. The test driver has been 249a75a7cdSDipen Patel * tested on Jetson AGX Xavier platform by shorting pin 32 and 16 on 40 pin 259a75a7cdSDipen Patel * header. 269a75a7cdSDipen Patel * 279a75a7cdSDipen Patel * Device tree snippet to activate this driver: 289a75a7cdSDipen Patel * tegra_hte_test { 299a75a7cdSDipen Patel * compatible = "nvidia,tegra194-hte-test"; 309a75a7cdSDipen Patel * in-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 1)>; 319a75a7cdSDipen Patel * out-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 0)>; 329a75a7cdSDipen Patel * timestamps = <&tegra_hte_aon TEGRA194_AON_GPIO(BB, 1)>, 339a75a7cdSDipen Patel * <&tegra_hte_lic 0x19>; 349a75a7cdSDipen Patel * timestamp-names = "hte-gpio", "hte-i2c-irq"; 359a75a7cdSDipen Patel * status = "okay"; 369a75a7cdSDipen Patel * }; 379a75a7cdSDipen Patel * 389a75a7cdSDipen Patel * How to run test driver: 399a75a7cdSDipen Patel * - Load test driver. 409a75a7cdSDipen Patel * - For the GPIO, at regular interval gpio_out pin toggles triggering 419a75a7cdSDipen Patel * HTE for rising edge on gpio_in pin. 429a75a7cdSDipen Patel * 439a75a7cdSDipen Patel * - For the LIC IRQ line, it uses 0x19 interrupt which is i2c controller 1. 449a75a7cdSDipen Patel * - Run i2cdetect -y 1 1>/dev/null, this command will generate i2c bus 459a75a7cdSDipen Patel * transactions which creates timestamp data. 469a75a7cdSDipen Patel * - It prints below message for both the lines. 479a75a7cdSDipen Patel * HW timestamp(<line id>:<ts seq number>): <timestamp>, edge: <edge>. 489a75a7cdSDipen Patel * - Unloading the driver disables and deallocate the HTE. 499a75a7cdSDipen Patel */ 509a75a7cdSDipen Patel 519a75a7cdSDipen Patel static struct tegra_hte_test { 529a75a7cdSDipen Patel int gpio_in_irq; 539a75a7cdSDipen Patel struct device *pdev; 549a75a7cdSDipen Patel struct gpio_desc *gpio_in; 559a75a7cdSDipen Patel struct gpio_desc *gpio_out; 569a75a7cdSDipen Patel struct hte_ts_desc *desc; 579a75a7cdSDipen Patel struct timer_list timer; 589a75a7cdSDipen Patel struct kobject *kobj; 599a75a7cdSDipen Patel } hte; 609a75a7cdSDipen Patel 619a75a7cdSDipen Patel static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) 629a75a7cdSDipen Patel { 639a75a7cdSDipen Patel char *edge; 649a75a7cdSDipen Patel struct hte_ts_desc *desc = p; 659a75a7cdSDipen Patel 669a75a7cdSDipen Patel if (!ts || !p) 679a75a7cdSDipen Patel return HTE_CB_HANDLED; 689a75a7cdSDipen Patel 699a75a7cdSDipen Patel if (ts->raw_level < 0) 709a75a7cdSDipen Patel edge = "Unknown"; 719a75a7cdSDipen Patel 729a75a7cdSDipen Patel pr_info("HW timestamp(%u: %llu): %llu, edge: %s\n", 739a75a7cdSDipen Patel desc->attr.line_id, ts->seq, ts->tsc, 749a75a7cdSDipen Patel (ts->raw_level >= 0) ? ((ts->raw_level == 0) ? 759a75a7cdSDipen Patel "falling" : "rising") : edge); 769a75a7cdSDipen Patel 779a75a7cdSDipen Patel return HTE_CB_HANDLED; 789a75a7cdSDipen Patel } 799a75a7cdSDipen Patel 809a75a7cdSDipen Patel static void gpio_timer_cb(struct timer_list *t) 819a75a7cdSDipen Patel { 829a75a7cdSDipen Patel (void)t; 839a75a7cdSDipen Patel 849a75a7cdSDipen Patel gpiod_set_value(hte.gpio_out, !gpiod_get_value(hte.gpio_out)); 859a75a7cdSDipen Patel mod_timer(&hte.timer, jiffies + msecs_to_jiffies(8000)); 869a75a7cdSDipen Patel } 879a75a7cdSDipen Patel 889a75a7cdSDipen Patel static irqreturn_t tegra_hte_test_gpio_isr(int irq, void *data) 899a75a7cdSDipen Patel { 909a75a7cdSDipen Patel (void)irq; 919a75a7cdSDipen Patel (void)data; 929a75a7cdSDipen Patel 939a75a7cdSDipen Patel return IRQ_HANDLED; 949a75a7cdSDipen Patel } 959a75a7cdSDipen Patel 969a75a7cdSDipen Patel static const struct of_device_id tegra_hte_test_of_match[] = { 979a75a7cdSDipen Patel { .compatible = "nvidia,tegra194-hte-test"}, 989a75a7cdSDipen Patel { } 999a75a7cdSDipen Patel }; 1009a75a7cdSDipen Patel MODULE_DEVICE_TABLE(of, tegra_hte_test_of_match); 1019a75a7cdSDipen Patel 1029a75a7cdSDipen Patel static int tegra_hte_test_probe(struct platform_device *pdev) 1039a75a7cdSDipen Patel { 1049a75a7cdSDipen Patel int ret = 0; 1059a75a7cdSDipen Patel int i, cnt; 1069a75a7cdSDipen Patel 1079a75a7cdSDipen Patel dev_set_drvdata(&pdev->dev, &hte); 1089a75a7cdSDipen Patel hte.pdev = &pdev->dev; 1099a75a7cdSDipen Patel 1109a75a7cdSDipen Patel hte.gpio_out = gpiod_get(&pdev->dev, "out", 0); 1119a75a7cdSDipen Patel if (IS_ERR(hte.gpio_out)) { 1129a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to get gpio out\n"); 1139a75a7cdSDipen Patel ret = -EINVAL; 1149a75a7cdSDipen Patel goto out; 1159a75a7cdSDipen Patel } 1169a75a7cdSDipen Patel 1179a75a7cdSDipen Patel hte.gpio_in = gpiod_get(&pdev->dev, "in", 0); 1189a75a7cdSDipen Patel if (IS_ERR(hte.gpio_in)) { 1199a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to get gpio in\n"); 1209a75a7cdSDipen Patel ret = -EINVAL; 1219a75a7cdSDipen Patel goto free_gpio_out; 1229a75a7cdSDipen Patel } 1239a75a7cdSDipen Patel 1249a75a7cdSDipen Patel ret = gpiod_direction_output(hte.gpio_out, 0); 1259a75a7cdSDipen Patel if (ret) { 1269a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to set output\n"); 1279a75a7cdSDipen Patel ret = -EINVAL; 1289a75a7cdSDipen Patel goto free_gpio_in; 1299a75a7cdSDipen Patel } 1309a75a7cdSDipen Patel 1319a75a7cdSDipen Patel ret = gpiod_direction_input(hte.gpio_in); 1329a75a7cdSDipen Patel if (ret) { 1339a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to set input\n"); 1349a75a7cdSDipen Patel ret = -EINVAL; 1359a75a7cdSDipen Patel goto free_gpio_in; 1369a75a7cdSDipen Patel } 1379a75a7cdSDipen Patel 1389a75a7cdSDipen Patel ret = gpiod_to_irq(hte.gpio_in); 1399a75a7cdSDipen Patel if (ret < 0) { 1409a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret); 1419a75a7cdSDipen Patel ret = -ENXIO; 1429a75a7cdSDipen Patel goto free_gpio_in; 1439a75a7cdSDipen Patel } 1449a75a7cdSDipen Patel 1459a75a7cdSDipen Patel hte.gpio_in_irq = ret; 1469a75a7cdSDipen Patel ret = request_irq(ret, tegra_hte_test_gpio_isr, 1479a75a7cdSDipen Patel IRQF_TRIGGER_RISING, 1489a75a7cdSDipen Patel "tegra_hte_gpio_test_isr", &hte); 1499a75a7cdSDipen Patel if (ret) { 1509a75a7cdSDipen Patel dev_err(&pdev->dev, "failed to acquire IRQ\n"); 1519a75a7cdSDipen Patel ret = -ENXIO; 1529a75a7cdSDipen Patel goto free_irq; 1539a75a7cdSDipen Patel } 1549a75a7cdSDipen Patel 1559a75a7cdSDipen Patel cnt = of_hte_req_count(hte.pdev); 1569a75a7cdSDipen Patel if (cnt < 0) 1579a75a7cdSDipen Patel goto free_irq; 1589a75a7cdSDipen Patel 1599a75a7cdSDipen Patel dev_info(&pdev->dev, "Total requested lines:%d\n", cnt); 1609a75a7cdSDipen Patel 1619a75a7cdSDipen Patel hte.desc = devm_kzalloc(hte.pdev, sizeof(*hte.desc) * cnt, GFP_KERNEL); 1629a75a7cdSDipen Patel if (!hte.desc) { 1639a75a7cdSDipen Patel ret = -ENOMEM; 1649a75a7cdSDipen Patel goto free_irq; 1659a75a7cdSDipen Patel } 1669a75a7cdSDipen Patel 1679a75a7cdSDipen Patel for (i = 0; i < cnt; i++) { 1689a75a7cdSDipen Patel if (i == 0) 1699a75a7cdSDipen Patel /* 1709a75a7cdSDipen Patel * GPIO hte init, line_id and name will be parsed from 1719a75a7cdSDipen Patel * the device tree node. The edge_flag is implicitly 1729a75a7cdSDipen Patel * set by request_irq call. Only line_data is needed to be 1739a75a7cdSDipen Patel * set. 1749a75a7cdSDipen Patel */ 1759a75a7cdSDipen Patel hte_init_line_attr(&hte.desc[i], 0, 0, NULL, 1769a75a7cdSDipen Patel hte.gpio_in); 1779a75a7cdSDipen Patel else 1789a75a7cdSDipen Patel /* 1799a75a7cdSDipen Patel * same comment as above except that IRQ does not need 1809a75a7cdSDipen Patel * line data. 1819a75a7cdSDipen Patel */ 1829a75a7cdSDipen Patel hte_init_line_attr(&hte.desc[i], 0, 0, NULL, NULL); 1839a75a7cdSDipen Patel 1849a75a7cdSDipen Patel ret = hte_ts_get(hte.pdev, &hte.desc[i], i); 1859a75a7cdSDipen Patel if (ret) 1869a75a7cdSDipen Patel goto ts_put; 1879a75a7cdSDipen Patel 1889a75a7cdSDipen Patel ret = devm_hte_request_ts_ns(hte.pdev, &hte.desc[i], 1899a75a7cdSDipen Patel process_hw_ts, NULL, 1909a75a7cdSDipen Patel &hte.desc[i]); 1919a75a7cdSDipen Patel if (ret) /* no need to ts_put, request API takes care */ 1929a75a7cdSDipen Patel goto free_irq; 1939a75a7cdSDipen Patel } 1949a75a7cdSDipen Patel 1959a75a7cdSDipen Patel timer_setup(&hte.timer, gpio_timer_cb, 0); 1969a75a7cdSDipen Patel mod_timer(&hte.timer, jiffies + msecs_to_jiffies(5000)); 1979a75a7cdSDipen Patel 1989a75a7cdSDipen Patel return 0; 1999a75a7cdSDipen Patel 2009a75a7cdSDipen Patel ts_put: 2019a75a7cdSDipen Patel cnt = i; 2029a75a7cdSDipen Patel for (i = 0; i < cnt; i++) 2039a75a7cdSDipen Patel hte_ts_put(&hte.desc[i]); 2049a75a7cdSDipen Patel free_irq: 2059a75a7cdSDipen Patel free_irq(hte.gpio_in_irq, &hte); 2069a75a7cdSDipen Patel free_gpio_in: 2079a75a7cdSDipen Patel gpiod_put(hte.gpio_in); 2089a75a7cdSDipen Patel free_gpio_out: 2099a75a7cdSDipen Patel gpiod_put(hte.gpio_out); 2109a75a7cdSDipen Patel out: 2119a75a7cdSDipen Patel 2129a75a7cdSDipen Patel return ret; 2139a75a7cdSDipen Patel } 2149a75a7cdSDipen Patel 2159a75a7cdSDipen Patel static int tegra_hte_test_remove(struct platform_device *pdev) 2169a75a7cdSDipen Patel { 2179a75a7cdSDipen Patel (void)pdev; 2189a75a7cdSDipen Patel 2199a75a7cdSDipen Patel free_irq(hte.gpio_in_irq, &hte); 2209a75a7cdSDipen Patel gpiod_put(hte.gpio_in); 2219a75a7cdSDipen Patel gpiod_put(hte.gpio_out); 222*0668e8ccSYang Yingliang del_timer_sync(&hte.timer); 2239a75a7cdSDipen Patel 2249a75a7cdSDipen Patel return 0; 2259a75a7cdSDipen Patel } 2269a75a7cdSDipen Patel 2279a75a7cdSDipen Patel static struct platform_driver tegra_hte_test_driver = { 2289a75a7cdSDipen Patel .probe = tegra_hte_test_probe, 2299a75a7cdSDipen Patel .remove = tegra_hte_test_remove, 2309a75a7cdSDipen Patel .driver = { 2319a75a7cdSDipen Patel .name = "tegra_hte_test", 2329a75a7cdSDipen Patel .of_match_table = tegra_hte_test_of_match, 2339a75a7cdSDipen Patel }, 2349a75a7cdSDipen Patel }; 2359a75a7cdSDipen Patel module_platform_driver(tegra_hte_test_driver); 2369a75a7cdSDipen Patel 2379a75a7cdSDipen Patel MODULE_AUTHOR("Dipen Patel <dipenp@nvidia.com>"); 2389a75a7cdSDipen Patel MODULE_LICENSE("GPL"); 239