diff --git a/drivers/bus/phytium_pio.c b/drivers/bus/phytium_pio.c index 713a51bcb7d9..9f5850a826ef 100644 --- a/drivers/bus/phytium_pio.c +++ b/drivers/bus/phytium_pio.c @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -25,10 +28,8 @@ #define PHYT_PIO_DRV_VER "1.1.0" static struct phytium_pio *ppio; -int phytium_pio_int_state; -int phytium_pio_clr_int; - -static irqreturn_t phytium_pio_irq_handler(int irq, void *dev_id); +static int phytium_pio_int_state; +static int phytium_pio_clr_int; bool check_cpu_type(void) { @@ -43,114 +44,97 @@ bool check_cpu_type(void) } EXPORT_SYMBOL(check_cpu_type); -void phytium_pio_clear_interrupt(u32 val) +static void phytium_pio_irq_noop(struct irq_data *d) { - unsigned long flags; - - if (!ppio) - return; - spin_lock_irqsave(&ppio->lock, flags); - writel(val, ppio->regbase + ppio->int_clear_reg); - readl(ppio->regbase + ppio->int_status_reg); - spin_unlock_irqrestore(&ppio->lock, flags); } -EXPORT_SYMBOL(phytium_pio_clear_interrupt); -void phytium_pio_handle_interrupt_bit(unsigned int bit) +static struct irq_chip phytium_pio_irq_chip = { + .name = PHYT_PIO_DRIVER_NAME, + .irq_mask = phytium_pio_irq_noop, + .irq_unmask = phytium_pio_irq_noop, + .irq_ack = phytium_pio_irq_noop, +}; + +/* One Linux irq per latch bit, acknowledged once before the dispatch. */ +static void phytium_pio_irq_dispatch(struct irq_desc *desc) { - unsigned long flags; + struct phytium_pio *pio = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long bits; + u32 pending, again; + unsigned int bit; - if (!ppio || bit >= 32) - return; + chained_irq_enter(chip, desc); - spin_lock_irqsave(&ppio->lock, flags); - ppio->handled_bits |= BIT(bit); - spin_unlock_irqrestore(&ppio->lock, flags); -} -EXPORT_SYMBOL(phytium_pio_handle_interrupt_bit); + pending = readl(pio->regbase + pio->int_status_reg); + if (!pending) + goto out; -void phytium_pio_unhandle_interrupt_bit(unsigned int bit) -{ - unsigned long flags; + /* The latch is write-zero-to-clear. */ + writel(~pending, pio->regbase + pio->int_clear_reg); + again = readl(pio->regbase + pio->int_status_reg); - if (!ppio || bit >= 32) - return; + bits = pending; + for_each_set_bit(bit, &bits, PHYTIUM_PIO_IRQ_NR) + generic_handle_domain_irq(pio->domain, bit); - spin_lock_irqsave(&ppio->lock, flags); - ppio->handled_bits &= ~BIT(bit); - spin_unlock_irqrestore(&ppio->lock, flags); + /* + * A source that is still asserted after its latch was cleared cannot + * be acknowledged; shut the line down instead of looping forever. + */ + if (again & pending && ++pio->stuck_acks > PHYTIUM_PIO_STUCK_LIMIT) { + pr_err("phytium-pio: latch bit 0x%x stuck, disabling irq %d\n", + again & pending, pio->irq); + disable_irq_nosync(pio->irq); + } else if (!(again & pending)) { + pio->stuck_acks = 0; + } +out: + chained_irq_exit(chip, desc); } -EXPORT_SYMBOL(phytium_pio_unhandle_interrupt_bit); -bool phytium_pio_clear_interrupt_bit(unsigned int bit) +static int phytium_pio_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { - unsigned long flags; - u32 pending; - - if (!ppio || bit >= 32) - return false; - - spin_lock_irqsave(&ppio->lock, flags); - pending = readl(ppio->regbase + ppio->int_status_reg); - if (pending & BIT(bit)) { - writel((u32)~BIT(bit), ppio->regbase + ppio->int_clear_reg); - readl(ppio->regbase + ppio->int_status_reg); - } - spin_unlock_irqrestore(&ppio->lock, flags); + irq_set_chip_and_handler(virq, &phytium_pio_irq_chip, handle_simple_irq); + irq_set_noprobe(virq); - return pending & BIT(bit); + return 0; } -EXPORT_SYMBOL(phytium_pio_clear_interrupt_bit); -u32 phytium_pio_get_int_status(void) +static const struct irq_domain_ops phytium_pio_domain_ops = { + .map = phytium_pio_domain_map, +}; + +/** + * phytium_pio_irq_get - Linux irq of a latch bit + * @bit: latch bit, see PHYTIUM_PIO_*_INT + * + * The mapping is created on the first call. + * + * Return: the Linux irq, or a negative errno. + */ +int phytium_pio_irq_get(unsigned int bit) { - unsigned long flags; - u32 ret = 0; + unsigned int virq; - if (!ppio) - return ret; - spin_lock_irqsave(&ppio->lock, flags); - ret = readl(ppio->regbase + ppio->int_status_reg); - spin_unlock_irqrestore(&ppio->lock, flags); + if (!ppio || !ppio->domain || bit >= PHYTIUM_PIO_IRQ_NR) + return -ENODEV; - return ret; -} -EXPORT_SYMBOL(phytium_pio_get_int_status); + virq = irq_create_mapping(ppio->domain, bit); -int phytium_pio_get_irq(void) -{ - if (!ppio) - return -EINVAL; - return ppio->irq; + return virq ? virq : -ENOMEM; } -EXPORT_SYMBOL(phytium_pio_get_irq); +EXPORT_SYMBOL_GPL(phytium_pio_irq_get); +/* Children live in the firmware window, not in the latch window. */ bool phytium_pio_is_lpc_mem(phys_addr_t addr) { - return ppio && addr >= ppio->lpc_base && - addr - ppio->lpc_base < PHYTIUM_LPC_IOMEM_SIZE; + return ppio && addr >= ppio->win_base && + addr - ppio->win_base < PHYTIUM_PIO_LENGTH_LONG; } EXPORT_SYMBOL(phytium_pio_is_lpc_mem); -static irqreturn_t phytium_pio_irq_handler(int irq, void *dev_id) -{ - struct phytium_pio *pio = dev_id; - unsigned long flags; - u32 clear_bits, pending; - - spin_lock_irqsave(&pio->lock, flags); - pending = readl(pio->regbase + pio->int_status_reg); - clear_bits = pending & ~pio->handled_bits; - - if (clear_bits) { - writel(~clear_bits, pio->regbase + pio->int_clear_reg); - readl(pio->regbase + pio->int_status_reg); - } - spin_unlock_irqrestore(&pio->lock, flags); - - return IRQ_RETVAL(pending); -} - /* * phytium_pio_in - input the data from the target I/O port * @hostdata: pointer to the device information relevant to PIO controller @@ -280,7 +264,6 @@ static int phytium_pio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct logic_pio_hwaddr *range; struct phytium_pio *pio; - resource_size_t reg_size = PHYTIUM_LPC_IOMEM_SIZE; phys_addr_t reg_start; struct resource *res; int ret; @@ -291,7 +274,7 @@ static int phytium_pio_probe(struct platform_device *pdev) spin_lock_init(&pio->lock); pio->membase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR_OR_NULL(pio->membase)) + if (IS_ERR(pio->membase)) return PTR_ERR(pio->membase); pio->irq = platform_get_irq(pdev, 0); @@ -300,21 +283,23 @@ static int phytium_pio_probe(struct platform_device *pdev) return pio->irq; } - /* - * The ACPI _CRS window only covers the short PIO aperture, while the - * interrupt latch registers live near the end of the long LPC window - * (base + 0x7ffffc0/0x7ffffc4). Map the long window starting at the - * firmware supplied LPC base (0x10000000 on D3000M, 0x20000000 on - * D2000/D3000): reading the latch through the short window can return - * unrelated device state such as UART_IIR. - */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; - reg_start = res->start; - pio->lpc_base = reg_start; + pio->win_base = res->start; - pio->regbase = devm_ioremap(dev, reg_start, reg_size); + /* The latch window is a SoC property, not what resource 0 describes. */ + if (cpu_is_ft_d2000()) { + reg_start = PHYTIUM_PIO_D2000_BASE; + } else if (is_pd2408()) { + reg_start = PHYTIUM_PIO_D3000M_BASE; + } else { + reg_start = res->start; + if (resource_size(res) < PHYTIUM_PIO_LENGTH_LONG) + dev_warn(dev, "short window on an unknown SoC, the latch address may be wrong\n"); + } + + pio->regbase = devm_ioremap(dev, reg_start, PHYTIUM_PIO_LENGTH_LONG); if (!pio->regbase) return -ENOMEM; @@ -334,24 +319,30 @@ static int phytium_pio_probe(struct platform_device *pdev) range->hostdata = pio; range->ops = &phytium_pio_ops; pio->range = range; - ppio = pio; - ret = devm_request_irq(dev, pio->irq, phytium_pio_irq_handler, - IRQF_SHARED, dev_name(dev), pio); - if (ret) { - dev_err(dev, "Failed to request irq (%d)!\n", ret); - goto out; + + pio->domain = irq_domain_create_linear(dev_fwnode(dev), + PHYTIUM_PIO_IRQ_NR, + &phytium_pio_domain_ops, pio); + if (!pio->domain) { + dev_err(dev, "Failed to create the latch irq domain\n"); + return -ENOMEM; } ret = logic_pio_register_range(range); if (ret) { dev_err(dev, "Failed to register logic pio range (%d)!\n", ret); - goto out; + irq_domain_remove(pio->domain); + pio->domain = NULL; + return ret; } + + irq_set_chained_handler_and_data(pio->irq, phytium_pio_irq_dispatch, pio); + + /* Children look the controller up through ppio. */ + ppio = pio; dev_set_drvdata(dev, pio); -out: - if (ret) - ppio = NULL; - return ret; + + return 0; } static int phytium_pio_remove(struct platform_device *pdev) @@ -363,6 +354,20 @@ static int phytium_pio_remove(struct platform_device *pdev) if (!range) return -EINVAL; + if (pio && pio->domain) { + int bit; + + irq_set_chained_handler_and_data(pio->irq, NULL, NULL); + for (bit = 0; bit < PHYTIUM_PIO_IRQ_NR; bit++) { + unsigned int virq = irq_find_mapping(pio->domain, bit); + + if (virq) + irq_dispose_mapping(virq); + } + irq_domain_remove(pio->domain); + pio->domain = NULL; + } + if (ppio == pio) ppio = NULL; logic_pio_unregister_range(range); diff --git a/drivers/bus/phytium_pio.h b/drivers/bus/phytium_pio.h index bb43c67e8912..16b818a621be 100644 --- a/drivers/bus/phytium_pio.h +++ b/drivers/bus/phytium_pio.h @@ -9,6 +9,7 @@ #define _PHYTIUM_PIO_H_ #include +#include #include #define PHYTIUM_PIO_LENGTH_LONG 0x8000000 @@ -16,39 +17,38 @@ #define FT3000_PIO_CLR_INT 0x7FFFFC0 #define FT2000_PIO_INT_STATE 0x7FFFFF4 #define FT2000_PIO_CLR_INT 0x7FFFFF0 -#define LEGACY_ISA_SIZE 0x400 +/* Base of the SoC window holding the interrupt latch registers. */ +#define PHYTIUM_PIO_D2000_BASE 0x20000000 +#define PHYTIUM_PIO_D3000M_BASE 0x10000000 -/* Long LPC window holding the interrupt latch registers. */ -#define PHYTIUM_LPC_IOMEM_SIZE 0x08000000 +#define PHYTIUM_PIO_IRQ_NR 32 +#define PHYTIUM_PIO_STUCK_LIMIT 1000 -/* LPC interrupt latch bits shared by the legacy children. */ +/* LPC interrupt latch bits, one Linux irq each. */ #define PHYTIUM_PIO_I8042_KBD_INT 1 #define PHYTIUM_PIO_UART_INT 4 #define PHYTIUM_PIO_EC_INT 11 #define PHYTIUM_PIO_I8042_AUX_INT 12 struct phytium_pio { + /* Firmware window: indirect PIO data accesses are decoded here. */ void __iomem *membase; - /* Long LPC window used for interrupt status/clear accesses. */ + /* Long LPC window: holds the interrupt latch registers. */ void __iomem *regbase; int irq; spinlock_t lock; u32 int_status_reg; u32 int_clear_reg; - /* LPC latch bits owned by a registered child handler. */ - u32 handled_bits; - phys_addr_t lpc_base; + struct irq_domain *domain; + u32 stuck_acks; + /* Physical start of the firmware window, see phytium_pio_is_lpc_mem(). */ + phys_addr_t win_base; struct logic_pio_hwaddr *range; }; bool check_cpu_type(void); -int phytium_pio_get_irq(void); -void phytium_pio_handle_interrupt_bit(unsigned int bit); -void phytium_pio_unhandle_interrupt_bit(unsigned int bit); -bool phytium_pio_clear_interrupt_bit(unsigned int bit); +int phytium_pio_irq_get(unsigned int bit); bool phytium_pio_is_lpc_mem(phys_addr_t addr); -void phytium_pio_clear_interrupt(u32 val); -u32 phytium_pio_get_int_status(void); #endif diff --git a/drivers/input/serio/i8042-phytio.h b/drivers/input/serio/i8042-phytio.h index cb140eeb28a6..378d3cf10ced 100644 --- a/drivers/input/serio/i8042-phytio.h +++ b/drivers/input/serio/i8042-phytio.h @@ -6,9 +6,12 @@ #ifndef _I8042_PHYTIO_H #define _I8042_PHYTIO_H +#include +#include +#include +#include #include #include -#include #include #include "../../bus/phytium_pio.h" @@ -35,10 +38,6 @@ static int i8042_aux_irq = 0x4c; #define I8042_STATUS_REG 0x64 #define I8042_DATA_REG 0x60 -#define EC_EVENT_BIT (1 << 11) -#define I8042_KEY_BIT (1 << 1) -#define I8042_TOUCH_BIT (1 << 12) - /* * i8042 IO operations using Logic PIO framework * The Logic PIO framework will automatically call Phytium PIO's @@ -64,29 +63,111 @@ static inline void i8042_write_command(int val) outb(val, I8042_COMMAND_REG); } +/* + * Boards that publish a KBCI8042 node but leave nothing behind the legacy + * ports: a read of 0x60/0x64 never completes, so i8042_probe() hangs. + */ +static const struct dmi_system_id phytium_i8042_skip[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FD2000ZX200MB1"), + }, + }, + { } +}; + +static inline bool phytium_i8042_acpi_described(void) +{ + return IS_ENABLED(CONFIG_ACPI) && + acpi_dev_present("KBCI8042", NULL, -1); +} + +#ifdef CONFIG_ACPI +static int phytium_i8042_io_cb(struct acpi_resource *ares, void *data) +{ + bool *found = data; + + if (ares->type == ACPI_RESOURCE_TYPE_IO && + ares->data.io.minimum <= 0x60 && ares->data.io.maximum >= 0x64) + *found = true; + + return 1; +} + +/* Does _CRS describe the controller's own ports? */ +static inline bool phytium_i8042_acpi_has_ports(void) +{ + struct acpi_device *adev; + LIST_HEAD(res); + bool found = false; + + adev = acpi_dev_get_first_match_dev("KBCI8042", NULL, -1); + if (!adev) + return false; + + acpi_dev_get_resources(adev, &res, phytium_i8042_io_cb, &found); + acpi_dev_free_resource_list(&res); + acpi_dev_put(adev); + + return found; +} +#else +static inline bool phytium_i8042_acpi_has_ports(void) +{ + return true; +} +#endif + /* * Platform initialization */ static inline int i8042_platform_init(void) { - int irq; + bool described = phytium_i8042_acpi_described(); + bool laptop; + int kbd_irq, aux_irq; /* Check if we're running on Phytium CPU */ if (!check_cpu_type()) { - pr_info("i8042-phytio: Not running on Phytium CPU\n"); + pr_info("Not running on Phytium CPU\n"); + return -ENODEV; + } + + if (dmi_check_system(phytium_i8042_skip)) { + pr_info("KBC does not answer on this board, skipping\n"); return -ENODEV; } - irq = phytium_pio_get_irq(); - if (irq >= 0) { - i8042_kbd_irq = irq; - i8042_aux_irq = irq; + /* + * Probe like before unless the firmware documents the controller + * without its ports. Laptops keep probing: an internal keyboard may + * be the only input. + */ + laptop = !IS_ENABLED(CONFIG_DMI) || chassis_types_is_laptop(); + if (described && !laptop && !phytium_i8042_acpi_has_ports()) { + pr_info("KBC has no ports described, skipping\n"); + return -ENODEV; } + /* + * The PIO controller hands out one Linux irq per latch bit and clears + * the latch itself, so the irq is not shared with the other legacy + * devices. + */ + kbd_irq = phytium_pio_irq_get(PHYTIUM_PIO_I8042_KBD_INT); + aux_irq = phytium_pio_irq_get(PHYTIUM_PIO_I8042_AUX_INT); + if (kbd_irq < 0 || aux_irq < 0) { + pr_err("no irq for the KBC\n"); + return -ENODEV; + } + + i8042_kbd_irq = kbd_irq; + i8042_aux_irq = aux_irq; + /* Set reset behavior */ i8042_reset = I8042_RESET_ALWAYS; - pr_info("i8042-phytio: Initialized on Phytium platform\n"); + pr_info("irqs %d/%d\n", kbd_irq, aux_irq); return 0; } diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 32110a2598dd..29ff30fce384 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -519,14 +519,6 @@ static bool i8042_filter(unsigned char data, unsigned char str, return false; } -#ifdef CONFIG_PHYTIUM_PIO -static bool i8042_phytium_shared_parent(void) -{ - return check_cpu_type() && I8042_KBD_IRQ == I8042_AUX_IRQ && - phytium_pio_get_irq() == I8042_KBD_IRQ; -} -#endif - /* * i8042_interrupt() is the most important function in this driver - * it handles the interrupts from the i8042, and sends incoming bytes @@ -546,13 +538,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) spin_lock_irqsave(&i8042_lock, flags); -#ifdef CONFIG_PHYTIUM_PIO - if (i8042_phytium_shared_parent()) { - phytium_pio_clear_interrupt_bit(PHYTIUM_PIO_I8042_KBD_INT); - phytium_pio_clear_interrupt_bit(PHYTIUM_PIO_I8042_AUX_INT); - } -#endif - str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { spin_unlock_irqrestore(&i8042_lock, flags); @@ -1459,57 +1444,12 @@ static void i8042_unregister_ports(void) } } -#ifdef CONFIG_PHYTIUM_PIO -static unsigned int i8042_phytium_irq_users; - -static int i8042_phytium_request_irq(void) -{ - int error; - - if (i8042_phytium_irq_users++) - return 0; - - error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); - if (error) - i8042_phytium_irq_users = 0; - - return error; -} - -static void i8042_phytium_free_irq(void) -{ - if (i8042_phytium_irq_users && !--i8042_phytium_irq_users) - free_irq(I8042_KBD_IRQ, i8042_platform_device); -} -#endif - static void i8042_free_irqs(void) { -#ifdef CONFIG_PHYTIUM_PIO - bool shared_parent = i8042_phytium_shared_parent(); -#endif - - if (i8042_aux_irq_registered) { -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_I8042_AUX_INT); - if (shared_parent) - i8042_phytium_free_irq(); - else -#endif - free_irq(I8042_AUX_IRQ, i8042_platform_device); - } - if (i8042_kbd_irq_registered) { -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_I8042_KBD_INT); - if (shared_parent) - i8042_phytium_free_irq(); - else -#endif - free_irq(I8042_KBD_IRQ, i8042_platform_device); - } + if (i8042_aux_irq_registered) + free_irq(I8042_AUX_IRQ, i8042_platform_device); + if (i8042_kbd_irq_registered) + free_irq(I8042_KBD_IRQ, i8042_platform_device); i8042_aux_irq_registered = i8042_kbd_irq_registered = false; } @@ -1519,9 +1459,6 @@ static int i8042_setup_aux(void) int (*aux_enable)(void); int error; int i; -#ifdef CONFIG_PHYTIUM_PIO - bool shared_parent = i8042_phytium_shared_parent(); -#endif if (i8042_check_aux()) return -ENODEV; @@ -1540,19 +1477,10 @@ static int i8042_setup_aux(void) aux_enable = i8042_enable_mux_ports; } -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - error = i8042_phytium_request_irq(); - else -#endif - error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); + error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); if (error) goto err_free_ports; -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_handle_interrupt_bit(PHYTIUM_PIO_I8042_AUX_INT); -#endif error = aux_enable(); if (error) @@ -1562,14 +1490,7 @@ static int i8042_setup_aux(void) return 0; err_free_irq: -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_I8042_AUX_INT); - if (shared_parent) - i8042_phytium_free_irq(); - else -#endif - free_irq(I8042_AUX_IRQ, i8042_platform_device); + free_irq(I8042_AUX_IRQ, i8042_platform_device); err_free_ports: i8042_free_aux_ports(); return error; @@ -1578,27 +1499,15 @@ static int i8042_setup_aux(void) static int i8042_setup_kbd(void) { int error; -#ifdef CONFIG_PHYTIUM_PIO - bool shared_parent = i8042_phytium_shared_parent(); -#endif error = i8042_create_kbd_port(); if (error) return error; -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - error = i8042_phytium_request_irq(); - else -#endif - error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); + error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); if (error) goto err_free_port; -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_handle_interrupt_bit(PHYTIUM_PIO_I8042_KBD_INT); -#endif error = i8042_enable_kbd_port(); if (error) @@ -1608,14 +1517,7 @@ static int i8042_setup_kbd(void) return 0; err_free_irq: -#ifdef CONFIG_PHYTIUM_PIO - if (shared_parent) - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_I8042_KBD_INT); - if (shared_parent) - i8042_phytium_free_irq(); - else -#endif - free_irq(I8042_KBD_IRQ, i8042_platform_device); + free_irq(I8042_KBD_IRQ, i8042_platform_device); err_free_port: i8042_free_kbd_port(); return error; diff --git a/drivers/platform/arm64/phytium/ec_it8528.c b/drivers/platform/arm64/phytium/ec_it8528.c index 87dd3afe1b1f..08e135663a4b 100644 --- a/drivers/platform/arm64/phytium/ec_it8528.c +++ b/drivers/platform/arm64/phytium/ec_it8528.c @@ -211,13 +211,3 @@ int it8528_init(void) } EXPORT_SYMBOL(it8528_init); -/* - * Acknowledge the EC interrupt source only. The PIO clear register treats a - * zero bit as "clear this source", so the complemented mask leaves the - * keyboard and touchpad sources pending for i8042 instead of dropping them. - */ -void lpc_ec_interrupt_clear(void) -{ - phytium_pio_clear_interrupt(~EC_EVENT_BIT); -} -EXPORT_SYMBOL(lpc_ec_interrupt_clear); diff --git a/drivers/platform/arm64/phytium/ec_it8528.h b/drivers/platform/arm64/phytium/ec_it8528.h index 38a23c5beb6e..96dffa8f55cf 100644 --- a/drivers/platform/arm64/phytium/ec_it8528.h +++ b/drivers/platform/arm64/phytium/ec_it8528.h @@ -5,8 +5,6 @@ #include #include "../../../bus/phytium_pio.h" -#define EC_EVENT_BIT (1 << 11) - extern spinlock_t i8042_lock; /* @@ -155,7 +153,6 @@ extern int it8528_query_clean_event(void); /* The ec interrupt functions */ extern int it8528_init(void); -extern void lpc_ec_interrupt_clear(void); /* EC sensor/hwmon registration */ extern void ec_sensors_register(struct platform_device *pdev); diff --git a/drivers/platform/arm64/phytium/phyt_laptop.c b/drivers/platform/arm64/phytium/phyt_laptop.c index f3e34066d6b6..7015f576de06 100644 --- a/drivers/platform/arm64/phytium/phyt_laptop.c +++ b/drivers/platform/arm64/phytium/phyt_laptop.c @@ -342,23 +342,13 @@ static bool ec_drain_events(void) return it8528_get_ec_evt_flags(); } -/* EC event interrupt, shared with i8042 over the PIO controller. */ +/* EC event interrupt, one of the PIO controller's latch sources. */ static irqreturn_t ft_ec_int_routine(int irq, void *dev_id) { unsigned long flags; - /* - * Test the EC status port rather than the PIO latch: i8042 may have - * acknowledged the shared interrupt before us and cleared the latch - * while the EC still has an event queued. - */ - if (!it8528_get_ec_evt_flags()) { - if (phytium_pio_clear_interrupt_bit(PHYTIUM_PIO_EC_INT)) - return IRQ_HANDLED; + if (!it8528_get_ec_evt_flags()) return IRQ_NONE; - } - - phytium_pio_clear_interrupt_bit(PHYTIUM_PIO_EC_INT); spin_lock_irqsave(&i8042_lock, flags); ec_drain_events(); @@ -380,22 +370,20 @@ static int lpc_int_init(void) strscpy(ft_lpc_device->name, EC_LPC_DEV, sizeof(ft_lpc_device->name)); - /* get lpc irq */ - ret = phytium_pio_get_irq(); + /* the PIO controller owns the EC latch and gives us a per-bit irq */ + ret = phytium_pio_irq_get(PHYTIUM_PIO_EC_INT); if (ret < 0) { pr_err("phytium ec: Failed to get lpc irq: %d\n", ret); goto err_free; } ft_lpc_device->irq = ret; - /* Register EC on the shared LPC parent IRQ. */ - ret = request_irq(ft_lpc_device->irq, ft_ec_int_routine, - IRQF_SHARED, ft_lpc_device->name, ft_lpc_device); + ret = request_irq(ft_lpc_device->irq, ft_ec_int_routine, 0, + ft_lpc_device->name, ft_lpc_device); if (ret) { pr_err("phytium ec: Request irq fail: %d\n", ret); goto err_free; } - phytium_pio_handle_interrupt_bit(PHYTIUM_PIO_EC_INT); pr_debug("phytium: LPC init successful.\n"); @@ -1253,10 +1241,8 @@ static void ft_ec_irq_exit(void) if (ft_gpio_device) free_irq(ft_gpio_device->irq, ft_gpio_device); - if (ft_lpc_device) { - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_EC_INT); + if (ft_lpc_device) free_irq(ft_lpc_device->irq, ft_lpc_device); - } kfree(ft_gpio_device); ft_gpio_device = NULL; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 28893444c26e..c536028e92dc 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -39,10 +39,6 @@ #include #endif -#if defined(CONFIG_PHYTIUM_PIO) -#include "../../../bus/phytium_pio.h" -#endif - #include #include "8250.h" @@ -118,11 +114,6 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) pr_debug("%s(%d): start\n", __func__, irq); -#if defined(CONFIG_PHYTIUM_PIO) - if (check_cpu_type() && irq == phytium_pio_get_irq()) - phytium_pio_clear_interrupt_bit(PHYTIUM_PIO_UART_INT); -#endif - spin_lock(&i->lock); l = i->head; @@ -152,13 +143,6 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) return IRQ_RETVAL(handled); } -#if defined(CONFIG_PHYTIUM_PIO) -static bool serial8250_is_phytium_pio_irq(unsigned int irq) -{ - return check_cpu_type() && irq == phytium_pio_get_irq(); -} -#endif - /* * To support ISA shared interrupts, we need to have one interrupt * handler that ensures that the IRQ line has been deasserted @@ -227,10 +211,6 @@ static int serial_link_irq_chain(struct uart_8250_port *up) up->port.irqflags, up->port.name, i); if (ret < 0) serial_do_unlink(i, up); -#if defined(CONFIG_PHYTIUM_PIO) - else if (serial8250_is_phytium_pio_irq(up->port.irq)) - phytium_pio_handle_interrupt_bit(PHYTIUM_PIO_UART_INT); -#endif } return ret; @@ -252,13 +232,8 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) BUG_ON(i == NULL); BUG_ON(i->head == NULL); - if (list_empty(i->head)) { -#if defined(CONFIG_PHYTIUM_PIO) - if (serial8250_is_phytium_pio_irq(up->port.irq)) - phytium_pio_unhandle_interrupt_bit(PHYTIUM_PIO_UART_INT); -#endif + if (list_empty(i->head)) free_irq(up->port.irq, i); - } serial_do_unlink(i, up); mutex_unlock(&hash_mutex); diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index c708486775dc..e35527ef624e 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -434,20 +434,22 @@ static int serial_pnp_guess_board(struct pnp_dev *dev) } #if defined(CONFIG_PHYTIUM_PIO) -static bool phytium_pnp_is_int_uart(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) +/* UARTs behind the PIO controller get their irq from it, not from firmware. */ +static int phytium_pnp_uart_irq(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) { - if (!check_cpu_type() || phytium_pio_get_irq() < 0) - return false; + if (!check_cpu_type()) + return -ENODEV; if (strcmp(dev_id->id, "PNP0501")) - return false; + return -ENODEV; - if (pnp_port_valid(dev, 0) || pnp_port_valid(dev, 2)) - return true; + if (!pnp_port_valid(dev, 0) && !pnp_port_valid(dev, 2) && + !(pnp_mem_valid(dev, 0) && + phytium_pio_is_lpc_mem(pnp_mem_start(dev, 0)))) + return -ENODEV; - return pnp_mem_valid(dev, 0) && - phytium_pio_is_lpc_mem(pnp_mem_start(dev, 0)); + return phytium_pio_irq_get(PHYTIUM_PIO_UART_INT); } #endif @@ -465,11 +467,15 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) memset(&uart, 0, sizeof(uart)); #if defined(CONFIG_PHYTIUM_PIO) - if (phytium_pnp_is_int_uart(dev, dev_id)) { - uart.port.irq = phytium_pio_get_irq(); - uart.port.flags |= UPF_SHARE_IRQ; - } else if (pnp_irq_valid(dev, 0)) { - uart.port.irq = pnp_irq(dev, 0); + { + int irq = phytium_pnp_uart_irq(dev, dev_id); + + if (irq >= 0) { + uart.port.irq = irq; + uart.port.flags |= UPF_SHARE_IRQ; + } else if (pnp_irq_valid(dev, 0)) { + uart.port.irq = pnp_irq(dev, 0); + } } #else if (pnp_irq_valid(dev, 0))