summaryrefslogtreecommitdiffstats
path: root/drivers/counter
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-06-09 12:11:49 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-06-09 12:11:49 +0200
commit6771fb0b940eb74f1a68fe3f180a7668103397d3 (patch)
tree0d17a52002350df09f684277831dd939f63883d3 /drivers/counter
parent54fd727f83a4d2f9c6e85cb1fad88325a56b555f (diff)
parent41340965b4f8055f975f73e1e3d23eff8038f013 (diff)
downloadlinux-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/Kconfig10
-rw-r--r--drivers/counter/Makefile1
-rw-r--r--drivers/counter/intel-qep.c546
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");