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