brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · b3e615c Raw
407 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2020 Microchip4 *5 * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>6 */7#include <linux/clk.h>8#include <linux/counter.h>9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/mutex.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15#include <soc/at91/atmel_tcb.h>16 17#define ATMEL_TC_CMR_MASK	(ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \18				 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \19				 ATMEL_TC_LDBSTOP)20 21#define ATMEL_TC_QDEN			BIT(8)22#define ATMEL_TC_POSEN			BIT(9)23 24struct mchp_tc_data {25	const struct atmel_tcb_config *tc_cfg;26	struct regmap *regmap;27	int qdec_mode;28	int num_channels;29	int channel[2];30};31 32static const enum counter_function mchp_tc_count_functions[] = {33	COUNTER_FUNCTION_INCREASE,34	COUNTER_FUNCTION_QUADRATURE_X4,35};36 37static const enum counter_synapse_action mchp_tc_synapse_actions[] = {38	COUNTER_SYNAPSE_ACTION_NONE,39	COUNTER_SYNAPSE_ACTION_RISING_EDGE,40	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,41	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,42};43 44static struct counter_signal mchp_tc_count_signals[] = {45	{46		.id = 0,47		.name = "Channel A",48	},49	{50		.id = 1,51		.name = "Channel B",52	}53};54 55static struct counter_synapse mchp_tc_count_synapses[] = {56	{57		.actions_list = mchp_tc_synapse_actions,58		.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),59		.signal = &mchp_tc_count_signals[0]60	},61	{62		.actions_list = mchp_tc_synapse_actions,63		.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),64		.signal = &mchp_tc_count_signals[1]65	}66};67 68static int mchp_tc_count_function_read(struct counter_device *counter,69				       struct counter_count *count,70				       enum counter_function *function)71{72	struct mchp_tc_data *const priv = counter_priv(counter);73 74	if (priv->qdec_mode)75		*function = COUNTER_FUNCTION_QUADRATURE_X4;76	else77		*function = COUNTER_FUNCTION_INCREASE;78 79	return 0;80}81 82static int mchp_tc_count_function_write(struct counter_device *counter,83					struct counter_count *count,84					enum counter_function function)85{86	struct mchp_tc_data *const priv = counter_priv(counter);87	u32 bmr, cmr;88 89	regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);90	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);91 92	/* Set capture mode */93	cmr &= ~ATMEL_TC_WAVE;94 95	switch (function) {96	case COUNTER_FUNCTION_INCREASE:97		priv->qdec_mode = 0;98		/* Set highest rate based on whether soc has gclk or not */99		bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);100		if (!priv->tc_cfg->has_gclk)101			cmr |= ATMEL_TC_TIMER_CLOCK2;102		else103			cmr |= ATMEL_TC_TIMER_CLOCK1;104		/* Setup the period capture mode */105		cmr |=  ATMEL_TC_CMR_MASK;106		cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);107		break;108	case COUNTER_FUNCTION_QUADRATURE_X4:109		if (!priv->tc_cfg->has_qdec)110			return -EINVAL;111		/* In QDEC mode settings both channels 0 and 1 are required */112		if (priv->num_channels < 2 || priv->channel[0] != 0 ||113		    priv->channel[1] != 1) {114			pr_err("Invalid channels number or id for quadrature mode\n");115			return -EINVAL;116		}117		priv->qdec_mode = 1;118		bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;119		cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;120		break;121	default:122		/* should never reach this path */123		return -EINVAL;124	}125 126	regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);127	regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);128 129	/* Enable clock and trigger counter */130	regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),131		     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);132 133	if (priv->qdec_mode) {134		regmap_write(priv->regmap,135			     ATMEL_TC_REG(priv->channel[1], CMR), cmr);136		regmap_write(priv->regmap,137			     ATMEL_TC_REG(priv->channel[1], CCR),138			     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);139	}140 141	return 0;142}143 144static int mchp_tc_count_signal_read(struct counter_device *counter,145				     struct counter_signal *signal,146				     enum counter_signal_level *lvl)147{148	struct mchp_tc_data *const priv = counter_priv(counter);149	bool sigstatus;150	u32 sr;151 152	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);153 154	if (signal->id == 1)155		sigstatus = (sr & ATMEL_TC_MTIOB);156	else157		sigstatus = (sr & ATMEL_TC_MTIOA);158 159	*lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;160 161	return 0;162}163 164static int mchp_tc_count_action_read(struct counter_device *counter,165				     struct counter_count *count,166				     struct counter_synapse *synapse,167				     enum counter_synapse_action *action)168{169	struct mchp_tc_data *const priv = counter_priv(counter);170	u32 cmr;171 172	if (priv->qdec_mode) {173		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;174		return 0;175	}176 177	/* Only TIOA signal is evaluated in non-QDEC mode */178	if (synapse->signal->id != 0) {179		*action = COUNTER_SYNAPSE_ACTION_NONE;180		return 0;181	}182 183	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);184 185	switch (cmr & ATMEL_TC_ETRGEDG) {186	default:187		*action = COUNTER_SYNAPSE_ACTION_NONE;188		break;189	case ATMEL_TC_ETRGEDG_RISING:190		*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;191		break;192	case ATMEL_TC_ETRGEDG_FALLING:193		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;194		break;195	case ATMEL_TC_ETRGEDG_BOTH:196		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;197		break;198	}199 200	return 0;201}202 203static int mchp_tc_count_action_write(struct counter_device *counter,204				      struct counter_count *count,205				      struct counter_synapse *synapse,206				      enum counter_synapse_action action)207{208	struct mchp_tc_data *const priv = counter_priv(counter);209	u32 edge = ATMEL_TC_ETRGEDG_NONE;210 211	/* QDEC mode is rising edge only; only TIOA handled in non-QDEC mode */212	if (priv->qdec_mode || synapse->signal->id != 0)213		return -EINVAL;214 215	switch (action) {216	case COUNTER_SYNAPSE_ACTION_NONE:217		edge = ATMEL_TC_ETRGEDG_NONE;218		break;219	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:220		edge = ATMEL_TC_ETRGEDG_RISING;221		break;222	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:223		edge = ATMEL_TC_ETRGEDG_FALLING;224		break;225	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:226		edge = ATMEL_TC_ETRGEDG_BOTH;227		break;228	default:229		/* should never reach this path */230		return -EINVAL;231	}232 233	return regmap_write_bits(priv->regmap,234				ATMEL_TC_REG(priv->channel[0], CMR),235				ATMEL_TC_ETRGEDG, edge);236}237 238static int mchp_tc_count_read(struct counter_device *counter,239			      struct counter_count *count, u64 *val)240{241	struct mchp_tc_data *const priv = counter_priv(counter);242	u32 cnt;243 244	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);245	*val = cnt;246 247	return 0;248}249 250static struct counter_count mchp_tc_counts[] = {251	{252		.id = 0,253		.name = "Timer Counter",254		.functions_list = mchp_tc_count_functions,255		.num_functions = ARRAY_SIZE(mchp_tc_count_functions),256		.synapses = mchp_tc_count_synapses,257		.num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),258	},259};260 261static const struct counter_ops mchp_tc_ops = {262	.signal_read    = mchp_tc_count_signal_read,263	.count_read     = mchp_tc_count_read,264	.function_read  = mchp_tc_count_function_read,265	.function_write = mchp_tc_count_function_write,266	.action_read    = mchp_tc_count_action_read,267	.action_write   = mchp_tc_count_action_write268};269 270static const struct atmel_tcb_config tcb_rm9200_config = {271		.counter_width = 16,272};273 274static const struct atmel_tcb_config tcb_sam9x5_config = {275		.counter_width = 32,276};277 278static const struct atmel_tcb_config tcb_sama5d2_config = {279		.counter_width = 32,280		.has_gclk = true,281		.has_qdec = true,282};283 284static const struct atmel_tcb_config tcb_sama5d3_config = {285		.counter_width = 32,286		.has_qdec = true,287};288 289static const struct of_device_id atmel_tc_of_match[] = {290	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },291	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },292	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },293	{ .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },294	{ /* sentinel */ }295};296 297static void mchp_tc_clk_remove(void *ptr)298{299	clk_disable_unprepare((struct clk *)ptr);300}301 302static int mchp_tc_probe(struct platform_device *pdev)303{304	struct device_node *np = pdev->dev.of_node;305	const struct atmel_tcb_config *tcb_config;306	const struct of_device_id *match;307	struct counter_device *counter;308	struct mchp_tc_data *priv;309	char clk_name[7];310	struct regmap *regmap;311	struct clk *clk[3];312	int channel;313	int ret, i;314 315	counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));316	if (!counter)317		return -ENOMEM;318	priv = counter_priv(counter);319 320	match = of_match_node(atmel_tc_of_match, np->parent);321	tcb_config = match->data;322	if (!tcb_config) {323		dev_err(&pdev->dev, "No matching parent node found\n");324		return -ENODEV;325	}326 327	regmap = syscon_node_to_regmap(np->parent);328	if (IS_ERR(regmap))329		return PTR_ERR(regmap);330 331	/* max. channels number is 2 when in QDEC mode */332	priv->num_channels = of_property_count_u32_elems(np, "reg");333	if (priv->num_channels < 0) {334		dev_err(&pdev->dev, "Invalid or missing channel\n");335		return -EINVAL;336	}337 338	/* Register channels and initialize clocks */339	for (i = 0; i < priv->num_channels; i++) {340		ret = of_property_read_u32_index(np, "reg", i, &channel);341		if (ret < 0 || channel > 2)342			return -ENODEV;343 344		priv->channel[i] = channel;345 346		snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);347 348		clk[i] = of_clk_get_by_name(np->parent, clk_name);349		if (IS_ERR(clk[i])) {350			/* Fallback to t0_clk */351			clk[i] = of_clk_get_by_name(np->parent, "t0_clk");352			if (IS_ERR(clk[i]))353				return PTR_ERR(clk[i]);354		}355 356		ret = clk_prepare_enable(clk[i]);357		if (ret)358			return ret;359 360		ret = devm_add_action_or_reset(&pdev->dev,361					       mchp_tc_clk_remove,362					       clk[i]);363		if (ret)364			return ret;365 366		dev_dbg(&pdev->dev,367			"Initialized capture mode on channel %d\n",368			channel);369	}370 371	priv->tc_cfg = tcb_config;372	priv->regmap = regmap;373	counter->name = dev_name(&pdev->dev);374	counter->parent = &pdev->dev;375	counter->ops = &mchp_tc_ops;376	counter->num_counts = ARRAY_SIZE(mchp_tc_counts);377	counter->counts = mchp_tc_counts;378	counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);379	counter->signals = mchp_tc_count_signals;380 381	ret = devm_counter_add(&pdev->dev, counter);382	if (ret < 0)383		return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");384 385	return 0;386}387 388static const struct of_device_id mchp_tc_dt_ids[] = {389	{ .compatible = "microchip,tcb-capture", },390	{ /* sentinel */ },391};392MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);393 394static struct platform_driver mchp_tc_driver = {395	.probe = mchp_tc_probe,396	.driver = {397		.name = "microchip-tcb-capture",398		.of_match_table = mchp_tc_dt_ids,399	},400};401module_platform_driver(mchp_tc_driver);402 403MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");404MODULE_DESCRIPTION("Microchip TCB Capture driver");405MODULE_LICENSE("GPL v2");406MODULE_IMPORT_NS(COUNTER);407