irqchip/gic-v3-its: Add VLPI configuration handling

When a VLPI is reconfigured (enabled, disabled, change in priority),
the full configuration byte must be written, and the caches invalidated.

Also, when using the irq_mask/irq_unmask methods, it is necessary
to disable the doorbell for that particular interrupt (by mapping it
to 1023) on top of clearing the Enable bit.

Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

+70 -5
+70 -5
drivers/irqchip/irq-gic-v3-its.c
··· 812 812 return d->hwirq - its_dev->event_map.lpi_base; 813 813 } 814 814 815 - static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) 815 + static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) 816 816 { 817 - struct its_device *its_dev = irq_data_get_irq_chip_data(d); 818 - irq_hw_number_t hwirq = d->hwirq; 817 + irq_hw_number_t hwirq; 819 818 struct page *prop_page; 820 819 u8 *cfg; 821 820 822 - prop_page = gic_rdists->prop_page; 821 + if (irqd_is_forwarded_to_vcpu(d)) { 822 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 823 + u32 event = its_get_event_id(d); 824 + 825 + prop_page = its_dev->event_map.vm->vprop_page; 826 + hwirq = its_dev->event_map.vlpi_maps[event].vintid; 827 + } else { 828 + prop_page = gic_rdists->prop_page; 829 + hwirq = d->hwirq; 830 + } 823 831 824 832 cfg = page_address(prop_page) + hwirq - 8192; 825 833 *cfg &= ~clr; 826 - *cfg |= set; 834 + *cfg |= set | LPI_PROP_GROUP1; 827 835 828 836 /* 829 837 * Make the above write visible to the redistributors. ··· 842 834 gic_flush_dcache_to_poc(cfg, sizeof(*cfg)); 843 835 else 844 836 dsb(ishst); 837 + } 838 + 839 + static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) 840 + { 841 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 842 + 843 + lpi_write_config(d, clr, set); 845 844 its_send_inv(its_dev, its_get_event_id(d)); 845 + } 846 + 847 + static void its_vlpi_set_doorbell(struct irq_data *d, bool enable) 848 + { 849 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 850 + u32 event = its_get_event_id(d); 851 + 852 + if (its_dev->event_map.vlpi_maps[event].db_enabled == enable) 853 + return; 854 + 855 + its_dev->event_map.vlpi_maps[event].db_enabled = enable; 856 + 857 + /* 858 + * More fun with the architecture: 859 + * 860 + * Ideally, we'd issue a VMAPTI to set the doorbell to its LPI 861 + * value or to 1023, depending on the enable bit. But that 862 + * would be issueing a mapping for an /existing/ DevID+EventID 863 + * pair, which is UNPREDICTABLE. Instead, let's issue a VMOVI 864 + * to the /same/ vPE, using this opportunity to adjust the 865 + * doorbell. Mouahahahaha. We loves it, Precious. 866 + */ 867 + its_send_vmovi(its_dev, event); 846 868 } 847 869 848 870 static void its_mask_irq(struct irq_data *d) 849 871 { 872 + if (irqd_is_forwarded_to_vcpu(d)) 873 + its_vlpi_set_doorbell(d, false); 874 + 850 875 lpi_update_config(d, LPI_PROP_ENABLED, 0); 851 876 } 852 877 853 878 static void its_unmask_irq(struct irq_data *d) 854 879 { 880 + if (irqd_is_forwarded_to_vcpu(d)) 881 + its_vlpi_set_doorbell(d, true); 882 + 855 883 lpi_update_config(d, 0, LPI_PROP_ENABLED); 856 884 } 857 885 ··· 899 855 struct its_device *its_dev = irq_data_get_irq_chip_data(d); 900 856 struct its_collection *target_col; 901 857 u32 id = its_get_event_id(d); 858 + 859 + /* A forwarded interrupt should use irq_set_vcpu_affinity */ 860 + if (irqd_is_forwarded_to_vcpu(d)) 861 + return -EINVAL; 902 862 903 863 /* lpi cannot be routed to a redistributor that is on a foreign node */ 904 864 if (its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { ··· 1072 1024 return ret; 1073 1025 } 1074 1026 1027 + static int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info) 1028 + { 1029 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 1030 + 1031 + if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) 1032 + return -EINVAL; 1033 + 1034 + if (info->cmd_type == PROP_UPDATE_AND_INV_VLPI) 1035 + lpi_update_config(d, 0xff, info->config); 1036 + else 1037 + lpi_write_config(d, 0xff, info->config); 1038 + its_vlpi_set_doorbell(d, !!(info->config & LPI_PROP_ENABLED)); 1039 + 1040 + return 0; 1041 + } 1042 + 1075 1043 static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 1076 1044 { 1077 1045 struct its_device *its_dev = irq_data_get_irq_chip_data(d); ··· 1110 1046 1111 1047 case PROP_UPDATE_VLPI: 1112 1048 case PROP_UPDATE_AND_INV_VLPI: 1049 + return its_vlpi_prop_update(d, info); 1113 1050 1114 1051 default: 1115 1052 return -EINVAL;