1# Clock 2 3## Overview 4 5### Function 6 7The clock regulates the timing and speed of all functions in a device. For example, the CPU clock is an internal time generator of the CPU. It operates in frequency and used to synchronize and control the operations within the CPU. 8 9### Basic Concepts 10 11The clock signal is used to synchronize and control the operations of the modules or components of an electronic device. It serves as a fundamental signal reference source to ensure proper functioning and accurate data transmission. 12 13### Working Principles 14 15In the Hardware Driver Foundation (HDF), the **Clock** module uses the unified service mode for API adaptation. In this mode, a service is used as the clock manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple 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 **Clock** module uses the unified service mode (see **Figure 1**). 16 17The **Clock** module is divided into the following layers: 18- Interface layer: provides the APIs for opening a device, writing data, and closing a device. 19- Core layer: binds services, initializes and releases the PlatformManager, and provides the capabilities of adding, deleting, and obtaining controllers. 20- Adaptation layer: implements hardware-related functions, such as controller initialization. 21 22In 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. 23 24**Figure 1** Unified service mode 25 26 27 28 29## Usage Guidelines 30 31### When to Use 32 33The **Clock** module provides chip-level clock management, including frequency division, frequency multiplication, clock source selection, and clock gating within the chip. Proper clock management can enhance chip efficiency while streamlining coordination and synergy among all functional components. 34 35### Available APIs 36 37To enable applications to successfully operate the hardware by calling the **Clock** APIs, the system provides the following hook APIs in **//drivers/hdf_core/framework/support/platform/include/clock/clock_core.h** for the core layer. You need to implement these hook APIs at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. 38 39Definitions of **ClockMethod** and **ClockLockMethod**: 40 41```c 42struct ClockMethod { 43 int32_t (*start)(struct ClockDevice *device); 44 int32_t (*stop)(struct ClockDevice *device); 45 int32_t (*setRate)(struct ClockDevice *device, uint32_t rate); 46 int32_t (*getRate)(struct ClockDevice *device, uint32_t *rate); 47 int32_t (*disable)(struct ClockDevice *device); 48 int32_t (*enable)(struct ClockDevice *device); 49 struct ClockDevice *(*getParent)(struct ClockDevice *device); 50 int32_t (*setParent)(struct ClockDevice *device, struct ClockDevice *parent); 51}; 52 53struct ClockLockMethod { 54 int32_t (*lock)(struct ClockDevice *device); 55 void (*unlock)(struct ClockDevice *device); 56}; 57 58``` 59 60The **ClockMethod** method must be implemented at the adaptation layer, and **ClockLockMethod** can be implemented based on service requirements. The core layer provides the default **ClockLockMethod** method, in which a spinlock is used to protect the critical section. 61 62```c 63static int32_t ClockDeviceLockDefault(struct ClockDevice *device) 64{ 65 if (device == NULL) { 66 HDF_LOGE("ClockDeviceLockDefault: device is null!"); 67 return HDF_ERR_INVALID_OBJECT; 68 } 69 return OsalSpinLock(&device->spin); 70} 71 72static void ClockDeviceUnlockDefault(struct ClockDevice *device) 73{ 74 if (device == NULL) { 75 HDF_LOGE("ClockDeviceUnlockDefault: device is null!"); 76 return; 77 } 78 (void)OsalSpinUnlock(&device->spin); 79} 80 81static const struct ClockLockMethod g_clockLockOpsDefault = { 82 .lock = ClockDeviceLockDefault, 83 .unlock = ClockDeviceUnlockDefault, 84}; 85 86``` 87 88If spinlock cannot be used, you can use another type of lock to implement **ClockLockMethod**. The customized **ClockLockMethod** method will replace the default **ClockLockMethod** method. 89 90 **Table 1** Hook APIs in **ClockMethod** 91 92| API | Input Parameter| Output Parameter| Return Value| Description| 93| -------- | -------- | -------- | -------- | -------- | 94| start | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Starts a clock device. | 95| stop | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Stops a clock device. | 96| setRate | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Sets the clock rate. | 97| getRate | **device**: structure pointer to the clock controller at the core layer.| Clock rate obtained. | HDF_STATUS | Obtains the clock rate. | 98| disable | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Enables clock. | 99| enable | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Disables clock. | 100| getParent | **device**: structure pointer to the clock controller at the core layer.| Structure pointer to the clock controller obtained. | HDF_STATUS | Obtains the parent clock. | 101| setParent | **device**: structure pointer to the clock controller at the core layer.| –| HDF_STATUS | Sets the parent clock. | 102 103**Table 2** APIs in **ClockLockMethod** 104 105| Function| Input Parameter| Output Parameter| Return Value| Description| 106| -------- | -------- | -------- | -------- | -------- | 107| lock | **device**: structure pointer to the clock device object at the core layer.| –| HDF_STATUS| Acquires the critical section lock. | 108| unlock | **device**: structure pointer to the clock device object at the core layer.| –| HDF_STATUS| Releases the critical section lock.| 109 110### How to Develop 111 112The **Clock** module adaptation involves the following steps: 113 1141. Instantiate the driver entry. 115 - Instantiate the **HdfDriverEntry** structure. 116 - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. 117 1182. Configure attribute files. 119 - Add the **deviceNode** information to the **device_info.hcs** file. 120 - (Optional) Add the **clock_config.hcs** file. 121 1223. Instantiate the core layer APIs. 123 - Initialize **ClockDevice**. 124 - Instantiate **ClockMethod** in the **ClockDevice** object. 125 > **NOTE** 126 > 127 > For details about the functions in **ClockMethod**, see [Available APIs](#available-apis). 128 1294. (Optional) Debug the driver. 130 131 Verify the basic driver functionalities. 132 133 134### Example 135 136The following uses the RK3568 driver **//drivers/hdf_core/adapter/khdf/linux/platform/clock/clock_adapter.c** as an example to describe how to perform the clock driver adaptation. 137 1381. Instantiate the driver entry. 139 140 The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **//drivers/hdf_core/interfaces/inner_api/host/shared/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 is collected to form a segment address space similar to an array for the upper layer to invoke. 141 142 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. 143 144 Clock driver entry example: 145 146 Multiple devices may connect to the clock controller. In the HDF, a manager object needs to be created for this type of devices. When a device needs to be started, the manager object locates the target device based on the specified parameters. 147 148 You do not need to implement the driver of the clock manager, which is implemented by the core layer. However, the **ClockDeviceAdd** method of the core layer must be invoked in the **Init** method to implement the related features. 149 150 ```c 151 struct HdfDriverEntry g_clockLinuxDriverEntry = { 152 .moduleVersion = 1, 153 .Bind = NULL, 154 .Init = LinuxClockInit, 155 .Release = LinuxClockRelease, 156 .moduleName = "linux_clock_adapter", // (Mandatory) The value must be the same as the module name in the device_info.hcs file. 157 }; 158 HDF_INIT(g_clockLinuxDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. 159 160 /* Driver entry of the clock_core.c manager service at the core layer */ 161 struct HdfDriverEntry g_clockManagerEntry = { 162 .moduleVersion = 1, 163 .Bind = ClockManagerBind, 164 .Init = ClockManagerInit, 165 .Release = ClockManagerRelease, 166 .moduleName = "HDF_PLATFORM_CLOCK_MANAGER", // The value must be that of device0 in the device_info.hcs file. 167 }; 168 HDF_INIT(g_clockManagerEntry); 169 ``` 170 1712. Add the **deviceNode** information to the **//vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs** file and configure the device attributes in **clock_config.hcs**. 172 173 The **deviceNode** information is related to the driver entry registration. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **ClockDevice** members at the core layer. 174 175 In the unified service mode, the first device node in the **device_info.hcs** file must be the clock manager. The parameters must be set as follows: 176 177 | Parameter| Value| 178 | -------- | -------- | 179 | policy | Service publishing policy. For the clock controller, it is **2**, which means to publish services for both kernel- and user-mode processes. | 180 | priority | Loading priority of the cloud driver. The value range is 0 to 200. A larger value indicates a lower priority.<br/>It is set to **59** for the clock controller. | 181 | permission | Permission for the device node created. It is **0664** for the clock controller. | 182 | moduleName | **HDF_PLATFORM_CLOCK_MANAGER**. | 183 | serviceName | **HDF_PLATFORM_CLOCK_MANAGER**. | 184 | deviceMatchAttr | Reserved.| 185 186 Configure clock controller information from the second node. This node specifies a type of clock controllers rather than a clock controller. In this example, there is only one clock device. If there are multiple clock devices, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **clock_config** file for each device. 187 188 - **device_info.hcs** example 189 190 ``` 191 root { 192 device_info { 193 platform :: host { 194 device_clock :: device { 195 device0 :: deviceNode { 196 policy = 2; 197 priority = 59; 198 permission = 0644; 199 moduleName = "HDF_PLATFORM_CLOCK_MANAGER"; 200 serviceName = "HDF_PLATFORM_CLOCK_MANAGER"; 201 } 202 device1 :: deviceNode { 203 policy = 0; // The value 0 indicates that no service is published. 204 priority = 65; // Driver startup priority. 205 permission = 0644; // Permission for the device node created. 206 moduleName = "linux_clock_adapter"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. 207 deviceMatchAttr = "linux_clock_adapter_0"; // (Mandatory) Private data of the controller. The value must be the same as that of the controller 208 } 209 } 210 } 211 } 212 } 213 ``` 214 215 - **clock_config.hcs** example 216 217 The following uses RK3568 as an example. 218 219 ``` 220 root { 221 platform { 222 clock_config { 223 match_attr = "linux_clock_adapter_0"; 224 template clock_device { 225 } 226 device_clock_0x0000 :: clock_device { 227 deviceName = "/cpus/cpu@0"; 228 deviceIndex = 1; 229 } 230 } 231 } 232 } 233 ``` 234 235 After the **clock_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. 236 237 For example, if the **clock_config.hcs** file is in **//vendor/hihope/rk3568/hdf_config/hdf_config/khdf/hdf.hcs**, add the following statement to **hdf.hcs** of the product: 238 239 ``` 240 #include "platform/clock_config_linux.hcs" // Relative path of the configuration file 241 ``` 242 243 This example is based on RK3568 development board that runs the Standard system. The **hdf.hcs** file is in **//vendor/hihope/rk3568/hdf_config/ hdf_config/khdf/**. You can modify the file as required. 244 2453. Initialize the **ClockDevice** 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 **ClocMethod** in **ClocDevice** (so that the underlying driver functions can be called). 246 247 - Define a custom structure. 248 249 To the driver, the custom structure holds parameters and data. The DeviceResourceIface() function provided by the HDF reads **clock_config.hcs** to initialize the custom structure and passes some important parameters, such as the device number and bus number, to the **ClockDevice** object at the core layer. 250 ```c 251 /* ClockDevice is the core layer controller structure. The **Init()** function assigns values to the members of ClockDevice. */ 252 struct ClockDevice { 253 const struct ClockMethod *ops; 254 OsalSpinlock spin; 255 const char *deviceName; 256 const char *clockName; 257 uint32_t deviceIndex; 258 const struct ClockLockMethod *lockOps; 259 void *clk; 260 void *priv; 261 struct ClockDevice *parent; 262 }; 263 ``` 264 265- Instantiate the hook function structure **ClockMethod** of **ClockDevice**. 266 267 The **ClockLockMethod** is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in the **Init** function. 268 269 ```c 270 struct ClockMethod { 271 int32_t (*start)(struct ClockDevice *device); 272 int32_t (*stop)(struct ClockDevice *device); 273 int32_t (*setRate)(struct ClockDevice *device, uint32_t rate); 274 int32_t (*getRate)(struct ClockDevice *device, uint32_t *rate); 275 int32_t (*disable)(struct ClockDevice *device); 276 int32_t (*enable)(struct ClockDevice *device); 277 struct ClockDevice *(*getParent)(struct ClockDevice *device); 278 int32_t (*setParent)(struct ClockDevice *device, struct ClockDevice *parent); 279 }; 280 ``` 281 282 - Implement the **Init** function. 283 284 Input parameter: 285 286 **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 287 288 Return value: 289 290 **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/interfaces/inner_api/utils/hdf_base.h** file. 291 292 **Table 4** HDF_STATUS 293 294 | Value | Description | 295 | -------- | -------- | 296 | HDF_ERR_INVALID_OBJECT | Invalid controller object.| 297 | HDF_ERR_INVALID_PARAM | Invalid parameter.| 298 | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.| 299 | HDF_ERR_IO | I/O error.| 300 | HDF_SUCCESS | Transmission successful.| 301 | HDF_FAILURE | Transmission failed.| 302 303 Function description: 304 305 Initializes the custom structure object and **ClockDevice**, and calls the **ClockDeviceAdd** function at the core layer. 306 307 ```c 308 static int32_t LinuxClockInit(struct HdfDeviceObject *device) 309 { 310 int32_t ret = HDF_SUCCESS; 311 struct DeviceResourceNode *childNode = NULL; 312 313 if (device == NULL || device->property == NULL) { 314 HDF_LOGE("LinuxClockInit: device or property is null"); 315 return HDF_ERR_INVALID_OBJECT; 316 } 317 318 DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 319 ret = ClockParseAndDeviceAdd(device, childNode); 320 if (ret != HDF_SUCCESS) { 321 HDF_LOGE("LinuxClockInit: clock init fail!"); 322 return ret; 323 } 324 } 325 HDF_LOGE("LinuxClockInit: clock init success!"); 326 327 return HDF_SUCCESS; 328 } 329 330 static int32_t ClockParseAndDeviceAdd(struct HdfDeviceObject *device, struct DeviceResourceNode *node) 331 { 332 int32_t ret; 333 struct ClockDevice *clockDevice = NULL; 334 335 (void)device; 336 clockDevice = (struct ClockDevice *)OsalMemCalloc(sizeof(*clockDevice)); 337 if (clockDevice == NULL) { 338 HDF_LOGE("ClockParseAndDeviceAdd: alloc clockDevice fail!"); 339 return HDF_ERR_MALLOC_FAIL; 340 } 341 ret = ClockReadDrs(clockDevice, node); 342 if (ret != HDF_SUCCESS) { 343 HDF_LOGE("ClockParseAndDeviceAdd: read drs fail, ret: %d!", ret); 344 OsalMemFree(clockDevice); 345 return ret; 346 } 347 348 clockDevice->priv = (void *)node; 349 clockDevice->ops = &g_method; 350 351 ret = ClockDeviceAdd(clockDevice); 352 if (ret != HDF_SUCCESS) { 353 HDF_LOGE("ClockParseAndDeviceAdd: add clock device:%u fail!", clockDevice->deviceIndex); 354 OsalMemFree(clockDevice); 355 return ret; 356 } 357 358 return HDF_SUCCESS; 359 } 360 361 static int32_t ClockReadDrs(struct ClockDevice *clockDevice, const struct DeviceResourceNode *node) 362 { 363 int32_t ret; 364 struct DeviceResourceIface *drsOps = NULL; 365 366 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 367 if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { 368 HDF_LOGE("ClockReadDrs: invalid drs ops!"); 369 return HDF_ERR_NOT_SUPPORT; 370 } 371 ret = drsOps->GetUint32(node, "deviceIndex", &clockDevice->deviceIndex, 0); 372 if (ret != HDF_SUCCESS) { 373 HDF_LOGE("ClockReadDrs: read deviceIndex fail, ret: %d!", ret); 374 return ret; 375 } 376 377 drsOps->GetString(node, "clockName", &clockDevice->clockName, 0); 378 379 ret = drsOps->GetString(node, "deviceName", &clockDevice->deviceName, 0); 380 if (ret != HDF_SUCCESS) { 381 HDF_LOGE("ClockReadDrs: read deviceName fail, ret: %d!", ret); 382 return ret; 383 } 384 return HDF_SUCCESS; 385 } 386 ``` 387 388 389 390 - Implement the **Release** function. 391 392 Input parameter: 393 394 **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 395 396 Return value: 397 398 No value is returned. 399 400 Function description: 401 402 403 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. 404 405 ```c 406 static void LinuxClockRelease(struct HdfDeviceObject *device) 407 { 408 const struct DeviceResourceNode *childNode = NULL; 409 if (device == NULL || device->property == NULL) { 410 HDF_LOGE("LinuxClockRelease: device or property is null!"); 411 return; 412 } 413 DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 414 ClockRemoveByNode(childNode); 415 } 416 } 417 418 static void ClockRemoveByNode(const struct DeviceResourceNode *node) 419 { 420 int32_t ret; 421 int32_t deviceIndex; 422 struct ClockDevice *device = NULL; 423 struct DeviceResourceIface *drsOps = NULL; 424 425 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 426 if (drsOps == NULL || drsOps->GetUint32 == NULL) { 427 HDF_LOGE("ClockRemoveByNode: invalid drs ops!"); 428 return; 429 } 430 431 ret = drsOps->GetUint32(node, "deviceIndex", (uint32_t *)&deviceIndex, 0); 432 if (ret != HDF_SUCCESS) { 433 HDF_LOGE("ClockRemoveByNode: read deviceIndex fail, ret: %d!", ret); 434 return; 435 } 436 437 device = ClockDeviceGet(deviceIndex); 438 if (device != NULL && device->priv == node) { 439 ret = ClockStop(device); 440 if (ret != HDF_SUCCESS) { 441 HDF_LOGE("ClockRemoveByNode: close fail, ret: %d!", ret); 442 } 443 if (device->parent && device->parent->deviceName == NULL) { 444 ClockDeviceRemove(device->parent); 445 OsalMemFree(device->parent); 446 } 447 ClockDeviceRemove(device); 448 OsalMemFree(device); 449 } 450 } 451 ``` 4524. (Optional) Debug the driver. 453 Verify the basic driver functionalities. 454