brintos

brintos / linux-shallow public Read only

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