summaryrefslogtreecommitdiffstats
path: root/arch/ppc64/kernel/iSeries_vio.c
blob: 6b754b0c83449651256e232ea8073f72a29bed31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * IBM PowerPC iSeries Virtual I/O Infrastructure Support.
 *
 *    Copyright (c) 2005 Stephen Rothwell, IBM Corp.
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */
#include <linux/types.h>
#include <linux/device.h>
#include <linux/init.h>

#include <asm/vio.h>
#include <asm/iommu.h>
#include <asm/abs_addr.h>
#include <asm/page.h>
#include <asm/iSeries/vio.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/HvLpConfig.h>
#include <asm/iSeries/HvCallXm.h>

struct device *iSeries_vio_dev = &vio_bus_device.dev;
EXPORT_SYMBOL(iSeries_vio_dev);

static struct iommu_table veth_iommu_table;
static struct iommu_table vio_iommu_table;

static void __init iommu_vio_init(void)
{
	struct iommu_table *t;
	struct iommu_table_cb cb;
	unsigned long cbp;
	unsigned long itc_entries;

	cb.itc_busno = 255;    /* Bus 255 is the virtual bus */
	cb.itc_virtbus = 0xff; /* Ask for virtual bus */

	cbp = virt_to_abs(&cb);
	HvCallXm_getTceTableParms(cbp);

	itc_entries = cb.itc_size * PAGE_SIZE / sizeof(union tce_entry);
	veth_iommu_table.it_size        = itc_entries / 2;
	veth_iommu_table.it_busno       = cb.itc_busno;
	veth_iommu_table.it_offset      = cb.itc_offset;
	veth_iommu_table.it_index       = cb.itc_index;
	veth_iommu_table.it_type        = TCE_VB;
	veth_iommu_table.it_blocksize	= 1;

	t = iommu_init_table(&veth_iommu_table);

	if (!t)
		printk("Virtual Bus VETH TCE table failed.\n");

	vio_iommu_table.it_size         = itc_entries - veth_iommu_table.it_size;
	vio_iommu_table.it_busno        = cb.itc_busno;
	vio_iommu_table.it_offset       = cb.itc_offset +
					  veth_iommu_table.it_size;
	vio_iommu_table.it_index        = cb.itc_index;
	vio_iommu_table.it_type         = TCE_VB;
	vio_iommu_table.it_blocksize	= 1;

	t = iommu_init_table(&vio_iommu_table);

	if (!t)
		printk("Virtual Bus VIO TCE table failed.\n");
}

/**
 * vio_register_device_iseries: - Register a new iSeries vio device.
 * @voidev:	The device to register.
 */
static struct vio_dev *__init vio_register_device_iseries(char *type,
		uint32_t unit_num)
{
	struct vio_dev *viodev;

	/* allocate a vio_dev for this device */
	viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL);
	if (!viodev)
		return NULL;
	memset(viodev, 0, sizeof(struct vio_dev));

	snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s%d", type, unit_num);

	viodev->name = viodev->dev.bus_id;
	viodev->type = type;
	viodev->unit_address = unit_num;
	viodev->iommu_table = &vio_iommu_table;
	if (vio_register_device(viodev) == NULL) {
		kfree(viodev);
		return NULL;
	}
	return viodev;
}

void __init probe_bus_iseries(void)
{
	HvLpIndexMap vlan_map;
	struct vio_dev *viodev;
	int i;

	/* there is only one of each of these */
	vio_register_device_iseries("viocons", 0);
	vio_register_device_iseries("vscsi", 0);

	vlan_map = HvLpConfig_getVirtualLanIndexMap();
	for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
		if ((vlan_map & (0x8000 >> i)) == 0)
			continue;
		viodev = vio_register_device_iseries("vlan", i);
		/* veth is special and has it own iommu_table */
		viodev->iommu_table = &veth_iommu_table;
	}
	for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++)
		vio_register_device_iseries("viodasd", i);
	for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++)
		vio_register_device_iseries("viocd", i);
	for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++)
		vio_register_device_iseries("viotape", i);
}

/**
 * vio_match_device_iseries: - Tell if a iSeries VIO device matches a
 *	vio_device_id
 */
static int vio_match_device_iseries(const struct vio_device_id *id,
		const struct vio_dev *dev)
{
	return strncmp(dev->type, id->type, strlen(id->type)) == 0;
}

static struct vio_bus_ops vio_bus_ops_iseries = {
	.match = vio_match_device_iseries,
};

/**
 * vio_bus_init_iseries: - Initialize the iSeries virtual IO bus
 */
static int __init vio_bus_init_iseries(void)
{
	int err;

	err = vio_bus_init(&vio_bus_ops_iseries);
	if (err == 0) {
		iommu_vio_init();
		vio_bus_device.iommu_table = &vio_iommu_table;
		iSeries_vio_dev = &vio_bus_device.dev;
		probe_bus_iseries();
	}
	return err;
}

__initcall(vio_bus_init_iseries);