diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index c1bb38493..7a8114bca 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -213,6 +213,9 @@ void mp_save_irq(struct mpc_intsrc *m) return; } + if (mp_irq_entries == 7) { + m->irqflag = MP_IRQPOL_ACTIVE_LOW | MP_IRQTRIG_LEVEL; + } memcpy(&mp_irqs[mp_irq_entries], m, sizeof(*m)); if (++mp_irq_entries == MAX_IRQ_SOURCES) panic("Max # of irq sources exceeded!!\n"); diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 78d621290..dc3ca60a5 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -25,7 +25,7 @@ static const struct acpi_device_id forbidden_id_list[] = { {"PNP0200", 0}, /* AT DMA Controller */ {"ACPI0009", 0}, /* IOxAPIC */ {"ACPI000A", 0}, /* IOAPIC */ - {"SMB0001", 0}, /* ACPI SMBUS virtual device */ + //{"SMB0001", 0}, /* ACPI SMBUS virtual device */ {"", 0}, }; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 8c1b31ed0..6c516e4ea 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -33,7 +33,7 @@ #include #include #include - +#include /* PIIX4 SMBus address offsets */ #define SMBHSTSTS (0 + piix4_smba) @@ -49,8 +49,49 @@ #define SMBSLVEVT (0xA + piix4_smba) #define SMBSLVDAT (0xC + piix4_smba) +/* ASF register offsets */ +#define ASF_PEC (0x08 + piix4_smba) +#define ASF_LISTEN_ADR (0x09 + piix4_smba) +#define ASF_STATUS (0x0a + piix4_smba) +#define ASF_STATUS_MASK0 (0x0b + piix4_smba) +#define ASF_STATUS_MASK1 (0x0c + piix4_smba) +#define ASF_SLAVE_STATUS (0x0d + piix4_smba) +#define ASF_REMOTE_CTRL_ADR (0x0e + piix4_smba) +#define ASF_SENSOR_ADR (0x0f + piix4_smba) +#define ASF_DATA_READ_POINTER (0x10 + piix4_smba) +#define ASF_DATA_WRITE_POINTER (0x11 + piix4_smba) +#define ASF_SET_DATA_READ_POINTER (0x12 + piix4_smba) +#define ASF_DATA_BANK_SEL (0x13 + piix4_smba) +#define ASF_SEMAPHORE (0x14 + piix4_smba) +#define ASF_SLAVE_EN (0x15 + piix4_smba) +#define ASF_DELAY_MASTER (0x16 + piix4_smba) + +/* ASF ListenAdr */ +#define ASF_LISTEN_ADR_EN BIT(0) + +/* ASF status */ +#define ASF_SLAVE_INTR BIT(6) + +/* ASF Host status */ +#define ASF_HOST_INTR BIT(1) + +/* ASF Data bank sel */ +#define ASF_SET_READ_DATA_BANK_OFFSET 4 +#define ASF_DATA_BANK_LAST_TOUCH BIT(0) +#define ASF_DATA_BANK_0_FULL BIT(2) +#define ASF_DATA_BANK_1_FULL BIT(3) +#define ASF_READ_HOST_DATA_BANK BIT(7) + +/* ASF semaphore */ +#define ASF_HOST_SEMAPHORE BIT(0) +#define ASF_CLR_HOST_SEMAPHORE BIT(1) + +/* ASF slave */ +#define ASF_SLAVE_INTR_EN BIT(1) +#define ASF_KILL_SLAVE BIT(4) + /* count for request_region */ -#define SMBIOSIZE 9 +#define SMBIOSIZE 0x20 /* PCI Address Constants */ #define SMBBA 0x090 @@ -77,6 +118,7 @@ /* SB800 constants */ #define SB800_PIIX4_SMB_IDX 0xcd6 +#define SB800_PIIX4_SMB_MAP_SIZE 2 #define KERNCZ_IMC_IDX 0x3e #define KERNCZ_IMC_DATA 0x3f @@ -97,6 +139,12 @@ #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 +#define SB800_PIIX4_FCH_PM_DECODEEN_MMIO_EN BIT(1) +#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300 +#define SB800_PIIX4_FCH_PM_SIZE 8 + +#define AMD_PCI_SMBUS_REVISION_MMIO 0x51 + /* insmod parameters */ /* If force is set to anything different from 0, we forcibly enable the @@ -114,6 +162,7 @@ MODULE_PARM_DESC(force_addr, "EXTREMELY DANGEROUS!"); static int srvrworks_csb5_delay; +static bool is_amd_kerncz = false; static struct pci_driver piix4_driver; static const struct dmi_system_id piix4_dmi_blacklist[] = { @@ -155,6 +204,12 @@ static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { }; static const char *piix4_aux_port_name_sb800 = " port 1"; +struct sb800_mmio_cfg { + void __iomem *addr; + struct resource *res; + bool use_mmio; +}; + struct i2c_piix4_adapdata { unsigned short smba; @@ -162,8 +217,94 @@ struct i2c_piix4_adapdata { bool sb800_main; bool notify_imc; u8 port; /* Port number, shifted */ + struct sb800_mmio_cfg mmio_cfg; + struct completion *completion; }; +/* +static void piix4_asf_dump_registers(struct i2c_adapter *piix4_adapter, char *label) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); + unsigned short piix4_smba = adapdata->smba; + int i; + u8 d[0x17]; + + for (i = 0; i < 0x17; ++i) { + if (i == 2 || i == 7) { + d[i] = 0; + } + else { + d[i] = inb_p(i + piix4_smba); + } + } + + dev_dbg(&piix4_adapter->dev, "ASF registers: %s %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\n", label, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23]); +} +*/ + +static int piix4_sb800_region_setup(struct device *dev, + struct sb800_mmio_cfg *mmio_cfg) +{ + if (mmio_cfg->use_mmio) { + struct resource *res; + void __iomem *addr; + + res = request_mem_region(SB800_PIIX4_FCH_PM_ADDR, + SB800_PIIX4_FCH_PM_SIZE, + "sb800_piix4_smb"); + if (!res) { + dev_err(dev, + "SMB base address memory region 0x%x already in use.\n", + SB800_PIIX4_FCH_PM_ADDR); + return -EBUSY; + } + + addr = ioremap(SB800_PIIX4_FCH_PM_ADDR, + SB800_PIIX4_FCH_PM_SIZE); + if (!addr) { + release_resource(res); + dev_err(dev, "SMB base address mapping failed.\n"); + return -ENOMEM; + } + + mmio_cfg->res = res; + mmio_cfg->addr = addr; + } else { + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, + SB800_PIIX4_SMB_MAP_SIZE, + "sb800_piix4_smb")) { + dev_err(dev, + "SMB base address index region 0x%x already in use.\n", + SB800_PIIX4_SMB_IDX); + return -EBUSY; + } + } + + return 0; +} + +static void piix4_sb800_region_release(struct device *dev, + struct sb800_mmio_cfg *mmio_cfg) +{ + if (mmio_cfg->use_mmio) { + iounmap(mmio_cfg->addr); + mmio_cfg->addr = NULL; + + release_resource(mmio_cfg->res); + mmio_cfg->res = NULL; + } else { + release_region(SB800_PIIX4_SMB_IDX, + SB800_PIIX4_SMB_MAP_SIZE); + } +} + +static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev) +{ + return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && + PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && + PIIX4_dev->revision >= AMD_PCI_SMBUS_REVISION_MMIO); +} + static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { @@ -263,12 +404,58 @@ static int piix4_setup(struct pci_dev *PIIX4_dev, return piix4_smba; } +static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, + u8 smb_en, + u8 aux, + u8 *smb_en_status, + unsigned short *piix4_smba) +{ + struct sb800_mmio_cfg mmio_cfg; + u8 smba_en_lo; + u8 smba_en_hi; + int retval; + + mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev); + + retval = piix4_sb800_region_setup(&PIIX4_dev->dev, &mmio_cfg); + if (retval) + return retval; + + if (mmio_cfg.use_mmio) { + iowrite32(ioread32(mmio_cfg.addr + 4) | SB800_PIIX4_FCH_PM_DECODEEN_MMIO_EN, + mmio_cfg.addr + 4); + + smba_en_lo = ioread8(mmio_cfg.addr); + smba_en_hi = ioread8(mmio_cfg.addr + 1); + } else { + outb_p(smb_en, SB800_PIIX4_SMB_IDX); + smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); + outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); + smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); + } + + piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); + + if (!smb_en) { + *smb_en_status = smba_en_lo & 0x10; + *piix4_smba = smba_en_hi << 8; + if (aux) + *piix4_smba |= 0x20; + } else { + *smb_en_status = smba_en_lo & 0x01; + *piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; + } + + return retval; +} + static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, const struct pci_device_id *id, u8 aux) { unsigned short piix4_smba; - u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel; + u8 smb_en, smb_en_status, port_sel; u8 i2ccfg, i2ccfg_offset = 0x10; + int retval; /* SB800 and later SMBus does not support forcing address */ if (force || force_addr) { @@ -290,29 +477,10 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, else smb_en = (aux) ? 0x28 : 0x2c; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) { - dev_err(&PIIX4_dev->dev, - "SMB base address index region 0x%x already in use.\n", - SB800_PIIX4_SMB_IDX); - return -EBUSY; - } - - outb_p(smb_en, SB800_PIIX4_SMB_IDX); - smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); - outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); - smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); - - release_region(SB800_PIIX4_SMB_IDX, 2); - - if (!smb_en) { - smb_en_status = smba_en_lo & 0x10; - piix4_smba = smba_en_hi << 8; - if (aux) - piix4_smba |= 0x20; - } else { - smb_en_status = smba_en_lo & 0x01; - piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; - } + retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, + aux, &smb_en_status, &piix4_smba); + if (retval) + return retval; if (!smb_en_status) { dev_err(&PIIX4_dev->dev, @@ -338,23 +506,18 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, } /* Request the SMBus I2C bus config region */ - if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { - dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " - "0x%x already in use!\n", piix4_smba + i2ccfg_offset); - release_region(piix4_smba, SMBIOSIZE); - return -EBUSY; - } - i2ccfg = inb_p(piix4_smba + i2ccfg_offset); - release_region(piix4_smba + i2ccfg_offset, 1); + if (!aux) { + i2ccfg = inb_p(piix4_smba + i2ccfg_offset); - if (i2ccfg & 1) - dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); - else - dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); + if (i2ccfg & 1) + dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); + else + dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); - dev_info(&PIIX4_dev->dev, - "SMBus Host Controller at 0x%x, revision %d\n", - piix4_smba, i2ccfg >> 4); + dev_info(&PIIX4_dev->dev, + "SMBus Host Controller at 0x%x, revision %d\n", + piix4_smba, i2ccfg >> 4); + } /* Find which register is used for port selection */ if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD || @@ -434,6 +597,101 @@ static int piix4_setup_aux(struct pci_dev *PIIX4_dev, return piix4_smba; } +static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; +static struct i2c_adapter *piix4_aux_adapter; +static int piix4_adapter_count; + + +static u8 read_asf_data_bank(struct i2c_adapter *piix4_adapter, u8 bank_number) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); + unsigned short piix4_smba = adapdata->smba; + u8 broadcast, addr, bank_sel; + + outb_p(bank_number << ASF_SET_READ_DATA_BANK_OFFSET, ASF_DATA_BANK_SEL); + bank_sel = inb_p(ASF_DATA_BANK_SEL); + inb_p(SMBHSTCNT); // reset DataIndex + broadcast = inb_p(SMBBLKDAT); + addr = inb_p(SMBBLKDAT); + + dev_dbg(&piix4_adapter->dev, "BankSel=%02x Data=%02x %02x\n", bank_sel, broadcast, addr); + + if (broadcast != 0x10) { + return 0; + } + + return addr; +} + + +static irqreturn_t piix4_isr(int irq, void *dev_id) +{ + struct i2c_adapter *piix4_adapter = (struct i2c_adapter *)dev_id; + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); + unsigned short piix4_smba = adapdata->smba; + + u8 host_status; + u8 bank_sel; + u8 asf_status; + u8 address[2] = {0x00, 0x00}; + u8 *current_address; + + inb_p(SMBHSTCNT); + host_status = inb_p(SMBHSTSTS); + + // Clear HostStatus Intr and complete waiting + if (host_status & ASF_HOST_INTR) { + outb_p(ASF_HOST_INTR, SMBHSTSTS); + + if (adapdata->completion) { + complete(adapdata->completion); // Notify caller + } + } + + current_address = &address[0]; + + bank_sel = inb_p(ASF_DATA_BANK_SEL); // DataBankSel + + if ((bank_sel & ASF_DATA_BANK_LAST_TOUCH) == 0) { // Last touched bank is 0 + if (bank_sel & ASF_DATA_BANK_1_FULL) { + *current_address = read_asf_data_bank(piix4_adapter, 1); + current_address++; + } + if (bank_sel & ASF_DATA_BANK_0_FULL) { + *current_address = read_asf_data_bank(piix4_adapter, 0); + } + } + else { // Last touched bank is 1 + if (bank_sel & ASF_DATA_BANK_0_FULL) { + *current_address = read_asf_data_bank(piix4_adapter, 0); + current_address++; + } + if (bank_sel & ASF_DATA_BANK_1_FULL) { + *current_address = read_asf_data_bank(piix4_adapter, 1); + } + } + + outb_p(bank_sel & (ASF_DATA_BANK_0_FULL | ASF_DATA_BANK_1_FULL), ASF_DATA_BANK_SEL); // Clear DataBankxFull + + // Trigger notifications + if (address[0] != 0x00) { + i2c_handle_smbus_host_notify(piix4_aux_adapter, address[0] >> 1); + } + if (address[1] != 0x00) { + i2c_handle_smbus_host_notify(piix4_aux_adapter, address[1] >> 1); + } + + // Clean ASFStatus SlaveIntr + asf_status = inb_p(ASF_STATUS); + if (asf_status & ASF_SLAVE_INTR) { + outb_p(ASF_SLAVE_INTR, (ASF_STATUS)); // ASFStatus SlaveIntr? (in doc 0x20) + } + + dev_dbg(&piix4_adapter->dev,"Interrupt HostStatus=%02x BankSel=%02x AsfStatus=%02x\n", host_status, bank_sel, asf_status); + + return IRQ_HANDLED; +} + static int piix4_transaction(struct i2c_adapter *piix4_adapter) { struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); @@ -463,20 +721,30 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter) /* start the transaction by setting bit 6 */ outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); - /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ - if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ - usleep_range(2000, 2100); - else - usleep_range(250, 500); - - while ((++timeout < MAX_TIMEOUT) && - ((temp = inb_p(SMBHSTSTS)) & 0x01)) - usleep_range(250, 500); - - /* If the SMBus is still busy, we give up */ - if (timeout == MAX_TIMEOUT) { - dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); - result = -ETIMEDOUT; + if (adapdata->completion) { + timeout = wait_for_completion_timeout(adapdata->completion, msecs_to_jiffies(100)); + if (timeout == 0) { + dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + temp = inb_p(SMBHSTSTS); + } + else { + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ + usleep_range(2000, 2100); + else + usleep_range(250, 500); + + while ((++timeout < MAX_TIMEOUT) && + ((temp = inb_p(SMBHSTSTS)) & 0x01)) + usleep_range(250, 500); + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) { + dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } } if (temp & 0x10) { @@ -560,6 +828,9 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr, if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) return -EINVAL; outb_p(len, SMBHSTDAT0); + if (is_amd_kerncz && adap == piix4_aux_adapter) { + outb_p(ASF_READ_HOST_DATA_BANK, ASF_DATA_BANK_SEL); // Set DataBankSel to host bank + } inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ for (i = 1; i <= len; i++) outb_p(data->block[i], SMBBLKDAT); @@ -591,8 +862,12 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr, break; case PIIX4_BLOCK_DATA: data->block[0] = inb_p(SMBHSTDAT0); - if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) { return -EPROTO; + } + if (is_amd_kerncz && adap == piix4_aux_adapter) { + outb_p(ASF_READ_HOST_DATA_BANK, ASF_DATA_BANK_SEL); + } inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ for (i = 1; i <= data->block[0]; i++) data->block[i] = inb_p(SMBBLKDAT); @@ -662,6 +937,29 @@ static void piix4_imc_wakeup(void) release_region(KERNCZ_IMC_IDX, 2); } +static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg) +{ + u8 smba_en_lo; + + if (mmio_cfg->use_mmio) { + smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800); + + if ((smba_en_lo & piix4_port_mask_sb800) != port) + iowrite8((smba_en_lo & ~piix4_port_mask_sb800) | port, + mmio_cfg->addr + piix4_port_sel_sb800); + } else { + outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); + smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); + + if ((smba_en_lo & piix4_port_mask_sb800) != port) + outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, + SB800_PIIX4_SMB_IDX + 1); + } + + return (smba_en_lo & piix4_port_mask_sb800); +} + + /* * Handles access to multiple SMBus ports on the SB800. * The port is selected by bits 2:1 of the smb_en register (0x2c). @@ -678,12 +976,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, unsigned short piix4_smba = adapdata->smba; int retries = MAX_TIMEOUT; int smbslvcnt; - u8 smba_en_lo; - u8 port; + u8 prev_port; int retval; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) - return -EBUSY; + retval = piix4_sb800_region_setup(&adap->dev, &adapdata->mmio_cfg); + if (retval) + return retval; /* Request the SMBUS semaphore, avoid conflicts with the IMC */ smbslvcnt = inb_p(SMBSLVCNT); @@ -738,18 +1036,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, } } - outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); - smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); - - port = adapdata->port; - if ((smba_en_lo & piix4_port_mask_sb800) != port) - outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, - SB800_PIIX4_SMB_IDX + 1); + prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg); retval = piix4_access(adap, addr, flags, read_write, command, size, data); - outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1); + piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg); /* Release the semaphore */ outb_p(smbslvcnt | 0x20, SMBSLVCNT); @@ -758,7 +1050,43 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, piix4_imc_wakeup(); release: - release_region(SB800_PIIX4_SMB_IDX, 2); + piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg); + return retval; +} + +static s32 piix4_access_asf(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + unsigned short piix4_smba = adapdata->smba; + int timeout = 0; + int retval = 0; + u8 temp; + + if (adapdata->completion) { + reinit_completion(adapdata->completion); + } + + // Acquire IMC semaphore + outb_p(ASF_HOST_SEMAPHORE, ASF_SEMAPHORE); + while ((++timeout < MAX_TIMEOUT) && (!((temp = inb_p(ASF_SEMAPHORE)) & ASF_HOST_SEMAPHORE))) { + usleep_range(250, 500); + } + if ((temp & ASF_HOST_SEMAPHORE) == 0) { + dev_dbg(&adap->dev, "Host semaphore not acquired\n"); + return -EBUSY; + } + + outb_p(inb_p(ASF_SLAVE_EN) | ASF_KILL_SLAVE, ASF_SLAVE_EN); // Kill slave + + retval = piix4_access(adap, addr, flags, read_write, command, size, data); + + outb_p(inb_p(ASF_SLAVE_EN) & (~ASF_KILL_SLAVE), ASF_SLAVE_EN); // Enable slave + + // Release semphore + outb_p(ASF_CLR_HOST_SEMAPHORE, ASF_SEMAPHORE); + return retval; } @@ -766,7 +1094,7 @@ static u32 piix4_func(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA; + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_HOST_NOTIFY; } static const struct i2c_algorithm smbus_algorithm = { @@ -779,6 +1107,11 @@ static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { .functionality = piix4_func, }; +static const struct i2c_algorithm piix4_smbus_algorithm_asf = { + .smbus_xfer = piix4_access_asf, + .functionality = piix4_func, +}; + static const struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, @@ -805,12 +1138,8 @@ static const struct pci_device_id piix4_ids[] = { MODULE_DEVICE_TABLE (pci, piix4_ids); -static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; -static struct i2c_adapter *piix4_aux_adapter; -static int piix4_adapter_count; - -static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, - bool sb800_main, u8 port, bool notify_imc, +static int piix4_add_adapter(struct pci_dev *dev, unsigned short piix4_smba, + bool sb800_main, int irq, u8 port, bool notify_imc, u8 hw_port_nr, const char *name, struct i2c_adapter **padap) { @@ -820,26 +1149,28 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, adap = kzalloc(sizeof(*adap), GFP_KERNEL); if (adap == NULL) { - release_region(smba, SMBIOSIZE); + release_region(piix4_smba, SMBIOSIZE); return -ENOMEM; } adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 - : &smbus_algorithm; + : &piix4_smbus_algorithm_asf; adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); if (adapdata == NULL) { kfree(adap); - release_region(smba, SMBIOSIZE); + release_region(piix4_smba, SMBIOSIZE); return -ENOMEM; } - adapdata->smba = smba; + adapdata->smba = piix4_smba; adapdata->sb800_main = sb800_main; adapdata->port = port << piix4_port_shift_sb800; adapdata->notify_imc = notify_imc; + adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev); + adapdata->completion = NULL; /* set up the sysfs linkage to our parent device */ adap->dev.parent = &dev->dev; @@ -851,7 +1182,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, } snprintf(adap->name, sizeof(adap->name), - "SMBus PIIX4 adapter%s at %04x", name, smba); + "SMBus PIIX4 adapter%s at %04x", name, piix4_smba); i2c_set_adapdata(adap, adapdata); @@ -859,11 +1190,29 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, if (retval) { kfree(adapdata); kfree(adap); - release_region(smba, SMBIOSIZE); + release_region(piix4_smba, SMBIOSIZE); return retval; } *padap = adap; + + if (irq >= 0) { + if (!devm_request_irq(&dev->dev, dev->irq, piix4_isr, IRQF_SHARED, "piix4_smbus", *padap)) { + adapdata->completion = kzalloc(sizeof(*adapdata->completion), GFP_KERNEL); + if (adapdata->completion == NULL) { + kfree(adapdata); + kfree(adap); + release_region(piix4_smba, SMBIOSIZE); + return -ENOMEM; + } + init_completion(adapdata->completion); + dev_info(&dev->dev, "SMBus using irq %d\n", dev->irq); + outb_p(inb_p(ASF_SLAVE_EN) | ASF_KILL_SLAVE, ASF_SLAVE_EN); // Kill slave + outb_p(inb_p(ASF_SLAVE_EN) & (~ASF_KILL_SLAVE), ASF_SLAVE_EN); // Enable slave + outb_p((0x08 << 1) | ASF_LISTEN_ADR_EN, ASF_LISTEN_ADR); // Listen SMBus broadcast + } + } + return 0; } @@ -885,7 +1234,7 @@ static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, for (port = 0; port < piix4_adapter_count; port++) { u8 hw_port_nr = port == 0 ? 0 : port + 1; - retval = piix4_add_adapter(dev, smba, true, port, notify_imc, + retval = piix4_add_adapter(dev, smba, true, -1, port, notify_imc, hw_port_nr, piix4_main_port_names_sb800[port], &piix4_main_adapters[port]); @@ -957,7 +1306,7 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) return retval; /* Try to register main SMBus adapter, give up if we can't */ - retval = piix4_add_adapter(dev, retval, false, 0, false, 0, + retval = piix4_add_adapter(dev, retval, false, 0, false, -1, 0, "", &piix4_main_adapters[0]); if (retval < 0) return retval; @@ -980,12 +1329,13 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS || dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) { retval = piix4_setup_sb800(dev, id, 1); + is_amd_kerncz = true; } if (retval > 0) { - /* Try to add the aux adapter if it exists, + /* try to add the aux adapter if it exists, * piix4_add_adapter will clean up if this fails */ - piix4_add_adapter(dev, retval, false, 0, false, 1, + piix4_add_adapter(dev, retval, false, 0, false, dev->irq, 1, is_sb800 ? piix4_aux_port_name_sb800 : "", &piix4_aux_adapter); } @@ -1001,6 +1351,9 @@ static void piix4_adap_remove(struct i2c_adapter *adap) i2c_del_adapter(adap); if (adapdata->port == (0 << piix4_port_shift_sb800)) release_region(adapdata->smba, SMBIOSIZE); + if (adapdata->completion) { + kfree(adapdata->completion); + } kfree(adapdata); kfree(adap); } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4537d1ea1..22d2375b7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -743,6 +743,16 @@ static void quirk_piix4_acpi(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, quirk_piix4_acpi); + + +static void quirk_piix4_amd(struct pci_dev *dev) { + printk(KERN_INFO "piix4 fixing interrupt line"); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 7); + dev->irq = 7; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, quirk_piix4_amd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, quirk_piix4_amd); + #define ICH_PMBASE 0x40 #define ICH_ACPI_CNTL 0x44 #define ICH4_ACPI_EN 0x10