drivers/uart/acpi: Add new device driver for UART attached devices
This driver generates an ACPI device object for a UART attached device
with all of the expected device support handlers like different interrupt
sources and power control GPIOs.
Example use:
chip drivers/uart/acpi
    register "name" = ""UDEV""
    register "desc" = ""UART Attached Device""
    register "hid" = "ACPI_DT_NAMESPACE_HID"
    register "compat_string" = ""google,cros-ec-uart""
    register "irq_gpio" = "ACPI_GPIO_IRQ_LEVEL_LOW_WAKE(GPP_C20)"
    register "uart" = "ACPI_UART_RAW_DEVICE(115200, 64)"
    device generic 0 on end
end
Resulting in this ACPI device:
Device (UDEV)
{
    Name (_HID, "PRP0001")
    Name (_UID, Zero)
    Name (_DDN, "UART Attached Device")
    Method (_STA, 0, NotSerialized)
    {
        Return (0x0F)
    }
    Name (_CRS, ResourceTemplate ()
    {
        UartSerialBusV2 (0x0001C200, DataBitsEight, StopBitsOne,
                         0x00, LittleEndian, ParityTypeNone, FlowControlNone,
                         0x0040, 0x0040, "\\_SB.PCI0.UAR2",
                         0x00, ResourceConsumer, , Exclusive)
        GpioInt (Level, ActiveLow, ExclusiveAndWake, PullDefault, 0x0000,
                 "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer)
        {
            0x0114
        }
    })
    Name (_DSD, Package (0x02)
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
        Package (0x01)
        {
            Package (0x02)
            {
                "compatible",
                "google,cros-ec-uart"
            }
        }
    })
}
Change-Id: Idfd2d9d2ab6990a82ddd401734c0d9b1b0b8f82d
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41793
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
			
			
This commit is contained in:
		
				
					committed by
					
						 Furquan Shaikh
						Furquan Shaikh
					
				
			
			
				
	
			
			
			
						parent
						
							dccef0da0c
						
					
				
				
					commit
					e530d816cc
				
			
							
								
								
									
										2
									
								
								src/drivers/uart/acpi/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/drivers/uart/acpi/Kconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| config DRIVERS_UART_ACPI | ||||
| 	bool | ||||
							
								
								
									
										1
									
								
								src/drivers/uart/acpi/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/drivers/uart/acpi/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ramstage-$(CONFIG_DRIVERS_UART_ACPI) += acpi.c | ||||
							
								
								
									
										174
									
								
								src/drivers/uart/acpi/acpi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/drivers/uart/acpi/acpi.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
|  | ||||
| #include <acpi/acpi_device.h> | ||||
| #include <acpi/acpigen.h> | ||||
| #include <console/console.h> | ||||
| #include <device/path.h> | ||||
| #include <string.h> | ||||
| #include "chip.h" | ||||
|  | ||||
| static bool uart_acpi_add_gpios_to_crs(struct drivers_uart_acpi_config *config) | ||||
| { | ||||
| 	/* | ||||
| 	 * Return false if: | ||||
| 	 * 1. Request to explicitly disable export of GPIOs in CRS, or | ||||
| 	 * 2. Both reset and enable GPIOs are not provided. | ||||
| 	 */ | ||||
| 	if (config->disable_gpio_export_in_crs || | ||||
| 	    ((config->reset_gpio.pin_count == 0) && | ||||
| 	     (config->enable_gpio.pin_count == 0))) | ||||
| 		return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static int uart_acpi_write_gpio(struct acpi_gpio *gpio, int *curr_index) | ||||
| { | ||||
| 	int ret = -1; | ||||
|  | ||||
| 	if (gpio->pin_count == 0) | ||||
| 		return ret; | ||||
|  | ||||
| 	acpi_device_write_gpio(gpio); | ||||
| 	ret = *curr_index; | ||||
| 	(*curr_index)++; | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void uart_acpi_fill_ssdt(const struct device *dev) | ||||
| { | ||||
| 	const char *scope = acpi_device_scope(dev); | ||||
| 	const char *path = acpi_device_path(dev); | ||||
| 	struct drivers_uart_acpi_config *config = dev->chip_info; | ||||
| 	int curr_index = 0; | ||||
| 	int irq_gpio_index = -1; | ||||
| 	int reset_gpio_index = -1; | ||||
| 	int enable_gpio_index = -1; | ||||
|  | ||||
| 	if (!dev->enabled || !scope) | ||||
| 		return; | ||||
|  | ||||
| 	if (!config->hid) { | ||||
| 		printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	acpigen_write_scope(scope); | ||||
| 	acpigen_write_device(acpi_device_name(dev)); | ||||
|  | ||||
| 	acpigen_write_name_string("_HID", config->hid); | ||||
| 	if (config->cid) | ||||
| 		acpigen_write_name_string("_CID", config->cid); | ||||
| 	acpigen_write_name_integer("_UID", config->uid); | ||||
| 	acpigen_write_name_string("_DDN", config->desc); | ||||
| 	acpigen_write_STA(acpi_device_status(dev)); | ||||
|  | ||||
| 	/* Resources */ | ||||
| 	acpigen_write_name("_CRS"); | ||||
| 	acpigen_write_resourcetemplate_header(); | ||||
|  | ||||
| 	/* Fix up resource pointer to this scope */ | ||||
| 	config->uart.resource = scope; | ||||
| 	acpi_device_write_uart(&config->uart); | ||||
|  | ||||
| 	/* Use either Interrupt() or GpioInt() */ | ||||
| 	if (config->irq_gpio.pin_count) | ||||
| 		acpi_device_write_gpio(&config->irq_gpio); | ||||
| 	else | ||||
| 		acpi_device_write_interrupt(&config->irq); | ||||
|  | ||||
| 	/* Add enable/reset GPIOs if needed */ | ||||
| 	if (uart_acpi_add_gpios_to_crs(config)) { | ||||
| 		reset_gpio_index = uart_acpi_write_gpio(&config->reset_gpio, | ||||
| 							&curr_index); | ||||
| 		enable_gpio_index = uart_acpi_write_gpio(&config->enable_gpio, | ||||
| 							 &curr_index); | ||||
| 	} | ||||
| 	acpigen_write_resourcetemplate_footer(); | ||||
|  | ||||
| 	/* Wake capabilities */ | ||||
| 	if (config->wake) { | ||||
| 		acpigen_write_name_integer("_S0W", ACPI_DEVICE_SLEEP_D3_COLD); | ||||
| 		acpigen_write_PRW(config->wake, SLP_TYP_S3); | ||||
| 	}; | ||||
|  | ||||
| 	/* Write device properties if needed */ | ||||
| 	if (config->compat_string || irq_gpio_index >= 0 || | ||||
| 	    reset_gpio_index >= 0 || enable_gpio_index >= 0) { | ||||
| 		struct acpi_dp *dsd = acpi_dp_new_table("_DSD"); | ||||
| 		if (config->compat_string) | ||||
| 			acpi_dp_add_string(dsd, "compatible", | ||||
| 					   config->compat_string); | ||||
| 		if (irq_gpio_index >= 0) | ||||
| 			acpi_dp_add_gpio(dsd, "irq-gpios", path, | ||||
| 					 irq_gpio_index, 0, | ||||
| 					 config->irq_gpio.polarity); | ||||
| 		if (reset_gpio_index >= 0) | ||||
| 			acpi_dp_add_gpio(dsd, "reset-gpios", path, | ||||
| 					 reset_gpio_index, 0, | ||||
| 					 config->reset_gpio.polarity); | ||||
| 		if (enable_gpio_index >= 0) | ||||
| 			acpi_dp_add_gpio(dsd, "enable-gpios", path, | ||||
| 					 enable_gpio_index, 0, | ||||
| 					 config->enable_gpio.polarity); | ||||
| 		acpi_dp_write(dsd); | ||||
| 	} | ||||
|  | ||||
| 	/* Power Resource */ | ||||
| 	if (config->has_power_resource) { | ||||
| 		const struct acpi_power_res_params power_res_params = { | ||||
| 			&config->reset_gpio, | ||||
| 			config->reset_delay_ms, | ||||
| 			config->reset_off_delay_ms, | ||||
| 			&config->enable_gpio, | ||||
| 			config->enable_delay_ms, | ||||
| 			config->enable_off_delay_ms, | ||||
| 			&config->stop_gpio, | ||||
| 			config->stop_delay_ms, | ||||
| 			config->stop_off_delay_ms | ||||
| 		}; | ||||
| 		acpi_device_add_power_res(&power_res_params); | ||||
| 	} | ||||
|  | ||||
| 	acpigen_pop_len(); /* Device */ | ||||
| 	acpigen_pop_len(); /* Scope */ | ||||
|  | ||||
| 	printk(BIOS_INFO, "%s: %s at %s\n", path, config->desc, dev_path(dev)); | ||||
| } | ||||
|  | ||||
| static const char *uart_acpi_name(const struct device *dev) | ||||
| { | ||||
| 	struct drivers_uart_acpi_config *config = dev->chip_info; | ||||
| 	static char name[5]; | ||||
|  | ||||
| 	if (config->name) | ||||
| 		return config->name; | ||||
|  | ||||
| 	snprintf(name, sizeof(name), "D%03.3X", dev->path.generic.id); | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| static struct device_operations uart_acpi_dev_ops = { | ||||
| 	.read_resources	= noop_read_resources, | ||||
| 	.set_resources	= noop_set_resources, | ||||
| 	.acpi_fill_ssdt	= uart_acpi_fill_ssdt, | ||||
| 	.acpi_name	= uart_acpi_name, | ||||
| }; | ||||
|  | ||||
| static void uart_acpi_enable(struct device *dev) | ||||
| { | ||||
| 	struct drivers_uart_acpi_config *config = dev->chip_info; | ||||
|  | ||||
| 	if (config->desc) | ||||
| 		dev->name = config->desc; | ||||
| 	else | ||||
| 		config->desc = dev->chip_ops->name; | ||||
|  | ||||
| 	dev->ops = &uart_acpi_dev_ops; | ||||
| } | ||||
|  | ||||
| struct chip_operations drivers_uart_acpi_ops = { | ||||
| 	CHIP_NAME("ACPI UART Device") | ||||
| 	.enable_dev = uart_acpi_enable | ||||
| }; | ||||
							
								
								
									
										48
									
								
								src/drivers/uart/acpi/chip.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/drivers/uart/acpi/chip.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
|  | ||||
| #ifndef __DRIVERS_UART_ACPI_H__ | ||||
| #define __DRIVERS_UART_ACPI_H__ | ||||
|  | ||||
| #include <acpi/acpi_device.h> | ||||
|  | ||||
| struct drivers_uart_acpi_config { | ||||
| 	const char *hid;		/* ACPI _HID (required) */ | ||||
| 	const char *cid;		/* ACPI _CID */ | ||||
| 	const char *name;		/* ACPI Device Name */ | ||||
| 	const char *desc;		/* Device Description */ | ||||
| 	unsigned int uid;		/* ACPI _UID */ | ||||
| 	unsigned int wake;		/* Wake GPE */ | ||||
| 	const char *compat_string;	/* Compatible string for _HID=PRP0001 */ | ||||
| 	struct acpi_irq irq;		/* PIRQ */ | ||||
| 	struct acpi_gpio irq_gpio;	/* GPIO to be used instead of PIRQ */ | ||||
| 	struct acpi_uart uart;		/* UART device information */ | ||||
|  | ||||
| 	/* Disable reset and enable GPIO export in _CRS */ | ||||
| 	bool disable_gpio_export_in_crs; | ||||
|  | ||||
| 	/* Does the device have a power resource? */ | ||||
| 	bool has_power_resource; | ||||
|  | ||||
| 	/* 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. */ | ||||
| 	unsigned int reset_delay_ms; | ||||
| 	/* Delay to be inserted after device is put into reset. */ | ||||
| 	unsigned int reset_off_delay_ms; | ||||
|  | ||||
| 	/* GPIO used to enable device. */ | ||||
| 	struct acpi_gpio enable_gpio; | ||||
| 	/* Delay to be inserted after device is enabled. */ | ||||
| 	unsigned int enable_delay_ms; | ||||
| 	/* Delay to be inserted after device is disabled. */ | ||||
| 	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. */ | ||||
| 	unsigned int stop_delay_ms; | ||||
| 	/* Delay to be inserted after enabling stop. */ | ||||
| 	unsigned int stop_off_delay_ms; | ||||
| }; | ||||
|  | ||||
| #endif /* __DRIVERS_UART_ACPI_H__ */ | ||||
		Reference in New Issue
	
	Block a user