1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2021 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 4 */ 5 6 #include <linux/counter.h> 7 #include <linux/gpio/consumer.h> 8 #include <linux/interrupt.h> 9 #include <linux/irq.h> 10 #include <linux/mod_devicetable.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 14 #define INTERRUPT_CNT_NAME "interrupt-cnt" 15 16 struct interrupt_cnt_priv { 17 atomic_t count; 18 struct counter_device counter; 19 struct gpio_desc *gpio; 20 int irq; 21 bool enabled; 22 struct counter_signal signals; 23 struct counter_synapse synapses; 24 struct counter_count cnts; 25 }; 26 27 static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) 28 { 29 struct interrupt_cnt_priv *priv = dev_id; 30 31 atomic_inc(&priv->count); 32 33 return IRQ_HANDLED; 34 } 35 36 static ssize_t interrupt_cnt_enable_read(struct counter_device *counter, 37 struct counter_count *count, 38 void *private, char *buf) 39 { 40 struct interrupt_cnt_priv *priv = counter->priv; 41 42 return sysfs_emit(buf, "%d\n", priv->enabled); 43 } 44 45 static ssize_t interrupt_cnt_enable_write(struct counter_device *counter, 46 struct counter_count *count, 47 void *private, const char *buf, 48 size_t len) 49 { 50 struct interrupt_cnt_priv *priv = counter->priv; 51 bool enable; 52 ssize_t ret; 53 54 ret = kstrtobool(buf, &enable); 55 if (ret) 56 return ret; 57 58 if (priv->enabled == enable) 59 return len; 60 61 if (enable) { 62 priv->enabled = true; 63 enable_irq(priv->irq); 64 } else { 65 disable_irq(priv->irq); 66 priv->enabled = false; 67 } 68 69 return len; 70 } 71 72 static const struct counter_count_ext interrupt_cnt_ext[] = { 73 { 74 .name = "enable", 75 .read = interrupt_cnt_enable_read, 76 .write = interrupt_cnt_enable_write, 77 }, 78 }; 79 80 static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = { 81 COUNTER_SYNAPSE_ACTION_RISING_EDGE, 82 }; 83 84 static int interrupt_cnt_action_get(struct counter_device *counter, 85 struct counter_count *count, 86 struct counter_synapse *synapse, 87 size_t *action) 88 { 89 *action = 0; 90 91 return 0; 92 } 93 94 static int interrupt_cnt_read(struct counter_device *counter, 95 struct counter_count *count, unsigned long *val) 96 { 97 struct interrupt_cnt_priv *priv = counter->priv; 98 99 *val = atomic_read(&priv->count); 100 101 return 0; 102 } 103 104 static int interrupt_cnt_write(struct counter_device *counter, 105 struct counter_count *count, 106 const unsigned long val) 107 { 108 struct interrupt_cnt_priv *priv = counter->priv; 109 110 if (val != (typeof(priv->count.counter))val) 111 return -ERANGE; 112 113 atomic_set(&priv->count, val); 114 115 return 0; 116 } 117 118 static const enum counter_function interrupt_cnt_functions[] = { 119 COUNTER_FUNCTION_INCREASE, 120 }; 121 122 static int interrupt_cnt_function_get(struct counter_device *counter, 123 struct counter_count *count, 124 size_t *function) 125 { 126 *function = 0; 127 128 return 0; 129 } 130 131 static int interrupt_cnt_signal_read(struct counter_device *counter, 132 struct counter_signal *signal, 133 enum counter_signal_level *level) 134 { 135 struct interrupt_cnt_priv *priv = counter->priv; 136 int ret; 137 138 if (!priv->gpio) 139 return -EINVAL; 140 141 ret = gpiod_get_value(priv->gpio); 142 if (ret < 0) 143 return ret; 144 145 *level = ret ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW; 146 147 return 0; 148 } 149 150 static const struct counter_ops interrupt_cnt_ops = { 151 .action_get = interrupt_cnt_action_get, 152 .count_read = interrupt_cnt_read, 153 .count_write = interrupt_cnt_write, 154 .function_get = interrupt_cnt_function_get, 155 .signal_read = interrupt_cnt_signal_read, 156 }; 157 158 static int interrupt_cnt_probe(struct platform_device *pdev) 159 { 160 struct device *dev = &pdev->dev; 161 struct interrupt_cnt_priv *priv; 162 int ret; 163 164 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 165 if (!priv) 166 return -ENOMEM; 167 168 priv->irq = platform_get_irq_optional(pdev, 0); 169 if (priv->irq == -ENXIO) 170 priv->irq = 0; 171 else if (priv->irq < 0) 172 return dev_err_probe(dev, priv->irq, "failed to get IRQ\n"); 173 174 priv->gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); 175 if (IS_ERR(priv->gpio)) 176 return dev_err_probe(dev, PTR_ERR(priv->gpio), "failed to get GPIO\n"); 177 178 if (!priv->irq && !priv->gpio) { 179 dev_err(dev, "IRQ and GPIO are not found. At least one source should be provided\n"); 180 return -ENODEV; 181 } 182 183 if (!priv->irq) { 184 int irq = gpiod_to_irq(priv->gpio); 185 186 if (irq < 0) 187 return dev_err_probe(dev, irq, "failed to get IRQ from GPIO\n"); 188 189 priv->irq = irq; 190 } 191 192 priv->signals.name = devm_kasprintf(dev, GFP_KERNEL, "IRQ %d", 193 priv->irq); 194 if (!priv->signals.name) 195 return -ENOMEM; 196 197 priv->counter.signals = &priv->signals; 198 priv->counter.num_signals = 1; 199 200 priv->synapses.actions_list = interrupt_cnt_synapse_actions; 201 priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actions); 202 priv->synapses.signal = &priv->signals; 203 204 priv->cnts.name = "Channel 0 Count"; 205 priv->cnts.functions_list = interrupt_cnt_functions; 206 priv->cnts.num_functions = ARRAY_SIZE(interrupt_cnt_functions); 207 priv->cnts.synapses = &priv->synapses; 208 priv->cnts.num_synapses = 1; 209 priv->cnts.ext = interrupt_cnt_ext; 210 priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext); 211 212 priv->counter.priv = priv; 213 priv->counter.name = dev_name(dev); 214 priv->counter.parent = dev; 215 priv->counter.ops = &interrupt_cnt_ops; 216 priv->counter.counts = &priv->cnts; 217 priv->counter.num_counts = 1; 218 219 irq_set_status_flags(priv->irq, IRQ_NOAUTOEN); 220 ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr, 221 IRQF_TRIGGER_RISING | IRQF_NO_THREAD, 222 dev_name(dev), priv); 223 if (ret) 224 return ret; 225 226 return devm_counter_register(dev, &priv->counter); 227 } 228 229 static const struct of_device_id interrupt_cnt_of_match[] = { 230 { .compatible = "interrupt-counter", }, 231 {} 232 }; 233 MODULE_DEVICE_TABLE(of, interrupt_cnt_of_match); 234 235 static struct platform_driver interrupt_cnt_driver = { 236 .probe = interrupt_cnt_probe, 237 .driver = { 238 .name = INTERRUPT_CNT_NAME, 239 .of_match_table = interrupt_cnt_of_match, 240 }, 241 }; 242 module_platform_driver(interrupt_cnt_driver); 243 244 MODULE_ALIAS("platform:interrupt-counter"); 245 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); 246 MODULE_DESCRIPTION("Interrupt counter driver"); 247 MODULE_LICENSE("GPL v2"); 248