diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-06-09 12:11:49 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-06-09 12:11:49 +0200 |
commit | 6771fb0b940eb74f1a68fe3f180a7668103397d3 (patch) | |
tree | 0d17a52002350df09f684277831dd939f63883d3 /drivers/counter | |
parent | 54fd727f83a4d2f9c6e85cb1fad88325a56b555f (diff) | |
parent | 41340965b4f8055f975f73e1e3d23eff8038f013 (diff) | |
download | linux-stable-6771fb0b940eb74f1a68fe3f180a7668103397d3.tar.gz linux-stable-6771fb0b940eb74f1a68fe3f180a7668103397d3.tar.bz2 linux-stable-6771fb0b940eb74f1a68fe3f180a7668103397d3.zip |
Merge tag 'iio-for-5.14a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
1st set of new IIO/counter device support, features and cleanup for 5.14
There are a couple of large cleanup sets in here alongside a number of new
drivers.
Note an immutable branch merged to add a stub for i2c_verify_client()
as needed to avoid a build issue in the fxls8962af driver as a result of a
workaround for a device errata that only applies when i2c interface is used.
Counters
========
New device support
* intel,quadrature encoder peripheral found on Elkhart Lake platforms.
- New driver.
IIO
===
New device support
* amstaos,tsl2591 ambient light sensor.
- New driver + bindings
- Follow up fix to ensure some local variables are suitable for error
handling.
* fsl,fxls8962af + fsl,fxls8964af accelerometers
- New driver + bindings
- Includes an errata work around that cause a build bot failure fixed
by adding a stub to i2c.
* kionix,kxcjk-1013
- Add support for KX023-1025 device. Mostly a different register map
that needed to be supported.
* murata,sca3300 accelerometer
- New driver + bindings
* st,lsm9ds0 IMU
- Rework of st,sensors driver to cleanly support this new glue driver
that enables the two parts of the LSM9DS0.
* ti,tsc2046 touchscreen controller ADC.
- New driver. Intent here is to use this with existing IIO consumer
drivers such as resistive-adc-touch.
- Follow up fix to avoid an issue with unsigned subtraction in error
check.
* ti,tmp117 digital temperature sensor
- New driver + bindings
Features
* adi,ad5755
- Add missing dt-binding doc
* adi,ad7298
- Add ACPI ID used on Intel Galileo Gen 1 boards.
- Add missing dt-binding doc
* adi,ad7476
- Add missing dt-binding doc
* adi,ad7746
- Add missing dt-binding doc for this driver that will hopefully move out
of staging shortly. Update staging driver to use the binding instead of
platform data.
* adi,adis16201 + adis16209
- Add missing dt-binding doc
* adi,adis16480
- Support burst mode for adis16495 and adis16497 parts.
* bosch,bma220
- Add missing dt-binding doc
* fsl,mma7455
- Add missing dt-binding doc
* iio-rescale
- Support handling of processed channels from provider. Some ADCs
require (typically non linear) calibration functions to be applied,
and so provide only IIO_CHAN_INFO_PROCESSED read back. They can be
used as providers to the iio-rescale driver which needs to handle them
somewhat differently from IIO_CHAN_INFO_RAW
* sensiron,sps30
- Support the serial interface. Note this required significant
refactoring of existing driver.
* st,st-sensors
- Add mount matrix support for normal dt-binding whilst continuing to
support the odd ACPI approach for accelerometers.
* ti,dac082s085 + similar
- Add missing dt-binding doc
* trivial-devices - add entries for
- memsic,mx4005, memsic,mx6255 and memsic,mxc6655
- sensortek,stk8312 and sensortek,stk8ba50
Cleanup / minor fixes
* core
- Use devm_add_action_or_reset() to replace boilerplate in several
driver and core IIO devm_* functions.
- Fix an error path issue introduced by above, that could return an
error pointer rather than the expected null from dev_iio_device_alloc()
- Move more IIO internals related fields from struct iio_dev to
struct iio_dev_opaque.
- Drop unused final update of in_loc in demux setup.
* Docs
- Move some docs from driver specific to core dos to avoid replication
of names which the documentation builder does not allow.
Note this means adding a few device specific notes to the general docs
to cover the more unusual uses of the ABI.
- ABI: Move old buffer/* and scan_elements/* docs to obsolete as now we
have the bufferX/* variant. Not we are not getting rid of these
interfaces, just encouraging new code to use the new interface.
* IIO wide:
- Tidy up new cases of dev.parent etc being set in drivers as the core
now does it.
- Fix more cases of possible miss-aligned buffers when passed to
iio_push_to_buffers_with_timestamp(). Note we only have one known
instance of anyone seeing this bug actually happening, so this has been
a low priority cleanup effort for several cycles.
- sysfs_emit() used in more drivers.
- Runtime pm tidy up and use of pm_runtime_resume_and_get()
- Buffer alignment fixes as iio_push_to_buffers_with_timestamp requires
that the timestamp when inserted by naturally aligned + consumers can
assume that all fields are naturally aligned. Part of a long running
effort, with at least 2 more series to come tackling additional
variants.
- Stop specifying "mount-matrix" property name in every lookup of the
mount matrix from firmware by hard coding it in the core.
* adi,ad7476
- Handle the variety of different regulators used by the parts supported
by this driver (came up in dt-binding review)
* adi,ad7746
- Trivial drop of if (ret) return ret; return 0; pattern
- Tidy up comments
- Pull capdac setup out to own function.
* adi,ad7766
- Trivial drop of if (ret) return ret; return 0; pattern
* adi,adis
- Avoid returning error codes in interrupt handlers.
- Handle a failure in spi_write in the trigger handler.
- Rework to add updating of device page after changing it.
- Don't push data to IIO buffers when read failed.
- Add configuration of burst max speed to core avoid handling this in
each driver.
- Use the adis_dev_lock() helper in adis16260 and adis16136 drivers.
- Excessive includes cleanup via include-what-you-use static checker
after zero day highlighted that these needed updating.
* afe
- Amend binding to add #io-channel-cells, thus allowing this IIO
consumer to also be an IIO provider.
* aosong,am2315
- Drop ACPI id. Unlikely this one is in the wild and it isn't valid
ACPI naming.
* bosch,bma180
- Adding missing bandwidth settings (500, 1000 Hz)
* bosch,bme680
- Drop ACPI id. Unlikely this one is in the wild and it isn't valid
ACPI naming.
* ep93xx_adc,
- Drop a redundant error print.
* maxim,max118
- Convert remainder of probe() to devm_ managed functions.
- Avoid some repeated jumping back and forth between iio_dev and
spi structures.
* maxim,max11100
- Use get_unaligned_be16() instead of open coding.
- Convert remainder of probe() to devm_ managed functions.
* samsung,exynos_adc
- Unused error value dropped.
* sensiron,sgp30
- Drop use of %hx in favor of %x and letting the normal type conversion
work.
* sensortek,stk8312
- Add lowercase device id and note uppercase version deprecated.
- Drop ACPI id. Unlikely this one is in the wild and it isn't valid
ACPI naming.
* sprx,sc72xx_adc
- add MODULE_DEVICE_TABLE
* st,lsm6dsx
- Fix docs of valid ODRs
* st,sensors
- dt-binding rework. Two efforts on this crossed in a previous cycle
so this update cherry picks the best of the two yaml conversions.
- Don't copy the channel spec array as now ext_info is no longer modified.
* st,stm32-adc
- tidy up some docs that were marked as kernel-doc but aren't.
* ti,adc081c, ti,adc0832, ti,adc108s102 and ti,adc161s626
- Convert remainder of probe() functions to devm_ managed functions
to simplify error handing and remove paths.
* tag 'iio-for-5.14a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (171 commits)
i2c: core: Add stub for i2c_verify_client() if !CONFIG_I2C
iio: adis: Cleanout unused headers
iio: accel: bma180: Add missing 500 Hz / 1000 Hz bandwidth
counter: Add support for Intel Quadrature Encoder Peripheral
staging: iio: cdc: ad7746: extract capac setup to own function
staging: iio: cdc: ad7746: clean up probe return
staging: iio: cdc: ad7746: remove ordinary comments
iio: adc: ti-adc161s626: Use devm managed functions for all of probe.
iio: adc: ti-adc108s102: Use devm managed functions for all of probe()
iio: adc: ti-adc0832: Use devm managed functions for all of probe()
iio: adc: ti-adc081c: Use devm managed functions for all of probe()
iio: adc: max1118: Avoid jumping back and forth between spi and iio structures
iio: adc: max1118: Use devm_ managed functions for all of probe
iio: adc: max11100: Use devm_ functions for rest of probe()
iio: adc: max11100: Use get_unaligned_be16() rather than opencoding.
iio: chemical: sgp30: Drop use of %hx in format string.
iio: gyro: st_gyro: Support mount matrix
iio: magnetometer: st_magn: Support mount matrix
iio: accel: st_sensors: Stop copying channels
iio: accel: st_sensors: Support generic mounting matrix
...
Diffstat (limited to 'drivers/counter')
-rw-r--r-- | drivers/counter/Kconfig | 10 | ||||
-rw-r--r-- | drivers/counter/Makefile | 1 | ||||
-rw-r--r-- | drivers/counter/intel-qep.c | 546 |
3 files changed, 557 insertions, 0 deletions
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 5328705aa09c..d5d2540b30c2 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -91,4 +91,14 @@ config MICROCHIP_TCB_CAPTURE To compile this driver as a module, choose M here: the module will be called microchip-tcb-capture. +config INTEL_QEP + tristate "Intel Quadrature Encoder Peripheral driver" + depends on PCI + help + Select this option to enable the Intel Quadrature Encoder Peripheral + driver. + + To compile this driver as a module, choose M here: the module + will be called intel-qep. + endif # COUNTER diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index cb646ed2f039..19742e6f5e3e 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o 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 diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c new file mode 100644 index 000000000000..ab10ba33f46a --- /dev/null +++ b/drivers/counter/intel-qep.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Quadrature Encoder Peripheral driver + * + * Copyright (C) 2019-2021 Intel Corporation + * + * Author: Felipe Balbi (Intel) + * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com> + * Author: Raymond Tan <raymond.tan@intel.com> + */ +#include <linux/bitops.h> +#include <linux/counter.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> + +#define INTEL_QEPCON 0x00 +#define INTEL_QEPFLT 0x04 +#define INTEL_QEPCOUNT 0x08 +#define INTEL_QEPMAX 0x0c +#define INTEL_QEPWDT 0x10 +#define INTEL_QEPCAPDIV 0x14 +#define INTEL_QEPCNTR 0x18 +#define INTEL_QEPCAPBUF 0x1c +#define INTEL_QEPINT_STAT 0x20 +#define INTEL_QEPINT_MASK 0x24 + +/* QEPCON */ +#define INTEL_QEPCON_EN BIT(0) +#define INTEL_QEPCON_FLT_EN BIT(1) +#define INTEL_QEPCON_EDGE_A BIT(2) +#define INTEL_QEPCON_EDGE_B BIT(3) +#define INTEL_QEPCON_EDGE_INDX BIT(4) +#define INTEL_QEPCON_SWPAB BIT(5) +#define INTEL_QEPCON_OP_MODE BIT(6) +#define INTEL_QEPCON_PH_ERR BIT(7) +#define INTEL_QEPCON_COUNT_RST_MODE BIT(8) +#define INTEL_QEPCON_INDX_GATING_MASK GENMASK(10, 9) +#define INTEL_QEPCON_INDX_GATING(n) (((n) & 3) << 9) +#define INTEL_QEPCON_INDX_PAL_PBL INTEL_QEPCON_INDX_GATING(0) +#define INTEL_QEPCON_INDX_PAL_PBH INTEL_QEPCON_INDX_GATING(1) +#define INTEL_QEPCON_INDX_PAH_PBL INTEL_QEPCON_INDX_GATING(2) +#define INTEL_QEPCON_INDX_PAH_PBH INTEL_QEPCON_INDX_GATING(3) +#define INTEL_QEPCON_CAP_MODE BIT(11) +#define INTEL_QEPCON_FIFO_THRE_MASK GENMASK(14, 12) +#define INTEL_QEPCON_FIFO_THRE(n) ((((n) - 1) & 7) << 12) +#define INTEL_QEPCON_FIFO_EMPTY BIT(15) + +/* QEPFLT */ +#define INTEL_QEPFLT_MAX_COUNT(n) ((n) & 0x1fffff) + +/* QEPINT */ +#define INTEL_QEPINT_FIFOCRIT BIT(5) +#define INTEL_QEPINT_FIFOENTRY BIT(4) +#define INTEL_QEPINT_QEPDIR BIT(3) +#define INTEL_QEPINT_QEPRST_UP BIT(2) +#define INTEL_QEPINT_QEPRST_DOWN BIT(1) +#define INTEL_QEPINT_WDT BIT(0) + +#define INTEL_QEPINT_MASK_ALL GENMASK(5, 0) + +#define INTEL_QEP_CLK_PERIOD_NS 10 + +#define INTEL_QEP_COUNTER_EXT_RW(_name) \ +{ \ + .name = #_name, \ + .read = _name##_read, \ + .write = _name##_write, \ +} + +struct intel_qep { + struct counter_device counter; + struct mutex lock; + struct device *dev; + void __iomem *regs; + bool enabled; + /* Context save registers */ + u32 qepcon; + u32 qepflt; + u32 qepmax; +}; + +static inline u32 intel_qep_readl(struct intel_qep *qep, u32 offset) +{ + return readl(qep->regs + offset); +} + +static inline void intel_qep_writel(struct intel_qep *qep, + u32 offset, u32 value) +{ + writel(value, qep->regs + offset); +} + +static void intel_qep_init(struct intel_qep *qep) +{ + u32 reg; + + reg = intel_qep_readl(qep, INTEL_QEPCON); + reg &= ~INTEL_QEPCON_EN; + intel_qep_writel(qep, INTEL_QEPCON, reg); + qep->enabled = false; + /* + * Make sure peripheral is disabled by flushing the write with + * a dummy read + */ + reg = intel_qep_readl(qep, INTEL_QEPCON); + + reg &= ~(INTEL_QEPCON_OP_MODE | INTEL_QEPCON_FLT_EN); + reg |= INTEL_QEPCON_EDGE_A | INTEL_QEPCON_EDGE_B | + INTEL_QEPCON_EDGE_INDX | INTEL_QEPCON_COUNT_RST_MODE; + intel_qep_writel(qep, INTEL_QEPCON, reg); + intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL); +} + +static int intel_qep_count_read(struct counter_device *counter, + struct counter_count *count, + unsigned long *val) +{ + struct intel_qep *const qep = counter->priv; + + pm_runtime_get_sync(qep->dev); + *val = intel_qep_readl(qep, INTEL_QEPCOUNT); + pm_runtime_put(qep->dev); + + return 0; +} + +static const enum counter_count_function intel_qep_count_functions[] = { + COUNTER_COUNT_FUNCTION_QUADRATURE_X4, +}; + +static int intel_qep_function_get(struct counter_device *counter, + struct counter_count *count, + size_t *function) +{ + *function = 0; + + return 0; +} + +static const enum counter_synapse_action intel_qep_synapse_actions[] = { + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, +}; + +static int intel_qep_action_get(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + size_t *action) +{ + *action = 0; + return 0; +} + +static const struct counter_ops intel_qep_counter_ops = { + .count_read = intel_qep_count_read, + .function_get = intel_qep_function_get, + .action_get = intel_qep_action_get, +}; + +#define INTEL_QEP_SIGNAL(_id, _name) { \ + .id = (_id), \ + .name = (_name), \ +} + +static struct counter_signal intel_qep_signals[] = { + INTEL_QEP_SIGNAL(0, "Phase A"), + INTEL_QEP_SIGNAL(1, "Phase B"), + INTEL_QEP_SIGNAL(2, "Index"), +}; + +#define INTEL_QEP_SYNAPSE(_signal_id) { \ + .actions_list = intel_qep_synapse_actions, \ + .num_actions = ARRAY_SIZE(intel_qep_synapse_actions), \ + .signal = &intel_qep_signals[(_signal_id)], \ +} + +static struct counter_synapse intel_qep_count_synapses[] = { + INTEL_QEP_SYNAPSE(0), + INTEL_QEP_SYNAPSE(1), + INTEL_QEP_SYNAPSE(2), +}; + +static ssize_t ceiling_read(struct counter_device *counter, + struct counter_count *count, + void *priv, char *buf) +{ + struct intel_qep *qep = counter->priv; + u32 reg; + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPMAX); + pm_runtime_put(qep->dev); + + return sysfs_emit(buf, "%u\n", reg); +} + +static ssize_t ceiling_write(struct counter_device *counter, + struct counter_count *count, + void *priv, const char *buf, size_t len) +{ + struct intel_qep *qep = counter->priv; + u32 max; + int ret; + + ret = kstrtou32(buf, 0, &max); + if (ret < 0) + return ret; + + mutex_lock(&qep->lock); + if (qep->enabled) { + ret = -EBUSY; + goto out; + } + + pm_runtime_get_sync(qep->dev); + intel_qep_writel(qep, INTEL_QEPMAX, max); + pm_runtime_put(qep->dev); + ret = len; + +out: + mutex_unlock(&qep->lock); + return ret; +} + +static ssize_t enable_read(struct counter_device *counter, + struct counter_count *count, + void *priv, char *buf) +{ + struct intel_qep *qep = counter->priv; + + return sysfs_emit(buf, "%u\n", qep->enabled); +} + +static ssize_t enable_write(struct counter_device *counter, + struct counter_count *count, + void *priv, const char *buf, size_t len) +{ + struct intel_qep *qep = counter->priv; + u32 reg; + bool val, changed; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + mutex_lock(&qep->lock); + changed = val ^ qep->enabled; + if (!changed) + goto out; + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPCON); + if (val) { + /* Enable peripheral and keep runtime PM always on */ + reg |= INTEL_QEPCON_EN; + pm_runtime_get_noresume(qep->dev); + } else { + /* Let runtime PM be idle and disable peripheral */ + pm_runtime_put_noidle(qep->dev); + reg &= ~INTEL_QEPCON_EN; + } + intel_qep_writel(qep, INTEL_QEPCON, reg); + pm_runtime_put(qep->dev); + qep->enabled = val; + +out: + mutex_unlock(&qep->lock); + return len; +} + +static ssize_t spike_filter_ns_read(struct counter_device *counter, + struct counter_count *count, + void *priv, char *buf) +{ + struct intel_qep *qep = counter->priv; + u32 reg; + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPCON); + if (!(reg & INTEL_QEPCON_FLT_EN)) { + pm_runtime_put(qep->dev); + return sysfs_emit(buf, "0\n"); + } + reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT)); + pm_runtime_put(qep->dev); + + return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS); +} + +static ssize_t spike_filter_ns_write(struct counter_device *counter, + struct counter_count *count, + void *priv, const char *buf, size_t len) +{ + struct intel_qep *qep = counter->priv; + u32 reg, length; + bool enable; + int ret; + + ret = kstrtou32(buf, 0, &length); + if (ret < 0) + return ret; + + /* + * Spike filter length is (MAX_COUNT + 2) clock periods. + * Disable filter when userspace writes 0, enable for valid + * nanoseconds values and error out otherwise. + */ + length /= INTEL_QEP_CLK_PERIOD_NS; + if (length == 0) { + enable = false; + length = 0; + } else if (length >= 2) { + enable = true; + length -= 2; + } else { + return -EINVAL; + } + + if (length > INTEL_QEPFLT_MAX_COUNT(length)) + return -EINVAL; + + mutex_lock(&qep->lock); + if (qep->enabled) { + ret = -EBUSY; + goto out; + } + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPCON); + if (enable) + reg |= INTEL_QEPCON_FLT_EN; + else + reg &= ~INTEL_QEPCON_FLT_EN; + intel_qep_writel(qep, INTEL_QEPFLT, length); + intel_qep_writel(qep, INTEL_QEPCON, reg); + pm_runtime_put(qep->dev); + ret = len; + +out: + mutex_unlock(&qep->lock); + return ret; +} + +static ssize_t preset_enable_read(struct counter_device *counter, + struct counter_count *count, + void *priv, char *buf) +{ + struct intel_qep *qep = counter->priv; + u32 reg; + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPCON); + pm_runtime_put(qep->dev); + return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE)); +} + +static ssize_t preset_enable_write(struct counter_device *counter, + struct counter_count *count, + void *priv, const char *buf, size_t len) +{ + struct intel_qep *qep = counter->priv; + u32 reg; + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + mutex_lock(&qep->lock); + if (qep->enabled) { + ret = -EBUSY; + goto out; + } + + pm_runtime_get_sync(qep->dev); + reg = intel_qep_readl(qep, INTEL_QEPCON); + if (val) + reg &= ~INTEL_QEPCON_COUNT_RST_MODE; + else + reg |= INTEL_QEPCON_COUNT_RST_MODE; + + intel_qep_writel(qep, INTEL_QEPCON, reg); + pm_runtime_put(qep->dev); + ret = len; + +out: + mutex_unlock(&qep->lock); + + return ret; +} + +static const struct counter_count_ext intel_qep_count_ext[] = { + INTEL_QEP_COUNTER_EXT_RW(ceiling), + INTEL_QEP_COUNTER_EXT_RW(enable), + INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns), + INTEL_QEP_COUNTER_EXT_RW(preset_enable) +}; + +static struct counter_count intel_qep_counter_count[] = { + { + .id = 0, + .name = "Channel 1 Count", + .functions_list = intel_qep_count_functions, + .num_functions = ARRAY_SIZE(intel_qep_count_functions), + .synapses = intel_qep_count_synapses, + .num_synapses = ARRAY_SIZE(intel_qep_count_synapses), + .ext = intel_qep_count_ext, + .num_ext = ARRAY_SIZE(intel_qep_count_ext), + }, +}; + +static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct intel_qep *qep; + struct device *dev = &pci->dev; + void __iomem *regs; + int ret; + + qep = devm_kzalloc(dev, sizeof(*qep), GFP_KERNEL); + if (!qep) + return -ENOMEM; + + ret = pcim_enable_device(pci); + if (ret) + return ret; + + pci_set_master(pci); + + ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); + if (ret) + return ret; + + regs = pcim_iomap_table(pci)[0]; + if (!regs) + return -ENOMEM; + + qep->dev = dev; + qep->regs = regs; + mutex_init(&qep->lock); + + intel_qep_init(qep); + pci_set_drvdata(pci, qep); + + qep->counter.name = pci_name(pci); + qep->counter.parent = dev; + qep->counter.ops = &intel_qep_counter_ops; + qep->counter.counts = intel_qep_counter_count; + qep->counter.num_counts = ARRAY_SIZE(intel_qep_counter_count); + qep->counter.signals = intel_qep_signals; + qep->counter.num_signals = ARRAY_SIZE(intel_qep_signals); + qep->counter.priv = qep; + qep->enabled = false; + + pm_runtime_put(dev); + pm_runtime_allow(dev); + + return devm_counter_register(&pci->dev, &qep->counter); +} + +static void intel_qep_remove(struct pci_dev *pci) +{ + struct intel_qep *qep = pci_get_drvdata(pci); + struct device *dev = &pci->dev; + + pm_runtime_forbid(dev); + if (!qep->enabled) + pm_runtime_get(dev); + + intel_qep_writel(qep, INTEL_QEPCON, 0); +} + +#ifdef CONFIG_PM +static int intel_qep_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct intel_qep *qep = pci_get_drvdata(pdev); + + qep->qepcon = intel_qep_readl(qep, INTEL_QEPCON); + qep->qepflt = intel_qep_readl(qep, INTEL_QEPFLT); + qep->qepmax = intel_qep_readl(qep, INTEL_QEPMAX); + + return 0; +} + +static int intel_qep_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct intel_qep *qep = pci_get_drvdata(pdev); + + /* + * Make sure peripheral is disabled when restoring registers and + * control register bits that are writable only when the peripheral + * is disabled + */ + intel_qep_writel(qep, INTEL_QEPCON, 0); + intel_qep_readl(qep, INTEL_QEPCON); + + intel_qep_writel(qep, INTEL_QEPFLT, qep->qepflt); + intel_qep_writel(qep, INTEL_QEPMAX, qep->qepmax); + intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL); + + /* Restore all other control register bits except enable status */ + intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon & ~INTEL_QEPCON_EN); + intel_qep_readl(qep, INTEL_QEPCON); + + /* Restore enable status */ + intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon); + + return 0; +} +#endif + +static UNIVERSAL_DEV_PM_OPS(intel_qep_pm_ops, + intel_qep_suspend, intel_qep_resume, NULL); + +static const struct pci_device_id intel_qep_id_table[] = { + /* EHL */ + { PCI_VDEVICE(INTEL, 0x4bc3), }, + { PCI_VDEVICE(INTEL, 0x4b81), }, + { PCI_VDEVICE(INTEL, 0x4b82), }, + { PCI_VDEVICE(INTEL, 0x4b83), }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(pci, intel_qep_id_table); + +static struct pci_driver intel_qep_driver = { + .name = "intel-qep", + .id_table = intel_qep_id_table, + .probe = intel_qep_probe, + .remove = intel_qep_remove, + .driver = { + .pm = &intel_qep_pm_ops, + } +}; + +module_pci_driver(intel_qep_driver); + +MODULE_AUTHOR("Felipe Balbi (Intel)"); +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"); |