📄 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>, <kristen.c.accardi@intel.com> * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/workqueue.h>#include "../pci.h"#include "shpchp.h"static void interrupt_event_handler(struct work_struct *work);static int shpchp_enable_slot(struct slot *p_slot);static int shpchp_disable_slot(struct slot *p_slot);static int queue_interrupt_event(struct slot *p_slot, u32 event_type){ struct event_info *info; info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) return -ENOMEM; info->event_type = event_type; info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); schedule_work(&info->work); return 0;}u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl){ struct slot *p_slot; u32 event_type; /* Attention Button Change */ dbg("shpchp: Attention button interrupt received.\n"); p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); /* * Button pressed - See if need to TAKE ACTION!!! */ info("Button pressed on Slot(%s)\n", slot_name(p_slot)); event_type = INT_BUTTON_PRESS; queue_interrupt_event(p_slot, event_type); return 0;}u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl){ struct slot *p_slot; u8 getstatus; u32 event_type; /* Switch Change */ dbg("shpchp: Switch interrupt received.\n"); p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); dbg("%s: Card present %x Power status %x\n", __func__, p_slot->presence_save, p_slot->pwr_save); if (getstatus) { /* * Switch opened */ info("Latch open on Slot(%s)\n", slot_name(p_slot)); event_type = INT_SWITCH_OPEN; if (p_slot->pwr_save && p_slot->presence_save) { event_type = INT_POWER_FAULT; err("Surprise Removal of card\n"); } } else { /* * Switch closed */ info("Latch close on Slot(%s)\n", slot_name(p_slot)); event_type = INT_SWITCH_CLOSE; } queue_interrupt_event(p_slot, event_type); return 1;}u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl){ struct slot *p_slot; u32 event_type; /* Presence Change */ dbg("shpchp: Presence/Notify input change.\n"); 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, &(p_slot->presence_save)); if (p_slot->presence_save) { /* * Card Present */ info("Card present on Slot(%s)\n", slot_name(p_slot)); event_type = INT_PRESENCE_ON; } else { /* * Not Present */ info("Card not present on Slot(%s)\n", slot_name(p_slot)); event_type = INT_PRESENCE_OFF; } queue_interrupt_event(p_slot, event_type); return 1;}u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl){ struct slot *p_slot; u32 event_type; /* Power fault */ dbg("shpchp: Power fault interrupt received.\n"); 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(%s)\n", slot_name(p_slot)); p_slot->status = 0x00; event_type = INT_POWER_FAULT_CLEAR; } else { /* * Power fault */ info("Power fault on Slot(%s)\n", slot_name(p_slot)); event_type = INT_POWER_FAULT; /* set power fault status for this board */ p_slot->status = 0xFF; info("power fault bit %x set\n", hp_slot); } queue_interrupt_event(p_slot, event_type); return 1;}/* The following routines constitute the bulk of the hotplug controller logic */static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, enum pci_bus_speed speed){ int rc = 0; dbg("%s: change to speed %d\n", __func__, speed); if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { err("%s: Issue of set bus speed mode command failed\n", __func__); return WRONG_BUS_FREQUENCY; } return rc;}static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, enum pci_bus_speed msp){ int rc = 0; /* * If other slots on the same bus are occupied, we cannot * change the bus speed. */ if (flag) { if (asp < bsp) { err("%s: speed of bus %x and adapter %x mismatch\n", __func__, bsp, asp); rc = WRONG_BUS_FREQUENCY; } return rc; } if (asp < msp) { if (bsp != asp) rc = change_bus_speed(ctrl, pslot, asp); } else { if (bsp != msp) rc = change_bus_speed(ctrl, pslot, msp); } return rc;}/** * board_added - Called after a board has been added to the system. * @p_slot: target &slot * * Turns power on for the board. * Configures board. */static int board_added(struct slot *p_slot){ u8 hp_slot; u8 slots_not_empty = 0; int rc = 0; enum pci_bus_speed asp, bsp, msp; struct controller *ctrl = p_slot->ctrl; hp_slot = p_slot->device - ctrl->slot_device_offset; dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", __func__, p_slot->device, ctrl->slot_device_offset, hp_slot); /* Power on slot without connecting to bus */ rc = p_slot->hpc_ops->power_on_slot(p_slot); if (rc) { err("%s: Failed to power on slot\n", __func__); return -1; } if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { if (slots_not_empty) return WRONG_BUS_FREQUENCY; if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { err("%s: Issue of set bus speed mode command failed\n", __func__); return WRONG_BUS_FREQUENCY; } /* turn on board, blink green LED, turn off Amber LED */ if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { err("%s: Issue of Slot Enable command failed\n", __func__); return rc; } } rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp); if (rc) { err("%s: Can't get adapter speed or bus mode mismatch\n", __func__); return WRONG_BUS_FREQUENCY; } rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp); if (rc) { err("%s: Can't get bus operation speed\n", __func__); return WRONG_BUS_FREQUENCY; } rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp); if (rc) { err("%s: Can't get max bus operation speed\n", __func__); msp = bsp; } /* Check if there are other slots or devices on the same bus */ if (!list_empty(&ctrl->pci_dev->subordinate->devices)) slots_not_empty = 1; dbg("%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, " "max_bus_speed %d\n", __func__, slots_not_empty, asp, bsp, msp); rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp); if (rc) return rc; /* turn on board, blink green LED, turn off Amber LED */ if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { err("%s: Issue of Slot Enable command failed\n", __func__); return rc; } /* Wait for ~1 second */ msleep(1000); dbg("%s: slot status = %x\n", __func__, p_slot->status); /* Check for a power fault */ if (p_slot->status == 0xFF) { /* power fault occurred, but it was benign */ dbg("%s: power fault\n", __func__); rc = POWER_FAILURE; p_slot->status = 0; goto err_exit; } if (shpchp_configure_device(p_slot)) { err("Cannot add device at 0x%x:0x%x\n", p_slot->bus, p_slot->device); goto err_exit; } p_slot->status = 0; p_slot->is_a_board = 0x01; p_slot->pwr_save = 1; p_slot->hpc_ops->green_led_on(p_slot); return 0;err_exit: /* turn off slot, turn on Amber LED, turn off Green LED */ rc = p_slot->hpc_ops->slot_disable(p_slot); if (rc) { err("%s: Issue of Slot Disable command failed\n", __func__); return rc; } return(rc);}/** * remove_board - Turns off slot and LEDs * @p_slot: target &slot */static int remove_board(struct slot *p_slot){ struct controller *ctrl = p_slot->ctrl; u8 hp_slot; int rc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -