You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/06/23 13:17:46 UTC
[incubator-nuttx] 02/02: Added PWM support to rp2040
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 412539e66c8b798a86847419f4776dd9b8d302ff
Author: curuvar <58...@users.noreply.github.com>
AuthorDate: Wed Jun 22 20:36:10 2022 -0400
Added PWM support to rp2040
---
arch/arm/src/rp2040/hardware/rp2040_pwm.h | 143 ++++++
arch/arm/src/rp2040/rp2040_pwm.c | 596 +++++++++++++++++++++++
arch/arm/src/rp2040/rp2040_pwm.h | 120 +++++
boards/arm/rp2040/common/include/rp2040_pwmdev.h | 78 +++
boards/arm/rp2040/common/src/rp2040_pwmdev.c | 95 ++++
5 files changed, 1032 insertions(+)
diff --git a/arch/arm/src/rp2040/hardware/rp2040_pwm.h b/arch/arm/src/rp2040/hardware/rp2040_pwm.h
new file mode 100644
index 0000000000..a5553871d5
--- /dev/null
+++ b/arch/arm/src/rp2040/hardware/rp2040_pwm.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/hardware/rp2040_pwm.h
+ *
+ * Generated from rp2040.svd originally provided by
+ * Raspberry Pi (Trading) Ltd.
+ *
+ * Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_PWM_H
+#define __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_PWM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/rp2040_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define RP2040_PWM_CSR_OFFSET(n) (0x000000 + (n) * 20) /* PWM control and status register */
+#define RP2040_PWM_DIV_OFFSET(n) (0x000004 + (n) * 20) /* PWM clock divisor register */
+#define RP2040_PWM_CTR_OFFSET(n) (0x000008 + (n) * 20) /* PWM counter register */
+#define RP2040_PWM_CC_OFFSET(n) (0x00000C + (n) * 20) /* PWM compare register */
+#define RP2040_PWM_TOP_OFFSET(n) (0x000010 + (n) * 20) /* PWM wrap value register */
+#define RP2040_PWM_ENA_OFFSET 0x0000A0 /* PWM enable register */
+#define RP2040_PWM_INTR_OFFSET 0x0000A4 /* PWM raw interrupt register */
+#define RP2040_PWM_INTE_OFFSET 0x0000A8 /* PWM interrupt enable register */
+#define RP2040_PWM_INTF_OFFSET 0x0000AC /* PWM interrupt force register */
+#define RP2040_PWM_INTS_OFFSET 0x0000BO /* PWM interrupt status register */
+
+/* Register definitions *****************************************************/
+
+#define RP2040_PWM_CSR(n) (RP2040_PWM_BASE + RP2040_PWM_CSR_OFFSET(n))
+#define RP2040_PWM_DIV(n) (RP2040_PWM_BASE + RP2040_PWM_DIV_OFFSET(n))
+#define RP2040_PWM_CTR(n) (RP2040_PWM_BASE + RP2040_PWM_CTR_OFFSET(n))
+#define RP2040_PWM_CC(n) (RP2040_PWM_BASE + RP2040_PWM_CC_OFFSET(n))
+#define RP2040_PWM_TOP(n) (RP2040_PWM_BASE + RP2040_PWM_TOP_OFFSET(n))
+#define RP2040_PWM_ENA (RP2040_PWM_BASE + RP2040_PWM_ENA_OFFSET)
+#define RP2040_PWM_INTR (RP2040_PWM_BASE + RP2040_PWM_INTR_OFFSET)
+#define RP2040_PWM_INTE (RP2040_PWM_BASE + RP2040_PWM_INTE_OFFSET)
+#define RP2040_PWM_INTF (RP2040_PWM_BASE + RP2040_PWM_INTF_OFFSET)
+#define RP2040_PWM_INTS (RP2040_PWM_BASE + RP2040_PWM_INTS_OFFSET)
+
+/* Register bit definitions *************************************************/
+
+#define RP2040_PWM_CSR_PH_ADV (1 << 7) /* advance phase of counter by one */
+#define RP2040_PWM_CSR_PH_RET (1 << 5) /* retard phase of counter by one */
+#define RP2040_PWM_CSR_DIVMODE_SHIFT (4) /* divisor mode */
+#define RP2040_PWM_CSR_DIVMODE_MASK (0x03 << RP2040_PWM_CSR_DIVMODE_SHIFT)
+#define RP2040_PWM_CSR_B_INV (1 << 3) /* invert output B */
+#define RP2040_PWM_CSR_A_INV (1 << 2) /* invert output A */
+#define RP2040_PWM_CSR_PH_CORRECT (1 << 1) /* enable phase correct modulation */
+#define RP2040_PWM_CSR_EN (1 << 0) /* enable the PWM channel */
+
+#define RP2040_PWN_CSR_DIVMODE_DIV 0x00
+#define RP2040_PWN_CSR_DIVMODE_LEVEL 0x01
+#define RP2040_PWN_CSR_DIVMODE_RISE 0x02
+#define RP2040_PWN_CSR_DIVMODE_FALL 0x03
+#define RP2040_PWM_DIV_INT_SHIFT (4) /* divisor integer part */
+#define RP2040_PWM_DIV_INT_MASK (0xff << RP2040_PWM_DIV_INT_SHIFT)
+#define RP2040_PWM_DIV_FRAC_SHIFT (0) /* divisor fraction part */
+#define RP2040_PWM_DIV_FRAC_MASK (0x0f << RP2040_PWM_DIV_FRAC_SHIFT)
+
+#define RP2040_PWM_CC_B_SHIFT (16) /* channel B compare register */
+#define RP2040_PWM_CC_B_MASK (0xffff << RP2040_PWM_CC_B_SHIFT)
+#define RP2040_PWM_CC_A_SHIFT (0) /* channel A compare register */
+#define RP2040_PWM_CC_A_MASK (0xffff << RP2040_PWM_CC_A_SHIFT)
+
+#define RP2040_PWM_TOP_SHIFT (0) /* channel A compare register */
+#define RP2040_PWM_TOP_MASK (0xffff << RP2040_PWM_TOP_SHIFT)
+
+/* Bit mask for ENA, INTR, INTE, INTF, and INTS registers */
+
+#define RP2040_PWM_CH7 (1 << 7) /* PWM channel 7 */
+#define RP2040_PWM_CH6 (1 << 6) /* PWM channel 6 */
+#define RP2040_PWM_CH5 (1 << 5) /* PWM channel 5 */
+#define RP2040_PWM_CH4 (1 << 4) /* PWM channel 4 */
+#define RP2040_PWM_CH3 (1 << 3) /* PWM channel 3 */
+#define RP2040_PWM_CH2 (1 << 2) /* PWM channel 2 */
+#define RP2040_PWM_CH1 (1 << 1) /* PWM channel 1 */
+#define RP2040_PWM_CH0 (1 << 0) /* PWM channel 0 */
+
+/****************************************************************************
+ * The following IOCTL values set additional flags in the RP2040 PWM
+ * device.
+ ****************************************************************************/
+
+/****************************************************************************
+ * PWMIOC_RP2040_SETINVERTPULSE sets the pulse invert flag.
+ *
+ * The argument is an integer where:
+ * bit zero is set to invert channel A
+ * bit one is set to invert channel B
+ ****************************************************************************/
+
+#define PWMIOC_RP2040_SETINVERTPULSE _PWMIOC(0x80)
+
+#define PWMIOC_RP2040_GETINVERTPULSE _PWMIOC(0x81)
+
+/****************************************************************************
+ * PWMIOC_RP2040_SETPHASECORRECT sets phase correct flags.
+ *
+ * The argument is an integer which if non-zero sets the phase correct flag.
+ ****************************************************************************/
+
+#define PWMIOC_RP2040_SETPHASECORRECT _PWMIOC(0x82)
+
+#define PWMIOC_RP2040_GETPHASECORRECT _PWMIOC(0x83)
+
+#endif
\ No newline at end of file
diff --git a/arch/arm/src/rp2040/rp2040_pwm.c b/arch/arm/src/rp2040/rp2040_pwm.c
new file mode 100644
index 0000000000..a28dc8ad2e
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_pwm.c
@@ -0,0 +1,596 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_pwm.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/timers/pwm.h>
+#include <arch/board/board.h>
+#include "rp2040_gpio.h"
+#include "rp2040_pwm.h"
+
+/****************************************************************************
+ * Local Function Prototypes
+ ****************************************************************************/
+
+static int pwm_setup (struct pwm_lowerhalf_s * dev);
+
+static int pwm_shutdown (struct pwm_lowerhalf_s * dev);
+
+static int pwm_start (struct pwm_lowerhalf_s * dev,
+ const struct pwm_info_s * info);
+
+static int pwm_stop (struct pwm_lowerhalf_s * dev);
+
+static int pwm_ioctl (struct pwm_lowerhalf_s * dev,
+ int cmd,
+ unsigned long arg);
+
+static void setup_period (struct rp2040_pwm_lowerhalf_s * priv);
+
+static void setup_pulse (struct rp2040_pwm_lowerhalf_s * priv);
+
+static void set_enabled (struct rp2040_pwm_lowerhalf_s * priv);
+
+static void clear_enabled(struct rp2040_pwm_lowerhalf_s * priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* PWM operations */
+
+static const struct pwm_ops_s g_pwmops =
+{
+ .setup = pwm_setup,
+ .shutdown = pwm_shutdown,
+ .start = pwm_start,
+ .stop = pwm_stop,
+ .ioctl = pwm_ioctl
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp2040_pwm_initialize
+ *
+ * Description:
+ * Initialize the selected PWM port. And return a unique instance of struct
+ * struct rp2040_pwm_lowerhalf_s. This function may be called to obtain
+ * multiple instances of the interface, each of which may be set up with a
+ * different frequency and address.
+ *
+ * Input Parameters:
+ * Port number (for hardware that has multiple PWM interfaces)
+ * GPIO pin number for pin A
+ * GPIO pin number for pin B (CONFIG_PWM_NCHANNELS == 2)
+ *
+ * Returned Value:
+ * Valid PWM device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+struct rp2040_pwm_lowerhalf_s *rp2040_pwm_initialize(int port,
+ int pin_a,
+ int pin_b,
+ uint32_t flags)
+#else
+struct rp2040_pwm_lowerhalf_s *rp2040_pwm_initialize(int port,
+ int pin,
+ uint32_t flags)
+#endif
+{
+ struct rp2040_pwm_lowerhalf_s *data;
+
+ data = calloc(1, sizeof (struct rp2040_pwm_lowerhalf_s));
+
+ if (data != NULL)
+ {
+ data->ops = &g_pwmops;
+ data->num = port;
+ data->flags = flags;
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ if (pin_a == 2*port || pin_a == 2*port + 16)
+ {
+ data->pin[0] = pin_a;
+ }
+ else
+ {
+ data->pin[0] = -1;
+ }
+
+ if (pin_b == 2*port + 1 || pin_b == 2*port + 17)
+ {
+ data->pin[1] = pin_b;
+ }
+ else
+ {
+ data->pin[1] = -1;
+ }
+#else
+ if (pin == 2*port || pin == 2*port + 16)
+ {
+ data->pin = pin;
+ }
+ else
+ {
+ data->pin = -1;
+ }
+
+#endif
+ }
+
+ return data;
+}
+
+/****************************************************************************
+ * Name: rp2040_pwm_uninitialize
+ *
+ * Description:
+ * De-initialize the selected pwm port, and power down the device.
+ *
+ * Input Parameter:
+ * Device structure as returned by the rp2040_pwmdev_initialize()
+ *
+ * Returned Value:
+ * OK on success, ERROR when internal reference count mismatch or dev
+ * points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int rp2040_pwm_uninitialize(struct pwm_lowerhalf_s *dev)
+{
+ free(dev);
+ return (OK);
+}
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pwm_setup
+ *
+ * Description:
+ * This method is called when the driver is opened. The lower half driver
+ * should configure and initialize the device so that it is ready for use.
+ * It should not, however, output pulses until the start method is called.
+ *
+ * Input Parameters:
+ * dev - A reference to the lower half PWM driver state structure
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+int pwm_setup(struct pwm_lowerhalf_s * dev)
+{
+ struct rp2040_pwm_lowerhalf_s *priv = (struct rp2040_pwm_lowerhalf_s *)dev;
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ pwminfo("PWM%d pin_a %d pin_b %d\n",
+ priv->num,
+ priv->pin[0],
+ priv->pin[1]);
+
+ if (priv->pin[0] >= 0)
+ {
+ rp2040_gpio_set_function(priv->pin[0], RP2040_GPIO_FUNC_PWM);
+ }
+
+ if (priv->pin[1] >= 0)
+ {
+ rp2040_gpio_set_function(priv->pin[1], RP2040_GPIO_FUNC_PWM);
+ }
+#else
+ if (priv->pin >= 0)
+ {
+ rp2040_gpio_set_function(priv->pin, RP2040_GPIO_FUNC_PWM);
+ }
+#endif
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: pwm_shutdown
+ *
+ * Description:
+ * This method is called when the driver is closed. The lower half driver
+ * stop pulsed output, free any resources, disable the timer hardware, and
+ * put the system into the lowest possible power usage state
+ *
+ * Input Parameters:
+ * dev - A reference to the lower half PWM driver state structure
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+int pwm_shutdown (struct pwm_lowerhalf_s * dev)
+{
+ struct rp2040_pwm_lowerhalf_s *priv = (struct rp2040_pwm_lowerhalf_s *)dev;
+
+ pwminfo("PWM%d\n", priv->num);
+
+ /* Stop timer */
+
+ pwm_stop(dev);
+
+ /* Force the GPIO pins to the appropriate idle state */
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ pwminfo("PWM%d pin_a %d pin_b %d\n",
+ priv->num,
+ priv->pin[0],
+ priv->pin[1]);
+
+ if (priv->pin[0] >= 0)
+ {
+ pwminfo("PWM%d setting pin_a %d\n",
+ priv->num,
+ (priv->flags & RP2040_PWM_CSR_A_INV) ? 1 : 0);
+
+ rp2040_gpio_setdir(priv->pin[0], true);
+ rp2040_gpio_put(priv->pin[0],
+ ((priv->flags & RP2040_PWM_CSR_A_INV) != 0));
+ rp2040_gpio_set_function(priv->pin[0], RP2040_GPIO_FUNC_SIO);
+ }
+
+ if (priv->pin[1] >= 0)
+ {
+ pwminfo("PWM%d setting pin_b %d\n",
+ priv->num,
+ (priv->flags & RP2040_PWM_CSR_B_INV) ? 1 : 0);
+
+ rp2040_gpio_setdir(priv->pin[1], true);
+ rp2040_gpio_put(priv->pin[1],
+ ((priv->flags & RP2040_PWM_CSR_B_INV) != 0));
+ rp2040_gpio_set_function(priv->pin[1], RP2040_GPIO_FUNC_SIO);
+ }
+#else
+ pwminfo("PWM%d pin %d\n", priv->num, priv->pin);
+
+ if (priv->pin >= 0)
+ {
+ rp2040_gpio_setdir(priv->pin[0], true);
+ rp2040_gpio_put(priv->pin[0],
+ ((priv->flags & RP2040_PWM_CSR_A_INV) != 0));
+ rp2040_gpio_set_function(priv->pin, RP2040_GPIO_FUNC_SIO);
+ }
+#endif
+
+ /* Clear timer and channel configuration */
+
+ priv->frequency = 0;
+ priv->divisor = 0x00000010; /* hex 1.0 */
+ priv->top = 0xffff;
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ for (int i = 0; i < CONFIG_PWM_NCHANNELS; ++i)
+ {
+ priv->duty[i] = 0;
+ }
+#else
+ priv->duty = 0;
+#endif
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: pwm_start
+ *
+ * Description:
+ * (Re-)initialize the timer resources and start the pulsed output
+ *
+ * Input Parameters:
+ * dev - A reference to the lower half PWM driver state structure
+ * info - A reference to the characteristics of the pulsed output
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+int pwm_start(struct pwm_lowerhalf_s * dev,
+ const struct pwm_info_s * info)
+{
+ struct rp2040_pwm_lowerhalf_s *priv = (struct rp2040_pwm_lowerhalf_s *)dev;
+
+ pwminfo("PWM%d\n", priv->num);
+
+ /* Update timer with given PWM timer frequency */
+
+ if (priv->frequency != info->frequency)
+ {
+ priv->frequency = info->frequency;
+
+ /* We want to compute the top and divisor to give the finest control */
+
+ setup_period(priv);
+ }
+
+ /* Update timer with given PWM channel duty */
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ for (int i = 0; i < CONFIG_PWM_NCHANNELS; i++)
+ {
+ if (priv->duty[i] != info->channels[i].duty)
+ {
+ priv->duty[i] = info->channels[i].duty;
+ }
+ }
+#else
+ if (priv->duty != info[0].duty)
+ {
+ priv->duty = info[0].duty;
+ }
+#endif
+
+ setup_pulse(priv);
+
+ set_enabled(priv);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: pwm_stop
+ *
+ * Description:
+ * Stop the pulsed output.
+ *
+ * Input Parameters:
+ * dev - A reference to the lower half PWM driver state structure
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+int pwm_stop(struct pwm_lowerhalf_s * dev)
+{
+ struct rp2040_pwm_lowerhalf_s *priv = (struct rp2040_pwm_lowerhalf_s *)dev;
+
+ pwminfo("PWM%d\n", priv->num);
+
+ clear_enabled(priv);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: pwm_ioctl
+ *
+ * Description:
+ * Lower-half logic may support platform-specific ioctl commands
+ *
+ * Input Parameters:
+ * dev - A reference to the lower half PWM driver state structure
+ * cmd - The ioctl command
+ * arg - The argument accompanying the ioctl command
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+int pwm_ioctl(struct pwm_lowerhalf_s * dev,
+ int cmd,
+ unsigned long arg)
+{
+ struct rp2040_pwm_lowerhalf_s *priv = (struct rp2040_pwm_lowerhalf_s *)dev;
+
+#ifdef CONFIG_DEBUG_PWM_INFO
+ pwminfo("PWM%d\n", priv->num);
+#endif
+
+ switch (cmd)
+ {
+ case PWMIOC_RP2040_SETINVERTPULSE:
+ priv->flags &= ~(RP2040_PWM_CSR_B_INV | RP2040_PWM_CSR_A_INV);
+ priv->flags |= (arg & 0x03) << 2;
+
+ setup_period(priv);
+ setup_pulse(priv);
+
+ return 0;
+
+ case PWMIOC_RP2040_GETINVERTPULSE:
+ return (priv->flags & (RP2040_PWM_CSR_B_INV
+ | RP2040_PWM_CSR_A_INV)) >> 2;
+
+ case PWMIOC_RP2040_SETPHASECORRECT:
+ priv->flags &= ~(RP2040_PWM_CSR_PH_CORRECT);
+ priv->flags |= (arg != 0) ? RP2040_PWM_CSR_PH_CORRECT : 0x00;
+
+ setup_period(priv);
+ setup_pulse(priv);
+
+ return 0;
+
+ case PWMIOC_RP2040_GETPHASECORRECT:
+ return (priv->flags & RP2040_PWM_CSR_PH_CORRECT) ? 1 : 0;
+ }
+
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: setup_period
+ *
+ * Description:
+ * compute and set the clock divisor and top value based on frequency.
+ *
+ * Input Parameters:
+ * priv - A reference to the lower half PWM driver state structure
+ *
+ ****************************************************************************/
+
+void setup_period(struct rp2040_pwm_lowerhalf_s * priv)
+{
+ irqstate_t flags;
+ uint32_t max_freq = BOARD_SYS_FREQ / 0x10000; /* initially, with full range count */
+ uint32_t frequency = priv->frequency;
+
+ /* If we are running phase correct we double the frequency value
+ * since the PWM will generate a pulse chain at half what it
+ * would be in normal (non-phase correct) mode
+ */
+
+ if (priv->flags & RP2040_PWM_CSR_PH_CORRECT)
+ {
+ frequency *= 2;
+ }
+
+ pwminfo("PWM%d freq %ld max %ld\n", priv->num, priv->frequency, max_freq);
+
+ if (frequency <= max_freq)
+ {
+ /* We can keep full range count and slow clock down with divisor */
+
+ priv->top = 0xffff;
+ }
+ else
+ {
+ /* we need to speed things up by reducing top */
+
+ priv->top = 0xffff / (frequency / max_freq);
+
+ /* compute new maximum frequency */
+
+ max_freq = BOARD_SYS_FREQ / (priv->top + 1);
+ }
+
+ priv->divisor = 16 * max_freq / frequency;
+
+ pwminfo("PWM%d top 0x%08X div 0x%08lX\n",
+ priv->num,
+ priv->top,
+ priv->divisor);
+
+ flags = enter_critical_section();
+
+ putreg32(priv->top, RP2040_PWM_TOP(priv->num));
+ putreg32(priv->divisor, RP2040_PWM_DIV(priv->num));
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: setup_pulse
+ *
+ * Description:
+ * compute and set the compare values and set CSR flags.
+ *
+ * Input Parameters:
+ * priv - A reference to the lower half PWM driver state structure
+ *
+ ****************************************************************************/
+
+void setup_pulse(struct rp2040_pwm_lowerhalf_s * priv)
+{
+ irqstate_t flags;
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ uint32_t compare =
+ (0xffff * (uint32_t)priv->duty[0] / priv->top)
+ + ((0xffff * (uint32_t)priv->duty[1] / priv->top) << 16);
+#else
+ uint32_t compare = 0xffff * (uint32_t)priv->duty / priv->top;
+#endif
+
+ pwminfo("PWM%d compare 0x%08lX flags 0x%08lX\n",
+ priv->num,
+ compare,
+ priv->flags);
+
+ flags = enter_critical_section();
+
+ putreg32(compare, RP2040_PWM_CC(priv->num));
+
+ modreg32(priv->flags,
+ RP2040_PWM_CSR_DIVMODE_MASK
+ | RP2040_PWM_CSR_B_INV
+ | RP2040_PWM_CSR_A_INV
+ | RP2040_PWM_CSR_PH_CORRECT,
+ RP2040_PWM_CSR(priv->num));
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: set_enabled
+ *
+ * Description:
+ * set the enable bit for a given slice.
+ *
+ * Input Parameters:
+ * priv - A reference to the lower half PWM driver state structure
+ *
+ ****************************************************************************/
+
+static inline void set_enabled(struct rp2040_pwm_lowerhalf_s * priv)
+{
+ irqstate_t flags = enter_critical_section();
+
+ modreg32(1 << priv->num, 1 << priv->num, RP2040_PWM_ENA);
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: clear_enabled
+ *
+ * Description:
+ * clear the enable bit for a given slice.
+ *
+ * Input Parameters:
+ * priv - A reference to the lower half PWM driver state structure
+ *
+ ****************************************************************************/
+
+static inline void clear_enabled(struct rp2040_pwm_lowerhalf_s * priv)
+{
+ irqstate_t flags = enter_critical_section();
+
+ modreg32(0, 1 << priv->num, RP2040_PWM_ENA);
+
+ leave_critical_section(flags);
+}
diff --git a/arch/arm/src/rp2040/rp2040_pwm.h b/arch/arm/src/rp2040/rp2040_pwm.h
new file mode 100644
index 0000000000..18a5a88a5d
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_pwm.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_pwm.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP2040_RP2040_PWM_H
+#define __ARCH_ARM_SRC_RP2040_RP2040_PWM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/rp2040_pwm.h"
+#include "nuttx/timers/pwm.h"
+
+#ifndef __ASSEMBLY__
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/* This structure represents the state of one PWM timer */
+
+struct rp2040_pwm_lowerhalf_s
+{
+ const struct pwm_ops_s * ops; /* PWM operations */
+
+ uint32_t frequency; /* PWM current frequency */
+ uint32_t divisor; /* PWM current clock divisor */
+ uint32_t flags; /* PWM mode flags */
+ uint16_t top; /* PWM current top value */
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ uint16_t duty[2];
+ int8_t pin[2];
+#else
+ uint16_t duty; /* Time duty value */
+ int8_t pin;
+#endif
+
+ uint8_t num; /* Timer ID */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp2040_pwm_initialize
+ *
+ * Description:
+ * Initialize the selected PWM port. And return a unique instance of struct
+ * struct rp2040_pwm_lowerhalf_s. This function may be called to obtain
+ * multiple instances of the interface, each of which may be set up with a
+ * different frequency and address.
+ *
+ * Input Parameters:
+ * Port number (for hardware that has multiple PWM interfaces)
+ * GPIO pin number for pin A
+ * GPIO pin number for pin B (CONFIG_PWM_NCHANNELS == 2)
+ *
+ * Returned Value:
+ * Valid PWM device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+struct rp2040_pwm_lowerhalf_s *rp2040_pwm_initialize(int port,
+ int pin_a,
+ int pin_b,
+ uint32_t flags);
+#else
+struct rp2040_pwm_lowerhalf_s *rp2040_pwm_initialize(int port,
+ int pin,
+ uint32_t flags);
+#endif
+
+/****************************************************************************
+ * Name: rp2040_pwmdev_uninitialize
+ *
+ * Description:
+ * De-initialize the selected pwm port, and power down the device.
+ *
+ * Input Parameter:
+ * Device structure as returned by the rp2040_pwmdev_initialize()
+ *
+ * Returned Value:
+ * OK on success, ERROR when internal reference count mismatch or dev
+ * points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int rp2040_pwm_uninitialize(struct pwm_lowerhalf_s *dev);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */
diff --git a/boards/arm/rp2040/common/include/rp2040_pwmdev.h b/boards/arm/rp2040/common/include/rp2040_pwmdev.h
new file mode 100644
index 0000000000..7b86ea3ded
--- /dev/null
+++ b/boards/arm/rp2040/common/include/rp2040_pwmdev.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/include/rp2040_pwmdev.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __BOARDS_ARM_RP2040_RASPBERRYPI_PICO_INCLUDE_RP2040_PWMDEV_H
+#define __BOARDS_ARM_RP2040_RASPBERRYPI_PICO_INCLUDE_RP2040_PWMDEV_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp2040_pwmdev_initialize
+ *
+ * Description:
+ * Initialize pwm driver and register the /dev/pwm device.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+int rp2040_pwmdev_initialize(int slice,
+ int pin_a,
+ int pin_b,
+ uint32_t flags);
+#else
+int rp2040_pwmdev_initialize(int slice,
+ int pin,
+ uint32_t flags);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __BOARDS_ARM_RP2040_RASPBERRYPI_PICO_INCLUDE_RP2040_PWMDEV_H */
diff --git a/boards/arm/rp2040/common/src/rp2040_pwmdev.c b/boards/arm/rp2040/common/src/rp2040_pwmdev.c
new file mode 100644
index 0000000000..902c75418c
--- /dev/null
+++ b/boards/arm/rp2040/common/src/rp2040_pwmdev.c
@@ -0,0 +1,95 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/src/rp2040_pwmdev.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include "rp2040_pwm.h"
+
+#ifdef CONFIG_RP2040_PWM
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_pwmdev_initialize
+ *
+ * Description:
+ * Initialize and register spi driver for the specified pwm port
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+int rp2040_pwmdev_initialize(int slice,
+ int pin_a,
+ int pin_b,
+ uint32_t flags)
+#else
+int rp2040_pwmdev_initialize(int slice,
+ int pin,
+ uint32_t flags)
+#endif
+{
+ int ret;
+ struct rp2040_pwm_lowerhalf_s *pwm_lowerhalf;
+
+ pwminfo("Initializing /dev/pwm%d a %d b %d f 0x%08lX..\n",
+ slice,
+ pin_a,
+ pin_b,
+ flags);
+
+ /* Initialize spi device */
+
+#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS == 2
+ pwm_lowerhalf = rp2040_pwm_initialize(slice, pin_a, pin_b, flags);
+#else
+ pwm_lowerhalf = rp2040_pwm_initialize(slice, pin, flags);
+#endif
+
+ if (!pwm_lowerhalf)
+ {
+ pwmerr("ERROR: Failed to initialize pwm%d.\n", slice);
+ return -ENODEV;
+ }
+
+ char path[10] = "/dev/pwmN";
+ path[8] = '0' + slice; /* replace "N" with slice number. */
+
+ ret = pwm_register(path, (struct pwm_lowerhalf_s *) pwm_lowerhalf);
+ if (ret < 0)
+ {
+ pwmerr("ERROR: Failed to register pwm%d: %d\n", slice, ret);
+ return -ENODEV;
+ }
+
+ return OK;
+}
+
+#endif /* CONFIG_RP2040_PWM */
+