[PATCH v3] pwm: add CSR SiRFSoC PWM driver
Thierry Reding
thierry.reding at gmail.com
Wed Feb 26 09:10:41 EST 2014
On Sat, Feb 08, 2014 at 06:23:22PM +0800, Barry Song wrote:
> From: Rongjun Ying <Rongjun.ying at csr.com>
>
> PWM controller of CSR SiRFSoC can generate 7 independent outputs. Each output
"The PWM controller of the CSR SiRFSoC..." and "Each output's..."
> duty cycle can be adjusted by setting the corresponding wait & hold registers.
>
> Supports 7 independent channel output: 6 for external(channel0-5) and 1 for
> internal(channel6).
This repeats part of the first sentence. Perhaps:
There are 6 external channels (0 to 5) and 1 internal channel (6).
?
> Supports wide frequency range: divide by 2 to 65536*2 of source clock.
"Supports a wide frequency range: the source clock divider can be from 2
up to 65536*2".
> Documentation/devicetree/bindings/pwm/pwm-sirf.txt | 17 +
> arch/arm/boot/dts/atlas6.dtsi | 3 +-
> arch/arm/boot/dts/prima2.dtsi | 3 +-
> drivers/pwm/Kconfig | 9 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-sirf.c | 308 ++++++++++++++++++++
> 6 files changed, 339 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sirf.txt
> create mode 100644 drivers/pwm/pwm-sirf.c
>
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-sirf.txt b/Documentation/devicetree/bindings/pwm/pwm-sirf.txt
> new file mode 100644
> index 0000000..4b10109
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-sirf.txt
> @@ -0,0 +1,17 @@
> +SiRF prima2 & atlas6 PWM drivers
> +
> +Required properties:
> +- compatible: "sirf,prima2-pwm"
> +- reg: physical base address and length of the controller's registers
> +- #pwm-cells: should be 2. The first cell specifies the per-chip index
> + of the PWM to use and the second cell is the period in nanoseconds.
> +- clocks: from common clock binding: the 1st clock is for PWM controller
> + the 2nd clock is the source to generate PWM waves
> +
> +Example:
> +pwm: pwm at b0130000 {
> + compatible = "sirf,prima2-pwm";
> + #pwm-cells = <2>;
> + reg = <0xb0130000 0x10000>;
> + clocks = <&clks 21>, <&clks 1>;
> +};
Please move this into a separate patch and Cc the device tree bindings
maintainers. There's nothing non-standard in this binding as far as I
can tell, but I'd still like to give them an opportunity to object.
Also you should be documenting the clock-names property here as well.
> diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi
> index f8674bc..5a09815 100644
> --- a/arch/arm/boot/dts/atlas6.dtsi
> +++ b/arch/arm/boot/dts/atlas6.dtsi
> @@ -614,8 +614,9 @@
>
> pwm at b0130000 {
> compatible = "sirf,prima2-pwm";
> + #pwm-cells = <2>;
> reg = <0xb0130000 0x10000>;
> - clocks = <&clks 21>;
> + clocks = <&clks 21>, <&clks 1>;
> };
>
> efusesys at b0140000 {
> diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi
> index 0e21993..3439e48 100644
> --- a/arch/arm/boot/dts/prima2.dtsi
> +++ b/arch/arm/boot/dts/prima2.dtsi
> @@ -642,8 +642,9 @@
>
> pwm at b0130000 {
> compatible = "sirf,prima2-pwm";
> + #pwm-cells = <2>;
> reg = <0xb0130000 0x10000>;
> - clocks = <&clks 21>;
> + clocks = <&clks 21>, <&clks 1>;
> };
>
> efusesys at b0140000 {
These changes need to go into separate patches and go in through the
arm-soc tree.
> diff --git a/drivers/pwm/pwm-sirf.c b/drivers/pwm/pwm-sirf.c
> new file mode 100644
> index 0000000..b717de0
> --- /dev/null
> +++ b/drivers/pwm/pwm-sirf.c
> @@ -0,0 +1,308 @@
> +/*
> + * SIRF serial SoC PWM device core driver
> + *
> + * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company.
> + *
> + * Licensed under GPLv2 or later.
> + */
[...]
> +#define SIRF_PWM_CHL_NUM 7
This is only used in a single location with a well-known meaning, so no
need to use a symbolic name.
> +#define SIRF_PWM_BLS_GRP_NUM 16
This isn't used as far as I can tell.
> +struct sirf_pwm {
> + void __iomem *base;
> + struct clk *clk;
> + struct pwm_chip chip;
> + unsigned long src_clk_rate;
> +};
Please drop the alignment of the structure field. Also perhaps move the
chip field to be the first, so that the upcasting below can be turned
into a noop.
> +#define to_sirf_chip(chip) container_of(chip, struct sirf_pwm, chip)
Please make this a static inline function.
> +
> +static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns)
Please wrap this to go on a single line.
> +{
> + struct sirf_pwm *spwm = to_sirf_chip(chip);
> + u64 dividend;
> + unsigned int cycle;
> +
> + dividend = spwm->src_clk_rate * time_ns + NSEC_PER_SEC / 2;
> + do_div(dividend, NSEC_PER_SEC);
> +
> + cycle = dividend & 0xFFFFFFFFUL;
I don't think you need the mask, since you're casting to a 32-bit
unsigned integer anyway.
> +
> + return cycle > 1 ? cycle : 1;
> +}
> +
> +static int sirf_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> + int duty_ns, int period_ns)
Please align arguments on subsequent lines with those of the first.
> +{
> + unsigned int period_cycles, high_cycles, low_cycles;
> + unsigned int val;
u32 please.
> + struct sirf_pwm *spwm = to_sirf_chip(chip);
> +
> + period_cycles = sirf_pwm_ns_to_cycles(chip, period_ns);
> +
> + high_cycles = sirf_pwm_ns_to_cycles(chip, duty_ns);
No need for the blank line above, since there's no blank line below
either.
> + low_cycles = period_cycles - high_cycles;
> +
> + if (period_cycles == 1) {
> + /* bypass mode */
> + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
> + val |= 0x1 << (BYPASS_MODE_BIT + pwm->hwpwm);
> + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
> + dev_warn(chip->dev, "period is too short!\n");
What does the bypass mode do? Would it perhaps be better to make this an
outright error rather than only warning about it?
> + } else {
> + /* divider mode */
> + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
> + val &= ~(0x1 << (BYPASS_MODE_BIT + pwm->hwpwm));
> + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
> +
> + if (high_cycles == period_cycles) {
> + high_cycles--;
> + low_cycles = 1;
> + }
> +
> + writel(high_cycles, spwm->base + SIRF_PWM_GET_WAIT_OFFSET(pwm->hwpwm));
> + writel(low_cycles, spwm->base + SIRF_PWM_GET_HOLD_OFFSET(pwm->hwpwm));
> + }
> +
> + return 0;
> +}
> +
> +static int sirf_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct sirf_pwm *spwm = to_sirf_chip(chip);
> + unsigned int val;
The type used by readl() and writel() is u32.
> + /* disable preclock */
> + val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
> + val &= ~(1 << pwm->hwpwm);
> + writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
> +
> + /* select preclock source must after disable preclk*/
> + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
> + val &= ~(0x7 << (SRC_FIELD_SIZE * pwm->hwpwm));
> + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
> + /* wait for some time */
> + udelay(100);
usleep_range() perhaps?
> +static void sirf_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + unsigned int val;
u32 again.
> + struct sirf_pwm *spwm = to_sirf_chip(chip);
> + /* disable output */
Blank line between the above two, please.
> + val = readl(spwm->base + SIRF_PWM_OE);
> + val &= ~(1 << pwm->hwpwm);
> + writel(val, spwm->base + SIRF_PWM_OE);
> +
> + /* disable postclock */
> + val = readl(spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
> + val &= ~(1 << pwm->hwpwm);
> + writel(val, spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
> +
> + /* disable preclock */
> + val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
> + val &= ~(1 << pwm->hwpwm);
> + writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
I think you need a lock to synchronize accesses to these registers.
> +}
> +
> +static struct pwm_ops sirf_pwm_ops = {
static const please.
> + .enable = sirf_pwm_enable,
> + .disable = sirf_pwm_disable,
> + .config = sirf_pwm_config,
> + .owner = THIS_MODULE,
> +};
> +
> +static int sirf_pwm_probe(struct platform_device *pdev)
> +{
> + struct sirf_pwm *spwm;
> + struct resource *mem_res;
> + struct clk *clk_pwm_src;
> + int ret;
> +
> + spwm = devm_kzalloc(&pdev->dev, sizeof(struct sirf_pwm),
> + GFP_KERNEL);
"sizeof(*spwm)", please. And the above fits on a single line, no need to
wrap them.
> + if (!spwm)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, spwm);
> +
> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + spwm->base = devm_ioremap_resource(&pdev->dev, mem_res);
> + if (!spwm->base)
> + return -ENOMEM;
> +
> + /*
> + * the 1st clock is for PWM controller
> + */
> + spwm->clk = of_clk_get(pdev->dev.of_node, 0);
Use a clock-names property and devm_clk_get() here, please.
> + if (IS_ERR(spwm->clk)) {
> + dev_err(&pdev->dev, "Get PWM controller clock failed.\n");
"failed to get PWM controller clock"?
> + return PTR_ERR(spwm->clk);
> + }
> + clk_prepare_enable(spwm->clk);
Space between the above two. Also you need to check the return value of
clk_prepare_enable().
> +
> + /*
> + * the 2nd clock is the source to generate PWM waves
I'd prefer "signals" over "waves".
> + * it is the OSC on SiRFSoC
The clock is configurable via DT, so this isn't necessarily true. Even
if all hardware in existence currently works that way, it isn't a given
to remain like that forever.
> + */
> + clk_pwm_src = of_clk_get(pdev->dev.of_node, 1);
> + if (IS_ERR(clk_pwm_src)) {
> + dev_err(&pdev->dev, "Get PWM source clock failed.\n");
"failed to get PWM source clock"?
> + return PTR_ERR(clk_pwm_src);
> + }
> + spwm->src_clk_rate = clk_get_rate(clk_pwm_src);
Space between the above two please.
> + clk_put(clk_pwm_src);
Why drop the reference to it here? Shouldn't you keep it around and even
call clk_prepare_enable() on it to make sure it's actually on?
> +
> + spwm->chip.dev = &pdev->dev;
> + spwm->chip.ops = &sirf_pwm_ops;
> + spwm->chip.base = 0;
> + spwm->chip.npwm = SIRF_PWM_CHL_NUM;
> +
> + ret = pwmchip_add(&spwm->chip);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to register pwm\n");
"PWM" please.
> + clk_disable_unprepare(spwm->clk);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int sirf_pwm_remove(struct platform_device *pdev)
> +{
> + struct sirf_pwm *spwm = platform_get_drvdata(pdev);
> + clk_disable_unprepare(spwm->clk);
I'd prefer a blank line between the two above.
> + clk_put(spwm->clk);
> +
> + pwmchip_remove(&spwm->chip);
> + return 0;
This should be "return pwmchip_remove(...);".
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int sirf_pwm_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct sirf_pwm *spwm = platform_get_drvdata(pdev);
> +
> + clk_disable_unprepare(spwm->clk);
> +
> + return 0;
> +}
> +
> +static void sirf_pwm_config_restore(struct sirf_pwm *spwm)
> +{
> + struct pwm_device *pwm;
> + int i;
> +
> + for (i = 0; i < spwm->chip.npwm; i++) {
> + pwm = &spwm->chip.pwms[i];
> + /*
> + * while restoring from hibernation, state of pwm is enabled,
> + * but PWM hardware is not re-enabled
> + */
> + if (test_bit(PWMF_REQUESTED, &pwm->flags) &&
> + test_bit(PWMF_ENABLED, &pwm->flags))
Indentation is off by one space here.
> + sirf_pwm_enable(&spwm->chip, pwm);
> + }
> +}
> +
> +static int sirf_pwm_resume(struct device *dev)
> +{
> + struct sirf_pwm *spwm = dev_get_drvdata(dev);
> +
> + clk_prepare_enable(spwm->clk);
> +
> + sirf_pwm_config_restore(spwm);
> +
> + return 0;
> +}
> +
> +static int sirf_pwm_restore(struct device *dev)
> +{
> + struct sirf_pwm *spwm = dev_get_drvdata(dev);
> +
> + /* back from hibernation, clock is already enabled */
> + sirf_pwm_config_restore(spwm);
Why is the clock already enabled? Shouldn't it be off until this driver
enables it?
> +
> + return 0;
> +}
> +
> +#else
> +#define sirf_pwm_resume NULL
> +#define sirf_pwm_suspend NULL
> +#define sirf_pwm_restore NULL
> +#endif
> +
> +static const struct dev_pm_ops sirf_pwm_pm_ops = {
> + .suspend = sirf_pwm_suspend,
> + .resume = sirf_pwm_resume,
> + .restore = sirf_pwm_restore,
> +};
Because if you can unify _resume and _restore, this could all be
simplified using SIMPLE_DEV_PM_OPS().
> +
> +static const struct of_device_id sirf_pwm_of_match[] = {
> + { .compatible = "sirf,prima2-pwm", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, sirf_pwm_of_match);
> +
> +static struct platform_driver sirf_pwm_driver = {
> + .driver = {
> + .name = "prima2-pwm",
"sirf-pwm"?
> + .owner = THIS_MODULE,
This is no longer necessary.
> + .pm = &sirf_pwm_pm_ops,
> + .of_match_table = sirf_pwm_of_match,
> + },
> + .probe = sirf_pwm_probe,
> + .remove = sirf_pwm_remove,
> +};
> +
> +module_platform_driver(sirf_pwm_driver);
> +
> +MODULE_DESCRIPTION("SIRF serial SoC PWM device core driver");
> +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying at csr.com>, "
> + "Huayi Li <huayi.li at csr.com>");
These could simply be two separate MODULE_AUTHOR() entries.
> +MODULE_LICENSE("GPL v2");
The file header says GPL v2 or later, which would be "GPL". "GPL v2" is
GPL v2 only.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140226/495cef86/attachment-0001.sig>
More information about the linux-arm-kernel
mailing list