summaryrefslogtreecommitdiffstats
path: root/drivers/counter
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 17:56:37 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 17:56:37 +0200
commita09476668e3016ea4a7b0a7ebd02f44e0546c12c (patch)
tree0a2e435d40b1785bc3df0c455fbf858adc28d36c /drivers/counter
parentMerge tag 'driver-core-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/... (diff)
parentw1: Split memcpy() of struct cn_msg flexible array (diff)
downloadlinux-a09476668e3016ea4a7b0a7ebd02f44e0546c12c.tar.xz
linux-a09476668e3016ea4a7b0a7ebd02f44e0546c12c.zip
Merge tag 'char-misc-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver updates from Greg KH: "Here is the large set of char/misc and other small driver subsystem changes for 6.1-rc1. Loads of different things in here: - IIO driver updates, additions, and changes. Probably the largest part of the diffstat - habanalabs driver update with support for new hardware and features, the second largest part of the diff. - fpga subsystem driver updates and additions - mhi subsystem updates - Coresight driver updates - gnss subsystem updates - extcon driver updates - icc subsystem updates - fsi subsystem updates - nvmem subsystem and driver updates - misc driver updates - speakup driver additions for new features - lots of tiny driver updates and cleanups All of these have been in the linux-next tree for a while with no reported issues" * tag 'char-misc-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (411 commits) w1: Split memcpy() of struct cn_msg flexible array spmi: pmic-arb: increase SPMI transaction timeout delay spmi: pmic-arb: block access for invalid PMIC arbiter v5 SPMI writes spmi: pmic-arb: correct duplicate APID to PPID mapping logic spmi: pmic-arb: add support to dispatch interrupt based on IRQ status spmi: pmic-arb: check apid against limits before calling irq handler spmi: pmic-arb: do not ack and clear peripheral interrupts in cleanup_irq spmi: pmic-arb: handle spurious interrupt spmi: pmic-arb: add a print in cleanup_irq drivers: spmi: Directly use ida_alloc()/free() MAINTAINERS: add TI ECAP driver info counter: ti-ecap-capture: capture driver support for ECAP Documentation: ABI: sysfs-bus-counter: add frequency & num_overflows items dt-bindings: counter: add ti,am62-ecap-capture.yaml counter: Introduce the COUNTER_COMP_ARRAY component type counter: Consolidate Counter extension sysfs attribute creation counter: Introduce the Count capture component counter: 104-quad-8: Add Signal polarity component counter: Introduce the Signal polarity component counter: interrupt-cnt: Implement watch_validate callback ...
Diffstat (limited to 'drivers/counter')
-rw-r--r--drivers/counter/104-quad-8.c36
-rw-r--r--drivers/counter/Kconfig15
-rw-r--r--drivers/counter/Makefile1
-rw-r--r--drivers/counter/counter-chrdev.c137
-rw-r--r--drivers/counter/counter-core.c14
-rw-r--r--drivers/counter/counter-sysfs.c304
-rw-r--r--drivers/counter/ftm-quaddec.c1
-rw-r--r--drivers/counter/intel-qep.c1
-rw-r--r--drivers/counter/interrupt-cnt.c12
-rw-r--r--drivers/counter/microchip-tcb-capture.c1
-rw-r--r--drivers/counter/stm32-lptimer-cnt.c1
-rw-r--r--drivers/counter/stm32-timer-cnt.c1
-rw-r--r--drivers/counter/ti-ecap-capture.c614
-rw-r--r--drivers/counter/ti-eqep.c1
14 files changed, 1064 insertions, 75 deletions
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index 4407203e0c9b..1188673d8521 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -549,6 +549,32 @@ static int quad8_index_polarity_set(struct counter_device *counter,
return 0;
}
+static int quad8_polarity_read(struct counter_device *counter,
+ struct counter_signal *signal,
+ enum counter_signal_polarity *polarity)
+{
+ int err;
+ u32 index_polarity;
+
+ err = quad8_index_polarity_get(counter, signal, &index_polarity);
+ if (err)
+ return err;
+
+ *polarity = (index_polarity) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
+ COUNTER_SIGNAL_POLARITY_NEGATIVE;
+
+ return 0;
+}
+
+static int quad8_polarity_write(struct counter_device *counter,
+ struct counter_signal *signal,
+ enum counter_signal_polarity polarity)
+{
+ const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? 1 : 0;
+
+ return quad8_index_polarity_set(counter, signal, pol);
+}
+
static const char *const quad8_synchronous_modes[] = {
"non-synchronous",
"synchronous"
@@ -977,6 +1003,13 @@ static struct counter_comp quad8_signal_ext[] = {
quad8_signal_fck_prescaler_write)
};
+static const enum counter_signal_polarity quad8_polarities[] = {
+ COUNTER_SIGNAL_POLARITY_POSITIVE,
+ COUNTER_SIGNAL_POLARITY_NEGATIVE,
+};
+
+static DEFINE_COUNTER_AVAILABLE(quad8_polarity_available, quad8_polarities);
+
static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
@@ -984,6 +1017,8 @@ static struct counter_comp quad8_index_ext[] = {
COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
quad8_index_polarity_set,
quad8_index_pol_enum),
+ COUNTER_COMP_POLARITY(quad8_polarity_read, quad8_polarity_write,
+ quad8_polarity_available),
COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
quad8_synchronous_mode_set,
quad8_synch_mode_enum),
@@ -1241,3 +1276,4 @@ module_isa_driver(quad8_driver, num_quad8);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index 5edd155f1911..d388bf26f4dc 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -101,4 +101,19 @@ config INTEL_QEP
To compile this driver as a module, choose M here: the module
will be called intel-qep.
+config TI_ECAP_CAPTURE
+ tristate "TI eCAP capture driver"
+ depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
+ depends on HAS_IOMEM
+ select REGMAP_MMIO
+ help
+ Select this option to enable the Texas Instruments Enhanced Capture
+ (eCAP) driver in input mode.
+
+ It can be used to timestamp events (falling/rising edges) detected
+ on ECAP input signal.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ti-ecap-capture.
+
endif # COUNTER
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index 8fde6c100ebc..b9a369e0d4fc 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_TI_EQEP) += ti-eqep.o
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o
obj-$(CONFIG_INTEL_QEP) += intel-qep.o
+obj-$(CONFIG_TI_ECAP_CAPTURE) += ti-ecap-capture.o
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
index 69d340be9c93..80acdf62794a 100644
--- a/drivers/counter/counter-chrdev.c
+++ b/drivers/counter/counter-chrdev.c
@@ -40,7 +40,11 @@ struct counter_comp_node {
a.signal_u32_read == b.signal_u32_read || \
a.device_u64_read == b.device_u64_read || \
a.count_u64_read == b.count_u64_read || \
- a.signal_u64_read == b.signal_u64_read)
+ a.signal_u64_read == b.signal_u64_read || \
+ a.signal_array_u32_read == b.signal_array_u32_read || \
+ a.device_array_u64_read == b.device_array_u64_read || \
+ a.count_array_u64_read == b.count_array_u64_read || \
+ a.signal_array_u64_read == b.signal_array_u64_read)
#define counter_comp_read_is_set(comp) \
(comp.action_read || \
@@ -52,7 +56,11 @@ struct counter_comp_node {
comp.signal_u32_read || \
comp.device_u64_read || \
comp.count_u64_read || \
- comp.signal_u64_read)
+ comp.signal_u64_read || \
+ comp.signal_array_u32_read || \
+ comp.device_array_u64_read || \
+ comp.count_array_u64_read || \
+ comp.signal_array_u64_read)
static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
size_t len, loff_t *f_ps)
@@ -228,6 +236,31 @@ static int counter_disable_events(struct counter_device *const counter)
return err;
}
+static int counter_get_ext(const struct counter_comp *const ext,
+ const size_t num_ext, const size_t component_id,
+ size_t *const ext_idx, size_t *const id)
+{
+ struct counter_array *element;
+
+ *id = 0;
+ for (*ext_idx = 0; *ext_idx < num_ext; (*ext_idx)++) {
+ if (*id == component_id)
+ return 0;
+
+ if (ext->type == COUNTER_COMP_ARRAY) {
+ element = ext->priv;
+
+ if (component_id - *id < element->length)
+ return 0;
+
+ *id += element->length;
+ } else
+ (*id)++;
+ }
+
+ return -EINVAL;
+}
+
static int counter_add_watch(struct counter_device *const counter,
const unsigned long arg)
{
@@ -237,6 +270,7 @@ static int counter_add_watch(struct counter_device *const counter,
size_t parent, id;
struct counter_comp *ext;
size_t num_ext;
+ size_t ext_idx, ext_id;
int err = 0;
if (copy_from_user(&watch, uwatch, sizeof(watch)))
@@ -314,11 +348,11 @@ static int counter_add_watch(struct counter_device *const counter,
comp_node.comp.priv = counter->counts[parent].synapses + id;
break;
case COUNTER_COMPONENT_EXTENSION:
- if (id >= num_ext)
- return -EINVAL;
- id = array_index_nospec(id, num_ext);
+ err = counter_get_ext(ext, num_ext, id, &ext_idx, &ext_id);
+ if (err < 0)
+ return err;
- comp_node.comp = ext[id];
+ comp_node.comp = ext[ext_idx];
break;
default:
return -EINVAL;
@@ -451,14 +485,56 @@ void counter_chrdev_remove(struct counter_device *const counter)
kfifo_free(&counter->events);
}
+static int counter_get_array_data(struct counter_device *const counter,
+ const enum counter_scope scope,
+ void *const parent,
+ const struct counter_comp *const comp,
+ const size_t idx, u64 *const value)
+{
+ const struct counter_array *const element = comp->priv;
+ u32 value_u32 = 0;
+ int ret;
+
+ switch (element->type) {
+ case COUNTER_COMP_SIGNAL_POLARITY:
+ if (scope != COUNTER_SCOPE_SIGNAL)
+ return -EINVAL;
+ ret = comp->signal_array_u32_read(counter, parent, idx,
+ &value_u32);
+ *value = value_u32;
+ return ret;
+ case COUNTER_COMP_U64:
+ switch (scope) {
+ case COUNTER_SCOPE_DEVICE:
+ return comp->device_array_u64_read(counter, idx, value);
+ case COUNTER_SCOPE_SIGNAL:
+ return comp->signal_array_u64_read(counter, parent, idx,
+ value);
+ case COUNTER_SCOPE_COUNT:
+ return comp->count_array_u64_read(counter, parent, idx,
+ value);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
static int counter_get_data(struct counter_device *const counter,
const struct counter_comp_node *const comp_node,
u64 *const value)
{
const struct counter_comp *const comp = &comp_node->comp;
- void *const parent = comp_node->parent;
+ const enum counter_scope scope = comp_node->component.scope;
+ const size_t id = comp_node->component.id;
+ struct counter_signal *const signal = comp_node->parent;
+ struct counter_count *const count = comp_node->parent;
u8 value_u8 = 0;
u32 value_u32 = 0;
+ const struct counter_comp *ext;
+ size_t num_ext;
+ size_t ext_idx, ext_id;
int ret;
if (comp_node->component.type == COUNTER_COMPONENT_NONE)
@@ -467,15 +543,15 @@ static int counter_get_data(struct counter_device *const counter,
switch (comp->type) {
case COUNTER_COMP_U8:
case COUNTER_COMP_BOOL:
- switch (comp_node->component.scope) {
+ switch (scope) {
case COUNTER_SCOPE_DEVICE:
ret = comp->device_u8_read(counter, &value_u8);
break;
case COUNTER_SCOPE_SIGNAL:
- ret = comp->signal_u8_read(counter, parent, &value_u8);
+ ret = comp->signal_u8_read(counter, signal, &value_u8);
break;
case COUNTER_SCOPE_COUNT:
- ret = comp->count_u8_read(counter, parent, &value_u8);
+ ret = comp->count_u8_read(counter, count, &value_u8);
break;
default:
return -EINVAL;
@@ -487,16 +563,17 @@ static int counter_get_data(struct counter_device *const counter,
case COUNTER_COMP_ENUM:
case COUNTER_COMP_COUNT_DIRECTION:
case COUNTER_COMP_COUNT_MODE:
- switch (comp_node->component.scope) {
+ case COUNTER_COMP_SIGNAL_POLARITY:
+ switch (scope) {
case COUNTER_SCOPE_DEVICE:
ret = comp->device_u32_read(counter, &value_u32);
break;
case COUNTER_SCOPE_SIGNAL:
- ret = comp->signal_u32_read(counter, parent,
+ ret = comp->signal_u32_read(counter, signal,
&value_u32);
break;
case COUNTER_SCOPE_COUNT:
- ret = comp->count_u32_read(counter, parent, &value_u32);
+ ret = comp->count_u32_read(counter, count, &value_u32);
break;
default:
return -EINVAL;
@@ -504,21 +581,43 @@ static int counter_get_data(struct counter_device *const counter,
*value = value_u32;
return ret;
case COUNTER_COMP_U64:
- switch (comp_node->component.scope) {
+ switch (scope) {
case COUNTER_SCOPE_DEVICE:
return comp->device_u64_read(counter, value);
case COUNTER_SCOPE_SIGNAL:
- return comp->signal_u64_read(counter, parent, value);
+ return comp->signal_u64_read(counter, signal, value);
case COUNTER_SCOPE_COUNT:
- return comp->count_u64_read(counter, parent, value);
+ return comp->count_u64_read(counter, count, value);
default:
return -EINVAL;
}
case COUNTER_COMP_SYNAPSE_ACTION:
- ret = comp->action_read(counter, parent, comp->priv,
- &value_u32);
+ ret = comp->action_read(counter, count, comp->priv, &value_u32);
*value = value_u32;
return ret;
+ case COUNTER_COMP_ARRAY:
+ switch (scope) {
+ case COUNTER_SCOPE_DEVICE:
+ ext = counter->ext;
+ num_ext = counter->num_ext;
+ break;
+ case COUNTER_SCOPE_SIGNAL:
+ ext = signal->ext;
+ num_ext = signal->num_ext;
+ break;
+ case COUNTER_SCOPE_COUNT:
+ ext = count->ext;
+ num_ext = count->num_ext;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = counter_get_ext(ext, num_ext, id, &ext_idx, &ext_id);
+ if (ret < 0)
+ return ret;
+
+ return counter_get_array_data(counter, scope, comp_node->parent,
+ comp, id - ext_id, value);
default:
return -EINVAL;
}
@@ -574,4 +673,4 @@ exit_early:
if (copied)
wake_up_poll(&counter->events_wait, EPOLLIN);
}
-EXPORT_SYMBOL_GPL(counter_push_event);
+EXPORT_SYMBOL_NS_GPL(counter_push_event, COUNTER);
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 938651f9e9e0..09c77afb33ca 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -73,7 +73,7 @@ void *counter_priv(const struct counter_device *const counter)
return &ch->privdata;
}
-EXPORT_SYMBOL_GPL(counter_priv);
+EXPORT_SYMBOL_NS_GPL(counter_priv, COUNTER);
/**
* counter_alloc - allocate a counter_device
@@ -133,13 +133,13 @@ err_ida_alloc:
return NULL;
}
-EXPORT_SYMBOL_GPL(counter_alloc);
+EXPORT_SYMBOL_NS_GPL(counter_alloc, COUNTER);
void counter_put(struct counter_device *counter)
{
put_device(&counter->dev);
}
-EXPORT_SYMBOL_GPL(counter_put);
+EXPORT_SYMBOL_NS_GPL(counter_put, COUNTER);
/**
* counter_add - complete registration of a counter
@@ -166,7 +166,7 @@ int counter_add(struct counter_device *counter)
/* implies device_add(dev) */
return cdev_device_add(&counter->chrdev, dev);
}
-EXPORT_SYMBOL_GPL(counter_add);
+EXPORT_SYMBOL_NS_GPL(counter_add, COUNTER);
/**
* counter_unregister - unregister Counter from the system
@@ -188,7 +188,7 @@ void counter_unregister(struct counter_device *const counter)
mutex_unlock(&counter->ops_exist_lock);
}
-EXPORT_SYMBOL_GPL(counter_unregister);
+EXPORT_SYMBOL_NS_GPL(counter_unregister, COUNTER);
static void devm_counter_release(void *counter)
{
@@ -223,7 +223,7 @@ struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv
return counter;
}
-EXPORT_SYMBOL_GPL(devm_counter_alloc);
+EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, COUNTER);
/**
* devm_counter_add - complete registration of a counter
@@ -244,7 +244,7 @@ int devm_counter_add(struct device *dev,
return devm_add_action_or_reset(dev, devm_counter_release, counter);
}
-EXPORT_SYMBOL_GPL(devm_counter_add);
+EXPORT_SYMBOL_NS_GPL(devm_counter_add, COUNTER);
#define COUNTER_DEV_MAX 256
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 04eac41dad33..b9efe66f9f8d 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -91,6 +91,11 @@ static const char *const counter_count_mode_str[] = {
[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
};
+static const char *const counter_signal_polarity_str[] = {
+ [COUNTER_SIGNAL_POLARITY_POSITIVE] = "positive",
+ [COUNTER_SIGNAL_POLARITY_NEGATIVE] = "negative"
+};
+
static ssize_t counter_comp_u8_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -201,6 +206,8 @@ static ssize_t counter_comp_u32_show(struct device *dev,
return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
case COUNTER_COMP_COUNT_MODE:
return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
+ case COUNTER_COMP_SIGNAL_POLARITY:
+ return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);
default:
return sysfs_emit(buf, "%u\n", (unsigned int)data);
}
@@ -252,6 +259,10 @@ static ssize_t counter_comp_u32_store(struct device *dev,
err = counter_find_enum(&data, avail->enums, avail->num_items,
buf, counter_count_mode_str);
break;
+ case COUNTER_COMP_SIGNAL_POLARITY:
+ err = counter_find_enum(&data, avail->enums, avail->num_items,
+ buf, counter_signal_polarity_str);
+ break;
default:
err = kstrtou32(buf, 0, &data);
break;
@@ -341,6 +352,124 @@ static ssize_t counter_comp_u64_store(struct device *dev,
return len;
}
+static ssize_t counter_comp_array_u32_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct counter_attribute *const a = to_counter_attribute(attr);
+ struct counter_device *const counter = counter_from_dev(dev);
+ const struct counter_array *const element = a->comp.priv;
+ int err;
+ u32 data = 0;
+
+ if (a->scope != COUNTER_SCOPE_SIGNAL ||
+ element->type != COUNTER_COMP_SIGNAL_POLARITY)
+ return -EINVAL;
+
+ err = a->comp.signal_array_u32_read(counter, a->parent, element->idx,
+ &data);
+ if (err < 0)
+ return err;
+
+ return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);
+}
+
+static ssize_t counter_comp_array_u32_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ const struct counter_attribute *const a = to_counter_attribute(attr);
+ struct counter_device *const counter = counter_from_dev(dev);
+ const struct counter_array *const element = a->comp.priv;
+ int err;
+ u32 data = 0;
+
+ if (element->type != COUNTER_COMP_SIGNAL_POLARITY ||
+ a->scope != COUNTER_SCOPE_SIGNAL)
+ return -EINVAL;
+
+ err = counter_find_enum(&data, element->avail->enums,
+ element->avail->num_items, buf,
+ counter_signal_polarity_str);
+ if (err < 0)
+ return err;
+
+ err = a->comp.signal_array_u32_write(counter, a->parent, element->idx,
+ data);
+ if (err < 0)
+ return err;
+
+ return len;
+}
+
+static ssize_t counter_comp_array_u64_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct counter_attribute *const a = to_counter_attribute(attr);
+ struct counter_device *const counter = counter_from_dev(dev);
+ const struct counter_array *const element = a->comp.priv;
+ int err;
+ u64 data = 0;
+
+ switch (a->scope) {
+ case COUNTER_SCOPE_DEVICE:
+ err = a->comp.device_array_u64_read(counter, element->idx,
+ &data);
+ break;
+ case COUNTER_SCOPE_SIGNAL:
+ err = a->comp.signal_array_u64_read(counter, a->parent,
+ element->idx, &data);
+ break;
+ case COUNTER_SCOPE_COUNT:
+ err = a->comp.count_array_u64_read(counter, a->parent,
+ element->idx, &data);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ return sysfs_emit(buf, "%llu\n", (unsigned long long)data);
+}
+
+static ssize_t counter_comp_array_u64_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ const struct counter_attribute *const a = to_counter_attribute(attr);
+ struct counter_device *const counter = counter_from_dev(dev);
+ const struct counter_array *const element = a->comp.priv;
+ int err;
+ u64 data = 0;
+
+ err = kstrtou64(buf, 0, &data);
+ if (err < 0)
+ return err;
+
+ switch (a->scope) {
+ case COUNTER_SCOPE_DEVICE:
+ err = a->comp.device_array_u64_write(counter, element->idx,
+ data);
+ break;
+ case COUNTER_SCOPE_SIGNAL:
+ err = a->comp.signal_array_u64_write(counter, a->parent,
+ element->idx, data);
+ break;
+ case COUNTER_SCOPE_COUNT:
+ err = a->comp.count_array_u64_write(counter, a->parent,
+ element->idx, data);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ return len;
+}
+
static ssize_t enums_available_show(const u32 *const enums,
const size_t num_enums,
const char *const strs[], char *buf)
@@ -435,6 +564,7 @@ static int counter_attr_create(struct device *const dev,
const enum counter_scope scope,
void *const parent)
{
+ const struct counter_array *const array = comp->priv;
struct counter_attribute *counter_attr;
struct device_attribute *dev_attr;
@@ -469,6 +599,7 @@ static int counter_attr_create(struct device *const dev,
case COUNTER_COMP_ENUM:
case COUNTER_COMP_COUNT_DIRECTION:
case COUNTER_COMP_COUNT_MODE:
+ case COUNTER_COMP_SIGNAL_POLARITY:
if (comp->device_u32_read) {
dev_attr->attr.mode |= 0444;
dev_attr->show = counter_comp_u32_show;
@@ -488,6 +619,32 @@ static int counter_attr_create(struct device *const dev,
dev_attr->store = counter_comp_u64_store;
}
break;
+ case COUNTER_COMP_ARRAY:
+ switch (array->type) {
+ case COUNTER_COMP_SIGNAL_POLARITY:
+ if (comp->signal_array_u32_read) {
+ dev_attr->attr.mode |= 0444;
+ dev_attr->show = counter_comp_array_u32_show;
+ }
+ if (comp->signal_array_u32_write) {
+ dev_attr->attr.mode |= 0200;
+ dev_attr->store = counter_comp_array_u32_store;
+ }
+ break;
+ case COUNTER_COMP_U64:
+ if (comp->device_array_u64_read) {
+ dev_attr->attr.mode |= 0444;
+ dev_attr->show = counter_comp_array_u64_show;
+ }
+ if (comp->device_array_u64_write) {
+ dev_attr->attr.mode |= 0200;
+ dev_attr->store = counter_comp_array_u64_store;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
default:
return -EINVAL;
}
@@ -580,6 +737,95 @@ static int counter_comp_id_attr_create(struct device *const dev,
return 0;
}
+static int counter_ext_attrs_create(struct device *const dev,
+ struct counter_attribute_group *const group,
+ const struct counter_comp *const ext,
+ const enum counter_scope scope,
+ void *const parent, const size_t id)
+{
+ int err;
+
+ /* Create main extension attribute */
+ err = counter_attr_create(dev, group, ext, scope, parent);
+ if (err < 0)
+ return err;
+
+ /* Create extension id attribute */
+ return counter_comp_id_attr_create(dev, group, ext->name, id);
+}
+
+static int counter_array_attrs_create(struct device *const dev,
+ struct counter_attribute_group *const group,
+ const struct counter_comp *const comp,
+ const enum counter_scope scope,
+ void *const parent, const size_t id)
+{
+ const struct counter_array *const array = comp->priv;
+ struct counter_comp ext = *comp;
+ struct counter_array *element;
+ size_t idx;
+ int err;
+
+ /* Create an attribute for each array element */
+ for (idx = 0; idx < array->length; idx++) {
+ /* Generate array element attribute name */
+ ext.name = devm_kasprintf(dev, GFP_KERNEL, "%s%zu", comp->name,
+ idx);
+ if (!ext.name)
+ return -ENOMEM;
+
+ /* Allocate and configure array element */
+ element = devm_kzalloc(dev, sizeof(*element), GFP_KERNEL);
+ if (!element)
+ return -ENOMEM;
+ element->type = array->type;
+ element->avail = array->avail;
+ element->idx = idx;
+ ext.priv = element;
+
+ /* Create all attributes associated with the array element */
+ err = counter_ext_attrs_create(dev, group, &ext, scope, parent,
+ id + idx);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int counter_sysfs_exts_add(struct device *const dev,
+ struct counter_attribute_group *const group,
+ const struct counter_comp *const exts,
+ const size_t num_ext,
+ const enum counter_scope scope,
+ void *const parent)
+{
+ size_t i;
+ const struct counter_comp *ext;
+ int err;
+ size_t id = 0;
+ const struct counter_array *array;
+
+ /* Create attributes for each extension */
+ for (i = 0; i < num_ext; i++) {
+ ext = &exts[i];
+ if (ext->type == COUNTER_COMP_ARRAY) {
+ err = counter_array_attrs_create(dev, group, ext, scope,
+ parent, id);
+ array = ext->priv;
+ id += array->length;
+ } else {
+ err = counter_ext_attrs_create(dev, group, ext, scope,
+ parent, id);
+ id++;
+ }
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static struct counter_comp counter_signal_comp = {
.type = COUNTER_COMP_SIGNAL_LEVEL,
.name = "signal",
@@ -593,8 +839,6 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
struct device *const dev = &counter->dev;
int err;
struct counter_comp comp;
- size_t i;
- struct counter_comp *ext;
/* Create main Signal attribute */
comp = counter_signal_comp;
@@ -608,21 +852,9 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
if (err < 0)
return err;
- /* Create an attribute for each extension */
- for (i = 0; i < signal->num_ext; i++) {
- ext = &signal->ext[i];
-
- err = counter_attr_create(dev, cattr_group, ext, scope, signal);
- if (err < 0)
- return err;
-
- err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
- i);
- if (err < 0)
- return err;
- }
-
- return 0;
+ /* Add Signal extensions */
+ return counter_sysfs_exts_add(dev, cattr_group, signal->ext,
+ signal->num_ext, scope, signal);
}
static int counter_sysfs_signals_add(struct counter_device *const counter,
@@ -707,8 +939,6 @@ static int counter_count_attrs_create(struct counter_device *const counter,
struct device *const dev = &counter->dev;
int err;
struct counter_comp comp;
- size_t i;
- struct counter_comp *ext;
/* Create main Count attribute */
comp = counter_count_comp;
@@ -731,21 +961,9 @@ static int counter_count_attrs_create(struct counter_device *const counter,
if (err < 0)
return err;
- /* Create an attribute for each extension */
- for (i = 0; i < count->num_ext; i++) {
- ext = &count->ext[i];
-
- err = counter_attr_create(dev, cattr_group, ext, scope, count);
- if (err < 0)
- return err;
-
- err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
- i);
- if (err < 0)
- return err;
- }
-
- return 0;
+ /* Add Count extensions */
+ return counter_sysfs_exts_add(dev, cattr_group, count->ext,
+ count->num_ext, scope, count);
}
static int counter_sysfs_counts_add(struct counter_device *const counter,
@@ -838,8 +1056,6 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
struct device *const dev = &counter->dev;
int err;
- size_t i;
- struct counter_comp *ext;
/* Add Signals sysfs attributes */
err = counter_sysfs_signals_add(counter, cattr_group);
@@ -876,19 +1092,9 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
if (err < 0)
return err;
- /* Create an attribute for each extension */
- for (i = 0; i < counter->num_ext; i++) {
- ext = &counter->ext[i];
-
- err = counter_attr_create(dev, cattr_group, ext, scope, NULL);
- if (err < 0)
- return err;
-
- err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
- i);
- if (err < 0)
- return err;
- }
+ /* Add device extensions */
+ return counter_sysfs_exts_add(dev, cattr_group, counter->ext,
+ counter->num_ext, scope, NULL);
return 0;
}
diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c
index 2a58582a9df4..aea6622a9b13 100644
--- a/drivers/counter/ftm-quaddec.c
+++ b/drivers/counter/ftm-quaddec.c
@@ -325,3 +325,4 @@ module_platform_driver(ftm_quaddec_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c
index 47a6a9dfc9e8..af5942e66f7d 100644
--- a/drivers/counter/intel-qep.c
+++ b/drivers/counter/intel-qep.c
@@ -523,3 +523,4 @@ MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel Quadrature Encoder Peripheral driver");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
index 3b13f56bbb11..229473855c5b 100644
--- a/drivers/counter/interrupt-cnt.c
+++ b/drivers/counter/interrupt-cnt.c
@@ -139,12 +139,23 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,
return 0;
}
+static int interrupt_cnt_watch_validate(struct counter_device *counter,
+ const struct counter_watch *watch)
+{
+ if (watch->channel != 0 ||
+ watch->event != COUNTER_EVENT_CHANGE_OF_STATE)
+ return -EINVAL;
+
+ return 0;
+}
+
static const struct counter_ops interrupt_cnt_ops = {
.action_read = interrupt_cnt_action_read,
.count_read = interrupt_cnt_read,
.count_write = interrupt_cnt_write,
.function_read = interrupt_cnt_function_read,
.signal_read = interrupt_cnt_signal_read,
+ .watch_validate = interrupt_cnt_watch_validate,
};
static int interrupt_cnt_probe(struct platform_device *pdev)
@@ -242,3 +253,4 @@ MODULE_ALIAS("platform:interrupt-counter");
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
MODULE_DESCRIPTION("Interrupt counter driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
index 00844445143b..f9dee15d9777 100644
--- a/drivers/counter/microchip-tcb-capture.c
+++ b/drivers/counter/microchip-tcb-capture.c
@@ -394,3 +394,4 @@ module_platform_driver(mchp_tc_driver);
MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
MODULE_DESCRIPTION("Microchip TCB Capture driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
index 68031d93ce89..d6b80b6dfc28 100644
--- a/drivers/counter/stm32-lptimer-cnt.c
+++ b/drivers/counter/stm32-lptimer-cnt.c
@@ -520,3 +520,4 @@ MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
MODULE_ALIAS("platform:stm32-lptimer-counter");
MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index 5779ae7c73cf..9bf20a5d6bda 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -417,3 +417,4 @@ MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
MODULE_ALIAS("platform:stm32-timer-counter");
MODULE_DESCRIPTION("STMicroelectronics STM32 TIMER counter driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c
new file mode 100644
index 000000000000..af10de30aba5
--- /dev/null
+++ b/drivers/counter/ti-ecap-capture.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ECAP Capture driver
+ *
+ * Copyright (C) 2022 Julien Panis <jpanis@baylibre.com>
+ */
+
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/counter.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#define ECAP_DRV_NAME "ecap"
+
+/* ECAP event IDs */
+#define ECAP_CEVT1 0
+#define ECAP_CEVT2 1
+#define ECAP_CEVT3 2
+#define ECAP_CEVT4 3
+#define ECAP_CNTOVF 4
+
+#define ECAP_CEVT_LAST ECAP_CEVT4
+#define ECAP_NB_CEVT (ECAP_CEVT_LAST + 1)
+
+#define ECAP_EVT_LAST ECAP_CNTOVF
+#define ECAP_NB_EVT (ECAP_EVT_LAST + 1)
+
+/* Registers */
+#define ECAP_TSCNT_REG 0x00
+
+#define ECAP_CAP_REG(i) (((i) << 2) + 0x08)
+
+#define ECAP_ECCTL_REG 0x28
+#define ECAP_CAPPOL_BIT(i) BIT((i) << 1)
+#define ECAP_EV_MODE_MASK GENMASK(7, 0)
+#define ECAP_CAPLDEN_BIT BIT(8)
+#define ECAP_CONT_ONESHT_BIT BIT(16)
+#define ECAP_STOPVALUE_MASK GENMASK(18, 17)
+#define ECAP_TSCNTSTP_BIT BIT(20)
+#define ECAP_SYNCO_DIS_MASK GENMASK(23, 22)
+#define ECAP_CAP_APWM_BIT BIT(25)
+#define ECAP_ECCTL_EN_MASK (ECAP_CAPLDEN_BIT | ECAP_TSCNTSTP_BIT)
+#define ECAP_ECCTL_CFG_MASK (ECAP_SYNCO_DIS_MASK | ECAP_STOPVALUE_MASK \
+ | ECAP_ECCTL_EN_MASK | ECAP_CAP_APWM_BIT \
+ | ECAP_CONT_ONESHT_BIT)
+
+#define ECAP_ECINT_EN_FLG_REG 0x2c
+#define ECAP_EVT_EN_MASK GENMASK(ECAP_NB_EVT, ECAP_NB_CEVT)
+#define ECAP_EVT_FLG_BIT(i) BIT((i) + 17)
+
+#define ECAP_ECINT_CLR_FRC_REG 0x30
+#define ECAP_INT_CLR_BIT BIT(0)
+#define ECAP_EVT_CLR_BIT(i) BIT((i) + 1)
+#define ECAP_EVT_CLR_MASK GENMASK(ECAP_NB_EVT, 0)
+
+#define ECAP_PID_REG 0x5c
+
+/* ECAP signals */
+#define ECAP_CLOCK_SIG 0
+#define ECAP_INPUT_SIG 1
+
+static const struct regmap_config ecap_cnt_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ECAP_PID_REG,
+};
+
+/**
+ * struct ecap_cnt_dev - device private data structure
+ * @enabled: device state
+ * @lock: synchronization lock to prevent I/O race conditions
+ * @clk: device clock
+ * @regmap: device register map
+ * @nb_ovf: number of overflows since capture start
+ * @pm_ctx: device context for PM operations
+ * @pm_ctx.ev_mode: event mode bits
+ * @pm_ctx.time_cntr: timestamp counter value
+ */
+struct ecap_cnt_dev {
+ bool enabled;
+ struct mutex lock;
+ struct clk *clk;
+ struct regmap *regmap;
+ atomic_t nb_ovf;
+ struct {
+ u8 ev_mode;
+ u32 time_cntr;
+ } pm_ctx;
+};
+
+static u8 ecap_cnt_capture_get_evmode(struct counter_device *counter)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+ unsigned int regval;
+
+ pm_runtime_get_sync(counter->parent);
+ regmap_read(ecap_dev->regmap, ECAP_ECCTL_REG, &regval);
+ pm_runtime_put_sync(counter->parent);
+
+ return regval;
+}
+
+static void ecap_cnt_capture_set_evmode(struct counter_device *counter, u8 ev_mode)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ pm_runtime_get_sync(counter->parent);
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_EV_MODE_MASK, ev_mode);
+ pm_runtime_put_sync(counter->parent);
+}
+
+static void ecap_cnt_capture_enable(struct counter_device *counter)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ pm_runtime_get_sync(counter->parent);
+
+ /* Enable interrupts on events */
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG,
+ ECAP_EVT_EN_MASK, ECAP_EVT_EN_MASK);
+
+ /* Run counter */
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_ECCTL_CFG_MASK,
+ ECAP_SYNCO_DIS_MASK | ECAP_STOPVALUE_MASK | ECAP_ECCTL_EN_MASK);
+}
+
+static void ecap_cnt_capture_disable(struct counter_device *counter)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ /* Stop counter */
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_ECCTL_EN_MASK, 0);
+
+ /* Disable interrupts on events */
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, ECAP_EVT_EN_MASK, 0);
+
+ pm_runtime_put_sync(counter->parent);
+}
+
+static u32 ecap_cnt_count_get_val(struct counter_device *counter, unsigned int reg)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+ unsigned int regval;
+
+ pm_runtime_get_sync(counter->parent);
+ regmap_read(ecap_dev->regmap, reg, &regval);
+ pm_runtime_put_sync(counter->parent);
+
+ return regval;
+}
+
+static void ecap_cnt_count_set_val(struct counter_device *counter, unsigned int reg, u32 val)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ pm_runtime_get_sync(counter->parent);
+ regmap_write(ecap_dev->regmap, reg, val);
+ pm_runtime_put_sync(counter->parent);
+}
+
+static int ecap_cnt_count_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ *val = ecap_cnt_count_get_val(counter, ECAP_TSCNT_REG);
+
+ return 0;
+}
+
+static int ecap_cnt_count_write(struct counter_device *counter,
+ struct counter_count *count, u64 val)
+{
+ if (val > U32_MAX)
+ return -ERANGE;
+
+ ecap_cnt_count_set_val(counter, ECAP_TSCNT_REG, val);
+
+ return 0;
+}
+
+static int ecap_cnt_function_read(struct counter_device *counter,
+ struct counter_count *count,
+ enum counter_function *function)
+{
+ *function = COUNTER_FUNCTION_INCREASE;
+
+ return 0;
+}
+
+static int ecap_cnt_action_read(struct counter_device *counter,
+ struct counter_count *count,
+ struct counter_synapse *synapse,
+ enum counter_synapse_action *action)
+{
+ *action = (synapse->signal->id == ECAP_CLOCK_SIG) ?
+ COUNTER_SYNAPSE_ACTION_RISING_EDGE :
+ COUNTER_SYNAPSE_ACTION_NONE;
+
+ return 0;
+}
+
+static int ecap_cnt_watch_validate(struct counter_device *counter,
+ const struct counter_watch *watch)
+{
+ if (watch->channel > ECAP_CEVT_LAST)
+ return -EINVAL;
+
+ switch (watch->event) {
+ case COUNTER_EVENT_CAPTURE:
+ case COUNTER_EVENT_OVERFLOW:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ecap_cnt_clk_get_freq(struct counter_device *counter,
+ struct counter_signal *signal, u64 *freq)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ *freq = clk_get_rate(ecap_dev->clk);
+
+ return 0;
+}
+
+static int ecap_cnt_pol_read(struct counter_device *counter,
+ struct counter_signal *signal,
+ size_t idx, enum counter_signal_polarity *pol)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+ int bitval;
+
+ pm_runtime_get_sync(counter->parent);
+ bitval = regmap_test_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx));
+ pm_runtime_put_sync(counter->parent);
+
+ *pol = bitval ? COUNTER_SIGNAL_POLARITY_NEGATIVE : COUNTER_SIGNAL_POLARITY_POSITIVE;
+
+ return 0;
+}
+
+static int ecap_cnt_pol_write(struct counter_device *counter,
+ struct counter_signal *signal,
+ size_t idx, enum counter_signal_polarity pol)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ pm_runtime_get_sync(counter->parent);
+ if (pol == COUNTER_SIGNAL_POLARITY_NEGATIVE)
+ regmap_set_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx));
+ else
+ regmap_clear_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx));
+ pm_runtime_put_sync(counter->parent);
+
+ return 0;
+}
+
+static int ecap_cnt_cap_read(struct counter_device *counter,
+ struct counter_count *count,
+ size_t idx, u64 *cap)
+{
+ *cap = ecap_cnt_count_get_val(counter, ECAP_CAP_REG(idx));
+
+ return 0;
+}
+
+static int ecap_cnt_cap_write(struct counter_device *counter,
+ struct counter_count *count,
+ size_t idx, u64 cap)
+{
+ if (cap > U32_MAX)
+ return -ERANGE;
+
+ ecap_cnt_count_set_val(counter, ECAP_CAP_REG(idx), cap);
+
+ return 0;
+}
+
+static int ecap_cnt_nb_ovf_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ *val = atomic_read(&ecap_dev->nb_ovf);
+
+ return 0;
+}
+
+static int ecap_cnt_nb_ovf_write(struct counter_device *counter,
+ struct counter_count *count, u64 val)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ if (val > U32_MAX)
+ return -ERANGE;
+
+ atomic_set(&ecap_dev->nb_ovf, val);
+
+ return 0;
+}
+
+static int ecap_cnt_ceiling_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ *val = U32_MAX;
+
+ return 0;
+}
+
+static int ecap_cnt_enable_read(struct counter_device *counter,
+ struct counter_count *count, u8 *enable)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ *enable = ecap_dev->enabled;
+
+ return 0;
+}
+
+static int ecap_cnt_enable_write(struct counter_device *counter,
+ struct counter_count *count, u8 enable)
+{
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter);
+
+ mutex_lock(&ecap_dev->lock);
+
+ if (enable == ecap_dev->enabled)
+ goto out;
+
+ if (enable)
+ ecap_cnt_capture_enable(counter);
+ else
+ ecap_cnt_capture_disable(counter);
+ ecap_dev->enabled = enable;
+
+out:
+ mutex_unlock(&ecap_dev->lock);
+
+ return 0;
+}
+
+static const struct counter_ops ecap_cnt_ops = {
+ .count_read = ecap_cnt_count_read,
+ .count_write = ecap_cnt_count_write,
+ .function_read = ecap_cnt_function_read,
+ .action_read = ecap_cnt_action_read,
+ .watch_validate = ecap_cnt_watch_validate,
+};
+
+static const enum counter_function ecap_cnt_functions[] = {
+ COUNTER_FUNCTION_INCREASE,
+};
+
+static const enum counter_synapse_action ecap_cnt_clock_actions[] = {
+ COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+};
+
+static const enum counter_synapse_action ecap_cnt_input_actions[] = {
+ COUNTER_SYNAPSE_ACTION_NONE,
+};
+
+static struct counter_comp ecap_cnt_clock_ext[] = {
+ COUNTER_COMP_SIGNAL_U64("frequency", ecap_cnt_clk_get_freq, NULL),
+};
+
+static const enum counter_signal_polarity ecap_cnt_pol_avail[] = {
+ COUNTER_SIGNAL_POLARITY_POSITIVE,
+ COUNTER_SIGNAL_POLARITY_NEGATIVE,
+};
+
+static DEFINE_COUNTER_ARRAY_POLARITY(ecap_cnt_pol_array, ecap_cnt_pol_avail, ECAP_NB_CEVT);
+
+static struct counter_comp ecap_cnt_signal_ext[] = {
+ COUNTER_COMP_ARRAY_POLARITY(ecap_cnt_pol_read, ecap_cnt_pol_write, ecap_cnt_pol_array),
+};
+
+static struct counter_signal ecap_cnt_signals[] = {
+ {
+ .id = ECAP_CLOCK_SIG,
+ .name = "Clock Signal",
+ .ext = ecap_cnt_clock_ext,
+ .num_ext = ARRAY_SIZE(ecap_cnt_clock_ext),
+ },
+ {
+ .id = ECAP_INPUT_SIG,
+ .name = "Input Signal",
+ .ext = ecap_cnt_signal_ext,
+ .num_ext = ARRAY_SIZE(ecap_cnt_signal_ext),
+ },
+};
+
+static struct counter_synapse ecap_cnt_synapses[] = {
+ {
+ .actions_list = ecap_cnt_clock_actions,
+ .num_actions = ARRAY_SIZE(ecap_cnt_clock_actions),
+ .signal = &ecap_cnt_signals[ECAP_CLOCK_SIG],
+ },
+ {
+ .actions_list = ecap_cnt_input_actions,
+ .num_actions = ARRAY_SIZE(ecap_cnt_input_actions),
+ .signal = &ecap_cnt_signals[ECAP_INPUT_SIG],
+ },
+};
+
+static DEFINE_COUNTER_ARRAY_CAPTURE(ecap_cnt_cap_array, ECAP_NB_CEVT);
+
+static struct counter_comp ecap_cnt_count_ext[] = {
+ COUNTER_COMP_ARRAY_CAPTURE(ecap_cnt_cap_read, ecap_cnt_cap_write, ecap_cnt_cap_array),
+ COUNTER_COMP_COUNT_U64("num_overflows", ecap_cnt_nb_ovf_read, ecap_cnt_nb_ovf_write),
+ COUNTER_COMP_CEILING(ecap_cnt_ceiling_read, NULL),
+ COUNTER_COMP_ENABLE(ecap_cnt_enable_read, ecap_cnt_enable_write),
+};
+
+static struct counter_count ecap_cnt_counts[] = {
+ {
+ .name = "Timestamp Counter",
+ .functions_list = ecap_cnt_functions,
+ .num_functions = ARRAY_SIZE(ecap_cnt_functions),
+ .synapses = ecap_cnt_synapses,
+ .num_synapses = ARRAY_SIZE(ecap_cnt_synapses),
+ .ext = ecap_cnt_count_ext,
+ .num_ext = ARRAY_SIZE(ecap_cnt_count_ext),
+ },
+};
+
+static irqreturn_t ecap_cnt_isr(int irq, void *dev_id)
+{
+ struct counter_device *counter_dev = dev_id;
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
+ unsigned int clr = 0;
+ unsigned int flg;
+ int i;
+
+ regmap_read(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, &flg);
+
+ /* Check capture events */
+ for (i = 0 ; i < ECAP_NB_CEVT ; i++) {
+ if (flg & ECAP_EVT_FLG_BIT(i)) {
+ counter_push_event(counter_dev, COUNTER_EVENT_CAPTURE, i);
+ clr |= ECAP_EVT_CLR_BIT(i);
+ }
+ }
+
+ /* Check counter overflow */
+ if (flg & ECAP_EVT_FLG_BIT(ECAP_CNTOVF)) {
+ atomic_inc(&ecap_dev->nb_ovf);
+ for (i = 0 ; i < ECAP_NB_CEVT ; i++)
+ counter_push_event(counter_dev, COUNTER_EVENT_OVERFLOW, i);
+ clr |= ECAP_EVT_CLR_BIT(ECAP_CNTOVF);
+ }
+
+ clr |= ECAP_INT_CLR_BIT;
+ regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_CLR_FRC_REG, ECAP_EVT_CLR_MASK, clr);
+
+ return IRQ_HANDLED;
+}
+
+static void ecap_cnt_pm_disable(void *dev)
+{
+ pm_runtime_disable(dev);
+}
+
+static int ecap_cnt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ecap_cnt_dev *ecap_dev;
+ struct counter_device *counter_dev;
+ void __iomem *mmio_base;
+ unsigned long clk_rate;
+ int ret;
+
+ counter_dev = devm_counter_alloc(dev, sizeof(*ecap_dev));
+ if (IS_ERR(counter_dev))
+ return PTR_ERR(counter_dev);
+
+ counter_dev->name = ECAP_DRV_NAME;
+ counter_dev->parent = dev;
+ counter_dev->ops = &ecap_cnt_ops;
+ counter_dev->signals = ecap_cnt_signals;
+ counter_dev->num_signals = ARRAY_SIZE(ecap_cnt_signals);
+ counter_dev->counts = ecap_cnt_counts;
+ counter_dev->num_counts = ARRAY_SIZE(ecap_cnt_counts);
+
+ ecap_dev = counter_priv(counter_dev);
+
+ mutex_init(&ecap_dev->lock);
+
+ ecap_dev->clk = devm_clk_get_enabled(dev, "fck");
+ if (IS_ERR(ecap_dev->clk))
+ return dev_err_probe(dev, PTR_ERR(ecap_dev->clk), "failed to get clock\n");
+
+ clk_rate = clk_get_rate(ecap_dev->clk);
+ if (!clk_rate) {
+ dev_err(dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio_base))
+ return PTR_ERR(mmio_base);
+
+ ecap_dev->regmap = devm_regmap_init_mmio(dev, mmio_base, &ecap_cnt_regmap_config);
+ if (IS_ERR(ecap_dev->regmap))
+ return dev_err_probe(dev, PTR_ERR(ecap_dev->regmap), "failed to init regmap\n");
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get irq\n");
+
+ ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq\n");
+
+ platform_set_drvdata(pdev, counter_dev);
+
+ pm_runtime_enable(dev);
+
+ /* Register a cleanup callback to care for disabling PM */
+ ret = devm_add_action_or_reset(dev, ecap_cnt_pm_disable, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add pm disable action\n");
+
+ ret = devm_counter_add(dev, counter_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add counter\n");
+
+ return 0;
+}
+
+static int ecap_cnt_remove(struct platform_device *pdev)
+{
+ struct counter_device *counter_dev = platform_get_drvdata(pdev);
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
+
+ if (ecap_dev->enabled)
+ ecap_cnt_capture_disable(counter_dev);
+
+ return 0;
+}
+
+static int ecap_cnt_suspend(struct device *dev)
+{
+ struct counter_device *counter_dev = dev_get_drvdata(dev);
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
+
+ /* If eCAP is running, stop capture then save timestamp counter */
+ if (ecap_dev->enabled) {
+ /*
+ * Disabling capture has the following effects:
+ * - interrupts are disabled
+ * - loading of capture registers is disabled
+ * - timebase counter is stopped
+ */
+ ecap_cnt_capture_disable(counter_dev);
+ ecap_dev->pm_ctx.time_cntr = ecap_cnt_count_get_val(counter_dev, ECAP_TSCNT_REG);
+ }
+
+ ecap_dev->pm_ctx.ev_mode = ecap_cnt_capture_get_evmode(counter_dev);
+
+ clk_disable(ecap_dev->clk);
+
+ return 0;
+}
+
+static int ecap_cnt_resume(struct device *dev)
+{
+ struct counter_device *counter_dev = dev_get_drvdata(dev);
+ struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
+
+ clk_enable(ecap_dev->clk);
+
+ ecap_cnt_capture_set_evmode(counter_dev, ecap_dev->pm_ctx.ev_mode);
+
+ /* If eCAP was running, restore timestamp counter then run capture */
+ if (ecap_dev->enabled) {
+ ecap_cnt_count_set_val(counter_dev, ECAP_TSCNT_REG, ecap_dev->pm_ctx.time_cntr);
+ ecap_cnt_capture_enable(counter_dev);
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ecap_cnt_pm_ops, ecap_cnt_suspend, ecap_cnt_resume);
+
+static const struct of_device_id ecap_cnt_of_match[] = {
+ { .compatible = "ti,am62-ecap-capture" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ecap_cnt_of_match);
+
+static struct platform_driver ecap_cnt_driver = {
+ .probe = ecap_cnt_probe,
+ .remove = ecap_cnt_remove,
+ .driver = {
+ .name = "ecap-capture",
+ .of_match_table = ecap_cnt_of_match,
+ .pm = pm_sleep_ptr(&ecap_cnt_pm_ops),
+ },
+};
+module_platform_driver(ecap_cnt_driver);
+
+MODULE_DESCRIPTION("ECAP Capture driver");
+MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
index 0489d26eb47c..b0f24cf3e891 100644
--- a/drivers/counter/ti-eqep.c
+++ b/drivers/counter/ti-eqep.c
@@ -456,3 +456,4 @@ module_platform_driver(ti_eqep_driver);
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
MODULE_DESCRIPTION("TI eQEP counter driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(COUNTER);