// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2011 Adam Lackorzynski <adam@os.inf.tu-dresden.de>
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */
#pragma once

#include <l4/vbus/vbus>
#include <l4/vbus/vbus_gpio.h>

/**
 * \addtogroup api_l4re_vbus
 *
 * \includefile{l4/vbus/vbus_gpio}
 */

namespace L4vbus {

/**
 * \brief A GPIO pin
 * \ingroup api_l4re_vbus
 *
 */
class Gpio_pin : public Device
{
public:
  Gpio_pin(Device const &dev, unsigned pin)
  : Device(dev), _pin(pin)
  {}

  /**
   * \brief Read value of GPIO input pin
   *
   * \return  Value of GPIO pin (usually 0 or 1), negative error code otherwise.
   */
  int get() const
  {
    return l4vbus_gpio_get(_bus.cap(), _dev, _pin);
  }

  /**
   * \brief Set GPIO output pin
   *
   * \param value   Value to write to the GPIO pin (usually 0 or 1)
   * \return        0 if OK, error code otherwise
   */
  int set(int value) const
  {
    return l4vbus_gpio_set(_bus.cap(), _dev, _pin, value);
  }

  /**
   * \brief Configure the function of a GPIO pin
   *
   * \param mode      GPIO function, see #L4vbus_gpio_generic_func for generic
   *                  functions. Hardware specific functions must be provided in
   *                  the lower 8 bits.
   * \param value     Optional value to set the GPIO pin to if it is configured
   *                  as an output pin
   * \return          0 if OK, error code otherwise
   */
  int setup(unsigned mode, unsigned value) const
  {
    return l4vbus_gpio_setup(_bus.cap(), _dev, _pin, mode, value);
  }

  /**
   * \brief Generic function to set pull up/down mode
   *
   * \param mode    mode for pull up/down resistors, see #L4vbus_gpio_pull_modes
   * \return        0 if OK, error code otherwise
   */
  int config_pull(unsigned mode) const
  {
    return l4vbus_gpio_config_pull(_bus.cap(), _dev, _pin, mode);
  }

  /**
   * \brief Hardware specific configuration function
   *
   * \param func    Hardware specific configuration register, usually offset to
   *                the GPIO chip's base address
   * \param value   Value which is written into the hardware specific
   *                configuration register for the specified pin
   * \return        0 if OK, error code otherwise
   */
  int config_pad(unsigned func, unsigned value) const
  {
    return l4vbus_gpio_config_pad(_bus.cap(), _dev, _pin, func, value);
  }

  /**
   * \brief Read hardware specific configuration
   *
   * \param func    Hardware specific configuration register to read from.
   *                Usually this is an offset to the GPIO chip's base address.
   * \param[out] value  The configuration value.
   * \return        0 if OK, error code otherwise
   */
  int config_get(unsigned func, unsigned *value) const
  {
    return l4vbus_gpio_config_get(_bus.cap(), _dev, _pin, func, value);
  }

  /**
   * \brief Create IRQ for GPIO pin
   *
   * \return   IRQ number if OK, negative error code otherwise
   */
  int to_irq() const
  {
    return l4vbus_gpio_to_irq(_bus.cap(), _dev, _pin);
  }

  /**
   * \brief Get pin number
   *
   * \return GPIO pin number
   */
  unsigned pin() const { return _pin; }

protected:
  Gpio_pin() {}
  unsigned _pin;
};

/**
 * \brief A Gpio_module groups multiple GPIO pins together
 * \ingroup api_l4re_vbus
 */
class Gpio_module : public Device
{
public:
  Gpio_module(Device dev)
  : Device(dev)
  {}

  /**
   * \brief A slice of the pins provided by this module.
   *
   * Data type to specify a selection of pins for the 'multi'
   * methods.
   */
  struct Pin_slice
  {
    Pin_slice(unsigned offset, unsigned mask) : offset(offset), mask(mask) {}
    unsigned offset, mask;
  };

  /**
   * \brief Configure function of multiple GPIO pins at once
   *
   * \param mask    Mask of GPIO pins to configure. A bit set to 1 configures
   *                this pin. A maximum of 32 pins can be configured at once.
   *                The real number depends on the hardware and the driver
   *                implementation.
   * \param mode    GPIO function, see #L4vbus_gpio_generic_func for generic
   *                functions. Hardware specific functions must be provided in
   *                the lower 8 bits.
   * \param value   Optional value to set the GPIO pins to if they are
   *                configured as output pins
   * \return        0 if OK, error code otherwise
   */
  int setup(Pin_slice const &mask, unsigned mode, unsigned value) const
  {
    return l4vbus_gpio_multi_setup(_bus.cap(), _dev, mask.offset, mask.mask,
                                   mode, value);
  }

  /**
   * \brief Hardware specific configuration function for multiple GPIO pins
   *
   * \param mask    Mask of GPIO pins to configure. A bit set to 1 configures
   *                this pin. A maximum of 32 pins can be configured at once.
   *                The real number depends on the hardware and the driver
   *                implementation.
   * \param func    Hardware specific configuration register, usually offset to
   *                the GPIO chip's base address.
   * \param value   Value which is written into the hardware specific
   *                configuration register for the specified pins
   * \return        0 if OK, error code otherwise
   */
  int config_pad(Pin_slice const &mask, unsigned func, unsigned value) const
  {
    return l4vbus_gpio_multi_config_pad(_bus.cap(), _dev, mask.offset,
                                        mask.mask, func, value);
  }

  /**
   * \brief Read values of multiple GPIO pins at once
   *
   * \param offset      Pin corresponding to the LSB in \a data.
   *                    Note: allowed may be hardware specific.
   * \param[out] data   Each bit returns the value (0 or 1) for the corresponding
   *                    GPIO pin. The value of pins that are not accessible is
   *                    undefined.
   * \return            0 if OK, error code otherwise
   */
  int get(unsigned offset, unsigned *data) const
  {
    return l4vbus_gpio_multi_get(_bus.cap(), _dev, offset, data);
  }

  /**
   * \brief Set multiple GPIO output pins at once
   *
   * \param mask    Mask of GPIO pins to set. A bit set to 1 selects this pin.
   *                A maximum of 32 pins can be set at once. The real number
   *                depends on the hardware and the driver implementation.
   * \param data    Each bit corresponds to the GPIO pin in \a mask. The value
   *                of each bit is written to the GPIO pin if its bit in \a mask
   *                is set.
   * \return        0 if OK, error code otherwise
   */
  int set(Pin_slice const &mask, unsigned data)
  {
    return l4vbus_gpio_multi_set(_bus.cap(), _dev, mask.offset,
                                 mask.mask, data);
  }

  /**
   * \brief Get Gpio_pin for a specific pin of this Gpio_module
   *
   * \param pin GPIO pin number to get Gpio_pin for.
   * \return    Gpio_pin
   */
  Gpio_pin pin(unsigned pin) const
  {
    return Gpio_pin(*this, pin);
  }

protected:
  Gpio_module() {}
};

}
