📄 shpchp_hpc.c
字号:
/* * Standard PCI Hot Plug 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/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/interrupt.h>#include "shpchp.h"#ifdef DEBUG#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT)#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE)/* Redefine this flagword to set debug level */#define DEBUG_LEVEL DBG_K_STANDARD#define DEFINE_DBG_BUFFER char __dbg_str_buf[256];#define DBG_PRINT( dbg_flags, args... ) \ do { \ if ( DEBUG_LEVEL & ( dbg_flags ) ) \ { \ int len; \ len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ __FILE__, __LINE__, __FUNCTION__ ); \ sprintf( __dbg_str_buf + len, args ); \ printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ } \ } while (0)#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]");#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]");#else#define DEFINE_DBG_BUFFER#define DBG_ENTER_ROUTINE#define DBG_LEAVE_ROUTINE#endif /* DEBUG *//* Slot Available Register I field definition */#define SLOT_33MHZ 0x0000001f#define SLOT_66MHZ_PCIX 0x00001f00#define SLOT_100MHZ_PCIX 0x001f0000#define SLOT_133MHZ_PCIX 0x1f000000/* Slot Available Register II field definition */#define SLOT_66MHZ 0x0000001f#define SLOT_66MHZ_PCIX_266 0x00000f00#define SLOT_100MHZ_PCIX_266 0x0000f000#define SLOT_133MHZ_PCIX_266 0x000f0000#define SLOT_66MHZ_PCIX_533 0x00f00000#define SLOT_100MHZ_PCIX_533 0x0f000000#define SLOT_133MHZ_PCIX_533 0xf0000000/* Slot Configuration */#define SLOT_NUM 0x0000001F#define FIRST_DEV_NUM 0x00001F00#define PSN 0x07FF0000#define UPDOWN 0x20000000#define MRLSENSOR 0x40000000#define ATTN_BUTTON 0x80000000/* Slot Status Field Definitions *//* Slot State */#define PWR_ONLY 0x0001#define ENABLED 0x0002#define DISABLED 0x0003/* Power Indicator State */#define PWR_LED_ON 0x0004#define PWR_LED_BLINK 0x0008#define PWR_LED_OFF 0x000c/* Attention Indicator State */#define ATTEN_LED_ON 0x0010#define ATTEN_LED_BLINK 0x0020#define ATTEN_LED_OFF 0x0030/* Power Fault */#define pwr_fault 0x0040/* Attention Button */#define ATTEN_BUTTON 0x0080/* MRL Sensor */#define MRL_SENSOR 0x0100/* 66 MHz Capable */#define IS_66MHZ_CAP 0x0200/* PRSNT1#/PRSNT2# */#define SLOT_EMP 0x0c00/* PCI-X Capability */#define NON_PCIX 0x0000#define PCIX_66 0x1000#define PCIX_133 0x3000#define PCIX_266 0x4000 /* For PI = 2 only */#define PCIX_533 0x5000 /* For PI = 2 only *//* SHPC 'write' operations/commands *//* Slot operation - 0x00h to 0x3Fh */#define NO_CHANGE 0x00/* Slot state - Bits 0 & 1 of controller command register */#define SET_SLOT_PWR 0x01 #define SET_SLOT_ENABLE 0x02 #define SET_SLOT_DISABLE 0x03 /* Power indicator state - Bits 2 & 3 of controller command register*/#define SET_PWR_ON 0x04 #define SET_PWR_BLINK 0x08 #define SET_PWR_OFF 0x0C /* Attention indicator state - Bits 4 & 5 of controller command register*/#define SET_ATTN_ON 0x010 #define SET_ATTN_BLINK 0x020#define SET_ATTN_OFF 0x030 /* Set bus speed/mode A - 0x40h to 0x47h */#define SETA_PCI_33MHZ 0x40#define SETA_PCI_66MHZ 0x41#define SETA_PCIX_66MHZ 0x42#define SETA_PCIX_100MHZ 0x43#define SETA_PCIX_133MHZ 0x44#define RESERV_1 0x45#define RESERV_2 0x46#define RESERV_3 0x47/* Set bus speed/mode B - 0x50h to 0x5fh */#define SETB_PCI_33MHZ 0x50#define SETB_PCI_66MHZ 0x51#define SETB_PCIX_66MHZ_PM 0x52#define SETB_PCIX_100MHZ_PM 0x53#define SETB_PCIX_133MHZ_PM 0x54#define SETB_PCIX_66MHZ_EM 0x55#define SETB_PCIX_100MHZ_EM 0x56#define SETB_PCIX_133MHZ_EM 0x57#define SETB_PCIX_66MHZ_266 0x58#define SETB_PCIX_100MHZ_266 0x59#define SETB_PCIX_133MHZ_266 0x5a#define SETB_PCIX_66MHZ_533 0x5b#define SETB_PCIX_100MHZ_533 0x5c#define SETB_PCIX_133MHZ_533 0x5d/* Power-on all slots - 0x48h */#define SET_PWR_ON_ALL 0x48/* Enable all slots - 0x49h */#define SET_ENABLE_ALL 0x49/* SHPC controller command error code */#define SWITCH_OPEN 0x1#define INVALID_CMD 0x2#define INVALID_SPEED_MODE 0x4/* For accessing SHPC Working Register Set */#define DWORD_SELECT 0x2#define DWORD_DATA 0x4#define BASE_OFFSET 0x0/* Field Offset in Logical Slot Register - byte boundary */#define SLOT_EVENT_LATCH 0x2#define SLOT_SERR_INT_MASK 0x3static spinlock_t hpc_event_lock;DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */static int ctlr_seq_num = 0; /* Controller sequenc # */static spinlock_t list_lock;static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs);static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);static int hpc_check_cmd_status(struct controller *ctrl);/* This is the interrupt polling timeout function. */static void int_poll_timeout(unsigned long lphp_ctlr){ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; DBG_ENTER_ROUTINE if ( !php_ctlr ) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return; } /* Poll for interrupt events. regs == NULL => polling */ shpc_isr( 0, (void *)php_ctlr, NULL ); init_timer(&php_ctlr->int_poll_timer); if (!shpchp_poll_time) shpchp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ start_int_poll_timer(php_ctlr, shpchp_poll_time); return;}/* This function starts the interrupt polling timer. */static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds){ if (!php_ctlr) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return; } if ( ( seconds <= 0 ) || ( seconds > 60 ) ) seconds = 2; /* Clamp to sane value */ php_ctlr->int_poll_timer.function = &int_poll_timeout; php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; add_timer(&php_ctlr->int_poll_timer); return;}static inline int shpc_wait_cmd(struct controller *ctrl){ int retval = 0; unsigned int timeout_msec = shpchp_poll_mode ? 2000 : 1000; unsigned long timeout = msecs_to_jiffies(timeout_msec); int rc = wait_event_interruptible_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); if (!rc) { retval = -EIO; err("Command not completed in %d msec\n", timeout_msec); } else if (rc < 0) { retval = -EINTR; info("Command was interrupted by a signal\n"); } ctrl->cmd_busy = 0; return retval;}static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd){ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; u16 cmd_status; int retval = 0; u16 temp_word; int i; DBG_ENTER_ROUTINE mutex_lock(&slot->ctrl->cmd_lock); if (!php_ctlr) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); retval = -EINVAL; goto out; } for (i = 0; i < 10; i++) { cmd_status = readw(php_ctlr->creg + CMD_STATUS); if (!(cmd_status & 0x1)) break; /* Check every 0.1 sec for a total of 1 sec*/ msleep(100); } cmd_status = readw(php_ctlr->creg + CMD_STATUS); if (cmd_status & 0x1) { /* After 1 sec and and the controller is still busy */ err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__); retval = -EBUSY; goto out; } ++t_slot; temp_word = (t_slot << 8) | (cmd & 0xFF); dbg("%s: t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd); /* To make sure the Controller Busy bit is 0 before we send out the * command. */ slot->ctrl->cmd_busy = 1; writew(temp_word, php_ctlr->creg + CMD); /* * Wait for command completion. */ retval = shpc_wait_cmd(slot->ctrl); if (retval) goto out; cmd_status = hpc_check_cmd_status(slot->ctrl); if (cmd_status) { err("%s: Failed to issued command 0x%x (error code = %d)\n", __FUNCTION__, cmd, cmd_status); retval = -EIO; } out: mutex_unlock(&slot->ctrl->cmd_lock); DBG_LEAVE_ROUTINE return retval;}static int hpc_check_cmd_status(struct controller *ctrl){ struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; u16 cmd_status; int retval = 0; DBG_ENTER_ROUTINE if (!ctrl->hpc_ctlr_handle) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return -1; } cmd_status = readw(php_ctlr->creg + CMD_STATUS) & 0x000F; switch (cmd_status >> 1) { case 0: retval = 0; break; case 1: retval = SWITCH_OPEN; err("%s: Switch opened!\n", __FUNCTION__); break; case 2: retval = INVALID_CMD; err("%s: Invalid HPC command!\n", __FUNCTION__); break; case 4: retval = INVALID_SPEED_MODE; err("%s: Invalid bus speed/mode!\n", __FUNCTION__); break; default: retval = cmd_status; } DBG_LEAVE_ROUTINE return retval;}static int hpc_get_attention_status(struct slot *slot, u8 *status){ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; u32 slot_reg; u16 slot_status; u8 atten_led_state; DBG_ENTER_ROUTINE if (!slot->ctrl->hpc_ctlr_handle) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return -1; } slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); slot_status = (u16) slot_reg; atten_led_state = (slot_status & 0x0030) >> 4; switch (atten_led_state) { case 0: *status = 0xFF; /* Reserved */ break; case 1: *status = 1; /* On */ break; case 2: *status = 2; /* Blink */ break; case 3: *status = 0; /* Off */ break; default: *status = 0xFF; break; } DBG_LEAVE_ROUTINE return 0;}static int hpc_get_power_status(struct slot * slot, u8 *status){ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; u32 slot_reg; u16 slot_status; u8 slot_state; int retval = 0; DBG_ENTER_ROUTINE if (!slot->ctrl->hpc_ctlr_handle) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return -1; } slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); slot_status = (u16) slot_reg; slot_state = (slot_status & 0x0003); switch (slot_state) { case 0: *status = 0xFF; break; case 1: *status = 2; /* Powered only */ break; case 2: *status = 1; /* Enabled */ break; case 3: *status = 0; /* Disabled */ break; default: *status = 0xFF; break; } DBG_LEAVE_ROUTINE return retval;}static int hpc_get_latch_status(struct slot *slot, u8 *status){ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; u32 slot_reg; u16 slot_status; DBG_ENTER_ROUTINE if (!slot->ctrl->hpc_ctlr_handle) { err("%s: Invalid HPC controller handle!\n", __FUNCTION__); return -1; } slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); slot_status = (u16)slot_reg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -