StarFive's Pulse Width Modulation driver support
Subsystems (4)
v8 cover StarFive's Pulse Width Modulation driver support
Hi,
This patchset adds initial rudimentary support for the StarFive Pulse Width Modulation controller driver. And this driver will be used in StarFive's VisionFive 2 board.The first patch add Documentations for the device and Patch 2 adds device probe for the module.
Changes v7->v8:
- Rebased to v6.7rc3.
- Changed compatible to "opencores,pwm-v1"
- Adjusted the clock unprepare order.
- Followed dt-bindings Coding style.
Changes v6->v7:
- Rebased to v6.6.
- Added dependency architecture.
- Adopted new rounding algorithm.
- Added limitation descripton.
- Used function interfaces instead of macro definitions.
- Followed the linux coding style.
Changes v5->v6:
- Rebased to v6.6rc5.
- Changed driver into a generic OpenCores driver.
- Modified dt-bindings description into OpenCores.
- Uesd the StarFive compatible string to parameterize.
Changes v4->v5:
- Rebased to v6.6rc2.
- Updated macro definition indent.
- Replaced the clock initializes the interface.
- Fixed patch description.
Changes v3->v4:
- Rebased to v6.5rc7.
- Sorted the header files in alphabetic order.
- Changed iowrite32() to writel().
- Added a way to turn off.
- Modified polarity inversion implementation.
- Added 7100 support.
- Added dts patches.
- Used the various helpers in linux/math.h.
- Corrected formatting problems.
- Renamed dtbinding to 'starfive,jh7100-pwm.yaml'.
- Dropped the redundant code.
Changes v2->v3:
- Fixed some formatting issues.
Changes v1->v2:
- Renamed the dt-binding 'pwm-starfive.yaml' to 'starfive,jh7110-pwm.yaml'.
- Dropped the compatible's Items.
- Dropped the unuse defines.
- Modified the code to follow the Linux coding style.
- Changed return value to dev_err_probe.
- Dropped the unnecessary local variable.
The patch series is based on v6.7rc3.
William Qiu (4):
dt-bindings: pwm: Add bindings for OpenCores PWM Controller
pwm: opencores: Add PWM driver support
riscv: dts: starfive: jh7100: Add PWM node and pins configuration
riscv: dts: starfive: jh7110: Add PWM node and pins configuration
.../bindings/pwm/opencores,pwm.yaml | 56 +++++ MAINTAINERS | 7 + .../boot/dts/starfive/jh7100-common.dtsi | 24 ++ arch/riscv/boot/dts/starfive/jh7100.dtsi | 9 + .../jh7110-starfive-visionfive-2.dtsi | 22 ++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 9 + drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-ocores.c | 232 ++++++++++++++++++ 9 files changed, 372 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml create mode 100644 drivers/pwm/pwm-ocores.c
--
2.34.1
PATCH v8 1/4 dt-bindings: pwm: Add bindings for OpenCores PWM Controller
Add bindings for OpenCores PWM Controller.
--- .../bindings/pwm/opencores,pwm.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +56 -0
@@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OpenCores PWM controller + +maintainers: + - William Qiu <[email protected]> + +description: + OpenCores PTC ip core contains a PWM controller. When operating in PWM mode, + the PTC core generates binary signal with user-programmable low and high + periods. All PTC counters and registers are 32-bit. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - starfive,jh7100-pwm + - starfive,jh7110-pwm + - const: opencores,pwm-v1 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + pwm@12490000 { + compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; + reg = <0x12490000 0x10000>; + clocks = <&clkgen 181>; + resets = <&rstgen 109>; + #pwm-cells = <3>; + };
Signature · 1 line
2.34.1
Conor Dooley
maintainerRe: [PATCH v8 1/4] dt-bindings: pwm: Add bindings for OpenCores PWM Controller
Quoted 18 lines · William Qiu
Add bindings for OpenCores PWM Controller.
Signed-off-by William Qiu <[email protected]>Reviewed-by Hal Feng <[email protected]>--- .../bindings/pwm/opencores,pwm.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yamlDocumentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +3
-0@@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +---
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml line 13
Quoted 10 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +10
-0+$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OpenCores PWM controller + +maintainers: + - William Qiu <[email protected]> + +description: + OpenCores PTC ip core contains a PWM controller. When operating in PWM mode,
nit: "The OpenCores PTC IP core"
Quoted 4 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +4
-0+ the PTC core generates binary signal with user-programmable low and high + periods. All PTC counters and registers are 32-bit. + +allOf:
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml line 27
Quoted 10 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +10
-0+ - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - starfive,jh7100-pwm + - starfive,jh7110-pwm + - const: opencores,pwm-v1
properties:
compatible:
items:
- enum:
- starfive,jh7100-pwm
- starfive,jh7110-pwm
- const: opencores,pwm-v1
Please use this form here instead.
Otherwise, this looks good to me now.
Quoted 33 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +29
-0+ + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + pwm@12490000 { + compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; + reg = <0x12490000 0x10000>; + clocks = <&clkgen 181>; + resets = <&rstgen 109>; + #pwm-cells = <3>; + };Signature · 1 line
2.34.1
Re: [PATCH v8 1/4] dt-bindings: pwm: Add bindings for OpenCores PWM Controller
Quoted 23 lines · Conor Dooley
Quoted 22 lines · William Qiu
Add bindings for OpenCores PWM Controller.
Signed-off-by William Qiu <[email protected]>Reviewed-by Hal Feng <[email protected]>--- .../bindings/pwm/opencores,pwm.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yamlDocumentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +7
-0@@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OpenCores PWM controller
Quoted 9 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml line 13
Quoted 6 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +6
-0+ +maintainers: + - William Qiu <[email protected]> + +description: + OpenCores PTC ip core contains a PWM controller. When operating in PWM mode,nit: "The OpenCores PTC IP core"
Will update.
Quoted 23 lines
Quoted 4 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +4
-0+ the PTC core generates binary signal with user-programmable low and high + periods. All PTC counters and registers are 32-bit. + +allOf:Documentation/devicetree/bindings/pwm/opencores,pwm.yaml line 27
Quoted 10 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +10
-0+ - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - starfive,jh7100-pwm + - starfive,jh7110-pwm + - const: opencores,pwm-v1properties:
compatible:
items:
- enum:
- starfive,jh7100-pwm
- starfive,jh7110-pwm
- const: opencores,pwm-v1
Quoted 4 lines
Please use this form here instead.
Otherwise, this looks good to me now.
I see. I'll update it.
Thanks for spending time to review this patchset.
Best Regards,
William
Quoted 33 lines
Quoted 33 lines
Documentation/devicetree/bindings/pwm/opencores,pwm.yaml new file +29
-0+ + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + pwm@12490000 { + compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; + reg = <0x12490000 0x10000>; + clocks = <&clkgen 181>; + resets = <&rstgen 109>; + #pwm-cells = <3>; + };Signature · 1 line
2.34.1
PATCH v8 2/4 pwm: opencores: Add PWM driver support
In reply to William QiuAdd driver for OpenCores PWM Controller. And add compatibility code which based on StarFive SoC.
--- MAINTAINERS | 7 ++ drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-ocores.c | 232 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 drivers/pwm/pwm-ocores.c
MAINTAINERS +7 -0
@@ -16206,6 +16206,13 @@ F: Documentation/i2c/busses/i2c-ocores.rst F: drivers/i2c/busses/i2c-ocores.c F: include/linux/platform_data/i2c-ocores.h +OPENCORES PWM DRIVER +M: William Qiu <[email protected]> +M: Hal Feng <[email protected]> +S: Supported +F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml +F: drivers/pwm/pwm-ocores.c + OPENRISC ARCHITECTURE M: Jonas Bonn <[email protected]> M: Stefan Kristiansson <[email protected]>
drivers/pwm/Kconfig +12 -0
@@ -444,6 +444,18 @@ config PWM_NTXEC controller found in certain e-book readers designed by the original design manufacturer Netronix. +config PWM_OCORES + tristate "OpenCores PWM support" + depends on HAS_IOMEM && OF + depends on COMMON_CLK && RESET_CONTROLLER + depends on ARCH_STARFIVE || COMPILE_TEST + help + If you say yes to this option, support will be included for the + OpenCores PWM. For details see https://opencores.org/projects/ptc. + + To compile this driver as a module, choose M here: the module + will be called pwm-ocores. + config PWM_OMAP_DMTIMER tristate "OMAP Dual-Mode Timer PWM support" depends on OF
drivers/pwm/Makefile +1 -0
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o +obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
drivers/pwm/pwm-ocores.c new file +232 -0
232 lines not on this page · raw message
Signature · 1 line
2.34.1
Re: [PATCH v8 2/4] pwm: opencores: Add PWM driver support
Quoted 255 lines · William Qiu
Add driver for OpenCores PWM Controller. And add compatibility code which based on StarFive SoC.
Co-developed-by Hal Feng <[email protected]>Signed-off-by Hal Feng <[email protected]>Signed-off-by William Qiu <[email protected]>--- MAINTAINERS | 7 ++ drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-ocores.c | 232 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 drivers/pwm/pwm-ocores.cMAINTAINERS +7
-0@@ -16206,6 +16206,13 @@ F: Documentation/i2c/busses/i2c-ocores.rst F: drivers/i2c/busses/i2c-ocores.c F: include/linux/platform_data/i2c-ocores.h +OPENCORES PWM DRIVER +M: William Qiu <[email protected]> +M: Hal Feng <[email protected]> +S: Supported +F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml +F: drivers/pwm/pwm-ocores.c + OPENRISC ARCHITECTURE M: Jonas Bonn <[email protected]> M: Stefan Kristiansson <[email protected]>drivers/pwm/Kconfig +12
-0@@ -444,6 +444,18 @@ config PWM_NTXEC controller found in certain e-book readers designed by the original design manufacturer Netronix. +config PWM_OCORES + tristate "OpenCores PWM support" + depends on HAS_IOMEM && OF + depends on COMMON_CLK && RESET_CONTROLLER + depends on ARCH_STARFIVE || COMPILE_TEST + help + If you say yes to this option, support will be included for the + OpenCores PWM. For details see https://opencores.org/projects/ptc. + + To compile this driver as a module, choose M here: the module + will be called pwm-ocores. + config PWM_OMAP_DMTIMER tristate "OMAP Dual-Mode Timer PWM support" depends on OFdrivers/pwm/Makefile +1
-0@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o +obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.odrivers/pwm/pwm-ocores.c new file +182
-0@@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* OpenCores PWM Driver +* +* https://opencores.org/projects/ptc +* +* Copyright (C) 2018-2023 StarFive Technology Co., Ltd. +* +* Limitations: +* - The hardware only do inverted polarity. +* - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns. +* - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns. +*/ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/reset.h> +#include <linux/slab.h> + +/* OCPWM_CTRL register bits*/ +#define REG_OCPWM_EN BIT(0) +#define REG_OCPWM_ECLK BIT(1) +#define REG_OCPWM_NEC BIT(2) +#define REG_OCPWM_OE BIT(3) +#define REG_OCPWM_SIGNLE BIT(4) +#define REG_OCPWM_INTE BIT(5) +#define REG_OCPWM_INT BIT(6) +#define REG_OCPWM_CNTRRST BIT(7) +#define REG_OCPWM_CAPTE BIT(8) + +struct ocores_pwm_device { +struct pwm_chip chip; +struct clk *clk; +struct reset_control *rst; +const struct ocores_pwm_data *data; +void __iomem *regs; +u32 clk_rate;/* PWM APB clock frequency */ +}; + +struct ocores_pwm_data { +void __iomem *(*get_ch_base)(void __iomem *base,unsigned int channel); +}; + +static inline u32 ocores_readl (struct ocores_pwm_device *ddata, +unsigned int channel, +unsigned int offset) +{ +void __iomem *base = ddata->data->get_ch_base ? + ddata->data->get_ch_base (ddata->regs, channel) : ddata->regs; + +return readl (base + offset); +} + +static inline void ocores_writel (struct ocores_pwm_device *ddata, +unsigned int channel, +unsigned int offset,u32 val) +{ +void __iomem *base = ddata->data->get_ch_base ? + ddata->data->get_ch_base (ddata->regs, channel) : ddata->regs; + +writel (val, base + offset); +} + +static inline struct ocores_pwm_device *chip_to_ocores (struct pwm_chip *chip) +{ + return container_of(chip, struct ocores_pwm_device, chip); +} + +static void __iomem *starfive_jh71x0_get_ch_base (void __iomem *base, +unsigned int channel) +{ +unsigned int offset = (channel >3 ?1 <<15 :0 ) + (channel &3 ) *0x10 ; + +return base + offset; +} + +static int ocores_pwm_get_state (struct pwm_chip *chip, +struct pwm_device *pwm, +struct pwm_state *state) +{ +struct ocores_pwm_device *ddata =chip_to_ocores (chip); +u32 period_data, duty_data, ctrl_data; + + period_data =ocores_readl (ddata, pwm->hwpwm,0x8 ); + duty_data =ocores_readl (ddata, pwm->hwpwm,0x4 ); + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); + + state->period =DIV_ROUND_UP_ULL ((u64 )period_data *NSEC_PER_SEC , ddata->clk_rate); + state->duty_cycle =DIV_ROUND_UP_ULL ((u64 )duty_data *NSEC_PER_SEC , ddata->clk_rate); + state->polarity =PWM_POLARITY_INVERSED ; + state->enabled = (ctrl_data ®_OCPWM_EN ) ? true : false; + +return 0 ; +} + +static int ocores_pwm_apply (struct pwm_chip *chip, +struct pwm_device *pwm, +const struct pwm_state *state) +{ +struct ocores_pwm_device *ddata =chip_to_ocores (chip); +u32 ctrl_data =0 ; +u64 period_data, duty_data; + +if (state->polarity !=PWM_POLARITY_INVERSED ) +return -EINVAL ; + + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); +ocores_writel (ddata, pwm->hwpwm,0xC ,0 ); + + period_data =DIV_ROUND_DOWN_ULL (state->period * ddata->clk_rate,NSEC_PER_SEC ); +if (period_data <=U32_MAX ) +ocores_writel (ddata, pwm->hwpwm,0x8 , (u32 )period_data); +else +return -EINVAL ; + + duty_data =DIV_ROUND_DOWN_ULL (state->duty_cycle * ddata->clk_rate,NSEC_PER_SEC ); +if (duty_data <=U32_MAX ) +ocores_writel (ddata, pwm->hwpwm,0x4 , (u32 )duty_data); +else +return -EINVAL ; + +ocores_writel (ddata, pwm->hwpwm,0xC ,0 ); + +if (state->enabled) { + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); +ocores_writel (ddata, pwm->hwpwm,0xC , ctrl_data |REG_OCPWM_EN |REG_OCPWM_OE ); + } + +return 0 ; +} + +static const struct pwm_ops ocores_pwm_ops = { + .get_state = ocores_pwm_get_state, + .apply = ocores_pwm_apply, +}; + +static const struct ocores_pwm_data jh7100_pwm_data = { + .get_ch_base = starfive_jh71x0_get_ch_base, +}; + +static const struct ocores_pwm_data jh7110_pwm_data = { + .get_ch_base = starfive_jh71x0_get_ch_base, +}; + +static const struct of_device_id ocores_pwm_of_match[] = { + { .compatible ="opencores,pwm-v1" }, + { .compatible ="starfive,jh7100-pwm" , .data = &jh7100_pwm_data}, + { .compatible ="starfive,jh7110-pwm" , .data = &jh7110_pwm_data}, + {/* sentinel */ } +}; +MODULE_DEVICE_TABLE (of, ocores_pwm_of_match); + +static int ocores_pwm_probe (struct platform_device *pdev) +{ +const struct of_device_id *id; +struct device *dev = &pdev->dev; +struct ocores_pwm_device *ddata; +struct pwm_chip *chip; +int ret; + + id =of_match_device (ocores_pwm_of_match, dev); +if (!id) +return -EINVAL ; + + ddata =devm_kzalloc (dev,sizeof (*ddata),GFP_KERNEL ); +if (!ddata) +return -ENOMEM ; + + ddata->data = id->data; + chip = &ddata->chip; + chip->dev = dev; + chip->ops = &ocores_pwm_ops; + chip->npwm =8 ; + chip->of_pwm_n_cells =3 ; + + ddata->regs =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (ddata->regs))
drivers/pwm/pwm-ocores.c line 192
Quoted 10 lines
drivers/pwm/pwm-ocores.c new file +10
-0+return dev_err_probe (dev,PTR_ERR (ddata->regs), +"Unable to map IO resources\n" ); + + ddata->clk =devm_clk_get_enabled (dev,NULL ); +if (IS_ERR (ddata->clk)) +return dev_err_probe (dev,PTR_ERR (ddata->clk), +"Unable to get pwm's clock\n" ); + + ddata->rst =devm_reset_control_get_optional_exclusive (dev,NULL ); +reset_control_deassert (ddata->rst);
You could use devm_add_action_or_reset() to add reset assertion for driver removal and the error paths here, allowing to drop ocores_pwm_remove() and ...
drivers/pwm/pwm-ocores.c line 197
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+ ddata->clk_rate =clk_get_rate (ddata->clk); +if (ddata->clk_rate <=0 ) +return dev_err_probe (dev, ddata->clk_rate, +"Unable to get clock's rate\n" );
... then this wouldn't leave the reset deasserted ...
drivers/pwm/pwm-ocores.c line 201
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+ + ret =devm_pwmchip_add (dev, chip); +if (ret <0 ) { +reset_control_assert (ddata->rst);
... and this could be dropped.
drivers/pwm/pwm-ocores.c line 201
Quoted 1 line
drivers/pwm/pwm-ocores.c new file +1
-0+clk_disable_unprepare (ddata->clk);
This can be dropped, devm_clk_get_enabled() already disables the clock on probe error.
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+return dev_err_probe (dev, ret,"Could not register PWM chip\n" ); + } + +platform_set_drvdata (pdev, ddata);
drivers/pwm/pwm-ocores.c line 216
Quoted 10 lines
drivers/pwm/pwm-ocores.c new file +10
-0+ +return ret; +} + +static void ocores_pwm_remove (struct platform_device *dev) +{ +struct ocores_pwm_device *ddata =platform_get_drvdata (dev); + +reset_control_assert (ddata->rst); +clk_disable_unprepare (ddata->clk);
Drop as well, devm_clk_get_enabled() already disables the clock on driver removal.
regards
Philipp
Re: [PATCH v8 2/4] pwm: opencores: Add PWM driver support
Quoted 262 lines · Philipp Zabel
Quoted 261 lines · William Qiu
Add driver for OpenCores PWM Controller. And add compatibility code which based on StarFive SoC.
Co-developed-by Hal Feng <[email protected]>Signed-off-by Hal Feng <[email protected]>Signed-off-by William Qiu <[email protected]>--- MAINTAINERS | 7 ++ drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-ocores.c | 232 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 drivers/pwm/pwm-ocores.cMAINTAINERS +7
-0@@ -16206,6 +16206,13 @@ F: Documentation/i2c/busses/i2c-ocores.rst F: drivers/i2c/busses/i2c-ocores.c F: include/linux/platform_data/i2c-ocores.h +OPENCORES PWM DRIVER +M: William Qiu <[email protected]> +M: Hal Feng <[email protected]> +S: Supported +F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml +F: drivers/pwm/pwm-ocores.c + OPENRISC ARCHITECTURE M: Jonas Bonn <[email protected]> M: Stefan Kristiansson <[email protected]>drivers/pwm/Kconfig +12
-0@@ -444,6 +444,18 @@ config PWM_NTXEC controller found in certain e-book readers designed by the original design manufacturer Netronix. +config PWM_OCORES + tristate "OpenCores PWM support" + depends on HAS_IOMEM && OF + depends on COMMON_CLK && RESET_CONTROLLER + depends on ARCH_STARFIVE || COMPILE_TEST + help + If you say yes to this option, support will be included for the + OpenCores PWM. For details see https://opencores.org/projects/ptc. + + To compile this driver as a module, choose M here: the module + will be called pwm-ocores. + config PWM_OMAP_DMTIMER tristate "OMAP Dual-Mode Timer PWM support" depends on OFdrivers/pwm/Makefile +1
-0@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o +obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.odrivers/pwm/pwm-ocores.c new file +188
-0@@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* OpenCores PWM Driver +* +* https://opencores.org/projects/ptc +* +* Copyright (C) 2018-2023 StarFive Technology Co., Ltd. +* +* Limitations: +* - The hardware only do inverted polarity. +* - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns. +* - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns. +*/ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/reset.h> +#include <linux/slab.h> + +/* OCPWM_CTRL register bits*/ +#define REG_OCPWM_EN BIT(0) +#define REG_OCPWM_ECLK BIT(1) +#define REG_OCPWM_NEC BIT(2) +#define REG_OCPWM_OE BIT(3) +#define REG_OCPWM_SIGNLE BIT(4) +#define REG_OCPWM_INTE BIT(5) +#define REG_OCPWM_INT BIT(6) +#define REG_OCPWM_CNTRRST BIT(7) +#define REG_OCPWM_CAPTE BIT(8) + +struct ocores_pwm_device { +struct pwm_chip chip; +struct clk *clk; +struct reset_control *rst; +const struct ocores_pwm_data *data; +void __iomem *regs; +u32 clk_rate;/* PWM APB clock frequency */ +}; + +struct ocores_pwm_data { +void __iomem *(*get_ch_base)(void __iomem *base,unsigned int channel); +}; + +static inline u32 ocores_readl (struct ocores_pwm_device *ddata, +unsigned int channel, +unsigned int offset) +{ +void __iomem *base = ddata->data->get_ch_base ? + ddata->data->get_ch_base (ddata->regs, channel) : ddata->regs; + +return readl (base + offset); +} + +static inline void ocores_writel (struct ocores_pwm_device *ddata, +unsigned int channel, +unsigned int offset,u32 val) +{ +void __iomem *base = ddata->data->get_ch_base ? + ddata->data->get_ch_base (ddata->regs, channel) : ddata->regs; + +writel (val, base + offset); +} + +static inline struct ocores_pwm_device *chip_to_ocores (struct pwm_chip *chip) +{ + return container_of(chip, struct ocores_pwm_device, chip); +} + +static void __iomem *starfive_jh71x0_get_ch_base (void __iomem *base, +unsigned int channel) +{ +unsigned int offset = (channel >3 ?1 <<15 :0 ) + (channel &3 ) *0x10 ; + +return base + offset; +} + +static int ocores_pwm_get_state (struct pwm_chip *chip, +struct pwm_device *pwm, +struct pwm_state *state) +{ +struct ocores_pwm_device *ddata =chip_to_ocores (chip); +u32 period_data, duty_data, ctrl_data; + + period_data =ocores_readl (ddata, pwm->hwpwm,0x8 ); + duty_data =ocores_readl (ddata, pwm->hwpwm,0x4 ); + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); + + state->period =DIV_ROUND_UP_ULL ((u64 )period_data *NSEC_PER_SEC , ddata->clk_rate); + state->duty_cycle =DIV_ROUND_UP_ULL ((u64 )duty_data *NSEC_PER_SEC , ddata->clk_rate); + state->polarity =PWM_POLARITY_INVERSED ; + state->enabled = (ctrl_data ®_OCPWM_EN ) ? true : false; + +return 0 ; +} + +static int ocores_pwm_apply (struct pwm_chip *chip, +struct pwm_device *pwm, +const struct pwm_state *state) +{ +struct ocores_pwm_device *ddata =chip_to_ocores (chip); +u32 ctrl_data =0 ; +u64 period_data, duty_data; + +if (state->polarity !=PWM_POLARITY_INVERSED ) +return -EINVAL ; + + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); +ocores_writel (ddata, pwm->hwpwm,0xC ,0 ); + + period_data =DIV_ROUND_DOWN_ULL (state->period * ddata->clk_rate,NSEC_PER_SEC ); +if (period_data <=U32_MAX ) +ocores_writel (ddata, pwm->hwpwm,0x8 , (u32 )period_data); +else +return -EINVAL ; + + duty_data =DIV_ROUND_DOWN_ULL (state->duty_cycle * ddata->clk_rate,NSEC_PER_SEC ); +if (duty_data <=U32_MAX ) +ocores_writel (ddata, pwm->hwpwm,0x4 , (u32 )duty_data); +else +return -EINVAL ; + +ocores_writel (ddata, pwm->hwpwm,0xC ,0 ); + +if (state->enabled) { + ctrl_data =ocores_readl (ddata, pwm->hwpwm,0xC ); +ocores_writel (ddata, pwm->hwpwm,0xC , ctrl_data |REG_OCPWM_EN |REG_OCPWM_OE ); + } + +return 0 ; +} + +static const struct pwm_ops ocores_pwm_ops = { + .get_state = ocores_pwm_get_state, + .apply = ocores_pwm_apply, +}; + +static const struct ocores_pwm_data jh7100_pwm_data = { + .get_ch_base = starfive_jh71x0_get_ch_base, +}; + +static const struct ocores_pwm_data jh7110_pwm_data = { + .get_ch_base = starfive_jh71x0_get_ch_base, +}; + +static const struct of_device_id ocores_pwm_of_match[] = { + { .compatible ="opencores,pwm-v1" }, + { .compatible ="starfive,jh7100-pwm" , .data = &jh7100_pwm_data}, + { .compatible ="starfive,jh7110-pwm" , .data = &jh7110_pwm_data}, + {/* sentinel */ } +}; +MODULE_DEVICE_TABLE (of, ocores_pwm_of_match); + +static int ocores_pwm_probe (struct platform_device *pdev) +{ +const struct of_device_id *id; +struct device *dev = &pdev->dev; +struct ocores_pwm_device *ddata; +struct pwm_chip *chip; +int ret; + + id =of_match_device (ocores_pwm_of_match, dev); +if (!id) +return -EINVAL ; + + ddata =devm_kzalloc (dev,sizeof (*ddata),GFP_KERNEL ); +if (!ddata) +return -ENOMEM ; + + ddata->data = id->data; + chip = &ddata->chip; + chip->dev = dev; + chip->ops = &ocores_pwm_ops; + chip->npwm =8 ; + chip->of_pwm_n_cells =3 ; + + ddata->regs =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (ddata->regs)) +return dev_err_probe (dev,PTR_ERR (ddata->regs), +"Unable to map IO resources\n" ); + + ddata->clk =devm_clk_get_enabled (dev,NULL ); +if (IS_ERR (ddata->clk)) + return dev_err_probe(dev, PTR_ERR(ddata->clk),
Quoted 9 lines
drivers/pwm/pwm-ocores.c line 192
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+ "Unable to get pwm's clock\n"); + + ddata->rst =devm_reset_control_get_optional_exclusive (dev,NULL ); +reset_control_deassert (ddata->rst);You could use
devm_add_action_or_reset()to add reset assertion for driver removal and the error paths here, allowing to dropocores_pwm_remove()and ...
I see. I would use devm_add_action_or_reset(), and drop redundant code.
Quoted 7 lines
drivers/pwm/pwm-ocores.c line 197
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+ ddata->clk_rate =clk_get_rate (ddata->clk); +if (ddata->clk_rate <=0 ) +return dev_err_probe (dev, ddata->clk_rate, +"Unable to get clock's rate\n" );... then this wouldn't leave the reset deasserted ...
...
Quoted 7 lines
drivers/pwm/pwm-ocores.c line 201
Quoted 4 lines
drivers/pwm/pwm-ocores.c new file +4
-0+ + ret =devm_pwmchip_add (dev, chip); +if (ret <0 ) { +reset_control_assert (ddata->rst);... and this could be dropped.
...
Quoted 5 lines
drivers/pwm/pwm-ocores.c
Quoted 1 line
drivers/pwm/pwm-ocores.c new file +1
-0+clk_disable_unprepare (ddata->clk);This can be dropped,
devm_clk_get_enabled()already disables the clock on probe error.
Will drop.
Quoted 9 lines
Quoted 9 lines
drivers/pwm/pwm-ocores.c new file +9
-0+return dev_err_probe (dev, ret,"Could not register PWM chip\n" ); + } + +platform_set_drvdata (pdev, ddata); + +return ret; +} + +static void ocores_pwm_remove (struct platform_device *dev)
Quoted 9 lines
drivers/pwm/pwm-ocores.c line 216
Quoted 5 lines
drivers/pwm/pwm-ocores.c new file +5
-0+{ +struct ocores_pwm_device *ddata =platform_get_drvdata (dev); + +reset_control_assert (ddata->rst); +clk_disable_unprepare (ddata->clk);Drop as well,
devm_clk_get_enabled()already disables the clock on driver removal.
I will drop all remove() interface.
Thank you for taking time to review this patchset.
Best Regards,
William
Quoted 2 lines
regards
Philipp
PATCH v8 3/4 riscv: dts: starfive: jh7100: Add PWM node and pins configuration
In reply to William QiuAdd OpenCores PWM controller node and add PWM pins configuration on VisionFive 1 board.
--- .../boot/dts/starfive/jh7100-common.dtsi | 24 +++++++++++++++++++ arch/riscv/boot/dts/starfive/jh7100.dtsi | 9 +++++++ 2 files changed, 33 insertions(+)
arch/riscv/boot/dts/starfive/jh7100-common.dtsi +24 -0
@@ -84,6 +84,24 @@ GPO_I2C2_PAD_SDA_OEN, }; }; + pwm_pins: pwm-0 { + pwm-pins { + pinmux = <GPIOMUX(7, + GPO_PWM_PAD_OUT_BIT0, + GPO_PWM_PAD_OE_N_BIT0, + GPI_NONE)>, + <GPIOMUX(5, + GPO_PWM_PAD_OUT_BIT1, + GPO_PWM_PAD_OE_N_BIT1, + GPI_NONE)>; + bias-disable; + drive-strength = <35>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + uart3_pins: uart3-0 { rx-pins { pinmux = <GPIOMUX(13, GPO_LOW, GPO_DISABLE,
@@ -154,6 +172,12 @@ &osc_aud { clock-frequency = <27000000>; }; +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + status = "okay"; +}; + &uart3 { pinctrl-names = "default"; pinctrl-0 = <&uart3_pins>;
arch/riscv/boot/dts/starfive/jh7100.dtsi +9 -0
@@ -280,6 +280,15 @@ watchdog@12480000 { <&rstgen JH7100_RSTN_WDT>; }; + pwm: pwm@12490000 { + compatible = "starfive,jh7100-pwm", "opencores,pwm-v1"; + reg = <0x0 0x12490000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_PWM_APB>; + resets = <&rstgen JH7100_RSTN_PWM_APB>; + #pwm-cells = <3>; + status = "disabled"; + }; + sfctemp: temperature-sensor@124a0000 { compatible = "starfive,jh7100-temp"; reg = <0x0 0x124a0000 0x0 0x10000>;
Signature · 1 line
2.34.1
PATCH v8 4/4 riscv: dts: starfive: jh7110: Add PWM node and pins configuration
In reply to William QiuAdd OpenCores PWM controller node and add PWM pins configuration on VisionFive 2 board.
--- .../jh7110-starfive-visionfive-2.dtsi | 22 +++++++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 9 ++++++++ 2 files changed, 31 insertions(+)
arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +22 -0
@@ -323,6 +323,12 @@ reserved-data@600000 { }; }; +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + status = "okay"; +}; + &spi0 { pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>;
@@ -513,6 +519,22 @@ GPOEN_ENABLE, }; }; + pwm_pins: pwm-0 { + pwm-pins { + pinmux = <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL0, + GPOEN_SYS_PWM0_CHANNEL0, + GPI_NONE)>, + <GPIOMUX(59, GPOUT_SYS_PWM_CHANNEL1, + GPOEN_SYS_PWM0_CHANNEL1, + GPI_NONE)>; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + spi0_pins: spi0-0 { mosi-pins { pinmux = <GPIOMUX(52, GPOUT_SYS_SPI0_TXD,
arch/riscv/boot/dts/starfive/jh7110.dtsi +9 -0
@@ -829,6 +829,15 @@ i2stx1: i2s@120c0000 { status = "disabled"; }; + pwm: pwm@120d0000 { + compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; + reg = <0x0 0x120d0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; + resets = <&syscrg JH7110_SYSRST_PWM_APB>; + #pwm-cells = <3>; + status = "disabled"; + }; + sfctemp: temperature-sensor@120e0000 { compatible = "starfive,jh7110-temp"; reg = <0x0 0x120e0000 0x0 0x10000>;
Signature · 1 line
2.34.1