From patchwork Tue Aug 29 13:40:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabrice Gasnier X-Patchwork-Id: 13368989 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 709D3C6FA8F for ; Tue, 29 Aug 2023 13:42:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ih/yql2RV8NKy7riI/sjy+dZNEiHubPhba9QC12g/X0=; b=vUTYsDRVBa+4M/ iFdSXgbxiT8N8mWui2SsckXbNX2CSjCWzGvyVR6aZP2HXIzqkTxiyGEA4b3PMJMBj9xD5twyEswls 0m76n2xnFeYcX1o2apK7cYLtgKMDpeJMnrvcEoXTWO+3Kzb8Grl2by/9sJvt+HO3N9L9Poup7aCzn iM3e9HAYFP+2NeTOozXtFvPAyX7fpDFgbN8WlBtudlRxxvMu8YgE7DBOghEl5NBY5qiR2x/CmzELx xDoy2NgIPQPF8UOADDtDEXFTBGVAlMZ9NcLA7F6DK7HiaoadQc4cfzdB6DGlP6/Gck76lnMjY8FoQ mYFfnCKi2Z6b7r+ds3aA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qayzD-00BdOL-3C; Tue, 29 Aug 2023 13:42:27 +0000 Received: from mx07-00178001.pphosted.com ([185.132.182.106]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1qayz4-00BdKE-1s for linux-arm-kernel@lists.infradead.org; Tue, 29 Aug 2023 13:42:22 +0000 Received: from pps.filterd (m0288072.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.17.1.22/8.17.1.22) with ESMTP id 37TC171o019260; Tue, 29 Aug 2023 15:42:13 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= selector1; bh=1/YJjDc86AStGiGgFDEynLvW3Our/eJ9zwFxAMjl428=; b=oV HWHaXee56Wa7RkhvYGclauCFFT5kagkZOKVAY18knexiWMPJ8JDUA0xM8BwJReS8 8JhwdhSZuYk2nEQb5RUSyDNfmk4bqGXwDgHH+L54P8KrXFa0gNp2jQUulaB69xkF Q+Gtxd7MRSX2toPupo0QCcuokHcdm+J0iHBofp27Hlz3L+dWknPxSSMS4gfOb8n2 KBTPBrMc2k8aQYK6PzFKV2E5dL52qwRCNPR6/+0E3+d8Gn5HGyY6ICVHXaElXPst jf9kiee2T93pTycOkkxGvCc30Z319sexCeZqc2yzCuOGT3A6LjVU99qXKwxp5op6 LfJHCA+2hAmZrrQfXxcQ== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com (PPS) with ESMTPS id 3sq6tfmcp7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 29 Aug 2023 15:42:13 +0200 (MEST) Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 9A306100057; Tue, 29 Aug 2023 15:42:12 +0200 (CEST) Received: from Webmail-eu.st.com (shfdag1node2.st.com [10.75.129.70]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 9146022AFFD; Tue, 29 Aug 2023 15:42:12 +0200 (CEST) Received: from localhost (10.201.22.39) by SHFDAG1NODE2.st.com (10.75.129.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Tue, 29 Aug 2023 15:42:09 +0200 From: Fabrice Gasnier To: , CC: , , , , , Subject: [PATCH 7/8] counter: stm32-timer-cnt: populate capture channels and check encoder Date: Tue, 29 Aug 2023 15:40:28 +0200 Message-ID: <20230829134029.2402868-8-fabrice.gasnier@foss.st.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230829134029.2402868-1-fabrice.gasnier@foss.st.com> References: <20230829134029.2402868-1-fabrice.gasnier@foss.st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.22.39] X-ClientProxiedBy: EQNCAS1NODE3.st.com (10.75.129.80) To SHFDAG1NODE2.st.com (10.75.129.70) X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.267,Aquarius:18.0.957,Hydra:6.0.601,FMLib:17.11.176.26 definitions=2023-08-29_10,2023-08-29_01,2023-05-22_02 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230829_064218_967965_F0294C61 X-CRM114-Status: GOOD ( 26.98 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org This is a precursor patch to support capture channels on all possible channels and stm32 timer types. Original driver was intended to be used only as quadrature encoder and simple counter on internal clock. So, add ch3 and ch4 definition. Also add a check on encoder capability, so the driver may be probed for timer instances without encoder feature. This way, all timers may be used as simple counter on internal clock, starting from here. Encoder capability is retrieved by using the timer index (originally in stm32-timer-trigger driver and dt-bindings). The need to keep backward compatibility with existing device tree lead to parse aside trigger node. Add diversity as STM32 timers with capture feature may have either 4, 2, 1 or no cc (capture/compare) channels. Signed-off-by: Fabrice Gasnier --- drivers/counter/stm32-timer-cnt.c | 242 ++++++++++++++++++++++++++++-- 1 file changed, 231 insertions(+), 11 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index b8d201383a64..e39b3964bc9d 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,8 @@ #define STM32_CLOCK_SIG 0 #define STM32_CH1_SIG 1 #define STM32_CH2_SIG 2 +#define STM32_CH3_SIG 3 +#define STM32_CH4_SIG 4 struct stm32_timer_regs { u32 cr1; @@ -38,6 +41,9 @@ struct stm32_timer_cnt { u32 max_arr; bool enabled; struct stm32_timer_regs bak; + bool has_encoder; + u32 idx; + unsigned int nchannels; }; static const enum counter_function stm32_count_functions[] = { @@ -265,6 +271,10 @@ static const enum counter_synapse_action stm32_synapse_actions[] = { COUNTER_SYNAPSE_ACTION_BOTH_EDGES }; +static const enum counter_synapse_action stm32_synapse_ch_actions[] = { + COUNTER_SYNAPSE_ACTION_NONE, +}; + static int stm32_action_read(struct counter_device *counter, struct counter_count *count, struct counter_synapse *synapse, @@ -333,7 +343,8 @@ static struct counter_comp stm32_count_clock_ext[] = { COUNTER_COMP_SIGNAL_U64("frequency", stm32_count_clk_get_freq, NULL), }; -static struct counter_signal stm32_signals[] = { +/* STM32 Timer with quadrature encoder and 4 capture channels */ +static struct counter_signal stm32_signals_enc_4ch[] = { { .id = STM32_CLOCK_SIG, .name = "Clock Signal", @@ -347,38 +358,210 @@ static struct counter_signal stm32_signals[] = { { .id = STM32_CH2_SIG, .name = "Channel 2 Quadrature B" + }, + { + .id = STM32_CH3_SIG, + .name = "Channel 3" + }, + { + .id = STM32_CH4_SIG, + .name = "Channel 4" } }; -static struct counter_synapse stm32_count_synapses[] = { +static struct counter_synapse stm32_count_synapses_enc_4ch[] = { { .actions_list = stm32_clock_synapse_actions, .num_actions = ARRAY_SIZE(stm32_clock_synapse_actions), - .signal = &stm32_signals[STM32_CLOCK_SIG] + .signal = &stm32_signals_enc_4ch[STM32_CLOCK_SIG] }, { .actions_list = stm32_synapse_actions, .num_actions = ARRAY_SIZE(stm32_synapse_actions), - .signal = &stm32_signals[STM32_CH1_SIG] + .signal = &stm32_signals_enc_4ch[STM32_CH1_SIG] }, { .actions_list = stm32_synapse_actions, .num_actions = ARRAY_SIZE(stm32_synapse_actions), - .signal = &stm32_signals[STM32_CH2_SIG] + .signal = &stm32_signals_enc_4ch[STM32_CH2_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH3_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH4_SIG] + }, +}; + +static struct counter_count stm32_counts_enc_4ch = { + .id = 0, + .name = "Channel 1 Count", + .functions_list = stm32_count_functions, + .num_functions = ARRAY_SIZE(stm32_count_functions), + .synapses = stm32_count_synapses_enc_4ch, + .num_synapses = ARRAY_SIZE(stm32_count_synapses_enc_4ch), + .ext = stm32_count_ext, + .num_ext = ARRAY_SIZE(stm32_count_ext) +}; + +/* STM32 Timer with up to 4 capture channels (without encoder) */ +static struct counter_signal stm32_signals_4ch[] = { + { + .id = STM32_CLOCK_SIG, + .name = "Clock Signal", + .ext = stm32_count_clock_ext, + .num_ext = ARRAY_SIZE(stm32_count_clock_ext), + }, + { + .id = STM32_CH1_SIG, + .name = "Channel 1" + }, + { + .id = STM32_CH2_SIG, + .name = "Channel 2" + }, + { + .id = STM32_CH3_SIG, + .name = "Channel 3" + }, + { + .id = STM32_CH4_SIG, + .name = "Channel 4" } }; +static struct counter_synapse stm32_count_synapses_no_enc[] = { + { + .actions_list = stm32_clock_synapse_actions, + .num_actions = ARRAY_SIZE(stm32_clock_synapse_actions), + .signal = &stm32_signals_enc_4ch[STM32_CLOCK_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH1_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH2_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH3_SIG] + }, + { + .actions_list = stm32_synapse_ch_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_ch_actions), + .signal = &stm32_signals_enc_4ch[STM32_CH4_SIG] + }, +}; + +static struct counter_count stm32_counts_4ch = { + .id = 0, + .name = "Channel 1 Count", + .functions_list = stm32_count_functions, + .num_functions = 1, /* increase */ + .synapses = stm32_count_synapses_no_enc, + .num_synapses = ARRAY_SIZE(stm32_count_synapses_no_enc), + .ext = stm32_count_ext, + .num_ext = ARRAY_SIZE(stm32_count_ext) +}; + +static struct counter_count stm32_counts_2ch = { + .id = 0, + .name = "Channel 1 Count", + .functions_list = stm32_count_functions, + .num_functions = 1, /* increase */ + .synapses = stm32_count_synapses_no_enc, + .num_synapses = 3, /* clock, ch1 and ch2 */ + .ext = stm32_count_ext, + .num_ext = ARRAY_SIZE(stm32_count_ext) +}; + +static struct counter_count stm32_counts_1ch = { + .id = 0, + .name = "Channel 1 Count", + .functions_list = stm32_count_functions, + .num_functions = 1, /* increase */ + .synapses = stm32_count_synapses_no_enc, + .num_synapses = 2, /* clock, ch1 */ + .ext = stm32_count_ext, + .num_ext = ARRAY_SIZE(stm32_count_ext) +}; + static struct counter_count stm32_counts = { .id = 0, .name = "Channel 1 Count", .functions_list = stm32_count_functions, - .num_functions = ARRAY_SIZE(stm32_count_functions), - .synapses = stm32_count_synapses, - .num_synapses = ARRAY_SIZE(stm32_count_synapses), + .num_functions = 1, /* increase */ + .synapses = stm32_count_synapses_no_enc, + .num_synapses = 1, /* clock only */ .ext = stm32_count_ext, .num_ext = ARRAY_SIZE(stm32_count_ext) }; +static void stm32_timer_cnt_detect_channels(struct platform_device *pdev, + struct stm32_timer_cnt *priv) +{ + u32 ccer, ccer_backup; + + regmap_read(priv->regmap, TIM_CCER, &ccer_backup); + regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE); + regmap_read(priv->regmap, TIM_CCER, &ccer); + regmap_write(priv->regmap, TIM_CCER, ccer_backup); + priv->nchannels = hweight32(ccer & TIM_CCER_CCXE); + + dev_dbg(&pdev->dev, "has %d cc channels\n", priv->nchannels); +} + +/* encoder supported on TIM1 TIM2 TIM3 TIM4 TIM5 TIM8 */ +#define STM32_TIM_ENCODER_SUPPORTED (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(7)) + +static const char * const stm32_timer_trigger_compat[] = { + "st,stm32-timer-trigger", + "st,stm32h7-timer-trigger", +}; + +static int stm32_timer_cnt_probe_encoder(struct platform_device *pdev, + struct stm32_timer_cnt *priv) +{ + struct device *parent = pdev->dev.parent; + struct device_node *tnode = NULL, *pnode = parent->of_node; + int i, ret; + + /* + * Need to retrieve the trigger node index from DT, to be able + * to determine if the counter supports encoder mode. It also + * enforce backward compatibility, and allow to support other + * counter modes in this driver (when the timer doesn't support + * encoder). + */ + for (i = 0; i < ARRAY_SIZE(stm32_timer_trigger_compat) && !tnode; i++) + tnode = of_get_compatible_child(pnode, stm32_timer_trigger_compat[i]); + if (!tnode) { + dev_err(&pdev->dev, "Can't find trigger node\n"); + return -ENODATA; + } + + ret = of_property_read_u32(tnode, "reg", &priv->idx); + if (ret) { + dev_err(&pdev->dev, "Can't get index (%d)\n", ret); + return ret; + } + + priv->has_encoder = !!(STM32_TIM_ENCODER_SUPPORTED & BIT(priv->idx)); + + dev_dbg(&pdev->dev, "encoder support: %s\n", priv->has_encoder ? "yes" : "no"); + + return 0; +} + static int stm32_timer_cnt_probe(struct platform_device *pdev) { struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); @@ -400,13 +583,50 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; + ret = stm32_timer_cnt_probe_encoder(pdev, priv); + if (ret) + return ret; + + stm32_timer_cnt_detect_channels(pdev, priv); + counter->name = dev_name(dev); counter->parent = dev; counter->ops = &stm32_timer_cnt_ops; - counter->counts = &stm32_counts; counter->num_counts = 1; - counter->signals = stm32_signals; - counter->num_signals = ARRAY_SIZE(stm32_signals); + + /* + * Handle diversity for stm32 timers features. For now encoder is found with + * advanced timers or gp timers with 4 channels. Timers with less channels + * doesn't support encoder. + */ + switch (priv->nchannels) { + case 4: + if (priv->has_encoder) { + counter->counts = &stm32_counts_enc_4ch; + counter->signals = stm32_signals_enc_4ch; + counter->num_signals = ARRAY_SIZE(stm32_signals_enc_4ch); + } else { + counter->counts = &stm32_counts_4ch; + counter->signals = stm32_signals_4ch; + counter->num_signals = ARRAY_SIZE(stm32_signals_4ch); + } + break; + case 2: + counter->counts = &stm32_counts_2ch; + counter->signals = stm32_signals_4ch; + counter->num_signals = 3; /* clock, ch1 and ch2 */ + break; + case 1: + counter->counts = &stm32_counts_1ch; + counter->signals = stm32_signals_4ch; + counter->num_signals = 2; /* clock, ch1 */ + break; + default: + counter->counts = &stm32_counts; + counter->signals = stm32_signals_4ch; + counter->num_signals = 1; /* clock */ + break; + } platform_set_drvdata(pdev, priv);