Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PMC里的PWM #12

Draft
wants to merge 3 commits into
base: histb
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Documentation/devicetree/bindings/pwm/hisilicon,pmc-pwm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pwm/hisilicon,pmc-pwm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: HiSilicon PMC PWM device

maintainers:
- Yang Xiwen <[email protected]>

allOf:
- $ref: pwm.yaml#

properties:
compatible:
items:
- enum:
- hisilicon,hi3798mv200-pwm
- const: hisilicon,pmc-pwm

reg:
maxItems: 1

clocks:
maxItems: 1

"#pwm-cells":
const: 3

required:
- compatible
- reg
- clocks

additionalProperties: false

examples:
- |
pwm@18 {
compatible = "hisilicon,hi3798mv200-pwm", "hisilicon,pmc-pwm";
reg = <0x18 0x4>;
clocks = <&clk>;
#pwm-cells = <3>;
};
10 changes: 10 additions & 0 deletions drivers/pwm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,16 @@ config PWM_HIBVT
To compile this driver as a module, choose M here: the module
will be called pwm-hibvt.

config PWM_HISI_PMC_PWM
tristate "HiSilicon PMC PWM support"
depends on ARCH_HISI || COMPILE_TEST
depends on COMMON_CLK
help
Generic PMC PWM driver for HiSilicon SoCs

To compile this driver as a module, choose M here: the module
will be called pwm-hisi-pmc-pwm.

config PWM_IMG
tristate "Imagination Technologies PWM driver"
depends on HAS_IOMEM
Expand Down
1 change: 1 addition & 0 deletions drivers/pwm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_HISI_PMC_PWM) += pwm-hisi-pmc-pwm.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
Expand Down
137 changes: 137 additions & 0 deletions drivers/pwm/pwn-hisi-pmc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the integrated PWM module in HiSilicon PMC core
*
* Copyright 2024 (c) Yang Xiwen
*/

#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>

#define HISI_PWM_PERIOD GENMASK(15, 0)
#define HISI_PWM_DUTY GENMASK(31, 16)

struct hisi_pmc_pwm {
struct pwm_chip chip;
void __iomem *base;
ulong rate;
};

#define to_hisi_pmc_pwm(chip) container_of(chip, struct hisi_pmc_pwm, chip)

static int hisi_pmc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct hisi_pmc_pwm *fpwm;
u64 period_cycles, duty_cycles;
u32 reg;

/* Turned on by boot loader, and PWMs in PMC are always critical */
if (!state->enabled || state->polarity == PWM_POLARITY_INVERSED)
return -EINVAL;

fpwm = to_hisi_pmc_pwm(chip);

period_cycles = mul_u64_u64_div_u64(fpwm->rate, state->period, NSEC_PER_SEC);
period_cycles = clamp(period_cycles, 0, 0xFFFF);

duty_cycles = mul_u64_u64_div_u64(fpwm->rate, state->duty_cycle, NSEC_PER_SEC);
duty_cycles = clamp(duty_cycles, 0, 0xFFFF);

reg = FIELD_PREP(HISI_PWM_PERIOD, period_cycles) |
FIELD_PREP(HISI_PWM_DUTY, duty_cycles);
writel(reg, fpwm->base);

return 0;
}

static int hisi_pmc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
struct hisi_pmc_pwm *fpwm;
u16 period_cycles, duty_cycles;
u32 reg;

fpwm = to_hisi_pmc_pwm(chip);

reg = readl(fpwm->base);

period_cycles = FIELD_GET(HISI_PWM_PERIOD, reg);
duty_cycles = FIELD_GET(HISI_PWM_DUTY, reg);

state->enabled = true;
state->polarity = PWM_POLARITY_NORMAL;

state->duty_cycle = DIV64_U64_ROUND_CLOSEST((u64)duty_cycles * NSEC_PER_SEC, fpwm->rate);
state->period = DIV64_U64_ROUND_CLOSEST((u64)period_cycles * NSEC_PER_SEC, fpwm->rate);

return 0;
}

static const struct pwm_ops hisi_pmc_pwm_ops = {
.apply = hisi_pmc_pwm_apply,
.get_state = hisi_pmc_pwm_get_state,
};

static int hisi_pmc_pwm_probe(struct platform_device *pdev)
{
struct hisi_pmc_pwm *fpwm;
struct clk *clk;
int ret;

fpwm = devm_kzalloc(&pdev->dev, sizeof(*fpwm), GFP_KERNEL);
if (!fpwm)
return -ENOMEM;

fpwm->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(fpwm->base))
return PTR_ERR(fpwm->base);

clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get the clock");

/*
* Uses the 24MHz system clock on all existing devices, can only
* happen if the device tree is broken
*
* This check is done to prevent an overflow in .apply
*/
fpwm->rate = clk_get_rate(clk);
if (fpwm->rate > NSEC_PER_SEC)
return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock out of range");

fpwm->chip.dev = &pdev->dev;
fpwm->chip.npwm = 1;
fpwm->chip.ops = &hisi_pmc_pwm_ops;

ret = devm_pwmchip_add(&pdev->dev, &fpwm->chip);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "unable to add pwm chip");

return 0;
}

static const struct of_device_id hisi_pmc_pwm_of_match[] = {
{ .compatible = "hisilicon,pmc-pwm" },
{ .compatible = "hisilicon,hi3798mv200-pwm" },
{ },
};
MODULE_DEVICE_TABLE(of, hisi_pmc_pwm_of_match);

static struct platform_driver hisi_pmc_pwm_driver = {
.probe = hisi_pmc_pwm_probe,
.driver = {
.name = "hisi-pmc-pwm",
.of_match_table = hisi_pmc_pwm_of_match,
},
};
module_platform_driver(hisi_pmc_pwm_driver);

MODULE_DESCRIPTION("HiSilicon SoC PMC PWM driver");
MODULE_AUTHOR("Yang Xiwen <[email protected]>");
MODULE_LICENSE("GPL");