Pin

Overview

Pin

The pin, also called pin controller, manages pin resources of system on a chip (SoC) vendors and provides the pin multiplexing function.

Basic Concepts

Pin, as a software concept, provides APIs for uniformly managing the pins from different SoC vendors, providing the pin multiplexing function, and configuring the electrical features of pins.

  • SoC

    An SOC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes.

  • Pin multiplexing

    When the number of pins of a chip cannot handle the increasing connection requests, you can set the software registers to make the pins to work in different states.

Working Principles

In the HDF, the pin module does not support the user mode and therefore does not need to publish services. It uses the service-free mode in interface adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the OS that does not distinguish the user mode and the kernel mode. The DevHandle, a void pointer, directly points to the kernel mode address of the device object.

The pin module is divided into the following layers:

  • Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and functions of a pin, and releasing a pin.
  • Core layer: provides the capabilities of matching pin resources and adding, removing, and managing pin controllers. The core layer interacts with the adaptation layer by using hooks.
  • Adaptation layer: instantiates hooks to implement specific functions.

Figure 1 Service-free mode

Constraints

Currently, the pin module supports only the kernels (LiteOS) of mini and small systems.

Development Guidelines

When to Use

The pin module is used to manage pin resources. When the devices from SoC vendors interconnect with the HDF, the pin driver needs to be adapted.

Available APIs

The PinCntlrMethod APIs are used to call the functions of the pin driver. PinCntlrMethod definition:

struct PinCntlrMethod {
    int32_t (*SetPinPull)(struct PinCntlr *cntlr, uint32_t index, enum PinPullType pullType);
    int32_t (*GetPinPull)(struct PinCntlr *cntlr, uint32_t index, enum PinPullType *pullType);
    int32_t (*SetPinStrength)(struct PinCntlr *cntlr, uint32_t index, uint32_t strength);
    int32_t (*GetPinStrength)(struct PinCntlr *cntlr, uint32_t index, uint32_t *strength);
    int32_t (*SetPinFunc)(struct PinCntlr *cntlr, uint32_t index, const char *funcName);
    int32_t (*GetPinFunc)(struct PinCntlr *cntlr, uint32_t index, const char **funcName);
};

Table 1 APIs for the members in the PinCntlrMethod structure

API Input Parameter Output Parameter Return Value Description
SetPinPull cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
pullType: pull type of the pin. It is an enum constant.
- HDF_STATUS Sets the pull type of a pin.
GetPinPull cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
pullType: pointer to the pull type of the pin. HDF_STATUS Obtains the pull type of a pin.
SetPinStrength cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
strength: pull strength of the pin. It is a uint32_t variable.
- HDF_STATUS Sets the pull strength of a pin.
GetPinStrength cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
strength: pointer to the pull strength of the pin. HDF_STATUS Obtains the pull strength of a pin.
SetPinFunc cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
funcName: char pointer to the pin function.
- HDF_STATUS Sets the pin function.
GetPinFunc cntlr: structure pointer to the pin controller at the core layer.
index: pin index, which is a uint32_t variable.
funcName: char double pointer to the pin function. HDF_STATUS Obtains the pin function.

How to Develop

The pin module adaptation procedure is as follows:

  • Instantiate the driver entry.
  • Configure attribute files.
  • Instantiate the core layer APIs.
  • Debug the driver.
  1. Instantiate the driver entry.

    • Instantiate the HdfDriverEntry structure. Instantiate the driver entry. The driver entry must be a global variable of the HdfDriverEntry type (defined in hdf_device_desc.h), and the value of moduleName must be the same as that in device_info.hcs.

    • Call HDF_INIT to register the HdfDriverEntry instance with the HDF. Generally, the HDF calls the Init() function to load the driver. If Init() fails to be called, the HDF calls Release() to release driver resources and exit.

      static struct HdfDriverEntry g_hi35xxPinDriverEntry = {
          .moduleVersion = 1,
          .Bind = Hi35xxPinBind,
          .Init = Hi35xxPinInit,
          .Release = Hi35xxPinRelease,
          .moduleName = "hi35xx_pin_driver",// (Mandatory) The value must be the same as that of moduleName in the .hcs file.
      };
      HDF_INIT(g_hi35xxPinDriverEntry);// Call HDF_INIT to register the driver entry with the HDF.
      
  2. Configure attribute files.

    • Add the device node description to the vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs file.
      root {
          device_info { 
              platform :: host {
                  hostName = "platform_host";
                  priority = 50;
                  device_pin :: device {
                      device0:: deviceNode {   // Set an HDF device node for each pin controller.
                          policy = 0;			  // 2: visible in user mode; 1: visible in kernel mode; 0: no service required.
                          priority = 10;        // Driver startup priority.
                          permission = 0644;    // Permission to create device nodes for the driver.
                          /* (Mandatory) Driver name, which must be the same as the moduleName in the driver entry. */
                          moduleName = "hi35xx_pin_driver";
                          /* (Mandatory) Set the controller private data, which must be same as that in pin_config.hcs. */
                          deviceMatchAttr = "hisilicon_hi35xx_pin_0";
                      }
                      device1 :: deviceNode {
                          policy = 0;
                          priority = 10;
                          permission = 0644;
                          moduleName = "hi35xx_pin_driver";
                          deviceMatchAttr = "hisilicon_hi35xx_pin_1";
                      }
                      ......
                  }
              }
          }
      }
      
    • Add the pin_config.hcs file. Configure the device attributes in the device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs file. The parameters are set as follows:
      root {
          platform {
              pin_config_hi35xx {
                  template pin_controller {    // (Mandatory) Template configuration. In the template, you can configure the common parameters shared by device nodes.
                      number = 0;              // (Mandatory) Controller ID.
                      regStartBasePhy = 0;     // (Mandatory) Start physical base address of the register.
                      regSize = 0;           // (Mandatory) Register bit width.
                      PinCount = 0;            // (Mandatory) Number of pins.
                      match_attr = "";
                      template pin_desc {
                          pinName = "";        // (Mandatory) Name of the pin. 
                          init = 0;           // (Mandatory) Default value of the register.
                          F0 = "";             // (Mandatory) Function 0.
                          F1 = "";             // Function 1.
                          F2 = "";             // Function 2.
                          F3 = "";             // Function 3.
                          F4 = "";             // Function 4.
                          F5 = "";             // Function 5.
                      }
                  }
                  controller_0 :: pin_controller {
                      number = 0;
                      regStartBasePhy = 0x10FF0000;
                      regSize = 0x48;
                      pinCount = 18;
                      match_attr = "hisilicon_hi35xx_pin_0";
                      T1 :: pin_desc {
                          pinName = "T1";
                          init = 0x0600;
                          F0 = "EMMC_CLK";
                          F1 = "SFC_CLK";
                          F2 = "SFC_BOOT_MODE";
                      }
                      ...... // Correspond to the pins of the pin controller. Add pins according to actual situation.
                  }
                  ...// Each pin controller corresponds to a controller node. If there are multiple pin controllers, add the corresponding controller nodes one by one.
              }
          }
      }
      
  3. Instantiate the pin controller object.

    • Initialize the PinCntlr object. Call Hi35xxPinCntlrInit to initialize the PinCntlr members.

      struct Hi35xxPinDesc {
          // Pin name.
          const char *pinName;
          // Initial value.
          uint32_t init;
          // Index of the pin.
          uint32_t index;
          // Pull type of the pin.
          int32_t pullType;
          // Pull strength of the pin.
          int32_t strength;
          // Array of pin function names.
          const char *func[HI35XX_PIN_FUNC_MAX];
      };
      
      struct Hi35xxPinCntlr {
          // Pin controller.
          struct PinCntlr cntlr;
          // Pointer to the pin description structure.
          struct Hi35xxPinDesc *desc;
          // Register mapping address.
          volatile unsigned char *regBase;
          // ID of the pin controller.
          uint16_t number;
          // Start address of the register physical base addresses.
          uint32_t regStartBasePhy;
          // Register bit width.
          uint32_t regSize;
          // Number of pins.
          uint32_t pinCount;
      };
      
      // PinCntlr is the controller structure at the core layer. Its members are assigned with values by using the Init() function.
      struct PinCntlr {
          struct IDeviceIoService service;
          struct HdfDeviceObject *device;
          struct PinCntlrMethod *method;
          struct DListHead node; // Node in the linked list.
          OsalSpinlock spin;      // Spinlock.
          uint16_t number;       // ID of the pin controller.
          uint16_t pinCount;      // Number of pins.
          struct PinDesc *pins;
          void *priv;             // Private data.
      };
      
      // Initialize PinCntlr.
      static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx)
      {
          struct DeviceResourceIface *drsOps = NULL;
          int32_t ret;
          // Read the pin controller attributes from the .hcs file.
          drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
          if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
              HDF_LOGE("%s: invalid drs ops fail!", __func__);
              return HDF_FAILURE;
          }
          ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0);
          if (ret != HDF_SUCCESS) {
              HDF_LOGE("%s: read number failed", __func__);
              return ret;
          }
          
          ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0);
          if (ret != HDF_SUCCESS) {
              HDF_LOGE("%s: read regStartBasePhy failed", __func__);
              return ret;
          }
          ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0);
          if (ret != HDF_SUCCESS) {
              HDF_LOGE("%s: read regSize failed", __func__);
              return ret;
          }
          ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0);
          if (ret != HDF_SUCCESS) {
              HDF_LOGE("%s: read pinCount failed", __func__);
              return ret;
          }
          // Assign the values read to the members of the pin controller to initialize the pin controller.
          hi35xx->cntlr.pinCount = hi35xx->pinCount;
          hi35xx->cntlr.number = hi35xx->number;
          hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // Pin controller mapping.
          if (hi35xx->regBase == NULL) {
              HDF_LOGE("%s: remap Pin base failed", __func__);
              return HDF_ERR_IO;
          }
          hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount);
          hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount);
          return HDF_SUCCESS;
      }
      
    • Instantiate the callback structure PinCntlrMethod in PinCntlr. Other members are initialized by using the Init() function.

      // The members of the PinCntlrMethod structure are all callbacks. Vendors need to implement the corresponding functions according to Table 1.
      static struct PinCntlrMethod g_method = {
          .SetPinPull = Hi35xxPinSetPull,              // Set the pull type.
          .GetPinPull = Hi35xxPinGetPull,             // Obtains the pull type.
          .SetPinStrength = Hi35xxPinSetStrength,      // Set the pull strength.
          .GetPinStrength = Hi35xxPinGetStrength,      // Obtains the pull strength.
          .SetPinFunc = Hi35xxPinSetFunc,              // Set the pin functions.
          .GetPinFunc = Hi35xxPinGetFunc,              // Obtain the pin functions.
      };
      
    • Init() function

      Input parameters: HdfDeviceObject, an interface parameter exposed by the driver, contains the .hcs information.

      Return value: HDF_STATUS (The following table lists some states. For more details, see HDF_STATUS in /drivers/framework/include/utils/hdf_base.h.)

State Description
HDF_ERR_INVALID_OBJECT Invalid controller object.
HDF_ERR_MALLOC_FAIL Failed to allocate memory.
HDF_ERR_INVALID_PARAM Invalid parameter.
HDF_ERR_IO I/O error.
HDF_SUCCESS Initialization successful.
HDF_FAILURE Initialization failed.
    Function description:
    Initializes the custom structure object and **PinCntlr** members, and connects to the pin controller by calling the **PinCntlrAdd()** function.
    
    ```c
    static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps)
    {
        int32_t ret;
        uint32_t funcNum = 0;
        // Read the pin function names of the pin controller child nodes from the .hcs file.
        ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL");
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: read F0 failed", __func__);
            return ret;
        }

        funcNum++;
        ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL");
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: read F1 failed", __func__);
            return ret;
        }

        funcNum++;
        ......
        return HDF_SUCCESS;
    }

    static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index)
    {
        int32_t ret;
        struct DeviceResourceIface *drsOps = NULL;
        // Read the pin attributes of the pin controller child nodes from the .hcs file.
        drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
        if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {
            HDF_LOGE("%s: invalid drs ops fail!", __func__);
            return HDF_FAILURE;
        }
        ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL");
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: read pinName failed", __func__);
            return ret;
        }
        ......
        ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s:Pin read Func failed", __func__);
            return ret;
        }
        hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName;
        hi35xx->cntlr.pins[index].priv = (void *)node;
        ......
        return HDF_SUCCESS;
    }

    static int32_t Hi35xxPinInit(struct HdfDeviceObject *device)
    {
        ......
        struct Hi35xxPinCntlr *hi35xx = NULL;
        ......
        ret = Hi35xxPinCntlrInit(device, hi35xx);    // Initialize the pin controller.
        ......
        DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // Traverses each child node of the pin controller.
            ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // Parsing the child nodes.
            ......
        }

        hi35xx->cntlr.method = &g_method;   // Instantiate method.
        ret = PinCntlrAdd(&hi35xx->cntlr);   // Connect to the controller.
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: add Pin cntlr: failed", __func__);
            ret = HDF_FAILURE;
        }
        return HDF_SUCCESS;
    }
    ```
  • Release() function

    Input parameters:

    HdfDeviceObject, an interface parameter exposed by the driver, contains the .hcs information.

    Return value:

    Function description:

    Releases memory and deletes the controller. This function assigns a value to the Release API in the driver entry structure. If the HDF fails to call the Init() function to initialize the driver, the Release() function can be called to release driver resources.

    static void Hi35xxPinRelease(struct HdfDeviceObject *device)
    {
        int32_t ret;
        uint16_t number;
        struct PinCntlr *cntlr = NULL;
        struct Hi35xxPinCntlr *hi35xx = NULL;
        struct DeviceResourceIface *drsOps = NULL;
    
        if (device == NULL || device->property == NULL) {
            HDF_LOGE("%s: device or property is null", __func__);
            return;
        }
        // Read the pin controller ID from the .hcs file.
        drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
        if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {   
            HDF_LOGE("%s: invalid drs ops", __func__);
            return;
        }
        ret = drsOps->GetUint16(device->property, "number", &number, 0);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: read cntlr number failed", __func__);
            return;
        }
     
        cntlr = PinCntlrGetByNumber(number);   // Obtain the pin controller based on the controller ID. 
        PinCntlrRemove(cntlr);
        hi35xx = (struct Hi35xxPinCntlr *)cntlr;
        if (hi35xx != NULL) {
            if (hi35xx->regBase != NULL) {
                OsalIoUnmap((void *)hi35xx->regBase);
            }
            OsalMemFree(hi35xx);
        }
    }
    
  1. Debug the driver. (Optional) Verify basic functionalities of new drivers. For example, verify the information returned when the driver is loaded and whether data is successfully transmitted.