Skip to content

Commit 67b9a3b

Browse files
committed
Merge feature/vpci (36ff104)
* msft/feature/vpci: Hyper-V: vPCI: Enable virtual PCI for ARM64 Hyper-V: pci: arm64: Allow use arch-specific pci sysdata Hyper-V: vPCI: Move the MSI entry struct definition to arch specific Hyper-V: pci: controller: Introduce arch-specific init/exit interface Hyper-V: pci: Generify irq/msi set-up and handling
2 parents 0ef2793 + 36ff104 commit 67b9a3b

File tree

14 files changed

+470
-28
lines changed

14 files changed

+470
-28
lines changed

arch/arm64/hyperv/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
obj-y := hv_core.o mshyperv.o hv_hvc.o
2+
obj-y := hv_core.o mshyperv.o hv_hvc.o hv_pci_vector.o

arch/arm64/hyperv/hv_pci_vector.c

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Architecture specific vector management for the Hyper-V vPCI.
5+
*
6+
* Copyright (C) 2018, Microsoft, Inc.
7+
*
8+
* Author : Sunil Muthuswamy <[email protected]>
9+
*
10+
* This program is free software; you can redistribute it and/or modify it
11+
* under the terms of the GNU General Public License version 2 as published
12+
* by the Free Software Foundation.
13+
*
14+
* This program is distributed in the hope that it will be useful, but
15+
* WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
17+
* NON INFRINGEMENT. See the GNU General Public License for more
18+
* details.
19+
*/
20+
21+
#include <asm/mshyperv.h>
22+
#include <linux/acpi.h>
23+
#include <linux/irqdomain.h>
24+
#include <linux/irq.h>
25+
#include <acpi/acpi_bus.h>
26+
27+
/*
28+
* Hyper-V ARM64 host uses 32 SPI's for vPCI.
29+
* Currently, starting from 40 and limiting it to 32
30+
* since there is an overlap in the 30s range.
31+
*/
32+
#define HV_PCI_MSI_SPI_START 40
33+
#define HV_PCI_MSI_SPI_NR 32
34+
35+
struct hv_pci_chip_data {
36+
spinlock_t lock; /* Protects this struct */
37+
struct irq_domain *domain;
38+
unsigned long bm;
39+
};
40+
41+
extern struct irq_domain *gicv3_vector_domain;
42+
static struct hv_pci_chip_data *chip_data;
43+
static struct irq_chip hv_msi_irq_chip = {
44+
.name = "Hyper-V ARM64 PCI MSI",
45+
.irq_set_affinity = irq_chip_set_affinity_parent,
46+
.irq_eoi = irq_chip_eoi_parent,
47+
.irq_mask = irq_chip_mask_parent,
48+
.irq_unmask = irq_chip_unmask_parent
49+
};
50+
51+
/**
52+
* Frees the specified number of interrupts.
53+
* @domain: The IRQ domain
54+
* @virq: The virtual IRQ number.
55+
* @nr_irqs: Number of IRQ's to free.
56+
*/
57+
static void hv_pci_vec_irq_domain_free(struct irq_domain *domain,
58+
unsigned int virq, unsigned int nr_irqs)
59+
{
60+
unsigned long flags;
61+
int i;
62+
63+
for (i = 0; i < nr_irqs; i++) {
64+
struct irq_data *irqd = irq_domain_get_irq_data(domain,
65+
virq + i);
66+
spin_lock_irqsave(&chip_data->lock, flags);
67+
clear_bit(irqd->hwirq - HV_PCI_MSI_SPI_START, &chip_data->bm);
68+
spin_unlock_irqrestore(&chip_data->lock, flags);
69+
irq_domain_reset_irq_data(irqd);
70+
}
71+
72+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
73+
}
74+
75+
/**
76+
* Allocate an interrupt from the domain.
77+
* @hwirq: Will be set to the allocated H/W IRQ.
78+
*
79+
* Return: 0 on success and error value on failure.
80+
*/
81+
static int hv_pci_vec_alloc_device_irq(irq_hw_number_t *hwirq)
82+
{
83+
unsigned long flags;
84+
int index;
85+
86+
spin_lock_irqsave(&chip_data->lock, flags);
87+
index = find_first_zero_bit(&chip_data->bm, HV_PCI_MSI_SPI_NR);
88+
if (index == HV_PCI_MSI_SPI_NR) {
89+
spin_unlock_irqrestore(&chip_data->lock, flags);
90+
pr_err("No more free IRQ vector available\n");
91+
return -ENOSPC;
92+
}
93+
94+
set_bit(index, &chip_data->bm);
95+
spin_unlock_irqrestore(&chip_data->lock, flags);
96+
*hwirq = index + HV_PCI_MSI_SPI_START;
97+
return 0;
98+
}
99+
100+
/**
101+
* Allocate an interrupt from the parent GIC domain.
102+
* @domain: The IRQ domain.
103+
* @virq: The virtual IRQ number.
104+
* @hwirq: The H/W IRQ number that needs to be allocated.
105+
*
106+
* Return: 0 on success and error value on failure.
107+
*/
108+
static int hv_pci_vec_irq_gic_domain_alloc(struct irq_domain *domain,
109+
unsigned int virq,
110+
irq_hw_number_t hwirq)
111+
{
112+
struct irq_fwspec fwspec;
113+
114+
fwspec.fwnode = domain->parent->fwnode;
115+
fwspec.param_count = 2;
116+
fwspec.param[0] = hwirq;
117+
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
118+
119+
return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
120+
}
121+
122+
/**
123+
* Allocate specfied number of interrupt from the domain.
124+
* @domain: The IRQ domain.
125+
* @virq: The starting virtual IRQ number.
126+
* @nr_irqs: Number of IRQ's to allocate.
127+
* @args: The MSI alloc information.
128+
*
129+
* Return: 0 on success and error value on failure.
130+
*/
131+
static int hv_pci_vec_irq_domain_alloc(struct irq_domain *domain,
132+
unsigned int virq, unsigned int nr_irqs,
133+
void *args)
134+
{
135+
irq_hw_number_t hwirq;
136+
int i;
137+
int ret;
138+
139+
for (i = 0; i < nr_irqs; i++) {
140+
ret = hv_pci_vec_alloc_device_irq(&hwirq);
141+
if (ret)
142+
goto free_irq;
143+
144+
ret = hv_pci_vec_irq_gic_domain_alloc(domain, virq + i, hwirq);
145+
if (ret)
146+
goto free_irq;
147+
148+
ret = irq_domain_set_hwirq_and_chip(domain, virq + i,
149+
hwirq, &hv_msi_irq_chip,
150+
domain->host_data);
151+
if (ret)
152+
goto free_irq;
153+
154+
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq + i)));
155+
pr_debug("pID:%d vID:%d\n", (int)hwirq, virq + i);
156+
}
157+
158+
return 0;
159+
160+
free_irq:
161+
hv_pci_vec_irq_domain_free(domain, virq, nr_irqs);
162+
return ret;
163+
}
164+
165+
/**
166+
* Activate the interrupt.
167+
* @domain: The IRQ domain.
168+
* @irqd: IRQ data.
169+
* @reserve: Indicates whether the IRQ's can be reserved.
170+
*
171+
* Return: 0 on success and error value on failure.
172+
*/
173+
static int hv_pci_vec_irq_domain_activate(struct irq_domain *domain,
174+
struct irq_data *irqd, bool reserve)
175+
{
176+
/* Bind the SPI to all available online CPUs */
177+
irq_data_update_effective_affinity(irqd, cpu_online_mask);
178+
return 0;
179+
}
180+
181+
/**
182+
* Deactivate the interrupt.
183+
* @domain: The IRQ domain.
184+
* @irqd: IRQ data pertaining to the interrupt..
185+
*/
186+
static void hv_pci_vec_irq_domain_deactivate(struct irq_domain *domain,
187+
struct irq_data *irqd)
188+
{
189+
/* TODO */
190+
//clear_irq_vector(irqd);
191+
}
192+
193+
static const struct irq_domain_ops hv_pci_domain_ops = {
194+
.alloc = hv_pci_vec_irq_domain_alloc,
195+
.free = hv_pci_vec_irq_domain_free,
196+
.activate = hv_pci_vec_irq_domain_activate,
197+
.deactivate = hv_pci_vec_irq_domain_deactivate
198+
};
199+
200+
201+
/**
202+
* This routine performs the arechitecture specific initialization for vector
203+
* domain to operate. It allocates an IRQ domain tree as a child of the GIC
204+
* IRQ domain.
205+
*
206+
* Return: 0 on success and error value on failure.
207+
*/
208+
int hv_pci_vector_init(void)
209+
{
210+
struct fwnode_handle *fn;
211+
int ret;
212+
213+
if (!is_fwnode_irqchip(gicv3_vector_domain->fwnode)) {
214+
pr_err("Unexpected parent IRQ chip\n");
215+
return -EINVAL;
216+
}
217+
218+
ret = -ENOMEM;
219+
chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
220+
if (!chip_data)
221+
return ret;
222+
223+
spin_lock_init(&chip_data->lock);
224+
fn = irq_domain_alloc_named_fwnode("Hyper-V ARM64 vPCI");
225+
if (!fn)
226+
goto free_chip;
227+
228+
chip_data->domain = irq_domain_create_tree(fn, &hv_pci_domain_ops,
229+
NULL);
230+
irq_domain_free_fwnode(fn);
231+
if (!chip_data->domain) {
232+
pr_err("Failed to create IRQ domain\n");
233+
goto free_chip;
234+
}
235+
236+
chip_data->domain->parent = gicv3_vector_domain;
237+
chip_data->domain->host_data = chip_data;
238+
return 0;
239+
240+
free_chip:
241+
kfree(chip_data);
242+
chip_data = NULL;
243+
return ret;
244+
}
245+
246+
/* This routine performs the cleanup for the IRQ domain. */
247+
void hv_pci_vector_free(void)
248+
{
249+
if (!chip_data)
250+
return;
251+
252+
if (chip_data->domain)
253+
irq_domain_remove(chip_data->domain);
254+
255+
kfree(chip_data);
256+
}
257+
258+
/* Performs the architecture specific initialization for Hyper-V PCI. */
259+
int hv_pci_arch_init(void)
260+
{
261+
return hv_pci_vector_init();
262+
}
263+
EXPORT_SYMBOL_GPL(hv_pci_arch_init);
264+
265+
/* Architecture specific cleanup for Hyper-V PCI. */
266+
void hv_pci_arch_free(void)
267+
{
268+
hv_pci_vector_free();
269+
}
270+
EXPORT_SYMBOL_GPL(hv_pci_arch_free);
271+
272+
struct irq_domain *hv_msi_parent_vector_domain(void)
273+
{
274+
return chip_data->domain;
275+
}
276+
EXPORT_SYMBOL_GPL(hv_msi_parent_vector_domain);
277+
278+
unsigned int hv_msi_get_int_vector(struct irq_data *irqd)
279+
{
280+
irqd = irq_domain_get_irq_data(chip_data->domain, irqd->irq);
281+
return irqd->hwirq;
282+
}
283+
EXPORT_SYMBOL_GPL(hv_msi_get_int_vector);

arch/arm64/include/asm/hyperv-tlfs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@
6464
#define HV_REGISTER_STIMER0_CONFIG 0x000B0000
6565
#define HV_REGISTER_STIMER0_COUNT 0x000B0001
6666

67+
union hv_msi_entry {
68+
u64 as_uint64[2];
69+
struct {
70+
u64 address;
71+
u32 data;
72+
u32 reserved;
73+
} __packed;
74+
};
75+
6776
#include <asm-generic/hyperv-tlfs.h>
6877

6978
#endif

arch/arm64/include/asm/mshyperv.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
#define _ASM_MSHYPERV_H
2020

2121
#include <linux/types.h>
22+
#include <linux/interrupt.h>
2223
#include <linux/arm-smccc.h>
24+
#include <linux/msi.h>
2325
#include <asm/hyperv-tlfs.h>
26+
#include <asm/msi.h>
2427
#include <clocksource/arm_arch_timer.h>
2528

2629
#if IS_ENABLED(CONFIG_HYPERV)
@@ -72,6 +75,43 @@ static inline u64 hv_get_raw_timer(void)
7275
ARM_SMCCC_OWNER_VENDOR_HYP, \
7376
HV_SMCCC_FUNC_NUMBER)
7477

78+
#define hv_msi_handler NULL
79+
#define hv_msi_handler_name NULL
80+
81+
/* Architecture specific Hyper-V PCI MSI initialization and cleanup routines. */
82+
int hv_pci_arch_init(void);
83+
void hv_pci_arch_free(void);
84+
85+
/* Returns the Hyper-V PCI parent MSI vector domain. */
86+
struct irq_domain *hv_msi_parent_vector_domain(void);
87+
88+
/* Returns the interrupt vector mapped to the given IRQ. */
89+
unsigned int hv_msi_get_int_vector(struct irq_data *data);
90+
91+
/* Returns the H/W interrupt vector mapped to the given MSI. */
92+
static inline irq_hw_number_t
93+
hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info,
94+
msi_alloc_info_t *arg)
95+
{
96+
return arg->hwirq;
97+
}
98+
99+
/* Get the IRQ delivery mode. */
100+
static inline u8 hv_msi_irq_delivery_mode(void)
101+
{
102+
return 0;
103+
}
104+
105+
#define hv_msi_prepare NULL
106+
107+
static inline void hv_set_msi_entry_from_desc(union hv_msi_entry *msi_entry,
108+
struct msi_desc *msi_desc)
109+
{
110+
msi_entry->address = ((u64)msi_desc->msg.address_hi << 32) |
111+
msi_desc->msg.address_lo;
112+
msi_entry->data = msi_desc->msg.data;
113+
}
114+
75115
#include <asm-generic/mshyperv.h>
76116

77117
#endif

0 commit comments

Comments
 (0)