[PATCH v3 1/1] Adding QEP driver support to BeagleBoneBlack BSP

James Fitzsimons james.fitzsimons at gmail.com
Sun Feb 21 09:29:10 UTC 2021


---
 bsps/arm/beagle/headers.am              |   2 +
 bsps/arm/beagle/include/bsp/bbb-pwm.h   |  11 -
 bsps/arm/beagle/include/bsp/pwmss.h     |  54 +++
 bsps/arm/beagle/include/bsp/qep.h       | 382 ++++++++++++++++++++
 bsps/arm/beagle/pwm/pwm.c               |  60 +---
 bsps/arm/beagle/pwmss/pwmss.c           |  64 ++++
 bsps/arm/beagle/qep/qep.c               | 445 ++++++++++++++++++++++++
 c/src/lib/libbsp/arm/beagle/Makefile.am |   6 +
 spec/build/bsps/arm/beagle/obj.yml      |   5 +-
 9 files changed, 959 insertions(+), 70 deletions(-)
 create mode 100644 bsps/arm/beagle/include/bsp/pwmss.h
 create mode 100644 bsps/arm/beagle/include/bsp/qep.h
 create mode 100644 bsps/arm/beagle/pwmss/pwmss.c
 create mode 100644 bsps/arm/beagle/qep/qep.c

diff --git a/bsps/arm/beagle/headers.am b/bsps/arm/beagle/headers.am
index 4dc35f2e2a..e4a746b2e1 100644
--- a/bsps/arm/beagle/headers.am
+++ b/bsps/arm/beagle/headers.am
@@ -13,3 +13,5 @@ include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/beagleboneb
 include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/i2c.h
 include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/irq.h
 include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/spi.h
+include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/qep.h
+include_bsp_HEADERS += ../../../../../../bsps/arm/beagle/include/bsp/pwmss.h
diff --git a/bsps/arm/beagle/include/bsp/bbb-pwm.h b/bsps/arm/beagle/include/bsp/bbb-pwm.h
index cf5d6fe552..6fdba486be 100644
--- a/bsps/arm/beagle/include/bsp/bbb-pwm.h
+++ b/bsps/arm/beagle/include/bsp/bbb-pwm.h
@@ -31,17 +31,6 @@ extern "C" {
 #define BBB_CONTROL_CONF_GPMC_AD(n)   (0x800 + (n * 4))
 #define BBB_CONTROL_CONF_LCD_DATA(n)   (0x8a0 + (n * 4))
 
-/**
- * @brief The set of possible PWM subsystem module
- *
- * Enumerated type to define various instance of pwm module.
- */
-typedef enum{
-  BBB_PWMSS0 = 0,
-  BBB_PWMSS1,
-  BBB_PWMSS2,
-  BBB_PWMSS_COUNT
-}BBB_PWMSS;
 
 typedef enum{
   BBB_P8_13_2B = 3,
diff --git a/bsps/arm/beagle/include/bsp/pwmss.h b/bsps/arm/beagle/include/bsp/pwmss.h
new file mode 100644
index 0000000000..3a9dcbb70a
--- /dev/null
+++ b/bsps/arm/beagle/include/bsp/pwmss.h
@@ -0,0 +1,54 @@
+/**
+ * @file
+ *
+ * @ingroup arm_beagle
+ *
+ * @brief Shared PWMSS module functions used by PWM, eQEP and eCAP (when added).
+ */
+
+/**
+ * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *.
+ */
+
+#ifndef LIBBSP_ARM_BEAGLE_PWMSS_H
+#define LIBBSP_ARM_BEAGLE_PWMSS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* The following definitions are bitmasks for the clk control registers for
+ * the PWMSS module clocks. All three modules have the same clock control
+ * hence the EPMSSx to signify these values are consistent across all
+ * EPWMSS instances. */
+#define AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE_ENABLE   (0x2u)
+#define AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE   (0x00000003u)
+#define AM335X_CM_PER_EPWMSSx_CLKCTRL_IDLEST_FUNC  (0x0u)
+#define AM335X_CM_PER_EPWMSSx_CLKCTRL_IDLEST_SHIFT (0x00000010u)
+#define AM335X_CM_PER_EPWMSSx_CLKCTRL_IDLEST       (0x00030000u)
+
+/**
+ * @brief The set of possible PWM subsystem module
+ *
+ * Enumerated type to define various instance of pwm module.
+ */
+typedef enum {
+  BBB_PWMSS0 = 0,
+  BBB_PWMSS1,
+  BBB_PWMSS2,
+  BBB_PWMSS_COUNT
+} BBB_PWMSS;
+
+
+rtems_status_code pwmss_module_clk_config(BBB_PWMSS pwmss_id);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBSP_ARM_BEAGLE_PWMSS_H */
diff --git a/bsps/arm/beagle/include/bsp/qep.h b/bsps/arm/beagle/include/bsp/qep.h
new file mode 100644
index 0000000000..fc086e3c80
--- /dev/null
+++ b/bsps/arm/beagle/include/bsp/qep.h
@@ -0,0 +1,382 @@
+/**
+ * @file
+ *
+ * @ingroup arm_beagle
+ *
+ * @brief eQEP (enhanced Quadrature Encoder Pulse) support API.
+ */
+
+/**
+ * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * For details of the Enhanced Quadrature Encoder Pulse (eQEP) Module refer to
+ * page 2511 of the TI Technical Reference Manual
+ * (https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf)
+ *
+ * This driver supports using the QEP modules in Quadrature-clock Mode.
+ * Direction-count Mode is not currently supported. Similarly the QEPI: Index
+ * or Zero Marker and QEPS: Strobe Input pins are not currently supported.
+ *
+ * The mode can be any one of:
+ *  - Quadrature-count mode - For encoders that generate pulses 90 degrees
+ *      out of phase for determining direction and speed.
+ *  - Direction-count mode - for position encoders that provide direction and
+ *      clock outputs, instead of quadrature outputs.
+ *  - UP-count mode - The counter direction signal is hard-wired for up count
+ *      and the position counter is used to measure the frequency of the QEPA
+ *      input.
+ *  - DOWN-count mode - The counter direction signal is hard-wired for a down
+ *      count and the position counter is used to measure the frequency of the
+ *      QEPA input.
+ *
+ * When the eQEP module is configured in quadrature mode, the module
+ * can either provide an absolute position, or a relative position. Absolute
+ * simply increments or decrements depending on the direction. Relative
+ * increments until the unit timer overflows at which point it latches the
+ * position value, resets the position count to zero and starts again.
+ */
+
+#ifndef LIBBSP_ARM_BEAGLE_QEP_H
+#define LIBBSP_ARM_BEAGLE_QEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define AM335X_EQEP_REGS                       (0x00000180)
+#define AM335X_EQEP_0_REGS                     (AM335X_PWMSS0_MMAP_ADDR + AM335X_EQEP_REGS)
+#define AM335X_EQEP_1_REGS                     (AM335X_PWMSS1_MMAP_ADDR + AM335X_EQEP_REGS)
+#define AM335X_EQEP_2_REGS                     (AM335X_PWMSS2_MMAP_ADDR + AM335X_EQEP_REGS)
+
+/* eQEP registers of the PWMSS modules - see page 1672 of the TRM for details */
+#define AM335x_EQEP_QPOSCNT       0x0   /* eQEP Position Counter */
+#define AM335x_EQEP_QPOSINIT      0x4   /* eQEP Position Counter Initialization */
+#define AM335x_EQEP_QPOSMAX       0x8   /* eQEP Maximum Position Count */
+#define AM335x_EQEP_QPOSCMP       0xC   /* eQEP Position-Compare */
+#define AM335x_EQEP_QPOSILAT      0x10  /* eQEP Index Position Latch */
+#define AM335x_EQEP_QPOSSLAT      0x14  /* eQEP Strobe Position Latch */
+#define AM335x_EQEP_QPOSLAT       0x18  /* eQEP Position Counter Latch */
+#define AM335x_EQEP_QUTMR         0x1C  /* eQEP Unit Timer */
+#define AM335x_EQEP_QUPRD         0x20  /* eQEP Unit Period */
+#define AM335x_EQEP_QWDTMR        0x24  /* eQEP Watchdog Timer */
+#define AM335x_EQEP_QWDPRD        0x26  /* eQEP Watchdog Period */
+#define AM335x_EQEP_QDECCTL       0x28  /* eQEP Decoder Control */
+#define AM335x_EQEP_QEPCTL        0x2A  /* eQEP Control */
+#define AM335x_EQEP_QCAPCTL       0x2C  /* eQEP Capture Control */
+#define AM335x_EQEP_QPOSCTL       0x2E  /* eQEP Position-Compare Control */
+#define AM335x_EQEP_QEINT         0x30  /* eQEP Interrupt Enable */
+#define AM335x_EQEP_QFLG          0x32  /* eQEP Interrupt Flag */
+#define AM335x_EQEP_QCLR          0x34  /* eQEP Interrupt Clear */
+#define AM335x_EQEP_QFRC          0x36  /* eQEP Interrupt Force */
+#define AM335x_EQEP_QEPSTS        0x38  /* eQEP Status */
+#define AM335x_EQEP_QCTMR         0x3A  /* eQEP Capture Timer */
+#define AM335x_EQEP_QCPRD         0x3C  /* eQEP Capture Period */
+#define AM335x_EQEP_QCTMRLAT      0x3E  /* eQEP Capture Timer Latch */
+#define AM335x_EQEP_QCPRDLAT      0x40  /* eQEP Capture Period Latch */
+#define AM335x_EQEP_REVID         0x5C  /* eQEP Revision ID */
+
+/* bitmasks for eQEP registers  */
+#define AM335x_EQEP_QEPCTL_UTE    (1 << 1)
+#define AM335x_EQEP_QEPCTL_QCLM   (1 << 2)
+#define AM335x_EQEP_QEPCTL_PHEN   (1 << 3)
+#define AM335x_EQEP_QEPCTL_IEL    (1 << 4)
+#define AM335x_EQEP_QEPCTL_SWI    (1 << 7)
+#define AM335x_EQEP_QEPCTL_PCRM   (3 << 12)
+#define AM335x_EQEP_QDECCTL_QSRC  (3 << 14)
+#define AM335x_EQEP_QDECCTL_XCR   (1 << 11)
+#define AM335x_EQEP_QDECCTL_SWAP  (1 << 10)
+#define AM335x_EQEP_QDECCTL_IGATE (1 << 9)
+#define AM335x_EQEP_QDECCTL_QAP   (1 << 8)
+#define AM335x_EQEP_QDECCTL_QBP   (1 << 7)
+#define AM335x_EQEP_QDECCTL_QIP   (1 << 6)
+#define AM335x_EQEP_QDECCTL_QSP   (1 << 5)
+#define AM335x_EQEP_CLK_EN        (1 << 4)
+#define AM335x_EQEP_QEINT_UTO     (1 << 11)
+#define AM335x_EQEP_QFLG_UTO      (1 << 11)
+#define AM335x_EQEP_QFLG_MASK     0x0FFF
+
+/* The pin mux modes for the QEP input pins on the P8 and P9 headers */
+#define BBB_P8_11_MUX_QEP 4
+#define BBB_P8_12_MUX_QEP 4
+#define BBB_P8_15_MUX_QEP 4
+#define BBB_P8_16_MUX_QEP 4
+#define BBB_P8_31_MUX_QEP 2
+#define BBB_P8_32_MUX_QEP 2
+#define BBB_P8_33_MUX_QEP 2
+#define BBB_P8_35_MUX_QEP 2
+#define BBB_P8_39_MUX_QEP 3
+#define BBB_P8_40_MUX_QEP 3
+#define BBB_P8_41_MUX_QEP 3
+#define BBB_P8_42_MUX_QEP 3
+#define BBB_P9_25_MUX_QEP 1
+#define BBB_P9_27_MUX_QEP 1
+#define BBB_P9_41_MUX_QEP 1
+#define BBB_P9_42_MUX_QEP 1
+
+#define NANO_SEC_PER_SEC  1000000000
+/* This is the max clock rate for the EPWMSS module. See 15.1.2.2 of the TRM.
+ * If the CPU was using dynamic scaling this could potentially be wrong */
+#define SYSCLKOUT         100000000
+
+/**
+ * @brief The set of possible eQEP Position Counter Input Modes
+ *
+ * Enumerated type to define various modes for the eQEP module. The values
+ * correspond to the values for the QSRC bits of the QDECCTL register.
+ */
+typedef enum {
+  QUADRATURE_COUNT = 0,
+  DIRECTION_COUNT,
+  UP_COUNT,
+  DOWN_COUNT
+} BBB_QEP_COUNT_MODE;
+
+/**
+ * @brief The set of possible modes for Quadrature decode
+ *
+ */
+typedef enum {
+  ABSOLUTE = 0,
+  RELATIVE
+} BBB_QEP_QUADRATURE_MODE;
+
+/**
+ * @brief The set of possible eQEP input pins
+ *
+ */
+typedef enum {
+  BBB_P8_11_2B_IN,
+  BBB_P8_12_2A_IN,
+  BBB_P8_15_2_STROBE,
+  BBB_P8_16_2_IDX,
+  BBB_P8_31_1_IDX,
+  BBB_P8_32_1_STROBE,
+  BBB_P8_33_1B_IN,
+  BBB_P8_35_1A_IN,
+  BBB_P8_39_2_IDX,
+  BBB_P8_40_2_STROBE,
+  BBB_P8_41_2A_IN,
+  BBB_P8_42_2B_IN,
+  BBB_P9_25_0_STROBE,
+  BBB_P9_27_0B_IN,
+  BBB_P9_41_0_IDX,
+  BBB_P9_42_0A_IN
+} bbb_qep_pin;
+
+
+/**
+ * @brief This function definition is used to declare a callback function that
+ * will be called by the interrupt handler of the QEP driver. In order for the
+ * interrupt event to trigger the driver must be configured in RELATIVE mode
+ * (using the beagle_qep_get_quadrature_mode function), and the unit timer must
+ * have been configured (using the beagle_eqep_set_timer_period function).
+ *
+ * @param BBB_PWMSS This argument is provided to the user call back function so
+ * that the user can tell which QEP module raised the interrupt.
+ *
+ * @param position The value of the position counter that was latched when the
+ * unit timer raised this interrupt. This is the value that would be returned
+ * by calling "beagle_qep_get_position".
+ *
+ * @param user This a pointer to a user provided data structure. The user sets
+ * this pointer value when configuring the unit timer callback via the
+ * beagle_eqep_set_timer_period function and it is returned here as an argument.
+ * The driver does not touch this value.
+ */
+typedef void (*bbb_eqep_timer_callback)(
+  BBB_PWMSS,
+  uint32_t position,
+  void* user
+);
+
+
+/**
+ * @brief This structure represents an eQEP module instance. The members
+ * represent the configuration of a specific eQEP module. There are three
+ * eQEP modules in the AM335x, one associated with each PWMSS unit.
+ * @var bbb_eqep::pwmss_id The PWMSS unit this eQEP module belongs to.
+ * @var bbb_eqep::mmio_base The base address for this eQEP modules registers
+ * @var bbb_eqep::irq The IRQ vector for this eQEP module
+ * @var bbb_eqep::timer_callback An optional user provided callback function
+ * when the driver is configured in RELATIVE mode using the unit timer
+ * @var bbb_eqep::user An optional pointer to user provided data that will be
+ * handed to the callback function as an argument.
+ * @var bbb_eqep::count_mode The count mode for this eQEP module. Defaults to
+ * QUADRATURE.
+ * @var bbb_eqep::quadrature_mode The mode for QUADRATURE operation. Defaults
+ * to ABSOLUTE - will count up to overflow or until the user resets the
+ * position. Can be set to RELATIVE which will trigger a call back of the unit
+ * timer if configured.
+ * @var bbb_eqep::invert_qa 1 to invert the A channel input, 0 to leave as is.
+ * @var bbb_eqep::invert_qb 1 to invert the B channel input, 0 to leave as is.
+ * @var bbb_eqep::invert_qi 1 to invert the INDEX input, 0 to leave as is.
+ * @var bbb_eqep::invert_qs 1 to invert the STROBE input, 0 to leave as is.
+ * @var bbb_eqep::swap_inputs 1 to swap the A and B channel inputs, 0 to leave
+ * as is.
+ *
+ */
+typedef struct {
+  const BBB_PWMSS pwmss_id;
+  const uint32_t mmio_base;
+  const rtems_vector_number irq;
+  bbb_eqep_timer_callback timer_callback;
+  void* user;
+  BBB_QEP_COUNT_MODE count_mode;
+  BBB_QEP_QUADRATURE_MODE quadrature_mode;
+  uint32_t invert_qa;
+  uint32_t invert_qb;
+  uint32_t invert_qi;
+  uint32_t invert_qs;
+  uint32_t swap_inputs;
+} bbb_eqep;
+
+
+/**
+ * @brief Initialises the eQEP module of the specified PWMSS unit. This
+ * configures the clocks, sets up the interrupt handler and unit timer,
+ * The module is configured in Quadrature decode mode using
+ * absolute position by default.
+ *
+ * @param pwmss_id The PWMSS module to configure the eQEP for.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_init(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Enables the eQEP module of the specified PWMSS unit.
+ *
+ * @param pwmss_id The PWMSS module which will have the eQEP function enabled.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_enable(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Disables the eQEP module of the specified PWMSS unit.
+ *
+ * @param pwmss_id The PWMSS module which will have the eQEP function disabled.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_disable(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Configures a given pin for use with the eQEP function of the supplied
+ * PWMSS module.
+ *
+ * @param pin_no The P9 or P8 header pin to be configured for the eQEP function.
+ * @param pwmss_id The PWMSS module which will have the eQEP function enabled.
+ * @param pullup_enable If true then the internal pull up resistor on the
+ * specified pin will be enabled, if false the pull down will be enabled.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_pinmux_setup(
+  bbb_qep_pin pin_no,
+  BBB_PWMSS pwmss_id,
+  bool pullup_enable
+);
+
+/**
+ * @brief Returns the current position value of the eQEP function for the
+ * specified PWMSS module.
+ *
+ * @param pwmss_id Identifies which PWMSS module to return the eQEP position for
+ * @return int32_t The current position value.
+ */
+int32_t beagle_qep_get_position(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Sets the initial position value of the eQEP function for the
+ * specified PWMSS module.
+ *
+ * @param pwmss_id Identifies which PWMSS module to set the eQEP position for
+ * @param position The value to initialise the position register with.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_set_position(
+    BBB_PWMSS pwmss_id,
+    uint32_t position
+);
+
+/**
+ * @brief Sets the count mode for the eQEP module.
+ * @param pwmss_id Identifies which PWMSS module to set the eQEP count mode for.
+ * @param mode One of the above modes to configure the eQEP module for.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_set_count_mode(
+  BBB_PWMSS pwmss_id,
+  BBB_QEP_COUNT_MODE mode
+);
+
+/**
+ * @brief Gets the currently configured count mode for the eQEP module.
+ * @param pwmss_id Identifies which PWMSS module to set the eQEP count mode for.
+ * @return An enum value representing the current count mode.
+ */
+BBB_QEP_COUNT_MODE beagle_qep_get_count_mode(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Returns the currently configured quadrature mode - either absolute,
+ * or relative.
+ * @param pwmss_id Identifies which PWMSS module to get the eQEP quadrature
+ * mode for.
+ * @return BBB_QEP_QUADRATURE_MODE The currently configured quadrature mode.
+ */
+BBB_QEP_QUADRATURE_MODE beagle_qep_get_quadrature_mode(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Sets the quadrature mode to either absolute or relative.
+ * @param pwmss_id Identifies which PWMSS module to set the eQEP quadrature
+ * mode for.
+ * @param mode BBB_QEP_QUADRATURE_MODE Set the mode of the eQEP to either
+ * absolute or relative.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_qep_set_quadrature_mode(
+    BBB_PWMSS pwmss_id,
+    BBB_QEP_QUADRATURE_MODE mode
+);
+
+/**
+ * @brief Returns the the currently configured unit timer period.
+ * @param pwmss_id Identifies which PWMSS module to get the eQEP timer value for
+ * @return uint32_t The current unit timer value in nanoseconds
+ */
+uint32_t beagle_eqep_get_timer_period(BBB_PWMSS pwmss_id);
+
+/**
+ * @brief Sets the unit timer period for the eQEP module.
+ * 0 = off, greater than zero sets the period.
+ * @param pwmss_id Identifies which PWMSS module to set the eQEP unit timer for.
+ * @param period The value in nanoseconds to set the unit timer period to.
+ * @param timer_callback This is the user provided callback function that will
+ * be called by the interrupt event handler on expiry of the unit timer. The
+ * user can provide NULL if they don't require a call back.
+ * @param user This is a pointer to a user provided data structure that will be
+ * handed back as an argument to the timer callback. The driver does not touch
+ * this value.
+ * @return RTEMS_SUCCESSFUL if ok, RTEMS_INVALID_ID if an invalid pwmss_id is
+ * supplied.
+ */
+rtems_status_code beagle_eqep_set_timer_period(
+    BBB_PWMSS pwmss_id,
+    uint64_t period,
+    bbb_eqep_timer_callback timer_callback,
+    void* user
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBSP_ARM_BEAGLE_QEP_H */
diff --git a/bsps/arm/beagle/pwm/pwm.c b/bsps/arm/beagle/pwm/pwm.c
index 81ace1254e..4bd09293e8 100644
--- a/bsps/arm/beagle/pwm/pwm.c
+++ b/bsps/arm/beagle/pwm/pwm.c
@@ -23,6 +23,7 @@
 #include <bsp/gpio.h>
 #include <bsp/bbb-gpio.h>
 #include <bsp.h>
+#include <bsp/pwmss.h>
 #include <bsp/bbb-pwm.h>
 #include <bsp/beagleboneblack.h>
 
@@ -38,7 +39,7 @@
  * @param pwm_id It is the instance number of EPWM of pwm sub system.
  * 
  * @return Base Address of respective pwm instant.
-*/
+ */
 static uint32_t select_pwm(BBB_PWMSS pwm_id)
 {
   uint32_t baseAddr=0;
@@ -181,63 +182,6 @@ static bool pwm_clock_enable(BBB_PWMSS pwm_id)
   return status;
 }
 
-/**
- * @brief   This function configures the L3 and L4_PER system clocks.
- *          It also configures the system clocks for the specified ePWMSS
- *          instance.
- *
- * @param   pwmss_id    The instance number of ePWMSS whose system clocks
- *                         have to be configured.
- *
- * 'pwmss_id' can take one of the following values:
- * (0 <= pwmss_id <= 2)
- *
- * @return  True if successful
- *          False if Unsuccessful 
- */
-static bool pwmss_module_clk_config(BBB_PWMSS pwmss_id)
-{
-  bool is_valid = true;
-  
-  if(pwmss_id == BBB_PWMSS0) {
-    const uint32_t is_functional = AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_FUNC <<
-                         AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_SHIFT;
-    const uint32_t clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL;
-    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST;
-    const uint32_t is_enable = AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE;
-    const uint32_t module_mode = AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE;
-    
-    REG(clkctrl) |= is_enable;
-    while((REG(clkctrl) & module_mode) != is_enable);
-    while((REG(clkctrl) & idle_bits) != is_functional);
-    }
-    else if(pwmss_id == BBB_PWMSS1) {
-    const uint32_t is_functional = AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_FUNC <<
-                         AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_SHIFT;
-    const uint32_t clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL;
-    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST;
-    const uint32_t is_enable = AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE_ENABLE;
-    const uint32_t module_mode = AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE;
-
-    REG(clkctrl) |= is_enable;
-    while((REG(clkctrl) & module_mode) != is_enable);
-    while((REG(clkctrl) & idle_bits) != is_functional);
-    } else if(pwmss_id == BBB_PWMSS2) {
-    const uint32_t is_functional = AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_FUNC <<
-                         AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_SHIFT;
-    const uint32_t clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL;
-    const uint32_t idle_bits = AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST;
-    const uint32_t is_enable = AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE_ENABLE;
-    const uint32_t module_mode = AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE;
-
-    REG(clkctrl) |= is_enable;
-    while((REG(clkctrl) & module_mode) != is_enable);
-    while((REG(clkctrl) & idle_bits) != is_functional);
-    } else 
-	is_valid = false;
-  return is_valid;
-}
-
 bool beagle_pwm_init(BBB_PWMSS pwmss_id)
 {
   const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
diff --git a/bsps/arm/beagle/pwmss/pwmss.c b/bsps/arm/beagle/pwmss/pwmss.c
new file mode 100644
index 0000000000..c4d36d8ae1
--- /dev/null
+++ b/bsps/arm/beagle/pwmss/pwmss.c
@@ -0,0 +1,64 @@
+/**
+ * @file
+ *
+ * @ingroup arm_beagle
+ *
+ * @brief Support for eQEP for the BeagleBone Black.
+ */
+
+/**
+ * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <libcpu/am335x.h>
+#include <stdio.h>
+#include <bsp.h>
+#include <bsp/pwmss.h>
+#include <bsp/beagleboneblack.h>
+
+
+/**
+ * @brief   This function configures the L3 and L4_PER system clocks.
+ *          It also configures the system clocks for the specified ePWMSS
+ *          instance.
+ *
+ * @param   pwmss_id    The instance number of ePWMSS whose system clocks
+ *                         have to be configured.
+ *
+ * 'pwmss_id' can take one of the following values:
+ * (0 <= pwmss_id <= 2)
+ *
+ * @return  True if successful
+ *          False if Unsuccessful
+ */
+rtems_status_code pwmss_module_clk_config(BBB_PWMSS pwmss_id)
+{
+    uint32_t clkctrl;
+
+    /* calculate the address of the clock control register for the PWMSS
+     * module we are configuring */
+    if(pwmss_id == BBB_PWMSS0) {
+        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL;
+    } else if(pwmss_id == BBB_PWMSS1) {
+        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL;
+    } else if(pwmss_id == BBB_PWMSS2) {
+        clkctrl = AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL;
+    }
+
+    /* when the module is functional the IDLEST bits (16 -17) of the
+     * CM_PER_EPWMSSx_CLKCTRL register will be 0x0. */
+    const uint32_t is_functional = 0x0;
+    const uint32_t idle_bits = AM335X_CM_PER_EPWMSSx_CLKCTRL_IDLEST;
+    const uint32_t is_enable = AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE_ENABLE;
+    const uint32_t module_mode = AM335X_CM_PER_EPWMSSx_CLKCTRL_MODULEMODE;
+
+    REG(clkctrl) |= is_enable;
+    while((REG(clkctrl) & module_mode) != is_enable);
+    while((REG(clkctrl) & idle_bits) != is_functional);
+
+    return RTEMS_SUCCESSFUL;
+}
diff --git a/bsps/arm/beagle/qep/qep.c b/bsps/arm/beagle/qep/qep.c
new file mode 100644
index 0000000000..34eb453258
--- /dev/null
+++ b/bsps/arm/beagle/qep/qep.c
@@ -0,0 +1,445 @@
+/**
+ * @file
+ *
+ * @ingroup arm_beagle
+ *
+ * @brief Support for eQEP for the BeagleBone Black.
+ */
+
+/**
+ * Copyright (c) 2020 James Fitzsimons <james.fitzsimons at gmail.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <libcpu/am335x.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <bsp/gpio.h>
+#include <bsp/bbb-gpio.h>
+#include <bsp.h>
+#include <bsp/pwmss.h>
+#include <bsp/qep.h>
+#include <bsp/beagleboneblack.h>
+
+
+/**
+ * @brief Represents all the PWMSS QEP modules and their default values.
+ */
+static bbb_eqep bbb_eqep_table[ BBB_PWMSS_COUNT ] =
+{
+ {
+  .pwmss_id = BBB_PWMSS0,
+  .mmio_base = AM335X_EQEP_0_REGS,
+  .irq = AM335X_INT_eQEP0INT,
+  .timer_callback = NULL,
+  .user = NULL,
+  .count_mode = QUADRATURE_COUNT,
+  .quadrature_mode = ABSOLUTE,
+  .invert_qa = 0,
+  .invert_qb = 0,
+  .invert_qi = 0,
+  .invert_qs = 0,
+  .swap_inputs = 0
+ },
+ {
+  .pwmss_id = BBB_PWMSS1,
+  .mmio_base = AM335X_EQEP_1_REGS,
+  .irq = AM335X_INT_eQEP1INT,
+  .timer_callback = NULL,
+  .user = NULL,
+  .count_mode = QUADRATURE_COUNT,
+  .quadrature_mode = ABSOLUTE,
+  .invert_qa = 0,
+  .invert_qb = 0,
+  .invert_qi = 0,
+  .invert_qs = 0,
+  .swap_inputs = 0
+ },
+ {
+  .pwmss_id = BBB_PWMSS2,
+  .mmio_base = AM335X_EQEP_2_REGS,
+  .irq = AM335X_INT_eQEP2INT,
+  .timer_callback = NULL,
+  .user = NULL,
+  .count_mode = QUADRATURE_COUNT,
+  .quadrature_mode = ABSOLUTE,
+  .invert_qa = 0,
+  .invert_qb = 0,
+  .invert_qi = 0,
+  .invert_qs = 0,
+  .swap_inputs = 0
+ }
+};
+
+/* eQEP Interrupt handler */
+static void beagle_eqep_irq_handler(void *arg)
+{
+  uint16_t flags;
+  int32_t position = 0;
+  bbb_eqep* eqep = arg;
+
+  /* Use the interrupt register (QFLG) mask to determine what caused the
+   * interrupt. */
+  flags = REG16(eqep->mmio_base + AM335x_EQEP_QFLG) & AM335x_EQEP_QFLG_MASK;
+  /* Check the interrupt source to see if it was a unit timer overflow  */
+  if (flags & AM335x_EQEP_QFLG_UTO && eqep->timer_callback != NULL) {
+    /* Handle the unit timer overflow interrupt */
+    position = beagle_qep_get_position(eqep->pwmss_id);
+    eqep->timer_callback(eqep->pwmss_id, position, eqep->user);
+  }
+
+  /* Clear interrupt flags (write back triggered flags to the clear register) */
+  REG16(eqep->mmio_base + AM335x_EQEP_QCLR) = flags;
+}
+
+rtems_status_code beagle_qep_init(BBB_PWMSS pwmss_id)
+{
+  rtems_status_code sc;
+  uint16_t qdecctl;
+
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  sc = pwmss_module_clk_config(eqep->pwmss_id);
+  if (sc != RTEMS_SUCCESSFUL) {
+    /* failed to successfully configure the PWMSS module clocks */
+    return sc;
+  }
+
+  /* This enables clock for EQEP module in PWMSS subsystem. */
+  REG(eqep->mmio_base + AM335X_PWMSS_CLKCONFIG) |= AM335x_EQEP_CLK_EN;
+
+  /* Setup interrupt handler */
+  sc = rtems_interrupt_handler_install(
+      eqep->irq,
+      NULL,
+      RTEMS_INTERRUPT_UNIQUE,
+      (rtems_interrupt_handler)beagle_eqep_irq_handler,
+      (void*)eqep
+  );
+
+  /* The QDECCTL register configures the QEP Decoder module. We use it to set */
+  /* the count mode, input inversion, channel swaps, unit timer interrupt etc. */
+  qdecctl = 0;
+  if (eqep->count_mode <= 3) {
+    qdecctl |= eqep->count_mode << 14;
+
+    /* If the count mode is UP_COUNT or DOWN_COUNT then only count on
+     * the rising edge. QUADRATURE_COUNT and DIRECTION_COUNT count on
+     * both edges.  */
+    if (eqep->count_mode >= 2) {
+      qdecctl |= AM335x_EQEP_QDECCTL_XCR;
+    }
+  }
+
+  /* Should we swap the cha and chb inputs */
+  if (eqep->swap_inputs == 1) {
+    qdecctl |= AM335x_EQEP_QDECCTL_SWAP;
+  }
+  /* Should we invert the qa input */
+  if (eqep->invert_qa == 1) {
+    qdecctl |= AM335x_EQEP_QDECCTL_QAP;
+  }
+  /* Should we invert the qb input */
+  if (eqep->invert_qb == 1) {
+    qdecctl |= AM335x_EQEP_QDECCTL_QBP;
+  }
+  /* Should we invert the index input */
+  if (eqep->invert_qi == 1) {
+    qdecctl |= AM335x_EQEP_QDECCTL_QIP;
+
+  }
+  /* Should we invert the strobe input */
+  if (eqep->invert_qs == 1) {
+    qdecctl |= AM335x_EQEP_QDECCTL_QSP;
+  }
+
+  /* Write the configured decoder control settings to the QDECCTL register */
+  REG16(eqep->mmio_base + AM335x_EQEP_QDECCTL) = qdecctl;
+  /* Set the position counter initialisation register */
+  REG(eqep->mmio_base + AM335x_EQEP_QPOSINIT) = 0;
+  /* initialise the maximum position counter value */
+  REG(eqep->mmio_base + AM335x_EQEP_QPOSMAX) = ~0;
+  /* initialise the position counter register */
+  REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT) = 0;
+  /* Enable Unit Time Period interrupt. */
+  REG16(eqep->mmio_base + AM335x_EQEP_QEINT) |= AM335x_EQEP_QEINT_UTO;
+
+  /* The following bitmasks enable the eQEP module with:
+   * - the unit timer disabled
+   * - will latch the value in QPOSLAT to QPOSCNT upon unit timer overflow
+   * - will latch QPOSILAT on index signal.
+   * - Software initialisation of position counter (will be set to 0 because
+   *   QPOSINIT = 0).
+   */
+  uint32_t value = AM335x_EQEP_QEPCTL_QCLM | AM335x_EQEP_QEPCTL_IEL |
+      AM335x_EQEP_QEPCTL_PHEN | AM335x_EQEP_QEPCTL_SWI;
+
+  /* set the enable bit of the control register */
+  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = value;
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code beagle_qep_enable(BBB_PWMSS pwmss_id)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+  /* set the enable bit of the control register */
+  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) |= AM335x_EQEP_QEPCTL_PHEN;
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code beagle_qep_disable(BBB_PWMSS pwmss_id)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+  /* clear the enable bit of the control register */
+  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) &= ~AM335x_EQEP_QEPCTL_PHEN;
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code beagle_qep_pinmux_setup(
+    bbb_qep_pin pin_no,
+    BBB_PWMSS pwmss_id,
+    bool pullup_enable
+)
+{
+  rtems_status_code result = RTEMS_SUCCESSFUL;
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  /* enable internal pull up / pull down resistor in pull up mode, and set the
+   * pin as an input. */
+  uint32_t pin_mode =  BBB_RXACTIVE;
+  if ( pullup_enable ) {
+    pin_mode |= BBB_PU_EN;
+  }
+  // The offsets from AM335X_PADCONF_BASE (44e10000) are named after the mode0 mux for that pin.
+  if(pwmss_id == BBB_PWMSS0) {
+    if (pin_no == BBB_P9_25_0_STROBE) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_AHCLKX) = pin_mode | BBB_MUXMODE(BBB_P9_25_MUX_QEP);
+    } else if (pin_no == BBB_P9_27_0B_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSR) = pin_mode | BBB_MUXMODE(BBB_P9_27_MUX_QEP);
+    } else if (pin_no == BBB_P9_41_0_IDX) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_AXR1) = pin_mode | BBB_MUXMODE(BBB_P9_41_MUX_QEP);
+    } else if (pin_no == BBB_P9_42_0A_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKR) = pin_mode | BBB_MUXMODE(BBB_P9_42_MUX_QEP);
+    } else {
+      result = RTEMS_INTERNAL_ERROR;
+    }
+  } else if (pwmss_id == BBB_PWMSS1) {
+    if (pin_no == BBB_P8_31_1_IDX) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA14) = pin_mode | BBB_MUXMODE(BBB_P8_31_MUX_QEP);
+    } else if (pin_no == BBB_P8_32_1_STROBE) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA15) = pin_mode | BBB_MUXMODE(BBB_P8_32_MUX_QEP);
+    } else if (pin_no == BBB_P8_33_1B_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA13) = pin_mode | BBB_MUXMODE(BBB_P8_33_MUX_QEP);
+    } else if (pin_no == BBB_P8_35_1A_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA12) = pin_mode | BBB_MUXMODE(BBB_P8_35_MUX_QEP);
+    } else {
+      result = RTEMS_INTERNAL_ERROR;
+    }
+  } else if (pwmss_id == BBB_PWMSS2) {
+    if (pin_no == BBB_P8_11_2B_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD13) = pin_mode | BBB_MUXMODE(BBB_P8_11_MUX_QEP);
+    } else if (pin_no == BBB_P8_12_2A_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD12) = pin_mode | BBB_MUXMODE(BBB_P8_12_MUX_QEP);
+    } else if (pin_no == BBB_P8_15_2_STROBE) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD15) = pin_mode | BBB_MUXMODE(BBB_P8_15_MUX_QEP);
+    } else if (pin_no == BBB_P8_16_2_IDX) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_AD14) = pin_mode | BBB_MUXMODE(BBB_P8_16_MUX_QEP);
+    } else if (pin_no == BBB_P8_39_2_IDX) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA6) = pin_mode | BBB_MUXMODE(BBB_P8_39_MUX_QEP);
+    } else if (pin_no == BBB_P8_40_2_STROBE) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA7) = pin_mode | BBB_MUXMODE(BBB_P8_40_MUX_QEP);
+    } else if (pin_no == BBB_P8_41_2A_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA4) = pin_mode | BBB_MUXMODE(BBB_P8_41_MUX_QEP);
+    } else if (pin_no == BBB_P8_42_2B_IN) {
+      REG(AM335X_PADCONF_BASE + AM335X_CONF_LCD_DATA5) = pin_mode | BBB_MUXMODE(BBB_P8_42_MUX_QEP);
+    } else {
+      result = RTEMS_INTERNAL_ERROR;
+    }
+  } else {
+    result = RTEMS_INTERNAL_ERROR;
+  }
+  return result;
+}
+
+int32_t beagle_qep_get_position(BBB_PWMSS pwmss_id)
+{
+  int32_t position = 0;
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return -1;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  if (eqep->quadrature_mode == ABSOLUTE) {
+    /* return the current value of the QPOSCNT register */
+    position = REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT);
+  } else if (eqep->quadrature_mode == RELATIVE) {
+    /* return the latched value from the last unit timer interrupt */
+    position = REG(eqep->mmio_base + AM335x_EQEP_QPOSLAT);
+  }
+
+  return position;
+}
+
+rtems_status_code beagle_qep_set_position(BBB_PWMSS pwmss_id, uint32_t position)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+  /* setting the position only really makes sense in ABSOLUTE mode. */
+  if (eqep->quadrature_mode == ABSOLUTE) {
+    REG(eqep->mmio_base + AM335x_EQEP_QPOSCNT) = position;
+  }
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code beagle_qep_set_count_mode(
+    BBB_PWMSS pwmss_id,
+    BBB_QEP_COUNT_MODE mode
+)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+  eqep->count_mode = mode;
+
+  return RTEMS_SUCCESSFUL;
+}
+
+BBB_QEP_COUNT_MODE beagle_qep_get_count_mode(BBB_PWMSS pwmss_id)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  return eqep->count_mode;
+}
+
+rtems_status_code beagle_qep_set_quadrature_mode(
+    BBB_PWMSS pwmss_id,
+    BBB_QEP_QUADRATURE_MODE mode
+)
+{
+  uint16_t qepctl;
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  qepctl = REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL);
+
+  if (mode == ABSOLUTE) {
+    /*
+     * Disable the unit timer position reset
+     */
+    qepctl &= ~AM335x_EQEP_QEPCTL_PCRM;
+
+    eqep->quadrature_mode = ABSOLUTE;
+  } else if (mode == RELATIVE) {
+    /*
+     *  enable the unit timer position reset
+     */
+    qepctl |= AM335x_EQEP_QEPCTL_PCRM;
+
+    eqep->quadrature_mode = RELATIVE;
+  }
+
+  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
+
+  return RTEMS_SUCCESSFUL;
+}
+
+BBB_QEP_QUADRATURE_MODE beagle_qep_get_quadrature_mode(BBB_PWMSS pwmss_id)
+{
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return -1;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  return eqep->quadrature_mode;
+}
+
+
+/* Function to read the period of the unit time event timer */
+uint32_t beagle_eqep_get_timer_period(BBB_PWMSS pwmss_id)
+{
+  uint64_t period;
+  uint32_t timer_period;
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return -1;
+  }
+  const bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  /* Convert from counts per interrupt back into period_ns */
+  period = REG(eqep->mmio_base + AM335x_EQEP_QUPRD);
+  period = period * NANO_SEC_PER_SEC;
+  timer_period = (uint32_t)(period / SYSCLKOUT);
+
+  return timer_period;
+}
+
+rtems_status_code beagle_eqep_set_timer_period(
+    BBB_PWMSS pwmss_id,
+    uint64_t period,
+    bbb_eqep_timer_callback timer_callback,
+    void* user
+)
+{
+  uint16_t qepctl;
+  uint64_t tmp_period;
+  uint32_t timer_period;
+  if ( pwmss_id >= BBB_PWMSS_COUNT ) {
+    return RTEMS_INVALID_ID;
+  }
+  bbb_eqep* eqep = &bbb_eqep_table[pwmss_id];
+
+  /* Disable the unit timer before modifying its period register */
+  qepctl = readw(eqep->mmio_base + AM335x_EQEP_QEPCTL);
+  qepctl &= ~(AM335x_EQEP_QEPCTL_UTE | AM335x_EQEP_QEPCTL_QCLM);
+  REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
+
+  /* Zero the unit timer counter register */
+  REG(eqep->mmio_base + AM335x_EQEP_QUTMR) = 0;
+
+  /* If the timer is enabled (a non-zero period has been passed) */
+  if (period) {
+    /* update the period */
+    tmp_period = period * SYSCLKOUT;
+    timer_period = (uint32_t)(tmp_period / NANO_SEC_PER_SEC);
+    REG(eqep->mmio_base + AM335x_EQEP_QUPRD) = timer_period;
+
+    /* Enable unit timer, and latch QPOSLAT to QPOSCNT on timer expiration */
+    qepctl |= AM335x_EQEP_QEPCTL_UTE | AM335x_EQEP_QEPCTL_QCLM;
+    REG16(eqep->mmio_base + AM335x_EQEP_QEPCTL) = qepctl;
+
+    /* attach the unit timer interrupt handler if one has been supplied */
+    if (timer_callback != NULL) {
+      eqep->timer_callback = timer_callback;
+    }
+    /* attach the user data if it has been provided */
+    if (user != NULL) {
+      eqep->user = user;
+    }
+  }
+
+  return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/lib/libbsp/arm/beagle/Makefile.am b/c/src/lib/libbsp/arm/beagle/Makefile.am
index e37472373c..c9492f41a1 100644
--- a/c/src/lib/libbsp/arm/beagle/Makefile.am
+++ b/c/src/lib/libbsp/arm/beagle/Makefile.am
@@ -76,9 +76,15 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/spi/spi.c
 # GPIO
 librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/gpio/bbb-gpio.c
 
+#pwmss shared
+librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/pwmss/pwmss.c
+
 #pwm
 librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/pwm/pwm.c
 
+#qep
+librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/qep/qep.c
+
 #RTC
 librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/beagle/rtc/rtc.c
 librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/rtc-support.c
diff --git a/spec/build/bsps/arm/beagle/obj.yml b/spec/build/bsps/arm/beagle/obj.yml
index 87c0ad873d..070dd94bf6 100644
--- a/spec/build/bsps/arm/beagle/obj.yml
+++ b/spec/build/bsps/arm/beagle/obj.yml
@@ -19,7 +19,8 @@ install:
   - bsps/arm/beagle/include/bsp/beagleboneblack.h
   - bsps/arm/beagle/include/bsp/i2c.h
   - bsps/arm/beagle/include/bsp/irq.h
-  - bsps/arm/beagle/include/bsp/spi.h
+  - bsps/arm/beagle/include/bsp/pwmss.h
+  - bsps/arm/beagle/include/bsp/qep.h
 - destination: ${BSP_LIBDIR}
   source:
   - bsps/arm/beagle/start/linkcmds
@@ -31,6 +32,8 @@ source:
 - bsps/arm/beagle/i2c/bbb-i2c.c
 - bsps/arm/beagle/irq/irq.c
 - bsps/arm/beagle/pwm/pwm.c
+- bsps/arm/beagle/pwmss/pwmss.c
+- bsps/arm/beagle/qep/qep.c
 - bsps/arm/beagle/rtc/rtc.c
 - bsps/arm/beagle/spi/spi.c
 - bsps/arm/beagle/start/bspdebug.c
-- 
2.25.1



More information about the devel mailing list