?? 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"/* 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/* * Interrupt Locator Register definitions */#define CMD_INTR_PENDING (1 << 0)#define SLOT_INTR_PENDING(i) (1 << (i + 1))/* * Controller SERR-INT Register */#define GLOBAL_INTR_MASK (1 << 0)#define GLOBAL_SERR_MASK (1 << 1)#define COMMAND_INTR_MASK (1 << 2)#define ARBITER_SERR_MASK (1 << 3)#define COMMAND_DETECTED (1 << 16)#define ARBITER_DETECTED (1 << 17)#define SERR_INTR_RSVDZ_MASK 0xfffc0000/* * Logical Slot Register definitions */#define SLOT_REG(i) (SLOT1 + (4 * i))#define SLOT_STATE_SHIFT (0)#define SLOT_STATE_MASK (3 << 0)#define SLOT_STATE_PWRONLY (1)#define SLOT_STATE_ENABLED (2)#define SLOT_STATE_DISABLED (3)#define PWR_LED_STATE_SHIFT (2)#define PWR_LED_STATE_MASK (3 << 2)#define ATN_LED_STATE_SHIFT (4)#define ATN_LED_STATE_MASK (3 << 4)#define ATN_LED_STATE_ON (1)#define ATN_LED_STATE_BLINK (2)#define ATN_LED_STATE_OFF (3)#define POWER_FAULT (1 << 6)#define ATN_BUTTON (1 << 7)#define MRL_SENSOR (1 << 8)#define MHZ66_CAP (1 << 9)#define PRSNT_SHIFT (10)#define PRSNT_MASK (3 << 10)#define PCIX_CAP_SHIFT (12)#define PCIX_CAP_MASK_PI1 (3 << 12)#define PCIX_CAP_MASK_PI2 (7 << 12)#define PRSNT_CHANGE_DETECTED (1 << 16)#define ISO_PFAULT_DETECTED (1 << 17)#define BUTTON_PRESS_DETECTED (1 << 18)#define MRL_CHANGE_DETECTED (1 << 19)#define CON_PFAULT_DETECTED (1 << 20)#define PRSNT_CHANGE_INTR_MASK (1 << 24)#define ISO_PFAULT_INTR_MASK (1 << 25)#define BUTTON_PRESS_INTR_MASK (1 << 26)#define MRL_CHANGE_INTR_MASK (1 << 27)#define CON_PFAULT_INTR_MASK (1 << 28)#define MRL_CHANGE_SERR_MASK (1 << 29)#define CON_PFAULT_SERR_MASK (1 << 30)#define SLOT_REG_RSVDZ_MASK (1 << 15) | (7 << 21)/* * SHPC Command Code definitnions * * Slot Operation 00h - 3Fh * Set Bus Segment Speed/Mode A 40h - 47h * Power-Only All Slots 48h * Enable All Slots 49h * Set Bus Segment Speed/Mode B (PI=2) 50h - 5Fh * Reserved Command Codes 60h - BFh * Vendor Specific Commands C0h - FFh */#define SET_SLOT_PWR 0x01 /* Slot Operation */#define SET_SLOT_ENABLE 0x02#define SET_SLOT_DISABLE 0x03#define SET_PWR_ON 0x04#define SET_PWR_BLINK 0x08#define SET_PWR_OFF 0x0c#define SET_ATTN_ON 0x10#define SET_ATTN_BLINK 0x20#define SET_ATTN_OFF 0x30#define SETA_PCI_33MHZ 0x40 /* Set Bus Segment Speed/Mode A */#define SETA_PCI_66MHZ 0x41#define SETA_PCIX_66MHZ 0x42#define SETA_PCIX_100MHZ 0x43#define SETA_PCIX_133MHZ 0x44#define SETA_RESERVED1 0x45#define SETA_RESERVED2 0x46#define SETA_RESERVED3 0x47#define SET_PWR_ONLY_ALL 0x48 /* Power-Only All Slots */#define SET_ENABLE_ALL 0x49 /* Enable All Slots */#define SETB_PCI_33MHZ 0x50 /* Set Bus Segment Speed/Mode B */#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#define SETB_RESERVED1 0x5e#define SETB_RESERVED2 0x5f/* * SHPC controller command error code */#define SWITCH_OPEN 0x1#define INVALID_CMD 0x2#define INVALID_SPEED_MODE 0x4/* * For accessing SHPC Working Register Set via PCI Configuration Space */#define DWORD_SELECT 0x2#define DWORD_DATA 0x4/* Field Offset in Logical Slot Register - byte boundary */#define SLOT_EVENT_LATCH 0x2#define SLOT_SERR_INT_MASK 0x3static atomic_t shpchp_num_controllers = ATOMIC_INIT(0);static irqreturn_t shpc_isr(int irq, void *dev_id);static void start_int_poll_timer(struct controller *ctrl, int sec);static int hpc_check_cmd_status(struct controller *ctrl);static inline u8 shpc_readb(struct controller *ctrl, int reg){ return readb(ctrl->creg + reg);}static inline void shpc_writeb(struct controller *ctrl, int reg, u8 val){ writeb(val, ctrl->creg + reg);}static inline u16 shpc_readw(struct controller *ctrl, int reg){ return readw(ctrl->creg + reg);}static inline void shpc_writew(struct controller *ctrl, int reg, u16 val){ writew(val, ctrl->creg + reg);}static inline u32 shpc_readl(struct controller *ctrl, int reg){ return readl(ctrl->creg + reg);}static inline void shpc_writel(struct controller *ctrl, int reg, u32 val){ writel(val, ctrl->creg + reg);}static inline int shpc_indirect_read(struct controller *ctrl, int index, u32 *value){ int rc; u32 cap_offset = ctrl->cap_offset; struct pci_dev *pdev = ctrl->pci_dev; rc = pci_write_config_byte(pdev, cap_offset + DWORD_SELECT, index); if (rc) return rc; return pci_read_config_dword(pdev, cap_offset + DWORD_DATA, value);}/* * This is the interrupt polling timeout function. */static void int_poll_timeout(unsigned long data){ struct controller *ctrl = (struct controller *)data; /* Poll for interrupt events. regs == NULL => polling */ shpc_isr(0, ctrl); init_timer(&ctrl->poll_timer); if (!shpchp_poll_time) shpchp_poll_time = 2; /* default polling interval is 2 sec */ start_int_poll_timer(ctrl, shpchp_poll_time);}/* * This function starts the interrupt polling timer. */static void start_int_poll_timer(struct controller *ctrl, int sec){ /* Clamp to sane value */ if ((sec <= 0) || (sec > 60)) sec = 2; ctrl->poll_timer.function = &int_poll_timeout; ctrl->poll_timer.data = (unsigned long)ctrl; ctrl->poll_timer.expires = jiffies + sec * HZ; add_timer(&ctrl->poll_timer);}static inline int is_ctrl_busy(struct controller *ctrl){ u16 cmd_status = shpc_readw(ctrl, CMD_STATUS); return cmd_status & 0x1;}/* * Returns 1 if SHPC finishes executing a command within 1 sec, * otherwise returns 0. */static inline int shpc_poll_ctrl_busy(struct controller *ctrl){ int i; if (!is_ctrl_busy(ctrl)) return 1; /* Check every 0.1 sec for a total of 1 sec */ for (i = 0; i < 10; i++) { msleep(100); if (!is_ctrl_busy(ctrl)) return 1; } return 0;}static inline int shpc_wait_cmd(struct controller *ctrl){ int retval = 0; unsigned long timeout = msecs_to_jiffies(1000); int rc; if (shpchp_poll_mode) rc = shpc_poll_ctrl_busy(ctrl); else rc = wait_event_interruptible_timeout(ctrl->queue, !is_ctrl_busy(ctrl), timeout); if (!rc && is_ctrl_busy(ctrl)) { retval = -EIO; err("Command not completed in 1000 msec\n"); } else if (rc < 0) { retval = -EINTR; info("Command was interrupted by a signal\n"); } return retval;}static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd){ struct controller *ctrl = slot->ctrl; u16 cmd_status; int retval = 0; u16 temp_word; mutex_lock(&slot->ctrl->cmd_lock); if (!shpc_poll_ctrl_busy(ctrl)) { /* After 1 sec and and the controller is still busy */ err("%s : Controller is still busy after 1 sec.\n", __func__); retval = -EBUSY; goto out; } ++t_slot; temp_word = (t_slot << 8) | (cmd & 0xFF); dbg("%s: t_slot %x cmd %x\n", __func__, t_slot, cmd); /* To make sure the Controller Busy bit is 0 before we send out the * command. */ shpc_writew(ctrl, CMD, temp_word); /* * 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", __func__, cmd, cmd_status); retval = -EIO; } out: mutex_unlock(&slot->ctrl->cmd_lock); return retval;}static int hpc_check_cmd_status(struct controller *ctrl){ int retval = 0; u16 cmd_status = shpc_readw(ctrl, CMD_STATUS) & 0x000F; switch (cmd_status >> 1) { case 0: retval = 0; break; case 1: retval = SWITCH_OPEN; err("%s: Switch opened!\n", __func__); break; case 2: retval = INVALID_CMD; err("%s: Invalid HPC command!\n", __func__); break; case 4: retval = INVALID_SPEED_MODE; err("%s: Invalid bus speed/mode!\n", __func__); break; default: retval = cmd_status; } return retval;}static int hpc_get_attention_status(struct slot *slot, u8 *status){ struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); u8 state = (slot_reg & ATN_LED_STATE_MASK) >> ATN_LED_STATE_SHIFT; switch (state) { case ATN_LED_STATE_ON: *status = 1; /* On */ break; case ATN_LED_STATE_BLINK: *status = 2; /* Blink */ break; case ATN_LED_STATE_OFF: *status = 0; /* Off */ break; default: *status = 0xFF; /* Reserved */ break; } return 0;}static int hpc_get_power_status(struct slot * slot, u8 *status){ struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); u8 state = (slot_reg & SLOT_STATE_MASK) >> SLOT_STATE_SHIFT; switch (state) { case SLOT_STATE_PWRONLY: *status = 2; /* Powered only */ break; case SLOT_STATE_ENABLED: *status = 1; /* Enabled */ break; case SLOT_STATE_DISABLED: *status = 0; /* Disabled */ break; default: *status = 0xFF; /* Reserved */ break; } return 0;}static int hpc_get_latch_status(struct slot *slot, u8 *status){ struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); *status = !!(slot_reg & MRL_SENSOR); /* 0 -> close; 1 -> open */ return 0;}static int hpc_get_adapter_status(struct slot *slot, u8 *status){ struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); u8 state = (slot_reg & PRSNT_MASK) >> PRSNT_SHIFT; *status = (state != 0x3) ? 1 : 0; return 0;}static int hpc_get_prog_int(struct slot *slot, u8 *prog_int){ struct controller *ctrl = slot->ctrl; *prog_int = shpc_readb(ctrl, PROG_INTERFACE); return 0;}static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value){ int retval = 0; struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); u8 m66_cap = !!(slot_reg & MHZ66_CAP); u8 pi, pcix_cap; if ((retval = hpc_get_prog_int(slot, &pi))) return retval; switch (pi) { case 1: pcix_cap = (slot_reg & PCIX_CAP_MASK_PI1) >> PCIX_CAP_SHIFT; break; case 2: pcix_cap = (slot_reg & PCIX_CAP_MASK_PI2) >> PCIX_CAP_SHIFT; break; default: return -ENODEV; } dbg("%s: slot_reg = %x, pcix_cap = %x, m66_cap = %x\n", __func__, slot_reg, pcix_cap, m66_cap); switch (pcix_cap) { case 0x0: *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; break; case 0x1: *value = PCI_SPEED_66MHz_PCIX; break; case 0x3: *value = PCI_SPEED_133MHz_PCIX; break; case 0x4: *value = PCI_SPEED_133MHz_PCIX_266; break; case 0x5: *value = PCI_SPEED_133MHz_PCIX_533; break; case 0x2: default: *value = PCI_SPEED_UNKNOWN; retval = -ENODEV; break; } dbg("Adapter speed = %d\n", *value); return retval;}static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode){ int retval = 0; struct controller *ctrl = slot->ctrl; u16 sec_bus_status = shpc_readw(ctrl, SEC_BUS_CONFIG); u8 pi = shpc_readb(ctrl, PROG_INTERFACE); if (pi == 2) { *mode = (sec_bus_status & 0x0100) >> 8; } else { retval = -1; } dbg("Mode 1 ECC cap = %d\n", *mode); return retval;}static int hpc_query_power_fault(struct slot * slot){ struct controller *ctrl = slot->ctrl; u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot)); /* Note: Logic 0 => fault */ return !(slot_reg & POWER_FAULT);}static int hpc_set_attention_status(struct slot *slot, u8 value){ u8 slot_cmd = 0; switch (value) { case 0 : slot_cmd = SET_ATTN_OFF; /* OFF */ break; case 1: slot_cmd = SET_ATTN_ON; /* ON */ break; case 2: slot_cmd = SET_ATTN_BLINK; /* BLINK */ break; default: return -1; } return shpc_write_cmd(slot, slot->hp_slot, slot_cmd);}static void hpc_set_green_led_on(struct slot *slot){
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -