2014-02-04 19:54:01

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 0/5] Add Broadcom Kona PWM Support

This series introduces the driver for the Kona PWM controller found in
Broadcom mobile SoCs like bcm281xx and updates the device tree and the
defconfig to enable use of this hardware on the bcm28155 AP board.

Changes since v1:
- Fixed up macros to be clearer and more complete
- Corrected spelling and punctuation mistakes
- Added support for polarity
- Made peripheral clock use more efficient
- Made prescale and duty computation clearer
- Moved Makefile addition to keep alphabetical
- Split complex lines into multiple steps

Tim Kryger (5):
Documentation: dt: Add Kona PWM binding
pwm: kona: Introduce Kona PWM controller support
ARM: dts: Declare the PWM for bcm11351 (bcm281xx)
ARM: dts: Enable the PWM for bcm28155 AP board
ARM: bcm_defconfig: Enable PWM and Backlight

.../devicetree/bindings/pwm/bcm-kona-pwm.txt | 24 ++
arch/arm/boot/dts/bcm11351.dtsi | 8 +
arch/arm/boot/dts/bcm28155-ap.dts | 4 +
arch/arm/configs/bcm_defconfig | 2 +
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-bcm-kona.c | 291 +++++++++++++++++++++
7 files changed, 340 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt
create mode 100644 drivers/pwm/pwm-bcm-kona.c

--
1.8.0.1


2014-02-04 19:52:56

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 3/5] ARM: dts: Declare the PWM for bcm11351 (bcm281xx)

Add the device tree node for the PWM on bcm11351 SoCs.

Signed-off-by: Tim Kryger <[email protected]>
Reviewed-by: Alex Elder <[email protected]>
Reviewed-by: Markus Mayer <[email protected]>
---
arch/arm/boot/dts/bcm11351.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
index e491b82..5296c3d 100644
--- a/arch/arm/boot/dts/bcm11351.dtsi
+++ b/arch/arm/boot/dts/bcm11351.dtsi
@@ -191,6 +191,14 @@
status = "disabled";
};

+ pwm: pwm@3e01a000 {
+ compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm";
+ reg = <0x3e01a000 0xcc>;
+ clocks = <&pwm_clk>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
clocks {
bsc1_clk: bsc1 {
compatible = "fixed-clock";
--
1.8.0.1

2014-02-04 19:53:06

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 1/5] Documentation: dt: Add Kona PWM binding

Add the binding description for the Kona PWM controller found on Broadcom's
mobile SoCs.

Signed-off-by: Tim Kryger <[email protected]>
Reviewed-by: Alex Elder <[email protected]>
Reviewed-by: Markus Mayer <[email protected]>
---
.../devicetree/bindings/pwm/bcm-kona-pwm.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt b/Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt
new file mode 100644
index 0000000..c8e2d13
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt
@@ -0,0 +1,24 @@
+Broadcom Kona PWM controller device tree bindings
+
+This controller has 6 channels.
+
+Required Properties :
+- compatible: should be "brcm,kona-pwm"
+- reg: physical base address and length of the controller's registers
+- clocks: phandle + clock specifier pair for the external clock
+- #pwm-cells: should be 3. The first cell specifies the per-chip index
+ of the PWM to use, the second cell is the period in nanoseconds, and
+ the third cell is the flags.
+
+Refer to pwm/pwm.txt for generic pwm controller node properties.
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+
+pwm: pwm@3e01a000 {
+ compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm";
+ reg = <0x3e01a000 0xc4>;
+ clocks = <&pwm_clk>;
+ #pwm-cells = <3>;
+};
--
1.8.0.1

2014-02-04 19:53:03

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 4/5] ARM: dts: Enable the PWM for bcm28155 AP board

Mark the PWM as enabled on the bcm28155 AP board.

Signed-off-by: Tim Kryger <[email protected]>
Reviewed-by: Alex Elder <[email protected]>
Reviewed-by: Markus Mayer <[email protected]>
---
arch/arm/boot/dts/bcm28155-ap.dts | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/bcm28155-ap.dts b/arch/arm/boot/dts/bcm28155-ap.dts
index 5ff2382..37c72eb 100644
--- a/arch/arm/boot/dts/bcm28155-ap.dts
+++ b/arch/arm/boot/dts/bcm28155-ap.dts
@@ -66,6 +66,10 @@
status = "okay";
};

+ pwm: pwm@3e01a000 {
+ status = "okay";
+ };
+
usbotg: usb@3f120000 {
status = "okay";
};
--
1.8.0.1

2014-02-04 19:52:52

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 2/5] pwm: kona: Introduce Kona PWM controller support

Add support for the six-channel Kona PWM controller found on Broadcom
mobile SoCs like bcm281xx.

Signed-off-by: Tim Kryger <[email protected]>
Reviewed-by: Alex Elder <[email protected]>
Reviewed-by: Markus Mayer <[email protected]>
---
drivers/pwm/Kconfig | 10 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-bcm-kona.c | 291 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 302 insertions(+)
create mode 100644 drivers/pwm/pwm-bcm-kona.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f28..1fd42af 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -62,6 +62,16 @@ config PWM_ATMEL_TCB
To compile this driver as a module, choose M here: the module
will be called pwm-atmel-tcb.

+config PWM_BCM_KONA
+ tristate "Kona PWM support"
+ depends on ARCH_BCM_MOBILE
+ default y
+ help
+ Generic PWM framework driver for Broadcom Kona PWM block.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-bcm-kona.
+
config PWM_BFIN
tristate "Blackfin PWM support"
depends on BFIN_GPTIMERS
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index d8906ec..7413090 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
+obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
new file mode 100644
index 0000000..631ec14
--- /dev/null
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define PWM_CONTROL_OFFSET (0x00000000)
+#define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan))
+#define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan))
+#define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan))
+#define PWM_CONTROL_ENABLE_SHIFT(chan) (chan)
+
+#define PRESCALE_OFFSET (0x00000004)
+#define PRESCALE_SHIFT(chan) ((chan) << 2)
+#define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan))
+#define PRESCALE_MIN (0x00000000)
+#define PRESCALE_MAX (0x00000007)
+
+#define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3))
+#define PERIOD_COUNT_MIN (0x00000002)
+#define PERIOD_COUNT_MAX (0x00ffffff)
+
+#define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3))
+#define DUTY_CYCLE_HIGH_MIN (0x00000000)
+#define DUTY_CYCLE_HIGH_MAX (0x00ffffff)
+
+struct kona_pwmc {
+ struct pwm_chip chip;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
+{
+ /* New settings take effect on rising edge of enable bit */
+ unsigned long value = readl(kp->base + PWM_CONTROL_OFFSET);
+
+ value &= ~(1 << PWM_CONTROL_ENABLE_SHIFT(chan));
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ value |= (1 << PWM_CONTROL_ENABLE_SHIFT(chan));
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+}
+
+static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct kona_pwmc *kp = dev_get_drvdata(chip->dev);
+ u64 val, div, clk_rate;
+ unsigned long prescale = PRESCALE_MIN, pc, dc;
+ unsigned int value, chan = pwm->hwpwm;
+
+ /*
+ * Find period count, duty count and prescale to suit duty_ns and
+ * period_ns. This is done according to formulas described below:
+ *
+ * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ *
+ * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+ * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+ */
+
+ clk_rate = clk_get_rate(kp->clk);
+
+ /* There is polarity support in HW but it is easier to manage in SW */
+ if (pwm->polarity == PWM_POLARITY_INVERSED)
+ duty_ns = period_ns - duty_ns;
+
+ while (1) {
+ div = 1000000000;
+ div *= 1 + prescale;
+ val = clk_rate * period_ns;
+ pc = div64_u64(val, div);
+ val = clk_rate * duty_ns;
+ dc = div64_u64(val, div);
+
+ /* If duty_ns or period_ns are not achievable then return */
+ if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN)
+ return -EINVAL;
+
+ /* If pc and dc are in bounds, the calculation is done */
+ if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
+ break;
+
+ /* Otherwise, increase prescale and recalculate pc and dc */
+ if (++prescale > PRESCALE_MAX)
+ return -EINVAL;
+ }
+
+ /* If the PWM channel is enabled, write the settings to the HW */
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ value = readl(kp->base + PRESCALE_OFFSET);
+ value &= ~PRESCALE_MASK(chan);
+ value |= prescale << PRESCALE_SHIFT(chan);
+ writel(value, kp->base + PRESCALE_OFFSET);
+
+ writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
+
+ writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+
+ kona_pwmc_apply_settings(kp, chan);
+ }
+
+ return 0;
+}
+
+static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ /*
+ * The framework only allows the polarity to be changed when a PWM is
+ * disabled so no immediate action is required here. When a channel is
+ * enabled, the polarity gets handled as part of the re-config step.
+ */
+
+ return 0;
+}
+
+static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct kona_pwmc *kp = dev_get_drvdata(chip->dev);
+ int ret;
+
+ /*
+ * The PWM framework does not clear the enable bit in the flags if an
+ * error is returned from a PWM driver's enable function so it must be
+ * cleared here if any trouble is encountered.
+ */
+
+ ret = clk_prepare_enable(kp->clk);
+ if (ret < 0) {
+ dev_err(chip->dev, "failed to enable clock: %d\n", ret);
+ clear_bit(PWMF_ENABLED, &pwm->flags);
+ return ret;
+ }
+
+ ret = kona_pwmc_config(chip, pwm, pwm->duty_cycle, pwm->period);
+ if (ret < 0) {
+ clk_disable_unprepare(kp->clk);
+ clear_bit(PWMF_ENABLED, &pwm->flags);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct kona_pwmc *kp = dev_get_drvdata(chip->dev);
+ unsigned int chan = pwm->hwpwm;
+
+ /*
+ * The "enable" bits in the control register only affect when settings
+ * start to take effect so the only real way to disable the PWM output
+ * is to program a zero duty cycle.
+ */
+
+ writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+ kona_pwmc_apply_settings(kp, chan);
+
+ /*
+ * When the PWM clock is disabled, the output is pegged high or low
+ * depending on its state at that instant. To guarantee that the new
+ * settings have taken effect and the output is low a delay of 400ns is
+ * required.
+ */
+
+ ndelay(400);
+
+ clk_disable_unprepare(kp->clk);
+}
+
+static const struct pwm_ops kona_pwm_ops = {
+ .config = kona_pwmc_config,
+ .set_polarity = kona_pwmc_set_polarity,
+ .enable = kona_pwmc_enable,
+ .disable = kona_pwmc_disable,
+ .owner = THIS_MODULE,
+};
+
+static int kona_pwmc_probe(struct platform_device *pdev)
+{
+ struct kona_pwmc *kp;
+ struct resource *res;
+ unsigned int chan, value;
+ int ret = 0;
+
+ kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+ if (kp == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, kp);
+
+ kp->chip.dev = &pdev->dev;
+ kp->chip.ops = &kona_pwm_ops;
+ kp->chip.base = -1;
+ kp->chip.npwm = 6;
+ kp->chip.of_xlate = of_pwm_xlate_with_flags;
+ kp->chip.of_pwm_n_cells = 3;
+ kp->chip.can_sleep = true;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ kp->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(kp->base))
+ return PTR_ERR(kp->base);
+
+ kp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(kp->clk)) {
+ dev_err(&pdev->dev, "failed to get clock: %ld\n",
+ PTR_ERR(kp->clk));
+ return PTR_ERR(kp->clk);
+ }
+
+ ret = clk_prepare_enable(kp->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Set smooth mode, push/pull, and normal polarity for all channels */
+ for (value = 0, chan = 0; chan < kp->chip.npwm; chan++) {
+ value |= (1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
+ value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan));
+ value |= (1 << PWM_CONTROL_POLARITY_SHIFT(chan));
+ }
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ clk_disable_unprepare(kp->clk);
+
+ ret = pwmchip_add(&kp->chip);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+
+ return ret;
+}
+
+static int kona_pwmc_remove(struct platform_device *pdev)
+{
+ struct kona_pwmc *kp = platform_get_drvdata(pdev);
+ unsigned int chan;
+
+ for (chan = 0; chan < kp->chip.npwm; chan++)
+ if (test_bit(PWMF_ENABLED, &kp->chip.pwms[chan].flags))
+ clk_disable_unprepare(kp->clk);
+
+ return pwmchip_remove(&kp->chip);
+}
+
+static const struct of_device_id bcm_kona_pwmc_dt[] = {
+ { .compatible = "brcm,kona-pwm" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt);
+
+static struct platform_driver kona_pwmc_driver = {
+
+ .driver = {
+ .name = "bcm-kona-pwm",
+ .of_match_table = bcm_kona_pwmc_dt,
+ },
+ .probe = kona_pwmc_probe,
+ .remove = kona_pwmc_remove,
+};
+
+module_platform_driver(kona_pwmc_driver);
+
+MODULE_AUTHOR("Broadcom Corporation <[email protected]>");
+MODULE_AUTHOR("Tim Kryger <[email protected]>");
+MODULE_DESCRIPTION("Driver for Kona PWM controller");
+MODULE_LICENSE("GPL v2");
--
1.8.0.1

2014-02-04 19:54:05

by Tim Kryger

[permalink] [raw]
Subject: [PATCH v2 5/5] ARM: bcm_defconfig: Enable PWM and Backlight

Enable PWM drivers and the PWM-based backlight driver.

Signed-off-by: Tim Kryger <[email protected]>
Reviewed-by: Alex Elder <[email protected]>
Reviewed-by: Markus Mayer <[email protected]>
---
arch/arm/configs/bcm_defconfig | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index 2519d6d..b1898c3 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -84,6 +84,7 @@ CONFIG_FB=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_PWM=y
# CONFIG_USB_SUPPORT is not set
CONFIG_MMC=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -97,6 +98,7 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_PWM=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
--
1.8.0.1