 Hello, everyone. My name is Bernardo Perez-Brievo. I'm a firmware engineer at Intel, and I'm part of the Chrome Platform Engineering team. We work in collaboration with Google engineers to provide support for new Chrome devices and features. Most of my contributions are on the platform embedded controller. Today, I will be talking about the application processor power sequence subsystem. Thanks for joining. In today's agenda, I would like to start by providing some background about what Chrome OS embedded controller is and what is the application processor power sequence. This will be followed by describing the motivation for bringing this proposal. Then we'll get an overview of this subsystem. I will also talk about how to provide implementation for this subsystem and describe the user interface that is exported. Finally, I will give a summary of this proposal. Let's start. This will provide some background for a couple of concepts that I mentioned. What is the Chrome OS embedded controller? Well, it's a microcontroller that is found on the platform and manages its hardware and power delivery, assists the application processor shorter for AP in many low-level tasks. You can think of the AP as the main processor in the platform and it's also known as the SOC for its cycloneams system on chip. And it has much more capabilities and resources compared to the embedded controller. This is the one that runs the Chrome OS where applications like Gmail and YouTube are executed. Those tasks that I was mentioning include the battery and charger management, thermal management, and communication ports and implement. It also handles the AP power sequencing. This is the way the power states of the AP are safely driven to power it up, power it down, send it to suspend, and wake it up from suspend. This is also one of the first components in the platform that receives power. AP power sequence consists of multiple stages of power state transition. On each state, different voltage rails will be turned on using control signals from the AP. The platform responds by ramping the required voltage and the required order to assert corresponding power goods signals required by the AP. And other platform components after certain timing requirements have been met. Here on the right side, it's a very simplified diagram to show how the power signals are directed. We see different voltage rails applying power to various components in the platform. These voltage rays are controlled by corresponding voltage converters. Note that EC is involved in many of these power signals, either by driving them or by monitoring state. So that way, EC knows what is the current power state of the AP. And it ensures that it's providing the appropriate voltage and signals and timing in order to drive the power state of the AP safely. It also can provide the wake-up signals to the AP. This is an example of an Intel SOC power sequence. This sequence is required to drive comment-led processor from G3 all the way to S0, passing through the intermediously power state S5, S4, S3, and so forth. On the left side, you can see that we're listening and describing all the power states that are supported from G3 all the way to S0. S0 is a state where the AP is active and G3 is completely off. And this diagram on the left side is what shows all the timings and the signals that are involved in this power sequence. This is provided by Intel on the platform design guide. And EC is the one that needs to adhere to be aware of this in order to provide the signals to the SOC. Let's talk about the motivation for this proposal. In July 2021, Chromebooks switched from the original Google Chrome ECOS to an application based on the Sefer project. And given that the AP power sequence is a predominant component that was on this Chrome EC application, it makes sense to have something that follows the Sefer driver model. This driver needs to be flexible and be flexible to provide routines and make it reusable and easy to extend. This way, new common designs will not require much code application or function for definition. And also, we wanted to keep a structure that is easy to follow. Now I will give another view of the AP power sequence subsystem. So what is the AP power sequence subsystem? It's a Sefer driver that runs on the EC and provides an underlying framework for driving and monitoring AP power state. Each power state is logically bound to state the underlying framework. Although it's currently fit the X86 CPU APs, it can be extended to other AP architectures. It's built on the Sefer state machine framework. And many of its features are leveraged. And each ACPI power state, it's present on this subsystem, has three levels of hierarchy. The architecture handles signals and voltages that are predominant on SOC with the same architecture. Chipset, this is targeted for signals that are enabled in the SOC input and output models. And application is for any topology variant in terms of voltage and power rails. I will give an example to that. So let's say that for one platform design, where a voltage ray is monitored by an IC and sends the PG signal to the EC. And later, we are required to reduce the bill of materials by removing this integrated circuit. And that way, EC has to do the monitoring itself, probably using an analog to digital converter. So this platform change can be addressed by this handler without having to change the existing handles or impacting the existing handles for architecture and chipset. So this driver responds to events posted by other components and the state machine implementation will process those events. That's the way it interacts. By processing these events, the state machine implementation can detect AP power state transition and notify other components on the EC. For example, let's say that I want to keep a sensor voltage rail on only while the AP is fully active. This means that it's on S0. To achieve this, we can register a callback to be notified when entering into the S0. And this callback, I can turn on this power rail. And also, I will register another callback that will notify when exceeding from S0 power state. So that way, I can turn down this voltage rail. And I can save power. It also supports additional power states. This depends on the application. If more power states or subsets are required. Since I have mentioned that this subsystem is built on the suffer state machine framework, I would like to explain its operation. In this example, I have defined two parent states, PS1 and PS2, and three child states. State 1 and S2 are child state of PS1. And S3 is a child state of PS2. Each state has its functions defined for entry, run, and exit. So first, the state machine execution is initiated by setting an initial state. This will position the state machine in that state and call the entry functions. In descending hierarchy order. This means that the parent state is entry functions called first and then the child state entry function. Later, one iteration is executed by calling this API. And this will call the current state run function in ascending order. That means the state 1 run as the child state and then the parent state run. In the next run iteration, state 1 run does a transition into S2. And this calls the exit function for S1 and the entry function for S2. This call is done in ascending order, but since S1 and S2 are sharing the same parent, that current entry and exit function is not called. And as I mentioned, the entry functions are called in descending order. Same as the step 1. At this point, we are in the state S2. And then next iteration takes place. And state 2 does a transition into S3. And since S2 and S3 have different parent, now the corresponding entry and exit functions are called in the expected order ascending for exit calls and descending for entry calls. And this can be shown in the following picture. We can see that the state 1 and state 2 are child states of PS1. And S3 is the child state for S2, for PS2. And we can see the arrows, what was the flow of the transitions. Now that I've mentioned how Sefer state machine framework works, I can continue with the power sequence state machine. So the hierarchical state machine is enabled. It's one of the features that we are using from the Sefer state machine framework. This will coordinate for us the execution of the entry, run, and exit functions. Sefer state machine framework APIs are not directly accessed. They are wrapped, and some other features are enabled. And also the implementation can access the macros and these APIs. Only the implementation can access those. This way, we are ensuring that the hierarchy is maintained. And here we can see three ACPI states with the corresponding send machine handlers, including the hierarchy that I mentioned previously. Now let's go over the execution flow. So here is the AB power sequence and it's interacting with these three components. So initially, the AP power sequence is not running. However, callback registration can take place. Now the application one starts the AP power sequence set. And in order to start it, it needs to set an initial state, an initial power state. And this will call the corresponding entry function. And at this point, there is no iteration made. Iterations are triggered by posting events into the AP power sequence. This will call the current power state run function. And the event will be visible to that function. If a power state transition occurs, the corresponding entry and exit functions are called in the hierarchical order that we previously described. And after that, the power sequence will go through the all the notifications that are registered. And it will find those that meet the criterion. And the criterion it will follow is that either it will entry or exit a specific power state. And after that, there is one more iteration that is made. And if there is no transition happening, the AP power sequence set will go back to sleep. And again, it will wait for any event to be posted. Now let's talk about how to provide an implementation for the AP power sequence system. These are the macros that are used for providing the action handlers for entry, run, and exit. The first parameter is a name of the state, the power state that they want to provide. And then the corresponding functions. Note that there is one macro available for each hierarchy level, one for application, one for chipset, and one for architecture. These macros are mandatory. They need to be provided as these to if we want to use the implement the AP power sequence. And these are optional. They only work when a stuff state is set on the device tree. These are the APIs that are available for the implementation. This API helps to set the next state or queries on information for where to know what is the entry or the exit function. And also, this way, the events that are posted can be also checked. This set event is only meant to be called for the run functions, as we will see later. Only one state transition is permitted per iteration. These functions will get what is the state that is entering or exiting. And these are only available for entry and exit functions. If this is called inside a run functions, it will return an error. And this function is meant to be used to query if the event is set. And this can be used in all the action handling functions. Here, I would like to show how macros and APIs come into play. For this example, I'm defining three power states. And for the sake of simplicity, I'm just providing run functions for each one. First, S5, here you can see how the macros are used to provide architecture, chipset, and application level power state run action handlers. Now, define S4 and S3. This is how the state machine diagram will look. You can see that action handlers were not defined as for application level on S4 and for architecture level on S3. This doesn't represent an issue. The hierarchy and execution order is maintained. Just the level not defined as will be omitted from execution. Inside the corresponding run function, implementation is expected to program the logic that fits with the power state it's representing. For example, S4, say that some power signal was expected to be asserted. And for some reason, it's not. And then you say, OK, there's something wrong. So I need to go back to S5. And that's what it will do. This way, the power states are synchronized with the AP power state. And the arrows denote the possible path the implementation sets for the state machine. And the order and signals are set by the AP vendor and need to be shared and known by the implementation and some signed document. In this quick example, I'm showing how the entry and exit functions work. When a state transition is made, these functions are executed in specific hierarchical order. First, the current state exit functions are called in ascending order. This is application level, chipset, then architecture. And then the next state in descending order, architecture, chipset, and application. The get entry state function, that one return the next state in the transition. And the get exit state returns the current state. State is exceeding. These APIs are meant to be called only inside implementation of the entry and exit functions. Otherwise, it will return error. Forger state transitions are not allowed inside these functions. Only the run functions implementation are allowed to do transitions. This is to avoid any stack overflow because these functions run in this stack. Another feature of this subsystem is the ultimate hook pattern. This allows upper level action handlers to finalize the current iteration execution flow by returning non-zero. This way, implementation can be overridden or complemented in different levels. If non-zero is returned from an application, action handlers, chipset, and architectures are not called for that iteration. And similar thing happens with the chipset. The architecture will not be called. This also applies to exit and entry function implementations. So all of them support this ultimate pattern. This example only has the run. But yeah, if I say entry and exit, follow the same. It is possible to add forger states, although only an application in chipset level. In this example, I created additional sub-states for providing action handlers used with corresponding macros. And I specify one more value, which is the parent state. When additional states are provided, its definition is to be complemented using the device tree. And this device tree will define the enumeration ID. This is how the state will look with additional sub-states. And again, the arrows denote the possible task. And you can see the three additional states with different color. And you can see how they are inside the application level or the chipset level. Now let's talk about the user interface and the services that are provided. Users of this subsystem have access to all these functions. On top, we have the function to get the device reference, followed by the API to query the current state and to get the string name of the state. This is for debugging purposes. Then we have the APIs for start, the execution, and post events into the subsystem. The user can also lock. Locking will prevent the threat from running. Events can still be posted, but they will not be processed until unlocked. And finally, those functions to register cowbacks when entering or exiting certain power states. And these are the enumeration available for power states and events that can be posted. Note that the additional states name will be resolved during the build time using the device tree. This is why we have this for each status OK of the power states. And also the callbacks requires to use this structure that will include the name of the callback function and the bitmask for each power state that we want to enable. This is a quick sample of how user registered power state callbacks. The callback functions prototype includes the device reference, entry state, and the exit state. This helps users to know where the state machine is going in the transition. Functions user entry callback is expected to be called during transitions where the state machine enters G3S3 and S0. And function user exit callback is expected in transitions where the state machine exits G3S4 and S2. And finally, let's do a summary of this presentation. Our state execution needs to be started with a designated power state. State machine does not process any event until this happens. Any user can start the state machine. Implementation notifications are executed within driver instance thread context. Keep this in mind to avoid deadlocks or on your registered callbacks. Only implementations do state transitions. This is to prevent any bug or race condition. Transitions can only be triggered by one action handers. This promotes an order state machine flow. Only one state transition is permitted per iteration. Entry and exit functions consume stack. We want to avoid any overflow. And this also promotes an order state machine flow. Transitioning into state with no action handlers implemented at any level will result on blocking the state machine execution indefinitely. Implementation needs to provide always a state machine policy on the handlers. And also needs to set possible next states. Otherwise, you will be stuck in the same state. It is recommended to lock power state when performing operation based on current power state. This will avoid any race condition between state machine and implementation itself. And always remember to unlock the power state from the user perspective. Otherwise, you will not do anything else. Thank you so much for your time. I hope this was of interest to you.