180 lines
7.3 KiB
Markdown
180 lines
7.3 KiB
Markdown
# GPIO toggling in ACPI AML for coreboot #
|
|
|
|
# Table of contents #
|
|
- Introduction
|
|
- Platform Interface
|
|
- Helper routines
|
|
- Implementation details
|
|
- Arguments and Local Variables Management
|
|
|
|
# Introduction #
|
|
|
|
ACPI provides platform-independent interfaces enabling the operating
|
|
system to perform power management for devices as well as the entire
|
|
system. An operating system can simply call into Method()s implemented
|
|
by the interface to request different power management operations. In
|
|
order to be able to perform these operations, an interface might
|
|
require toggling of GPIOs. e.g. a touchscreen device interface might
|
|
require toggling of reset-gpio in order to take the device out of
|
|
reset or to put it back into reset.
|
|
|
|
Thus, any coreboot driver that implements such an ACPI interface might
|
|
require the ability to toggle GPIOs. However, toggling of GPIO is not
|
|
the same across different platforms and it will require the driver to
|
|
depend upon platform to do the required work. This document presents a
|
|
simple interface that can be used by any coreboot driver to generate
|
|
ACPI AML code for reading or toggling platform GPIOs.
|
|
|
|
# Platform Interface #
|
|
|
|
All platforms that use drivers requiring ACPI AML code for GPIO
|
|
interactions need to be implement the following functions:
|
|
1. Return GPIO Rx value if it is acting as input
|
|
int acpigen_soc_read_rx_gpio(unsigned int gpio_num)
|
|
2. Return GPIO Tx value if it is acting as output
|
|
int acpigen_soc_get_tx_gpio(unsigned int gpio_num)
|
|
3. Set GPIO Tx value to 1 if it is acting as output
|
|
int acpigen_soc_set_tx_gpio(unsigned int gpio_num)
|
|
4. Set GPIO Tx value to 0 if it is acting as output
|
|
int acpigen_soc_clear_tx_gpio(unsigned int gpio_num)
|
|
|
|
Each of the above functions takes as input gpio_num which is the gpio
|
|
number that needs to be read or toggled and returns an integer which
|
|
is:
|
|
1. Error = -1
|
|
2. Success = 0
|
|
|
|
Above callback functions are chosen to be implemented in C rather than
|
|
adding them as AML code callbacks for the following reasons:
|
|
1. It is easier to add error prints in C which will inform the
|
|
developer that these callbacks are missing. It restricts debugging
|
|
to coreboot logs.
|
|
2. GPIO conversion from number to register offset can be easily done
|
|
in C by reusing implemented functions rather than adding all the
|
|
logic to AML code or depending upon complicated macros to be added
|
|
to device-tree.
|
|
3. Allows GPIO AML methods to be present under any device scope and
|
|
gives SoC the flexibility to call them without any restrictions.
|
|
|
|
# Helper routines #
|
|
|
|
In order to relieve drivers of the task of implementing the same code
|
|
for enabling/disabling Tx GPIOs based on the GPIO polarity, helper
|
|
routines are provided which implement this common code and can be used
|
|
directly in the driver routines:
|
|
1. Enable Tx GPIO
|
|
int acpigen_enable_tx_gpio(struct acpi_gpio gpio)
|
|
2. Disable Tx GPIO
|
|
int acpigen_disable_tx_gpio(struct acpi_gpio gpio)
|
|
|
|
Both the above functions take as input struct acpi_gpio type and
|
|
return -1 on error and 0 on success. These helper routines end up
|
|
calling the platform specific acpigen_soc_{set,clear}_tx_gpio
|
|
functions internally. Thus, all the ACPI AML calling conventions for
|
|
the platform functions apply to these helper functions as well.
|
|
|
|
# Implementation Details #
|
|
|
|
ACPI library in coreboot will provide weak definitions for all the
|
|
above functions with error messages indicating that these functions
|
|
are being used. This allows drivers to conditionally make use of GPIOs
|
|
based on device-tree entries or any other config option. It is
|
|
recommended that the SoC code in coreboot should provide
|
|
implementations of all the above functions generating ACPI AML code
|
|
irrespective of them being used in any driver. This allows mainboards
|
|
to use any drivers and take advantage of this common infrastructure.
|
|
|
|
Platforms are restricted to using Local5, Local6 and Local7 variables
|
|
only in implementations of the above functions. Any AML methods called
|
|
by the above functions do not have any such restrictions on use of
|
|
Local variables in AML code. Local0 is to be used for all get/read
|
|
functions to return values. This means that the driver code should not
|
|
make any assumptions about the values in Local5, Local6 and Local7
|
|
variables.
|
|
|
|
**Function** **Operation** **Return**
|
|
acpigen_soc_read_rx_gpio Generate ACPI AML code to Error = -1
|
|
read value of Rx in Local0. Success = 0
|
|
acpigen_soc_get_tx_gpio Generate ACPI AML code to Error = -1
|
|
get value of Tx in Local0. Success = 0
|
|
acpigen_soc_set_tx_gpio Generate ACPI AML code to Error = -1
|
|
set Tx to 1. Success = 0
|
|
acpigen_soc_clear_tx_gpio Generate ACPI AML code to Error = -1
|
|
set Tx to 0. Success = 0
|
|
|
|
Ideally, the operation column in the above table should use one or
|
|
more functions implemented by the platform in AML code library (like
|
|
gpiolib.asl). In the example below SPC0 and GPC0 need to be
|
|
implemented by the SoC in AML code library and they can be used by
|
|
acpi_soc_set_tx_gpio to read and set bit in the appropriate register
|
|
for the GPIO.
|
|
|
|
**acpigen_soc_set_tx_gpio**
|
|
|
|
uint64_t gpio_reg_offset = gpio_get_reg_offset(gpio_num);
|
|
|
|
/* Store (\_SB.GPC0(gpio_reg_offset, Local5) */
|
|
acpigen_write_store();
|
|
acpigen_emit_namestring(“\\_SB.GPC0”);
|
|
acpigen_write_integer(gpio_reg_offset);
|
|
acpigen_emit_byte(LOCAL5_OP);
|
|
|
|
|
|
/* Or (Local5, TX_BIT, Local5) */
|
|
acpigen_write_or(LOCAL5_OP, TX_BIT, LOCAL5_OP);
|
|
|
|
/* \_SB.SPC0(gpio_reg_offset, LOCAL5) */
|
|
acpigen_emit_namestring(“\\_SB.SPC0”);
|
|
acpigen_write_integer(gpio_reg_offset);
|
|
acpigen_emit_byte(LOCAL5_OP);
|
|
|
|
return 0;
|
|
|
|
**acpigen_soc_get_tx_gpio**
|
|
|
|
uint64_t gpio_reg_offset = gpio_get_reg_offset(gpio_num);
|
|
|
|
|
|
/* Store (\_SB.GPC0(gpio_reg_offset, Local5) */
|
|
acpigen_write_store();
|
|
acpigen_emit_namestring(“\\_SB.GPC0”);
|
|
acpigen_write_integer(gpio_reg_offset);
|
|
acpigen_emit_byte(LOCAL5_OP);
|
|
|
|
|
|
/*
|
|
* If (And (Local5, TX_BIT)) Store (One, Local0) Else Store (Zero,
|
|
* Local0)
|
|
*/
|
|
acpigen_write_if_and(Local5, TX_BIT);
|
|
acpigen_write_store_args(ONE_OP, LOCAL0_OP);
|
|
acpigen_pop_len();
|
|
acpigen_write_else();
|
|
acpigen_write_store_args(ZERO_OP, LOCAL0_OP);
|
|
acpigen_pop_len();
|
|
|
|
return 0;
|
|
|
|
|
|
These are reference implementations and the platforms are free to
|
|
implement these functions in any way they like. Coreboot driver can
|
|
then simply call into these functions to generate ACPI AML code to
|
|
get/set/clear any GPIO. In order to decide whether GPIO operations are
|
|
required, driver code can rely either on some config option or read
|
|
device-tree to use any user-provided GPIOs.
|
|
|
|
# Arguments and Local Variables Management #
|
|
|
|
Platform-defined functions can call methods using the same calling
|
|
conventions provided by AML code. However, use of Local Variables is
|
|
restricted to Local5, Local6 and Local7 unless they call into some
|
|
other method. Called method can use any Local variables, Local0 -
|
|
Local7. In case of functions expected to return back value to the
|
|
caller, this value is expected to be returned in Local0.
|
|
|
|
Driver code should not make any assumptions about the contents of
|
|
Local5, Local6 and Local7 across callbacks to SoC code. If it makes a
|
|
read or get call to SoC, the return value should be used from Local0
|
|
on return. However, if it makes a set or clear call to SoC, the value
|
|
in Local0 is undefined.
|