162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and 462306a36Sopenharmony_ci Philip Edelbrock <phil@netroedge.com> and 562306a36Sopenharmony_ci Mark D. Studebaker <mdsxyz123@yahoo.com> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci*/ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci This is the driver for the SMB Host controller on 1162306a36Sopenharmony_ci Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci The M1543C is a South bridge for desktop systems. 1462306a36Sopenharmony_ci The M1533 is a South bridge for portable systems. 1562306a36Sopenharmony_ci They are part of the following ALI chipsets: 1662306a36Sopenharmony_ci "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge 1762306a36Sopenharmony_ci with AGP and 100MHz CPU Front Side bus 1862306a36Sopenharmony_ci "Aladdin V": Includes the M1541 Socket 7 North bridge 1962306a36Sopenharmony_ci with AGP and 100MHz CPU Front Side bus 2062306a36Sopenharmony_ci "Aladdin IV": Includes the M1541 Socket 7 North bridge 2162306a36Sopenharmony_ci with host bus up to 83.3 MHz. 2262306a36Sopenharmony_ci For an overview of these chips see http://www.acerlabs.com 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci The M1533/M1543C devices appear as FOUR separate devices 2562306a36Sopenharmony_ci on the PCI bus. An output of lspci will show something similar 2662306a36Sopenharmony_ci to the following: 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci 00:02.0 USB Controller: Acer Laboratories Inc. M5237 2962306a36Sopenharmony_ci 00:03.0 Bridge: Acer Laboratories Inc. M7101 3062306a36Sopenharmony_ci 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 3162306a36Sopenharmony_ci 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci The SMB controller is part of the 7101 device, which is an 3462306a36Sopenharmony_ci ACPI-compliant Power Management Unit (PMU). 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci The whole 7101 device has to be enabled for the SMB to work. 3762306a36Sopenharmony_ci You can't just enable the SMB alone. 3862306a36Sopenharmony_ci The SMB and the ACPI have separate I/O spaces. 3962306a36Sopenharmony_ci We make sure that the SMB is enabled. We leave the ACPI alone. 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci This driver controls the SMB Host only. 4262306a36Sopenharmony_ci The SMB Slave controller on the M15X3 is not enabled. 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci This driver does not use interrupts. 4562306a36Sopenharmony_ci*/ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <linux/module.h> 5062306a36Sopenharmony_ci#include <linux/pci.h> 5162306a36Sopenharmony_ci#include <linux/kernel.h> 5262306a36Sopenharmony_ci#include <linux/stddef.h> 5362306a36Sopenharmony_ci#include <linux/ioport.h> 5462306a36Sopenharmony_ci#include <linux/delay.h> 5562306a36Sopenharmony_ci#include <linux/i2c.h> 5662306a36Sopenharmony_ci#include <linux/acpi.h> 5762306a36Sopenharmony_ci#include <linux/io.h> 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* ALI15X3 SMBus address offsets */ 6062306a36Sopenharmony_ci#define SMBHSTSTS (0 + ali15x3_smba) 6162306a36Sopenharmony_ci#define SMBHSTCNT (1 + ali15x3_smba) 6262306a36Sopenharmony_ci#define SMBHSTSTART (2 + ali15x3_smba) 6362306a36Sopenharmony_ci#define SMBHSTCMD (7 + ali15x3_smba) 6462306a36Sopenharmony_ci#define SMBHSTADD (3 + ali15x3_smba) 6562306a36Sopenharmony_ci#define SMBHSTDAT0 (4 + ali15x3_smba) 6662306a36Sopenharmony_ci#define SMBHSTDAT1 (5 + ali15x3_smba) 6762306a36Sopenharmony_ci#define SMBBLKDAT (6 + ali15x3_smba) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* PCI Address Constants */ 7062306a36Sopenharmony_ci#define SMBCOM 0x004 7162306a36Sopenharmony_ci#define SMBBA 0x014 7262306a36Sopenharmony_ci#define SMBATPC 0x05B /* used to unlock xxxBA registers */ 7362306a36Sopenharmony_ci#define SMBHSTCFG 0x0E0 7462306a36Sopenharmony_ci#define SMBSLVC 0x0E1 7562306a36Sopenharmony_ci#define SMBCLK 0x0E2 7662306a36Sopenharmony_ci#define SMBREV 0x008 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Other settings */ 7962306a36Sopenharmony_ci#define MAX_TIMEOUT 200 /* times 1/100 sec */ 8062306a36Sopenharmony_ci#define ALI15X3_SMB_IOSIZE 32 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. 8362306a36Sopenharmony_ci We don't use these here. If the bases aren't set to some value we 8462306a36Sopenharmony_ci tell user to upgrade BIOS and we fail. 8562306a36Sopenharmony_ci*/ 8662306a36Sopenharmony_ci#define ALI15X3_SMB_DEFAULTBASE 0xE800 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* ALI15X3 address lock bits */ 8962306a36Sopenharmony_ci#define ALI15X3_LOCK 0x06 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* ALI15X3 command constants */ 9262306a36Sopenharmony_ci#define ALI15X3_ABORT 0x02 9362306a36Sopenharmony_ci#define ALI15X3_T_OUT 0x04 9462306a36Sopenharmony_ci#define ALI15X3_QUICK 0x00 9562306a36Sopenharmony_ci#define ALI15X3_BYTE 0x10 9662306a36Sopenharmony_ci#define ALI15X3_BYTE_DATA 0x20 9762306a36Sopenharmony_ci#define ALI15X3_WORD_DATA 0x30 9862306a36Sopenharmony_ci#define ALI15X3_BLOCK_DATA 0x40 9962306a36Sopenharmony_ci#define ALI15X3_BLOCK_CLR 0x80 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* ALI15X3 status register bits */ 10262306a36Sopenharmony_ci#define ALI15X3_STS_IDLE 0x04 10362306a36Sopenharmony_ci#define ALI15X3_STS_BUSY 0x08 10462306a36Sopenharmony_ci#define ALI15X3_STS_DONE 0x10 10562306a36Sopenharmony_ci#define ALI15X3_STS_DEV 0x20 /* device error */ 10662306a36Sopenharmony_ci#define ALI15X3_STS_COLL 0x40 /* collision or no response */ 10762306a36Sopenharmony_ci#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ 10862306a36Sopenharmony_ci#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* If force_addr is set to anything different from 0, we forcibly enable 11262306a36Sopenharmony_ci the device at the given address. */ 11362306a36Sopenharmony_cistatic u16 force_addr; 11462306a36Sopenharmony_cimodule_param_hw(force_addr, ushort, ioport, 0); 11562306a36Sopenharmony_ciMODULE_PARM_DESC(force_addr, 11662306a36Sopenharmony_ci "Initialize the base address of the i2c controller"); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct pci_driver ali15x3_driver; 11962306a36Sopenharmony_cistatic unsigned short ali15x3_smba; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int ali15x3_setup(struct pci_dev *ALI15X3_dev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci u16 a; 12462306a36Sopenharmony_ci unsigned char temp; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Check the following things: 12762306a36Sopenharmony_ci - SMB I/O address is initialized 12862306a36Sopenharmony_ci - Device is enabled 12962306a36Sopenharmony_ci - We can use the addresses 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Unlock the register. 13362306a36Sopenharmony_ci The data sheet says that the address registers are read-only 13462306a36Sopenharmony_ci if the lock bits are 1, but in fact the address registers 13562306a36Sopenharmony_ci are zero unless you clear the lock bits. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); 13862306a36Sopenharmony_ci if (temp & ALI15X3_LOCK) { 13962306a36Sopenharmony_ci temp &= ~ALI15X3_LOCK; 14062306a36Sopenharmony_ci pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Determine the address of the SMBus area */ 14462306a36Sopenharmony_ci pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); 14562306a36Sopenharmony_ci ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); 14662306a36Sopenharmony_ci if (ali15x3_smba == 0 && force_addr == 0) { 14762306a36Sopenharmony_ci dev_err(&ALI15X3_dev->dev, "ALI15X3_smb region uninitialized " 14862306a36Sopenharmony_ci "- upgrade BIOS or use force_addr=0xaddr\n"); 14962306a36Sopenharmony_ci return -ENODEV; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if(force_addr) 15362306a36Sopenharmony_ci ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (acpi_check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, 15662306a36Sopenharmony_ci ali15x3_driver.name)) 15762306a36Sopenharmony_ci return -EBUSY; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, 16062306a36Sopenharmony_ci ali15x3_driver.name)) { 16162306a36Sopenharmony_ci dev_err(&ALI15X3_dev->dev, 16262306a36Sopenharmony_ci "ALI15X3_smb region 0x%x already in use!\n", 16362306a36Sopenharmony_ci ali15x3_smba); 16462306a36Sopenharmony_ci return -ENODEV; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if(force_addr) { 16862306a36Sopenharmony_ci int ret; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n", 17162306a36Sopenharmony_ci ali15x3_smba); 17262306a36Sopenharmony_ci ret = pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba); 17362306a36Sopenharmony_ci if (ret != PCIBIOS_SUCCESSFUL) 17462306a36Sopenharmony_ci goto error; 17562306a36Sopenharmony_ci ret = pci_read_config_word(ALI15X3_dev, SMBBA, &a); 17662306a36Sopenharmony_ci if (ret != PCIBIOS_SUCCESSFUL) 17762306a36Sopenharmony_ci goto error; 17862306a36Sopenharmony_ci if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { 17962306a36Sopenharmony_ci /* make sure it works */ 18062306a36Sopenharmony_ci dev_err(&ALI15X3_dev->dev, 18162306a36Sopenharmony_ci "force address failed - not supported?\n"); 18262306a36Sopenharmony_ci goto error; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci /* check if whole device is enabled */ 18662306a36Sopenharmony_ci pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); 18762306a36Sopenharmony_ci if ((temp & 1) == 0) { 18862306a36Sopenharmony_ci dev_info(&ALI15X3_dev->dev, "enabling SMBus device\n"); 18962306a36Sopenharmony_ci pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Is SMB Host controller enabled? */ 19362306a36Sopenharmony_ci pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); 19462306a36Sopenharmony_ci if ((temp & 1) == 0) { 19562306a36Sopenharmony_ci dev_info(&ALI15X3_dev->dev, "enabling SMBus controller\n"); 19662306a36Sopenharmony_ci pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* set SMB clock to 74KHz as recommended in data sheet */ 20062306a36Sopenharmony_ci pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci The interrupt routing for SMB is set up in register 0x77 in the 20462306a36Sopenharmony_ci 1533 ISA Bridge device, NOT in the 7101 device. 20562306a36Sopenharmony_ci Don't bother with finding the 1533 device and reading the register. 20662306a36Sopenharmony_ci if ((....... & 0x0F) == 1) 20762306a36Sopenharmony_ci dev_dbg(&ALI15X3_dev->dev, "ALI15X3 using Interrupt 9 for SMBus.\n"); 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); 21062306a36Sopenharmony_ci dev_dbg(&ALI15X3_dev->dev, "SMBREV = 0x%X\n", temp); 21162306a36Sopenharmony_ci dev_dbg(&ALI15X3_dev->dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_cierror: 21562306a36Sopenharmony_ci release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); 21662306a36Sopenharmony_ci return -ENODEV; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* Another internally used function */ 22062306a36Sopenharmony_cistatic int ali15x3_transaction(struct i2c_adapter *adap) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci int temp; 22362306a36Sopenharmony_ci int result = 0; 22462306a36Sopenharmony_ci int timeout = 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, " 22762306a36Sopenharmony_ci "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), 22862306a36Sopenharmony_ci inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), 22962306a36Sopenharmony_ci inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* get status */ 23262306a36Sopenharmony_ci temp = inb_p(SMBHSTSTS); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 23562306a36Sopenharmony_ci /* Check the busy bit first */ 23662306a36Sopenharmony_ci if (temp & ALI15X3_STS_BUSY) { 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci If the host controller is still busy, it may have timed out in the 23962306a36Sopenharmony_ci previous transaction, resulting in a "SMBus Timeout" Dev. 24062306a36Sopenharmony_ci I've tried the following to reset a stuck busy bit. 24162306a36Sopenharmony_ci 1. Reset the controller with an ABORT command. 24262306a36Sopenharmony_ci (this doesn't seem to clear the controller if an external 24362306a36Sopenharmony_ci device is hung) 24462306a36Sopenharmony_ci 2. Reset the controller and the other SMBus devices with a 24562306a36Sopenharmony_ci T_OUT command. (this clears the host busy bit if an 24662306a36Sopenharmony_ci external device is hung, but it comes back upon a new access 24762306a36Sopenharmony_ci to a device) 24862306a36Sopenharmony_ci 3. Disable and reenable the controller in SMBHSTCFG 24962306a36Sopenharmony_ci Worst case, nothing seems to work except power reset. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci /* Abort - reset the host controller */ 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci Try resetting entire SMB bus, including other devices - 25462306a36Sopenharmony_ci This may not work either - it clears the BUSY bit but 25562306a36Sopenharmony_ci then the BUSY bit may come back on when you try and use the chip again. 25662306a36Sopenharmony_ci If that's the case you are stuck. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci dev_info(&adap->dev, "Resetting entire SMB Bus to " 25962306a36Sopenharmony_ci "clear busy condition (%02x)\n", temp); 26062306a36Sopenharmony_ci outb_p(ALI15X3_T_OUT, SMBHSTCNT); 26162306a36Sopenharmony_ci temp = inb_p(SMBHSTSTS); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* now check the error bits and the busy bit */ 26562306a36Sopenharmony_ci if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { 26662306a36Sopenharmony_ci /* do a clear-on-write */ 26762306a36Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 26862306a36Sopenharmony_ci if ((temp = inb_p(SMBHSTSTS)) & 26962306a36Sopenharmony_ci (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { 27062306a36Sopenharmony_ci /* this is probably going to be correctable only by a power reset 27162306a36Sopenharmony_ci as one of the bits now appears to be stuck */ 27262306a36Sopenharmony_ci /* This may be a bus or device with electrical problems. */ 27362306a36Sopenharmony_ci dev_err(&adap->dev, "SMBus reset failed! (0x%02x) - " 27462306a36Sopenharmony_ci "controller or device on bus is probably hung\n", 27562306a36Sopenharmony_ci temp); 27662306a36Sopenharmony_ci return -EBUSY; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci /* check and clear done bit */ 28062306a36Sopenharmony_ci if (temp & ALI15X3_STS_DONE) { 28162306a36Sopenharmony_ci outb_p(temp, SMBHSTSTS); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* start the transaction by writing anything to the start register */ 28662306a36Sopenharmony_ci outb_p(0xFF, SMBHSTSTART); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* We will always wait for a fraction of a second! */ 28962306a36Sopenharmony_ci timeout = 0; 29062306a36Sopenharmony_ci do { 29162306a36Sopenharmony_ci msleep(1); 29262306a36Sopenharmony_ci temp = inb_p(SMBHSTSTS); 29362306a36Sopenharmony_ci } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) 29462306a36Sopenharmony_ci && (timeout++ < MAX_TIMEOUT)); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* If the SMBus is still busy, we give up */ 29762306a36Sopenharmony_ci if (timeout > MAX_TIMEOUT) { 29862306a36Sopenharmony_ci result = -ETIMEDOUT; 29962306a36Sopenharmony_ci dev_err(&adap->dev, "SMBus Timeout!\n"); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (temp & ALI15X3_STS_TERM) { 30362306a36Sopenharmony_ci result = -EIO; 30462306a36Sopenharmony_ci dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci Unfortunately the ALI SMB controller maps "no response" and "bus 30962306a36Sopenharmony_ci collision" into a single bit. No response is the usual case so don't 31062306a36Sopenharmony_ci do a printk. 31162306a36Sopenharmony_ci This means that bus collisions go unreported. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci if (temp & ALI15X3_STS_COLL) { 31462306a36Sopenharmony_ci result = -ENXIO; 31562306a36Sopenharmony_ci dev_dbg(&adap->dev, 31662306a36Sopenharmony_ci "Error: no response or bus collision ADD=%02x\n", 31762306a36Sopenharmony_ci inb_p(SMBHSTADD)); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* haven't ever seen this */ 32162306a36Sopenharmony_ci if (temp & ALI15X3_STS_DEV) { 32262306a36Sopenharmony_ci result = -EIO; 32362306a36Sopenharmony_ci dev_err(&adap->dev, "Error: device error\n"); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci dev_dbg(&adap->dev, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, " 32662306a36Sopenharmony_ci "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), 32762306a36Sopenharmony_ci inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), 32862306a36Sopenharmony_ci inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); 32962306a36Sopenharmony_ci return result; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* Return negative errno on error. */ 33362306a36Sopenharmony_cistatic s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, 33462306a36Sopenharmony_ci unsigned short flags, char read_write, u8 command, 33562306a36Sopenharmony_ci int size, union i2c_smbus_data * data) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int i, len; 33862306a36Sopenharmony_ci int temp; 33962306a36Sopenharmony_ci int timeout; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* clear all the bits (clear-on-write) */ 34262306a36Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 34362306a36Sopenharmony_ci /* make sure SMBus is idle */ 34462306a36Sopenharmony_ci temp = inb_p(SMBHSTSTS); 34562306a36Sopenharmony_ci for (timeout = 0; 34662306a36Sopenharmony_ci (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); 34762306a36Sopenharmony_ci timeout++) { 34862306a36Sopenharmony_ci msleep(1); 34962306a36Sopenharmony_ci temp = inb_p(SMBHSTSTS); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci if (timeout >= MAX_TIMEOUT) { 35262306a36Sopenharmony_ci dev_err(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci switch (size) { 35662306a36Sopenharmony_ci case I2C_SMBUS_QUICK: 35762306a36Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 35862306a36Sopenharmony_ci SMBHSTADD); 35962306a36Sopenharmony_ci size = ALI15X3_QUICK; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci case I2C_SMBUS_BYTE: 36262306a36Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 36362306a36Sopenharmony_ci SMBHSTADD); 36462306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 36562306a36Sopenharmony_ci outb_p(command, SMBHSTCMD); 36662306a36Sopenharmony_ci size = ALI15X3_BYTE; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 36962306a36Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 37062306a36Sopenharmony_ci SMBHSTADD); 37162306a36Sopenharmony_ci outb_p(command, SMBHSTCMD); 37262306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 37362306a36Sopenharmony_ci outb_p(data->byte, SMBHSTDAT0); 37462306a36Sopenharmony_ci size = ALI15X3_BYTE_DATA; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 37762306a36Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 37862306a36Sopenharmony_ci SMBHSTADD); 37962306a36Sopenharmony_ci outb_p(command, SMBHSTCMD); 38062306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 38162306a36Sopenharmony_ci outb_p(data->word & 0xff, SMBHSTDAT0); 38262306a36Sopenharmony_ci outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci size = ALI15X3_WORD_DATA; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 38762306a36Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 38862306a36Sopenharmony_ci SMBHSTADD); 38962306a36Sopenharmony_ci outb_p(command, SMBHSTCMD); 39062306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 39162306a36Sopenharmony_ci len = data->block[0]; 39262306a36Sopenharmony_ci if (len < 0) { 39362306a36Sopenharmony_ci len = 0; 39462306a36Sopenharmony_ci data->block[0] = len; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci if (len > 32) { 39762306a36Sopenharmony_ci len = 32; 39862306a36Sopenharmony_ci data->block[0] = len; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci outb_p(len, SMBHSTDAT0); 40162306a36Sopenharmony_ci /* Reset SMBBLKDAT */ 40262306a36Sopenharmony_ci outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); 40362306a36Sopenharmony_ci for (i = 1; i <= len; i++) 40462306a36Sopenharmony_ci outb_p(data->block[i], SMBBLKDAT); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci size = ALI15X3_BLOCK_DATA; 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci default: 40962306a36Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 41062306a36Sopenharmony_ci return -EOPNOTSUPP; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci outb_p(size, SMBHSTCNT); /* output command */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci temp = ali15x3_transaction(adap); 41662306a36Sopenharmony_ci if (temp) 41762306a36Sopenharmony_ci return temp; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci switch (size) { 42462306a36Sopenharmony_ci case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ 42562306a36Sopenharmony_ci data->byte = inb_p(SMBHSTDAT0); 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci case ALI15X3_BYTE_DATA: 42862306a36Sopenharmony_ci data->byte = inb_p(SMBHSTDAT0); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case ALI15X3_WORD_DATA: 43162306a36Sopenharmony_ci data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case ALI15X3_BLOCK_DATA: 43462306a36Sopenharmony_ci len = inb_p(SMBHSTDAT0); 43562306a36Sopenharmony_ci if (len > 32) 43662306a36Sopenharmony_ci len = 32; 43762306a36Sopenharmony_ci data->block[0] = len; 43862306a36Sopenharmony_ci /* Reset SMBBLKDAT */ 43962306a36Sopenharmony_ci outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); 44062306a36Sopenharmony_ci for (i = 1; i <= data->block[0]; i++) { 44162306a36Sopenharmony_ci data->block[i] = inb_p(SMBBLKDAT); 44262306a36Sopenharmony_ci dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", 44362306a36Sopenharmony_ci len, i, data->block[i]); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic u32 ali15x3_func(struct i2c_adapter *adapter) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 45362306a36Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 45462306a36Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 45862306a36Sopenharmony_ci .smbus_xfer = ali15x3_access, 45962306a36Sopenharmony_ci .functionality = ali15x3_func, 46062306a36Sopenharmony_ci}; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic struct i2c_adapter ali15x3_adapter = { 46362306a36Sopenharmony_ci .owner = THIS_MODULE, 46462306a36Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 46562306a36Sopenharmony_ci .algo = &smbus_algorithm, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct pci_device_id ali15x3_ids[] = { 46962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, 47062306a36Sopenharmony_ci { 0, } 47162306a36Sopenharmony_ci}; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ciMODULE_DEVICE_TABLE (pci, ali15x3_ids); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci if (ali15x3_setup(dev)) { 47862306a36Sopenharmony_ci dev_err(&dev->dev, 47962306a36Sopenharmony_ci "ALI15X3 not detected, module not inserted.\n"); 48062306a36Sopenharmony_ci return -ENODEV; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 48462306a36Sopenharmony_ci ali15x3_adapter.dev.parent = &dev->dev; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci snprintf(ali15x3_adapter.name, sizeof(ali15x3_adapter.name), 48762306a36Sopenharmony_ci "SMBus ALI15X3 adapter at %04x", ali15x3_smba); 48862306a36Sopenharmony_ci return i2c_add_adapter(&ali15x3_adapter); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void ali15x3_remove(struct pci_dev *dev) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci i2c_del_adapter(&ali15x3_adapter); 49462306a36Sopenharmony_ci release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct pci_driver ali15x3_driver = { 49862306a36Sopenharmony_ci .name = "ali15x3_smbus", 49962306a36Sopenharmony_ci .id_table = ali15x3_ids, 50062306a36Sopenharmony_ci .probe = ali15x3_probe, 50162306a36Sopenharmony_ci .remove = ali15x3_remove, 50262306a36Sopenharmony_ci}; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cimodule_pci_driver(ali15x3_driver); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); 50762306a36Sopenharmony_ciMODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>"); 50862306a36Sopenharmony_ciMODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); 50962306a36Sopenharmony_ciMODULE_DESCRIPTION("ALI15X3 SMBus driver"); 51062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 511