cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

microchip-tcb-capture.c (10413B)


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