📄 cpqphp_ctrl.c
字号:
/* * Compaq 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. * * 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> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/wait.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include "cpqphp.h"static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);static int configure_new_function(struct controller* ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);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 *//* things needed for the long_delay function */static struct semaphore delay_sem;static wait_queue_head_t delay_wait;/* delay is in jiffies to wait for */static void long_delay (int delay){ DECLARE_WAITQUEUE(wait, current); /* only allow 1 customer into the delay queue at once * yes this makes some people wait even longer, but who really cares? * this is for _huge_ delays to make the hardware happy as the * signals bounce around */ down (&delay_sem); init_waitqueue_head (&delay_wait); add_wait_queue(&delay_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(delay); remove_wait_queue(&delay_wait, &wait); set_current_state(TASK_RUNNING); up (&delay_sem);}//FIXME: The following line needs to be somewhere else...#define WRONG_BUS_FREQUENCY 0x07static u8 handle_switch_change(u8 change, struct controller * ctrl){ int hp_slot; u8 rc = 0; u16 temp_word; struct pci_func *func; struct event_info *taskInfo; if (!change) return 0; // Switch Change dbg("cpqsbd: Switch interrupt received.\n"); for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x1L << hp_slot)) { //********************************* // this one changed. //********************************* func = cpqhp_slot_find(ctrl->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++; temp_word = ctrl->ctrl_int_comp >> 16; func->presence_save = (temp_word >> hp_slot) & 0x01; func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { //********************************* // Switch opened //********************************* func->switch_save = 0; taskInfo->event_type = INT_SWITCH_OPEN; } else { //********************************* // Switch closed //********************************* func->switch_save = 0x10; taskInfo->event_type = INT_SWITCH_CLOSE; } } } return rc;}/* * find_slot */static inline struct slot *find_slot (struct controller * ctrl, u8 device){ struct slot *slot; if (!ctrl) return NULL; slot = ctrl->slot; while (slot && (slot->device != device)) { slot = slot->next; } return slot;}static u8 handle_presence_change(u16 change, struct controller * ctrl){ int hp_slot; u8 rc = 0; u8 temp_byte; u16 temp_word; struct pci_func *func; struct event_info *taskInfo; struct slot *p_slot; if (!change) return 0; //********************************* // Presence Change //********************************* dbg("cpqsbd: Presence/Notify input change.\n"); dbg(" Changed bits are 0x%4.4x\n", change ); for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x0101 << hp_slot)) { //********************************* // this one changed. //********************************* func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; p_slot = find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4)); // If the switch closed, must be a button // If not in button mode, nevermind if (func->switch_save && (ctrl->push_button == 1)) { temp_word = ctrl->ctrl_int_comp >> 16; temp_byte = (temp_word >> hp_slot) & 0x01; temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; if (temp_byte != func->presence_save) { //********************************* // button Pressed (doesn't do anything) //********************************* dbg("hp_slot %d button pressed\n", hp_slot); taskInfo->event_type = INT_BUTTON_PRESS; } else { //********************************* // button Released - TAKE ACTION!!!! //********************************* dbg("hp_slot %d button released\n", hp_slot); taskInfo->event_type = INT_BUTTON_RELEASE; // Cancel if we are still blinking if ((p_slot->state == BLINKINGON_STATE) || (p_slot->state == BLINKINGOFF_STATE)) { taskInfo->event_type = INT_BUTTON_CANCEL; dbg("hp_slot %d button cancel\n", hp_slot); } else if ((p_slot->state == POWERON_STATE) || (p_slot->state == POWEROFF_STATE)) { //info(msg_button_ignore, p_slot->number); taskInfo->event_type = INT_BUTTON_IGNORE; dbg("hp_slot %d button ignore\n", hp_slot); } } } else { // Switch is open, assume a presence change // Save the presence state temp_word = ctrl->ctrl_int_comp >> 16; func->presence_save = (temp_word >> hp_slot) & 0x01; func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) || (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) { //********************************* // Present //********************************* taskInfo->event_type = INT_PRESENCE_ON; } else { //********************************* // Not Present //********************************* taskInfo->event_type = INT_PRESENCE_OFF; } } } } return rc;}static u8 handle_power_fault(u8 change, struct controller * ctrl){ int hp_slot; u8 rc = 0; struct pci_func *func; struct event_info *taskInfo; if (!change) return 0; //********************************* // power fault //********************************* info("power fault interrupt\n"); for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x01 << hp_slot)) { //********************************* // this one changed. //********************************* func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; rc++; if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { //********************************* // power fault Cleared //********************************* func->status = 0x00; taskInfo->event_type = INT_POWER_FAULT_CLEAR; } else { //********************************* // power fault //********************************* taskInfo->event_type = INT_POWER_FAULT; if (ctrl->rev < 4) { amber_LED_on (ctrl, hp_slot); green_LED_off (ctrl, hp_slot); set_SOGO (ctrl); // this is a fatal condition, we want to crash the // machine to protect from data corruption // simulated_NMI shouldn't ever return //FIXME //simulated_NMI(hp_slot, ctrl); //The following code causes a software crash just in //case simulated_NMI did return //FIXME //panic(msg_power_fault); } else { // set power fault status for this board func->status = 0xFF; info("power fault bit %x set\n", hp_slot); } } } } 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 = cpqhp_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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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 = cpqhp_resource_sort_and_combine(head); if (rc) return(NULL); node = *head; while (node->next) { prevnode = node; node = node->next; kfree(prevnode);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -