brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · af5942e Raw
527 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Intel Quadrature Encoder Peripheral driver4 *5 * Copyright (C) 2019-2021 Intel Corporation6 *7 * Author: Felipe Balbi (Intel)8 * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>9 * Author: Raymond Tan <raymond.tan@intel.com>10 */11#include <linux/counter.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/mutex.h>15#include <linux/pci.h>16#include <linux/pm_runtime.h>17 18#define INTEL_QEPCON			0x0019#define INTEL_QEPFLT			0x0420#define INTEL_QEPCOUNT			0x0821#define INTEL_QEPMAX			0x0c22#define INTEL_QEPWDT			0x1023#define INTEL_QEPCAPDIV			0x1424#define INTEL_QEPCNTR			0x1825#define INTEL_QEPCAPBUF			0x1c26#define INTEL_QEPINT_STAT		0x2027#define INTEL_QEPINT_MASK		0x2428 29/* QEPCON */30#define INTEL_QEPCON_EN			BIT(0)31#define INTEL_QEPCON_FLT_EN		BIT(1)32#define INTEL_QEPCON_EDGE_A		BIT(2)33#define INTEL_QEPCON_EDGE_B		BIT(3)34#define INTEL_QEPCON_EDGE_INDX		BIT(4)35#define INTEL_QEPCON_SWPAB		BIT(5)36#define INTEL_QEPCON_OP_MODE		BIT(6)37#define INTEL_QEPCON_PH_ERR		BIT(7)38#define INTEL_QEPCON_COUNT_RST_MODE	BIT(8)39#define INTEL_QEPCON_INDX_GATING_MASK	GENMASK(10, 9)40#define INTEL_QEPCON_INDX_GATING(n)	(((n) & 3) << 9)41#define INTEL_QEPCON_INDX_PAL_PBL	INTEL_QEPCON_INDX_GATING(0)42#define INTEL_QEPCON_INDX_PAL_PBH	INTEL_QEPCON_INDX_GATING(1)43#define INTEL_QEPCON_INDX_PAH_PBL	INTEL_QEPCON_INDX_GATING(2)44#define INTEL_QEPCON_INDX_PAH_PBH	INTEL_QEPCON_INDX_GATING(3)45#define INTEL_QEPCON_CAP_MODE		BIT(11)46#define INTEL_QEPCON_FIFO_THRE_MASK	GENMASK(14, 12)47#define INTEL_QEPCON_FIFO_THRE(n)	((((n) - 1) & 7) << 12)48#define INTEL_QEPCON_FIFO_EMPTY		BIT(15)49 50/* QEPFLT */51#define INTEL_QEPFLT_MAX_COUNT(n)	((n) & 0x1fffff)52 53/* QEPINT */54#define INTEL_QEPINT_FIFOCRIT		BIT(5)55#define INTEL_QEPINT_FIFOENTRY		BIT(4)56#define INTEL_QEPINT_QEPDIR		BIT(3)57#define INTEL_QEPINT_QEPRST_UP		BIT(2)58#define INTEL_QEPINT_QEPRST_DOWN	BIT(1)59#define INTEL_QEPINT_WDT		BIT(0)60 61#define INTEL_QEPINT_MASK_ALL		GENMASK(5, 0)62 63#define INTEL_QEP_CLK_PERIOD_NS		1064 65struct intel_qep {66	struct mutex lock;67	struct device *dev;68	void __iomem *regs;69	bool enabled;70	/* Context save registers */71	u32 qepcon;72	u32 qepflt;73	u32 qepmax;74};75 76static inline u32 intel_qep_readl(struct intel_qep *qep, u32 offset)77{78	return readl(qep->regs + offset);79}80 81static inline void intel_qep_writel(struct intel_qep *qep,82				    u32 offset, u32 value)83{84	writel(value, qep->regs + offset);85}86 87static void intel_qep_init(struct intel_qep *qep)88{89	u32 reg;90 91	reg = intel_qep_readl(qep, INTEL_QEPCON);92	reg &= ~INTEL_QEPCON_EN;93	intel_qep_writel(qep, INTEL_QEPCON, reg);94	qep->enabled = false;95	/*96	 * Make sure peripheral is disabled by flushing the write with97	 * a dummy read98	 */99	reg = intel_qep_readl(qep, INTEL_QEPCON);100 101	reg &= ~(INTEL_QEPCON_OP_MODE | INTEL_QEPCON_FLT_EN);102	reg |= INTEL_QEPCON_EDGE_A | INTEL_QEPCON_EDGE_B |103	       INTEL_QEPCON_EDGE_INDX | INTEL_QEPCON_COUNT_RST_MODE;104	intel_qep_writel(qep, INTEL_QEPCON, reg);105	intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL);106}107 108static int intel_qep_count_read(struct counter_device *counter,109				struct counter_count *count, u64 *val)110{111	struct intel_qep *const qep = counter_priv(counter);112 113	pm_runtime_get_sync(qep->dev);114	*val = intel_qep_readl(qep, INTEL_QEPCOUNT);115	pm_runtime_put(qep->dev);116 117	return 0;118}119 120static const enum counter_function intel_qep_count_functions[] = {121	COUNTER_FUNCTION_QUADRATURE_X4,122};123 124static int intel_qep_function_read(struct counter_device *counter,125				   struct counter_count *count,126				   enum counter_function *function)127{128	*function = COUNTER_FUNCTION_QUADRATURE_X4;129 130	return 0;131}132 133static const enum counter_synapse_action intel_qep_synapse_actions[] = {134	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,135};136 137static int intel_qep_action_read(struct counter_device *counter,138				 struct counter_count *count,139				 struct counter_synapse *synapse,140				 enum counter_synapse_action *action)141{142	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;143	return 0;144}145 146static const struct counter_ops intel_qep_counter_ops = {147	.count_read = intel_qep_count_read,148	.function_read = intel_qep_function_read,149	.action_read = intel_qep_action_read,150};151 152#define INTEL_QEP_SIGNAL(_id, _name) {				\153	.id = (_id),						\154	.name = (_name),					\155}156 157static struct counter_signal intel_qep_signals[] = {158	INTEL_QEP_SIGNAL(0, "Phase A"),159	INTEL_QEP_SIGNAL(1, "Phase B"),160	INTEL_QEP_SIGNAL(2, "Index"),161};162 163#define INTEL_QEP_SYNAPSE(_signal_id) {				\164	.actions_list = intel_qep_synapse_actions,		\165	.num_actions = ARRAY_SIZE(intel_qep_synapse_actions),	\166	.signal = &intel_qep_signals[(_signal_id)],		\167}168 169static struct counter_synapse intel_qep_count_synapses[] = {170	INTEL_QEP_SYNAPSE(0),171	INTEL_QEP_SYNAPSE(1),172	INTEL_QEP_SYNAPSE(2),173};174 175static int intel_qep_ceiling_read(struct counter_device *counter,176				  struct counter_count *count, u64 *ceiling)177{178	struct intel_qep *qep = counter_priv(counter);179 180	pm_runtime_get_sync(qep->dev);181	*ceiling = intel_qep_readl(qep, INTEL_QEPMAX);182	pm_runtime_put(qep->dev);183 184	return 0;185}186 187static int intel_qep_ceiling_write(struct counter_device *counter,188				   struct counter_count *count, u64 max)189{190	struct intel_qep *qep = counter_priv(counter);191	int ret = 0;192 193	/* Intel QEP ceiling configuration only supports 32-bit values */194	if (max != (u32)max)195		return -ERANGE;196 197	mutex_lock(&qep->lock);198	if (qep->enabled) {199		ret = -EBUSY;200		goto out;201	}202 203	pm_runtime_get_sync(qep->dev);204	intel_qep_writel(qep, INTEL_QEPMAX, max);205	pm_runtime_put(qep->dev);206 207out:208	mutex_unlock(&qep->lock);209	return ret;210}211 212static int intel_qep_enable_read(struct counter_device *counter,213				 struct counter_count *count, u8 *enable)214{215	struct intel_qep *qep = counter_priv(counter);216 217	*enable = qep->enabled;218 219	return 0;220}221 222static int intel_qep_enable_write(struct counter_device *counter,223				  struct counter_count *count, u8 val)224{225	struct intel_qep *qep = counter_priv(counter);226	u32 reg;227	bool changed;228 229	mutex_lock(&qep->lock);230	changed = val ^ qep->enabled;231	if (!changed)232		goto out;233 234	pm_runtime_get_sync(qep->dev);235	reg = intel_qep_readl(qep, INTEL_QEPCON);236	if (val) {237		/* Enable peripheral and keep runtime PM always on */238		reg |= INTEL_QEPCON_EN;239		pm_runtime_get_noresume(qep->dev);240	} else {241		/* Let runtime PM be idle and disable peripheral */242		pm_runtime_put_noidle(qep->dev);243		reg &= ~INTEL_QEPCON_EN;244	}245	intel_qep_writel(qep, INTEL_QEPCON, reg);246	pm_runtime_put(qep->dev);247	qep->enabled = val;248 249out:250	mutex_unlock(&qep->lock);251	return 0;252}253 254static int intel_qep_spike_filter_ns_read(struct counter_device *counter,255					  struct counter_count *count,256					  u64 *length)257{258	struct intel_qep *qep = counter_priv(counter);259	u32 reg;260 261	pm_runtime_get_sync(qep->dev);262	reg = intel_qep_readl(qep, INTEL_QEPCON);263	if (!(reg & INTEL_QEPCON_FLT_EN)) {264		pm_runtime_put(qep->dev);265		return 0;266	}267	reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));268	pm_runtime_put(qep->dev);269 270	*length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS;271 272	return 0;273}274 275static int intel_qep_spike_filter_ns_write(struct counter_device *counter,276					   struct counter_count *count,277					   u64 length)278{279	struct intel_qep *qep = counter_priv(counter);280	u32 reg;281	bool enable;282	int ret = 0;283 284	/*285	 * Spike filter length is (MAX_COUNT + 2) clock periods.286	 * Disable filter when userspace writes 0, enable for valid287	 * nanoseconds values and error out otherwise.288	 */289	do_div(length, INTEL_QEP_CLK_PERIOD_NS);290	if (length == 0) {291		enable = false;292		length = 0;293	} else if (length >= 2) {294		enable = true;295		length -= 2;296	} else {297		return -EINVAL;298	}299 300	if (length > INTEL_QEPFLT_MAX_COUNT(length))301		return -ERANGE;302 303	mutex_lock(&qep->lock);304	if (qep->enabled) {305		ret = -EBUSY;306		goto out;307	}308 309	pm_runtime_get_sync(qep->dev);310	reg = intel_qep_readl(qep, INTEL_QEPCON);311	if (enable)312		reg |= INTEL_QEPCON_FLT_EN;313	else314		reg &= ~INTEL_QEPCON_FLT_EN;315	intel_qep_writel(qep, INTEL_QEPFLT, length);316	intel_qep_writel(qep, INTEL_QEPCON, reg);317	pm_runtime_put(qep->dev);318 319out:320	mutex_unlock(&qep->lock);321	return ret;322}323 324static int intel_qep_preset_enable_read(struct counter_device *counter,325					struct counter_count *count,326					u8 *preset_enable)327{328	struct intel_qep *qep = counter_priv(counter);329	u32 reg;330 331	pm_runtime_get_sync(qep->dev);332	reg = intel_qep_readl(qep, INTEL_QEPCON);333	pm_runtime_put(qep->dev);334 335	*preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE);336 337	return 0;338}339 340static int intel_qep_preset_enable_write(struct counter_device *counter,341					 struct counter_count *count, u8 val)342{343	struct intel_qep *qep = counter_priv(counter);344	u32 reg;345	int ret = 0;346 347	mutex_lock(&qep->lock);348	if (qep->enabled) {349		ret = -EBUSY;350		goto out;351	}352 353	pm_runtime_get_sync(qep->dev);354	reg = intel_qep_readl(qep, INTEL_QEPCON);355	if (val)356		reg &= ~INTEL_QEPCON_COUNT_RST_MODE;357	else358		reg |= INTEL_QEPCON_COUNT_RST_MODE;359 360	intel_qep_writel(qep, INTEL_QEPCON, reg);361	pm_runtime_put(qep->dev);362 363out:364	mutex_unlock(&qep->lock);365 366	return ret;367}368 369static struct counter_comp intel_qep_count_ext[] = {370	COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write),371	COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write),372	COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read,373				   intel_qep_preset_enable_write),374	COUNTER_COMP_COUNT_U64("spike_filter_ns",375			       intel_qep_spike_filter_ns_read,376			       intel_qep_spike_filter_ns_write),377};378 379static struct counter_count intel_qep_counter_count[] = {380	{381		.id = 0,382		.name = "Channel 1 Count",383		.functions_list = intel_qep_count_functions,384		.num_functions = ARRAY_SIZE(intel_qep_count_functions),385		.synapses = intel_qep_count_synapses,386		.num_synapses = ARRAY_SIZE(intel_qep_count_synapses),387		.ext = intel_qep_count_ext,388		.num_ext = ARRAY_SIZE(intel_qep_count_ext),389	},390};391 392static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id)393{394	struct counter_device *counter;395	struct intel_qep *qep;396	struct device *dev = &pci->dev;397	void __iomem *regs;398	int ret;399 400	counter = devm_counter_alloc(dev, sizeof(*qep));401	if (!counter)402		return -ENOMEM;403	qep = counter_priv(counter);404 405	ret = pcim_enable_device(pci);406	if (ret)407		return ret;408 409	pci_set_master(pci);410 411	ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci));412	if (ret)413		return ret;414 415	regs = pcim_iomap_table(pci)[0];416	if (!regs)417		return -ENOMEM;418 419	qep->dev = dev;420	qep->regs = regs;421	mutex_init(&qep->lock);422 423	intel_qep_init(qep);424	pci_set_drvdata(pci, qep);425 426	counter->name = pci_name(pci);427	counter->parent = dev;428	counter->ops = &intel_qep_counter_ops;429	counter->counts = intel_qep_counter_count;430	counter->num_counts = ARRAY_SIZE(intel_qep_counter_count);431	counter->signals = intel_qep_signals;432	counter->num_signals = ARRAY_SIZE(intel_qep_signals);433	qep->enabled = false;434 435	pm_runtime_put(dev);436	pm_runtime_allow(dev);437 438	ret = devm_counter_add(&pci->dev, counter);439	if (ret < 0)440		return dev_err_probe(&pci->dev, ret, "Failed to add counter\n");441 442	return 0;443}444 445static void intel_qep_remove(struct pci_dev *pci)446{447	struct intel_qep *qep = pci_get_drvdata(pci);448	struct device *dev = &pci->dev;449 450	pm_runtime_forbid(dev);451	if (!qep->enabled)452		pm_runtime_get(dev);453 454	intel_qep_writel(qep, INTEL_QEPCON, 0);455}456 457static int __maybe_unused intel_qep_suspend(struct device *dev)458{459	struct pci_dev *pdev = to_pci_dev(dev);460	struct intel_qep *qep = pci_get_drvdata(pdev);461 462	qep->qepcon = intel_qep_readl(qep, INTEL_QEPCON);463	qep->qepflt = intel_qep_readl(qep, INTEL_QEPFLT);464	qep->qepmax = intel_qep_readl(qep, INTEL_QEPMAX);465 466	return 0;467}468 469static int __maybe_unused intel_qep_resume(struct device *dev)470{471	struct pci_dev *pdev = to_pci_dev(dev);472	struct intel_qep *qep = pci_get_drvdata(pdev);473 474	/*475	 * Make sure peripheral is disabled when restoring registers and476	 * control register bits that are writable only when the peripheral477	 * is disabled478	 */479	intel_qep_writel(qep, INTEL_QEPCON, 0);480	intel_qep_readl(qep, INTEL_QEPCON);481 482	intel_qep_writel(qep, INTEL_QEPFLT, qep->qepflt);483	intel_qep_writel(qep, INTEL_QEPMAX, qep->qepmax);484	intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL);485 486	/* Restore all other control register bits except enable status */487	intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon & ~INTEL_QEPCON_EN);488	intel_qep_readl(qep, INTEL_QEPCON);489 490	/* Restore enable status */491	intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon);492 493	return 0;494}495 496static UNIVERSAL_DEV_PM_OPS(intel_qep_pm_ops,497			    intel_qep_suspend, intel_qep_resume, NULL);498 499static const struct pci_device_id intel_qep_id_table[] = {500	/* EHL */501	{ PCI_VDEVICE(INTEL, 0x4bc3), },502	{ PCI_VDEVICE(INTEL, 0x4b81), },503	{ PCI_VDEVICE(INTEL, 0x4b82), },504	{ PCI_VDEVICE(INTEL, 0x4b83), },505	{  } /* Terminating Entry */506};507MODULE_DEVICE_TABLE(pci, intel_qep_id_table);508 509static struct pci_driver intel_qep_driver = {510	.name = "intel-qep",511	.id_table = intel_qep_id_table,512	.probe = intel_qep_probe,513	.remove = intel_qep_remove,514	.driver = {515		.pm = &intel_qep_pm_ops,516	}517};518 519module_pci_driver(intel_qep_driver);520 521MODULE_AUTHOR("Felipe Balbi (Intel)");522MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");523MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");524MODULE_LICENSE("GPL");525MODULE_DESCRIPTION("Intel Quadrature Encoder Peripheral driver");526MODULE_IMPORT_NS(COUNTER);527