18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
48c2ecf20Sopenharmony_ci    Philip Edelbrock <phil@netroedge.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci*/
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* Note: we assume there can only be one SIS5595 with one SMBus interface */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci   Note: all have mfr. ID 0x1039.
128c2ecf20Sopenharmony_ci   SUPPORTED		PCI ID
138c2ecf20Sopenharmony_ci	5595		0008
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci   Note: these chips contain a 0008 device which is incompatible with the
168c2ecf20Sopenharmony_ci         5595. We recognize these by the presence of the listed
178c2ecf20Sopenharmony_ci         "blacklist" PCI ID and refuse to load.
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci   NOT SUPPORTED	PCI ID		BLACKLIST PCI ID
208c2ecf20Sopenharmony_ci	 540		0008		0540
218c2ecf20Sopenharmony_ci	 550		0008		0550
228c2ecf20Sopenharmony_ci	5513		0008		5511
238c2ecf20Sopenharmony_ci	5581		0008		5597
248c2ecf20Sopenharmony_ci	5582		0008		5597
258c2ecf20Sopenharmony_ci	5597		0008		5597
268c2ecf20Sopenharmony_ci	5598		0008		5597/5598
278c2ecf20Sopenharmony_ci	 630		0008		0630
288c2ecf20Sopenharmony_ci	 645		0008		0645
298c2ecf20Sopenharmony_ci	 646		0008		0646
308c2ecf20Sopenharmony_ci	 648		0008		0648
318c2ecf20Sopenharmony_ci	 650		0008		0650
328c2ecf20Sopenharmony_ci	 651		0008		0651
338c2ecf20Sopenharmony_ci	 730		0008		0730
348c2ecf20Sopenharmony_ci	 735		0008		0735
358c2ecf20Sopenharmony_ci	 745		0008		0745
368c2ecf20Sopenharmony_ci	 746		0008		0746
378c2ecf20Sopenharmony_ci*/
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* TO DO:
408c2ecf20Sopenharmony_ci * Add Block Transfers (ugly, but supported by the adapter)
418c2ecf20Sopenharmony_ci * Add adapter resets
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <linux/kernel.h>
458c2ecf20Sopenharmony_ci#include <linux/module.h>
468c2ecf20Sopenharmony_ci#include <linux/delay.h>
478c2ecf20Sopenharmony_ci#include <linux/pci.h>
488c2ecf20Sopenharmony_ci#include <linux/ioport.h>
498c2ecf20Sopenharmony_ci#include <linux/init.h>
508c2ecf20Sopenharmony_ci#include <linux/i2c.h>
518c2ecf20Sopenharmony_ci#include <linux/acpi.h>
528c2ecf20Sopenharmony_ci#include <linux/io.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int blacklist[] = {
558c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_540,
568c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_550,
578c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_630,
588c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_645,
598c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_646,
608c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_648,
618c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_650,
628c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_651,
638c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_730,
648c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_735,
658c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_745,
668c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_746,
678c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_5511,	/* 5513 chip has the 0008 device but that ID
688c2ecf20Sopenharmony_ci				   shows up in other chips so we use the 5511
698c2ecf20Sopenharmony_ci				   ID for recognition */
708c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_5597,
718c2ecf20Sopenharmony_ci	PCI_DEVICE_ID_SI_5598,
728c2ecf20Sopenharmony_ci	0,			/* terminates the list */
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* Length of ISA address segment */
768c2ecf20Sopenharmony_ci#define SIS5595_EXTENT		8
778c2ecf20Sopenharmony_ci/* SIS5595 SMBus registers */
788c2ecf20Sopenharmony_ci#define SMB_STS_LO		0x00
798c2ecf20Sopenharmony_ci#define SMB_STS_HI		0x01
808c2ecf20Sopenharmony_ci#define SMB_CTL_LO		0x02
818c2ecf20Sopenharmony_ci#define SMB_CTL_HI		0x03
828c2ecf20Sopenharmony_ci#define SMB_ADDR		0x04
838c2ecf20Sopenharmony_ci#define SMB_CMD			0x05
848c2ecf20Sopenharmony_ci#define SMB_PCNT		0x06
858c2ecf20Sopenharmony_ci#define SMB_CNT			0x07
868c2ecf20Sopenharmony_ci#define SMB_BYTE		0x08
878c2ecf20Sopenharmony_ci#define SMB_DEV			0x10
888c2ecf20Sopenharmony_ci#define SMB_DB0			0x11
898c2ecf20Sopenharmony_ci#define SMB_DB1			0x12
908c2ecf20Sopenharmony_ci#define SMB_HAA			0x13
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* PCI Address Constants */
938c2ecf20Sopenharmony_ci#define SMB_INDEX		0x38
948c2ecf20Sopenharmony_ci#define SMB_DAT			0x39
958c2ecf20Sopenharmony_ci#define SIS5595_ENABLE_REG	0x40
968c2ecf20Sopenharmony_ci#define ACPI_BASE		0x90
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* Other settings */
998c2ecf20Sopenharmony_ci#define MAX_TIMEOUT		500
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* SIS5595 constants */
1028c2ecf20Sopenharmony_ci#define SIS5595_QUICK		0x00
1038c2ecf20Sopenharmony_ci#define SIS5595_BYTE		0x02
1048c2ecf20Sopenharmony_ci#define SIS5595_BYTE_DATA	0x04
1058c2ecf20Sopenharmony_ci#define SIS5595_WORD_DATA	0x06
1068c2ecf20Sopenharmony_ci#define SIS5595_PROC_CALL	0x08
1078c2ecf20Sopenharmony_ci#define SIS5595_BLOCK_DATA	0x0A
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* insmod parameters */
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* If force_addr is set to anything different from 0, we forcibly enable
1128c2ecf20Sopenharmony_ci   the device at the given address. */
1138c2ecf20Sopenharmony_cistatic u16 force_addr;
1148c2ecf20Sopenharmony_cimodule_param_hw(force_addr, ushort, ioport, 0);
1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller");
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct pci_driver sis5595_driver;
1188c2ecf20Sopenharmony_cistatic unsigned short sis5595_base;
1198c2ecf20Sopenharmony_cistatic struct pci_dev *sis5595_pdev;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic u8 sis5595_read(u8 reg)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	outb(reg, sis5595_base + SMB_INDEX);
1248c2ecf20Sopenharmony_ci	return inb(sis5595_base + SMB_DAT);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic void sis5595_write(u8 reg, u8 data)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	outb(reg, sis5595_base + SMB_INDEX);
1308c2ecf20Sopenharmony_ci	outb(data, sis5595_base + SMB_DAT);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int sis5595_setup(struct pci_dev *SIS5595_dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	u16 a;
1368c2ecf20Sopenharmony_ci	u8 val;
1378c2ecf20Sopenharmony_ci	int *i;
1388c2ecf20Sopenharmony_ci	int retval;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Look for imposters */
1418c2ecf20Sopenharmony_ci	for (i = blacklist; *i != 0; i++) {
1428c2ecf20Sopenharmony_ci		struct pci_dev *dev;
1438c2ecf20Sopenharmony_ci		dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
1448c2ecf20Sopenharmony_ci		if (dev) {
1458c2ecf20Sopenharmony_ci			dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
1468c2ecf20Sopenharmony_ci			pci_dev_put(dev);
1478c2ecf20Sopenharmony_ci			return -ENODEV;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Determine the address of the SMBus areas */
1528c2ecf20Sopenharmony_ci	pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
1538c2ecf20Sopenharmony_ci	if (sis5595_base == 0 && force_addr == 0) {
1548c2ecf20Sopenharmony_ci		dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
1558c2ecf20Sopenharmony_ci		return -ENODEV;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (force_addr)
1598c2ecf20Sopenharmony_ci		sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
1608c2ecf20Sopenharmony_ci	dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* NB: We grab just the two SMBus registers here, but this may still
1638c2ecf20Sopenharmony_ci	 * interfere with ACPI :-(  */
1648c2ecf20Sopenharmony_ci	retval = acpi_check_region(sis5595_base + SMB_INDEX, 2,
1658c2ecf20Sopenharmony_ci				   sis5595_driver.name);
1668c2ecf20Sopenharmony_ci	if (retval)
1678c2ecf20Sopenharmony_ci		return retval;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!request_region(sis5595_base + SMB_INDEX, 2,
1708c2ecf20Sopenharmony_ci			    sis5595_driver.name)) {
1718c2ecf20Sopenharmony_ci		dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n",
1728c2ecf20Sopenharmony_ci			sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1);
1738c2ecf20Sopenharmony_ci		return -ENODEV;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (force_addr) {
1778c2ecf20Sopenharmony_ci		dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base);
1788c2ecf20Sopenharmony_ci		if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)
1798c2ecf20Sopenharmony_ci		    != PCIBIOS_SUCCESSFUL)
1808c2ecf20Sopenharmony_ci			goto error;
1818c2ecf20Sopenharmony_ci		if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)
1828c2ecf20Sopenharmony_ci		    != PCIBIOS_SUCCESSFUL)
1838c2ecf20Sopenharmony_ci			goto error;
1848c2ecf20Sopenharmony_ci		if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
1858c2ecf20Sopenharmony_ci			/* doesn't work for some chips! */
1868c2ecf20Sopenharmony_ci			dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n");
1878c2ecf20Sopenharmony_ci			goto error;
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
1928c2ecf20Sopenharmony_ci	    != PCIBIOS_SUCCESSFUL)
1938c2ecf20Sopenharmony_ci		goto error;
1948c2ecf20Sopenharmony_ci	if ((val & 0x80) == 0) {
1958c2ecf20Sopenharmony_ci		dev_info(&SIS5595_dev->dev, "enabling ACPI\n");
1968c2ecf20Sopenharmony_ci		if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80)
1978c2ecf20Sopenharmony_ci		    != PCIBIOS_SUCCESSFUL)
1988c2ecf20Sopenharmony_ci			goto error;
1998c2ecf20Sopenharmony_ci		if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
2008c2ecf20Sopenharmony_ci		    != PCIBIOS_SUCCESSFUL)
2018c2ecf20Sopenharmony_ci			goto error;
2028c2ecf20Sopenharmony_ci		if ((val & 0x80) == 0) {
2038c2ecf20Sopenharmony_ci			/* doesn't work for some chips? */
2048c2ecf20Sopenharmony_ci			dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n");
2058c2ecf20Sopenharmony_ci			goto error;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Everything is happy */
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cierror:
2138c2ecf20Sopenharmony_ci	release_region(sis5595_base + SMB_INDEX, 2);
2148c2ecf20Sopenharmony_ci	return -ENODEV;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int sis5595_transaction(struct i2c_adapter *adap)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int temp;
2208c2ecf20Sopenharmony_ci	int result = 0;
2218c2ecf20Sopenharmony_ci	int timeout = 0;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Make sure the SMBus host is ready to start transmitting */
2248c2ecf20Sopenharmony_ci	temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
2258c2ecf20Sopenharmony_ci	if (temp != 0x00) {
2268c2ecf20Sopenharmony_ci		dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp);
2278c2ecf20Sopenharmony_ci		sis5595_write(SMB_STS_LO, temp & 0xff);
2288c2ecf20Sopenharmony_ci		sis5595_write(SMB_STS_HI, temp >> 8);
2298c2ecf20Sopenharmony_ci		if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
2308c2ecf20Sopenharmony_ci			dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
2318c2ecf20Sopenharmony_ci			return -EBUSY;
2328c2ecf20Sopenharmony_ci		} else {
2338c2ecf20Sopenharmony_ci			dev_dbg(&adap->dev, "Successful!\n");
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* start the transaction by setting bit 4 */
2388c2ecf20Sopenharmony_ci	sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* We will always wait for a fraction of a second! */
2418c2ecf20Sopenharmony_ci	do {
2428c2ecf20Sopenharmony_ci		msleep(1);
2438c2ecf20Sopenharmony_ci		temp = sis5595_read(SMB_STS_LO);
2448c2ecf20Sopenharmony_ci	} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* If the SMBus is still busy, we give up */
2478c2ecf20Sopenharmony_ci	if (timeout > MAX_TIMEOUT) {
2488c2ecf20Sopenharmony_ci		dev_dbg(&adap->dev, "SMBus Timeout!\n");
2498c2ecf20Sopenharmony_ci		result = -ETIMEDOUT;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (temp & 0x10) {
2538c2ecf20Sopenharmony_ci		dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
2548c2ecf20Sopenharmony_ci		result = -ENXIO;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (temp & 0x20) {
2588c2ecf20Sopenharmony_ci		dev_err(&adap->dev, "Bus collision! SMBus may be locked until "
2598c2ecf20Sopenharmony_ci			"next hard reset (or not...)\n");
2608c2ecf20Sopenharmony_ci		/* Clock stops and slave is stuck in mid-transmission */
2618c2ecf20Sopenharmony_ci		result = -EIO;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
2658c2ecf20Sopenharmony_ci	if (temp != 0x00) {
2668c2ecf20Sopenharmony_ci		sis5595_write(SMB_STS_LO, temp & 0xff);
2678c2ecf20Sopenharmony_ci		sis5595_write(SMB_STS_HI, temp >> 8);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
2718c2ecf20Sopenharmony_ci	if (temp != 0x00)
2728c2ecf20Sopenharmony_ci		dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return result;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/* Return negative errno on error. */
2788c2ecf20Sopenharmony_cistatic s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
2798c2ecf20Sopenharmony_ci			  unsigned short flags, char read_write,
2808c2ecf20Sopenharmony_ci			  u8 command, int size, union i2c_smbus_data *data)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int status;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	switch (size) {
2858c2ecf20Sopenharmony_ci	case I2C_SMBUS_QUICK:
2868c2ecf20Sopenharmony_ci		sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
2878c2ecf20Sopenharmony_ci		size = SIS5595_QUICK;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE:
2908c2ecf20Sopenharmony_ci		sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
2918c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_WRITE)
2928c2ecf20Sopenharmony_ci			sis5595_write(SMB_CMD, command);
2938c2ecf20Sopenharmony_ci		size = SIS5595_BYTE;
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE_DATA:
2968c2ecf20Sopenharmony_ci		sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
2978c2ecf20Sopenharmony_ci		sis5595_write(SMB_CMD, command);
2988c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_WRITE)
2998c2ecf20Sopenharmony_ci			sis5595_write(SMB_BYTE, data->byte);
3008c2ecf20Sopenharmony_ci		size = SIS5595_BYTE_DATA;
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	case I2C_SMBUS_PROC_CALL:
3038c2ecf20Sopenharmony_ci	case I2C_SMBUS_WORD_DATA:
3048c2ecf20Sopenharmony_ci		sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
3058c2ecf20Sopenharmony_ci		sis5595_write(SMB_CMD, command);
3068c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_WRITE) {
3078c2ecf20Sopenharmony_ci			sis5595_write(SMB_BYTE, data->word & 0xff);
3088c2ecf20Sopenharmony_ci			sis5595_write(SMB_BYTE + 1,
3098c2ecf20Sopenharmony_ci				      (data->word & 0xff00) >> 8);
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci		size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA;
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	default:
3148c2ecf20Sopenharmony_ci		dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
3158c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	status = sis5595_transaction(adap);
3218c2ecf20Sopenharmony_ci	if (status)
3228c2ecf20Sopenharmony_ci		return status;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if ((size != SIS5595_PROC_CALL) &&
3258c2ecf20Sopenharmony_ci	    ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
3268c2ecf20Sopenharmony_ci		return 0;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	switch (size) {
3308c2ecf20Sopenharmony_ci	case SIS5595_BYTE:
3318c2ecf20Sopenharmony_ci	case SIS5595_BYTE_DATA:
3328c2ecf20Sopenharmony_ci		data->byte = sis5595_read(SMB_BYTE);
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case SIS5595_WORD_DATA:
3358c2ecf20Sopenharmony_ci	case SIS5595_PROC_CALL:
3368c2ecf20Sopenharmony_ci		data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8);
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic u32 sis5595_func(struct i2c_adapter *adapter)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
3458c2ecf20Sopenharmony_ci	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
3468c2ecf20Sopenharmony_ci	    I2C_FUNC_SMBUS_PROC_CALL;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = {
3508c2ecf20Sopenharmony_ci	.smbus_xfer	= sis5595_access,
3518c2ecf20Sopenharmony_ci	.functionality	= sis5595_func,
3528c2ecf20Sopenharmony_ci};
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic struct i2c_adapter sis5595_adapter = {
3558c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3568c2ecf20Sopenharmony_ci	.class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
3578c2ecf20Sopenharmony_ci	.algo		= &smbus_algorithm,
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic const struct pci_device_id sis5595_ids[] = {
3618c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
3628c2ecf20Sopenharmony_ci	{ 0, }
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, sis5595_ids);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	int err;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (sis5595_setup(dev)) {
3728c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
3738c2ecf20Sopenharmony_ci		return -ENODEV;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* set up the sysfs linkage to our parent device */
3778c2ecf20Sopenharmony_ci	sis5595_adapter.dev.parent = &dev->dev;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name),
3808c2ecf20Sopenharmony_ci		 "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX);
3818c2ecf20Sopenharmony_ci	err = i2c_add_adapter(&sis5595_adapter);
3828c2ecf20Sopenharmony_ci	if (err) {
3838c2ecf20Sopenharmony_ci		release_region(sis5595_base + SMB_INDEX, 2);
3848c2ecf20Sopenharmony_ci		return err;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Always return failure here.  This is to allow other drivers to bind
3888c2ecf20Sopenharmony_ci	 * to this pci device.  We don't really want to have control over the
3898c2ecf20Sopenharmony_ci	 * pci device, we only wanted to read as few register values from it.
3908c2ecf20Sopenharmony_ci	 */
3918c2ecf20Sopenharmony_ci	sis5595_pdev =  pci_dev_get(dev);
3928c2ecf20Sopenharmony_ci	return -ENODEV;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic struct pci_driver sis5595_driver = {
3968c2ecf20Sopenharmony_ci	.name		= "sis5595_smbus",
3978c2ecf20Sopenharmony_ci	.id_table	= sis5595_ids,
3988c2ecf20Sopenharmony_ci	.probe		= sis5595_probe,
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int __init i2c_sis5595_init(void)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	return pci_register_driver(&sis5595_driver);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void __exit i2c_sis5595_exit(void)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	pci_unregister_driver(&sis5595_driver);
4098c2ecf20Sopenharmony_ci	if (sis5595_pdev) {
4108c2ecf20Sopenharmony_ci		i2c_del_adapter(&sis5595_adapter);
4118c2ecf20Sopenharmony_ci		release_region(sis5595_base + SMB_INDEX, 2);
4128c2ecf20Sopenharmony_ci		pci_dev_put(sis5595_pdev);
4138c2ecf20Sopenharmony_ci		sis5595_pdev = NULL;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
4188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SIS5595 SMBus driver");
4198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cimodule_init(i2c_sis5595_init);
4228c2ecf20Sopenharmony_cimodule_exit(i2c_sis5595_exit);
423