brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · e6c6f23 Raw
705 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Standard Hot Plug Controller Driver4 *5 * Copyright (C) 1995,2001 Compaq Computer Corporation6 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)7 * Copyright (C) 2001 IBM Corp.8 * Copyright (C) 2003-2004 Intel Corporation9 *10 * All rights reserved.11 *12 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>13 *14 */15 16#include <linux/module.h>17#include <linux/kernel.h>18#include <linux/types.h>19#include <linux/slab.h>20#include <linux/pci.h>21#include "../pci.h"22#include "shpchp.h"23 24static void interrupt_event_handler(struct work_struct *work);25static int shpchp_enable_slot(struct slot *p_slot);26static int shpchp_disable_slot(struct slot *p_slot);27 28static int queue_interrupt_event(struct slot *p_slot, u32 event_type)29{30	struct event_info *info;31 32	info = kmalloc(sizeof(*info), GFP_ATOMIC);33	if (!info)34		return -ENOMEM;35 36	info->event_type = event_type;37	info->p_slot = p_slot;38	INIT_WORK(&info->work, interrupt_event_handler);39 40	queue_work(p_slot->wq, &info->work);41 42	return 0;43}44 45u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)46{47	struct slot *p_slot;48	u32 event_type;49 50	/* Attention Button Change */51	ctrl_dbg(ctrl, "Attention button interrupt received\n");52 53	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);54	shpchp_get_adapter_status(p_slot, &p_slot->presence_save);55 56	/*57	 *  Button pressed - See if need to TAKE ACTION!!!58	 */59	ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));60	event_type = INT_BUTTON_PRESS;61 62	queue_interrupt_event(p_slot, event_type);63 64	return 0;65 66}67 68u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl)69{70	struct slot *p_slot;71	u8 getstatus;72	u32 event_type;73 74	/* Switch Change */75	ctrl_dbg(ctrl, "Switch interrupt received\n");76 77	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);78	shpchp_get_adapter_status(p_slot, &p_slot->presence_save);79	shpchp_get_latch_status(p_slot, &getstatus);80	ctrl_dbg(ctrl, "Card present %x Power status %x\n",81		 p_slot->presence_save, p_slot->pwr_save);82 83	if (getstatus) {84		/*85		 * Switch opened86		 */87		ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));88		event_type = INT_SWITCH_OPEN;89		if (p_slot->pwr_save && p_slot->presence_save) {90			event_type = INT_POWER_FAULT;91			ctrl_err(ctrl, "Surprise Removal of card\n");92		}93	} else {94		/*95		 *  Switch closed96		 */97		ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));98		event_type = INT_SWITCH_CLOSE;99	}100 101	queue_interrupt_event(p_slot, event_type);102 103	return 1;104}105 106u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)107{108	struct slot *p_slot;109	u32 event_type;110 111	/* Presence Change */112	ctrl_dbg(ctrl, "Presence/Notify input change\n");113 114	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);115 116	/*117	 * Save the presence state118	 */119	shpchp_get_adapter_status(p_slot, &p_slot->presence_save);120	if (p_slot->presence_save) {121		/*122		 * Card Present123		 */124		ctrl_info(ctrl, "Card present on Slot(%s)\n",125			  slot_name(p_slot));126		event_type = INT_PRESENCE_ON;127	} else {128		/*129		 * Not Present130		 */131		ctrl_info(ctrl, "Card not present on Slot(%s)\n",132			  slot_name(p_slot));133		event_type = INT_PRESENCE_OFF;134	}135 136	queue_interrupt_event(p_slot, event_type);137 138	return 1;139}140 141u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)142{143	struct slot *p_slot;144	u32 event_type;145 146	/* Power fault */147	ctrl_dbg(ctrl, "Power fault interrupt received\n");148 149	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);150 151	if (!(shpchp_query_power_fault(p_slot))) {152		/*153		 * Power fault Cleared154		 */155		ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",156			  slot_name(p_slot));157		p_slot->status = 0x00;158		event_type = INT_POWER_FAULT_CLEAR;159	} else {160		/*161		 *   Power fault162		 */163		ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));164		event_type = INT_POWER_FAULT;165		/* set power fault status for this board */166		p_slot->status = 0xFF;167		ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot);168	}169 170	queue_interrupt_event(p_slot, event_type);171 172	return 1;173}174 175/* The following routines constitute the bulk of the176   hotplug controller logic177 */178static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,179		enum pci_bus_speed speed)180{181	int rc = 0;182 183	ctrl_dbg(ctrl, "Change speed to %d\n", speed);184	rc = shpchp_set_bus_speed_mode(p_slot, speed);185	if (rc) {186		ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",187			 __func__);188		return WRONG_BUS_FREQUENCY;189	}190	return rc;191}192 193static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,194		u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp,195		enum pci_bus_speed msp)196{197	int rc = 0;198 199	/*200	 * If other slots on the same bus are occupied, we cannot201	 * change the bus speed.202	 */203	if (flag) {204		if (asp < bsp) {205			ctrl_err(ctrl, "Speed of bus %x and adapter %x mismatch\n",206				 bsp, asp);207			rc = WRONG_BUS_FREQUENCY;208		}209		return rc;210	}211 212	if (asp < msp) {213		if (bsp != asp)214			rc = change_bus_speed(ctrl, pslot, asp);215	} else {216		if (bsp != msp)217			rc = change_bus_speed(ctrl, pslot, msp);218	}219	return rc;220}221 222/**223 * board_added - Called after a board has been added to the system.224 * @p_slot: target &slot225 *226 * Turns power on for the board.227 * Configures board.228 */229static int board_added(struct slot *p_slot)230{231	u8 hp_slot;232	u8 slots_not_empty = 0;233	int rc = 0;234	enum pci_bus_speed asp, bsp, msp;235	struct controller *ctrl = p_slot->ctrl;236	struct pci_bus *parent = ctrl->pci_dev->subordinate;237 238	hp_slot = p_slot->device - ctrl->slot_device_offset;239 240	ctrl_dbg(ctrl, "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",241		 __func__, p_slot->device, ctrl->slot_device_offset, hp_slot);242 243	/* Power on slot without connecting to bus */244	rc = shpchp_power_on_slot(p_slot);245	if (rc) {246		ctrl_err(ctrl, "Failed to power on slot\n");247		return -1;248	}249 250	if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {251		rc = shpchp_set_bus_speed_mode(p_slot, PCI_SPEED_33MHz);252		if (rc) {253			ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",254				 __func__);255			return WRONG_BUS_FREQUENCY;256		}257 258		/* turn on board, blink green LED, turn off Amber LED */259		rc = shpchp_slot_enable(p_slot);260		if (rc) {261			ctrl_err(ctrl, "Issue of Slot Enable command failed\n");262			return rc;263		}264	}265 266	rc = shpchp_get_adapter_speed(p_slot, &asp);267	if (rc) {268		ctrl_err(ctrl, "Can't get adapter speed or bus mode mismatch\n");269		return WRONG_BUS_FREQUENCY;270	}271 272	bsp = ctrl->pci_dev->subordinate->cur_bus_speed;273	msp = ctrl->pci_dev->subordinate->max_bus_speed;274 275	/* Check if there are other slots or devices on the same bus */276	if (!list_empty(&ctrl->pci_dev->subordinate->devices))277		slots_not_empty = 1;278 279	ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, max_bus_speed %d\n",280		 __func__, slots_not_empty, asp,281		 bsp, msp);282 283	rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);284	if (rc)285		return rc;286 287	/* turn on board, blink green LED, turn off Amber LED */288	rc = shpchp_slot_enable(p_slot);289	if (rc) {290		ctrl_err(ctrl, "Issue of Slot Enable command failed\n");291		return rc;292	}293 294	/* Wait for ~1 second */295	msleep(1000);296 297	ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status);298	/* Check for a power fault */299	if (p_slot->status == 0xFF) {300		/* power fault occurred, but it was benign */301		ctrl_dbg(ctrl, "%s: Power fault\n", __func__);302		p_slot->status = 0;303		goto err_exit;304	}305 306	if (shpchp_configure_device(p_slot)) {307		ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n",308			 pci_domain_nr(parent), p_slot->bus, p_slot->device);309		goto err_exit;310	}311 312	p_slot->status = 0;313	p_slot->is_a_board = 0x01;314	p_slot->pwr_save = 1;315 316	shpchp_green_led_on(p_slot);317 318	return 0;319 320err_exit:321	/* turn off slot, turn on Amber LED, turn off Green LED */322	rc = shpchp_slot_disable(p_slot);323	if (rc) {324		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",325			 __func__);326		return rc;327	}328 329	return(rc);330}331 332 333/**334 * remove_board - Turns off slot and LEDs335 * @p_slot: target &slot336 */337static int remove_board(struct slot *p_slot)338{339	struct controller *ctrl = p_slot->ctrl;340	u8 hp_slot;341	int rc;342 343	shpchp_unconfigure_device(p_slot);344 345	hp_slot = p_slot->device - ctrl->slot_device_offset;346	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);347 348	ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot);349 350	/* Change status to shutdown */351	if (p_slot->is_a_board)352		p_slot->status = 0x01;353 354	/* turn off slot, turn on Amber LED, turn off Green LED */355	rc = shpchp_slot_disable(p_slot);356	if (rc) {357		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",358			 __func__);359		return rc;360	}361 362	rc = shpchp_set_attention_status(p_slot, 0);363	if (rc) {364		ctrl_err(ctrl, "Issue of Set Attention command failed\n");365		return rc;366	}367 368	p_slot->pwr_save = 0;369	p_slot->is_a_board = 0;370 371	return 0;372}373 374 375struct pushbutton_work_info {376	struct slot *p_slot;377	struct work_struct work;378};379 380/**381 * shpchp_pushbutton_thread - handle pushbutton events382 * @work: &struct work_struct to be handled383 *384 * Scheduled procedure to handle blocking stuff for the pushbuttons.385 * Handles all pending events and exits.386 */387static void shpchp_pushbutton_thread(struct work_struct *work)388{389	struct pushbutton_work_info *info =390		container_of(work, struct pushbutton_work_info, work);391	struct slot *p_slot = info->p_slot;392 393	mutex_lock(&p_slot->lock);394	switch (p_slot->state) {395	case POWEROFF_STATE:396		mutex_unlock(&p_slot->lock);397		shpchp_disable_slot(p_slot);398		mutex_lock(&p_slot->lock);399		p_slot->state = STATIC_STATE;400		break;401	case POWERON_STATE:402		mutex_unlock(&p_slot->lock);403		if (shpchp_enable_slot(p_slot))404			shpchp_green_led_off(p_slot);405		mutex_lock(&p_slot->lock);406		p_slot->state = STATIC_STATE;407		break;408	default:409		break;410	}411	mutex_unlock(&p_slot->lock);412 413	kfree(info);414}415 416void shpchp_queue_pushbutton_work(struct work_struct *work)417{418	struct slot *p_slot = container_of(work, struct slot, work.work);419	struct pushbutton_work_info *info;420 421	info = kmalloc(sizeof(*info), GFP_KERNEL);422	if (!info) {423		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",424			 __func__);425		return;426	}427	info->p_slot = p_slot;428	INIT_WORK(&info->work, shpchp_pushbutton_thread);429 430	mutex_lock(&p_slot->lock);431	switch (p_slot->state) {432	case BLINKINGOFF_STATE:433		p_slot->state = POWEROFF_STATE;434		break;435	case BLINKINGON_STATE:436		p_slot->state = POWERON_STATE;437		break;438	default:439		kfree(info);440		goto out;441	}442	queue_work(p_slot->wq, &info->work);443 out:444	mutex_unlock(&p_slot->lock);445}446 447static void update_slot_info(struct slot *slot)448{449	shpchp_get_power_status(slot, &slot->pwr_save);450	shpchp_get_attention_status(slot, &slot->attention_save);451	shpchp_get_latch_status(slot, &slot->latch_save);452	shpchp_get_adapter_status(slot, &slot->presence_save);453}454 455/*456 * Note: This function must be called with slot->lock held457 */458static void handle_button_press_event(struct slot *p_slot)459{460	u8 getstatus;461	struct controller *ctrl = p_slot->ctrl;462 463	switch (p_slot->state) {464	case STATIC_STATE:465		shpchp_get_power_status(p_slot, &getstatus);466		if (getstatus) {467			p_slot->state = BLINKINGOFF_STATE;468			ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",469				  slot_name(p_slot));470		} else {471			p_slot->state = BLINKINGON_STATE;472			ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",473				  slot_name(p_slot));474		}475		/* blink green LED and turn off amber */476		shpchp_green_led_blink(p_slot);477		shpchp_set_attention_status(p_slot, 0);478 479		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);480		break;481	case BLINKINGOFF_STATE:482	case BLINKINGON_STATE:483		/*484		 * Cancel if we are still blinking; this means that we485		 * press the attention again before the 5 sec. limit486		 * expires to cancel hot-add or hot-remove487		 */488		ctrl_info(ctrl, "Button cancel on Slot(%s)\n",489			  slot_name(p_slot));490		cancel_delayed_work(&p_slot->work);491		if (p_slot->state == BLINKINGOFF_STATE)492			shpchp_green_led_on(p_slot);493		else494			shpchp_green_led_off(p_slot);495		shpchp_set_attention_status(p_slot, 0);496		ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",497			  slot_name(p_slot));498		p_slot->state = STATIC_STATE;499		break;500	case POWEROFF_STATE:501	case POWERON_STATE:502		/*503		 * Ignore if the slot is on power-on or power-off state;504		 * this means that the previous attention button action505		 * to hot-add or hot-remove is undergoing506		 */507		ctrl_info(ctrl, "Button ignore on Slot(%s)\n",508			  slot_name(p_slot));509		update_slot_info(p_slot);510		break;511	default:512		ctrl_warn(ctrl, "Not a valid state\n");513		break;514	}515}516 517static void interrupt_event_handler(struct work_struct *work)518{519	struct event_info *info = container_of(work, struct event_info, work);520	struct slot *p_slot = info->p_slot;521 522	mutex_lock(&p_slot->lock);523	switch (info->event_type) {524	case INT_BUTTON_PRESS:525		handle_button_press_event(p_slot);526		break;527	case INT_POWER_FAULT:528		ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__);529		shpchp_set_attention_status(p_slot, 1);530		shpchp_green_led_off(p_slot);531		break;532	default:533		update_slot_info(p_slot);534		break;535	}536	mutex_unlock(&p_slot->lock);537 538	kfree(info);539}540 541 542static int shpchp_enable_slot (struct slot *p_slot)543{544	u8 getstatus = 0;545	int rc, retval = -ENODEV;546	struct controller *ctrl = p_slot->ctrl;547 548	/* Check to see if (latch closed, card present, power off) */549	mutex_lock(&p_slot->ctrl->crit_sect);550	rc = shpchp_get_adapter_status(p_slot, &getstatus);551	if (rc || !getstatus) {552		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));553		goto out;554	}555	rc = shpchp_get_latch_status(p_slot, &getstatus);556	if (rc || getstatus) {557		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));558		goto out;559	}560	rc = shpchp_get_power_status(p_slot, &getstatus);561	if (rc || getstatus) {562		ctrl_info(ctrl, "Already enabled on slot(%s)\n",563			  slot_name(p_slot));564		goto out;565	}566 567	p_slot->is_a_board = 1;568 569	/* We have to save the presence info for these slots */570	shpchp_get_adapter_status(p_slot, &p_slot->presence_save);571	shpchp_get_power_status(p_slot, &p_slot->pwr_save);572	ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save);573	shpchp_get_latch_status(p_slot, &getstatus);574 575	if ((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD &&576	     p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)577	     && p_slot->ctrl->num_slots == 1) {578		/* handle AMD POGO errata; this must be done before enable  */579		amd_pogo_errata_save_misc_reg(p_slot);580		retval = board_added(p_slot);581		/* handle AMD POGO errata; this must be done after enable  */582		amd_pogo_errata_restore_misc_reg(p_slot);583	} else584		retval = board_added(p_slot);585 586	if (retval) {587		shpchp_get_adapter_status(p_slot, &p_slot->presence_save);588		shpchp_get_latch_status(p_slot, &getstatus);589	}590 591	update_slot_info(p_slot);592 out:593	mutex_unlock(&p_slot->ctrl->crit_sect);594	return retval;595}596 597 598static int shpchp_disable_slot (struct slot *p_slot)599{600	u8 getstatus = 0;601	int rc, retval = -ENODEV;602	struct controller *ctrl = p_slot->ctrl;603 604	if (!p_slot->ctrl)605		return -ENODEV;606 607	/* Check to see if (latch closed, card present, power on) */608	mutex_lock(&p_slot->ctrl->crit_sect);609 610	rc = shpchp_get_adapter_status(p_slot, &getstatus);611	if (rc || !getstatus) {612		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));613		goto out;614	}615	rc = shpchp_get_latch_status(p_slot, &getstatus);616	if (rc || getstatus) {617		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));618		goto out;619	}620	rc = shpchp_get_power_status(p_slot, &getstatus);621	if (rc || !getstatus) {622		ctrl_info(ctrl, "Already disabled on slot(%s)\n",623			  slot_name(p_slot));624		goto out;625	}626 627	retval = remove_board(p_slot);628	update_slot_info(p_slot);629 out:630	mutex_unlock(&p_slot->ctrl->crit_sect);631	return retval;632}633 634int shpchp_sysfs_enable_slot(struct slot *p_slot)635{636	int retval = -ENODEV;637	struct controller *ctrl = p_slot->ctrl;638 639	mutex_lock(&p_slot->lock);640	switch (p_slot->state) {641	case BLINKINGON_STATE:642		cancel_delayed_work(&p_slot->work);643		fallthrough;644	case STATIC_STATE:645		p_slot->state = POWERON_STATE;646		mutex_unlock(&p_slot->lock);647		retval = shpchp_enable_slot(p_slot);648		mutex_lock(&p_slot->lock);649		p_slot->state = STATIC_STATE;650		break;651	case POWERON_STATE:652		ctrl_info(ctrl, "Slot %s is already in powering on state\n",653			  slot_name(p_slot));654		break;655	case BLINKINGOFF_STATE:656	case POWEROFF_STATE:657		ctrl_info(ctrl, "Already enabled on slot %s\n",658			  slot_name(p_slot));659		break;660	default:661		ctrl_err(ctrl, "Not a valid state on slot %s\n",662			 slot_name(p_slot));663		break;664	}665	mutex_unlock(&p_slot->lock);666 667	return retval;668}669 670int shpchp_sysfs_disable_slot(struct slot *p_slot)671{672	int retval = -ENODEV;673	struct controller *ctrl = p_slot->ctrl;674 675	mutex_lock(&p_slot->lock);676	switch (p_slot->state) {677	case BLINKINGOFF_STATE:678		cancel_delayed_work(&p_slot->work);679		fallthrough;680	case STATIC_STATE:681		p_slot->state = POWEROFF_STATE;682		mutex_unlock(&p_slot->lock);683		retval = shpchp_disable_slot(p_slot);684		mutex_lock(&p_slot->lock);685		p_slot->state = STATIC_STATE;686		break;687	case POWEROFF_STATE:688		ctrl_info(ctrl, "Slot %s is already in powering off state\n",689			  slot_name(p_slot));690		break;691	case BLINKINGON_STATE:692	case POWERON_STATE:693		ctrl_info(ctrl, "Already disabled on slot %s\n",694			  slot_name(p_slot));695		break;696	default:697		ctrl_err(ctrl, "Not a valid state on slot %s\n",698			 slot_name(p_slot));699		break;700	}701	mutex_unlock(&p_slot->lock);702 703	return retval;704}705