📄 shpchp_ctrl.c
字号:
/* * 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. * * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/workqueue.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/wait.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include "shpchp.h"#include "shpchprm.h"static u32 configure_new_device(struct controller *ctrl, struct pci_func *func, u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);static int configure_new_function( struct controller *ctrl, struct pci_func *func, u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);static void interrupt_event_handler(struct controller *ctrl);static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */static int event_finished;static unsigned long pushbutton_pending; /* = 0 */u8 shpchp_disk_irq;u8 shpchp_nic_irq;u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id){ struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; u8 rc = 0; u8 getstatus; struct pci_func *func; struct event_info *taskInfo; /* Attention Button Change */ dbg("shpchp: Attention button interrupt received.\n"); func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); /* This is the structure that tells the worker thread what to do */ taskInfo = &(ctrl->event_queue[ctrl->next_event]); p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; /* * Button pressed - See if need to TAKE ACTION!!! */ info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); taskInfo->event_type = INT_BUTTON_PRESS; if ((p_slot->state == BLINKINGON_STATE) || (p_slot->state == BLINKINGOFF_STATE)) { /* Cancel if we are still blinking; this means that we press the * attention again before the 5 sec. limit expires to cancel hot-add * or hot-remove */ taskInfo->event_type = INT_BUTTON_CANCEL; info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); } else if ((p_slot->state == POWERON_STATE) || (p_slot->state == POWEROFF_STATE)) { /* Ignore if the slot is on power-on or power-off state; this * means that the previous attention button action to hot-add or * hot-remove is undergoing */ taskInfo->event_type = INT_BUTTON_IGNORE; info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); } if (rc) up(&event_semaphore); /* signal event thread that new event is posted */ return 0;}u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id){ struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; u8 rc = 0; u8 getstatus; struct pci_func *func; struct event_info *taskInfo; /* Switch Change */ dbg("shpchp: Switch interrupt received.\n"); func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); /* This is the structure that tells the worker thread * what to do */ taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); dbg("%s: Card present %x Power status %x\n", __FUNCTION__, func->presence_save, func->pwr_save); if (getstatus) { /* * Switch opened */ info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); func->switch_save = 0; taskInfo->event_type = INT_SWITCH_OPEN; if (func->pwr_save && func->presence_save) { taskInfo->event_type = INT_POWER_FAULT; err("Surprise Removal of card\n"); } } else { /* * Switch closed */ info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); func->switch_save = 0x10; taskInfo->event_type = INT_SWITCH_CLOSE; } if (rc) up(&event_semaphore); /* signal event thread that new event is posted */ return rc;}u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id){ struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; u8 rc = 0; /*u8 temp_byte;*/ struct pci_func *func; struct event_info *taskInfo; /* Presence Change */ dbg("shpchp: Presence/Notify input change.\n"); func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); /* This is the structure that tells the worker thread * what to do */ taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); /* * Save the presence state */ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); if (func->presence_save) { /* * Card Present */ info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); taskInfo->event_type = INT_PRESENCE_ON; } else { /* * Not Present */ info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); taskInfo->event_type = INT_PRESENCE_OFF; } if (rc) up(&event_semaphore); /* signal event thread that new event is posted */ return rc;}u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id){ struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; u8 rc = 0; struct pci_func *func; struct event_info *taskInfo; /* Power fault */ dbg("shpchp: Power fault interrupt received.\n"); func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); /* This is the structure that tells the worker thread * what to do */ taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { /* * Power fault Cleared */ info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); func->status = 0x00; taskInfo->event_type = INT_POWER_FAULT_CLEAR; } else { /* * Power fault */ info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); taskInfo->event_type = INT_POWER_FAULT; /* set power fault status for this board */ func->status = 0xFF; info("power fault bit %x set\n", hp_slot); } if (rc) up(&event_semaphore); /* signal event thread that new event is posted */ return rc;}/* * sort_by_size * * Sorts nodes on the list by their length. * Smallest first. * */static int sort_by_size(struct pci_resource **head){ struct pci_resource *current_res; struct pci_resource *next_res; int out_of_order = 1; if (!(*head)) return(1); if (!((*head)->next)) return(0); while (out_of_order) { out_of_order = 0; /* Special case for swapping list head */ if (((*head)->next) && ((*head)->length > (*head)->next->length)) { out_of_order++; current_res = *head; *head = (*head)->next; current_res->next = (*head)->next; (*head)->next = current_res; } current_res = *head; while (current_res->next && current_res->next->next) { if (current_res->next->length > current_res->next->next->length) { out_of_order++; next_res = current_res->next; current_res->next = current_res->next->next; current_res = current_res->next; next_res->next = current_res->next; current_res->next = next_res; } else current_res = current_res->next; } } /* End of out_of_order loop */ return(0);}/* * sort_by_max_size * * Sorts nodes on the list by their length. * Largest first. * */static int sort_by_max_size(struct pci_resource **head){ struct pci_resource *current_res; struct pci_resource *next_res; int out_of_order = 1; if (!(*head)) return(1); if (!((*head)->next)) return(0); while (out_of_order) { out_of_order = 0; /* Special case for swapping list head */ if (((*head)->next) && ((*head)->length < (*head)->next->length)) { out_of_order++; current_res = *head; *head = (*head)->next; current_res->next = (*head)->next; (*head)->next = current_res; } current_res = *head; while (current_res->next && current_res->next->next) { if (current_res->next->length < current_res->next->next->length) { out_of_order++; next_res = current_res->next; current_res->next = current_res->next->next; current_res = current_res->next; next_res->next = current_res->next; current_res->next = next_res; } else current_res = current_res->next; } } /* End of out_of_order loop */ return(0);}/* * do_pre_bridge_resource_split * * Returns zero or one node of resources that aren't in use * */static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment){ struct pci_resource *prevnode = NULL; struct pci_resource *node; struct pci_resource *split_node; u32 rc; u32 temp_dword; dbg("do_pre_bridge_resource_split\n"); if (!(*head) || !(*orig_head)) return(NULL); rc = shpchp_resource_sort_and_combine(head); if (rc) return(NULL); if ((*head)->base != (*orig_head)->base) return(NULL); if ((*head)->length == (*orig_head)->length) return(NULL); /* If we got here, there the bridge requires some of the resource, but * we may be able to split some off of the front */ node = *head; if (node->length & (alignment -1)) { /* This one isn't an aligned length, so we'll make a new entry * and split it up. */ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); if (!split_node) return(NULL); temp_dword = (node->length | (alignment-1)) + 1 - alignment; split_node->base = node->base; split_node->length = temp_dword; node->length -= temp_dword; node->base += split_node->length; /* Put it in the list */ *head = split_node; split_node->next = node; } if (node->length < alignment) { return(NULL); } /* Now unlink it */ if (*head == node) { *head = node->next; node->next = NULL; } else { prevnode = *head; while (prevnode->next != node) prevnode = prevnode->next; prevnode->next = node->next; node->next = NULL; } return(node);}/* * do_bridge_resource_split * * Returns zero or one node of resources that aren't in use * */static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment){ struct pci_resource *prevnode = NULL; struct pci_resource *node; u32 rc; u32 temp_dword; if (!(*head)) return(NULL); rc = shpchp_resource_sort_and_combine(head); if (rc) return(NULL); node = *head; while (node->next) { prevnode = node; node = node->next; kfree(prevnode); } if (node->length < alignment) { kfree(node); return(NULL); } if (node->base & (alignment - 1)) { /* Short circuit if adjusted size is too small */ temp_dword = (node->base | (alignment-1)) + 1; if ((node->length - (temp_dword - node->base)) < alignment) { kfree(node); return(NULL); } node->length -= (temp_dword - node->base); node->base = temp_dword; } if (node->length & (alignment - 1)) { /* There's stuff in use after this node */ kfree(node); return(NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -