• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# GPIO
2
3## Overview
4
5### Function
6
7A general-purpose input/output (GPIO) controller manages all GPIO pins by group. Each group of GPIO pins is associated with one or more registers. The GPIO controller manages the pins by reading data from and writing data to the registers.
8
9### Basic Concepts
10
11A GPIO can be used as an input, an output, or both, and is controllable by software.
12
13- GPIO input
14
15  When a GPIO is used as an input, it reads the level state (high or low) of each pin. Common input modes include analog input, floating input, pull-up input, and pull-down input.
16
17- GPIO output
18
19  When a GPIO is used as an output, it sets the pin level. Common output modes include open-drain output, push-pull output, multiplexed open-drain output, and multiplexed push-pull output.
20
21### Working Principles
22
23In the Hardware Driver Foundation (HDF), the GPIO module uses the unified service mode for API adaptation. In this mode, a device service is used as the GPIO manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode.
24
25In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller.
26
27The GPIO module is divided into the following layers:
28
29- Interface layer: provides APIs for operating GPIO pins.
30- Core layer: provides the capabilities of adding and removing a GPIO controller and managing GPIO pins. This layer interacts with the adaptation layer through hook functions to allow the GPIO chip drivers of different vendors to quickly access the HDF.
31- Adaptation layer: instantiates hook functions to implement specific features.
32
33**Figure 1** Unified service mode
34
35![](figures/unified-service-mode.png)
36
37## Development Guidelines
38
39### When to Use
40
41As a concept at the software layer, GPIO is used to manage GPIO pin resources. You can use the APIs provided by the GPIO module to control pins. Before using your GPIO driver with OpenHarmony, you need to perform GPIO driver adaptation. The following sections describe how to adapt the GPIO driver.
42
43### Available APIs
44
45To enable the upper layer to successfully operate GPIO pins by calling the GPIO APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/gpio/gpio_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer.
46
47**GpioMethod**:
48
49```c
50struct GpioMethod {
51    int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);                // Reserved.
52    int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);                // Reserved.
53    int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val);
54    int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val);
55    int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir);
56    int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir);
57    int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq);   // Reserved.
58    int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg);
59    int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local);
60    int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local);
61    int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local);
62}
63```
64
65**Table 1** Hook functions in **GpioMethod**
66
67| Function| Input Parameter| Output Parameter| Return Value| Description|
68| -------- | -------- | -------- | -------- | -------- |
69| write | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.<br>**val**: level value to write, which is of the uint16_t type.| –| HDF_STATUS| Writes the level for a GPIO pin.|
70| read | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.| **val**: level value read, which is of the uint16_t type.| HDF_STATUS| Reads the level of a GPIO pin.|
71| setDir | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.<br>**dir**: pin direction to set, which is of the uint16_t type.| –| HDF_STATUS| Sets the direction (input or output) for a GPIO pin.|
72| getDir | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.| **dir**: pin direction read, which is of the uint16_t type.| HDF_STATUS| Obtains the input or output direction of a GPIO pin.|
73| setIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.<br>**mode**: interrupt trigger mode, which can be edge or level. The value is of the uint16_t type.<br>**func**: pointer to the interrupt request (IRQ) handler.<br>**arg**: void pointer to the input parameters of the IRQ handler.| –| HDF_STATUS| Sets an IRQ function for a GPIO pin.|
74| unsetIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Cancels the IRQ function for a GPIO pin.|
75| enableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Enables interrupts for a GPIO pin.|
76| disableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.<br>**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Disables interrupts for a GPIO pin.|
77
78### How to Develop
79
80The GPIO module adaptation procedure is as follows:
81
821. Instantiate the driver entry.
832. Configure attribute files.
843. Instantiate the GPIO controller object.
854. Debug the driver.
86
87### Example
88
89The following uses the **//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the driver adaptation.
90
911. Instantiate the driver entry.
92
93   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**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers are collected to form a segment address space similar to an array for the upper layer to invoke.
94   Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
95
96   GPIO driver entry example:
97
98   ```c
99   struct HdfDriverEntry g_gpioDriverEntry = {
100       .moduleVersion = 1,
101       .Bind = Pl061GpioBind,                // GPIO does not use the Bind function, which is an empty implementation in this example. You can add related operations as required.
102       ..Init = Pl061GpioInit,               // See the Init function.
103       .Release = Pl061GpioRelease,          // See the Release function.
104       .moduleName = "hisi_pl061_driver",    // (Mandatory) The value must be the same as that of moduleName in the .hcs file.
105   };
106   HDF_INIT(g_gpioDriverEntry);              // Call HDF_INIT to register the driver entry with the HDF.
107   ```
108
1092. Configure attribute files.
110
111   Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is closely related to driver entry registration. In this example, there is only one GPIO controller. If there are multiple GPIO controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values are closely related to the default values or value ranges of the **GpioCntlr** members at the core layer, and are configured in **gpio_config.hcs**.
112
113   - **device_info.hcs** example
114
115      Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file.
116
117      ```c
118      root {
119          device_info {
120              platform :: host {
121                  hostName = "platform_host";
122                  priority = 50;
123                  device_gpio :: device {
124                      device0 :: deviceNode {
125                       policy = 0;                                 // The value 0 indicates that no service needs to be published.
126                      priority = 10;                               // Driver startup priority.
127                      permission = 0644;                           // Permission for the device node created.
128                      oduleName = "hisi_pl061_driver";             // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
129                      deviceMatchAttr = "hisilicon_hi35xx_pl061";  // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs.
130                                                                   // The private information about all controllers is in the gpio_config.hcs file.
131                      }
132                  }
133              }
134          }
135      }
136      ```
137
138   - **gpio_config.hcs** example
139
140      Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs** file. The parameters are as follows:
141
142      ```c
143      root {
144          platform {
145              gpio_config {
146                  controller_0x120d0000 {
147                      match_attr = "hisilicon_hi35xx_pl061";   // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
148                      groupNum = 12;                           // (Mandatory) GPIO group number.
149                      bitNum = 8;                              // (Mandatory) Number of GPIO pins in each group.
150                      regBase = 0x120d0000;                    // (Mandatory) Physical base address.
151                      regStep = 0x1000;                        // (Mandatory) Register offset step.
152                      irqStart = 48;                           // (Mandatory) Enable interrupts.
153                      irqShare = 0;                            // (Mandatory) Whether to share interrupt. The value 1 means to share interrupt; the value 0 means the opposite.
154                  }
155                  ...
156              }
157          }
158      }
159      ```
160
161      After the **gpio_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect.
162
163      ```c
164      #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // Relative path of the gpio_config.hcs file.
165      ```
166
1673. Instantiate the GPIO controller object.
168
169   Initialize the **GpioCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **GpioMethod** in **GpioCntlr** (so that the underlying driver functions can be called).
170
171   - Define a custom structure.
172
173      To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **gpio_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the GPIO group number and the number of pins, to the **GpioCntlr** object at the core layer.
174
175      ```c
176      // Define the GPIO group information.
177      struct Pl061GpioGroup {
178          struct GpioCntlr cntlr             // (Mandatory) Control object of the core layer.
179          volatile unsigned char *regBase;   // (Mandatory) Register base address.
180          unsigned int index;
181          unsigned int irq;
182          OsalIRQHandle irqFunc;
183          OsalSpinlock lock;
184          uint32_t irqSave;
185          bool irqShare;
186          struct PlatformDumper *dumper;
187          char *dumperName;
188      };
189
190      struct Pl061GpioData {
191          volatile unsigned char *regBase;   // (Mandatory) Register base address.
192          uint32_t phyBase;                  // (Mandatory) Physical base address.
193          uint32_t regStep;;                 // (Mandatory) Register offset step.
194          uint32_t irqStart;                 // (Mandatory) Enable interrupts.
195          uint16_t groupNum;                 // (Mandatory) Parameter of the GPIO port number.
196          uint16_t bitNum;                   // (Mandatory) Parameter of the GPIO port number.
197          uint8_t irqShare;                  // (Mandatory) Whether to share interrupt.
198          struct Pl061GpioGroup *groups;     // (Optional) Set as required.
199          struct GpioInfo *gpioInfo;
200          void *priv;
201      };
202
203      struct GpioInfo {
204          struct GpioCntlr *cntlr;
205          char name[GPIO_NAME_LEN];
206          OsalSpinlock spin;
207          uint32_t irqSave;
208          struct GpioIrqRecord *irqRecord;
209      };
210      // GpioCntlr is the controller structure at the core layer. The Init function assigns values to the members of GpioCntlr.
211      struct GpioCntlr {
212          struct PlatformDevice device;
213          struct GpioMethod *ops;
214          uint16_t start;
215          uint16_t count;
216          struct GpioInfo *ginfos;
217          bool isAutoAlloced;
218          void *priv;
219      };
220      ```
221
222   - Instantiate the **GpioMethod** structure in **GpioCntlr**.
223
224      ```c
225      // The members of the GpioMethod structure are hook functions. You need to implement them by referring to Table 1.
226      static struct GpioMethod g_method = {
227          .request = NULL,
228          .release = NULL,
229          .write = Pl061GpioWrite,              // Write the pin level.
230          .read = Pl061GpioRead,                // Read the pin level.
231          .setDir = Pl061GpioSetDir,            // Set the pin direction.
232          .getDir = Pl061GpioGetDir,            // Obtain the pin direction.
233          .toIrq = NULL,
234          .setIrq = Pl061GpioSetIrq,            // Set an IRQ function for a pin. Skip it if this capability is not available.
235          .unsetIrq = Pl061GpioUnsetIrq,        // Cancel the IRQ function for a pin. Skip it if this capability is not available.
236          .enableIrq = Pl061GpioEnableIrq,      // Enable interrupts for a pin. Skip it if this capability is not available.
237          .disableIrq = Pl061GpioDisableIrq,    // Disable interrupts for a pin. Skip it if this capability is not available.
238      };
239      ```
240
241   - Implement the **Init** function.
242
243      Input parameters:
244
245      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
246
247      Return value:
248
249      **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file.
250
251      **Table 2** HDF_STATUS
252
253      | Status| Description|
254      | -------- | -------- |
255      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
256      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
257      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
258      | HDF_ERR_IO | I/O error.|
259      | HDF_SUCCESS | Initialization successful.|
260      | HDF_FAILURE | Initialization failed.|
261
262      Function description:
263
264      Initializes the custom structure object and **GpioCntlr**, calls **GpioCntlrAdd()** at the core layer, and (optional) accesses the virtual file system (VFS).
265
266      ```c
267      static struct Pl061GpioData g_pl061 = {
268          .groups = NULL,
269          .groupNum = PL061_GROUP_MAX,
270          .bitNum = PL061_BIT_MAX,
271      };
272
273      static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061)
274      {
275          int32_t ret;
276          uint16_t i;
277          struct Pl061GpioGroup *groups = NULL;
278
279          if (pl061 == NULL) {
280              return HDF_ERR_INVALID_PARAM;
281          }
282
283          groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum);
284          if (groups == NULL) {
285              return HDF_ERR_MALLOC_FAIL;
286          }
287          pl061->groups = groups;
288
289          for (i = 0; i < pl061->groupNum; i++) {
290              // Initialize related information.
291              groups[i].index = i;
292              groups[i].regBase = pl061->regBase + i * pl061->regStep;
293              groups[i].irq = pl061->irqStart + i;
294              groups[i].irqShare = pl061->irqShare;
295              groups[i].cntlr.start = i * pl061->bitNum;
296              groups[i].cntlr.count = pl061->bitNum;
297              groups[i].cntlr.ops = &g_method;
298              groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum];
299
300              if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) {
301                  goto ERR_EXIT;
302              }
303
304              ret = GpioCntlrAdd(&groups[i].cntlr); // Add related information to the HDF core.
305              if (ret != HDF_SUCCESS) {
306                  HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__,
307                      groups[i].cntlr.start, groups[i].cntlr.count, ret);
308                  (void)OsalSpinDestroy(&groups[i].lock);
309                  goto ERR_EXIT;
310              }
311          }
312          return HDF_SUCCESS;
313
314      ERR_EXIT:
315          while (i-- > 0) {
316              GpioCntlrRemove(&groups[i].cntlr);
317              (void)OsalSpinDestroy(&groups[i].lock);
318          }
319          pl061->groups = NULL;
320          OsalMemFree(groups);
321          return ret;
322      }
323
324      static int32_t Pl061GpioInit(struct HdfDeviceObject *device)
325      {
326          int32_t ret;
327          struct Pl061GpioData *pl061 = &g_pl061;
328
329          if (device == NULL || device->property == NULL) {
330              HDF_LOGE("%s: device or property null!", __func__);
331              return HDF_ERR_INVALID_OBJECT;
332          }
333
334          pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM);
335          if (pl061->gpioInfo == NULL) {
336              HDF_LOGE("%s: failed to calloc gpioInfo!", __func__);
337              return HDF_ERR_MALLOC_FAIL;
338          }
339
340          ret = Pl061GpioReadDrs(pl061, device->property);// Use the attribute values read from the gpio_config.hcs file to initialize the members of the custom structure object.
341          if (ret != HDF_SUCCESS) {
342              HDF_LOGE("%s: failed to read drs:%d", __func__, ret);
343              return ret;
344          }
345
346          if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 ||
347              pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) {
348              HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum);
349              return HDF_ERR_INVALID_PARAM;
350          }
351
352          pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);// Create address mapping.
353          if (pl061->regBase == NULL) {
354              HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase);
355              return HDF_ERR_IO;
356          }
357
358          ret = Pl061GpioInitGroups(pl061); // Initialize the group information and add it to the HDF core layer.
359          if (ret != HDF_SUCCESS) {
360              HDF_LOGE("%s: err init groups:%d", __func__, ret);
361              OsalIoUnmap((void *)pl061->regBase);
362              pl061->regBase = NULL;
363              return ret;
364          }
365          pl061->priv = (void *)device->property;
366          device->priv = (void *)pl061;
367          Pl061GpioDebug(pl061);
368
369      #ifdef PL061_GPIO_USER_SUPPORT
370          if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) {
371              HDF_LOGE("%s: add vfs fail!", __func__);
372          }
373      #endif
374          HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device));
375          return HDF_SUCCESS;
376      }
377      ```
378
379   - Implement the **Release** function.
380
381      Input parameters:
382
383      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
384
385      Return value:
386
387      No value is returned.
388
389      Function description:
390
391      Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources.
392
393      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
394      >
395      > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
396
397      ```c
398      static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061)
399      {
400          uint16_t i;
401          struct Pl061GpioGroup *group = NULL;
402
403          for (i = 0; i < pl061->groupNum; i++) {
404              group = &pl061->groups[i];
405              GpioDumperDestroy(&pl061->groups[i]);
406              GpioCntlrRemove(&group->cntlr); // Remove from the HDF core layer.
407          }
408
409          OsalMemFree(pl061->groups);
410          pl061->groups = NULL;
411      }
412
413      static void Pl061GpioRelease(struct HdfDeviceObject *device)
414      {
415          struct Pl061GpioData *pl061 = NULL;
416
417          HDF_LOGI("%s: enter", __func__);
418          if (device == NULL) {
419              HDF_LOGE("%s: device is null!", __func__);
420              return;
421          }
422
423      #ifdef PL061_GPIO_USER_SUPPORT
424          GpioRemoveVfs();
425      #endif
426
427          pl061 = (struct Pl061GpioData *)device->priv;
428          if (pl061 == NULL) {
429              HDF_LOGE("%s: device priv is null", __func__);
430              return;
431          }
432
433          Pl061GpioUninitGroups(pl061);
434          OsalMemFree(pl061->gpioInfo);
435          pl061->gpioInfo = NULL;
436          OsalIoUnmap((void *)pl061->regBase);
437          pl061->regBase = NULL;
438      }
439      ```
440
4414. Debug the driver.
442
443   (Optional) For new drivers, verify the basic functions, such as the GPIO status control and response to interrupts.
444