Logo Search packages:      
Sourcecode: linux version File versions  Download package

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    0x3

static 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",
                __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.
       */
      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",
                __FUNCTION__, 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", __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;
      }

      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",
          __FUNCTION__, 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)
{
      shpc_write_cmd(slot, slot->hp_slot, SET_PWR_ON);
}

static void hpc_set_green_led_off(struct slot *slot)
{
      shpc_write_cmd(slot, slot->hp_slot, SET_PWR_OFF);
}

static void hpc_set_green_led_blink(struct slot *slot)
{
      shpc_write_cmd(slot, slot->hp_slot, SET_PWR_BLINK);
}

static void hpc_release_ctlr(struct controller *ctrl)
{
      int i;
      u32 slot_reg, serr_int;

      /*
       * Mask event interrupts and SERRs of all slots
       */
      for (i = 0; i < ctrl->num_slots; i++) {
            slot_reg = shpc_readl(ctrl, SLOT_REG(i));
            slot_reg |= (PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
                       BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
                       CON_PFAULT_INTR_MASK   | MRL_CHANGE_SERR_MASK |
                       CON_PFAULT_SERR_MASK);
            slot_reg &= ~SLOT_REG_RSVDZ_MASK;
            shpc_writel(ctrl, SLOT_REG(i), slot_reg);
      }

      cleanup_slots(ctrl);

      /*
       * Mask SERR and System Interrut generation
       */
      serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
      serr_int |= (GLOBAL_INTR_MASK  | GLOBAL_SERR_MASK |
                 COMMAND_INTR_MASK | ARBITER_SERR_MASK);
      serr_int &= ~SERR_INTR_RSVDZ_MASK;
      shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);

      if (shpchp_poll_mode)
            del_timer(&ctrl->poll_timer);
      else {
            free_irq(ctrl->pci_dev->irq, ctrl);
            pci_disable_msi(ctrl->pci_dev);
      }

      iounmap(ctrl->creg);
      release_mem_region(ctrl->mmio_base, ctrl->mmio_size);

      /*
       * If this is the last controller to be released, destroy the
       * shpchpd work queue
       */
      if (atomic_dec_and_test(&shpchp_num_controllers))
            destroy_workqueue(shpchp_wq);
}

static int hpc_power_on_slot(struct slot * slot)
{
      int retval;

      retval = shpc_write_cmd(slot, slot->hp_slot, SET_SLOT_PWR);
      if (retval)
            err("%s: Write command failed!\n", __FUNCTION__);

      return retval;
}

static int hpc_slot_enable(struct slot * slot)
{
      int retval;

      /* Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */
      retval = shpc_write_cmd(slot, slot->hp_slot,
                  SET_SLOT_ENABLE | SET_PWR_BLINK | SET_ATTN_OFF);
      if (retval)
            err("%s: Write command failed!\n", __FUNCTION__);

      return retval;
}

static int hpc_slot_disable(struct slot * slot)
{
      int retval;

      /* Slot - Disable, Power Indicator - Off, Attention Indicator - On */
      retval = shpc_write_cmd(slot, slot->hp_slot,
                  SET_SLOT_DISABLE | SET_PWR_OFF | SET_ATTN_ON);
      if (retval)
            err("%s: Write command failed!\n", __FUNCTION__);

      return retval;
}

static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
{
      int retval;
      struct controller *ctrl = slot->ctrl;
      u8 pi, cmd;

      pi = shpc_readb(ctrl, PROG_INTERFACE);
      if ((pi == 1) && (value > PCI_SPEED_133MHz_PCIX))
            return -EINVAL;

      switch (value) {
      case PCI_SPEED_33MHz:
            cmd = SETA_PCI_33MHZ;
            break;
      case PCI_SPEED_66MHz:
            cmd = SETA_PCI_66MHZ;
            break;
      case PCI_SPEED_66MHz_PCIX:
            cmd = SETA_PCIX_66MHZ;
            break;
      case PCI_SPEED_100MHz_PCIX:
            cmd = SETA_PCIX_100MHZ;
            break;
      case PCI_SPEED_133MHz_PCIX:
            cmd = SETA_PCIX_133MHZ;
            break;
      case PCI_SPEED_66MHz_PCIX_ECC:
            cmd = SETB_PCIX_66MHZ_EM;
            break;
      case PCI_SPEED_100MHz_PCIX_ECC:
            cmd = SETB_PCIX_100MHZ_EM;
            break;
      case PCI_SPEED_133MHz_PCIX_ECC:
            cmd = SETB_PCIX_133MHZ_EM;
            break;
      case PCI_SPEED_66MHz_PCIX_266:
            cmd = SETB_PCIX_66MHZ_266;
            break;
      case PCI_SPEED_100MHz_PCIX_266:
            cmd = SETB_PCIX_100MHZ_266;
            break;
      case PCI_SPEED_133MHz_PCIX_266:
            cmd = SETB_PCIX_133MHZ_266;
            break;
      case PCI_SPEED_66MHz_PCIX_533:
            cmd = SETB_PCIX_66MHZ_533;
            break;
      case PCI_SPEED_100MHz_PCIX_533:
            cmd = SETB_PCIX_100MHZ_533;
            break;
      case PCI_SPEED_133MHz_PCIX_533:
            cmd = SETB_PCIX_133MHZ_533;
            break;
      default:
            return -EINVAL;
      }

      retval = shpc_write_cmd(slot, 0, cmd);
      if (retval)
            err("%s: Write command failed!\n", __FUNCTION__);

      return retval;
}

static irqreturn_t shpc_isr(int irq, void *dev_id)
{
      struct controller *ctrl = (struct controller *)dev_id;
      u32 serr_int, slot_reg, intr_loc, intr_loc2;
      int hp_slot;

      /* Check to see if it was our interrupt */
      intr_loc = shpc_readl(ctrl, INTR_LOC);
      if (!intr_loc)
            return IRQ_NONE;

      dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc);

      if(!shpchp_poll_mode) {
            /*
             * Mask Global Interrupt Mask - see implementation
             * note on p. 139 of SHPC spec rev 1.0
             */
            serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
            serr_int |= GLOBAL_INTR_MASK;
            serr_int &= ~SERR_INTR_RSVDZ_MASK;
            shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);

            intr_loc2 = shpc_readl(ctrl, INTR_LOC);
            dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
      }

      if (intr_loc & CMD_INTR_PENDING) {
            /*
             * Command Complete Interrupt Pending
             * RO only - clear by writing 1 to the Command Completion
             * Detect bit in Controller SERR-INT register
             */
            serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
            serr_int &= ~SERR_INTR_RSVDZ_MASK;
            shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);

            wake_up_interruptible(&ctrl->queue);
      }

      if (!(intr_loc & ~CMD_INTR_PENDING))
            goto out;

      for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
            /* To find out which slot has interrupt pending */
            if (!(intr_loc & SLOT_INTR_PENDING(hp_slot)))
                  continue;

            slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
            dbg("%s: Slot %x with intr, slot register = %x\n",
                __FUNCTION__, hp_slot, slot_reg);

            if (slot_reg & MRL_CHANGE_DETECTED)
                  shpchp_handle_switch_change(hp_slot, ctrl);

            if (slot_reg & BUTTON_PRESS_DETECTED)
                  shpchp_handle_attention_button(hp_slot, ctrl);

            if (slot_reg & PRSNT_CHANGE_DETECTED)
                  shpchp_handle_presence_change(hp_slot, ctrl);

            if (slot_reg & (ISO_PFAULT_DETECTED | CON_PFAULT_DETECTED))
                  shpchp_handle_power_fault(hp_slot, ctrl);

            /* Clear all slot events */
            slot_reg &= ~SLOT_REG_RSVDZ_MASK;
            shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
      }
 out:
      if (!shpchp_poll_mode) {
            /* Unmask Global Interrupt Mask */
            serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
            serr_int &= ~(GLOBAL_INTR_MASK | SERR_INTR_RSVDZ_MASK);
            shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
      }

      return IRQ_HANDLED;
}

static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
{
      int retval = 0;
      struct controller *ctrl = slot->ctrl;
      enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
      u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
      u32 slot_avail1 = shpc_readl(ctrl, SLOT_AVAIL1);
      u32 slot_avail2 = shpc_readl(ctrl, SLOT_AVAIL2);

      if (pi == 2) {
            if (slot_avail2 & SLOT_133MHZ_PCIX_533)
                  bus_speed = PCI_SPEED_133MHz_PCIX_533;
            else if (slot_avail2 & SLOT_100MHZ_PCIX_533)
                  bus_speed = PCI_SPEED_100MHz_PCIX_533;
            else if (slot_avail2 & SLOT_66MHZ_PCIX_533)
                  bus_speed = PCI_SPEED_66MHz_PCIX_533;
            else if (slot_avail2 & SLOT_133MHZ_PCIX_266)
                  bus_speed = PCI_SPEED_133MHz_PCIX_266;
            else if (slot_avail2 & SLOT_100MHZ_PCIX_266)
                  bus_speed = PCI_SPEED_100MHz_PCIX_266;
            else if (slot_avail2 & SLOT_66MHZ_PCIX_266)
                  bus_speed = PCI_SPEED_66MHz_PCIX_266;
      }

      if (bus_speed == PCI_SPEED_UNKNOWN) {
            if (slot_avail1 & SLOT_133MHZ_PCIX)
                  bus_speed = PCI_SPEED_133MHz_PCIX;
            else if (slot_avail1 & SLOT_100MHZ_PCIX)
                  bus_speed = PCI_SPEED_100MHz_PCIX;
            else if (slot_avail1 & SLOT_66MHZ_PCIX)
                  bus_speed = PCI_SPEED_66MHz_PCIX;
            else if (slot_avail2 & SLOT_66MHZ)
                  bus_speed = PCI_SPEED_66MHz;
            else if (slot_avail1 & SLOT_33MHZ)
                  bus_speed = PCI_SPEED_33MHz;
            else
                  retval = -ENODEV;
      }

      *value = bus_speed;
      dbg("Max bus speed = %d\n", bus_speed);

      return retval;
}

static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value)
{
      int retval = 0;
      struct controller *ctrl = slot->ctrl;
      enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
      u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
      u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
      u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);

      if ((pi == 1) && (speed_mode > 4)) {
            *value = PCI_SPEED_UNKNOWN;
            return -ENODEV;
      }

      switch (speed_mode) {
      case 0x0:
            *value = PCI_SPEED_33MHz;
            break;
      case 0x1:
            *value = PCI_SPEED_66MHz;
            break;
      case 0x2:
            *value = PCI_SPEED_66MHz_PCIX;
            break;
      case 0x3:
            *value = PCI_SPEED_100MHz_PCIX;
            break;
      case 0x4:
            *value = PCI_SPEED_133MHz_PCIX;
            break;
      case 0x5:
            *value = PCI_SPEED_66MHz_PCIX_ECC;
            break;
      case 0x6:
            *value = PCI_SPEED_100MHz_PCIX_ECC;
            break;
      case 0x7:
            *value = PCI_SPEED_133MHz_PCIX_ECC;
            break;
      case 0x8:
            *value = PCI_SPEED_66MHz_PCIX_266;
            break;
      case 0x9:
            *value = PCI_SPEED_100MHz_PCIX_266;
            break;
      case 0xa:
            *value = PCI_SPEED_133MHz_PCIX_266;
            break;
      case 0xb:
            *value = PCI_SPEED_66MHz_PCIX_533;
            break;
      case 0xc:
            *value = PCI_SPEED_100MHz_PCIX_533;
            break;
      case 0xd:
            *value = PCI_SPEED_133MHz_PCIX_533;
            break;
      default:
            *value = PCI_SPEED_UNKNOWN;
            retval = -ENODEV;
            break;
      }

      dbg("Current bus speed = %d\n", bus_speed);
      return retval;
}

static struct hpc_ops shpchp_hpc_ops = {
      .power_on_slot                = hpc_power_on_slot,
      .slot_enable                  = hpc_slot_enable,
      .slot_disable                 = hpc_slot_disable,
      .set_bus_speed_mode           = hpc_set_bus_speed_mode,
      .set_attention_status   = hpc_set_attention_status,
      .get_power_status       = hpc_get_power_status,
      .get_attention_status   = hpc_get_attention_status,
      .get_latch_status       = hpc_get_latch_status,
      .get_adapter_status           = hpc_get_adapter_status,

      .get_max_bus_speed            = hpc_get_max_bus_speed,
      .get_cur_bus_speed            = hpc_get_cur_bus_speed,
      .get_adapter_speed            = hpc_get_adapter_speed,
      .get_mode1_ECC_cap            = hpc_get_mode1_ECC_cap,
      .get_prog_int                 = hpc_get_prog_int,

      .query_power_fault            = hpc_query_power_fault,
      .green_led_on                 = hpc_set_green_led_on,
      .green_led_off                = hpc_set_green_led_off,
      .green_led_blink        = hpc_set_green_led_blink,

      .release_ctlr                 = hpc_release_ctlr,
};

int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
{
      int rc = -1, num_slots = 0;
      u8 hp_slot;
      u32 shpc_base_offset;
      u32 tempdword, slot_reg, slot_config;
      u8 i;

      ctrl->pci_dev = pdev;  /* pci_dev of the P2P bridge */

      if ((pdev->vendor == PCI_VENDOR_ID_AMD) || (pdev->device ==
                        PCI_DEVICE_ID_AMD_GOLAM_7450)) {
            /* amd shpc driver doesn't use Base Offset; assume 0 */
            ctrl->mmio_base = pci_resource_start(pdev, 0);
            ctrl->mmio_size = pci_resource_len(pdev, 0);
      } else {
            ctrl->cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC);
            if (!ctrl->cap_offset) {
                  err("%s : cap_offset == 0\n", __FUNCTION__);
                  goto abort;
            }
            dbg("%s: cap_offset = %x\n", __FUNCTION__, ctrl->cap_offset);

            rc = shpc_indirect_read(ctrl, 0, &shpc_base_offset);
            if (rc) {
                  err("%s: cannot read base_offset\n", __FUNCTION__);
                  goto abort;
            }

            rc = shpc_indirect_read(ctrl, 3, &tempdword);
            if (rc) {
                  err("%s: cannot read slot config\n", __FUNCTION__);
                  goto abort;
            }
            num_slots = tempdword & SLOT_NUM;
            dbg("%s: num_slots (indirect) %x\n", __FUNCTION__, num_slots);

            for (i = 0; i < 9 + num_slots; i++) {
                  rc = shpc_indirect_read(ctrl, i, &tempdword);
                  if (rc) {
                        err("%s: cannot read creg (index = %d)\n",
                            __FUNCTION__, i);
                        goto abort;
                  }
                  dbg("%s: offset %d: value %x\n", __FUNCTION__,i,
                              tempdword);
            }

            ctrl->mmio_base =
                  pci_resource_start(pdev, 0) + shpc_base_offset;
            ctrl->mmio_size = 0x24 + 0x4 * num_slots;
      }

      info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, pdev->subsystem_vendor,
            pdev->subsystem_device);

      rc = pci_enable_device(pdev);
      if (rc) {
            err("%s: pci_enable_device failed\n", __FUNCTION__);
            goto abort;
      }

      if (!request_mem_region(ctrl->mmio_base, ctrl->mmio_size, MY_NAME)) {
            err("%s: cannot reserve MMIO region\n", __FUNCTION__);
            rc = -1;
            goto abort;
      }

      ctrl->creg = ioremap(ctrl->mmio_base, ctrl->mmio_size);
      if (!ctrl->creg) {
            err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__,
                ctrl->mmio_size, ctrl->mmio_base);
            release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
            rc = -1;
            goto abort;
      }
      dbg("%s: ctrl->creg %p\n", __FUNCTION__, ctrl->creg);

      mutex_init(&ctrl->crit_sect);
      mutex_init(&ctrl->cmd_lock);

      /* Setup wait queue */
      init_waitqueue_head(&ctrl->queue);

      ctrl->hpc_ops = &shpchp_hpc_ops;

      /* Return PCI Controller Info */
      slot_config = shpc_readl(ctrl, SLOT_CONFIG);
      ctrl->slot_device_offset = (slot_config & FIRST_DEV_NUM) >> 8;
      ctrl->num_slots = slot_config & SLOT_NUM;
      ctrl->first_slot = (slot_config & PSN) >> 16;
      ctrl->slot_num_inc = ((slot_config & UPDOWN) >> 29) ? 1 : -1;

      /* Mask Global Interrupt Mask & Command Complete Interrupt Mask */
      tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
      dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
      tempdword |= (GLOBAL_INTR_MASK  | GLOBAL_SERR_MASK |
                  COMMAND_INTR_MASK | ARBITER_SERR_MASK);
      tempdword &= ~SERR_INTR_RSVDZ_MASK;
      shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
      tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
      dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);

      /* Mask the MRL sensor SERR Mask of individual slot in
       * Slot SERR-INT Mask & clear all the existing event if any
       */
      for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
            slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
            dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
                  hp_slot, slot_reg);
            slot_reg |= (PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
                       BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
                       CON_PFAULT_INTR_MASK   | MRL_CHANGE_SERR_MASK |
                       CON_PFAULT_SERR_MASK);
            slot_reg &= ~SLOT_REG_RSVDZ_MASK;
            shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
      }

      if (shpchp_poll_mode) {
            /* Install interrupt polling timer. Start with 10 sec delay */
            init_timer(&ctrl->poll_timer);
            start_int_poll_timer(ctrl, 10);
      } else {
            /* Installs the interrupt handler */
            rc = pci_enable_msi(pdev);
            if (rc) {
                  info("Can't get msi for the hotplug controller\n");
                  info("Use INTx for the hotplug controller\n");
            }

            rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
                         MY_NAME, (void *)ctrl);
            dbg("%s: request_irq %d for hpc%d (returns %d)\n",
                __FUNCTION__, ctrl->pci_dev->irq,
                atomic_read(&shpchp_num_controllers), rc);
            if (rc) {
                  err("Can't get irq %d for the hotplug controller\n",
                      ctrl->pci_dev->irq);
                  goto abort_iounmap;
            }
      }
      dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __FUNCTION__,
                  pdev->bus->number, PCI_SLOT(pdev->devfn),
                  PCI_FUNC(pdev->devfn), pdev->irq);
      get_hp_hw_control_from_firmware(pdev);

      /*
       * If this is the first controller to be initialized,
       * initialize the shpchpd work queue
       */
      if (atomic_add_return(1, &shpchp_num_controllers) == 1) {
            shpchp_wq = create_singlethread_workqueue("shpchpd");
            if (!shpchp_wq) {
                  rc = -ENOMEM;
                  goto abort_iounmap;
            }
      }

      /*
       * Unmask all event interrupts of all slots
       */
      for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
            slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
            dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
                  hp_slot, slot_reg);
            slot_reg &= ~(PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
                        BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
                        CON_PFAULT_INTR_MASK | SLOT_REG_RSVDZ_MASK);
            shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
      }
      if (!shpchp_poll_mode) {
            /* Unmask all general input interrupts and SERR */
            tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
            tempdword &= ~(GLOBAL_INTR_MASK | COMMAND_INTR_MASK |
                         SERR_INTR_RSVDZ_MASK);
            shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
            tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
            dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
      }

      return 0;

      /* We end up here for the many possible ways to fail this API.  */
abort_iounmap:
      iounmap(ctrl->creg);
abort:
      return rc;
}

Generated by  Doxygen 1.6.0   Back to index