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