ACPI Clock Management

Device drivers need access to the clock framework to function properly. In some cases, they only need to know the base clock rate, while in others (such as audio or display), the clock rate needs to be changed based on the required sampling rate or resolution. Additionally, device drivers may also use clock gating in suspend mode to save power. Many existing Arm platforms use ACPI to provide the fixed clock frequency used by devices through the clock-frequency property in Device specific data (_DSD) or hardcoded in the driver code. However, this method only works for fixed clock frequencies and cannot be used for clock gating or setting different clock rates. The ACPI v6.5 specification introduced the ClockInput resource, which supports fixed and variable frequency properties to handle complex clock management cases.

This page captures our investigation into the existing mechanisms and suggestions for handling complex clock management cases as mentioned above.

ACPI ClockInput Resource

Please checkout https://linaro.atlassian.net/wiki/spaces/CLIENTPC/pages/28822175758 to see our investigations into multiple approaches to leverage ACPI ClockInput Resources.

ACPI Clock Control from AML using SCMI

Linux Kernel

https://gitlab.com/LinaroLtd/clientpc/linux/-/merge_requests/5

  • Add support for evaluating GCLK AML method to get clock rate for I2C and UART

EDK2

https://gitlab.com/LinaroLtd/clientpc/edk2-platforms/-/merge_requests/4

  • Implements SCMI protocol support

  • SCMI AML methods to set clock state and get clock rate

  • Device power management for I2C and UART

  • GCLK AML method to get clock rate using SCMI

TF-A

https://gitlab.com/LinaroLtd/clientpc/tf-a/-/merge_requests/2/

  • Add support for SCMI protocol

  • Implements a simple clock driver for I.MX8MP platform and SCMI clock management support

Device (I2C1) { Name (_HID, "NXP0104") Name (_HRV, 0x1) Name (_UID, 0x1) Name (_CLK, 13) ... Method(GCLK, 0, NotSerialized) { local0 = SC06(_CLK) // SCMI interface to get clock rate Return (local0) } } // Operation Region for Shared memory transport OperationRegion(SHM, SystemMemory, 0x403f0000, 44) Field(SHM, ByteAcc, NoLock, Preserve) { RES1, 32, SCHS, 32, RES2, 64, SCHF, 32, SLEN, 32, SMSH, 32, SPLD, 128, } // SC06 - SCMI CLOCK_RATE_GET // Arg0: SCMI Clock ID // Returns: Clock rate Method (SC06, 1, Serialized) { Name(BUFF, Buffer(4){}) CreateDWordField(BUFF, 0, PLW0) PLW0 = Arg0 local1 = SMT(0x14, 0x6, BUFF) CreateDWordField(local1, 0, RET) CreateDWordField(local1, 4, CLKL) CreateDWordField(local1, 8, CLKH) Name(CLK, 0) CLK = CLKH << 32 | CLKL Return (CLK) } // SC07 - SCMI CLOCK_CONFIG_SET // Arg0: SCMI Clock ID // Arg1: Clock Attributes ( 1 -> Enable, 0 -> Disable) // Returns: 1 if config is set succesfully, 0 otherwise Method (SC07, 2, Serialized) { Name(BUFF, Buffer(8){}) CreateDWordField(BUFF, 0, PLW0) CreateDWordField(BUFF, 4, PLW1) PLW0 = Arg0 PLW1 = Arg1 local0 = SMT(0x14, 0x7, BUFF) CreateDWordField(local0, 0, RET) Return (RET) } // SMT - Interface to send SCMI message over shared memory channel // Arg0: SCMI Protocol ID // Arg1: SCMI Message ID // Arg2: SCMI Input Argument Buffer // Returns: SCMI Payload Method (SMT, 3, Serialized) { // SCMI Message header with SCMI protocol and message ID Name(MSGH, Buffer(8){}) CreateField(MSGH, 10, 8, PROI) CreateField(MSGH, 0, 8, MSGI) PROI = Arg0 MSGI = Arg1 SCHS = 0x0 SCHF = 0x0 SMSH = MSGH SLEN = 4 + sizeof(Arg2) SPLD = Arg2 // SMC call OperationRegion(AFFH, FFixedHW, 1, 8) Field (AFFH, BufferAcc, NoLock, Preserve) { SMCC, 64 } Name(BUFF, Buffer(8){}) CreateQWordField(BUFF, 0x00, BFX0) BFX0 = 0xC20000FE // SCMI FID BUFF = (SMCC = BUFF) Return (SPLD) }