vio.c 9.37 KB
Newer Older
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
/* vio.c: Virtual I/O channel devices probing infrastructure.
 *
 *    Copyright (c) 2003-2005 IBM Corp.
 *     Dave Engebretsen engebret@us.ibm.com
 *     Santiago Leon santil@us.ibm.com
 *     Hollis Blanchard <hollisb@us.ibm.com>
 *     Stephen Rothwell
 *
 * Adapted to sparc64 by David S. Miller davem@davemloft.net
 */

#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/init.h>

#include <asm/mdesc.h>
#include <asm/vio.h>

static const struct vio_device_id *vio_match_device(
	const struct vio_device_id *matches,
	const struct vio_dev *dev)
{
	const char *type, *compat;
	int len;

	type = dev->type;
	compat = dev->compat;
	len = dev->compat_len;

	while (matches->type[0] || matches->compat[0]) {
		int match = 1;
32 33 34
		if (matches->type[0])
			match &= !strcmp(matches->type, type);

35
		if (matches->compat[0]) {
36
			match &= len &&
37
				of_find_in_proplist(compat, matches->compat, len);
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
		}
		if (match)
			return matches;
		matches++;
	}
	return NULL;
}

static int vio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct vio_dev *vio_dev = to_vio_dev(dev);
	struct vio_driver *vio_drv = to_vio_driver(drv);
	const struct vio_device_id *matches = vio_drv->id_table;

	if (!matches)
		return 0;

	return vio_match_device(matches, vio_dev) != NULL;
}

static int vio_device_probe(struct device *dev)
{
	struct vio_dev *vdev = to_vio_dev(dev);
	struct vio_driver *drv = to_vio_driver(dev->driver);
	const struct vio_device_id *id;
	int error = -ENODEV;

	if (drv->probe) {
		id = vio_match_device(drv->id_table, vdev);
		if (id)
			error = drv->probe(vdev, id);
	}

	return error;
}

static int vio_device_remove(struct device *dev)
{
	struct vio_dev *vdev = to_vio_dev(dev);
	struct vio_driver *drv = to_vio_driver(dev->driver);

	if (drv->remove)
		return drv->remove(vdev);

	return 1;
}

static ssize_t devspec_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct vio_dev *vdev = to_vio_dev(dev);
	const char *str = "none";

91
	if (!strcmp(vdev->type, "vnet-port"))
92
		str = "vnet";
93
	else if (!strcmp(vdev->type, "vdc-port"))
94
		str = "vdisk";
95 96 97 98

	return sprintf(buf, "%s\n", str);
}

99 100 101 102 103 104 105
static ssize_t type_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct vio_dev *vdev = to_vio_dev(dev);
	return sprintf(buf, "%s\n", vdev->type);
}

106 107
static struct device_attribute vio_dev_attrs[] = {
	__ATTR_RO(devspec),
108
	__ATTR_RO(type),
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 156 157 158 159
	__ATTR_NULL
};

static struct bus_type vio_bus_type = {
	.name		= "vio",
	.dev_attrs	= vio_dev_attrs,
	.match		= vio_bus_match,
	.probe		= vio_device_probe,
	.remove		= vio_device_remove,
};

int vio_register_driver(struct vio_driver *viodrv)
{
	viodrv->driver.bus = &vio_bus_type;

	return driver_register(&viodrv->driver);
}
EXPORT_SYMBOL(vio_register_driver);

void vio_unregister_driver(struct vio_driver *viodrv)
{
	driver_unregister(&viodrv->driver);
}
EXPORT_SYMBOL(vio_unregister_driver);

static void __devinit vio_dev_release(struct device *dev)
{
	kfree(to_vio_dev(dev));
}

static ssize_t
show_pciobppath_attr(struct device *dev, struct device_attribute *attr,
		     char *buf)
{
	struct vio_dev *vdev;
	struct device_node *dp;

	vdev = to_vio_dev(dev);
	dp = vdev->dp;

	return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name);
}

static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH,
		   show_pciobppath_attr, NULL);

struct device_node *cdev_node;

static struct vio_dev *root_vdev;
static u64 cdev_cfg_handle;

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
				  struct vio_dev *vdev)
{
	u64 a;

	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
		const u64 *chan_id;
		const u64 *irq;
		u64 target;

		target = mdesc_arc_target(hp, a);

		irq = mdesc_get_property(hp, target, "tx-ino", NULL);
		if (irq)
			vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);

		irq = mdesc_get_property(hp, target, "rx-ino", NULL);
		if (irq)
			vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);

		chan_id = mdesc_get_property(hp, target, "id", NULL);
		if (chan_id)
			vdev->channel_id = *chan_id;
	}
}

static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
187 188
				      struct device *parent)
{
189
	const char *type, *compat, *bus_id_name;
190 191
	struct device_node *dp;
	struct vio_dev *vdev;
192
	int err, tlen, clen;
193 194
	const u64 *id, *cfg_handle;
	u64 a;
195

196
	type = mdesc_get_property(hp, mp, "device-type", &tlen);
197
	if (!type) {
198 199
		type = mdesc_get_property(hp, mp, "name", &tlen);
		if (!type) {
200
			type = mdesc_node_name(hp, mp);
201 202 203 204 205 206 207
			tlen = strlen(type) + 1;
		}
	}
	if (tlen > VIO_MAX_TYPE_LEN) {
		printk(KERN_ERR "VIO: Type string [%s] is too long.\n",
		       type);
		return NULL;
208
	}
209

210
	id = mdesc_get_property(hp, mp, "id", NULL);
211

212 213 214
	cfg_handle = NULL;
	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
		u64 target;
215

216 217
		target = mdesc_arc_target(hp, a);
		cfg_handle = mdesc_get_property(hp, target,
218
						"cfg-handle", NULL);
219 220 221
		if (cfg_handle)
			break;
	}
222

223 224 225 226 227 228 229 230 231 232
	bus_id_name = type;
	if (!strcmp(type, "domain-services-port"))
		bus_id_name = "ds";

	if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) {
		printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
		       bus_id_name);
		return NULL;
	}

233
	compat = mdesc_get_property(hp, mp, "device-type", &clen);
234 235 236 237 238 239 240
	if (!compat) {
		clen = 0;
	} else if (clen > VIO_MAX_COMPAT_LEN) {
		printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
		       clen, type);
		return NULL;
	}
241 242 243 244 245 246 247 248

	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
	if (!vdev) {
		printk(KERN_ERR "VIO: Could not allocate vio_dev\n");
		return NULL;
	}

	vdev->mp = mp;
249 250 251 252 253
	memcpy(vdev->type, type, tlen);
	if (compat)
		memcpy(vdev->compat, compat, clen);
	else
		memset(vdev->compat, 0, sizeof(vdev->compat));
254 255
	vdev->compat_len = clen;

256 257 258
	vdev->channel_id = ~0UL;
	vdev->tx_irq = ~0;
	vdev->rx_irq = ~0;
259

260
	vio_fill_channel_info(hp, mp, vdev);
261

262
	if (!id) {
263 264
		snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s",
			 bus_id_name);
265
		vdev->dev_no = ~(u64)0;
266
	} else if (!cfg_handle) {
267 268
		snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu",
			 bus_id_name, *id);
269
		vdev->dev_no = *id;
270 271 272 273
	} else {
		snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu-%lu",
			 bus_id_name, *cfg_handle, *id);
		vdev->dev_no = *cfg_handle;
274
	}
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	vdev->dev.parent = parent;
	vdev->dev.bus = &vio_bus_type;
	vdev->dev.release = vio_dev_release;

	if (parent == NULL) {
		dp = cdev_node;
	} else if (to_vio_dev(parent) == root_vdev) {
		dp = of_get_next_child(cdev_node, NULL);
		while (dp) {
			if (!strcmp(dp->type, type))
				break;

			dp = of_get_next_child(cdev_node, dp);
		}
	} else {
		dp = to_vio_dev(parent)->dp;
	}
	vdev->dp = dp;

295 296
	printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id);

297 298 299 300 301 302 303 304 305 306 307 308 309 310
	err = device_register(&vdev->dev);
	if (err) {
		printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
		       vdev->dev.bus_id, err);
		kfree(vdev);
		return NULL;
	}
	if (vdev->dp)
		err = sysfs_create_file(&vdev->dev.kobj,
					&dev_attr_obppath.attr);

	return vdev;
}

311
static void vio_add(struct mdesc_handle *hp, u64 node)
312
{
313
	(void) vio_create_one(hp, node, &root_vdev->dev);
314 315
}

316
static int vio_md_node_match(struct device *dev, void *arg)
317
{
318
	struct vio_dev *vdev = to_vio_dev(dev);
319

320 321
	if (vdev->mp == (u64) arg)
		return 1;
322

323 324
	return 0;
}
325

326 327 328
static void vio_remove(struct mdesc_handle *hp, u64 node)
{
	struct device *dev;
329

330 331 332 333 334 335
	dev = device_find_child(&root_vdev->dev, (void *) node,
				vio_md_node_match);
	if (dev) {
		printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id);

		device_unregister(dev);
336
	}
337 338
}

339 340 341 342 343 344 345 346 347 348 349 350
static struct mdesc_notifier_client vio_device_notifier = {
	.add		= vio_add,
	.remove		= vio_remove,
	.node_name	= "virtual-device-port",
};

static struct mdesc_notifier_client vio_ds_notifier = {
	.add		= vio_add,
	.remove		= vio_remove,
	.node_name	= "domain-services-port",
};

351 352 353 354 355 356
const char *channel_devices_node = "channel-devices";
const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
const char *cfg_handle_prop = "cfg-handle";

static int __init vio_init(void)
{
357
	struct mdesc_handle *hp;
358 359 360
	const char *compat;
	const u64 *cfg_handle;
	int err, len;
361 362
	u64 root;

363 364 365 366 367 368 369
	err = bus_register(&vio_bus_type);
	if (err) {
		printk(KERN_ERR "VIO: Could not register bus type err=%d\n",
		       err);
		return err;
	}

370 371 372
	hp = mdesc_grab();
	if (!hp)
		return 0;
373

374 375
	root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node);
	if (root == MDESC_NODE_NULL) {
376
		printk(KERN_INFO "VIO: No channel-devices MDESC node.\n");
377
		mdesc_release(hp);
378 379 380 381
		return 0;
	}

	cdev_node = of_find_node_by_name(NULL, "channel-devices");
382
	err = -ENODEV;
383 384
	if (!cdev_node) {
		printk(KERN_INFO "VIO: No channel-devices OBP node.\n");
385
		goto out_release;
386 387
	}

388
	compat = mdesc_get_property(hp, root, "compatible", &len);
389 390 391
	if (!compat) {
		printk(KERN_ERR "VIO: Channel devices lacks compatible "
		       "property\n");
392
		goto out_release;
393
	}
394
	if (!of_find_in_proplist(compat, channel_devices_compat, len)) {
395 396
		printk(KERN_ERR "VIO: Channel devices node lacks (%s) "
		       "compat entry.\n", channel_devices_compat);
397
		goto out_release;
398 399
	}

400
	cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL);
401 402 403
	if (!cfg_handle) {
		printk(KERN_ERR "VIO: Channel devices lacks %s property\n",
		       cfg_handle_prop);
404
		goto out_release;
405 406 407 408
	}

	cdev_cfg_handle = *cfg_handle;

409 410 411 412 413 414 415
	root_vdev = vio_create_one(hp, root, NULL);
	err = -ENODEV;
	if (!root_vdev) {
		printk(KERN_ERR "VIO: Coult not create root device.\n");
		goto out_release;
	}

416 417 418
	mdesc_register_notifier(&vio_device_notifier);
	mdesc_register_notifier(&vio_ds_notifier);

419
	mdesc_release(hp);
420

421
	return err;
422 423 424 425

out_release:
	mdesc_release(hp);
	return err;
426 427 428
}

postcore_initcall(vio_init);