18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SMBus 2.0 driver for AMD-8111 IO-Hub.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2002 Vojtech Pavlik
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/stddef.h>
128c2ecf20Sopenharmony_ci#include <linux/ioport.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/acpi.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
208c2ecf20Sopenharmony_ciMODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");
218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct amd_smbus {
248c2ecf20Sopenharmony_ci	struct pci_dev *dev;
258c2ecf20Sopenharmony_ci	struct i2c_adapter adapter;
268c2ecf20Sopenharmony_ci	int base;
278c2ecf20Sopenharmony_ci	int size;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct pci_driver amd8111_driver;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * AMD PCI control registers definitions.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define AMD_PCI_MISC	0x48
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_SCI	0x04	/* deliver SCI */
398c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_INT	0x02	/* deliver PCI IRQ */
408c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_SPEEDUP	0x01	/* 16x clock speedup */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 PCI interface definitions.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define AMD_EC_DATA	0x00	/* data register */
478c2ecf20Sopenharmony_ci#define AMD_EC_SC	0x04	/* status of controller */
488c2ecf20Sopenharmony_ci#define AMD_EC_CMD	0x04	/* command register */
498c2ecf20Sopenharmony_ci#define AMD_EC_ICR	0x08	/* interrupt control register */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define AMD_EC_SC_SMI	0x04	/* smi event pending */
528c2ecf20Sopenharmony_ci#define AMD_EC_SC_SCI	0x02	/* sci event pending */
538c2ecf20Sopenharmony_ci#define AMD_EC_SC_BURST	0x01	/* burst mode enabled */
548c2ecf20Sopenharmony_ci#define AMD_EC_SC_CMD	0x08	/* byte in data reg is command */
558c2ecf20Sopenharmony_ci#define AMD_EC_SC_IBF	0x02	/* data ready for embedded controller */
568c2ecf20Sopenharmony_ci#define AMD_EC_SC_OBF	0x01	/* data ready for host */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define AMD_EC_CMD_RD	0x80	/* read EC */
598c2ecf20Sopenharmony_ci#define AMD_EC_CMD_WR	0x81	/* write EC */
608c2ecf20Sopenharmony_ci#define AMD_EC_CMD_BE	0x82	/* enable burst mode */
618c2ecf20Sopenharmony_ci#define AMD_EC_CMD_BD	0x83	/* disable burst mode */
628c2ecf20Sopenharmony_ci#define AMD_EC_CMD_QR	0x84	/* query EC */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 access of registers of the EC
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int amd_ec_wait_write(struct amd_smbus *smbus)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int timeout = 500;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout)
738c2ecf20Sopenharmony_ci		udelay(1);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!timeout) {
768c2ecf20Sopenharmony_ci		dev_warn(&smbus->dev->dev,
778c2ecf20Sopenharmony_ci			 "Timeout while waiting for IBF to clear\n");
788c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int amd_ec_wait_read(struct amd_smbus *smbus)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int timeout = 500;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout)
898c2ecf20Sopenharmony_ci		udelay(1);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (!timeout) {
928c2ecf20Sopenharmony_ci		dev_warn(&smbus->dev->dev,
938c2ecf20Sopenharmony_ci			 "Timeout while waiting for OBF to set\n");
948c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int amd_ec_read(struct amd_smbus *smbus, unsigned char address,
1018c2ecf20Sopenharmony_ci		unsigned char *data)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int status;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	status = amd_ec_wait_write(smbus);
1068c2ecf20Sopenharmony_ci	if (status)
1078c2ecf20Sopenharmony_ci		return status;
1088c2ecf20Sopenharmony_ci	outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	status = amd_ec_wait_write(smbus);
1118c2ecf20Sopenharmony_ci	if (status)
1128c2ecf20Sopenharmony_ci		return status;
1138c2ecf20Sopenharmony_ci	outb(address, smbus->base + AMD_EC_DATA);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	status = amd_ec_wait_read(smbus);
1168c2ecf20Sopenharmony_ci	if (status)
1178c2ecf20Sopenharmony_ci		return status;
1188c2ecf20Sopenharmony_ci	*data = inb(smbus->base + AMD_EC_DATA);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int amd_ec_write(struct amd_smbus *smbus, unsigned char address,
1248c2ecf20Sopenharmony_ci		unsigned char data)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	int status;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	status = amd_ec_wait_write(smbus);
1298c2ecf20Sopenharmony_ci	if (status)
1308c2ecf20Sopenharmony_ci		return status;
1318c2ecf20Sopenharmony_ci	outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	status = amd_ec_wait_write(smbus);
1348c2ecf20Sopenharmony_ci	if (status)
1358c2ecf20Sopenharmony_ci		return status;
1368c2ecf20Sopenharmony_ci	outb(address, smbus->base + AMD_EC_DATA);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	status = amd_ec_wait_write(smbus);
1398c2ecf20Sopenharmony_ci	if (status)
1408c2ecf20Sopenharmony_ci		return status;
1418c2ecf20Sopenharmony_ci	outb(data, smbus->base + AMD_EC_DATA);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/*
1478c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL	0x00	/* protocol, PEC */
1518c2ecf20Sopenharmony_ci#define AMD_SMB_STS	0x01	/* status */
1528c2ecf20Sopenharmony_ci#define AMD_SMB_ADDR	0x02	/* address */
1538c2ecf20Sopenharmony_ci#define AMD_SMB_CMD	0x03	/* command */
1548c2ecf20Sopenharmony_ci#define AMD_SMB_DATA	0x04	/* 32 data registers */
1558c2ecf20Sopenharmony_ci#define AMD_SMB_BCNT	0x24	/* number of data bytes */
1568c2ecf20Sopenharmony_ci#define AMD_SMB_ALRM_A	0x25	/* alarm address */
1578c2ecf20Sopenharmony_ci#define AMD_SMB_ALRM_D	0x26	/* 2 bytes alarm data */
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci#define AMD_SMB_STS_DONE	0x80
1608c2ecf20Sopenharmony_ci#define AMD_SMB_STS_ALRM	0x40
1618c2ecf20Sopenharmony_ci#define AMD_SMB_STS_RES		0x20
1628c2ecf20Sopenharmony_ci#define AMD_SMB_STS_STATUS	0x1f
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_OK	0x00
1658c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_FAIL	0x07
1668c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_DNAK	0x10
1678c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_DERR	0x11
1688c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_CMD_DENY	0x12
1698c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_UNKNOWN	0x13
1708c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_ACC_DENY	0x17
1718c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_TIMEOUT	0x18
1728c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_NOTSUP	0x19
1738c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_BUSY	0x1A
1748c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_PEC	0x1F
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_WRITE		0x00
1778c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_READ		0x01
1788c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_QUICK		0x02
1798c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BYTE		0x04
1808c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BYTE_DATA		0x06
1818c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_WORD_DATA		0x08
1828c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BLOCK_DATA	0x0a
1838c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_PROC_CALL		0x0c
1848c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
1858c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_I2C_BLOCK_DATA	0x4a
1868c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_PEC		0x80
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
1908c2ecf20Sopenharmony_ci		unsigned short flags, char read_write, u8 command, int size,
1918c2ecf20Sopenharmony_ci		union i2c_smbus_data * data)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct amd_smbus *smbus = adap->algo_data;
1948c2ecf20Sopenharmony_ci	unsigned char protocol, len, pec, temp[2];
1958c2ecf20Sopenharmony_ci	int i, status;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ
1988c2ecf20Sopenharmony_ci						  : AMD_SMB_PRTCL_WRITE;
1998c2ecf20Sopenharmony_ci	pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	switch (size) {
2028c2ecf20Sopenharmony_ci		case I2C_SMBUS_QUICK:
2038c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_QUICK;
2048c2ecf20Sopenharmony_ci			read_write = I2C_SMBUS_WRITE;
2058c2ecf20Sopenharmony_ci			break;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE:
2088c2ecf20Sopenharmony_ci			if (read_write == I2C_SMBUS_WRITE) {
2098c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_CMD,
2108c2ecf20Sopenharmony_ci						      command);
2118c2ecf20Sopenharmony_ci				if (status)
2128c2ecf20Sopenharmony_ci					return status;
2138c2ecf20Sopenharmony_ci			}
2148c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_BYTE;
2158c2ecf20Sopenharmony_ci			break;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE_DATA:
2188c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
2198c2ecf20Sopenharmony_ci			if (status)
2208c2ecf20Sopenharmony_ci				return status;
2218c2ecf20Sopenharmony_ci			if (read_write == I2C_SMBUS_WRITE) {
2228c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_DATA,
2238c2ecf20Sopenharmony_ci						      data->byte);
2248c2ecf20Sopenharmony_ci				if (status)
2258c2ecf20Sopenharmony_ci					return status;
2268c2ecf20Sopenharmony_ci			}
2278c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_BYTE_DATA;
2288c2ecf20Sopenharmony_ci			break;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		case I2C_SMBUS_WORD_DATA:
2318c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
2328c2ecf20Sopenharmony_ci			if (status)
2338c2ecf20Sopenharmony_ci				return status;
2348c2ecf20Sopenharmony_ci			if (read_write == I2C_SMBUS_WRITE) {
2358c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_DATA,
2368c2ecf20Sopenharmony_ci						      data->word & 0xff);
2378c2ecf20Sopenharmony_ci				if (status)
2388c2ecf20Sopenharmony_ci					return status;
2398c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
2408c2ecf20Sopenharmony_ci						      data->word >> 8);
2418c2ecf20Sopenharmony_ci				if (status)
2428c2ecf20Sopenharmony_ci					return status;
2438c2ecf20Sopenharmony_ci			}
2448c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
2458c2ecf20Sopenharmony_ci			break;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_DATA:
2488c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
2498c2ecf20Sopenharmony_ci			if (status)
2508c2ecf20Sopenharmony_ci				return status;
2518c2ecf20Sopenharmony_ci			if (read_write == I2C_SMBUS_WRITE) {
2528c2ecf20Sopenharmony_ci				len = min_t(u8, data->block[0],
2538c2ecf20Sopenharmony_ci					    I2C_SMBUS_BLOCK_MAX);
2548c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
2558c2ecf20Sopenharmony_ci				if (status)
2568c2ecf20Sopenharmony_ci					return status;
2578c2ecf20Sopenharmony_ci				for (i = 0; i < len; i++) {
2588c2ecf20Sopenharmony_ci					status =
2598c2ecf20Sopenharmony_ci					  amd_ec_write(smbus, AMD_SMB_DATA + i,
2608c2ecf20Sopenharmony_ci						       data->block[i + 1]);
2618c2ecf20Sopenharmony_ci					if (status)
2628c2ecf20Sopenharmony_ci						return status;
2638c2ecf20Sopenharmony_ci				}
2648c2ecf20Sopenharmony_ci			}
2658c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		case I2C_SMBUS_I2C_BLOCK_DATA:
2698c2ecf20Sopenharmony_ci			len = min_t(u8, data->block[0],
2708c2ecf20Sopenharmony_ci				    I2C_SMBUS_BLOCK_MAX);
2718c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
2728c2ecf20Sopenharmony_ci			if (status)
2738c2ecf20Sopenharmony_ci				return status;
2748c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
2758c2ecf20Sopenharmony_ci			if (status)
2768c2ecf20Sopenharmony_ci				return status;
2778c2ecf20Sopenharmony_ci			if (read_write == I2C_SMBUS_WRITE)
2788c2ecf20Sopenharmony_ci				for (i = 0; i < len; i++) {
2798c2ecf20Sopenharmony_ci					status =
2808c2ecf20Sopenharmony_ci					  amd_ec_write(smbus, AMD_SMB_DATA + i,
2818c2ecf20Sopenharmony_ci						       data->block[i + 1]);
2828c2ecf20Sopenharmony_ci					if (status)
2838c2ecf20Sopenharmony_ci						return status;
2848c2ecf20Sopenharmony_ci				}
2858c2ecf20Sopenharmony_ci			protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		case I2C_SMBUS_PROC_CALL:
2898c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
2908c2ecf20Sopenharmony_ci			if (status)
2918c2ecf20Sopenharmony_ci				return status;
2928c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_DATA,
2938c2ecf20Sopenharmony_ci					      data->word & 0xff);
2948c2ecf20Sopenharmony_ci			if (status)
2958c2ecf20Sopenharmony_ci				return status;
2968c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
2978c2ecf20Sopenharmony_ci					      data->word >> 8);
2988c2ecf20Sopenharmony_ci			if (status)
2998c2ecf20Sopenharmony_ci				return status;
3008c2ecf20Sopenharmony_ci			protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
3018c2ecf20Sopenharmony_ci			read_write = I2C_SMBUS_READ;
3028c2ecf20Sopenharmony_ci			break;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_PROC_CALL:
3058c2ecf20Sopenharmony_ci			len = min_t(u8, data->block[0],
3068c2ecf20Sopenharmony_ci				    I2C_SMBUS_BLOCK_MAX - 1);
3078c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_CMD, command);
3088c2ecf20Sopenharmony_ci			if (status)
3098c2ecf20Sopenharmony_ci				return status;
3108c2ecf20Sopenharmony_ci			status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
3118c2ecf20Sopenharmony_ci			if (status)
3128c2ecf20Sopenharmony_ci				return status;
3138c2ecf20Sopenharmony_ci			for (i = 0; i < len; i++) {
3148c2ecf20Sopenharmony_ci				status = amd_ec_write(smbus, AMD_SMB_DATA + i,
3158c2ecf20Sopenharmony_ci						      data->block[i + 1]);
3168c2ecf20Sopenharmony_ci				if (status)
3178c2ecf20Sopenharmony_ci					return status;
3188c2ecf20Sopenharmony_ci			}
3198c2ecf20Sopenharmony_ci			protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
3208c2ecf20Sopenharmony_ci			read_write = I2C_SMBUS_READ;
3218c2ecf20Sopenharmony_ci			break;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		default:
3248c2ecf20Sopenharmony_ci			dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
3258c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
3298c2ecf20Sopenharmony_ci	if (status)
3308c2ecf20Sopenharmony_ci		return status;
3318c2ecf20Sopenharmony_ci	status = amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
3328c2ecf20Sopenharmony_ci	if (status)
3338c2ecf20Sopenharmony_ci		return status;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
3368c2ecf20Sopenharmony_ci	if (status)
3378c2ecf20Sopenharmony_ci		return status;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (~temp[0] & AMD_SMB_STS_DONE) {
3408c2ecf20Sopenharmony_ci		udelay(500);
3418c2ecf20Sopenharmony_ci		status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
3428c2ecf20Sopenharmony_ci		if (status)
3438c2ecf20Sopenharmony_ci			return status;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (~temp[0] & AMD_SMB_STS_DONE) {
3478c2ecf20Sopenharmony_ci		msleep(1);
3488c2ecf20Sopenharmony_ci		status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
3498c2ecf20Sopenharmony_ci		if (status)
3508c2ecf20Sopenharmony_ci			return status;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
3548c2ecf20Sopenharmony_ci		return -EIO;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (read_write == I2C_SMBUS_WRITE)
3578c2ecf20Sopenharmony_ci		return 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	switch (size) {
3608c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE:
3618c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE_DATA:
3628c2ecf20Sopenharmony_ci			status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
3638c2ecf20Sopenharmony_ci			if (status)
3648c2ecf20Sopenharmony_ci				return status;
3658c2ecf20Sopenharmony_ci			break;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		case I2C_SMBUS_WORD_DATA:
3688c2ecf20Sopenharmony_ci		case I2C_SMBUS_PROC_CALL:
3698c2ecf20Sopenharmony_ci			status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
3708c2ecf20Sopenharmony_ci			if (status)
3718c2ecf20Sopenharmony_ci				return status;
3728c2ecf20Sopenharmony_ci			status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
3738c2ecf20Sopenharmony_ci			if (status)
3748c2ecf20Sopenharmony_ci				return status;
3758c2ecf20Sopenharmony_ci			data->word = (temp[1] << 8) | temp[0];
3768c2ecf20Sopenharmony_ci			break;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_DATA:
3798c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_PROC_CALL:
3808c2ecf20Sopenharmony_ci			status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
3818c2ecf20Sopenharmony_ci			if (status)
3828c2ecf20Sopenharmony_ci				return status;
3838c2ecf20Sopenharmony_ci			len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
3848c2ecf20Sopenharmony_ci			fallthrough;
3858c2ecf20Sopenharmony_ci		case I2C_SMBUS_I2C_BLOCK_DATA:
3868c2ecf20Sopenharmony_ci			for (i = 0; i < len; i++) {
3878c2ecf20Sopenharmony_ci				status = amd_ec_read(smbus, AMD_SMB_DATA + i,
3888c2ecf20Sopenharmony_ci						     data->block + i + 1);
3898c2ecf20Sopenharmony_ci				if (status)
3908c2ecf20Sopenharmony_ci					return status;
3918c2ecf20Sopenharmony_ci			}
3928c2ecf20Sopenharmony_ci			data->block[0] = len;
3938c2ecf20Sopenharmony_ci			break;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic u32 amd8111_func(struct i2c_adapter *adapter)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	return	I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
4038c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_BYTE_DATA |
4048c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
4058c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
4068c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = {
4108c2ecf20Sopenharmony_ci	.smbus_xfer = amd8111_access,
4118c2ecf20Sopenharmony_ci	.functionality = amd8111_func,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic const struct pci_device_id amd8111_ids[] = {
4168c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) },
4178c2ecf20Sopenharmony_ci	{ 0, }
4188c2ecf20Sopenharmony_ci};
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, amd8111_ids);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct amd_smbus *smbus;
4258c2ecf20Sopenharmony_ci	int error;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
4288c2ecf20Sopenharmony_ci		return -ENODEV;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	smbus = kzalloc(sizeof(struct amd_smbus), GFP_KERNEL);
4318c2ecf20Sopenharmony_ci	if (!smbus)
4328c2ecf20Sopenharmony_ci		return -ENOMEM;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	smbus->dev = dev;
4358c2ecf20Sopenharmony_ci	smbus->base = pci_resource_start(dev, 0);
4368c2ecf20Sopenharmony_ci	smbus->size = pci_resource_len(dev, 0);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	error = acpi_check_resource_conflict(&dev->resource[0]);
4398c2ecf20Sopenharmony_ci	if (error) {
4408c2ecf20Sopenharmony_ci		error = -ENODEV;
4418c2ecf20Sopenharmony_ci		goto out_kfree;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) {
4458c2ecf20Sopenharmony_ci		error = -EBUSY;
4468c2ecf20Sopenharmony_ci		goto out_kfree;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	smbus->adapter.owner = THIS_MODULE;
4508c2ecf20Sopenharmony_ci	snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
4518c2ecf20Sopenharmony_ci		"SMBus2 AMD8111 adapter at %04x", smbus->base);
4528c2ecf20Sopenharmony_ci	smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
4538c2ecf20Sopenharmony_ci	smbus->adapter.algo = &smbus_algorithm;
4548c2ecf20Sopenharmony_ci	smbus->adapter.algo_data = smbus;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* set up the sysfs linkage to our parent device */
4578c2ecf20Sopenharmony_ci	smbus->adapter.dev.parent = &dev->dev;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);
4608c2ecf20Sopenharmony_ci	error = i2c_add_adapter(&smbus->adapter);
4618c2ecf20Sopenharmony_ci	if (error)
4628c2ecf20Sopenharmony_ci		goto out_release_region;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, smbus);
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci out_release_region:
4688c2ecf20Sopenharmony_ci	release_region(smbus->base, smbus->size);
4698c2ecf20Sopenharmony_ci out_kfree:
4708c2ecf20Sopenharmony_ci	kfree(smbus);
4718c2ecf20Sopenharmony_ci	return error;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic void amd8111_remove(struct pci_dev *dev)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct amd_smbus *smbus = pci_get_drvdata(dev);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	i2c_del_adapter(&smbus->adapter);
4798c2ecf20Sopenharmony_ci	release_region(smbus->base, smbus->size);
4808c2ecf20Sopenharmony_ci	kfree(smbus);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic struct pci_driver amd8111_driver = {
4848c2ecf20Sopenharmony_ci	.name		= "amd8111_smbus2",
4858c2ecf20Sopenharmony_ci	.id_table	= amd8111_ids,
4868c2ecf20Sopenharmony_ci	.probe		= amd8111_probe,
4878c2ecf20Sopenharmony_ci	.remove		= amd8111_remove,
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cimodule_pci_driver(amd8111_driver);
491