shpchp_core.c 10.0 KB
Newer Older
L
Linus Torvalds 已提交
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
/*
 * Standard Hot Plug Controller Driver
 *
 * Copyright (C) 1995,2001 Compaq Computer Corporation
 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2001 IBM Corp.
 * Copyright (C) 2003-2004 Intel Corporation
 *
 * All rights reserved.
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
26
 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
L
Linus Torvalds 已提交
27 28 29 30 31 32 33
 *
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
34
#include <linux/slab.h>
L
Linus Torvalds 已提交
35 36 37 38
#include <linux/pci.h>
#include "shpchp.h"

/* Global variables */
39 40
bool shpchp_debug;
bool shpchp_poll_mode;
L
Linus Torvalds 已提交
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
int shpchp_poll_time;

#define DRIVER_VERSION	"0.4"
#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
#define DRIVER_DESC	"Standard Hot Plug PCI Controller Driver"

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

module_param(shpchp_debug, bool, 0644);
module_param(shpchp_poll_mode, bool, 0644);
module_param(shpchp_poll_time, int, 0644);
MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");

#define SHPC_MODULE_NAME "shpchp"

static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int enable_slot		(struct hotplug_slot *slot);
static int disable_slot		(struct hotplug_slot *slot);
static int get_power_status	(struct hotplug_slot *slot, u8 *value);
static int get_attention_status	(struct hotplug_slot *slot, u8 *value);
static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);

static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
	.set_attention_status =	set_attention_status,
	.enable_slot =		enable_slot,
	.disable_slot =		disable_slot,
	.get_power_status =	get_power_status,
	.get_attention_status =	get_attention_status,
	.get_latch_status =	get_latch_status,
	.get_adapter_status =	get_adapter_status,
};

/**
 * release_slot - free up the memory used by a slot
 * @hotplug_slot: slot to free
 */
static void release_slot(struct hotplug_slot *hotplug_slot)
{
84
	struct slot *slot = hotplug_slot->private;
L
Linus Torvalds 已提交
85

86
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
87
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95

	kfree(slot->hotplug_slot->info);
	kfree(slot->hotplug_slot);
	kfree(slot);
}

static int init_slots(struct controller *ctrl)
{
96 97 98
	struct slot *slot;
	struct hotplug_slot *hotplug_slot;
	struct hotplug_slot_info *info;
A
Alex Chiang 已提交
99
	char name[SLOT_NAME_SIZE];
100
	int retval;
A
Alex Chiang 已提交
101
	int i;
102 103

	for (i = 0; i < ctrl->num_slots; i++) {
104
		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
105 106
		if (!slot) {
			retval = -ENOMEM;
L
Linus Torvalds 已提交
107
			goto error;
108
		}
L
Linus Torvalds 已提交
109

110
		hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
111 112
		if (!hotplug_slot) {
			retval = -ENOMEM;
L
Linus Torvalds 已提交
113
			goto error_slot;
114
		}
115
		slot->hotplug_slot = hotplug_slot;
L
Linus Torvalds 已提交
116

117
		info = kzalloc(sizeof(*info), GFP_KERNEL);
118 119
		if (!info) {
			retval = -ENOMEM;
L
Linus Torvalds 已提交
120
			goto error_hpslot;
121
		}
122 123 124 125
		hotplug_slot->info = info;

		slot->hp_slot = i;
		slot->ctrl = ctrl;
126
		slot->bus = ctrl->pci_dev->subordinate->number;
127 128
		slot->device = ctrl->slot_device_offset + i;
		slot->hpc_ops = ctrl->hpc_ops;
129
		slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
130 131 132 133 134 135 136 137

		snprintf(name, sizeof(name), "shpchp-%d", slot->number);
		slot->wq = alloc_workqueue(name, 0, 0);
		if (!slot->wq) {
			retval = -ENOMEM;
			goto error_info;
		}

138
		mutex_init(&slot->lock);
139
		INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
L
Linus Torvalds 已提交
140 141

		/* register this slot with the hotplug pci core */
142 143
		hotplug_slot->private = slot;
		hotplug_slot->release = &release_slot;
A
Alex Chiang 已提交
144
		snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
145 146
		hotplug_slot->ops = &shpchp_hotplug_slot_ops;

147 148 149 150 151
 		ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x "
 			 "hp_slot=%x sun=%x slot_device_offset=%x\n",
 			 pci_domain_nr(ctrl->pci_dev->subordinate),
 			 slot->bus, slot->device, slot->hp_slot, slot->number,
 			 ctrl->slot_device_offset);
A
Alex Chiang 已提交
152
		retval = pci_hp_register(slot->hotplug_slot,
A
Alex Chiang 已提交
153
				ctrl->pci_dev->subordinate, slot->device, name);
154
		if (retval) {
155 156
			ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
				 retval);
157
			goto error_slotwq;
L
Linus Torvalds 已提交
158 159
		}

A
Alex Chiang 已提交
160 161 162 163 164
		get_power_status(hotplug_slot, &info->power_status);
		get_attention_status(hotplug_slot, &info->attention_status);
		get_latch_status(hotplug_slot, &info->latch_status);
		get_adapter_status(hotplug_slot, &info->adapter_status);

165
		list_add(&slot->slot_list, &ctrl->slot_list);
L
Linus Torvalds 已提交
166 167 168
	}

	return 0;
169 170
error_slotwq:
	destroy_workqueue(slot->wq);
L
Linus Torvalds 已提交
171
error_info:
172
	kfree(info);
L
Linus Torvalds 已提交
173
error_hpslot:
174
	kfree(hotplug_slot);
L
Linus Torvalds 已提交
175
error_slot:
176
	kfree(slot);
L
Linus Torvalds 已提交
177
error:
178
	return retval;
L
Linus Torvalds 已提交
179 180
}

181
void cleanup_slots(struct controller *ctrl)
L
Linus Torvalds 已提交
182
{
183 184 185
	struct list_head *tmp;
	struct list_head *next;
	struct slot *slot;
L
Linus Torvalds 已提交
186

187 188 189
	list_for_each_safe(tmp, next, &ctrl->slot_list) {
		slot = list_entry(tmp, struct slot, slot_list);
		list_del(&slot->slot_list);
190
		cancel_delayed_work(&slot->work);
191
		destroy_workqueue(slot->wq);
192
		pci_hp_deregister(slot->hotplug_slot);
L
Linus Torvalds 已提交
193 194 195 196 197 198 199 200
	}
}

/*
 * set_attention_status - Turns the Amber LED for a slot on, off or blink
 */
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
{
K
Kenji Kaneshige 已提交
201
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
202

203
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
204
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
205 206 207 208 209 210 211 212 213

	hotplug_slot->info->attention_status = status;
	slot->hpc_ops->set_attention_status(slot, status);

	return 0;
}

static int enable_slot (struct hotplug_slot *hotplug_slot)
{
K
Kenji Kaneshige 已提交
214
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
215

216
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
217
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
218

219
	return shpchp_sysfs_enable_slot(slot);
L
Linus Torvalds 已提交
220 221 222 223
}

static int disable_slot (struct hotplug_slot *hotplug_slot)
{
K
Kenji Kaneshige 已提交
224
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
225

226
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
227
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
228

229
	return shpchp_sysfs_disable_slot(slot);
L
Linus Torvalds 已提交
230 231 232 233
}

static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
K
Kenji Kaneshige 已提交
234
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
235 236
	int retval;

237
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
238
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
239 240 241 242 243 244 245 246 247 248

	retval = slot->hpc_ops->get_power_status(slot, value);
	if (retval < 0)
		*value = hotplug_slot->info->power_status;

	return 0;
}

static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
K
Kenji Kaneshige 已提交
249
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
250 251
	int retval;

252
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
253
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
254 255 256 257 258 259 260 261 262 263

	retval = slot->hpc_ops->get_attention_status(slot, value);
	if (retval < 0)
		*value = hotplug_slot->info->attention_status;

	return 0;
}

static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
K
Kenji Kaneshige 已提交
264
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
265 266
	int retval;

267
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
268
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
269 270 271 272 273 274 275 276 277 278

	retval = slot->hpc_ops->get_latch_status(slot, value);
	if (retval < 0)
		*value = hotplug_slot->info->latch_status;

	return 0;
}

static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
K
Kenji Kaneshige 已提交
279
	struct slot *slot = get_slot(hotplug_slot);
L
Linus Torvalds 已提交
280 281
	int retval;

282
	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
283
		 __func__, slot_name(slot));
L
Linus Torvalds 已提交
284 285 286 287 288 289 290 291

	retval = slot->hpc_ops->get_adapter_status(slot, value);
	if (retval < 0)
		*value = hotplug_slot->info->adapter_status;

	return 0;
}

292 293
static int is_shpc_capable(struct pci_dev *dev)
{
294 295
	if (dev->vendor == PCI_VENDOR_ID_AMD &&
	    dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
296 297 298 299 300 301
		return 1;
	if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
		return 0;
	if (get_hp_hw_control_from_firmware(dev))
		return 0;
	return 1;
302 303
}

L
Linus Torvalds 已提交
304 305 306 307 308
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	int rc;
	struct controller *ctrl;

309 310 311
	if (!is_shpc_capable(pdev))
		return -ENODEV;

312
	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
L
Linus Torvalds 已提交
313
	if (!ctrl) {
314
		dev_err(&pdev->dev, "%s: Out of memory\n", __func__);
L
Linus Torvalds 已提交
315 316
		goto err_out_none;
	}
317
	INIT_LIST_HEAD(&ctrl->slot_list);
L
Linus Torvalds 已提交
318

319
	rc = shpc_init(ctrl, pdev);
L
Linus Torvalds 已提交
320
	if (rc) {
321
		ctrl_dbg(ctrl, "Controller initialization failed\n");
L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329
		goto err_out_free_ctrl;
	}

	pci_set_drvdata(pdev, ctrl);

	/* Setup the slot information structures */
	rc = init_slots(ctrl);
	if (rc) {
330
		ctrl_err(ctrl, "Slot initialization failed\n");
331
		goto err_out_release_ctlr;
L
Linus Torvalds 已提交
332 333
	}

334 335 336
	rc = shpchp_create_ctrl_files(ctrl);
	if (rc)
		goto err_cleanup_slots;
L
Linus Torvalds 已提交
337 338 339

	return 0;

340 341
err_cleanup_slots:
	cleanup_slots(ctrl);
342
err_out_release_ctlr:
L
Linus Torvalds 已提交
343 344 345 346 347 348 349
	ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
	kfree(ctrl);
err_out_none:
	return -ENODEV;
}

350
static void shpc_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
351
{
352
	struct controller *ctrl = pci_get_drvdata(dev);
L
Linus Torvalds 已提交
353

354 355 356
	shpchp_remove_ctrl_files(ctrl);
	ctrl->hpc_ops->release_ctlr(ctrl);
	kfree(ctrl);
L
Linus Torvalds 已提交
357 358 359
}

static struct pci_device_id shpcd_pci_tbl[] = {
360
	{PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)},
L
Linus Torvalds 已提交
361 362 363 364 365 366 367 368
	{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);

static struct pci_driver shpc_driver = {
	.name =		SHPC_MODULE_NAME,
	.id_table =	shpcd_pci_tbl,
	.probe =	shpc_probe,
369
	.remove =	shpc_remove,
L
Linus Torvalds 已提交
370 371 372 373
};

static int __init shpcd_init(void)
{
374
	int retval;
T
Tejun Heo 已提交
375

376
	retval = pci_register_driver(&shpc_driver);
377
	dbg("%s: pci_register_driver = %d\n", __func__, retval);
378
	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
379

L
Linus Torvalds 已提交
380 381 382 383 384 385 386 387 388 389 390 391
	return retval;
}

static void __exit shpcd_cleanup(void)
{
	dbg("unload_shpchpd()\n");
	pci_unregister_driver(&shpc_driver);
	info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}

module_init(shpcd_init);
module_exit(shpcd_cleanup);