• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPI
2
3
4## Overview
5
6Serial Peripheral Interface (SPI) is a serial bus specification used for high-speed, full-duplex, and synchronous communication. In the Hardware Driver Foundation (HDF), the SPI module uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage.
7
8  **Figure 1** Independent service mode
9
10  ![image](figures/independent-service-mode.png)
11
12## Available APIs
13
14**SpiCntlrMethod**:
15
16
17```
18struct SpiCntlrMethod {
19  int32_t (*GetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg);
20  int32_t (*SetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg);
21  int32_t (*Transfer)(struct SpiCntlr *cntlr, struct SpiMsg *msg, uint32_t count);
22  int32_t (*Open)(struct SpiCntlr *cntlr);
23  int32_t (*Close)(struct SpiCntlr *cntlr);
24};
25```
26
27  **Table 1** Description of the callback functions in SpiCntlrMethod
28
29| Function| Input Parameter| Return Value| Description|
30| -------- | -------- | -------- | -------- |
31| Transfer | **cntlr**: structure pointer to the SPI controller at the core layer.<br>**msg**: structure pointer to the SPI message.<br>**count**: number of messages. The value is of the uint32_t type.| HDF_STATUS| Transfers messages.|
32| SetCfg | **cntlr**: structure pointer to the SPI controller at the core layer.<br>**cfg**: structure pointer to the SPI attributes.| HDF_STATUS| Sets SPI controller attributes.|
33| GetCfg | **cntlr**: structure pointer to the SPI controller at the core layer.<br>**cfg**: structure pointer to the SPI attributes.| HDF_STATUS| Obtains SPI controller attributes.|
34| Open | **cntlr**: structure pointer to the SPI controller at the core layer.| HDF_STATUS| Opens an SPI device.|
35| Close | **cntlr**: structure pointer to the SPI controller at the core layer.| HDF_STATUS| Closes an SPI device.|
36
37
38## How to Develop
39
40The SPI module adaptation involves the following steps:
41
421. Instantiate the driver entry.
43   - Instantiate the **HdfDriverEntry** structure.
44   - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF.
45
462. Configure attribute files.
47   - Add the **deviceNode** information to the **device_info.hcs** file.
48   - (Optional) Add the **spi_config.hcs** file.
49
503. Instantiate the SPI controller object.
51   - Initialize **SpiCntlr**.
52   - Instantiate **SpiCntlrMethod** in the **SpiCntlr** object.
53      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
54      > For details about the functions in **SpiCntlrMethod**, see [Available APIs](#available-apis).
55
564. Debug the driver.
57
58   (Optional) For new drivers, verify the basic functions, such as the SPI status control and response to interrupts.
59
60
61## Development Example
62
63The following uses **spi_hi35xx.c** as an example to present the information required for implementing device functions.
64
651. Instantiate the driver entry.
66
67   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**.
68
69   In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke.
70
71   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.
72
73   SPI driver entry example:
74
75   ```
76   struct HdfDriverEntry g_hdfSpiDevice = {
77       .moduleVersion = 1,
78       .moduleName = "HDF_PLATFORM_SPI",// (Mandatory) The value must be the same as that of moduleName in the .hcs file.
79       .Bind = HdfSpiDeviceBind,        // See the Bind function.
80       .Init = HdfSpiDeviceInit,        // See the Init function.
81       .Release = HdfSpiDeviceRelease,  //See the Release function.
82   };
83   // Call HDF_INIT to register the driver entry with the HDF.
84   HDF_INIT(g_hdfSpiDevice);
85   ```
86
872. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **spi_config.hcs** file.
88
89   The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **SpiCntlr** members at the core layer.
90
91   In this example, there is only one SPI controller. If there are multiple SPI controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **spi_config** file for each controller.
92
93   - **device_info.hcs** configuration example
94
95
96     ```
97     root {
98       device_info {
99         match_attr = "hdf_manager";
100         platform :: host {
101           hostName = "platform_host";
102           priority = 50;
103           device_spi :: device {                            // Configure an HDF device node for each SPI controller.
104             device0 :: deviceNode {
105               policy = 1;
106               priority = 60;
107               permission = 0644;
108               moduleName = "HDF_PLATFORM_SPI";
109               serviceName = "HDF_PLATFORM_SPI_0";
110               deviceMatchAttr = "hisilicon_hi35xx_spi_0";
111             }
112             device1 :: deviceNode {
113               policy = 1;
114               priority = 60;
115               permission = 0644;
116               moduleName = "HDF_PLATFORM_SPI";             // (Mandatory) Driver name, which must be the same as that of moduleName in the driver entry structure.
117               serviceName = "HDF_PLATFORM_SPI_1";           // (Mandatory) Unique name of the service published by the driver.
118               deviceMatchAttr = "hisilicon_hi35xx_spi_1";       // The value must be the same as that of match_attr in the .hcs file.
119             }
120             ...
121           }
122         }
123       }
124     }
125     ```
126
127   - **spi_config.hcs** configuration example
128
129
130     ```
131     root {
132       platform {
133         spi_config {                        // Configure private data for each SPI controller.
134           template spi_controller {           // Template configuration. In the template, you can configure the common parameters shared by device nodes.
135             serviceName = "";
136             match_attr = "";
137             transferMode = 0; // Data transfer mode. The value **0** indicates interrupt transfer, **1** indicates flow control transfer, and **2** indicates DMA transfer.
138             busNum = 0;           // Bus number.
139             clkRate = 100000000;
140             bitsPerWord = 8;                // Number of bits per word.
141             mode = 19;                      // SPI data input/output mode.
142             maxSpeedHz = 0;                 // Maximum clock frequency.
143             minSpeedHz = 0;                 // Minimum clock frequency.
144             speed = 2000000;                // Current message transfer speed.
145             fifoSize = 256;                 // FIFO size.
146             numCs = 1;                      // Chip select (CS) number.
147             regBase = 0x120c0000;           // Used for address mapping.
148             irqNum = 100;                   // Interrupt request (IRQ) number.
149             REG_CRG_SPI = 0x120100e4;       // CRG_REG_BASE(0x12010000) + 0x0e4
150             CRG_SPI_CKEN = 0;
151             CRG_SPI_RST = 0;
152             REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24
153             MISC_CTRL_SPI_CS = 0;
154             MISC_CTRL_SPI_CS_SHIFT = 0;
155           }
156           controller_0x120c0000 :: spi_controller {
157             busNum = 0;                           // (Mandatory) Bus number.
158             CRG_SPI_CKEN = 0x10000;               // (0x1 << 16) 0:close clk, 1:open clk
159             CRG_SPI_RST = 0x1;                    // (0x1 << 0) 0:cancel reset, 1:reset
160             match_attr = "hisilicon_hi35xx_spi_0";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
161           }
162           controller_0x120c1000 :: spi_controller {
163             busNum = 1;
164             CRG_SPI_CKEN = 0x20000;    // (0x1 << 17) 0:close clk, 1:open clk
165             CRG_SPI_RST = 0x2;         // (0x1 << 1) 0:cancel reset, 1:reset
166             match_attr = "hisilicon_hi35xx_spi_1";
167             regBase = 0x120c1000;      // (Mandatory) Used for address mapping.
168             irqNum = 101;              // (Mandatory) IRQ number.
169           }
170         ...
171         //(Optional) Add nodes to the device_info.hcs file as required.
172         }
173       }
174     }
175     ```
176
1773. Initialize the **SpiCntlr** 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 **SpiCntlrMethod** in **SpiCntlr** (so that the underlying driver functions can be called).
178
179   - Defining a custom structure
180
181     To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **spi_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **SpiCntlr** object at the core layer.
182
183
184      ```
185      struct Pl022 {// Corresponds to parameters in .hcs.
186          struct SpiCntlr *cntlr;
187          struct DListHead deviceList;
188          struct OsalSem sem;
189          volatile unsigned char *phyBase;
190          volatile unsigned char *regBase;
191          uint32_t irqNum;
192          uint32_t busNum;
193          uint32_t numCs;
194          uint32_t curCs;
195          uint32_t speed;
196          uint32_t fifoSize;
197          uint32_t clkRate;
198          uint32_t maxSpeedHz;
199          uint32_t minSpeedHz;
200          uint32_t regCrg;
201          uint32_t clkEnBit;
202          uint32_t clkRstBit;
203          uint32_t regMiscCtrl;
204          uint32_t miscCtrlCsShift;
205          uint32_t miscCtrlCs;
206          uint16_t mode;
207          uint8_t bitsPerWord;
208          uint8_t transferMode;
209      };
210
211      // SpiCntlr is the core layer controller structure. The Init function assigns values to the members of SpiCntlr.
212      struct SpiCntlr {
213          struct IDeviceIoService service;
214          struct HdfDeviceObject *device;
215          uint32_t busNum;
216          uint32_t numCs;
217          uint32_t curCs;
218          struct OsalMutex lock;
219          struct SpiCntlrMethod *method;
220          struct DListHead list;
221          void *priv;
222      };
223      ```
224
225   - Instantiating **SpiCntlrMethod** in **SpiCntlr** (other members are initialized by **Init**)
226
227
228      ```
229      // Example in spi_hi35xx.c: instantiate the hooks.
230      struct SpiCntlrMethod g_method = {
231          .Transfer = Pl022Transfer,
232          .SetCfg = Pl022SetCfg,
233          .GetCfg = Pl022GetCfg,
234          .Open = Pl022Open,
235          .Close = Pl022Close,
236      };
237      ```
238
239   - **Bind** function
240
241      **Input parameter**:
242
243      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
244
245      **Return value**:
246
247      **HDF_STATUS**
248
249      **Function description**:
250
251      Associates the **SpiCntlr** object with **HdfDeviceObject**.
252
253
254      ```
255      static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device)
256      {
257          ...
258          return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
259      }
260
261      struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device)
262      {
263          struct SpiCntlr *cntlr = NULL;                           // Create an SpiCntlr object.
264          ...
265          cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr));// Allocate memory.
266          ...
267          cntlr->device = device;                                  // Prerequisites for conversion between HdfDeviceObject and SpiCntlr.
268          device->service = &(cntlr->service);                     // Prerequisites for conversion between HdfDeviceObject and SpiCntlr.
269          (void)OsalMutexInit(&cntlr->lock);                       // Initialize the lock.
270          DListHeadInit(&cntlr->list);                             // Add nodes.
271          cntlr->priv = NULL;
272          return cntlr;
273      }
274      ```
275
276   - **Init** function
277
278      **Input parameter**:
279
280      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
281
282      **Return value**:
283
284      **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
285
286        **Table 2** Description of HDF_STATUS
287
288      | Status| Description|
289      | -------- | -------- |
290      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
291      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
292      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
293      | HDF_ERR_IO | I/O error.|
294      | HDF_SUCCESS | Initialization successful.|
295      | HDF_FAILURE | Initialization failed.|
296
297      **Function description**:
298
299      Initializes the custom structure object and **SpiCntlr**.
300
301
302      ```
303      static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device)
304      {
305      int32_t ret;
306      struct SpiCntlr *cntlr = NULL;
307      ...
308      cntlr = SpiCntlrFromDevice(device); // Forcibly convert HdfDeviceObject to SpiCntlr using service. For details about the value assignment, see the Bind function.
309                                          // return (device == NULL) ? NULL : (struct SpiCntlr *)device->service;
310      ...
311      ret = Pl022Init(cntlr, device);     // (Mandatory) Instantiate the custom operation object. The following is an example:
312      ...
313      ret = Pl022Probe(cntlr->priv);
314      ...
315      return ret;
316      }
317
318      static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device)
319      {
320      int32_t ret;
321      struct Pl022 *pl022 = NULL;
322      ...
323      pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022));// Request memory.
324      ...
325      ret = SpiGetBaseCfgFromHcs(pl022, device->property);  // Initialize busNum, numCs, speed, fifoSize, clkRate, mode, bitsPerWord, and transferMode.
326      ...
327      ret = SpiGetRegCfgFromHcs(pl022, device->property);   // Initialize regBase, phyBase, irqNum, regCrg, clkEnBit, clkRstBit, regMiscCtrl, regMiscCtrl, miscCtrlCs, and miscCtrlCsShift.
328      ...
329      // Calculate the frequencies corresponding to the maximum and minimum speeds.
330      pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN);
331      pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX);
332      DListHeadInit(&pl022->deviceList);// Initialize the DList linked list.
333      pl022->cntlr = cntlr;              // Prerequisite for conversion between Pl022 and SpiCntlr.
334      cntlr->priv = pl022;               // Prerequisite for conversion between Pl022 and SpiCntlr.
335      cntlr->busNum = pl022->busNum;     // Assign a value to busNum in SpiCntlr.
336      cntlr->method = &g_method;         // Attach the SpiCntlrMethod instance.
337      ...
338      ret = Pl022CreatAndInitDevice(pl022);
339      if (ret != 0) {
340          Pl022Release(pl022);           // Release the Pl022 object if the initialization fails.
341          return ret;
342      }
343      return 0;
344      }
345      ```
346   - **Release** function
347
348      **Input parameter**:
349
350      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
351
352      **Return value**:
353
354      No value is returned.
355
356      **Function description**:
357
358      Releases the memory and deletes the controller. This function assigns values to the **Release** function 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.
359
360      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
361      > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
362
363
364      ```
365      static void HdfSpiDeviceRelease(struct HdfDeviceObject *device)
366      {
367          struct SpiCntlr *cntlr = NULL;
368          ...
369          cntlr = SpiCntlrFromDevice(device);             // Forced conversion from HdfDeviceObject to SpiCntlr is involved. For details about the value assignment, see the Bind function.
370                                                          // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service;
371          ...
372          if (cntlr->priv != NULL) {
373              Pl022Remove((struct Pl022 *)cntlr->priv);// A forced conversion from SpiCntlr to Pl022 is involved.
374          }
375          SpiCntlrDestroy(cntlr);                         // Release the Pl022 object.
376      }
377      ```
378