[{"id":1770655,"web_url":"http://patchwork.ozlabs.org/comment/1770655/","msgid":"<CA+E=qVewq5NWa5GHDkJ7pVOzJxwPAV6Q7+2retZVBONYVfCKXQ@mail.gmail.com>","list_archive_url":null,"date":"2017-09-19T05:00:15","subject":"Re: [U-Boot] [PATCH 1/3] pwm: sunxi: add support for PWM found on\n\tAllwinner A64 and H3","submitter":{"id":6930,"url":"http://patchwork.ozlabs.org/api/people/6930/","name":"Vasily Khoruzhick","email":"anarsoul@gmail.com"},"content":"Please discard this, I forgot to include maintainers in CC. Will resend.\n\nOn Sun, Sep 17, 2017 at 8:28 PM, Vasily Khoruzhick <anarsoul@gmail.com> wrote:\n> This commit adds basic support for PWM found on Allwinner A64 and H3\n>\n> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>\n> ---\n>  arch/arm/include/asm/arch-sunxi/gpio.h |   1 +\n>  arch/arm/include/asm/arch-sunxi/pwm.h  |  12 +++\n>  arch/arm/mach-sunxi/board.c            |  11 +++\n>  drivers/pwm/Kconfig                    |   7 ++\n>  drivers/pwm/Makefile                   |   1 +\n>  drivers/pwm/sunxi_pwm.c                | 174 +++++++++++++++++++++++++++++++++\n>  6 files changed, 206 insertions(+)\n>  create mode 100644 drivers/pwm/sunxi_pwm.c\n>\n> diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h\n> index 24f85206c8..7265d18099 100644\n> --- a/arch/arm/include/asm/arch-sunxi/gpio.h\n> +++ b/arch/arm/include/asm/arch-sunxi/gpio.h\n> @@ -173,6 +173,7 @@ enum sunxi_gpio_number {\n>  #define SUN8I_GPD_SDC1         3\n>  #define SUNXI_GPD_LCD0         2\n>  #define SUNXI_GPD_LVDS0                3\n> +#define SUNXI_GPD_PWM          2\n>\n>  #define SUN5I_GPE_SDC2         3\n>  #define SUN8I_GPE_TWI2         3\n> diff --git a/arch/arm/include/asm/arch-sunxi/pwm.h b/arch/arm/include/asm/arch-sunxi/pwm.h\n> index 5884b5dbe7..673e0eb7b5 100644\n> --- a/arch/arm/include/asm/arch-sunxi/pwm.h\n> +++ b/arch/arm/include/asm/arch-sunxi/pwm.h\n> @@ -11,8 +11,15 @@\n>  #define SUNXI_PWM_CH0_PERIOD           (SUNXI_PWM_BASE + 4)\n>\n>  #define SUNXI_PWM_CTRL_PRESCALE0(x)    ((x) & 0xf)\n> +#define SUNXI_PWM_CTRL_PRESCALE0_MASK  (0xf)\n>  #define SUNXI_PWM_CTRL_ENABLE0         (0x5 << 4)\n>  #define SUNXI_PWM_CTRL_POLARITY0(x)    ((x) << 5)\n> +#define SUNXI_PWM_CTRL_POLARITY0_MASK  (1 << 5)\n> +#define SUNXI_PWM_CTRL_CLK_GATE                (1 << 6)\n> +\n> +#define SUNXI_PWM_CH0_PERIOD_MAX       (0xffff)\n> +#define SUNXI_PWM_CH0_PERIOD_PRD(x)    ((x & 0xffff) << 16)\n> +#define SUNXI_PWM_CH0_PERIOD_DUTY(x)   ((x) & 0xffff)\n>\n>  #define SUNXI_PWM_PERIOD_80PCT         0x04af03c0\n>\n> @@ -31,4 +38,9 @@\n>  #define SUNXI_PWM_MUX                  SUN8I_GPH_PWM\n>  #endif\n>\n> +struct sunxi_pwm {\n> +       u32 ctrl;\n> +       u32 ch0_period;\n> +};\n> +\n>  #endif\n> diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c\n> index 65b1ebd837..a85f973a46 100644\n> --- a/arch/arm/mach-sunxi/board.c\n> +++ b/arch/arm/mach-sunxi/board.c\n> @@ -141,6 +141,16 @@ static int gpio_init(void)\n>         return 0;\n>  }\n>\n> +static int pwm_init(void)\n> +{\n> +#ifdef CONFIG_PWM_SUNXI\n> +#ifdef CONFIG_MACH_SUN50I\n> +       sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM);\n> +#endif\n> +#endif\n> +       return 0;\n> +}\n> +\n>  #if defined(CONFIG_SPL_BOARD_LOAD_IMAGE) && defined(CONFIG_SPL_BUILD)\n>  static int spl_board_load_image(struct spl_image_info *spl_image,\n>                                 struct spl_boot_device *bootdev)\n> @@ -204,6 +214,7 @@ void s_init(void)\n>         clock_init();\n>         timer_init();\n>         gpio_init();\n> +       pwm_init();\n>  #ifndef CONFIG_DM_I2C\n>         i2c_init_board();\n>  #endif\n> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig\n> index e827558052..67e3f355e7 100644\n> --- a/drivers/pwm/Kconfig\n> +++ b/drivers/pwm/Kconfig\n> @@ -43,3 +43,10 @@ config PWM_TEGRA\n>           four channels with a programmable period and duty cycle. Only a\n>           32KHz clock is supported by the driver but the duty cycle is\n>           configurable.\n> +\n> +config PWM_SUNXI\n> +       bool \"Enable support for the Allwinner Sunxi PWM\"\n> +       depends on DM_PWM\n> +       help\n> +         This PWM is found on A64 and other Allwinner SoCs. It supports a\n> +         programmable period and duty cycle. A 32-bit counter is used.\n> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile\n> index 29d59916cb..1a8f8a58bc 100644\n> --- a/drivers/pwm/Makefile\n> +++ b/drivers/pwm/Makefile\n> @@ -17,3 +17,4 @@ obj-$(CONFIG_PWM_IMX)         += pwm-imx.o pwm-imx-util.o\n>  obj-$(CONFIG_PWM_ROCKCHIP)     += rk_pwm.o\n>  obj-$(CONFIG_PWM_SANDBOX)      += sandbox_pwm.o\n>  obj-$(CONFIG_PWM_TEGRA)                += tegra_pwm.o\n> +obj-$(CONFIG_PWM_SUNXI)                += sunxi_pwm.o\n> diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c\n> new file mode 100644\n> index 0000000000..3e6d69fa1c\n> --- /dev/null\n> +++ b/drivers/pwm/sunxi_pwm.c\n> @@ -0,0 +1,174 @@\n> +/*\n> + * Copyright (c) 2017 Vasily Khoruzhick <anarsoul@gmail.com>\n> + *\n> + * SPDX-License-Identifier:    GPL-2.0+\n> + */\n> +\n> +#include <common.h>\n> +#include <div64.h>\n> +#include <dm.h>\n> +#include <pwm.h>\n> +#include <regmap.h>\n> +#include <syscon.h>\n> +#include <asm/io.h>\n> +#include <asm/arch/pwm.h>\n> +#include <power/regulator.h>\n> +\n> +DECLARE_GLOBAL_DATA_PTR;\n> +\n> +struct sunxi_pwm_priv {\n> +       struct sunxi_pwm *regs;\n> +       ulong freq;\n> +       bool invert;\n> +       uint32_t prescaler;\n> +};\n> +\n> +static const uint32_t prescaler_table[] = {\n> +       120,    /* 0000 */\n> +       180,    /* 0001 */\n> +       240,    /* 0010 */\n> +       360,    /* 0011 */\n> +       480,    /* 0100 */\n> +       0,      /* 0101 */\n> +       0,      /* 0110 */\n> +       0,      /* 0111 */\n> +       12000,  /* 1000 */\n> +       24000,  /* 1001 */\n> +       36000,  /* 1010 */\n> +       48000,  /* 1011 */\n> +       72000,  /* 1100 */\n> +       0,      /* 1101 */\n> +       0,      /* 1110 */\n> +       1,      /* 1111 */\n> +};\n> +\n> +static const uint64_t nsecs_per_sec = 1000000000L;\n> +\n> +static int sunxi_pwm_set_invert(struct udevice *dev, uint channel, bool polarity)\n> +{\n> +       struct sunxi_pwm_priv *priv = dev_get_priv(dev);\n> +\n> +       debug(\"%s: polarity=%u\\n\", __func__, polarity);\n> +       priv->invert = polarity;\n> +\n> +       return 0;\n> +}\n> +\n> +static int sunxi_pwm_set_config(struct udevice *dev, uint channel, uint period_ns,\n> +                            uint duty_ns)\n> +{\n> +       struct sunxi_pwm_priv *priv = dev_get_priv(dev);\n> +       struct sunxi_pwm *regs = priv->regs;\n> +       int prescaler;\n> +       u32 v, period, duty;\n> +       uint64_t div = 0, pval = 0, scaled_freq = 0;\n> +\n> +       debug(\"%s: period_ns=%u, duty_ns=%u\\n\", __func__, period_ns, duty_ns);\n> +\n> +       for (prescaler = 0; prescaler < SUNXI_PWM_CTRL_PRESCALE0_MASK; prescaler++) {\n> +               if (!prescaler_table[prescaler])\n> +                       continue;\n> +               div = priv->freq;\n> +               pval = prescaler_table[prescaler];\n> +               scaled_freq = lldiv(div, pval);\n> +               div = scaled_freq * period_ns;\n> +               div = lldiv(div, nsecs_per_sec);\n> +               if (div - 1 <= SUNXI_PWM_CH0_PERIOD_MAX)\n> +                       break;\n> +       }\n> +\n> +       if (div - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {\n> +               debug(\"%s: failed to find prescaler value\\n\", __func__);\n> +               return -EINVAL;\n> +       }\n> +\n> +       period = div;\n> +       div = scaled_freq * duty_ns;\n> +       div = lldiv(div, nsecs_per_sec);\n> +       duty = div;\n> +\n> +       if (priv->prescaler != prescaler) {\n> +               /* Mask clock to update prescaler */\n> +               v = readl(&regs->ctrl);\n> +               v &= ~SUNXI_PWM_CTRL_CLK_GATE;\n> +               writel(v, &regs->ctrl);\n> +               v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK;\n> +               v |= (priv->prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);\n> +               writel(v, &regs->ctrl);\n> +               v |= SUNXI_PWM_CTRL_CLK_GATE;\n> +               writel(v, &regs->ctrl);\n> +               priv->prescaler = prescaler;\n> +       }\n> +\n> +       writel(SUNXI_PWM_CH0_PERIOD_PRD(period) |\n> +              SUNXI_PWM_CH0_PERIOD_DUTY(duty), &regs->ch0_period);\n> +\n> +       debug(\"%s: prescaler: %d, period: %d, duty: %d\\n\", __func__, priv->prescaler,\n> +             period, duty);\n> +\n> +       return 0;\n> +}\n> +\n> +static int sunxi_pwm_set_enable(struct udevice *dev, uint channel, bool enable)\n> +{\n> +       struct sunxi_pwm_priv *priv = dev_get_priv(dev);\n> +       struct sunxi_pwm *regs = priv->regs;\n> +       uint32_t v;\n> +\n> +       debug(\"%s: Enable '%s'\\n\", __func__, dev->name);\n> +\n> +       v = readl(&regs->ctrl);\n> +       if (!enable) {\n> +               v &= ~SUNXI_PWM_CTRL_ENABLE0;\n> +               writel(v, &regs->ctrl);\n> +               return 0;\n> +       }\n> +\n> +       v &= ~SUNXI_PWM_CTRL_POLARITY0_MASK;\n> +       v |= priv->invert ? SUNXI_PWM_CTRL_POLARITY0(0) :\n> +                     SUNXI_PWM_CTRL_POLARITY0(1);\n> +       v |= SUNXI_PWM_CTRL_ENABLE0;\n> +       writel(v, &regs->ctrl);\n> +\n> +       return 0;\n> +}\n> +\n> +static int sunxi_pwm_ofdata_to_platdata(struct udevice *dev)\n> +{\n> +       struct sunxi_pwm_priv *priv = dev_get_priv(dev);\n> +\n> +       priv->regs = (struct sunxi_pwm *)devfdt_get_addr(dev);\n> +\n> +       return 0;\n> +}\n> +\n> +static int sunxi_pwm_probe(struct udevice *dev)\n> +{\n> +       struct sunxi_pwm_priv *priv = dev_get_priv(dev);\n> +\n> +       priv->freq = 24000000;\n> +\n> +       return 0;\n> +}\n> +\n> +static const struct pwm_ops sunxi_pwm_ops = {\n> +       .set_invert     = sunxi_pwm_set_invert,\n> +       .set_config     = sunxi_pwm_set_config,\n> +       .set_enable     = sunxi_pwm_set_enable,\n> +};\n> +\n> +static const struct udevice_id sunxi_pwm_ids[] = {\n> +       { .compatible = \"allwinner,sun8i-h3-pwm\" },\n> +       { .compatible = \"allwinner,sun50i-a64-pwm\" },\n> +       { }\n> +};\n> +\n> +U_BOOT_DRIVER(sunxi_pwm) = {\n> +       .name   = \"sunxi_pwm\",\n> +       .id     = UCLASS_PWM,\n> +       .of_match = sunxi_pwm_ids,\n> +       .ops    = &sunxi_pwm_ops,\n> +       .ofdata_to_platdata     = sunxi_pwm_ofdata_to_platdata,\n> +       .probe          = sunxi_pwm_probe,\n> +       .priv_auto_alloc_size   = sizeof(struct sunxi_pwm_priv),\n> +};\n> --\n> 2.14.1\n>","headers":{"Return-Path":"<u-boot-bounces@lists.denx.de>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=lists.denx.de\n\t(client-ip=81.169.180.215; helo=lists.denx.de;\n\tenvelope-from=u-boot-bounces@lists.denx.de;\n\treceiver=<UNKNOWN>)","ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"miwYsLfZ\"; dkim-atps=neutral"],"Received":["from lists.denx.de (dione.denx.de [81.169.180.215])\n\tby ozlabs.org (Postfix) with ESMTP id 3xx9k13cYlz9s5L\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 19 Sep 2017 15:03:05 +1000 (AEST)","by lists.denx.de (Postfix, from userid 105)\n\tid 3EDF5C21E98; Tue, 19 Sep 2017 05:02:39 +0000 (UTC)","from lists.denx.de (localhost [IPv6:::1])\n\tby lists.denx.de (Postfix) with ESMTP id 890DEC21E61;\n\tTue, 19 Sep 2017 05:02:13 +0000 (UTC)","by lists.denx.de (Postfix, from userid 105)\n\tid 52F5CC21E3D; Tue, 19 Sep 2017 05:02:12 +0000 (UTC)","from mail-io0-f193.google.com (mail-io0-f193.google.com\n\t[209.85.223.193])\n\tby lists.denx.de (Postfix) with ESMTPS id 891CEC21DB1\n\tfor <u-boot@lists.denx.de>; Tue, 19 Sep 2017 05:00:37 +0000 (UTC)","by mail-io0-f193.google.com with SMTP id d16so3444813ioj.2\n\tfor <u-boot@lists.denx.de>; Mon, 18 Sep 2017 22:00:37 -0700 (PDT)","by 10.157.9.69 with HTTP; Mon, 18 Sep 2017 22:00:15 -0700 (PDT)"],"X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de","X-Spam-Level":"","X-Spam-Status":"No, score=0.0 required=5.0 tests=FREEMAIL_FROM,\n\tRCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,\n\tT_DKIM_INVALID autolearn=unavailable\n\tautolearn_force=no version=3.4.0","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:in-reply-to:references:from:date:message-id:subject:to\n\t:cc; bh=LSvie/V/bhhI3iv1O6yMXy6D3Hp8sLLL/7nXSZ1w1Nk=;\n\tb=miwYsLfZxsUgSNYQp/kGDpwopTNIN4FBABbt+vCD8a844ZaFVOPTaLWWy+OThfkTmV\n\tV4nYuO3jIRpIK2qb3yVoBDxd2icTmpD4r1auu+Q0YLmc5fbSLdIh/huQtoVrr92HVyQ9\n\tUZfZ15cko352xbOt9z5JDHyfhP5vtjTx5tcV2bsxlG8wbrMaHQWpDfmCaJ0R4kn1U7Cl\n\tkfkYpATM1IqRt0XDyD4Onrv84T2m4bSSBjfmDbjmqMn7cBF9/5jMisphM9glsKoc35B9\n\t00GqcJ/EmrphAn7GD+Wf1PZu8bS+rvO2t9pE3JTlv3aAwKL61iERvGBZ5K5c88BP+lUW\n\tZF/A==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:in-reply-to:references:from:date\n\t:message-id:subject:to:cc;\n\tbh=LSvie/V/bhhI3iv1O6yMXy6D3Hp8sLLL/7nXSZ1w1Nk=;\n\tb=OSdwDbAJqiySqrP+v+WSUS4Ok1ByDR8KRAbVBgB9bIMwQ0cauhhMEVopjGym8ZGSgS\n\t205Xkfbs7T87/O2bd+mGwPtAO++uysdm27ozU9egVHuWJ7PyUFtzJwE9+YjGdFgc/Rz4\n\tF7Eqae5BL5XyHs1bX4F/c4TWIL2TouIL2DqF3vMnwpi4u3Tc3ZVA8chlms8FrDBE2uNW\n\t9La9469xKCOCnq8nyx9Zdzi83ChsqJOnp6bKSAiagKQvwf0e9INt8XJzZNSFb0zZqWFS\n\tM355oPLxhLAOROsjycx968tFRg3D3rBapFpKIr60KwBwCUttx9r0kC26bzCm8v312D03\n\to53w==","X-Gm-Message-State":"AHPjjUg/w44Ba2UDLglGwLXeIt+T4ZAqoUDlNn2xI1Rccy9/vxElM5BF\n\tcy8XoIkT+U50cylEe/tS8JMJZWTw/Rb3XYxhaBPTGKhg","X-Google-Smtp-Source":"AOwi7QAy1qRScZ56djg/HdgUU8Pkjs9N3OD8bEZQgJWliu070obmRx7fvltvNzlWurSo4dWignmCo7RZvxlKeAa0UpI=","X-Received":"by 10.202.75.149 with SMTP id y143mr187091oia.34.1505797236213; \n\tMon, 18 Sep 2017 22:00:36 -0700 (PDT)","MIME-Version":"1.0","In-Reply-To":"<20170918032815.25626-1-anarsoul@gmail.com>","References":"<20170918032815.25626-1-anarsoul@gmail.com>","From":"Vasily Khoruzhick <anarsoul@gmail.com>","Date":"Mon, 18 Sep 2017 22:00:15 -0700","Message-ID":"<CA+E=qVewq5NWa5GHDkJ7pVOzJxwPAV6Q7+2retZVBONYVfCKXQ@mail.gmail.com>","To":"u-boot@lists.denx.de, icenowy@aosc.io","Subject":"Re: [U-Boot] [PATCH 1/3] pwm: sunxi: add support for PWM found on\n\tAllwinner A64 and H3","X-BeenThere":"u-boot@lists.denx.de","X-Mailman-Version":"2.1.18","Precedence":"list","List-Id":"U-Boot discussion <u-boot.lists.denx.de>","List-Unsubscribe":"<https://lists.denx.de/options/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=unsubscribe>","List-Archive":"<http://lists.denx.de/pipermail/u-boot/>","List-Post":"<mailto:u-boot@lists.denx.de>","List-Help":"<mailto:u-boot-request@lists.denx.de?subject=help>","List-Subscribe":"<https://lists.denx.de/listinfo/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=subscribe>","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"u-boot-bounces@lists.denx.de","Sender":"\"U-Boot\" <u-boot-bounces@lists.denx.de>"}}]