diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-08 08:56:37 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-08 08:56:37 -0700 |
commit | a09476668e3016ea4a7b0a7ebd02f44e0546c12c (patch) | |
tree | 0a2e435d40b1785bc3df0c455fbf858adc28d36c /drivers/counter | |
parent | e8bc52cb8df80c31c73c726ab58ea9746e9ff734 (diff) | |
parent | 7cd04013fbf3e6dcb67ca6b59aa813269a2ad9ce (diff) |
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.c | 36 | ||||
-rw-r--r-- | drivers/counter/Kconfig | 15 | ||||
-rw-r--r-- | drivers/counter/Makefile | 1 | ||||
-rw-r--r-- | drivers/counter/counter-chrdev.c | 137 | ||||
-rw-r--r-- | drivers/counter/counter-core.c | 14 | ||||
-rw-r--r-- | drivers/counter/counter-sysfs.c | 304 | ||||
-rw-r--r-- | drivers/counter/ftm-quaddec.c | 1 | ||||
-rw-r--r-- | drivers/counter/intel-qep.c | 1 | ||||
-rw-r--r-- | drivers/counter/interrupt-cnt.c | 12 | ||||
-rw-r--r-- | drivers/counter/microchip-tcb-capture.c | 1 | ||||
-rw-r--r-- | drivers/counter/stm32-lptimer-cnt.c | 1 | ||||
-rw-r--r-- | drivers/counter/stm32-timer-cnt.c | 1 | ||||
-rw-r--r-- | drivers/counter/ti-ecap-capture.c | 614 | ||||
-rw-r--r-- | drivers/counter/ti-eqep.c | 1 |
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, ®val); + 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, ®val); + 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); |