1181643Skmacy/*
2181643Skmacy * Copyright (c) 2006, Cisco Systems, Inc.
3181643Skmacy * All rights reserved.
4181643Skmacy *
5181643Skmacy * Redistribution and use in source and binary forms, with or without
6181643Skmacy * modification, are permitted provided that the following conditions
7181643Skmacy * are met:
8181643Skmacy *
9181643Skmacy * 1. Redistributions of source code must retain the above copyright
10181643Skmacy *    notice, this list of conditions and the following disclaimer.
11181643Skmacy * 2. Redistributions in binary form must reproduce the above copyright
12181643Skmacy *    notice, this list of conditions and the following disclaimer in the
13181643Skmacy *    documentation and/or other materials provided with the distribution.
14181643Skmacy * 3. Neither the name of Cisco Systems, Inc. nor the names of its contributors
15181643Skmacy *    may be used to endorse or promote products derived from this software
16181643Skmacy *    without specific prior written permission.
17181643Skmacy *
18181643Skmacy * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19181643Skmacy * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20181643Skmacy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21181643Skmacy * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22181643Skmacy * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23181643Skmacy * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24181643Skmacy * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25181643Skmacy * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26181643Skmacy * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27181643Skmacy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28181643Skmacy * POSSIBILITY OF SUCH DAMAGE.
29181643Skmacy */
30181643Skmacy
31181643Skmacy#include <sys/cdefs.h>
32181643Skmacy__FBSDID("$FreeBSD$");
33181643Skmacy
34181643Skmacy#include <sys/param.h>
35181643Skmacy#include <sys/module.h>
36181643Skmacy#include <sys/systm.h>
37181643Skmacy#include <sys/mbuf.h>
38181643Skmacy#include <sys/malloc.h>
39181643Skmacy#include <sys/kernel.h>
40181643Skmacy#include <sys/socket.h>
41181643Skmacy#include <sys/queue.h>
42181643Skmacy
43181643Skmacy#include <machine/vmparam.h>
44181643Skmacy#include <vm/vm.h>
45181643Skmacy#include <vm/pmap.h>
46181643Skmacy
47181643Skmacy#include <machine/bus.h>
48181643Skmacy#include <machine/resource.h>
49181643Skmacy#include <machine/frame.h>
50181643Skmacy
51181643Skmacy#include <sys/bus.h>
52181643Skmacy#include <sys/rman.h>
53181643Skmacy
54181643Skmacy#include <machine/intr_machdep.h>
55181643Skmacy
56181643Skmacy#include <machine/xen-os.h>
57181643Skmacy#include <machine/hypervisor.h>
58181643Skmacy#include <machine/hypervisor-ifs.h>
59181643Skmacy#include <machine/xen_intr.h>
60181643Skmacy#include <machine/evtchn.h>
61181643Skmacy#include <machine/xenbus.h>
62181643Skmacy#include <machine/gnttab.h>
63181643Skmacy#include <machine/xen-public/memory.h>
64181643Skmacy#include <machine/xen-public/io/pciif.h>
65181643Skmacy
66181643Skmacy#include <sys/pciio.h>
67181643Skmacy#include <dev/pci/pcivar.h>
68181643Skmacy#include "pcib_if.h"
69181643Skmacy
70181643Skmacy#ifdef XEN_PCIDEV_FE_DEBUG
71181643Skmacy#define DPRINTF(fmt, args...) \
72181643Skmacy    printf("pcifront (%s:%d): " fmt, __FUNCTION__, __LINE__, ##args)
73181643Skmacy#else
74181643Skmacy#define DPRINTF(fmt, args...) ((void)0)
75181643Skmacy#endif
76181643Skmacy#define WPRINTF(fmt, args...) \
77181643Skmacy    printf("pcifront (%s:%d): " fmt, __FUNCTION__, __LINE__, ##args)
78181643Skmacy
79181643Skmacy#define INVALID_GRANT_REF (0)
80181643Skmacy#define INVALID_EVTCHN    (-1)
81181643Skmacy#define virt_to_mfn(x) (vtomach(x) >> PAGE_SHIFT)
82181643Skmacy
83181643Skmacystruct pcifront_device {
84181643Skmacy	STAILQ_ENTRY(pcifront_device) next;
85181643Skmacy
86181643Skmacy	struct xenbus_device *xdev;
87181643Skmacy
88181643Skmacy	int unit;
89181643Skmacy	int evtchn;
90181643Skmacy	int gnt_ref;
91181643Skmacy
92181643Skmacy	/* Lock this when doing any operations in sh_info */
93181643Skmacy	struct mtx sh_info_lock;
94181643Skmacy	struct xen_pci_sharedinfo *sh_info;
95181643Skmacy
96181643Skmacy	device_t ndev;
97181643Skmacy
98181643Skmacy	int ref_cnt;
99181643Skmacy};
100181643Skmacy
101181643Skmacystatic STAILQ_HEAD(pcifront_dlist, pcifront_device) pdev_list = STAILQ_HEAD_INITIALIZER(pdev_list);
102181643Skmacy
103181643Skmacystruct xpcib_softc {
104181643Skmacy	int domain;
105181643Skmacy	int bus;
106181643Skmacy	struct pcifront_device *pdev;
107181643Skmacy};
108181643Skmacy
109181643Skmacy/* Allocate a PCI device structure */
110181643Skmacystatic struct pcifront_device *
111181643Skmacyalloc_pdev(struct xenbus_device *xdev)
112181643Skmacy{
113181643Skmacy	struct pcifront_device *pdev = NULL;
114181643Skmacy	int err, unit;
115181643Skmacy
116181643Skmacy	err = sscanf(xdev->nodename, "device/pci/%d", &unit);
117181643Skmacy	if (err != 1) {
118181643Skmacy		if (err == 0)
119181643Skmacy			err = -EINVAL;
120181643Skmacy		xenbus_dev_fatal(pdev->xdev, err, "Error scanning pci device instance number");
121181643Skmacy		goto out;
122181643Skmacy	}
123181643Skmacy
124181643Skmacy	pdev = (struct pcifront_device *)malloc(sizeof(struct pcifront_device), M_DEVBUF, M_NOWAIT);
125181643Skmacy	if (pdev == NULL) {
126181643Skmacy		err = -ENOMEM;
127181643Skmacy		xenbus_dev_fatal(xdev, err, "Error allocating pcifront_device struct");
128181643Skmacy		goto out;
129181643Skmacy	}
130181643Skmacy	pdev->unit = unit;
131181643Skmacy	pdev->xdev = xdev;
132181643Skmacy	pdev->ref_cnt = 1;
133181643Skmacy
134181643Skmacy	pdev->sh_info = (struct xen_pci_sharedinfo *)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
135181643Skmacy	if (pdev->sh_info == NULL) {
136181643Skmacy		free(pdev, M_DEVBUF);
137181643Skmacy		pdev = NULL;
138181643Skmacy		err = -ENOMEM;
139181643Skmacy		xenbus_dev_fatal(xdev, err, "Error allocating sh_info struct");
140181643Skmacy		goto out;
141181643Skmacy	}
142181643Skmacy	pdev->sh_info->flags = 0;
143181643Skmacy
144181643Skmacy	xdev->data = pdev;
145181643Skmacy
146181643Skmacy	mtx_init(&pdev->sh_info_lock, "info_lock", "pci shared dev info lock", MTX_DEF);
147181643Skmacy
148181643Skmacy	pdev->evtchn = INVALID_EVTCHN;
149181643Skmacy	pdev->gnt_ref = INVALID_GRANT_REF;
150181643Skmacy
151181643Skmacy	STAILQ_INSERT_TAIL(&pdev_list, pdev, next);
152181643Skmacy
153181643Skmacy	DPRINTF("Allocated pdev @ 0x%p (unit=%d)\n", pdev, unit);
154181643Skmacy
155181643Skmacy out:
156181643Skmacy	return pdev;
157181643Skmacy}
158181643Skmacy
159181643Skmacy/* Hold a reference to a pcifront device */
160181643Skmacystatic void
161181643Skmacyget_pdev(struct pcifront_device *pdev)
162181643Skmacy{
163181643Skmacy	pdev->ref_cnt++;
164181643Skmacy}
165181643Skmacy
166181643Skmacy/* Release a reference to a pcifront device */
167181643Skmacystatic void
168181643Skmacyput_pdev(struct pcifront_device *pdev)
169181643Skmacy{
170181643Skmacy	if (--pdev->ref_cnt > 0)
171181643Skmacy		return;
172181643Skmacy
173181643Skmacy	DPRINTF("freeing pdev @ 0x%p (ref_cnt=%d)\n", pdev, pdev->ref_cnt);
174181643Skmacy
175181643Skmacy	if (pdev->evtchn != INVALID_EVTCHN)
176181643Skmacy		xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
177181643Skmacy
178181643Skmacy	if (pdev->gnt_ref != INVALID_GRANT_REF)
179181643Skmacy		gnttab_end_foreign_access(pdev->gnt_ref, 0, (void *)pdev->sh_info);
180181643Skmacy
181181643Skmacy	pdev->xdev->data = NULL;
182181643Skmacy
183181643Skmacy	free(pdev, M_DEVBUF);
184181643Skmacy}
185181643Skmacy
186181643Skmacy
187181643Skmacy/* Write to the xenbus info needed by backend */
188181643Skmacystatic int
189181643Skmacypcifront_publish_info(struct pcifront_device *pdev)
190181643Skmacy{
191181643Skmacy	int err = 0;
192181643Skmacy	struct xenbus_transaction *trans;
193181643Skmacy
194181643Skmacy	err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info));
195181643Skmacy	if (err < 0) {
196181643Skmacy		WPRINTF("error granting access to ring page\n");
197181643Skmacy		goto out;
198181643Skmacy	}
199181643Skmacy
200181643Skmacy	pdev->gnt_ref = err;
201181643Skmacy
202181643Skmacy	err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
203181643Skmacy	if (err)
204181643Skmacy		goto out;
205181643Skmacy
206181643Skmacy do_publish:
207181643Skmacy	trans = xenbus_transaction_start();
208181643Skmacy	if (IS_ERR(trans)) {
209181643Skmacy		xenbus_dev_fatal(pdev->xdev, err,
210181643Skmacy						 "Error writing configuration for backend "
211181643Skmacy						 "(start transaction)");
212181643Skmacy		goto out;
213181643Skmacy	}
214181643Skmacy
215181643Skmacy	err = xenbus_printf(trans, pdev->xdev->nodename,
216181643Skmacy						"pci-op-ref", "%u", pdev->gnt_ref);
217181643Skmacy	if (!err)
218181643Skmacy		err = xenbus_printf(trans, pdev->xdev->nodename,
219181643Skmacy							"event-channel", "%u", pdev->evtchn);
220181643Skmacy	if (!err)
221181643Skmacy		err = xenbus_printf(trans, pdev->xdev->nodename,
222181643Skmacy							"magic", XEN_PCI_MAGIC);
223181643Skmacy	if (!err)
224181643Skmacy		err = xenbus_switch_state(pdev->xdev, trans,
225181643Skmacy								  XenbusStateInitialised);
226181643Skmacy
227181643Skmacy	if (err) {
228181643Skmacy		xenbus_transaction_end(trans, 1);
229181643Skmacy		xenbus_dev_fatal(pdev->xdev, err,
230181643Skmacy						 "Error writing configuration for backend");
231181643Skmacy		goto out;
232181643Skmacy	} else {
233181643Skmacy		err = xenbus_transaction_end(trans, 0);
234181643Skmacy		if (err == -EAGAIN)
235181643Skmacy			goto do_publish;
236181643Skmacy		else if (err) {
237181643Skmacy			xenbus_dev_fatal(pdev->xdev, err,
238181643Skmacy							 "Error completing transaction for backend");
239181643Skmacy			goto out;
240181643Skmacy		}
241181643Skmacy	}
242181643Skmacy
243181643Skmacy out:
244181643Skmacy	return err;
245181643Skmacy}
246181643Skmacy
247181643Skmacy/* The backend is now connected so complete the connection process on our side */
248181643Skmacystatic int
249181643Skmacypcifront_connect(struct pcifront_device *pdev)
250181643Skmacy{
251181643Skmacy	device_t nexus;
252181643Skmacy	devclass_t nexus_devclass;
253181643Skmacy
254181643Skmacy	/* We will add our device as a child of the nexus0 device */
255181643Skmacy	if (!(nexus_devclass = devclass_find("nexus")) ||
256181643Skmacy		!(nexus = devclass_get_device(nexus_devclass, 0))) {
257181643Skmacy		WPRINTF("could not find nexus0!\n");
258181643Skmacy		return -1;
259181643Skmacy	}
260181643Skmacy
261181643Skmacy	/* Create a newbus device representing this frontend instance */
262181643Skmacy	pdev->ndev = BUS_ADD_CHILD(nexus, 0, "xpcife", pdev->unit);
263181643Skmacy	if (!pdev->ndev) {
264181643Skmacy		WPRINTF("could not create xpcife%d!\n", pdev->unit);
265181643Skmacy		return -EFAULT;
266181643Skmacy	}
267181643Skmacy	get_pdev(pdev);
268181643Skmacy	device_set_ivars(pdev->ndev, pdev);
269181643Skmacy
270181643Skmacy	/* Good to go connected now */
271181643Skmacy	xenbus_switch_state(pdev->xdev, NULL, XenbusStateConnected);
272181643Skmacy
273181643Skmacy	printf("pcifront: connected to %s\n", pdev->xdev->nodename);
274181643Skmacy
275181643Skmacy	mtx_lock(&Giant);
276181643Skmacy	device_probe_and_attach(pdev->ndev);
277181643Skmacy	mtx_unlock(&Giant);
278181643Skmacy
279181643Skmacy	return 0;
280181643Skmacy}
281181643Skmacy
282181643Skmacy/* The backend is closing so process a disconnect */
283181643Skmacystatic int
284181643Skmacypcifront_disconnect(struct pcifront_device *pdev)
285181643Skmacy{
286181643Skmacy	int err = 0;
287181643Skmacy	XenbusState prev_state;
288181643Skmacy
289181643Skmacy	prev_state = xenbus_read_driver_state(pdev->xdev->nodename);
290181643Skmacy
291181643Skmacy	if (prev_state < XenbusStateClosing) {
292181643Skmacy		err = xenbus_switch_state(pdev->xdev, NULL, XenbusStateClosing);
293181643Skmacy		if (!err && prev_state == XenbusStateConnected) {
294181643Skmacy			/* TODO - need to detach the newbus devices */
295181643Skmacy		}
296181643Skmacy	}
297181643Skmacy
298181643Skmacy	return err;
299181643Skmacy}
300181643Skmacy
301181643Skmacy/* Process a probe from the xenbus */
302181643Skmacystatic int
303181643Skmacypcifront_probe(struct xenbus_device *xdev,
304181643Skmacy			   const struct xenbus_device_id *id)
305181643Skmacy{
306181643Skmacy	int err = 0;
307181643Skmacy	struct pcifront_device *pdev;
308181643Skmacy
309181643Skmacy	DPRINTF("xenbus probing\n");
310181643Skmacy
311181643Skmacy	if ((pdev = alloc_pdev(xdev)) == NULL)
312181643Skmacy		goto out;
313181643Skmacy
314181643Skmacy	err = pcifront_publish_info(pdev);
315181643Skmacy
316181643Skmacy out:
317181643Skmacy	if (err)
318181643Skmacy		put_pdev(pdev);
319181643Skmacy	return err;
320181643Skmacy}
321181643Skmacy
322181643Skmacy/* Remove the xenbus PCI device */
323181643Skmacystatic int
324181643Skmacypcifront_remove(struct xenbus_device *xdev)
325181643Skmacy{
326181643Skmacy	DPRINTF("removing xenbus device node (%s)\n", xdev->nodename);
327181643Skmacy	if (xdev->data)
328181643Skmacy		put_pdev(xdev->data);
329181643Skmacy	return 0;
330181643Skmacy}
331181643Skmacy
332181643Skmacy/* Called by xenbus when our backend node changes state */
333181643Skmacystatic void
334181643Skmacypcifront_backend_changed(struct xenbus_device *xdev,
335181643Skmacy						 XenbusState be_state)
336181643Skmacy{
337181643Skmacy	struct pcifront_device *pdev = xdev->data;
338181643Skmacy
339181643Skmacy	switch (be_state) {
340181643Skmacy	case XenbusStateClosing:
341181643Skmacy		DPRINTF("backend closing (%s)\n", xdev->nodename);
342181643Skmacy		pcifront_disconnect(pdev);
343181643Skmacy		break;
344181643Skmacy
345181643Skmacy	case XenbusStateClosed:
346181643Skmacy		DPRINTF("backend closed (%s)\n", xdev->nodename);
347181643Skmacy		pcifront_disconnect(pdev);
348181643Skmacy		break;
349181643Skmacy
350181643Skmacy	case XenbusStateConnected:
351181643Skmacy		DPRINTF("backend connected (%s)\n", xdev->nodename);
352181643Skmacy		pcifront_connect(pdev);
353181643Skmacy		break;
354181643Skmacy
355181643Skmacy	default:
356181643Skmacy		break;
357181643Skmacy	}
358181643Skmacy}
359181643Skmacy
360181643Skmacy/* Process PCI operation */
361181643Skmacystatic int
362181643Skmacydo_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op)
363181643Skmacy{
364181643Skmacy	int err = 0;
365181643Skmacy	struct xen_pci_op *active_op = &pdev->sh_info->op;
366181643Skmacy	evtchn_port_t port = pdev->evtchn;
367181643Skmacy	time_t timeout;
368181643Skmacy
369181643Skmacy	mtx_lock(&pdev->sh_info_lock);
370181643Skmacy
371181643Skmacy	memcpy(active_op, op, sizeof(struct xen_pci_op));
372181643Skmacy
373181643Skmacy	/* Go */
374181643Skmacy	wmb();
375181643Skmacy	set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
376181643Skmacy	notify_remote_via_evtchn(port);
377181643Skmacy
378181643Skmacy	timeout = time_uptime + 2;
379181643Skmacy
380181643Skmacy	clear_evtchn(port);
381181643Skmacy
382181643Skmacy	/* Spin while waiting for the answer */
383181643Skmacy	while (test_bit
384181643Skmacy	       (_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags)) {
385181643Skmacy		int err = HYPERVISOR_poll(&port, 1, 3 * hz);
386181643Skmacy		if (err)
387181643Skmacy			panic("Failed HYPERVISOR_poll: err=%d", err);
388181643Skmacy		clear_evtchn(port);
389181643Skmacy		if (time_uptime > timeout) {
390181643Skmacy			WPRINTF("pciback not responding!!!\n");
391181643Skmacy			clear_bit(_XEN_PCIF_active,
392181643Skmacy				  (unsigned long *)&pdev->sh_info->flags);
393181643Skmacy			err = XEN_PCI_ERR_dev_not_found;
394181643Skmacy			goto out;
395181643Skmacy		}
396181643Skmacy	}
397181643Skmacy
398181643Skmacy	memcpy(op, active_op, sizeof(struct xen_pci_op));
399181643Skmacy
400181643Skmacy	err = op->err;
401181643Skmacy out:
402181643Skmacy	mtx_unlock(&pdev->sh_info_lock);
403181643Skmacy	return err;
404181643Skmacy}
405181643Skmacy
406181643Skmacy/* ** XenBus Driver registration ** */
407181643Skmacy
408181643Skmacystatic struct xenbus_device_id pcifront_ids[] = {
409181643Skmacy	{ "pci" },
410181643Skmacy	{ "" }
411181643Skmacy};
412181643Skmacy
413181643Skmacystatic struct xenbus_driver pcifront = {
414181643Skmacy	.name = "pcifront",
415181643Skmacy	.ids = pcifront_ids,
416181643Skmacy	.probe = pcifront_probe,
417181643Skmacy	.remove = pcifront_remove,
418181643Skmacy	.otherend_changed = pcifront_backend_changed,
419181643Skmacy};
420181643Skmacy
421181643Skmacy/* Register the driver with xenbus during sys init */
422181643Skmacystatic void
423181643Skmacypcifront_init(void *unused)
424181643Skmacy{
425181643Skmacy	if ((xen_start_info->flags & SIF_INITDOMAIN))
426181643Skmacy		return;
427181643Skmacy
428181643Skmacy	DPRINTF("xenbus registering\n");
429181643Skmacy
430181643Skmacy	xenbus_register_frontend(&pcifront);
431181643Skmacy}
432181643Skmacy
433181643SkmacySYSINIT(pciif, SI_SUB_PSEUDO, SI_ORDER_ANY, pcifront_init, NULL)
434181643Skmacy
435181643Skmacy
436181643Skmacy/* Newbus xpcife device driver probe */
437181643Skmacystatic int
438181643Skmacyxpcife_probe(device_t dev)
439181643Skmacy{
440181643Skmacy#ifdef XEN_PCIDEV_FE_DEBUG
441181643Skmacy	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(dev);
442181643Skmacy	DPRINTF("xpcife probe (unit=%d)\n", pdev->unit);
443181643Skmacy#endif
444265999Sian	return (BUS_PROBE_NOWILDCARD);
445181643Skmacy}
446181643Skmacy
447181643Skmacy/* Newbus xpcife device driver attach */
448181643Skmacystatic int
449181643Skmacyxpcife_attach(device_t dev)
450181643Skmacy{
451181643Skmacy	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(dev);
452181643Skmacy	int i, num_roots, len, err;
453181643Skmacy	char str[64];
454181643Skmacy	unsigned int domain, bus;
455181643Skmacy
456181643Skmacy	DPRINTF("xpcife attach (unit=%d)\n", pdev->unit);
457181643Skmacy
458181643Skmacy	err = xenbus_scanf(NULL, pdev->xdev->otherend,
459181643Skmacy					   "root_num", "%d", &num_roots);
460181643Skmacy	if (err != 1) {
461181643Skmacy		if (err == 0)
462181643Skmacy			err = -EINVAL;
463181643Skmacy		xenbus_dev_fatal(pdev->xdev, err,
464181643Skmacy						 "Error reading number of PCI roots");
465181643Skmacy		goto out;
466181643Skmacy	}
467181643Skmacy
468181643Skmacy	/* Add a pcib device for each root */
469181643Skmacy	for (i = 0; i < num_roots; i++) {
470181643Skmacy		device_t child;
471181643Skmacy
472181643Skmacy		len = snprintf(str, sizeof(str), "root-%d", i);
473181643Skmacy		if (unlikely(len >= (sizeof(str) - 1))) {
474181643Skmacy			err = -ENOMEM;
475181643Skmacy			goto out;
476181643Skmacy		}
477181643Skmacy
478181643Skmacy		err = xenbus_scanf(NULL, pdev->xdev->otherend, str,
479181643Skmacy						   "%x:%x", &domain, &bus);
480181643Skmacy		if (err != 2) {
481181643Skmacy			if (err >= 0)
482181643Skmacy				err = -EINVAL;
483181643Skmacy			xenbus_dev_fatal(pdev->xdev, err,
484181643Skmacy							 "Error reading PCI root %d", i);
485181643Skmacy			goto out;
486181643Skmacy		}
487181643Skmacy		err = 0;
488181643Skmacy		if (domain != pdev->xdev->otherend_id) {
489181643Skmacy			err = -EINVAL;
490181643Skmacy			xenbus_dev_fatal(pdev->xdev, err,
491181643Skmacy							 "Domain mismatch %d != %d", domain, pdev->xdev->otherend_id);
492181643Skmacy			goto out;
493181643Skmacy		}
494181643Skmacy
495181643Skmacy		child = device_add_child(dev, "pcib", bus);
496181643Skmacy		if (!child) {
497181643Skmacy			err = -ENOMEM;
498181643Skmacy			xenbus_dev_fatal(pdev->xdev, err,
499181643Skmacy							 "Unable to create pcib%d", bus);
500181643Skmacy			goto out;
501181643Skmacy		}
502181643Skmacy	}
503181643Skmacy
504181643Skmacy out:
505181643Skmacy	return bus_generic_attach(dev);
506181643Skmacy}
507181643Skmacy
508181643Skmacystatic devclass_t xpcife_devclass;
509181643Skmacy
510181643Skmacystatic device_method_t xpcife_methods[] = {
511181643Skmacy	/* Device interface */
512181643Skmacy	DEVMETHOD(device_probe, xpcife_probe),
513181643Skmacy	DEVMETHOD(device_attach, xpcife_attach),
514181643Skmacy	DEVMETHOD(device_detach,	bus_generic_detach),
515181643Skmacy	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
516181643Skmacy	DEVMETHOD(device_suspend,	bus_generic_suspend),
517181643Skmacy	DEVMETHOD(device_resume,	bus_generic_resume),
518181643Skmacy    /* Bus interface */
519181643Skmacy    DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
520181643Skmacy    DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
521181643Skmacy    DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
522181643Skmacy    DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
523181643Skmacy    DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
524181643Skmacy    DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
525227843Smarius
526227843Smarius	DEVMETHOD_END
527181643Skmacy};
528181643Skmacy
529181643Skmacystatic driver_t xpcife_driver = {
530181643Skmacy	"xpcife",
531181643Skmacy	xpcife_methods,
532181643Skmacy	0,
533181643Skmacy};
534181643Skmacy
535181643SkmacyDRIVER_MODULE(xpcife, nexus, xpcife_driver, xpcife_devclass, 0, 0);
536181643Skmacy
537181643Skmacy
538181643Skmacy/* Newbus xen pcib device driver probe */
539181643Skmacystatic int
540181643Skmacyxpcib_probe(device_t dev)
541181643Skmacy{
542181643Skmacy	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
543181643Skmacy	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(device_get_parent(dev));
544181643Skmacy
545181643Skmacy	DPRINTF("xpcib probe (bus=%d)\n", device_get_unit(dev));
546181643Skmacy
547181643Skmacy	sc->domain = pdev->xdev->otherend_id;
548181643Skmacy	sc->bus = device_get_unit(dev);
549181643Skmacy	sc->pdev = pdev;
550181643Skmacy
551181643Skmacy	return 0;
552181643Skmacy}
553181643Skmacy
554181643Skmacy/* Newbus xen pcib device driver attach */
555181643Skmacystatic int
556181643Skmacyxpcib_attach(device_t dev)
557181643Skmacy{
558181643Skmacy	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
559181643Skmacy
560181643Skmacy	DPRINTF("xpcib attach (bus=%d)\n", sc->bus);
561181643Skmacy
562181643Skmacy	device_add_child(dev, "pci", sc->bus);
563181643Skmacy	return bus_generic_attach(dev);
564181643Skmacy}
565181643Skmacy
566181643Skmacystatic int
567181643Skmacyxpcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
568181643Skmacy{
569181643Skmacy	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
570181643Skmacy	switch (which) {
571181643Skmacy	case  PCIB_IVAR_BUS:
572181643Skmacy		*result = sc->bus;
573181643Skmacy		return 0;
574181643Skmacy	}
575181643Skmacy	return ENOENT;
576181643Skmacy}
577181643Skmacy
578181643Skmacy/* Return the number of slots supported */
579181643Skmacystatic int
580181643Skmacyxpcib_maxslots(device_t dev)
581181643Skmacy{
582181643Skmacy	return 31;
583181643Skmacy}
584181643Skmacy
585181643Skmacy#define PCI_DEVFN(slot,func)	((((slot) & 0x1f) << 3) | ((func) & 0x07))
586181643Skmacy
587181643Skmacy/* Read configuration space register */
588181643Skmacystatic u_int32_t
589181643Skmacyxpcib_read_config(device_t dev, int bus, int slot, int func,
590181643Skmacy				  int reg, int bytes)
591181643Skmacy{
592181643Skmacy	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
593181643Skmacy	struct xen_pci_op op = {
594181643Skmacy		.cmd    = XEN_PCI_OP_conf_read,
595181643Skmacy		.domain = sc->domain,
596181643Skmacy		.bus    = sc->bus,
597181643Skmacy		.devfn  = PCI_DEVFN(slot, func),
598181643Skmacy		.offset = reg,
599181643Skmacy		.size   = bytes,
600181643Skmacy	};
601181643Skmacy	int err;
602181643Skmacy
603181643Skmacy	err = do_pci_op(sc->pdev, &op);
604181643Skmacy
605181643Skmacy	DPRINTF("read config (b=%d, s=%d, f=%d, reg=%d, len=%d, val=%x, err=%d)\n",
606181643Skmacy			bus, slot, func, reg, bytes, op.value, err);
607181643Skmacy
608181643Skmacy	if (err)
609181643Skmacy		op.value = ~0;
610181643Skmacy
611181643Skmacy	return op.value;
612181643Skmacy}
613181643Skmacy
614181643Skmacy/* Write configuration space register */
615181643Skmacystatic void
616181643Skmacyxpcib_write_config(device_t dev, int bus, int slot, int func,
617181643Skmacy				   int reg, u_int32_t data, int bytes)
618181643Skmacy{
619181643Skmacy	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
620181643Skmacy	struct xen_pci_op op = {
621181643Skmacy		.cmd    = XEN_PCI_OP_conf_write,
622181643Skmacy		.domain = sc->domain,
623181643Skmacy		.bus    = sc->bus,
624181643Skmacy		.devfn  = PCI_DEVFN(slot, func),
625181643Skmacy		.offset = reg,
626181643Skmacy		.size   = bytes,
627181643Skmacy		.value  = data,
628181643Skmacy	};
629181643Skmacy	int err;
630181643Skmacy
631181643Skmacy	err = do_pci_op(sc->pdev, &op);
632181643Skmacy
633181643Skmacy	DPRINTF("write config (b=%d, s=%d, f=%d, reg=%d, len=%d, val=%x, err=%d)\n",
634181643Skmacy			bus, slot, func, reg, bytes, data, err);
635181643Skmacy}
636181643Skmacy
637181643Skmacystatic int
638181643Skmacyxpcib_route_interrupt(device_t pcib, device_t dev, int pin)
639181643Skmacy{
640181643Skmacy	struct pci_devinfo *dinfo = device_get_ivars(dev);
641181643Skmacy	pcicfgregs *cfg = &dinfo->cfg;
642181643Skmacy
643181643Skmacy	DPRINTF("route intr (pin=%d, line=%d)\n", pin, cfg->intline);
644181643Skmacy
645181643Skmacy	return cfg->intline;
646181643Skmacy}
647181643Skmacy
648181643Skmacystatic device_method_t xpcib_methods[] = {
649181643Skmacy    /* Device interface */
650227843Smarius    DEVMETHOD(device_probe,		xpcib_probe),
651181643Skmacy    DEVMETHOD(device_attach,		xpcib_attach),
652181643Skmacy    DEVMETHOD(device_detach,		bus_generic_detach),
653181643Skmacy    DEVMETHOD(device_shutdown,		bus_generic_shutdown),
654181643Skmacy    DEVMETHOD(device_suspend,		bus_generic_suspend),
655181643Skmacy    DEVMETHOD(device_resume,		bus_generic_resume),
656181643Skmacy
657181643Skmacy    /* Bus interface */
658181643Skmacy    DEVMETHOD(bus_read_ivar,		xpcib_read_ivar),
659181643Skmacy    DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
660181643Skmacy    DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
661181643Skmacy    DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
662181643Skmacy    DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
663181643Skmacy    DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
664181643Skmacy    DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
665181643Skmacy
666181643Skmacy    /* pcib interface */
667181643Skmacy    DEVMETHOD(pcib_maxslots,		xpcib_maxslots),
668181643Skmacy    DEVMETHOD(pcib_read_config,		xpcib_read_config),
669181643Skmacy    DEVMETHOD(pcib_write_config,	xpcib_write_config),
670181643Skmacy    DEVMETHOD(pcib_route_interrupt,	xpcib_route_interrupt),
671227843Smarius
672227843Smarius    DEVMETHOD_END
673181643Skmacy};
674181643Skmacy
675181643Skmacystatic devclass_t xpcib_devclass;
676181643Skmacy
677181643SkmacyDEFINE_CLASS_0(pcib, xpcib_driver, xpcib_methods, sizeof(struct xpcib_softc));
678181643SkmacyDRIVER_MODULE(pcib, xpcife, xpcib_driver, xpcib_devclass, 0, 0);
679181643Skmacy
680181643Skmacy/*
681181643Skmacy * Local variables:
682181643Skmacy * mode: C
683181643Skmacy * c-set-style: "BSD"
684181643Skmacy * c-basic-offset: 4
685181643Skmacy * tab-width: 4
686181643Skmacy * indent-tabs-mode: t
687181643Skmacy * End:
688181643Skmacy */
689