Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

pmic_convity.c

Go to the documentation of this file.
/*
 * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file mc13783/pmic_convity.c
 * @brief Implementation of the PMIC Connectivity driver APIs.
 *
 * The PMIC connectivity device driver and this API were developed to support
 * the external connectivity capabilities of several power management ICs that
 * are available from Freescale Semiconductor, Inc.
 *
 * The following operating modes, in terms of external connectivity, are
 * supported:
 *
 * @verbatim
       Operating Mode     mc13783
       ---------------    -------
       USB (incl. OTG)     Yes
       RS-232              Yes
       CEA-936             Yes

   @endverbatim
 *
 * @ingroup PMIC_CONNECTIVITY
 */

#include <linux/interrupt.h>  /* For tasklet interface.                  */
#include <linux/platform_device.h>  /* For kernel module interface.            */
#include <linux/spinlock.h>   /* For spinlock interface.                 */
#include <linux/pmic_adc.h>   /* For PMIC ADC driver interface.          */
#include <linux/pmic_status.h>
#include <mach/pmic_convity.h>      /* For PMIC Connectivity driver interface. */

/*
 * mc13783 Connectivity API
 */
/* EXPORTED FUNCTIONS */
EXPORT_SYMBOL(pmic_convity_open);
EXPORT_SYMBOL(pmic_convity_close);
EXPORT_SYMBOL(pmic_convity_set_mode);
EXPORT_SYMBOL(pmic_convity_get_mode);
EXPORT_SYMBOL(pmic_convity_reset);
EXPORT_SYMBOL(pmic_convity_set_callback);
EXPORT_SYMBOL(pmic_convity_clear_callback);
EXPORT_SYMBOL(pmic_convity_get_callback);
EXPORT_SYMBOL(pmic_convity_usb_set_speed);
EXPORT_SYMBOL(pmic_convity_usb_get_speed);
EXPORT_SYMBOL(pmic_convity_usb_set_power_source);
EXPORT_SYMBOL(pmic_convity_usb_get_power_source);
EXPORT_SYMBOL(pmic_convity_usb_set_xcvr);
EXPORT_SYMBOL(pmic_convity_usb_get_xcvr);
EXPORT_SYMBOL(pmic_convity_usb_otg_set_dlp_duration);
EXPORT_SYMBOL(pmic_convity_usb_otg_get_dlp_duration);
EXPORT_SYMBOL(pmic_convity_usb_otg_set_config);
EXPORT_SYMBOL(pmic_convity_usb_otg_clear_config);
EXPORT_SYMBOL(pmic_convity_usb_otg_get_config);
EXPORT_SYMBOL(pmic_convity_set_output);
EXPORT_SYMBOL(pmic_convity_rs232_set_config);
EXPORT_SYMBOL(pmic_convity_rs232_get_config);
EXPORT_SYMBOL(pmic_convity_cea936_exit_signal);

/*! @def SET_BITS
 * Set a register field to a given value.
 */

00076 #define SET_BITS(reg, field, value)    (((value) << reg.field.offset) & \
                                        reg.field.mask)

/*! @def GET_BITS
 * Get the current value of a given register field.
 */
00082 #define GET_BITS(reg, value)    (((value) & reg.mask) >> \
                                 reg.offset)

/*!
 * @brief Define the possible states for a device handle.
 *
 * This enumeration is used to track the current state of each device handle.
 */
00090 typedef enum {
00091       HANDLE_FREE,            /*!< Handle is available for use. */
00092       HANDLE_IN_USE           /*!< Handle is currently in use.  */
} HANDLE_STATE;

/*
 * This structure is used to define a specific hardware register field.
 *
 * All hardware register fields are defined using an offset to the LSB
 * and a mask. The offset is used to right shift a register value before
 * applying the mask to actually obtain the value of the field.
 */
typedef struct {
      const unsigned char offset;   /* Offset of LSB of register field.           */
      const unsigned int mask;      /* Mask value used to isolate register field. */
} REGFIELD;

/*!
 * @brief This structure is used to identify the fields in the USBCNTRL_REG_0 hardware register.
 *
 * This structure lists all of the fields within the USBCNTRL_REG_0 hardware
 * register.
 */
00113 typedef struct {
00114       REGFIELD FSENB;         /*!< USB Full Speed Enable                            */
00115       REGFIELD USB_SUSPEND;   /*!< USB Suspend Mode Enable                          */
00116       REGFIELD USB_PU;  /*!< USB Pullup Enable                                */
00117       REGFIELD UDP_PD;  /*!< USB Data Plus Pulldown Enable                    */
00118       REGFIELD UDM_PD;  /*!< USB 150K UDP Pullup Enable                       */
00119       REGFIELD DP150K_PU;     /*!< USB Pullup/Pulldown Override Enable              */
00120       REGFIELD VBUSPDENB;     /*!< USB VBUS Pulldown NMOS Switch Enable             */
00121       REGFIELD CURRENT_LIMIT; /*!< USB Regulator Current Limit Setting-3 bits       */
00122       REGFIELD DLP_SRP; /*!< USB Data Line Pulsing Timer Enable               */
00123       REGFIELD SE0_CONN;      /*!< USB Pullup Connect When SE0 Detected             */
00124       REGFIELD USBXCVREN;     /*!< USB Transceiver Enabled When INTERFACE_MODE[2:0]=000 and RESETB=high */
00125       REGFIELD PULLOVR; /*!< 1K5 Pullup and UDP/UDM Pulldown Disable When UTXENB=Low             */
00126       REGFIELD INTERFACE_MODE;      /*!< Connectivity Interface Mode Select-3 Bits        */
00127       REGFIELD DATSE0;  /*!< USB Single or Differential Mode Select           */
00128       REGFIELD BIDIR;         /*!< USB Unidirectional/Bidirectional Transmission    */
00129       REGFIELD USBCNTRL;      /*!< USB Mode of Operation controlled By USBEN/SPI Pin */
00130       REGFIELD IDPD;          /*!< USB UID Pulldown Enable                          */
00131       REGFIELD IDPULSE; /*!< USB Pulse to Gnd on UID Line Generated           */
00132       REGFIELD IDPUCNTRL;     /*!< USB UID Pin pulled high By 5ua Curr Source       */
00133       REGFIELD DMPULSE; /*!< USB Positive pulse on the UDM Line Generated     */
} USBCNTRL_REG_0;

/*!
 * @brief This variable is used to access the USBCNTRL_REG_0 hardware register.
 *
 * This variable defines how to access all of the fields within the
 * USBCNTRL_REG_0  hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
00143 static const USBCNTRL_REG_0 regUSB0 = {
      {0, 0x000001},          /*!< FSENB        */
      {1, 0x000002},          /*!< USB_SUSPEND  */
      {2, 0x000004},          /*!< USB_PU       */
      {3, 0x000008},          /*!< UDP_PD       */
      {4, 0x000010},          /*!< UDM_PD       */
      {5, 0x000020},          /*!< DP150K_PU    */
      {6, 0x000040},          /*!< VBUSPDENB    */
      {7, 0x000380},          /*!< CURRENT_LIMIT */
      {10, 0x000400},         /*!< DLP_SRP      */
      {11, 0x000800},         /*!< SE0_CONN     */
      {12, 0x001000},         /*!< USBXCVREN    */
      {13, 0x002000},         /*!< PULLOVR      */
      {14, 0x01c000},         /*!< INTERFACE_MODE */
      {17, 0x020000},         /*!< DATSE0       */
      {18, 0x040000},         /*!< BIDIR        */
      {19, 0x080000},         /*!< USBCNTRL     */
      {20, 0x100000},         /*!< IDPD         */
      {21, 0x200000},         /*!< IDPULSE      */
      {22, 0x400000},         /*!< IDPUCNTRL    */
      {23, 0x800000}          /*!< DMPULSE      */

};

/*!
 * @brief This structure is used to identify the fields in the USBCNTRL_REG_1 hardware register.
 *
 * This structure lists all of the fields within the USBCNTRL_REG_1 hardware
 * register.
 */
00173 typedef struct {
00174       REGFIELD VUSBIN;  /*!< Controls The Input Source For VUSB             */
00175       REGFIELD VUSB;          /*!< VUSB Output Voltage Select-High=3.3V Low=2.775V */
00176       REGFIELD VUSBEN;  /*!< VUSB Output Enable-                            */
00177       REGFIELD VBUSEN;  /*!< VBUS Output Enable-                            */
00178       REGFIELD RSPOL;         /*!< Low=RS232 TX on UDM, RX on UDP
                           High= RS232 TX on UDP, RX on UDM               */
00180       REGFIELD RSTRI;         /*!< TX Forced To Tristate in RS232 Mode Only       */
00181       REGFIELD ID100kPU;      /*!< 100k UID Pullup Enabled                        */
} USBCNTRL_REG_1;

/*!
 * @brief This variable is used to access the USBCNTRL_REG_1 hardware register.
 *
 * This variable defines how to access all of the fields within the
 * USBCNTRL_REG_1  hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
00191 static const USBCNTRL_REG_1 regUSB1 = {
      {0, 0x000003},          /*!< VUSBIN-2 Bits  */
      {2, 0x000004},          /*!< VUSB           */
      {3, 0x000008},          /*!< VUSBEN         */
      /*{4, 0x000010} *//*!< Reserved       */
      {5, 0x000020},          /*!< VBUSEN         */
      {6, 0x000040},          /*!< RSPOL          */
      {7, 0x000080},          /*!< RSTRI          */
      {8, 0x000100}           /*!< ID100kPU       */
      /*!< 9-23 Unused    */
};

/*! Define a mask to access the entire hardware register. */
00204 static const unsigned int REG_FULLMASK = 0xffffff;

/*! Define the mc13783 USBCNTRL_REG_0 register power on reset state. */
00207 static const unsigned int RESET_USBCNTRL_REG_0 = 0x080060;

/*! Define the mc13783 USBCNTRL_REG_1 register power on reset state. */
00210 static const unsigned int RESET_USBCNTRL_REG_1 = 0x000006;

static pmic_event_callback_t eventNotify;

/*!
 * @brief This structure is used to maintain the current device driver state.
 *
 * This structure maintains the current state of the connectivity driver. This
 * includes both the PMIC hardware state as well as the device handle and
 * callback states.
 */

00222 typedef struct {
00223       PMIC_CONVITY_HANDLE handle;   /*!< Device handle.   */
00224       HANDLE_STATE handle_state;    /*!< Device handle
                                 state.           */
00226       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
00227       PMIC_CONVITY_CALLBACK callback;     /*!< Event callback function pointer. */
00228       PMIC_CONVITY_EVENTS eventMask;      /*!< Event mask.      */
00229       PMIC_CONVITY_USB_SPEED usbSpeed;    /*!< USB connection
                                       speed.           */
00231       PMIC_CONVITY_USB_MODE usbMode;      /*!< USB connection
                                 mode.            */
00233       PMIC_CONVITY_USB_POWER_IN usbPowerIn;     /*!< USB transceiver
                                       power source.    */
00235       PMIC_CONVITY_USB_POWER_OUT usbPowerOut;   /*!< USB transceiver
                                       power output
                                       level.           */
00238       PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode;  /*!< USB transceiver
                                             mode.            */
00240       unsigned int usbDlpDuration;  /*!< USB Data Line
                                 Pulsing duration. */
00242       PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg;    /*!< USB OTG
                                       configuration
                                       options.         */
00245       PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal;   /*!< RS-232 internal
                                             connections.     */
00247       PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal;   /*!< RS-232 external
                                             connections.     */
} pmic_convity_state_struct;

/*!
 * @brief This structure is used to maintain the current device driver state.
 *
 * This structure maintains the current state of the driver in USB mode. This
 * includes both the PMIC hardware state as well as the device handle and
 * callback states.
 */

00259 typedef struct {
00260       PMIC_CONVITY_HANDLE handle;   /*!< Device handle.   */
00261       HANDLE_STATE handle_state;    /*!< Device handle
                                 state.           */
00263       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
00264       PMIC_CONVITY_CALLBACK callback;     /*!< Event callback function pointer. */
00265       PMIC_CONVITY_EVENTS eventMask;      /*!< Event mask.      */
00266       PMIC_CONVITY_USB_SPEED usbSpeed;    /*!< USB connection
                                       speed.           */
00268       PMIC_CONVITY_USB_MODE usbMode;      /*!< USB connection
                                 mode.            */
00270       PMIC_CONVITY_USB_POWER_IN usbPowerIn;     /*!< USB transceiver
                                       power source.    */
00272       PMIC_CONVITY_USB_POWER_OUT usbPowerOut;   /*!< USB transceiver
                                       power output
                                       level.           */
00275       PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode;  /*!< USB transceiver
                                             mode.            */
00277       unsigned int usbDlpDuration;  /*!< USB Data Line
                                 Pulsing duration. */
00279       PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg;    /*!< USB OTG
                                       configuration
                                       options.         */
} pmic_convity_usb_state;

/*!
 * @brief This structure is used to maintain the current device driver state.
 *
 * This structure maintains the current state of the driver in RS_232 mode. This
 * includes both the PMIC hardware state as well as the device handle and
 * callback states.
 */

00292 typedef struct {
00293       PMIC_CONVITY_HANDLE handle;   /*!< Device handle.   */
00294       HANDLE_STATE handle_state;    /*!< Device handle
                                 state.           */
00296       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
00297       PMIC_CONVITY_CALLBACK callback;     /*!< Event callback function pointer. */
00298       PMIC_CONVITY_EVENTS eventMask;      /*!< Event mask.      */
00299       PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal;   /*!< RS-232 internal
                                             connections.     */
00301       PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal;   /*!< RS-232 external
                                             connections.     */
} pmic_convity_rs232_state;

/*!
 * @brief This structure is used to maintain the current device driver state.
 *
 * This structure maintains the current state of the driver in cea-936 mode. This
 * includes both the PMIC hardware state as well as the device handle and
 * callback states.
 */

00313 typedef struct {
00314       PMIC_CONVITY_HANDLE handle;   /*!< Device handle.   */
00315       HANDLE_STATE handle_state;    /*!< Device handle
                                 state.           */
00317       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
00318       PMIC_CONVITY_CALLBACK callback;     /*!< Event callback function pointer. */
00319       PMIC_CONVITY_EVENTS eventMask;      /*!< Event mask.      */

} pmic_convity_cea936_state;

/*!
 * @brief Identifies the hardware interrupt source.
 *
 * This enumeration identifies which of the possible hardware interrupt
 * sources actually caused the current interrupt handler to be called.
 */
00329 typedef enum {
00330       CORE_EVENT_4V4 = 1,     /*!< Detected USB 4.4 V event.              */
00331       CORE_EVENT_2V0 = 2,     /*!< Detected USB 2.0 V event.              */
00332       CORE_EVENT_0V8 = 4,     /*!< Detected USB 0.8 V event.              */
00333       CORE_EVENT_ABDET = 8    /*!< Detected USB mini A-B connector event. */
} PMIC_CORE_EVENT;

/*!
 * @brief This structure defines the reset/power on state for the Connectivity driver.
 */
00339 static const pmic_convity_state_struct reset = {
      0,
      HANDLE_FREE,
      USB,
      NULL,
      0,
      USB_FULL_SPEED,
      USB_PERIPHERAL,
      USB_POWER_INTERNAL,
      USB_POWER_3V3,
      USB_TRANSCEIVER_OFF,
      0,
      USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
      RS232_TX_USE0VM_RX_UDATVP,
      RS232_TX_UDM_RX_UDP
};

/*!
 * @brief This structure maintains the current state of the Connectivity driver.
 *
 * The initial values must be identical to the reset state defined by the
 * #reset variable.
 */
00362 static pmic_convity_usb_state usb = {
      0,
      HANDLE_FREE,
      USB,
      NULL,
      0,
      USB_FULL_SPEED,
      USB_PERIPHERAL,
      USB_POWER_INTERNAL,
      USB_POWER_3V3,
      USB_TRANSCEIVER_OFF,
      0,
      USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
};

/*!
 * @brief This structure maintains the current state of the Connectivity driver.
 *
 * The initial values must be identical to the reset state defined by the
 * #reset variable.
 */
00383 static pmic_convity_rs232_state rs_232 = {
      0,
      HANDLE_FREE,
      RS232_1,
      NULL,
      0,
      RS232_TX_USE0VM_RX_UDATVP,
      RS232_TX_UDM_RX_UDP
};

/*!
 * @brief This structure maintains the current state of the Connectivity driver.
 *
 * The initial values must be identical to the reset state defined by the
 * #reset variable.
 */
00399 static pmic_convity_cea936_state cea_936 = {
      0,
      HANDLE_FREE,
      CEA936_MONO,
      NULL,
      0,
};

/*!
 * @brief This spinlock is used to provide mutual exclusion.
 *
 * Create a spinlock that can be used to provide mutually exclusive
 * read/write access to the globally accessible "convity" data structure
 * that was defined above. Mutually exclusive access is required to
 * ensure that the convity data structure is consistent at all times
 * when possibly accessed by multiple threads of execution (for example,
 * while simultaneously handling a user request and an interrupt event).
 *
 * We need to use a spinlock sometimes because we need to provide mutual
 * exclusion while handling a hardware interrupt.
 */
00420 static spinlock_t lock = SPIN_LOCK_UNLOCKED;

/*!
 * @brief This mutex is used to provide mutual exclusion.
 *
 * Create a mutex that can be used to provide mutually exclusive
 * read/write access to the globally accessible data structures
 * that were defined above. Mutually exclusive access is required to
 * ensure that the Connectivity data structures are consistent at all
 * times when possibly accessed by multiple threads of execution.
 *
 * Note that we use a mutex instead of the spinlock whenever disabling
 * interrupts while in the critical section is not required. This helps
 * to minimize kernel interrupt handling latency.
 */
static DECLARE_MUTEX(mutex);

/* Prototype for the connectivity driver tasklet function. */
static void pmic_convity_tasklet(struct work_struct *work);

/*!
 * @brief Tasklet handler for the connectivity driver.
 *
 * Declare a tasklet that will do most of the processing for all of the
 * connectivity-related interrupt events (USB4.4VI, USB2.0VI, USB0.8VI,
 * and AB_DETI). Note that we cannot do all of the required processing
 * within the interrupt handler itself because we may need to call the
 * ADC driver to measure voltages as well as calling any user-registered
 * callback functions.
 */
DECLARE_WORK(convityTasklet, pmic_convity_tasklet);

/*!
 * @brief Global variable to track currently active interrupt events.
 *
 * This global variable is used to keep track of all of the currently
 * active interrupt events for the connectivity driver. Note that access
 * to this variable may occur while within an interrupt context and,
 * therefore, must be guarded by using a spinlock.
 */
00460 static PMIC_CORE_EVENT eventID = 0;

/* Prototypes for all static connectivity driver functions. */
static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode);
static PMIC_STATUS pmic_convity_deregister_all(void);
static void pmic_convity_event_handler(void *param);

/**************************************************************************
 * General setup and configuration functions.
 **************************************************************************
 */

/*!
 * @name General Setup and Configuration Connectivity APIs
 * Functions for setting up and configuring the connectivity hardware.
 */
/*@{*/

/*!
 * Attempt to open and gain exclusive access to the PMIC connectivity
 * hardware. An initial operating mode must also be specified.
 *
 * If the open request is successful, then a numeric handle is returned
 * and this handle must be used in all subsequent function calls. The
 * same handle must also be used in the pmic_convity_close() call when use
 * of the PMIC connectivity hardware is no longer required.
 *
 * @param       handle          device handle from open() call
 * @param       mode            initial connectivity operating mode
 *
 * @return      PMIC_SUCCESS    if the open request was successful
 */
00492 PMIC_STATUS pmic_convity_open(PMIC_CONVITY_HANDLE * const handle,
                        const PMIC_CONVITY_MODE mode)
{
      PMIC_STATUS rc = PMIC_ERROR;

      if (handle == (PMIC_CONVITY_HANDLE *) NULL) {
            /* Do not dereference a NULL pointer. */
            return PMIC_ERROR;
      }

      /* We only need to acquire a mutex here because the interrupt handler
       * never modifies the device handle or device handle state. Therefore,
       * we don't need to worry about conflicts with the interrupt handler
       * or the need to execute in an interrupt context.
       *
       * But we do need a critical section here to avoid problems in case
       * multiple calls to pmic_convity_open() are made since we can only
       * allow one of them to succeed.
       */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      /* Check the current device handle state and acquire the handle if
       * it is available.
       */
      if ((usb.handle_state != HANDLE_FREE)
          && (rs_232.handle_state != HANDLE_FREE)
          && (cea_936.handle_state != HANDLE_FREE)) {

            /* Cannot open the PMIC connectivity hardware at this time or an invalid
             * mode was requested.
             */
            *handle = reset.handle;
      } else {

            if (mode == USB) {
                  usb.handle = (PMIC_CONVITY_HANDLE) (&usb);
                  usb.handle_state = HANDLE_IN_USE;
            } else if ((mode == RS232_1) || (mode == RS232_2)) {
                  rs_232.handle = (PMIC_CONVITY_HANDLE) (&rs_232);
                  rs_232.handle_state = HANDLE_IN_USE;
            } else if ((mode == CEA936_STEREO) || (mode == CEA936_MONO)
                     || (mode == CEA936_TEST_LEFT)
                     || (mode == CEA936_TEST_RIGHT)) {
                  cea_936.handle = (PMIC_CONVITY_HANDLE) (&cea_936);
                  cea_936.handle_state = HANDLE_IN_USE;

            }
            /* Let's begin by acquiring the connectivity device handle. */
            /* Then we can try to set the desired operating mode. */
            rc = pmic_convity_set_mode_internal(mode);

            if (rc == PMIC_SUCCESS) {
                  /* Successfully set the desired operating mode, now return the
                   * handle to the caller.
                   */
                  if (mode == USB) {
                        *handle = usb.handle;
                  } else if ((mode == RS232_1) || (mode == RS232_2)) {
                        *handle = rs_232.handle;
                  } else if ((mode == CEA936_STEREO)
                           || (mode == CEA936_MONO)
                           || (mode == CEA936_TEST_LEFT)
                           || (mode == CEA936_TEST_RIGHT)) {
                        *handle = cea_936.handle;
                  }
            } else {
                  /* Failed to set the desired mode, return the handle to an unused
                   * state.
                   */
                  if (mode == USB) {
                        usb.handle = reset.handle;
                        usb.handle_state = reset.handle_state;
                  } else if ((mode == RS232_1) || (mode == RS232_2)) {
                        rs_232.handle = reset.handle;
                        rs_232.handle_state = reset.handle_state;
                  } else if ((mode == CEA936_STEREO)
                           || (mode == CEA936_MONO)
                           || (mode == CEA936_TEST_LEFT)
                           || (mode == CEA936_TEST_RIGHT)) {
                        cea_936.handle = reset.handle;
                        cea_936.handle_state = reset.handle_state;
                  }

                  *handle = reset.handle;
            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Terminate further access to the PMIC connectivity hardware. Also allows
 * another process to call pmic_convity_open() to gain access.
 *
 * @param       handle          device handle from open() call
 *
 * @return      PMIC_SUCCESS    if the close request was successful
 */
00594 PMIC_STATUS pmic_convity_close(const PMIC_CONVITY_HANDLE handle)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Begin a critical section here to avoid the possibility of race
       * conditions if multiple threads happen to call this function and
       * pmic_convity_open() at the same time.
       */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      /* Confirm that the device handle matches the one assigned in the
       * pmic_convity_open() call and then close the connection.
       */
      if (((handle == usb.handle) &&
           (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                          && (rs_232.handle_state ==
                                            HANDLE_IN_USE))
          || ((handle == cea_936.handle)
            && (cea_936.handle_state == HANDLE_IN_USE))) {
            rc = PMIC_SUCCESS;

            /* Deregister for all existing callbacks if necessary and make sure
             * that the event handling settings are consistent following the
             * close operation.
             */
            if ((usb.callback != reset.callback)
                || (rs_232.callback != reset.callback)
                || (cea_936.callback != reset.callback)) {
                  /* Deregister the existing callback function and all registered
                   * events before we completely close the handle.
                   */
                  rc = pmic_convity_deregister_all();
                  if (rc == PMIC_SUCCESS) {

                  } else if (usb.eventMask != reset.eventMask) {
                        /* Having a non-zero eventMask without a callback function being
                         * defined should never occur but let's just make sure here that
                         * we keep things consistent.
                         */
                        usb.eventMask = reset.eventMask;
                        /* Mark the connectivity device handle as being closed. */
                        usb.handle = reset.handle;
                        usb.handle_state = reset.handle_state;

                  } else if (rs_232.eventMask != reset.eventMask) {

                        rs_232.eventMask = reset.eventMask;
                        /* Mark the connectivity device handle as being closed. */
                        rs_232.handle = reset.handle;
                        rs_232.handle_state = reset.handle_state;

                  } else if (cea_936.eventMask != reset.eventMask) {
                        cea_936.eventMask = reset.eventMask;
                        /* Mark the connectivity device handle as being closed. */
                        cea_936.handle = reset.handle;
                        cea_936.handle_state = reset.handle_state;

                  }

            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Change the current operating mode of the PMIC connectivity hardware.
 * The available connectivity operating modes is hardware dependent and
 * consists of one or more of the following: USB (including USB On-the-Go),
 * RS-232, and CEA-936. Requesting an operating mode that is not supported
 * by the PMIC hardware will return PMIC_NOT_SUPPORTED.
 *
 * @param       handle          device handle from
                              open() call
 * @param       mode            desired operating mode
 *
 * @return      PMIC_SUCCESS    if the requested mode was successfully set
 */
00676 PMIC_STATUS pmic_convity_set_mode(const PMIC_CONVITY_HANDLE handle,
                          const PMIC_CONVITY_MODE mode)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if (((handle == usb.handle) &&
           (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                          && (rs_232.handle_state ==
                                            HANDLE_IN_USE))
          || ((handle == cea_936.handle)
            && (cea_936.handle_state == HANDLE_IN_USE))) {
            rc = pmic_convity_set_mode_internal(mode);
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the current operating mode for the PMIC connectivity hardware.
 *
 * @param       handle          device handle from open() call
 * @param       mode            the current PMIC connectivity operating mode
 *
 * @return      PMIC_SUCCESS    if the requested mode was successfully set
 */
00708 PMIC_STATUS pmic_convity_get_mode(const PMIC_CONVITY_HANDLE handle,
                          PMIC_CONVITY_MODE * const mode)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((((handle == usb.handle) &&
            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                           && (rs_232.
                                             handle_state ==
                                             HANDLE_IN_USE))
           || ((handle == cea_936.handle)
             && (cea_936.handle_state == HANDLE_IN_USE)))
          && (mode != (PMIC_CONVITY_MODE *) NULL)) {

            *mode = usb.mode;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Restore all registers to the initial power-on/reset state.
 *
 * @param       handle          device handle from open() call
 *
 * @return      PMIC_SUCCESS    if the reset was successful
 */
00744 PMIC_STATUS pmic_convity_reset(const PMIC_CONVITY_HANDLE handle)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;
      if (((handle == usb.handle) &&
           (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                          && (rs_232.handle_state ==
                                            HANDLE_IN_USE))
          || ((handle == cea_936.handle)
            && (cea_936.handle_state == HANDLE_IN_USE))) {

            /* Reset the PMIC Connectivity register to it's power on state. */
            rc = pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0,
                            REG_FULLMASK);

            rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                            RESET_USBCNTRL_REG_1, REG_FULLMASK);

            if (rc == PMIC_SUCCESS) {
                  /* Also reset the device driver state data structure. */

            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Register a callback function that will be used to signal PMIC connectivity
 * events. For example, the USB subsystem should register a callback function
 * in order to be notified of device connect/disconnect events. Note, however,
 * that non-USB events may also be signalled depending upon the PMIC hardware
 * capabilities. Therefore, the callback function must be able to properly
 * handle all of the possible events if support for non-USB peripherals is
 * also to be included.
 *
 * @param       handle          device handle from open() call
 * @param       func            a pointer to the callback function
 * @param       eventMask       a mask selecting events to be notified
 *
 * @return      PMIC_SUCCESS    if the callback was successful registered
 */
00792 PMIC_STATUS pmic_convity_set_callback(const PMIC_CONVITY_HANDLE handle,
                              const PMIC_CONVITY_CALLBACK func,
                              const PMIC_CONVITY_EVENTS eventMask)
{
      unsigned long flags;
      PMIC_STATUS rc = PMIC_ERROR;

      /* We need to start a critical section here to ensure a consistent state
       * in case simultaneous calls to pmic_convity_set_callback() are made. In
       * that case, we must serialize the calls to ensure that the "callback"
       * and "eventMask" state variables are always consistent.
       *
       * Note that we don't actually need to acquire the spinlock until later
       * when we are finally ready to update the "callback" and "eventMask"
       * state variables which are shared with the interrupt handler.
       */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {

            /* Return an error if either the callback function or event mask
             * is not properly defined.
             *
             * It is also considered an error if a callback function has already
             * been defined. If you wish to register for a new set of events,
             * then you must first call pmic_convity_clear_callback() to
             * deregister the existing callback function and list of events
             * before trying to register a new callback function.
             */
            if ((func == NULL) || (eventMask == 0)
                || (usb.callback != NULL)) {
                  rc = PMIC_ERROR;

                  /* Register for PMIC events from the core protocol driver. */
            } else {

                  if ((eventMask & USB_DETECT_4V4_RISE) ||
                      (eventMask & USB_DETECT_4V4_FALL)) {
                        /* We need to register for the 4.4V interrupt. */
                        //EVENT_USBI or EVENT_USB_44VI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_4V4);
                        rc = pmic_event_subscribe(EVENT_USBI,
                                            eventNotify);

                        if (rc != PMIC_SUCCESS) {
                              return rc;
                        }
                  }

                  if ((eventMask & USB_DETECT_2V0_RISE) ||
                      (eventMask & USB_DETECT_2V0_FALL)) {
                        /* We need to register for the 2.0V interrupt. */
                        //EVENT_USB_20VI or EVENT_USBI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_2V0);
                        rc = pmic_event_subscribe(EVENT_USBI,
                                            eventNotify);

                        if (rc != PMIC_SUCCESS) {
                              goto Cleanup_4V4;
                        }
                  }

                  if ((eventMask & USB_DETECT_0V8_RISE) ||
                      (eventMask & USB_DETECT_0V8_FALL)) {
                        /* We need to register for the 0.8V interrupt. */
                        //EVENT_USB_08VI or EVENT_USBI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_0V8);
                        rc = pmic_event_subscribe(EVENT_USBI,
                                            eventNotify);

                        if (rc != PMIC_SUCCESS) {
                              goto Cleanup_2V0;
                        }
                  }

                  if ((eventMask & USB_DETECT_MINI_A) ||
                      (eventMask & USB_DETECT_MINI_B)
                      || (eventMask & USB_DETECT_NON_USB_ACCESSORY)
                      || (eventMask & USB_DETECT_FACTORY_MODE)) {
                        /* We need to register for the AB_DET interrupt. */
                        //EVENT_AB_DETI or EVENT_IDI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_ABDET);
                        rc = pmic_event_subscribe(EVENT_IDI,
                                            eventNotify);

                        if (rc != PMIC_SUCCESS) {
                              goto Cleanup_0V8;
                        }
                  }

                  /* Use a critical section to maintain a consistent state. */
                  spin_lock_irqsave(&lock, flags);

                  /* Successfully registered for all events. */
                  usb.callback = func;
                  usb.eventMask = eventMask;
                  spin_unlock_irqrestore(&lock, flags);

                  goto End;

                  /* This section unregisters any already registered events if we should
                   * encounter an error partway through the registration process. Note
                   * that we don't check the return status here since it is already set
                   * to PMIC_ERROR before we get here.
                   */
                  Cleanup_0V8:

                  if ((eventMask & USB_DETECT_0V8_RISE) ||
                      (eventMask & USB_DETECT_0V8_FALL)) {
                        //EVENT_USB_08VI or EVENT_USBI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_0V8);
                        pmic_event_unsubscribe(EVENT_USBI, eventNotify);
                        goto End;
                  }

                  Cleanup_2V0:

                  if ((eventMask & USB_DETECT_2V0_RISE) ||
                      (eventMask & USB_DETECT_2V0_FALL)) {
                        //EVENT_USB_20VI or EVENT_USBI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_2V0);
                        pmic_event_unsubscribe(EVENT_USBI, eventNotify);
                        goto End;
                  }

                  Cleanup_4V4:

                  if ((eventMask & USB_DETECT_4V4_RISE) ||
                      (eventMask & USB_DETECT_4V4_FALL)) {
                        //EVENT_USB_44VI or EVENT_USBI
                        eventNotify.func = pmic_convity_event_handler;
                        eventNotify.param = (void *)(CORE_EVENT_4V4);
                        pmic_event_unsubscribe(EVENT_USBI, eventNotify);
                  }
            }
            /* Exit the critical section. */

      }
      End:up(&mutex);
      return rc;

}

/*!
 * Clears the current callback function. If this function returns successfully
 * then all future Connectivity events will only be handled by the default
 * handler within the Connectivity driver.
 *
 * @param       handle          device handle from open() call
 *
 * @return      PMIC_SUCCESS    if the callback was successful cleared
 */
00951 PMIC_STATUS pmic_convity_clear_callback(const PMIC_CONVITY_HANDLE handle)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;
      if (((handle == usb.handle) &&
           (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                          && (rs_232.handle_state ==
                                            HANDLE_IN_USE))
          || ((handle == cea_936.handle)
            && (cea_936.handle_state == HANDLE_IN_USE))) {

            rc = pmic_convity_deregister_all();
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the current callback function and event mask.
 *
 * @param       handle          device handle from open() call
 * @param       func            the current callback function
 * @param       eventMask       the current event selection mask
 *
 * @return      PMIC_SUCCESS    if the callback information was successful
 *                              retrieved
 */
00984 PMIC_STATUS pmic_convity_get_callback(const PMIC_CONVITY_HANDLE handle,
                              PMIC_CONVITY_CALLBACK * const func,
                              PMIC_CONVITY_EVENTS * const eventMask)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;
      if ((((handle == usb.handle) &&
            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
                                           && (rs_232.
                                             handle_state ==
                                             HANDLE_IN_USE))
           || ((handle == cea_936.handle)
             && (cea_936.handle_state == HANDLE_IN_USE)))
          && (func != (PMIC_CONVITY_CALLBACK *) NULL)
          && (eventMask != (PMIC_CONVITY_EVENTS *) NULL)) {
            *func = usb.callback;
            *eventMask = usb.eventMask;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */

      up(&mutex);

      return rc;
}

/*@*/

/**************************************************************************
 * USB-specific configuration and setup functions.
 **************************************************************************
 */

/*!
 * @name USB and USB-OTG Connectivity APIs
 * Functions for controlling USB and USB-OTG connectivity.
 */
/*@{*/

/*!
 * Set the USB transceiver speed.
 *
 * @param       handle          device handle from open() call
 * @param       speed           the desired USB transceiver speed
 *
 * @return      PMIC_SUCCESS    if the transceiver speed was successfully set
 */
01036 PMIC_STATUS pmic_convity_usb_set_speed(const PMIC_CONVITY_HANDLE handle,
                               const PMIC_CONVITY_USB_SPEED speed)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask = SET_BITS(regUSB0, FSENB, 1);

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if (handle == (rs_232.handle || cea_936.handle)) {
            return PMIC_PARAMETER_ERROR;
      } else {
            if ((handle == usb.handle) &&
                (usb.handle_state == HANDLE_IN_USE)) {
                  /* Validate the function parameters and if they are valid, then
                   * configure the pull-up and pull-down resistors as required for
                   * the desired operating mode.
                   */
                  if ((speed == USB_HIGH_SPEED)) {
                        /*
                         * The USB transceiver also does not support the high speed mode
                         * (which is also optional under the USB OTG specification).
                         */
                        rc = PMIC_NOT_SUPPORTED;
                  } else if ((speed != USB_LOW_SPEED)
                           && (speed != USB_FULL_SPEED)) {
                        /* Final validity check on the speed parameter. */
                        rc = PMIC_ERROR;;
                  } else {
                        /* First configure the D+ and D- pull-up/pull-down resistors as
                         * per the USB OTG specification.
                         */
                        if (speed == USB_FULL_SPEED) {
                              /* Activate pull-up on D+ and pull-down on D-. */
                              reg_value =
                                  SET_BITS(regUSB0, UDM_PD, 1);
                        } else if (speed == USB_LOW_SPEED) {
                              /* Activate pull-up on D+ and pull-down on D-. */
                              reg_value = SET_BITS(regUSB0, FSENB, 1);
                        }

                        /* Now set the desired USB transceiver speed. Note that
                         * USB_FULL_SPEED simply requires FSENB=0 (which it
                         * already is).
                         */

                        rc = pmic_write_reg(REG_USB, reg_value,
                                        reg_mask);

                        if (rc == PMIC_SUCCESS) {
                              usb.usbSpeed = speed;
                        }
                  }
            }
      }
      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the USB transceiver speed.
 *
 * @param       handle          device handle from open() call
 * @param       speed           the current USB transceiver speed
 * @param       mode            the current USB transceiver mode
 *
 * @return      PMIC_SUCCESS    if the transceiver speed was successfully
 *                              obtained
 */
01109 PMIC_STATUS pmic_convity_usb_get_speed(const PMIC_CONVITY_HANDLE handle,
                               PMIC_CONVITY_USB_SPEED * const speed,
                               PMIC_CONVITY_USB_MODE * const mode)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) &&
          (usb.handle_state == HANDLE_IN_USE) &&
          (speed != (PMIC_CONVITY_USB_SPEED *) NULL) &&
          (mode != (PMIC_CONVITY_USB_MODE *) NULL)) {
            *speed = usb.usbSpeed;
            *mode = usb.usbMode;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * This function enables/disables VUSB and VBUS output.
 * This API configures the VUSBEN and VBUSEN bits of USB register
 *
 * @param       handle          device handle from open() call
 * @param        out_type true, for VBUS
 *                        false, for VUSB
 * @param        out    if true, output is enabled
 *                      if false, output is disabled
 *
 * @return       This function returns PMIC_SUCCESS if successful.
 */
01147 PMIC_STATUS pmic_convity_set_output(const PMIC_CONVITY_HANDLE handle,
                            bool out_type, bool out)
{

      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;

      unsigned int reg_mask = 0;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {

            if ((out_type == 0) && (out == 1)) {

                  reg_value = SET_BITS(regUSB1, VUSBEN, 1);
                  reg_mask = SET_BITS(regUSB1, VUSBEN, 1);

                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                                  reg_value, reg_mask);
            } else if (out_type == 0 && out == 0) {
                  reg_mask = SET_BITS(regUSB1, VBUSEN, 1);

                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                                  reg_value, reg_mask);
            } else if (out_type == 1 && out == 1) {

                  reg_value = SET_BITS(regUSB1, VBUSEN, 1);
                  reg_mask = SET_BITS(regUSB1, VBUSEN, 1);

                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                                  reg_value, reg_mask);
            }

            else if (out_type == 1 && out == 0) {

                  reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                                  reg_value, reg_mask);
            }

            /*else {

               rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
               reg_value, reg_mask);
               } */
      }

      up(&mutex);

      return rc;
}

/*!
 * Set the USB transceiver's power supply configuration.
 *
 * @param       handle          device handle from open() call
 * @param       pwrin           USB transceiver regulator input power source
 * @param       pwrout          USB transceiver regulator output power level
 *
 * @return      PMIC_SUCCESS    if the USB transceiver's power supply
 *                              configuration was successfully set
 */
01212 PMIC_STATUS pmic_convity_usb_set_power_source(const PMIC_CONVITY_HANDLE handle,
                                    const PMIC_CONVITY_USB_POWER_IN
                                    pwrin,
                                    const PMIC_CONVITY_USB_POWER_OUT
                                    pwrout)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask = 0;
      /* SET_BITS(regUSB1, VUSBEN, 1) | SET_BITS(regUSB1, VBUSEN,
         1) | SET_BITS(regUSB1,
         VUSBIN,
         2) | */
      //    SET_BITS(regUSB1, VUSB, 1);
/*        SET_BITS(regUSB1, VUSBIN, 1) | SET_BITS(regUSB1, VUSBEN,
                                        1) | SET_BITS(regUSB1,
                                                  VBUSEN, 1);*/

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {

            if (pwrin == USB_POWER_INTERNAL_BOOST) {
                  reg_value |= SET_BITS(regUSB1, VUSBIN, 0);
                  reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
            } else if (pwrin == USB_POWER_VBUS) {
                  reg_value |= SET_BITS(regUSB1, VUSBIN, 1);
                  reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
            }

            else if (pwrin == USB_POWER_INTERNAL) {
                  reg_value |= SET_BITS(regUSB1, VUSBIN, 2);
                  reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
            }

            if (pwrout == USB_POWER_3V3) {
                  reg_value |= SET_BITS(regUSB1, VUSB, 1);
                  reg_mask |= SET_BITS(regUSB1, VUSB, 1);
            }

            else if (pwrout == USB_POWER_2V775) {
                  reg_value |= SET_BITS(regUSB1, VUSB, 0);
                  reg_mask |= SET_BITS(regUSB1, VUSB, 1);
            }
            rc = pmic_write_reg(REG_CHARGE_USB_SPARE, reg_value, reg_mask);

            if (rc == PMIC_SUCCESS) {
                  usb.usbPowerIn = pwrin;
                  usb.usbPowerOut = pwrout;
            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the USB transceiver's current power supply configuration.
 *
 * @param       handle          device handle from open() call
 * @param       pwrin           USB transceiver regulator input power source
 * @param       pwrout          USB transceiver regulator output power level
 *
 * @return      PMIC_SUCCESS    if the USB transceiver's power supply
 *                              configuration was successfully retrieved
 */
01282 PMIC_STATUS pmic_convity_usb_get_power_source(const PMIC_CONVITY_HANDLE handle,
                                    PMIC_CONVITY_USB_POWER_IN *
                                    const pwrin,
                                    PMIC_CONVITY_USB_POWER_OUT *
                                    const pwrout)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) &&
          (usb.handle_state == HANDLE_IN_USE) &&
          (pwrin != (PMIC_CONVITY_USB_POWER_IN *) NULL) &&
          (pwrout != (PMIC_CONVITY_USB_POWER_OUT *) NULL)) {
            *pwrin = usb.usbPowerIn;
            *pwrout = usb.usbPowerOut;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Set the USB transceiver's operating mode.
 *
 * @param       handle          device handle from open() call
 * @param       mode            desired operating mode
 *
 * @return      PMIC_SUCCESS    if the USB transceiver's operating mode
 *                              was successfully configured
 */
01319 PMIC_STATUS pmic_convity_usb_set_xcvr(const PMIC_CONVITY_HANDLE handle,
                              const PMIC_CONVITY_USB_TRANSCEIVER_MODE
                              mode)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask = 0;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {

            if (mode == USB_TRANSCEIVER_OFF) {
                  reg_value = SET_BITS(regUSB0, USBXCVREN, 0);
                  reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);

                  rc = pmic_write_reg(REG_USB, reg_value, reg_mask);

            }

            if (mode == USB_SINGLE_ENDED_UNIDIR) {
                  reg_value |=
                      SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
                                                    BIDIR, 0);
                  reg_mask |=
                      SET_BITS(regUSB0, USB_SUSPEND,
                             1) | SET_BITS(regUSB0, DATSE0,
                                       1) | SET_BITS(regUSB0, BIDIR,
                                                 1);
            } else if (mode == USB_SINGLE_ENDED_BIDIR) {
                  reg_value |=
                      SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
                                                    BIDIR, 1);
                  reg_mask |=
                      SET_BITS(regUSB0, USB_SUSPEND,
                             1) | SET_BITS(regUSB0, DATSE0,
                                       1) | SET_BITS(regUSB0, BIDIR,
                                                 1);
            } else if (mode == USB_DIFFERENTIAL_UNIDIR) {
                  reg_value |=
                      SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
                                                    BIDIR, 0);
                  reg_mask |=
                      SET_BITS(regUSB0, USB_SUSPEND,
                             1) | SET_BITS(regUSB0, DATSE0,
                                       1) | SET_BITS(regUSB0, BIDIR,
                                                 1);
            } else if (mode == USB_DIFFERENTIAL_BIDIR) {
                  reg_value |=
                      SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
                                                    BIDIR, 1);
                  reg_mask |=
                      SET_BITS(regUSB0, USB_SUSPEND,
                             1) | SET_BITS(regUSB0, DATSE0,
                                       1) | SET_BITS(regUSB0, BIDIR,
                                                 1);
            }

            if (mode == USB_SUSPEND_ON) {
                  reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 1);
                  reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
            } else if (mode == USB_SUSPEND_OFF) {
                  reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 0);
                  reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
            }

            if (mode == USB_OTG_SRP_DLP_START) {
                  reg_value |= SET_BITS(regUSB0, USB_PU, 0);
                  reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
            } else if (mode == USB_OTG_SRP_DLP_STOP) {
                  reg_value &= SET_BITS(regUSB0, USB_PU, 1);
                  reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
            }

            rc = pmic_write_reg(REG_USB, reg_value, reg_mask);

            if (rc == PMIC_SUCCESS) {
                  usb.usbXcvrMode = mode;
            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the USB transceiver's current operating mode.
 *
 * @param       handle          device handle from open() call
 * @param       mode            current operating mode
 *
 * @return      PMIC_SUCCESS    if the USB transceiver's operating mode
 *                              was successfully retrieved
 */
01417 PMIC_STATUS pmic_convity_usb_get_xcvr(const PMIC_CONVITY_HANDLE handle,
                              PMIC_CONVITY_USB_TRANSCEIVER_MODE *
                              const mode)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) &&
          (usb.handle_state == HANDLE_IN_USE) &&
          (mode != (PMIC_CONVITY_USB_TRANSCEIVER_MODE *) NULL)) {
            *mode = usb.usbXcvrMode;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Set the Data Line Pulse duration (in milliseconds) for the USB OTG
 * Session Request Protocol.
 *
 * For mc13783, this feature is not supported.So return PMIC_NOT_SUPPORTED
 *
 * @param       handle          device handle from open() call
 * @param       duration        the data line pulse duration (ms)
 *
 * @return      PMIC_SUCCESS    if the pulse duration was successfully set
 */
01452 PMIC_STATUS pmic_convity_usb_otg_set_dlp_duration(const PMIC_CONVITY_HANDLE
                                      handle,
                                      const unsigned int duration)
{
      PMIC_STATUS rc = PMIC_NOT_SUPPORTED;

      /* The setting of the dlp duration is not supported by the mc13783 PMIC hardware. */

      /* No critical section is required. */

      if ((handle != usb.handle)
          || (usb.handle_state != HANDLE_IN_USE)) {
            /* Must return error indication for invalid handle parameter to be
             * consistent with other APIs.
             */
            rc = PMIC_ERROR;
      }

      return rc;
}

/*!
 * Get the current Data Line Pulse duration (in milliseconds) for the USB
 * OTG Session Request Protocol.
 *
 * @param       handle          device handle from open() call
 * @param       duration        the data line pulse duration (ms)
 *
 * @return      PMIC_SUCCESS    if the pulse duration was successfully obtained
 */
01482 PMIC_STATUS pmic_convity_usb_otg_get_dlp_duration(const PMIC_CONVITY_HANDLE
                                      handle,
                                      unsigned int *const duration)
{
      PMIC_STATUS rc = PMIC_NOT_SUPPORTED;

      /* The setting of dlp duration is not supported by the mc13783 PMIC hardware. */

      /* No critical section is required. */

      if ((handle != usb.handle)
          || (usb.handle_state != HANDLE_IN_USE)) {
            /* Must return error indication for invalid handle parameter to be
             * consistent with other APIs.
             */
            rc = PMIC_ERROR;
      }

      return rc;
}

/*!
 * Set the USB On-The-Go (OTG) configuration.
 *
 * @param       handle          device handle from open() call
 * @param       cfg             desired USB OTG configuration
 *
 * @return      PMIC_SUCCESS    if the OTG configuration was successfully set
 */
01511 PMIC_STATUS pmic_convity_usb_otg_set_config(const PMIC_CONVITY_HANDLE handle,
                                  const PMIC_CONVITY_USB_OTG_CONFIG
                                  cfg)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask = 0;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
            if (cfg & USB_OTG_SE0CONN) {
                  reg_value = SET_BITS(regUSB0, SE0_CONN, 1);
                  reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
            }
            if (cfg & USBXCVREN) {
                  reg_value |= SET_BITS(regUSB0, USBXCVREN, 1);
                  reg_mask |= SET_BITS(regUSB0, USBXCVREN, 1);
            }

            if (cfg & USB_OTG_DLP_SRP) {
                  reg_value |= SET_BITS(regUSB0, DLP_SRP, 1);
                  reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
            }

            if (cfg & USB_PULL_OVERRIDE) {
                  reg_value |= SET_BITS(regUSB0, PULLOVR, 1);
                  reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
            }

            if (cfg & USB_PU) {
                  reg_value |= SET_BITS(regUSB0, USB_PU, 1);
                  reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
            }

            if (cfg & USB_UDM_PD) {
                  reg_value |= SET_BITS(regUSB0, UDM_PD, 1);
                  reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
            }

            if (cfg & USB_UDP_PD) {
                  reg_value |= SET_BITS(regUSB0, UDP_PD, 1);
                  reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
            }

            if (cfg & USB_DP150K_PU) {
                  reg_value |= SET_BITS(regUSB0, DP150K_PU, 1);
                  reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
            }

            if (cfg & USB_USBCNTRL) {
                  reg_value |= SET_BITS(regUSB0, USBCNTRL, 1);
                  reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
            }

            if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
            }
            if (cfg & USB_VBUS_CURRENT_LIMIT_LOW) {
                  reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
            }

            if (cfg & USB_VBUS_PULLDOWN) {
                  reg_value |= SET_BITS(regUSB0, VBUSPDENB, 1);
                  reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
            }

            rc = pmic_write_reg(REG_USB, reg_value, reg_mask);

            if (rc == PMIC_SUCCESS) {
                  if ((cfg & USB_VBUS_CURRENT_LIMIT_HIGH) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
                      (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS)) {
                        /* Make sure that the VBUS current limit state is
                         * correctly set to either USB_VBUS_CURRENT_LIMIT_HIGH
                         * or USB_VBUS_CURRENT_LIMIT_LOW but never both at the
                         * same time.
                         *
                         * We guarantee this by first clearing both of the
                         * status bits and then resetting the correct one.
                         */
                        usb.usbOtgCfg &=
                            ~(USB_VBUS_CURRENT_LIMIT_HIGH |
                              USB_VBUS_CURRENT_LIMIT_LOW |
                              USB_VBUS_CURRENT_LIMIT_LOW_10MS |
                              USB_VBUS_CURRENT_LIMIT_LOW_20MS |
                              USB_VBUS_CURRENT_LIMIT_LOW_30MS |
                              USB_VBUS_CURRENT_LIMIT_LOW_40MS |
                              USB_VBUS_CURRENT_LIMIT_LOW_50MS |
                              USB_VBUS_CURRENT_LIMIT_LOW_60MS);
                  }

                  usb.usbOtgCfg |= cfg;
            }
      }
      //}

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Clears the USB On-The-Go (OTG) configuration. Multiple configuration settings
 * may be OR'd together in a single call. However, selecting conflicting
 * settings (e.g., multiple VBUS current limits) will result in undefined
 * behavior.
 *
 * @param   handle          Device handle from open() call.
 * @param   cfg             USB OTG configuration settings to be cleared.
 *
 * @retval      PMIC_SUCCESS         If the OTG configuration was successfully
 *                                   cleared.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 * @retval      PMIC_NOT_SUPPORTED   If the desired USB OTG configuration is
 *                                   not supported by the PMIC hardware.
 */
01649 PMIC_STATUS pmic_convity_usb_otg_clear_config(const PMIC_CONVITY_HANDLE handle,
                                    const PMIC_CONVITY_USB_OTG_CONFIG
                                    cfg)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask = 0;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
            /* if ((cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
               (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
               (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
               (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
               (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
               (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS))
               {
               rc = PMIC_NOT_SUPPORTED;
               } */
            //else

            if (cfg & USB_OTG_SE0CONN) {
                  reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
            }

            if (cfg & USB_OTG_DLP_SRP) {
                  reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
            }

            if (cfg & USB_DP150K_PU) {
                  reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
            }

            if (cfg & USB_PULL_OVERRIDE) {
                  reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
            }

            if (cfg & USB_PU) {

                  reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
            }

            if (cfg & USB_UDM_PD) {

                  reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
            }

            if (cfg & USB_UDP_PD) {

                  reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
            }

            if (cfg & USB_USBCNTRL) {
                  reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
            }

            if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
            } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
                  reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
            }

            if (cfg & USB_VBUS_PULLDOWN) {
                  //      reg_value |= SET_BITS(regUSB0, VBUSPDENB, 1);
                  reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
            }

            rc = pmic_write_reg(REG_USB, reg_value, reg_mask);

            if (rc == PMIC_SUCCESS) {
                  usb.usbOtgCfg &= ~cfg;
            }
      }
      //}

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the current USB On-The-Go (OTG) configuration.
 *
 * @param       handle          device handle from open() call
 * @param       cfg             the current USB OTG configuration
 *
 * @return      PMIC_SUCCESS    if the OTG configuration was successfully
 *                              retrieved
 */
01752 PMIC_STATUS pmic_convity_usb_otg_get_config(const PMIC_CONVITY_HANDLE handle,
                                  PMIC_CONVITY_USB_OTG_CONFIG *
                                  const cfg)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == usb.handle) &&
          (usb.handle_state == HANDLE_IN_USE) &&
          (cfg != (PMIC_CONVITY_USB_OTG_CONFIG *) NULL)) {
            *cfg = usb.usbOtgCfg;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*@}*/

/**************************************************************************
 * RS-232-specific configuration and setup functions.
 **************************************************************************
 */

/*!
 * @name RS-232 Serial Connectivity APIs
 * Functions for controlling RS-232 serial connectivity.
 */
/*@{*/

/*!
 * Set the connectivity interface to the selected RS-232 operating
 * configuration. Note that the RS-232 operating mode will be automatically
 * overridden if the USB_EN is asserted at any time (e.g., when a USB device
 * is attached). However, we will also automatically return to the RS-232
 * mode once the USB device is detached.
 *
 * @param       handle          device handle from open() call
 * @param       cfgInternal     RS-232 transceiver internal connections
 * @param       cfgExternal     RS-232 transceiver external connections
 *
 * @return      PMIC_SUCCESS    if the requested mode was set
 */
01802 PMIC_STATUS pmic_convity_rs232_set_config(const PMIC_CONVITY_HANDLE handle,
                                const PMIC_CONVITY_RS232_INTERNAL
                                cfgInternal,
                                const PMIC_CONVITY_RS232_EXTERNAL
                                cfgExternal)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value0 = 0, reg_value1 = 0;
      unsigned int reg_mask = SET_BITS(regUSB1, RSPOL, 1);

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == rs_232.handle) && (rs_232.handle_state == HANDLE_IN_USE)) {
            rc = PMIC_SUCCESS;

            /* Validate the calling parameters. */
            /*if ((cfgInternal !=  RS232_TX_USE0VM_RX_UDATVP) &&
               (cfgInternal != RS232_TX_RX_INTERNAL_DEFAULT) && (cfgInternal !=  RS232_TX_UDATVP_RX_URXVM))
               {

               rc = PMIC_NOT_SUPPORTED;
               } */
            if (cfgInternal == RS232_TX_USE0VM_RX_UDATVP) {

                  reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);

            } else if (cfgInternal == RS232_TX_RX_INTERNAL_DEFAULT) {

                  reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);
                  reg_mask |= SET_BITS(regUSB1, RSPOL, 1);

            } else if (cfgInternal == RS232_TX_UDATVP_RX_URXVM) {

                  reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 2);
                  reg_value1 |= SET_BITS(regUSB1, RSPOL, 1);

            } else if ((cfgExternal == RS232_TX_UDM_RX_UDP) ||
                     (cfgExternal == RS232_TX_RX_EXTERNAL_DEFAULT)) {
                  /* Configure for TX on D+ and RX on D-. */
                  reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1);
                  reg_value1 |= SET_BITS(regUSB1, RSPOL, 0);
            } else if (cfgExternal != RS232_TX_UDM_RX_UDP) {
                  /* Any other RS-232 configuration is an error. */
                  rc = PMIC_ERROR;
            }

            if (rc == PMIC_SUCCESS) {
                  /* Configure for TX on D- and RX on D+. */
                  rc = pmic_write_reg(REG_USB, reg_value0, reg_mask);

                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
                                  reg_value1, reg_mask);

                  if (rc == PMIC_SUCCESS) {
                        rs_232.rs232CfgInternal = cfgInternal;
                        rs_232.rs232CfgExternal = cfgExternal;
                  }
            }
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*!
 * Get the connectivity interface's current RS-232 operating configuration.
 *
 * @param       handle          device handle from open() call
 * @param       cfgInternal     RS-232 transceiver internal connections
 * @param       cfgExternal     RS-232 transceiver external connections
 *
 * @return      PMIC_SUCCESS    if the requested mode was retrieved
 */
01879 PMIC_STATUS pmic_convity_rs232_get_config(const PMIC_CONVITY_HANDLE handle,
                                PMIC_CONVITY_RS232_INTERNAL *
                                const cfgInternal,
                                PMIC_CONVITY_RS232_EXTERNAL *
                                const cfgExternal)
{
      PMIC_STATUS rc = PMIC_ERROR;

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle == rs_232.handle) &&
          (rs_232.handle_state == HANDLE_IN_USE) &&
          (cfgInternal != (PMIC_CONVITY_RS232_INTERNAL *) NULL) &&
          (cfgExternal != (PMIC_CONVITY_RS232_EXTERNAL *) NULL)) {
            *cfgInternal = rs_232.rs232CfgInternal;
            *cfgExternal = rs_232.rs232CfgExternal;

            rc = PMIC_SUCCESS;
      }

      /* Exit the critical section. */
      up(&mutex);

      return rc;
}

/*@}*/

/**************************************************************************
 * CEA-936-specific configuration and setup functions.
 **************************************************************************
 */

/*!
 * @name CEA-936 Connectivity APIs
 * Functions for controlling CEA-936 connectivity.
 */
/*@{*/

/*!
 * Signal the attached device to exit the current CEA-936 operating mode.
 * Returns an error if the current operating mode is not CEA-936.
 *
 * @param       handle          device handle from open() call
 * @param       signal          type of exit signal to be sent
 *
 * @return      PMIC_SUCCESS    if exit signal was sent
 */
01929 PMIC_STATUS pmic_convity_cea936_exit_signal(const PMIC_CONVITY_HANDLE handle,
                                  const
                                  PMIC_CONVITY_CEA936_EXIT_SIGNAL
                                  signal)
{
      PMIC_STATUS rc = PMIC_ERROR;
      unsigned int reg_value = 0;
      unsigned int reg_mask =
          SET_BITS(regUSB0, IDPD, 1) | SET_BITS(regUSB0, IDPULSE, 1);

      /* Use a critical section to maintain a consistent state. */
      if (down_interruptible(&mutex))
            return PMIC_SYSTEM_ERROR_EINTR;

      if ((handle != cea_936.handle)
          || (cea_936.handle_state != HANDLE_IN_USE)) {
            /* Must return error indication for invalid handle parameter to be
             * consistent with other APIs.
             */
            rc = PMIC_ERROR;
      } else if (signal == CEA936_UID_PULLDOWN_6MS) {
            reg_value =
                SET_BITS(regUSB0, IDPULSE, 0) | SET_BITS(regUSB0, IDPD, 0);
      } else if (signal == CEA936_UID_PULLDOWN_6MS) {
            reg_value = SET_BITS(regUSB0, IDPULSE, 1);
      } else if (signal == CEA936_UID_PULLDOWN) {
            reg_value = SET_BITS(regUSB0, IDPD, 1);
      } else if (signal == CEA936_UDMPULSE) {
            reg_value = SET_BITS(regUSB0, DMPULSE, 1);
      }

      rc = pmic_write_reg(REG_USB, reg_value, reg_mask);

      up(&mutex);

      return rc;
}

/*@}*/

/**************************************************************************
 * Static functions.
 **************************************************************************
 */

/*!
 * @name Connectivity Driver Internal Support Functions
 * These non-exported internal functions are used to support the functionality
 * of the exported connectivity APIs.
 */
/*@{*/

/*!
 * This internal helper function sets the desired operating mode (either USB
 * OTG or RS-232). It must be called with the mutex already acquired.
 *
 * @param       mode               the desired operating mode (USB or RS232)
 *
 * @return      PMIC_SUCCESS       if the desired operating mode was set
 * @return      PMIC_NOT_SUPPORTED if the desired operating mode is invalid
 */
01990 static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode)
{
      unsigned int reg_value0 = 0, reg_value1 = 0;
      unsigned int reg_mask0 = 0, reg_mask1 = 0;

      //unsigned int reg_mask1 = SET_BITS(regUSB1, VBUSEN, 1);

      PMIC_STATUS rc = PMIC_SUCCESS;

      switch (mode) {
      case USB:
            /* For the USB mode, we start by tri-stating the USB bus (by
             * setting VBUSEN = 0) until a device is connected (i.e.,
             * until we receive a 4.4V rising edge event). All pull-up
             * and pull-down resistors are also disabled until a USB
             * device is actually connected and we have determined which
             * device is the host and the desired USB bus speed.
             *
             * Also tri-state the RS-232 buffers (by setting RSTRI = 1).
             * This prevents the hardware from automatically returning to
             * the RS-232 mode when the USB device is detached.
             */

            reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 0);
            reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);

            /*reg_value1 = SET_BITS(regUSB1, RSTRI, 1); */

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
            /*      if (rc == PMIC_SUCCESS) {
               CHECK_ERROR(pmic_write_reg
               (REG_CHARGE_USB_SPARE,
               reg_value1, reg_mask1));
               } */

            break;

      case RS232_1:
            /* For the RS-232 mode, we tri-state the USB bus (by setting
             * VBUSEN = 0) and enable the RS-232 transceiver (by setting
             * RS232ENB = 0).
             *
             * Note that even in the RS-232 mode, if a USB device is
             * plugged in, we will receive a 4.4V rising edge event which
             * will automatically disable the RS-232 transceiver and
             * tri-state the RS-232 buffers. This allows us to temporarily
             * switch over to USB mode while the USB device is attached.
             * The RS-232 transceiver and buffers will be automatically
             * re-enabled when the USB device is detached.
             */

            /* Explicitly disconnect all of the USB pull-down resistors
             * and the VUSB power regulator here just to be safe.
             *
             * But we do connect the internal pull-up resistor on USB_D+
             * to avoid having an extra load on the USB_D+ line when in
             * RS-232 mode.
             */

            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1) |
                SET_BITS(regUSB0, VBUSPDENB, 1) |
                SET_BITS(regUSB0, USB_PU, 1);
            reg_mask0 =
                SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
                                                    VBUSPDENB,
                                                    1) |
                SET_BITS(regUSB0, USB_PU, 1);

            reg_value1 = SET_BITS(regUSB1, RSPOL, 0);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);

            if (rc == PMIC_SUCCESS) {
                  CHECK_ERROR(pmic_write_reg
                            (REG_CHARGE_USB_SPARE,
                             reg_value1, reg_mask1));
            }
            break;

      case RS232_2:
            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 2) |
                SET_BITS(regUSB0, VBUSPDENB, 1) |
                SET_BITS(regUSB0, USB_PU, 1);
            reg_mask0 =
                SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
                                                    VBUSPDENB,
                                                    1) |
                SET_BITS(regUSB0, USB_PU, 1);

            reg_value1 = SET_BITS(regUSB1, RSPOL, 1);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);

            if (rc == PMIC_SUCCESS) {
                  CHECK_ERROR(pmic_write_reg
                            (REG_CHARGE_USB_SPARE,
                             reg_value1, reg_mask1));
            }
            break;

      case CEA936_MONO:
            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 4);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
            break;

      case CEA936_STEREO:
            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 5);
            reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
            break;

      case CEA936_TEST_RIGHT:
            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 6);
            reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
            break;

      case CEA936_TEST_LEFT:
            reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 7);
            reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);

            rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
            break;

      default:
            rc = PMIC_NOT_SUPPORTED;
      }

      if (rc == PMIC_SUCCESS) {
            if (mode == USB) {
                  usb.mode = mode;
            } else if ((mode == RS232_1) || (mode == RS232_1)) {
                  rs_232.mode = mode;
            } else if ((mode == CEA936_MONO) || (mode == CEA936_STEREO) ||
                     (mode == CEA936_TEST_RIGHT)
                     || (mode == CEA936_TEST_LEFT)) {
                  cea_936.mode = mode;
            }
      }

      return rc;
}

/*!
 * This internal helper function deregisters all of the currently registered
 * callback events. It must be called with the mutual exclusion spinlock
 * already acquired.
 *
 * We've defined the event and callback deregistration code here as a separate
 * function because it can be called by either the pmic_convity_close() or the
 * pmic_convity_clear_callback() APIs. We also wanted to avoid any possible
 * issues with having the same thread calling spin_lock_irq() twice.
 *
 * Note that the mutex must have already been acquired. We will also acquire
 * the spinlock here to avoid any possible race conditions with the interrupt
 * handler.
 *
 * @return      PMIC_SUCCESS    if all of the callback events were cleared
 */
02152 static PMIC_STATUS pmic_convity_deregister_all(void)
{
      unsigned long flags;
      PMIC_STATUS rc = PMIC_SUCCESS;

      /* Deregister each of the PMIC events that we had previously
       * registered for by using pmic_event_subscribe().
       */

      if ((usb.eventMask & USB_DETECT_MINI_A) ||
          (usb.eventMask & USB_DETECT_MINI_B) ||
          (usb.eventMask & USB_DETECT_NON_USB_ACCESSORY) ||
          (usb.eventMask & USB_DETECT_FACTORY_MODE)) {
            //EVENT_AB_DETI or EVENT_IDI
            eventNotify.func = pmic_convity_event_handler;
            eventNotify.param = (void *)(CORE_EVENT_ABDET);

            if (pmic_event_unsubscribe(EVENT_IDI, eventNotify) ==
                PMIC_SUCCESS) {
                  /* Also acquire the spinlock here to avoid any possible race
                   * conditions with the interrupt handler.
                   */

                  spin_lock_irqsave(&lock, flags);

                  usb.eventMask &= ~(USB_DETECT_MINI_A |
                                 USB_DETECT_MINI_B |
                                 USB_DETECT_NON_USB_ACCESSORY |
                                 USB_DETECT_FACTORY_MODE);

                  spin_unlock_irqrestore(&lock, flags);
            } else {
                  pr_debug
                      ("%s: pmic_event_unsubscribe() for EVENT_AB_DETI failed\n",
                       __FILE__);
                  rc = PMIC_ERROR;
            }
      }

      else if ((usb.eventMask & USB_DETECT_0V8_RISE) ||
             (usb.eventMask & USB_DETECT_0V8_FALL)) {
            //EVENT_USB_08VI or EVENT_USBI
            eventNotify.func = pmic_convity_event_handler;
            eventNotify.param = (void *)(CORE_EVENT_0V8);
            if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
                PMIC_SUCCESS) {
                  /* Also acquire the spinlock here to avoid any possible race
                   * conditions with the interrupt handler.
                   */
                  spin_lock_irqsave(&lock, flags);

                  usb.eventMask &= ~(USB_DETECT_0V8_RISE |
                                 USB_DETECT_0V8_FALL);

                  spin_unlock_irqrestore(&lock, flags);
            } else {
                  pr_debug
                      ("%s: pmic_event_unsubscribe() for EVENT_USB_08VI failed\n",
                       __FILE__);
                  rc = PMIC_ERROR;
            }

      }

      else if ((usb.eventMask & USB_DETECT_2V0_RISE) ||
             (usb.eventMask & USB_DETECT_2V0_FALL)) {
            //EVENT_USB_20VI or EVENT_USBI
            eventNotify.func = pmic_convity_event_handler;
            eventNotify.param = (void *)(CORE_EVENT_2V0);
            if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
                PMIC_SUCCESS) {
                  /* Also acquire the spinlock here to avoid any possible race
                   * conditions with the interrupt handler.
                   */
                  spin_lock_irqsave(&lock, flags);

                  usb.eventMask &= ~(USB_DETECT_2V0_RISE |
                                 USB_DETECT_2V0_FALL);

                  spin_unlock_irqrestore(&lock, flags);
            } else {
                  pr_debug
                      ("%s: pmic_event_unsubscribe() for EVENT_USB_20VI failed\n",
                       __FILE__);
                  rc = PMIC_ERROR;
            }
      }

      else if ((usb.eventMask & USB_DETECT_4V4_RISE) ||
             (usb.eventMask & USB_DETECT_4V4_FALL)) {

            //EVENT_USB_44VI or EVENT_USBI
            eventNotify.func = pmic_convity_event_handler;
            eventNotify.param = (void *)(CORE_EVENT_4V4);

            if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
                PMIC_SUCCESS) {

                  /* Also acquire the spinlock here to avoid any possible race
                   * conditions with the interrupt handler.
                   */
                  spin_lock_irqsave(&lock, flags);

                  usb.eventMask &= ~(USB_DETECT_4V4_RISE |
                                 USB_DETECT_4V4_FALL);

                  spin_unlock_irqrestore(&lock, flags);
            } else {
                  pr_debug
                      ("%s: pmic_event_unsubscribe() for EVENT_USB_44VI failed\n",
                       __FILE__);
                  rc = PMIC_ERROR;
            }
      }

      if (rc == PMIC_SUCCESS) {
            /* Also acquire the spinlock here to avoid any possible race
             * conditions with the interrupt handler.
             */
            spin_lock_irqsave(&lock, flags);

            /* Restore the initial reset values for the callback function
             * and event mask parameters. This should be NULL and zero,
             * respectively.
             *
             * Note that we wait until the end here to fully reset everything
             * just in case some of the pmic_event_unsubscribe() calls above
             * failed for some reason (which normally shouldn't happen).
             */
            usb.callback = reset.callback;
            usb.eventMask = reset.eventMask;

            spin_unlock_irqrestore(&lock, flags);
      }
      return rc;
}

/*!
 * This is the default event handler for all connectivity-related events
 * and hardware interrupts.
 *
 * @param       param           event ID
 */
02295 static void pmic_convity_event_handler(void *param)
{
      unsigned long flags;

      /* Update the global list of active interrupt events. */
      spin_lock_irqsave(&lock, flags);
      eventID |= (PMIC_CORE_EVENT) (param);
      spin_unlock_irqrestore(&lock, flags);

      /* Schedule the tasklet to be run as soon as it is convenient to do so. */
      schedule_work(&convityTasklet);
}

/*!
 * @brief This is the connectivity driver tasklet that handles interrupt events.
 *
 * This function is scheduled by the connectivity driver interrupt handler
 * pmic_convity_event_handler() to complete the processing of all of the
 * connectivity-related interrupt events.
 *
 * Since this tasklet runs with interrupts enabled, we can safely call
 * the ADC driver, if necessary, to properly detect the type of USB connection
 * that is being made and to call any user-registered callback functions.
 *
 * @param   arg                The parameter that was provided above in
 *                                  the DECLARE_TASKLET() macro (unused).
 */
02322 static void pmic_convity_tasklet(struct work_struct *work)
{

      PMIC_CONVITY_EVENTS activeEvents = 0;
      unsigned long flags = 0;

      /* Check the interrupt sense bits to determine exactly what
       * event just occurred.
       */
      if (eventID & CORE_EVENT_4V4) {
            spin_lock_irqsave(&lock, flags);
            eventID &= ~CORE_EVENT_4V4;
            spin_unlock_irqrestore(&lock, flags);

            activeEvents |= pmic_check_sensor(SENSE_USB4V4S) ?
                USB_DETECT_4V4_RISE : USB_DETECT_4V4_FALL;

            if (activeEvents & ~usb.eventMask) {
                  /* The default handler for 4.4 V rising/falling edge detection
                   * is to simply ignore the event.
                   */
                  ;
            }
      }
      if (eventID & CORE_EVENT_2V0) {
            spin_lock_irqsave(&lock, flags);
            eventID &= ~CORE_EVENT_2V0;
            spin_unlock_irqrestore(&lock, flags);

            activeEvents |= pmic_check_sensor(SENSE_USB2V0S) ?
                USB_DETECT_2V0_RISE : USB_DETECT_2V0_FALL;
            if (activeEvents & ~usb.eventMask) {
                  /* The default handler for 2.0 V rising/falling edge detection
                   * is to simply ignore the event.
                   */
                  ;
            }
      }
      if (eventID & CORE_EVENT_0V8) {
            spin_lock_irqsave(&lock, flags);
            eventID &= ~CORE_EVENT_0V8;
            spin_unlock_irqrestore(&lock, flags);

            activeEvents |= pmic_check_sensor(SENSE_USB0V8S) ?
                USB_DETECT_0V8_RISE : USB_DETECT_0V8_FALL;

            if (activeEvents & ~usb.eventMask) {
                  /* The default handler for 0.8 V rising/falling edge detection
                   * is to simply ignore the event.
                   */
                  ;
            }
      }
      if (eventID & CORE_EVENT_ABDET) {
            spin_lock_irqsave(&lock, flags);
            eventID &= ~CORE_EVENT_ABDET;
            spin_unlock_irqrestore(&lock, flags);

            activeEvents |= pmic_check_sensor(SENSE_ID_GNDS) ?
                USB_DETECT_MINI_A : 0;

            activeEvents |= pmic_check_sensor(SENSE_ID_FLOATS) ?
                USB_DETECT_MINI_B : 0;
      }

      /* Begin a critical section here so that we don't register/deregister
       * for events or open/close the connectivity driver while the existing
       * event handler (if it is currently defined) is in the middle of handling
       * the current event.
       */
      spin_lock_irqsave(&lock, flags);

      /* Finally, call the user-defined callback function if required. */
      if ((usb.handle_state == HANDLE_IN_USE) &&
          (usb.callback != NULL) && (activeEvents & usb.eventMask)) {
            (*usb.callback) (activeEvents);
      }

      spin_unlock_irqrestore(&lock, flags);
}

/*@}*/

/**************************************************************************
 * Module initialization and termination functions.
 *
 * Note that if this code is compiled into the kernel, then the
 * module_init() function will be called within the device_initcall()
 * group.
 **************************************************************************
 */

/*!
 * @name Connectivity Driver Loading/Unloading Functions
 * These non-exported internal functions are used to support the connectivity
 * device driver initialization and de-initialization operations.
 */
/*@{*/

/*!
 * @brief This is the connectivity device driver initialization function.
 *
 * This function is called by the kernel when this device driver is first
 * loaded.
 */
02427 static int __init mc13783_pmic_convity_init(void)
{
      printk(KERN_INFO "PMIC Connectivity driver loading..\n");

      return 0;
}

/*!
 * @brief This is the Connectivity device driver de-initialization function.
 *
 * This function is called by the kernel when this device driver is about
 * to be unloaded.
 */
02440 static void __exit mc13783_pmic_convity_exit(void)
{
      printk(KERN_INFO "PMIC Connectivity driver unloading\n");

      /* Close the device handle if it is still open. This will also
       * deregister any callbacks that may still be active.
       */
      if (usb.handle_state == HANDLE_IN_USE) {
            pmic_convity_close(usb.handle);
      } else if (usb.handle_state == HANDLE_IN_USE) {
            pmic_convity_close(rs_232.handle);
      } else if (usb.handle_state == HANDLE_IN_USE) {
            pmic_convity_close(cea_936.handle);
      }

      /* Reset the PMIC Connectivity register to it's power on state.
       * We should do this when unloading the module so that we don't
       * leave the hardware in a state which could cause problems when
       * no device driver is loaded.
       */
      pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0, REG_FULLMASK);
      pmic_write_reg(REG_CHARGE_USB_SPARE, RESET_USBCNTRL_REG_1,
                   REG_FULLMASK);
      /* Note that there is no need to reset the "convity" device driver
       * state structure to the reset state since we are in the final
       * stage of unloading the device driver. The device driver state
       * structure will be automatically and properly reinitialized if
       * this device driver is reloaded.
       */
}

/*@}*/

/*
 * Module entry points and description information.
 */

module_init(mc13783_pmic_convity_init);
module_exit(mc13783_pmic_convity_exit);

MODULE_DESCRIPTION("mc13783 Connectivity device driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index