/* SPDX-License-Identifier: GPL-2.0-only */ /* This file is part of the coreboot project. */ #ifndef __ACPI_ACPI_DEVICE_H__ #define __ACPI_ACPI_DEVICE_H__ #include #include #include enum acpi_dp_type { ACPI_DP_TYPE_UNKNOWN, ACPI_DP_TYPE_INTEGER, ACPI_DP_TYPE_STRING, ACPI_DP_TYPE_REFERENCE, ACPI_DP_TYPE_TABLE, ACPI_DP_TYPE_ARRAY, ACPI_DP_TYPE_CHILD, }; struct acpi_dp { enum acpi_dp_type type; const char *name; struct acpi_dp *next; union { struct acpi_dp *child; struct acpi_dp *array; }; union { uint64_t integer; const char *string; }; }; #define ACPI_DESCRIPTOR_LARGE (1 << 7) #define ACPI_DESCRIPTOR_INTERRUPT (ACPI_DESCRIPTOR_LARGE | 9) #define ACPI_DESCRIPTOR_GPIO (ACPI_DESCRIPTOR_LARGE | 12) #define ACPI_DESCRIPTOR_SERIAL_BUS (ACPI_DESCRIPTOR_LARGE | 14) /* * PRP0001 is a special DT namespace link device ID. It provides a means to use * existing DT-compatible device identification in ACPI. When this _HID is used * by an ACPI device, the ACPI subsystem in OS looks up "compatible" property in * device object's _DSD and will use the value of that property to identify the * corresponding device in analogy with the original DT device identification * algorithm. * More details can be found in Linux kernel documentation: * Documentation/acpi/enumeration.txt */ #define ACPI_DT_NAMESPACE_HID "PRP0001" struct device; const char *acpi_device_name(const struct device *dev); const char *acpi_device_hid(const struct device *dev); uint32_t acpi_device_uid(const struct device *dev); const char *acpi_device_path(const struct device *dev); const char *acpi_device_scope(const struct device *dev); const char *acpi_device_path_join(const struct device *dev, const char *name); int acpi_device_status(const struct device *dev); void acpi_device_write_uid(const struct device *dev); /* * ACPI Descriptor for extended Interrupt() */ enum acpi_irq_mode { ACPI_IRQ_EDGE_TRIGGERED, ACPI_IRQ_LEVEL_TRIGGERED }; enum acpi_irq_polarity { ACPI_IRQ_ACTIVE_LOW, ACPI_IRQ_ACTIVE_HIGH, ACPI_IRQ_ACTIVE_BOTH }; enum acpi_irq_shared { ACPI_IRQ_EXCLUSIVE, ACPI_IRQ_SHARED }; enum acpi_irq_wake { ACPI_IRQ_NO_WAKE, ACPI_IRQ_WAKE }; struct acpi_irq { unsigned int pin; enum acpi_irq_mode mode; enum acpi_irq_polarity polarity; enum acpi_irq_shared shared; enum acpi_irq_wake wake; }; #define ACPI_IRQ_CFG(_pin, _mode, _pol, _shared, _wake) { \ .pin = (_pin), \ .mode = (_mode), \ .polarity = (_pol), \ .shared = (_shared), \ .wake = (_wake) } #define ACPI_IRQ_EDGE_LOW(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_EDGE_TRIGGERED, ACPI_IRQ_ACTIVE_LOW, \ ACPI_IRQ_EXCLUSIVE, ACPI_IRQ_NO_WAKE) #define ACPI_IRQ_EDGE_HIGH(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_EDGE_TRIGGERED, ACPI_IRQ_ACTIVE_HIGH, \ ACPI_IRQ_EXCLUSIVE, ACPI_IRQ_NO_WAKE) #define ACPI_IRQ_LEVEL_LOW(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_LEVEL_TRIGGERED, ACPI_IRQ_ACTIVE_LOW, \ ACPI_IRQ_SHARED, ACPI_IRQ_NO_WAKE) #define ACPI_IRQ_LEVEL_HIGH(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_LEVEL_TRIGGERED, ACPI_IRQ_ACTIVE_HIGH, \ ACPI_IRQ_SHARED, ACPI_IRQ_NO_WAKE) #define ACPI_IRQ_WAKE_EDGE_LOW(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_EDGE_TRIGGERED, ACPI_IRQ_ACTIVE_LOW, \ ACPI_IRQ_EXCLUSIVE, ACPI_IRQ_WAKE) #define ACPI_IRQ_WAKE_EDGE_HIGH(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_EDGE_TRIGGERED, ACPI_IRQ_ACTIVE_HIGH, \ ACPI_IRQ_EXCLUSIVE, ACPI_IRQ_WAKE) #define ACPI_IRQ_WAKE_LEVEL_LOW(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_LEVEL_TRIGGERED, ACPI_IRQ_ACTIVE_LOW, \ ACPI_IRQ_SHARED, ACPI_IRQ_WAKE) #define ACPI_IRQ_WAKE_LEVEL_HIGH(x) \ ACPI_IRQ_CFG((x), ACPI_IRQ_LEVEL_TRIGGERED, ACPI_IRQ_ACTIVE_HIGH, \ ACPI_IRQ_SHARED, ACPI_IRQ_WAKE) /* Write extended Interrupt() descriptor to SSDT AML output */ void acpi_device_write_interrupt(const struct acpi_irq *irq); /* * ACPI Descriptors for GpioIo() and GpioInterrupt() */ enum acpi_gpio_type { ACPI_GPIO_TYPE_INTERRUPT, ACPI_GPIO_TYPE_IO }; enum acpi_gpio_pull { ACPI_GPIO_PULL_DEFAULT, ACPI_GPIO_PULL_UP, ACPI_GPIO_PULL_DOWN, ACPI_GPIO_PULL_NONE }; enum acpi_gpio_io_restrict { ACPI_GPIO_IO_RESTRICT_NONE, ACPI_GPIO_IO_RESTRICT_INPUT, ACPI_GPIO_IO_RESTRICT_OUTPUT, ACPI_GPIO_IO_RESTRICT_PRESERVE }; enum acpi_gpio_polarity { ACPI_GPIO_ACTIVE_HIGH = 0, ACPI_GPIO_ACTIVE_LOW = 1, }; #define ACPI_GPIO_REVISION_ID 1 #define ACPI_GPIO_MAX_PINS 8 struct acpi_gpio { int pin_count; uint16_t pins[ACPI_GPIO_MAX_PINS]; enum acpi_gpio_type type; enum acpi_gpio_pull pull; const char *resource; /* GpioInt */ uint16_t interrupt_debounce_timeout; /* 1/100 ms */ struct acpi_irq irq; /* GpioIo */ uint16_t output_drive_strength; /* 1/100 mA */ int io_shared; enum acpi_gpio_io_restrict io_restrict; enum acpi_gpio_polarity polarity; }; /* Basic output GPIO with default pull settings */ #define ACPI_GPIO_OUTPUT_ACTIVE_HIGH(gpio) { \ .type = ACPI_GPIO_TYPE_IO, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .io_restrict = ACPI_GPIO_IO_RESTRICT_OUTPUT, \ .polarity = ACPI_GPIO_ACTIVE_HIGH, \ .pin_count = 1, \ .pins = { (gpio) } } #define ACPI_GPIO_OUTPUT_ACTIVE_LOW(gpio) { \ .type = ACPI_GPIO_TYPE_IO, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .io_restrict = ACPI_GPIO_IO_RESTRICT_OUTPUT, \ .polarity = ACPI_GPIO_ACTIVE_LOW, \ .pin_count = 1, \ .pins = { (gpio) } } /* Basic input GPIO with default pull settings */ #define ACPI_GPIO_INPUT_ACTIVE_HIGH(gpio) { \ .type = ACPI_GPIO_TYPE_IO, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT, \ .polarity = ACPI_GPIO_ACTIVE_HIGH, \ .pin_count = 1, \ .pins = { (gpio) } } #define ACPI_GPIO_INPUT_ACTIVE_LOW(gpio) { \ .type = ACPI_GPIO_TYPE_IO, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT, \ .polarity = ACPI_GPIO_ACTIVE_LOW, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active High GPIO interrupt */ #define ACPI_GPIO_IRQ_EDGE_HIGH(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_HIGH, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active Low GPIO interrupt */ #define ACPI_GPIO_IRQ_EDGE_LOW(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_LOW, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active Both GPIO interrupt */ #define ACPI_GPIO_IRQ_EDGE_BOTH(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_BOTH, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active High GPIO interrupt with wake */ #define ACPI_GPIO_IRQ_EDGE_HIGH_WAKE(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_HIGH, \ .irq.wake = ACPI_IRQ_WAKE, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active Low GPIO interrupt with wake */ #define ACPI_GPIO_IRQ_EDGE_LOW_WAKE(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_LOW, \ .irq.wake = ACPI_IRQ_WAKE, \ .pin_count = 1, \ .pins = { (gpio) } } /* Edge Triggered Active Both GPIO interrupt with wake */ #define ACPI_GPIO_IRQ_EDGE_BOTH_WAKE(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_EDGE_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_BOTH, \ .irq.wake = ACPI_IRQ_WAKE, \ .pin_count = 1, \ .pins = { (gpio) } } /* Level Triggered Active High GPIO interrupt */ #define ACPI_GPIO_IRQ_LEVEL_HIGH(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_LEVEL_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_HIGH, \ .pin_count = 1, \ .pins = { (gpio) } } /* Level Triggered Active Low GPIO interrupt */ #define ACPI_GPIO_IRQ_LEVEL_LOW(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_LEVEL_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_LOW, \ .pin_count = 1, \ .pins = { (gpio) } } /* Level Triggered Active High GPIO interrupt with wake */ #define ACPI_GPIO_IRQ_LEVEL_HIGH_WAKE(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_LEVEL_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_HIGH, \ .irq.wake = ACPI_IRQ_WAKE, \ .pin_count = 1, \ .pins = { (gpio) } } /* Level Triggered Active Low GPIO interrupt with wake */ #define ACPI_GPIO_IRQ_LEVEL_LOW_WAKE(gpio) { \ .type = ACPI_GPIO_TYPE_INTERRUPT, \ .pull = ACPI_GPIO_PULL_DEFAULT, \ .irq.mode = ACPI_IRQ_LEVEL_TRIGGERED, \ .irq.polarity = ACPI_IRQ_ACTIVE_LOW, \ .irq.wake = ACPI_IRQ_WAKE, \ .pin_count = 1, \ .pins = { (gpio) } } /* Write GpioIo() or GpioInt() descriptor to SSDT AML output */ void acpi_device_write_gpio(const struct acpi_gpio *gpio); /* * ACPI Descriptors for Serial Bus interfaces */ #define ACPI_SERIAL_BUS_TYPE_I2C 1 #define ACPI_SERIAL_BUS_TYPE_SPI 2 #define ACPI_I2C_SERIAL_BUS_REVISION_ID 1 /* TODO: upgrade to 2 */ #define ACPI_I2C_TYPE_SPECIFIC_REVISION_ID 1 #define ACPI_SPI_SERIAL_BUS_REVISION_ID 1 #define ACPI_SPI_TYPE_SPECIFIC_REVISION_ID 1 /* * ACPI I2C Bus */ struct acpi_i2c { /* I2C Address */ uint16_t address; /* 7 or 10 bit Address Mode */ enum i2c_address_mode mode_10bit; /* I2C Bus Speed in Hz */ enum i2c_speed speed; /* Reference to I2C controller */ const char *resource; }; /* Write I2cSerialBus() descriptor to SSDT AML output */ void acpi_device_write_i2c(const struct acpi_i2c *i2c); /* * ACPI SPI Bus */ struct acpi_spi { /* Device selection */ uint16_t device_select; /* Device selection line is active high or low */ enum spi_polarity device_select_polarity; /* 3 or 4 wire SPI connection */ enum spi_wire_mode wire_mode; /* Connection speed in HZ */ unsigned int speed; /* Size in bits of smallest transfer unit */ u8 data_bit_length; /* Phase of clock pulse on which to capture data */ enum spi_clock_phase clock_phase; /* Indicate if clock is high or low during first phase */ enum spi_polarity clock_polarity; /* Reference to SPI controller */ const char *resource; }; /* Write SPI Bus descriptor to SSDT AML output */ void acpi_device_write_spi(const struct acpi_spi *spi); /* GPIO/timing information for the power on/off sequences */ struct acpi_power_res_params { /* GPIO used to take device out of reset or to put it into reset. */ struct acpi_gpio *reset_gpio; /* Delay to be inserted after device is taken out of reset. * (_ON method delay) */ unsigned int reset_delay_ms; /* Delay to be inserted after device is put into reset. * (_OFF method delay) */ unsigned int reset_off_delay_ms; /* GPIO used to enable device. */ struct acpi_gpio *enable_gpio; /* Delay to be inserted after device is enabled. * (_ON method delay) */ unsigned int enable_delay_ms; /* Delay to be inserted after device is disabled. * (_OFF method delay) */ unsigned int enable_off_delay_ms; /* GPIO used to stop operation of device. */ struct acpi_gpio *stop_gpio; /* Delay to be inserted after disabling stop. * (_ON method delay) */ unsigned int stop_delay_ms; /* Delay to be inserted after enabling stop. * (_OFF method delay) */ unsigned int stop_off_delay_ms; }; /* * Add a basic PowerResource block for a device that includes * GPIOs to control enable, reset and stop operation of the device. Each * GPIO is optional, but at least one must be provided. * * Reset - Put the device into / take the device out of reset. * Enable - Enable / disable power to device. * Stop - Stop / start operation of device. */ void acpi_device_add_power_res(const struct acpi_power_res_params *params); /* * Writing Device Properties objects via _DSD * * http://uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf * http://uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.pdf * * The Device Property Hierarchy can be multiple levels deep with multiple * children possible in each level. In order to support this flexibility * the device property hierarchy must be built up before being written out. * * For example: * * // Child table with string and integer * struct acpi_dp *child = acpi_dp_new_table("CHLD"); * acpi_dp_add_string(child, "childstring", "CHILD"); * acpi_dp_add_integer(child, "childint", 100); * * // _DSD table with integer and gpio and child pointer * struct acpi_dp *dsd = acpi_dp_new_table("_DSD"); * acpi_dp_add_integer(dsd, "number1", 1); * acpi_dp_add_gpio(dsd, "gpio", "\_SB.PCI0.GPIO", 0, 0, 1); * acpi_dp_add_child(dsd, "child", child); * * // Write entries into SSDT and clean up resources * acpi_dp_write(dsd); * * Name(_DSD, Package() { * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") * Package() { * Package() { "gpio", Package() { \_SB.PCI0.GPIO, 0, 0, 0 } } * Package() { "number1", 1 } * } * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b") * Package() { * Package() { "child", CHLD } * } * } * Name(CHLD, Package() { * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") * Package() { * Package() { "childstring", "CHILD" } * Package() { "childint", 100 } * } * } */ /* Start a new Device Property table with provided ACPI reference */ struct acpi_dp *acpi_dp_new_table(const char *ref); /* Add integer Device Property */ struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name, uint64_t value); /* Add string Device Property */ struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name, const char *string); /* Add ACPI reference Device Property */ struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name, const char *reference); /* Add an array of Device Properties */ struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array); /* Add an array of integers Device Property */ struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, const uint64_t *array, int len); /* Add a GPIO binding Device Property */ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, const char *ref, int index, int pin, int active_low); /* Add a child table of Device Properties */ struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, struct acpi_dp *child); /* Add a list of Device Properties, returns the number of properties added */ size_t acpi_dp_add_property_list(struct acpi_dp *dp, const struct acpi_dp *property_list, size_t property_count); /* Write Device Property hierarchy and clean up resources */ void acpi_dp_write(struct acpi_dp *table); /* * Helper function to write a PCI device with _ADR object defined. * * IMPORTANT: Scope of a device created in SSDT cannot be used to add ACPI nodes under that * scope in DSDT. So, if there are any references to this PCI device scope required from static * asl files, do not use this function and instead add the device to DSDT as well. */ void acpi_device_write_pci_dev(const struct device *dev); #endif /* __ACPI_ACPI_DEVICE_H__ */