From nobody Mon Apr 27 03:23:03 2026 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A17EC43334 for ; Fri, 17 Jun 2022 11:46:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1381875AbiFQLqg (ORCPT ); Fri, 17 Jun 2022 07:46:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42702 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235682AbiFQLqc (ORCPT ); Fri, 17 Jun 2022 07:46:32 -0400 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F263F6CF46; Fri, 17 Jun 2022 04:46:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1655466389; x=1687002389; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ks7vKp2+OkgOf8fUD0T6Pb4JtsT5Do93qY1jFbyfeVI=; b=AdmjVz9C4tsKNNfxawdtO2hg/1e42W1Uyt8mAjscRR5nSxhQjFHLwaxx sRj204wzXsmXGU9VQy77za63+ny6duN1oiF3ikE+hazbtgkl/9yuCBkIn JwNaeiIENjufSXDQei15k6Jn6iqkJhG6X97Bjs4O0u6i4UwGlrDMqXvsq W0kTevfMAU/quqgWGmbvxftD43q56J2c0S0iuliXII4KdQZGRkRWRdXoW 9m34F1GuV87BuG1+8fLsBI1bSS2EP1VLXOV70ezLOWgU5ekDB6QDx60Dg qmWajjiRBALTwEo5nk6TSQCJvFqAyRDKgJca4Byy/hsSiuxkwow4bM1F+ w==; X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="100506604" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 17 Jun 2022 04:46:28 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.87.72) by chn-vm-ex02.mchp-main.com (10.10.87.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.17; Fri, 17 Jun 2022 04:46:28 -0700 Received: from wendy.microchip.com (10.10.115.15) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server id 15.1.2375.17 via Frontend Transport; Fri, 17 Jun 2022 04:46:26 -0700 From: Conor Dooley To: Thierry Reding , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , "Lee Jones" CC: Daire McNamara , , , , Conor Dooley Subject: [PATCH v3 1/2] pwm: add microchip soft ip corePWM driver Date: Fri, 17 Jun 2022 12:44:42 +0100 Message-ID: <20220617114442.998357-2-conor.dooley@microchip.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220617114442.998357-1-conor.dooley@microchip.com> References: <20220617114442.998357-1-conor.dooley@microchip.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add a driver that supports the Microchip FPGA "soft" PWM IP core. Signed-off-by: Conor Dooley --- drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-microchip-core.c | 325 +++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 drivers/pwm/pwm-microchip-core.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 21e3b05a5153..a651848e444b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -383,6 +383,16 @@ config PWM_MEDIATEK To compile this driver as a module, choose M here: the module will be called pwm-mediatek. =20 +config PWM_MICROCHIP_CORE + tristate "Microchip corePWM PWM support" + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on HAS_IOMEM && OF + help + PWM driver for Microchip FPGA soft IP core. + + To compile this driver as a module, choose M here: the module + will be called pwm-microchip-core. + config PWM_MXS tristate "Freescale MXS PWM support" depends on ARCH_MXS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 708840b7fba8..d29754c20f91 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_LPSS_PCI) +=3D pwm-lpss-pci.o obj-$(CONFIG_PWM_LPSS_PLATFORM) +=3D pwm-lpss-platform.o obj-$(CONFIG_PWM_MESON) +=3D pwm-meson.o obj-$(CONFIG_PWM_MEDIATEK) +=3D pwm-mediatek.o +obj-$(CONFIG_PWM_MICROCHIP_CORE) +=3D pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) +=3D pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) +=3D pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) +=3D pwm-ntxec.o diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-c= ore.c new file mode 100644 index 000000000000..abbfc1cd23c4 --- /dev/null +++ b/drivers/pwm/pwm-microchip-core.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * corePWM driver for Microchip "soft" FPGA IP cores. + * + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. + * Author: Conor Dooley + * Documentation: + * https://www.microsemi.com/document-portal/doc_download/1245275-corepwm-= hb + * + * Limitations: + * - If the IP block is configured without "shadow registers", all register + * writes will take effect immediately, causing glitches on the output. + * If shadow registers *are* enabled, a write to the "SYNC_UPDATE" regis= ter + * notifies the core that it needs to update the registers defining the + * waveform from the contents of the "shadow registers". + * - The IP block has no concept of a duty cycle, only rising/falling edge= s of + * the waveform. Unfortunately, if the rising & falling edges registers = have + * the same value written to them the IP block will do whichever of a ri= sing + * or a falling edge is possible. I.E. a 50% waveform at twice the reque= sted + * period. Therefore to get a 0% waveform, the output is set the max hig= h/low + * time depending on polarity. + * - The PWM period is set for the whole IP block not per channel. The dri= ver + * will only change the period if no other PWM output is enabled. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREG_TO_VAL(PREG) ((PREG) + 1) + +#define COREPWM_PRESCALE_REG 0x00u +#define COREPWM_PERIOD_REG 0x04u +#define COREPWM_EN_LOW_REG 0x08u +#define COREPWM_EN_HIGH_REG 0x0Cu +#define COREPWM_SYNC_UPD_REG 0xE4u +#define COREPWM_POSEDGE_OFFSET 0x10u +#define COREPWM_NEGEDGE_OFFSET 0x14u +#define COREPWM_CHANNEL_OFFSET 0x08u + +struct mchp_core_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; + u32 sync_update_mask; +}; + +static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip = *chip) +{ + return container_of(chip, struct mchp_core_pwm_chip, chip); +} + +static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device = *pwm, bool enable) +{ + struct mchp_core_pwm_chip *mchp_core_pwm =3D to_mchp_core_pwm(chip); + u8 channel_enable, reg_offset, shift; + + /* + * There are two adjacent 8 bit control regs, the lower reg controls + * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg + * and if so, offset by the bus width. + */ + reg_offset =3D COREPWM_EN_LOW_REG + (pwm->hwpwm >> 3) * sizeof(u32); + shift =3D pwm->hwpwm > 7 ? pwm->hwpwm - 8 : pwm->hwpwm; + + channel_enable =3D readb_relaxed(mchp_core_pwm->base + reg_offset); + channel_enable &=3D ~(1 << shift); + channel_enable |=3D (enable << shift); + + writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset); + + /* + * Write to the sync update registers so that channels with shadow + * registers will also get their enable update. This operation is a NOP + * for channels without shadow registers. + */ + writel_relaxed(1U, mchp_core_pwm->base + COREPWM_SYNC_UPD_REG); +} + +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_dev= ice *pwm, + const struct pwm_state *state, u8 prescale, u8 period_steps) +{ + struct mchp_core_pwm_chip *mchp_core_pwm =3D to_mchp_core_pwm(chip); + void __iomem *channel_base =3D mchp_core_pwm->base + pwm->hwpwm * COREPWM= _CHANNEL_OFFSET; + u64 duty_steps, period, tmp; + u8 posedge, negedge; + u8 prescale_val =3D PREG_TO_VAL(prescale); + u8 period_steps_val =3D PREG_TO_VAL(period_steps); + + period =3D period_steps_val * prescale_val * NSEC_PER_SEC; + period =3D div64_u64(period, clk_get_rate(mchp_core_pwm->clk)); + + /* + * Calculate the duty cycle in multiples of the prescaled period: + * duty_steps =3D duty_in_ns / step_in_ns + * step_in_ns =3D (prescale * NSEC_PER_SEC) / clk_rate + * The code below is rearranged slightly to only divide once. + * + * Because the period is per channel, it is possible that the requested + * duty cycle is longer than the period, in which case cap it to the + * period. + */ + if (state->duty_cycle > period) { + duty_steps =3D period_steps; + } else { + duty_steps =3D state->duty_cycle * clk_get_rate(mchp_core_pwm->clk); + tmp =3D prescale_val * NSEC_PER_SEC; + duty_steps =3D div64_u64(duty_steps, tmp); + } + + /* + * Turn the output on unless posedge =3D=3D negedge, in which case the + * duty is intended to be 0, but limitations of the IP block don't + * allow a zero length duty cycle - so just set the max high/low time + * respectively. + */ + if (state->polarity =3D=3D PWM_POLARITY_INVERSED) { + negedge =3D !duty_steps ? period_steps : 0u; + posedge =3D duty_steps; + } else { + posedge =3D !duty_steps ? period_steps : 0u; + negedge =3D duty_steps; + } + + writel_relaxed(posedge, channel_base + COREPWM_POSEDGE_OFFSET); + writel_relaxed(negedge, channel_base + COREPWM_NEGEDGE_OFFSET); +} + +static void mchp_core_pwm_apply_period(struct pwm_chip *chip, const struct= pwm_state *state, + u8 *prescale, u8 *period_steps) +{ + struct mchp_core_pwm_chip *mchp_core_pwm =3D to_mchp_core_pwm(chip); + u64 tmp =3D state->period; + + /* + * Calculate the period cycles and prescale values. + * The registers are each 8 bits wide & multiplied to compute the period + * so the maximum period that can be generated is 0xFFFF times the period + * of the input clock. + */ + tmp *=3D clk_get_rate(mchp_core_pwm->clk); + do_div(tmp, NSEC_PER_SEC); + + if (tmp > 0xFFFFu) { + *prescale =3D 0xFFu; + *period_steps =3D 0xFFu; + } else { + *prescale =3D tmp >> 8; + do_div(tmp, PREG_TO_VAL(*prescale)); + *period_steps =3D tmp - 1; + } + + writel_relaxed(*prescale, mchp_core_pwm->base + COREPWM_PRESCALE_REG); + writel_relaxed(*period_steps, mchp_core_pwm->base + COREPWM_PERIOD_REG); +} + +static int mchp_core_pwm_apply(struct pwm_chip *chip, struct pwm_device *p= wm, + const struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm =3D to_mchp_core_pwm(chip); + struct pwm_state current_state =3D pwm->state; + bool period_locked; + u16 channel_enabled; + u8 prescale, period_steps; + + if (!state->enabled) { + mchp_core_pwm_enable(chip, pwm, false); + return 0; + } + + /* + * If the only thing that has changed is the duty cycle or the polarity, + * we can shortcut the calculations and just compute/apply the new duty + * cycle pos & neg edges + * As all the channels share the same period, do not allow it to be + * changed if any other channels are enabled. + */ + channel_enabled =3D (((u16)readb_relaxed(mchp_core_pwm->base + COREPWM_EN= _HIGH_REG) << 8) | + readb_relaxed(mchp_core_pwm->base + COREPWM_EN_LOW_REG)); + period_locked =3D channel_enabled & ~(1 << pwm->hwpwm); + + if ((!current_state.enabled || current_state.period !=3D state->period) &= & !period_locked) { + mchp_core_pwm_apply_period(chip, state, &prescale, &period_steps); + } else { + prescale =3D readb_relaxed(mchp_core_pwm->base + COREPWM_PRESCALE_REG); + period_steps =3D readb_relaxed(mchp_core_pwm->base + COREPWM_PERIOD_REG); + } + + mchp_core_pwm_apply_duty(chip, pwm, state, prescale, period_steps); + + /* + * Notify the block to update the waveform from the shadow registers. + * The updated values will not appear on the bus until they have been + * applied to the waveform at the beginning of the next period. + */ + if (mchp_core_pwm->sync_update_mask & (1 << pwm->hwpwm)) { + writel_relaxed(1U, mchp_core_pwm->base + COREPWM_SYNC_UPD_REG); + usleep_range(state->period, state->period * 2); + } + + mchp_core_pwm_enable(chip, pwm, true); + + return 0; +} + +static void mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_devi= ce *pwm, + struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm =3D to_mchp_core_pwm(chip); + void __iomem *channel_base =3D mchp_core_pwm->base + pwm->hwpwm * COREPWM= _CHANNEL_OFFSET; + u8 prescale, period_steps, duty_steps; + u8 posedge, negedge; + u16 channel_enabled; + + channel_enabled =3D (((u16)readb_relaxed(mchp_core_pwm->base + COREPWM_EN= _HIGH_REG) << 8) | + readb_relaxed(mchp_core_pwm->base + COREPWM_EN_LOW_REG)); + + if (channel_enabled & 1 << pwm->hwpwm) + state->enabled =3D true; + else + state->enabled =3D false; + + prescale =3D PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PRES= CALE_REG)); + + posedge =3D readb_relaxed(channel_base + COREPWM_POSEDGE_OFFSET); + negedge =3D readb_relaxed(channel_base + COREPWM_NEGEDGE_OFFSET); + + duty_steps =3D abs((s16)posedge - (s16)negedge); + state->duty_cycle =3D duty_steps * prescale * NSEC_PER_SEC; + do_div(state->duty_cycle, clk_get_rate(mchp_core_pwm->clk)); + + state->polarity =3D negedge < posedge ? PWM_POLARITY_INVERSED : PWM_POLAR= ITY_NORMAL; + + period_steps =3D PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_= PERIOD_REG)); + state->period =3D period_steps * prescale * NSEC_PER_SEC; + do_div(state->period, clk_get_rate(mchp_core_pwm->clk)); +} + +static const struct pwm_ops mchp_core_pwm_ops =3D { + .apply =3D mchp_core_pwm_apply, + .get_state =3D mchp_core_pwm_get_state, + .owner =3D THIS_MODULE, +}; + +static const struct of_device_id mchp_core_of_match[] =3D { + { + .compatible =3D "microchip,corepwm-rtl-v4", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_core_of_match); + +static int mchp_core_pwm_probe(struct platform_device *pdev) +{ + struct mchp_core_pwm_chip *mchp_pwm; + struct resource *regs; + int ret; + + mchp_pwm =3D devm_kzalloc(&pdev->dev, sizeof(*mchp_pwm), GFP_KERNEL); + if (!mchp_pwm) + return -ENOMEM; + + mchp_pwm->base =3D devm_platform_get_and_ioremap_resource(pdev, 0, ®s); + if (IS_ERR(mchp_pwm->base)) + return PTR_ERR(mchp_pwm->base); + + mchp_pwm->clk =3D devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mchp_pwm->clk)) + return PTR_ERR(mchp_pwm->clk); + + if (of_property_read_u32(pdev->dev.of_node, "microchip,sync-update-mask", + &mchp_pwm->sync_update_mask)) + mchp_pwm->sync_update_mask =3D 0u; + + ret =3D clk_prepare_enable(mchp_pwm->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to prepare PWM clock\n"); + + mchp_pwm->chip.dev =3D &pdev->dev; + mchp_pwm->chip.ops =3D &mchp_core_pwm_ops; + mchp_pwm->chip.of_xlate =3D of_pwm_xlate_with_flags; + mchp_pwm->chip.of_pwm_n_cells =3D 3; + mchp_pwm->chip.npwm =3D 16; + + ret =3D pwmchip_add(&mchp_pwm->chip); + if (ret < 0) { + clk_disable_unprepare(mchp_pwm->clk); + return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); + } + + platform_set_drvdata(pdev, mchp_pwm); + dev_info(&pdev->dev, "Successfully registered Microchip corePWM\n"); + + return 0; +} + +static int mchp_core_pwm_remove(struct platform_device *pdev) +{ + struct mchp_core_pwm_chip *mchp_pwm =3D platform_get_drvdata(pdev); + + pwmchip_remove(&mchp_pwm->chip); + clk_disable_unprepare(mchp_pwm->clk); + + return 0; +} + +static struct platform_driver mchp_core_pwm_driver =3D { + .driver =3D { + .name =3D "mchp-core-pwm", + .of_match_table =3D mchp_core_of_match, + }, + .probe =3D mchp_core_pwm_probe, + .remove =3D mchp_core_pwm_remove, +}; +module_platform_driver(mchp_core_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Conor Dooley "); +MODULE_DESCRIPTION("corePWM driver for Microchip FPGAs"); --=20 2.36.1 From nobody Mon Apr 27 03:23:03 2026 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DFBC6C433EF for ; Fri, 17 Jun 2022 11:46:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1381904AbiFQLqi (ORCPT ); Fri, 17 Jun 2022 07:46:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42708 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1381619AbiFQLqd (ORCPT ); Fri, 17 Jun 2022 07:46:33 -0400 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB74E6CF48; Fri, 17 Jun 2022 04:46:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1655466392; x=1687002392; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Py8zRXdzOiFvbMocJpc5Z5DwAynLOWkd9TOuzKCbQu8=; b=iiLRrKVSqLvKNeebaytrhjb1HNbSlYFgq2RZSxB2vf5aLZyHDUT1tvoJ HtPNOOHWHpl5PMmW+ZLNX3fhklItVztSrA/TrTJsCZIillqvAw2zhHcEt 2lGtf+o0otvJ1f8bs514yP15c8FBji3cGxvwRhvUHgegsSco0B1HBY6WI wDDdB6bANW4bOZe3lrUwqHEJx5/FrMLoc5+5nAePVkkrboQ3YKeIXxZQW ShKqtpoHn/JPgUtrQi5dtxNhLCVx/oi3Wu+eellkWQpge2MIBIb0gIRzK oFQUBgA/O5Q+CBgj5EIHV8oF9Ir/pzFTreAS4j+A8/uizMc4Lpb/ydfFw A==; X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="163847732" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa2.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 17 Jun 2022 04:46:31 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.87.72) by chn-vm-ex02.mchp-main.com (10.10.87.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.17; Fri, 17 Jun 2022 04:46:30 -0700 Received: from wendy.microchip.com (10.10.115.15) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server id 15.1.2375.17 via Frontend Transport; Fri, 17 Jun 2022 04:46:28 -0700 From: Conor Dooley To: Thierry Reding , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , "Lee Jones" CC: Daire McNamara , , , , Conor Dooley Subject: [PATCH v3 2/2] MAINTAINERS: add pwm to PolarFire SoC entry Date: Fri, 17 Jun 2022 12:44:43 +0100 Message-ID: <20220617114442.998357-3-conor.dooley@microchip.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220617114442.998357-1-conor.dooley@microchip.com> References: <20220617114442.998357-1-conor.dooley@microchip.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add the newly introduced pwm driver to the existing PolarFire SoC entry. Signed-off-by: Conor Dooley --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index f1b4b77daa5f..d0b39fa4f309 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17091,6 +17091,7 @@ L: linux-riscv@lists.infradead.org S: Supported F: arch/riscv/boot/dts/microchip/ F: drivers/mailbox/mailbox-mpfs.c +F: drivers/pwm/pwm-microchip-core.c F: drivers/soc/microchip/ F: include/soc/microchip/mpfs.h =20 --=20 2.36.1