Path: blob/master/drivers/counter/microchip-tcb-capture.c
26278 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2020 Microchip3*4* Author: Kamel Bouhara <[email protected]>5*/6#include <linux/clk.h>7#include <linux/counter.h>8#include <linux/interrupt.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_irq.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <uapi/linux/counter/microchip-tcb-capture.h>17#include <soc/at91/atmel_tcb.h>1819#define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \20ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \21ATMEL_TC_LDBSTOP)2223#define ATMEL_TC_DEF_IRQS (ATMEL_TC_ETRGS | ATMEL_TC_COVFS | \24ATMEL_TC_LDRAS | ATMEL_TC_LDRBS | ATMEL_TC_CPCS)2526#define ATMEL_TC_QDEN BIT(8)27#define ATMEL_TC_POSEN BIT(9)2829struct mchp_tc_data {30const struct atmel_tcb_config *tc_cfg;31struct regmap *regmap;32int qdec_mode;33int num_channels;34int channel[2];35};3637static const enum counter_function mchp_tc_count_functions[] = {38COUNTER_FUNCTION_INCREASE,39COUNTER_FUNCTION_QUADRATURE_X4,40};4142static const enum counter_synapse_action mchp_tc_synapse_actions[] = {43COUNTER_SYNAPSE_ACTION_NONE,44COUNTER_SYNAPSE_ACTION_RISING_EDGE,45COUNTER_SYNAPSE_ACTION_FALLING_EDGE,46COUNTER_SYNAPSE_ACTION_BOTH_EDGES,47};4849static struct counter_signal mchp_tc_count_signals[] = {50{51.id = 0,52.name = "Channel A",53},54{55.id = 1,56.name = "Channel B",57}58};5960static struct counter_synapse mchp_tc_count_synapses[] = {61{62.actions_list = mchp_tc_synapse_actions,63.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),64.signal = &mchp_tc_count_signals[0]65},66{67.actions_list = mchp_tc_synapse_actions,68.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),69.signal = &mchp_tc_count_signals[1]70}71};7273static int mchp_tc_count_function_read(struct counter_device *counter,74struct counter_count *count,75enum counter_function *function)76{77struct mchp_tc_data *const priv = counter_priv(counter);7879if (priv->qdec_mode)80*function = COUNTER_FUNCTION_QUADRATURE_X4;81else82*function = COUNTER_FUNCTION_INCREASE;8384return 0;85}8687static int mchp_tc_count_function_write(struct counter_device *counter,88struct counter_count *count,89enum counter_function function)90{91struct mchp_tc_data *const priv = counter_priv(counter);92u32 bmr, cmr;9394regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);95regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);9697/* Set capture mode */98cmr &= ~ATMEL_TC_WAVE;99100switch (function) {101case COUNTER_FUNCTION_INCREASE:102priv->qdec_mode = 0;103/* Set highest rate based on whether soc has gclk or not */104bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);105if (!priv->tc_cfg->has_gclk)106cmr |= ATMEL_TC_TIMER_CLOCK2;107else108cmr |= ATMEL_TC_TIMER_CLOCK1;109/* Setup the period capture mode */110cmr |= ATMEL_TC_CMR_MASK;111cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);112break;113case COUNTER_FUNCTION_QUADRATURE_X4:114if (!priv->tc_cfg->has_qdec)115return -EINVAL;116/* In QDEC mode settings both channels 0 and 1 are required */117if (priv->num_channels < 2 || priv->channel[0] != 0 ||118priv->channel[1] != 1) {119pr_err("Invalid channels number or id for quadrature mode\n");120return -EINVAL;121}122priv->qdec_mode = 1;123bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;124cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;125break;126default:127/* should never reach this path */128return -EINVAL;129}130131regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);132regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);133134/* Enable clock and trigger counter */135regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),136ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);137138if (priv->qdec_mode) {139regmap_write(priv->regmap,140ATMEL_TC_REG(priv->channel[1], CMR), cmr);141regmap_write(priv->regmap,142ATMEL_TC_REG(priv->channel[1], CCR),143ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);144}145146return 0;147}148149static int mchp_tc_count_signal_read(struct counter_device *counter,150struct counter_signal *signal,151enum counter_signal_level *lvl)152{153struct mchp_tc_data *const priv = counter_priv(counter);154bool sigstatus;155u32 sr;156157regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);158159if (signal->id == 1)160sigstatus = (sr & ATMEL_TC_MTIOB);161else162sigstatus = (sr & ATMEL_TC_MTIOA);163164*lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;165166return 0;167}168169static int mchp_tc_count_action_read(struct counter_device *counter,170struct counter_count *count,171struct counter_synapse *synapse,172enum counter_synapse_action *action)173{174struct mchp_tc_data *const priv = counter_priv(counter);175u32 cmr;176177if (priv->qdec_mode) {178*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;179return 0;180}181182/* Only TIOA signal is evaluated in non-QDEC mode */183if (synapse->signal->id != 0) {184*action = COUNTER_SYNAPSE_ACTION_NONE;185return 0;186}187188regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);189190switch (cmr & ATMEL_TC_ETRGEDG) {191default:192*action = COUNTER_SYNAPSE_ACTION_NONE;193break;194case ATMEL_TC_ETRGEDG_RISING:195*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;196break;197case ATMEL_TC_ETRGEDG_FALLING:198*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;199break;200case ATMEL_TC_ETRGEDG_BOTH:201*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;202break;203}204205return 0;206}207208static int mchp_tc_count_action_write(struct counter_device *counter,209struct counter_count *count,210struct counter_synapse *synapse,211enum counter_synapse_action action)212{213struct mchp_tc_data *const priv = counter_priv(counter);214u32 edge = ATMEL_TC_ETRGEDG_NONE;215216/* QDEC mode is rising edge only; only TIOA handled in non-QDEC mode */217if (priv->qdec_mode || synapse->signal->id != 0)218return -EINVAL;219220switch (action) {221case COUNTER_SYNAPSE_ACTION_NONE:222edge = ATMEL_TC_ETRGEDG_NONE;223break;224case COUNTER_SYNAPSE_ACTION_RISING_EDGE:225edge = ATMEL_TC_ETRGEDG_RISING;226break;227case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:228edge = ATMEL_TC_ETRGEDG_FALLING;229break;230case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:231edge = ATMEL_TC_ETRGEDG_BOTH;232break;233default:234/* should never reach this path */235return -EINVAL;236}237238return regmap_write_bits(priv->regmap,239ATMEL_TC_REG(priv->channel[0], CMR),240ATMEL_TC_ETRGEDG, edge);241}242243static int mchp_tc_count_read(struct counter_device *counter,244struct counter_count *count, u64 *val)245{246struct mchp_tc_data *const priv = counter_priv(counter);247u32 cnt;248249regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);250*val = cnt;251252return 0;253}254255static int mchp_tc_count_cap_read(struct counter_device *counter,256struct counter_count *count, size_t idx, u64 *val)257{258struct mchp_tc_data *const priv = counter_priv(counter);259u32 cnt;260int ret;261262switch (idx) {263case COUNTER_MCHP_EXCAP_RA:264ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), &cnt);265break;266case COUNTER_MCHP_EXCAP_RB:267ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), &cnt);268break;269default:270return -EINVAL;271}272273if (ret < 0)274return ret;275276*val = cnt;277278return 0;279}280281static int mchp_tc_count_cap_write(struct counter_device *counter,282struct counter_count *count, size_t idx, u64 val)283{284struct mchp_tc_data *const priv = counter_priv(counter);285int ret;286287if (val > U32_MAX)288return -ERANGE;289290switch (idx) {291case COUNTER_MCHP_EXCAP_RA:292ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), val);293break;294case COUNTER_MCHP_EXCAP_RB:295ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), val);296break;297default:298return -EINVAL;299}300301return ret;302}303304static int mchp_tc_count_compare_read(struct counter_device *counter, struct counter_count *count,305u64 *val)306{307struct mchp_tc_data *const priv = counter_priv(counter);308u32 cnt;309int ret;310311ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), &cnt);312if (ret < 0)313return ret;314315*val = cnt;316317return 0;318}319320static int mchp_tc_count_compare_write(struct counter_device *counter, struct counter_count *count,321u64 val)322{323struct mchp_tc_data *const priv = counter_priv(counter);324325if (val > U32_MAX)326return -ERANGE;327328return regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), val);329}330331static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2);332333static struct counter_comp mchp_tc_count_ext[] = {334COUNTER_COMP_ARRAY_CAPTURE(mchp_tc_count_cap_read, mchp_tc_count_cap_write,335mchp_tc_cnt_cap_array),336COUNTER_COMP_COMPARE(mchp_tc_count_compare_read, mchp_tc_count_compare_write),337};338339static int mchp_tc_watch_validate(struct counter_device *counter,340const struct counter_watch *watch)341{342if (watch->channel == COUNTER_MCHP_EVCHN_CV || watch->channel == COUNTER_MCHP_EVCHN_RA)343switch (watch->event) {344case COUNTER_EVENT_CHANGE_OF_STATE:345case COUNTER_EVENT_OVERFLOW:346case COUNTER_EVENT_CAPTURE:347return 0;348default:349return -EINVAL;350}351352if (watch->channel == COUNTER_MCHP_EVCHN_RB && watch->event == COUNTER_EVENT_CAPTURE)353return 0;354355if (watch->channel == COUNTER_MCHP_EVCHN_RC && watch->event == COUNTER_EVENT_THRESHOLD)356return 0;357358return -EINVAL;359}360361static struct counter_count mchp_tc_counts[] = {362{363.id = 0,364.name = "Timer Counter",365.functions_list = mchp_tc_count_functions,366.num_functions = ARRAY_SIZE(mchp_tc_count_functions),367.synapses = mchp_tc_count_synapses,368.num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),369.ext = mchp_tc_count_ext,370.num_ext = ARRAY_SIZE(mchp_tc_count_ext),371},372};373374static const struct counter_ops mchp_tc_ops = {375.signal_read = mchp_tc_count_signal_read,376.count_read = mchp_tc_count_read,377.function_read = mchp_tc_count_function_read,378.function_write = mchp_tc_count_function_write,379.action_read = mchp_tc_count_action_read,380.action_write = mchp_tc_count_action_write,381.watch_validate = mchp_tc_watch_validate,382};383384static const struct atmel_tcb_config tcb_rm9200_config = {385.counter_width = 16,386};387388static const struct atmel_tcb_config tcb_sam9x5_config = {389.counter_width = 32,390};391392static const struct atmel_tcb_config tcb_sama5d2_config = {393.counter_width = 32,394.has_gclk = true,395.has_qdec = true,396};397398static const struct atmel_tcb_config tcb_sama5d3_config = {399.counter_width = 32,400.has_qdec = true,401};402403static const struct of_device_id atmel_tc_of_match[] = {404{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },405{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },406{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },407{ .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },408{ /* sentinel */ }409};410411static irqreturn_t mchp_tc_isr(int irq, void *dev_id)412{413struct counter_device *const counter = dev_id;414struct mchp_tc_data *const priv = counter_priv(counter);415u32 sr, mask;416417regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);418regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], IMR), &mask);419420sr &= mask;421if (!(sr & ATMEL_TC_ALL_IRQ))422return IRQ_NONE;423424if (sr & ATMEL_TC_ETRGS)425counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE,426COUNTER_MCHP_EVCHN_CV);427if (sr & ATMEL_TC_LDRAS)428counter_push_event(counter, COUNTER_EVENT_CAPTURE,429COUNTER_MCHP_EVCHN_RA);430if (sr & ATMEL_TC_LDRBS)431counter_push_event(counter, COUNTER_EVENT_CAPTURE,432COUNTER_MCHP_EVCHN_RB);433if (sr & ATMEL_TC_CPCS)434counter_push_event(counter, COUNTER_EVENT_THRESHOLD,435COUNTER_MCHP_EVCHN_RC);436if (sr & ATMEL_TC_COVFS)437counter_push_event(counter, COUNTER_EVENT_OVERFLOW,438COUNTER_MCHP_EVCHN_CV);439440return IRQ_HANDLED;441}442443static void mchp_tc_irq_remove(void *ptr)444{445struct mchp_tc_data *priv = ptr;446447regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IDR), ATMEL_TC_DEF_IRQS);448}449450static int mchp_tc_irq_enable(struct counter_device *const counter, int irq)451{452struct mchp_tc_data *const priv = counter_priv(counter);453int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0,454dev_name(counter->parent), counter);455456if (ret < 0)457return ret;458459ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IER), ATMEL_TC_DEF_IRQS);460if (ret < 0)461return ret;462463ret = devm_add_action_or_reset(counter->parent, mchp_tc_irq_remove, priv);464if (ret < 0)465return ret;466467return 0;468}469470static void mchp_tc_clk_remove(void *ptr)471{472clk_disable_unprepare((struct clk *)ptr);473}474475static int mchp_tc_probe(struct platform_device *pdev)476{477struct device_node *np = pdev->dev.of_node;478const struct atmel_tcb_config *tcb_config;479const struct of_device_id *match;480struct counter_device *counter;481struct mchp_tc_data *priv;482char clk_name[7];483struct regmap *regmap;484struct clk *clk[3];485int channel;486int ret, i;487488counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));489if (!counter)490return -ENOMEM;491priv = counter_priv(counter);492493match = of_match_node(atmel_tc_of_match, np->parent);494tcb_config = match->data;495if (!tcb_config) {496dev_err(&pdev->dev, "No matching parent node found\n");497return -ENODEV;498}499500regmap = syscon_node_to_regmap(np->parent);501if (IS_ERR(regmap))502return PTR_ERR(regmap);503504/* max. channels number is 2 when in QDEC mode */505priv->num_channels = of_property_count_u32_elems(np, "reg");506if (priv->num_channels < 0) {507dev_err(&pdev->dev, "Invalid or missing channel\n");508return -EINVAL;509}510511/* Register channels and initialize clocks */512for (i = 0; i < priv->num_channels; i++) {513ret = of_property_read_u32_index(np, "reg", i, &channel);514if (ret < 0 || channel > 2)515return -ENODEV;516517priv->channel[i] = channel;518519snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);520521clk[i] = of_clk_get_by_name(np->parent, clk_name);522if (IS_ERR(clk[i])) {523/* Fallback to t0_clk */524clk[i] = of_clk_get_by_name(np->parent, "t0_clk");525if (IS_ERR(clk[i]))526return PTR_ERR(clk[i]);527}528529ret = clk_prepare_enable(clk[i]);530if (ret)531return ret;532533ret = devm_add_action_or_reset(&pdev->dev,534mchp_tc_clk_remove,535clk[i]);536if (ret)537return ret;538539dev_dbg(&pdev->dev,540"Initialized capture mode on channel %d\n",541channel);542}543544/* Disable Quadrature Decoder and position measure */545ret = regmap_update_bits(regmap, ATMEL_TC_BMR, ATMEL_TC_QDEN | ATMEL_TC_POSEN, 0);546if (ret)547return ret;548549/* Setup the period capture mode */550ret = regmap_update_bits(regmap, ATMEL_TC_REG(priv->channel[0], CMR),551ATMEL_TC_WAVE | ATMEL_TC_ABETRG | ATMEL_TC_CMR_MASK |552ATMEL_TC_TCCLKS,553ATMEL_TC_CMR_MASK);554if (ret)555return ret;556557/* Enable clock and trigger counter */558ret = regmap_write(regmap, ATMEL_TC_REG(priv->channel[0], CCR),559ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);560if (ret)561return ret;562563priv->tc_cfg = tcb_config;564priv->regmap = regmap;565counter->name = dev_name(&pdev->dev);566counter->parent = &pdev->dev;567counter->ops = &mchp_tc_ops;568counter->num_counts = ARRAY_SIZE(mchp_tc_counts);569counter->counts = mchp_tc_counts;570counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);571counter->signals = mchp_tc_count_signals;572573i = of_irq_get(np->parent, 0);574if (i == -EPROBE_DEFER)575return -EPROBE_DEFER;576if (i > 0) {577ret = mchp_tc_irq_enable(counter, i);578if (ret < 0)579return dev_err_probe(&pdev->dev, ret, "Failed to set up IRQ");580}581582ret = devm_counter_add(&pdev->dev, counter);583if (ret < 0)584return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");585586return 0;587}588589static const struct of_device_id mchp_tc_dt_ids[] = {590{ .compatible = "microchip,tcb-capture", },591{ /* sentinel */ },592};593MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);594595static struct platform_driver mchp_tc_driver = {596.probe = mchp_tc_probe,597.driver = {598.name = "microchip-tcb-capture",599.of_match_table = mchp_tc_dt_ids,600},601};602module_platform_driver(mchp_tc_driver);603604MODULE_AUTHOR("Kamel Bouhara <[email protected]>");605MODULE_DESCRIPTION("Microchip TCB Capture driver");606MODULE_LICENSE("GPL v2");607MODULE_IMPORT_NS("COUNTER");608609610