text: /*
   * 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.
   */
  
  #include <assert.h>
  #include <string.h>
  #include <errno.h>
  #include "os/mynewt.h"
  #include <bsp.h>
  #include <hal/hal_bsp.h>
  #include <pwm/pwm.h>
  #include <mcu/cmsis_nvic.h>
  
  /* Nordic headers */
  #include <nrfx.h>
  #include <nrfx_pwm.h>
  #include <nrf_pwm.h>
  
  /* Mynewt Nordic driver */
  #include "pwm_nrf52/pwm_nrf52.h"
  
  /* Max number on PWM instances on existing nRF52xxx MCUs */
  #define NRF52_PWM_MAX_INSTANCES     4
  
  struct nrf52_pwm_dev_global {
      bool in_use;
      bool playing;
      nrfx_pwm_t drv_instance;
      nrfx_pwm_config_t config;
      nrf_pwm_values_individual_t duty_cycles;
      uint32_t n_cycles;
      nrfx_pwm_flag_t flags;
      nrfx_pwm_handler_t internal_handler;
      user_handler_t cycle_handler;
      user_handler_t seq_end_handler;
      void* cycle_data;
      void* seq_end_data;
  };
  
  static struct nrf52_pwm_dev_global instances[] =
  {
  #if MYNEWT_VAL(PWM_0)
      [0].in_use = false,
      [0].playing = false,
      [0].drv_instance = NRFX_PWM_INSTANCE(0),
      [0].config = NRFX_PWM_DEFAULT_CONFIG(NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED),
      [0].flags = NRFX_PWM_FLAG_LOOP,
      [0].duty_cycles = {0},
      [0].n_cycles = 1,
      [0].internal_handler = NULL,
      [0].cycle_handler = NULL,
      [0].cycle_data = NULL,
      [0].seq_end_handler = NULL,
      [0].seq_end_data = NULL,
  #endif
  #if MYNEWT_VAL(PWM_1)
      [1].in_use = false,
      [1].playing = false,
      [1].drv_instance = NRFX_PWM_INSTANCE(1),
      [1].config = NRFX_PWM_DEFAULT_CONFIG(NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED),
      [1].flags = NRFX_PWM_FLAG_LOOP,
      [1].duty_cycles = {0},
      [1].n_cycles = 1,
      [1].internal_handler = NULL,
      [1].cycle_handler = NULL,
      [1].cycle_data = NULL,
      [1].seq_end_handler = NULL,
      [1].seq_end_data = NULL,
  #endif
  #if MYNEWT_VAL(PWM_2)
      [2].in_use = false,
      [2].playing = false,
      [2].drv_instance = NRFX_PWM_INSTANCE(2),
      [2].config = NRFX_PWM_DEFAULT_CONFIG(NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED),
      [2].flags = NRFX_PWM_FLAG_LOOP,
      [2].duty_cycles = {0},
      [2].n_cycles = 1,
      [2].internal_handler = NULL,
      [2].cycle_handler = NULL,
      [2].cycle_data = NULL,
      [2].seq_end_handler = NULL,
      [2].seq_end_data = NULL,
  #endif
  #if MYNEWT_VAL(PWM_3)
      [3].in_use = false,
      [3].playing = false,
      [3].drv_instance = NRFX_PWM_INSTANCE(3),
      [3].config = NRFX_PWM_DEFAULT_CONFIG(NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED,
                                           NRFX_PWM_PIN_NOT_USED),
      [3].flags = NRFX_PWM_FLAG_LOOP,
      [3].duty_cycles = {0},
      [3].n_cycles = 1,
      [3].internal_handler = NULL,
      [3].cycle_handler = NULL,
      [3].cycle_data = NULL,
      [3].seq_end_handler = NULL,
      [3].seq_end_data = NULL,
  #endif
  };
  
  #if MYNEWT_VAL(PWM_0)
  static void
  handler_0(nrfx_pwm_evt_type_t event_type, void *unused)
  {
      switch (event_type) {
      case NRFX_PWM_EVT_END_SEQ0:
      case NRFX_PWM_EVT_END_SEQ1:
          instances[0].cycle_handler(instances[0].cycle_data);
          break;
  
      case NRFX_PWM_EVT_FINISHED:
          instances[0].seq_end_handler(instances[0].seq_end_data);
          break;
  
      case NRFX_PWM_EVT_STOPPED:
          break;
  
      default:
          assert(0);
      }
  }
  #endif
  
  #if MYNEWT_VAL(PWM_1)
  static void
  handler_1(nrfx_pwm_evt_type_t event_type, void *unused)
  {
      switch (event_type) {
      case NRFX_PWM_EVT_END_SEQ0:
      case NRFX_PWM_EVT_END_SEQ1:
          instances[1].cycle_handler(instances[1].cycle_data);
          break;
  
      case NRFX_PWM_EVT_FINISHED:
          instances[1].seq_end_handler(instances[1].seq_end_data);
          break;
  
      case NRFX_PWM_EVT_STOPPED:
          break;
  
      default:
          assert(0);
      }
  }
  #endif
  
  #if MYNEWT_VAL(PWM_2)
  static void
  handler_2(nrfx_pwm_evt_type_t event_type, void *unused)
  {
      switch (event_type) {
      case NRFX_PWM_EVT_END_SEQ0:
      case NRFX_PWM_EVT_END_SEQ1:
          instances[2].cycle_handler(instances[2].cycle_data);
          break;
  
      case NRFX_PWM_EVT_FINISHED:
          instances[2].seq_end_handler(instances[2].seq_end_data);
          break;
  
      case NRFX_PWM_EVT_STOPPED:
          break;
  
      default:
          assert(0);
      }
  
  }
  #endif
  
  #if MYNEWT_VAL(PWM_3)
  static void
  handler_3(nrfx_pwm_evt_type_t event_type, void *unused)
  {
      switch (event_type) {
      case NRFX_PWM_EVT_END_SEQ0:
      case NRFX_PWM_EVT_END_SEQ1:
          instances[3].cycle_handler(instances[3].cycle_data);
          break;
  
      case NRFX_PWM_EVT_FINISHED:
          instances[3].seq_end_handler(instances[3].seq_end_data);
          break;
  
      case NRFX_PWM_EVT_STOPPED:
          break;
  
      default:
          assert(0);
      }
  }
  #endif
  
  static nrfx_pwm_handler_t internal_handlers[] = {
  #if MYNEWT_VAL(PWM_0)
  handler_0,
  #else
  NULL,
  #endif
  #if MYNEWT_VAL(PWM_1)
  handler_1,
  #else
  NULL,
  #endif
  #if MYNEWT_VAL(PWM_2)
  handler_2,
  #else
  NULL,
  #endif
  #if MYNEWT_VAL(PWM_3)
  handler_3,
  #else
  NULL,
  #endif
  };
  
  /**
   * Initialize a driver instance.
   */
  static int
  init_instance(int inst_id, nrfx_pwm_config_t* init_conf)
  {
      nrfx_pwm_config_t *config;
  
      config = &instances[inst_id].config;
      if (!init_conf) {
          config->output_pins[0] = NRFX_PWM_PIN_NOT_USED;
          config->output_pins[1] = NRFX_PWM_PIN_NOT_USED;
          config->output_pins[2] = NRFX_PWM_PIN_NOT_USED;
          config->output_pins[3] = NRFX_PWM_PIN_NOT_USED;
          config->irq_priority = 3; /* APP_IRQ_PRIORITY_LOW */
          config->base_clock = NRF_PWM_CLK_1MHz;
          config->count_mode = NRF_PWM_MODE_UP;
          config->top_value = 10000;
          config->load_mode = NRF_PWM_LOAD_INDIVIDUAL;
          config->step_mode = NRF_PWM_STEP_AUTO;
          config->skip_gpio_cfg = false;
      } else {
          memcpy(config, init_conf, sizeof(nrfx_pwm_config_t));
      }
      return (0);
  }
  
  /**
   * Cleanup a driver instance.
   */
  static void
  cleanup_instance(int inst_id)
  {
      instances[inst_id].in_use = false;
      instances[inst_id].playing = false;
      instances[inst_id].internal_handler = NULL;
      instances[inst_id].cycle_handler = NULL;
      instances[inst_id].seq_end_handler = NULL;
      instances[inst_id].cycle_data = NULL;
      instances[inst_id].seq_end_data = NULL;
      memset((uint16_t *) &instances[inst_id].duty_cycles,
             0x00,
             4 * sizeof(uint16_t));
  }
  
  /**
   * Open the NRF52 PWM device
   *
   * This function locks the device for access from other tasks.
   *
   * @param odev The OS device to open
   * @param wait The time in MS to wait.  If 0 specified, returns immediately
   *             if resource unavailable.  If OS_WAIT_FOREVER specified, blocks
   *             until resource is available.
   * @param arg  Argument provided by higher layer to open, in this case
   *             it can be a nrf_drv_pwm_config_t, to override the default
   *             configuration.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  static int
  nrf52_pwm_open(struct os_dev *odev, uint32_t wait, void *arg)
  {
      struct pwm_dev *dev;
      int stat = 0;
      int inst_id;
      dev = (struct pwm_dev *) odev;
      inst_id = dev->pwm_instance_id;
  
      if (instances[inst_id].in_use) {
          return OS_EBUSY;
      }
      instances[inst_id].in_use = true;
  
      if (os_started()) {
          stat = os_mutex_pend(&dev->pwm_lock, wait);
          if (stat != OS_OK) {
              return (stat);
          }
      }
  
      if (odev->od_flags & OS_DEV_F_STATUS_OPEN) {
          if (os_started()) {
              os_mutex_release(&dev->pwm_lock);
          }
          stat = OS_EBUSY;
          return (stat);
      }
  
      stat = init_instance(inst_id, arg);
      if (stat) {
          return (stat);
      }
  
      return (0);
  }
  
  /**
   * Close the NRF52 PWM device.
   *
   * This function unlocks the device.
   *
   * @param odev The device to close.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  static int
  nrf52_pwm_close(struct os_dev *odev)
  {
      struct pwm_dev *dev;
      int inst_id;
  
      dev = (struct pwm_dev *) odev;
      inst_id = dev->pwm_instance_id;
  
      if (!instances[inst_id].in_use) {
          return OS_EINVAL;
      }
  
      if(instances[inst_id].playing) {
          nrfx_pwm_uninit(&instances[inst_id].drv_instance);
      }
  
      cleanup_instance(inst_id);
  
      if (os_started()) {
          os_mutex_release(&dev->pwm_lock);
      }
  
      return (0);
  }
  
  /**
   * Play using current configuration.
   */
  static void
  play_current_config(struct nrf52_pwm_dev_global *instance)
  {
      nrf_pwm_sequence_t const seq =
          {
              .values.p_individual = &instance->duty_cycles,
              .length              = 4,
              .repeats             = 0,
              .end_delay           = 0
          };
  
      nrfx_pwm_simple_playback(&instance->drv_instance,
                               &seq,
                               instance->n_cycles,
                               instance->flags);
  }
  
  /**
   * Configure a PWM device.
   *
   * @param dev The device to configure.
   * @param cfg Configuration data for this device. If NULL the device will be
   * given default configuration values.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  int
  nrf52_pwm_configure_device(struct pwm_dev *dev, struct pwm_dev_cfg *cfg)
  {
      int inst_id = dev->pwm_instance_id;
      struct nrf52_pwm_dev_global *instance = &instances[inst_id];
  
      instance->n_cycles = (cfg->n_cycles) ? cfg->n_cycles : 1;
  
      /* Configure Interrupts  */
      if (cfg->cycle_handler || cfg->seq_end_handler) {
          instance->config.irq_priority = cfg->int_prio;
          instance->internal_handler = internal_handlers[inst_id];
          instance->cycle_handler = (user_handler_t) cfg->cycle_handler;
          instance->seq_end_handler = (user_handler_t) cfg->seq_end_handler;
          instance->cycle_data = cfg->cycle_data;
          instance->seq_end_data = cfg->seq_end_data;
      } else {
          instance->internal_handler = NULL;
      }
  
      instance->flags = (instance->n_cycles > 1) ?
          0 :
          NRFX_PWM_FLAG_LOOP;
      instance->flags |= (instance->cycle_handler) ?
          (NRFX_PWM_FLAG_SIGNAL_END_SEQ0 | NRFX_PWM_FLAG_SIGNAL_END_SEQ0) :
          0;
      instance->flags |= (instance->seq_end_handler) ?
          0 :
          NRFX_PWM_FLAG_NO_EVT_FINISHED;
  
      if (instance->playing) {
          nrfx_pwm_uninit(&instance->drv_instance);
          nrfx_pwm_init(&instance->drv_instance,
                        &instance->config,
                        instance->internal_handler,
                        NULL);
  
          play_current_config(instance);
      }
      return (0);
  }
  
  /**
   * Configure a channel on the PWM device.
   *
   * @param dev The device to configure.
   * @param cnum The channel number to configure.
   * @param cfg  A pwm_chan_cfg data structure, where .
   *
   * @return 0 on success, non-zero error code on failure.
   */
  static int
  nrf52_pwm_configure_channel(struct pwm_dev *dev,
                              uint8_t cnum,
                              struct pwm_chan_cfg *cfg)
  {
      int inst_id = dev->pwm_instance_id;
      struct nrf52_pwm_dev_global *instance = &instances[inst_id];
      nrfx_pwm_config_t *config = &instance->config;
  
      if (!instance->in_use) {
          return (EINVAL);
      }
  
      config->output_pins[cnum] = (uint8_t) cfg->pin;
      config->output_pins[cnum] |= (cfg->inverted) ?
          NRFX_PWM_PIN_INVERTED : config->output_pins[cnum];
  
      if (instance->playing) {
          nrfx_pwm_uninit(&instance->drv_instance);
          nrfx_pwm_init(&instance->drv_instance,
                        &instance->config,
                        instance->internal_handler,
                        NULL);
  
          play_current_config(instance);
      }
      return (0);
  }
  
  /**
   * Set the specified duty cycle on a PWM Channel.
   *
   * This duty cycle is a fractional duty cycle where 0 == off,
   * base_freq / pwm_freq == 100% and any value in between is on for fraction clock
   * cycles and off for (base_freq / pwm_freq)-fraction clock cycles.
   *
   * @param dev The device which the channel belongs to.
   * @param cnum The channel number. This channel should be already configured.
   * @param fraction The fraction value.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  static int
  nrf52_pwm_set_duty_cycle(struct pwm_dev *dev, uint8_t cnum, uint16_t fraction)
  {
      int inst_id = dev->pwm_instance_id;
      nrfx_pwm_config_t *config;
      bool inverted;
  
      if (!instances[inst_id].in_use) {
          return (EINVAL);
      }
  
      config = &instances[inst_id].config;
      if (config->output_pins[cnum] == NRFX_PWM_PIN_NOT_USED) {
          return (EINVAL);
      }
  
      inverted = ((config->output_pins[cnum] & NRFX_PWM_PIN_INVERTED) != 0);
  
      ((uint16_t *) &instances[inst_id].duty_cycles)[cnum] =
          (inverted) ? fraction : config->top_value - fraction;
  
      return (0);
  }
  
  /**
   * Enable a given PWM device.
   * This device should start playing on its previously configured channels.
   *
   * @param dev The PWM device to be enabled.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  int
  nrf52_pwm_enable(struct pwm_dev *dev)
  {
      int inst_id = dev->pwm_instance_id;
      struct nrf52_pwm_dev_global *instance = &instances[inst_id];
  
      nrfx_pwm_init(&instance->drv_instance,
                    &instance->config,
                    instance->internal_handler,
                    NULL);
      play_current_config(instance);
      instance->playing = true;
  
      return (0);
  }
  
  /**
   * Check whether a PWM channel is enabled on a given device.
   *
   * @param dev The device which the channel belongs to.
   * @param cnum The channel being queried.
   *
   * @return true if enabled, false if not.
   */
  static bool
  nrf52_pwm_is_enabled(struct pwm_dev *dev)
  {
      return (instances[dev->pwm_instance_id].playing);
  }
  
  /**
   * Disable the PWM device, the device will stop playing while remaining configured.
   *
   * @param dev The device to disable.
   *
   * @return 0 on success, non-zero error code on failure.
   */
  static int
  nrf52_pwm_disable(struct pwm_dev *dev)
  {
      int inst_id = dev->pwm_instance_id;
      if (!instances[inst_id].in_use) {
          return (EINVAL);
      }
  
      if (!instances[inst_id].playing) {
          return (EINVAL);
      }
  
      nrfx_pwm_uninit(&instances[inst_id].drv_instance);
      instances[inst_id].playing = false;
  
      return (0);
  }
  
  /**
   * Set the frequency for the device's clock.
   * This frequency must be between 1/2 the clock frequency and
   * the clock divided by the resolution. NOTE: This will affect
   * all PWM channels belonging to the device.
   *
   * @param dev The device to configure.
   * @param freq_hz The frequency value in Hz.
   *
   * @return A value is in Hz on success, negative error code on failure.
   */
  static int
  nrf52_pwm_set_frequency(struct pwm_dev *dev, uint32_t freq_hz)
  {
      int inst_id = dev->pwm_instance_id;
      if (!instances[inst_id].in_use) {
          return (-EINVAL);
      }
  
      nrf_pwm_clk_t *frq = &instances[inst_id].config.base_clock;
      uint16_t *top_value = &instances[inst_id].config.top_value;
      uint32_t base_freq_val;
  
      freq_hz = (freq_hz > 7999999) ? 7999999 : freq_hz;
      freq_hz = (freq_hz < 3) ? 3 : freq_hz;
  
      if (freq_hz > 488) {
          *frq = NRF_PWM_CLK_16MHz;
          base_freq_val = 16000000;
      } else if (freq_hz > 244) {
          *frq = NRF_PWM_CLK_8MHz;
          base_freq_val = 8000000;
      } else if (freq_hz > 122) {
          *frq = NRF_PWM_CLK_4MHz;
          base_freq_val = 4000000;
      } else if (freq_hz > 61) {
          *frq = NRF_PWM_CLK_2MHz;
          base_freq_val = 2000000;
      } else if (freq_hz > 30) {
          *frq = NRF_PWM_CLK_1MHz;
          base_freq_val = 1000000;
      } else if (freq_hz > 14) {
          *frq = NRF_PWM_CLK_500kHz;
          base_freq_val = 500000;
      } else if (freq_hz > 6) {
          *frq = NRF_PWM_CLK_250kHz;
          base_freq_val = 250000;
      } else if (freq_hz > 2) {
          *frq = NRF_PWM_CLK_125kHz;
          base_freq_val = 125000;
      }
  
      *top_value = base_freq_val / freq_hz;
  
      if (instances[inst_id].playing) {
          nrfx_pwm_uninit(&instances[inst_id].drv_instance);
          nrfx_pwm_init(&instances[inst_id].drv_instance,
                        &instances[inst_id].config,
                        instances[inst_id].internal_handler,
                        NULL);
  
          play_current_config(&instances[inst_id]);
      }
  
      return base_freq_val;
  }
  
  /**
   * Get the underlying clock driving the PWM device.
   *
   * @param dev
   *
   * @return value is in Hz on success, error code on failure.
   */
  static int
  nrf52_pwm_get_clock_freq(struct pwm_dev *dev)
  {
      int inst_id = dev->pwm_instance_id;
      if (!instances[inst_id].in_use) {
          return (-EINVAL);
      }
  
      switch (instances[inst_id].config.base_clock) {
      case NRF_PWM_CLK_16MHz:
          return (16000000);
      case NRF_PWM_CLK_8MHz:
          return (8000000);
      case NRF_PWM_CLK_4MHz:
          return (4000000);
      case NRF_PWM_CLK_2MHz:
          return (2000000);
      case NRF_PWM_CLK_1MHz:
          return (1000000);
      case NRF_PWM_CLK_500kHz:
          return (500000);
      case NRF_PWM_CLK_250kHz:
          return (250000);
      case NRF_PWM_CLK_125kHz:
          return (125000);
      }
      return (-EINVAL);
  }
  
  /**
   * Get the top value for the cycle counter, i.e. the value which sets
   * the duty cycle to 100%.
   *
   * @param dev
   *
   * @return value in cycles on success, negative error code on failure.
   */
  int
  nrf52_pwm_get_top_value(struct pwm_dev *dev)
  {
      int inst_id = dev->pwm_instance_id;
      if (!instances[inst_id].in_use) {
          return (-EINVAL);
      }
  
      return (instances[inst_id].config.top_value);
  }
  
  /**
   * Get the resolution of the PWM in bits.
   *
   * @param dev The device to query.
   *
   * @return The value in bits on success, negative error code on failure.
   */
  static int
  nrf52_pwm_get_resolution_bits(struct pwm_dev *dev)
  {
      int inst_id = dev->pwm_instance_id;
      if (!instances[inst_id].in_use) {
          return (-EINVAL);
      }
  
      uint16_t top_val = instances[inst_id].config.top_value;
      if (top_val >= 32768)
      {
          return (15);
      } else if (top_val >= 16384) {
          return (14);
      } else if (top_val >= 8192) {
          return (13);
      } else if (top_val >= 4096) {
          return (12);
      } else if (top_val >= 2048) {
          return (11);
      } else if (top_val >= 1024) {
          return (10);
      } else if (top_val >= 512) {
          return (9);
      } else if (top_val >= 256) {
          return (8);
      } else if (top_val >= 128) {
          return (7);
      } else if (top_val >= 64) {
          return (6);
      } else if (top_val >= 32) {
          return (5);
      } else if (top_val >= 16) {
          return (4);
      } else if (top_val >= 8) {
          return (3);
      } else if (top_val >= 4) {
          return (2);
      } else if (top_val >= 2) {
          return (1);
      }
  
      return (-EINVAL);
  }
  
  #if MYNEWT_VAL(OS_SYSVIEW)
  #if MYNEWT_VAL(PWM_0)
  static void
  pwm_0_irq_handler(void)
  {
      os_trace_isr_enter();
      nrfx_pwm_0_irq_handler();
      os_trace_isr_exit();
  }
  #endif
  
  #if MYNEWT_VAL(PWM_1)
  static void
  pwm_1_irq_handler(void)
  {
      os_trace_isr_enter();
      nrfx_pwm_1_irq_handler();
      os_trace_isr_exit();
  }
  #endif
  
  #if MYNEWT_VAL(PWM_2)
  static void
  pwm_2_irq_handler(void)
  {
      os_trace_isr_enter();
      nrfx_pwm_2_irq_handler();
      os_trace_isr_exit();
  }
  #endif
  
  #if MYNEWT_VAL(PWM_3)
  static void
  pwm_3_irq_handler(void)
  {
      os_trace_isr_enter();
      nrfx_pwm_3_irq_handler();
      os_trace_isr_exit();
  }
  #endif
  
  #define PWM_IRQ_HANDLER(_pwm_no) \
                              (uint32_t) pwm_ ## _pwm_no ## _irq_handler
  #else
  #define PWM_IRQ_HANDLER(_pwm_no) \
                              (uint32_t) nrfx_pwm_ ## _pwm_no ## _irq_handler
  #endif
  
  /**
   * Callback to initialize an adc_dev structure from the os device
   * initialization callback.  This sets up a nrf52_pwm_device(), so
   * that subsequent lookups to this device allow us to manipulate it.
   */
  int
  nrf52_pwm_dev_init(struct os_dev *odev, void *arg)
  {
      struct pwm_dev *dev;
      struct pwm_driver_funcs *pwm_funcs;
      IRQn_Type irqn;
      uint32_t irqh;
  
      assert(odev);
      dev = (struct pwm_dev *) odev;
  
      /*
       * Originally arg was expected to be a pointer to int with pwm_instance_id,
       * however this wastes sizeof(int) of memory only to keep a simple number
       * which is used only once. Instead, instance_id can be passed as int casted
       * to pointer using helper macro so let's handle both versions here:
       * - if number is valid instance_id, let's use it directly
       * - otherwise assume it's a valid pointer
       */
      if (POINTER_TO_UINT(arg) < NRF52_PWM_MAX_INSTANCES) {
          dev->pwm_instance_id = POINTER_TO_UINT(arg);
      } else {
          dev->pwm_instance_id = *((int*) arg);
          assert(dev->pwm_instance_id < NRF52_PWM_MAX_INSTANCES);
      }
  
      dev->pwm_chan_count = NRF_PWM_CHANNEL_COUNT;
      os_mutex_init(&dev->pwm_lock);
  
      OS_DEV_SETHANDLERS(odev, nrf52_pwm_open, nrf52_pwm_close);
  
      pwm_funcs = &dev->pwm_funcs;
      pwm_funcs->pwm_configure_device = nrf52_pwm_configure_device;
      pwm_funcs->pwm_configure_channel = nrf52_pwm_configure_channel;
      pwm_funcs->pwm_set_duty_cycle = nrf52_pwm_set_duty_cycle;
      pwm_funcs->pwm_enable = nrf52_pwm_enable;
      pwm_funcs->pwm_is_enabled = nrf52_pwm_is_enabled;
      pwm_funcs->pwm_set_frequency = nrf52_pwm_set_frequency;
      pwm_funcs->pwm_get_clock_freq = nrf52_pwm_get_clock_freq;
      pwm_funcs->pwm_get_top_value = nrf52_pwm_get_top_value;
      pwm_funcs->pwm_get_resolution_bits = nrf52_pwm_get_resolution_bits;
      pwm_funcs->pwm_disable = nrf52_pwm_disable;
  
      switch (dev->pwm_instance_id) {
  #if MYNEWT_VAL(PWM_0)
      case 0:
          irqn = PWM0_IRQn;
          irqh = PWM_IRQ_HANDLER(0);
          break;
  #endif
  
  #if MYNEWT_VAL(PWM_1)
      case 1:
          irqn = PWM1_IRQn;
          irqh = PWM_IRQ_HANDLER(1);
          break;
  #endif
  
  #if MYNEWT_VAL(PWM_2)
      case 2:
          irqn = PWM2_IRQn;
          irqh = PWM_IRQ_HANDLER(2);
          break;
  #endif
  
  #if MYNEWT_VAL(PWM_3)
      case 3:
          irqn = PWM3_IRQn;
          irqh = PWM_IRQ_HANDLER(3);
          break;
  #endif
      default:
          assert(0);
          return 0;
      }
  
      NVIC_SetVector(irqn, irqh);
  
      return (0);
  }
  