1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2021-2022 NVIDIA Corporation 4 * 5 * Author: Dipen Patel <dipenp@nvidia.com> 6 */ 7 8 #include <linux/err.h> 9 #include <linux/gpio/consumer.h> 10 #include <linux/hte.h> 11 #include <linux/interrupt.h> 12 #include <linux/mod_devicetable.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/timer.h> 16 #include <linux/workqueue.h> 17 18 /* 19 * This sample HTE test driver demonstrates HTE API usage by enabling 20 * hardware timestamp on gpio_in and specified LIC IRQ lines. 21 * 22 * Note: gpio_out and gpio_in need to be shorted externally in order for this 23 * test driver to work for the GPIO monitoring. The test driver has been 24 * tested on Jetson AGX Xavier platform by shorting pin 32 and 16 on 40 pin 25 * header. 26 * 27 * Device tree snippet to activate this driver: 28 * tegra_hte_test { 29 * compatible = "nvidia,tegra194-hte-test"; 30 * in-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 1)>; 31 * out-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 0)>; 32 * timestamps = <&tegra_hte_aon TEGRA194_AON_GPIO(BB, 1)>, 33 * <&tegra_hte_lic 0x19>; 34 * timestamp-names = "hte-gpio", "hte-i2c-irq"; 35 * status = "okay"; 36 * }; 37 * 38 * How to run test driver: 39 * - Load test driver. 40 * - For the GPIO, at regular interval gpio_out pin toggles triggering 41 * HTE for rising edge on gpio_in pin. 42 * 43 * - For the LIC IRQ line, it uses 0x19 interrupt which is i2c controller 1. 44 * - Run i2cdetect -y 1 1>/dev/null, this command will generate i2c bus 45 * transactions which creates timestamp data. 46 * - It prints below message for both the lines. 47 * HW timestamp(<line id>:<ts seq number>): <timestamp>, edge: <edge>. 48 * - Unloading the driver disables and deallocate the HTE. 49 */ 50 51 static struct tegra_hte_test { 52 int gpio_in_irq; 53 struct device *pdev; 54 struct gpio_desc *gpio_in; 55 struct gpio_desc *gpio_out; 56 struct hte_ts_desc *desc; 57 struct timer_list timer; 58 struct kobject *kobj; 59 } hte; 60 61 static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) 62 { 63 char *edge; 64 struct hte_ts_desc *desc = p; 65 66 if (!ts || !p) 67 return HTE_CB_HANDLED; 68 69 if (ts->raw_level < 0) 70 edge = "Unknown"; 71 72 pr_info("HW timestamp(%u: %llu): %llu, edge: %s\n", 73 desc->attr.line_id, ts->seq, ts->tsc, 74 (ts->raw_level >= 0) ? ((ts->raw_level == 0) ? 75 "falling" : "rising") : edge); 76 77 return HTE_CB_HANDLED; 78 } 79 80 static void gpio_timer_cb(struct timer_list *t) 81 { 82 (void)t; 83 84 gpiod_set_value(hte.gpio_out, !gpiod_get_value(hte.gpio_out)); 85 mod_timer(&hte.timer, jiffies + msecs_to_jiffies(8000)); 86 } 87 88 static irqreturn_t tegra_hte_test_gpio_isr(int irq, void *data) 89 { 90 (void)irq; 91 (void)data; 92 93 return IRQ_HANDLED; 94 } 95 96 static const struct of_device_id tegra_hte_test_of_match[] = { 97 { .compatible = "nvidia,tegra194-hte-test"}, 98 { } 99 }; 100 MODULE_DEVICE_TABLE(of, tegra_hte_test_of_match); 101 102 static int tegra_hte_test_probe(struct platform_device *pdev) 103 { 104 int ret = 0; 105 int i, cnt; 106 107 dev_set_drvdata(&pdev->dev, &hte); 108 hte.pdev = &pdev->dev; 109 110 hte.gpio_out = gpiod_get(&pdev->dev, "out", 0); 111 if (IS_ERR(hte.gpio_out)) { 112 dev_err(&pdev->dev, "failed to get gpio out\n"); 113 ret = -EINVAL; 114 goto out; 115 } 116 117 hte.gpio_in = gpiod_get(&pdev->dev, "in", 0); 118 if (IS_ERR(hte.gpio_in)) { 119 dev_err(&pdev->dev, "failed to get gpio in\n"); 120 ret = -EINVAL; 121 goto free_gpio_out; 122 } 123 124 ret = gpiod_direction_output(hte.gpio_out, 0); 125 if (ret) { 126 dev_err(&pdev->dev, "failed to set output\n"); 127 ret = -EINVAL; 128 goto free_gpio_in; 129 } 130 131 ret = gpiod_direction_input(hte.gpio_in); 132 if (ret) { 133 dev_err(&pdev->dev, "failed to set input\n"); 134 ret = -EINVAL; 135 goto free_gpio_in; 136 } 137 138 ret = gpiod_to_irq(hte.gpio_in); 139 if (ret < 0) { 140 dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret); 141 ret = -ENXIO; 142 goto free_gpio_in; 143 } 144 145 hte.gpio_in_irq = ret; 146 ret = request_irq(ret, tegra_hte_test_gpio_isr, 147 IRQF_TRIGGER_RISING, 148 "tegra_hte_gpio_test_isr", &hte); 149 if (ret) { 150 dev_err(&pdev->dev, "failed to acquire IRQ\n"); 151 ret = -ENXIO; 152 goto free_irq; 153 } 154 155 cnt = of_hte_req_count(hte.pdev); 156 if (cnt < 0) 157 goto free_irq; 158 159 dev_info(&pdev->dev, "Total requested lines:%d\n", cnt); 160 161 hte.desc = devm_kzalloc(hte.pdev, sizeof(*hte.desc) * cnt, GFP_KERNEL); 162 if (!hte.desc) { 163 ret = -ENOMEM; 164 goto free_irq; 165 } 166 167 for (i = 0; i < cnt; i++) { 168 if (i == 0) 169 /* 170 * GPIO hte init, line_id and name will be parsed from 171 * the device tree node. The edge_flag is implicitly 172 * set by request_irq call. Only line_data is needed to be 173 * set. 174 */ 175 hte_init_line_attr(&hte.desc[i], 0, 0, NULL, 176 hte.gpio_in); 177 else 178 /* 179 * same comment as above except that IRQ does not need 180 * line data. 181 */ 182 hte_init_line_attr(&hte.desc[i], 0, 0, NULL, NULL); 183 184 ret = hte_ts_get(hte.pdev, &hte.desc[i], i); 185 if (ret) 186 goto ts_put; 187 188 ret = devm_hte_request_ts_ns(hte.pdev, &hte.desc[i], 189 process_hw_ts, NULL, 190 &hte.desc[i]); 191 if (ret) /* no need to ts_put, request API takes care */ 192 goto free_irq; 193 } 194 195 timer_setup(&hte.timer, gpio_timer_cb, 0); 196 mod_timer(&hte.timer, jiffies + msecs_to_jiffies(5000)); 197 198 return 0; 199 200 ts_put: 201 cnt = i; 202 for (i = 0; i < cnt; i++) 203 hte_ts_put(&hte.desc[i]); 204 free_irq: 205 free_irq(hte.gpio_in_irq, &hte); 206 free_gpio_in: 207 gpiod_put(hte.gpio_in); 208 free_gpio_out: 209 gpiod_put(hte.gpio_out); 210 out: 211 212 return ret; 213 } 214 215 static int tegra_hte_test_remove(struct platform_device *pdev) 216 { 217 (void)pdev; 218 219 free_irq(hte.gpio_in_irq, &hte); 220 gpiod_put(hte.gpio_in); 221 gpiod_put(hte.gpio_out); 222 del_timer_sync(&hte.timer); 223 224 return 0; 225 } 226 227 static struct platform_driver tegra_hte_test_driver = { 228 .probe = tegra_hte_test_probe, 229 .remove = tegra_hte_test_remove, 230 .driver = { 231 .name = "tegra_hte_test", 232 .of_match_table = tegra_hte_test_of_match, 233 }, 234 }; 235 module_platform_driver(tegra_hte_test_driver); 236 237 MODULE_AUTHOR("Dipen Patel <dipenp@nvidia.com>"); 238 MODULE_LICENSE("GPL"); 239