brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 42a9b12 Raw
491 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * SMBus 2.0 driver for AMD-8111 IO-Hub.4 *5 * Copyright (c) 2002 Vojtech Pavlik6 */7 8#include <linux/module.h>9#include <linux/pci.h>10#include <linux/kernel.h>11#include <linux/stddef.h>12#include <linux/ioport.h>13#include <linux/i2c.h>14#include <linux/delay.h>15#include <linux/acpi.h>16#include <linux/slab.h>17#include <linux/io.h>18 19MODULE_LICENSE("GPL");20MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");21MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");22 23struct amd_smbus {24	struct pci_dev *dev;25	struct i2c_adapter adapter;26	int base;27	int size;28};29 30static struct pci_driver amd8111_driver;31 32/*33 * AMD PCI control registers definitions.34 */35 36#define AMD_PCI_MISC	0x4837 38#define AMD_PCI_MISC_SCI	0x04	/* deliver SCI */39#define AMD_PCI_MISC_INT	0x02	/* deliver PCI IRQ */40#define AMD_PCI_MISC_SPEEDUP	0x01	/* 16x clock speedup */41 42/*43 * ACPI 2.0 chapter 13 PCI interface definitions.44 */45 46#define AMD_EC_DATA	0x00	/* data register */47#define AMD_EC_SC	0x04	/* status of controller */48#define AMD_EC_CMD	0x04	/* command register */49#define AMD_EC_ICR	0x08	/* interrupt control register */50 51#define AMD_EC_SC_SMI	0x04	/* smi event pending */52#define AMD_EC_SC_SCI	0x02	/* sci event pending */53#define AMD_EC_SC_BURST	0x01	/* burst mode enabled */54#define AMD_EC_SC_CMD	0x08	/* byte in data reg is command */55#define AMD_EC_SC_IBF	0x02	/* data ready for embedded controller */56#define AMD_EC_SC_OBF	0x01	/* data ready for host */57 58#define AMD_EC_CMD_RD	0x80	/* read EC */59#define AMD_EC_CMD_WR	0x81	/* write EC */60#define AMD_EC_CMD_BE	0x82	/* enable burst mode */61#define AMD_EC_CMD_BD	0x83	/* disable burst mode */62#define AMD_EC_CMD_QR	0x84	/* query EC */63 64/*65 * ACPI 2.0 chapter 13 access of registers of the EC66 */67 68static int amd_ec_wait_write(struct amd_smbus *smbus)69{70	int timeout = 500;71 72	while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout)73		udelay(1);74 75	if (!timeout) {76		dev_warn(&smbus->dev->dev,77			 "Timeout while waiting for IBF to clear\n");78		return -ETIMEDOUT;79	}80 81	return 0;82}83 84static int amd_ec_wait_read(struct amd_smbus *smbus)85{86	int timeout = 500;87 88	while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout)89		udelay(1);90 91	if (!timeout) {92		dev_warn(&smbus->dev->dev,93			 "Timeout while waiting for OBF to set\n");94		return -ETIMEDOUT;95	}96 97	return 0;98}99 100static int amd_ec_read(struct amd_smbus *smbus, unsigned char address,101		unsigned char *data)102{103	int status;104 105	status = amd_ec_wait_write(smbus);106	if (status)107		return status;108	outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);109 110	status = amd_ec_wait_write(smbus);111	if (status)112		return status;113	outb(address, smbus->base + AMD_EC_DATA);114 115	status = amd_ec_wait_read(smbus);116	if (status)117		return status;118	*data = inb(smbus->base + AMD_EC_DATA);119 120	return 0;121}122 123static int amd_ec_write(struct amd_smbus *smbus, unsigned char address,124		unsigned char data)125{126	int status;127 128	status = amd_ec_wait_write(smbus);129	if (status)130		return status;131	outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);132 133	status = amd_ec_wait_write(smbus);134	if (status)135		return status;136	outb(address, smbus->base + AMD_EC_DATA);137 138	status = amd_ec_wait_write(smbus);139	if (status)140		return status;141	outb(data, smbus->base + AMD_EC_DATA);142 143	return 0;144}145 146/*147 * ACPI 2.0 chapter 13 SMBus 2.0 EC register model148 */149 150#define AMD_SMB_PRTCL	0x00	/* protocol, PEC */151#define AMD_SMB_STS	0x01	/* status */152#define AMD_SMB_ADDR	0x02	/* address */153#define AMD_SMB_CMD	0x03	/* command */154#define AMD_SMB_DATA	0x04	/* 32 data registers */155#define AMD_SMB_BCNT	0x24	/* number of data bytes */156#define AMD_SMB_ALRM_A	0x25	/* alarm address */157#define AMD_SMB_ALRM_D	0x26	/* 2 bytes alarm data */158 159#define AMD_SMB_STS_DONE	0x80160#define AMD_SMB_STS_ALRM	0x40161#define AMD_SMB_STS_RES		0x20162#define AMD_SMB_STS_STATUS	0x1f163 164#define AMD_SMB_STATUS_OK	0x00165#define AMD_SMB_STATUS_FAIL	0x07166#define AMD_SMB_STATUS_DNAK	0x10167#define AMD_SMB_STATUS_DERR	0x11168#define AMD_SMB_STATUS_CMD_DENY	0x12169#define AMD_SMB_STATUS_UNKNOWN	0x13170#define AMD_SMB_STATUS_ACC_DENY	0x17171#define AMD_SMB_STATUS_TIMEOUT	0x18172#define AMD_SMB_STATUS_NOTSUP	0x19173#define AMD_SMB_STATUS_BUSY	0x1A174#define AMD_SMB_STATUS_PEC	0x1F175 176#define AMD_SMB_PRTCL_WRITE		0x00177#define AMD_SMB_PRTCL_READ		0x01178#define AMD_SMB_PRTCL_QUICK		0x02179#define AMD_SMB_PRTCL_BYTE		0x04180#define AMD_SMB_PRTCL_BYTE_DATA		0x06181#define AMD_SMB_PRTCL_WORD_DATA		0x08182#define AMD_SMB_PRTCL_BLOCK_DATA	0x0a183#define AMD_SMB_PRTCL_PROC_CALL		0x0c184#define AMD_SMB_PRTCL_BLOCK_PROC_CALL	0x0d185#define AMD_SMB_PRTCL_I2C_BLOCK_DATA	0x4a186#define AMD_SMB_PRTCL_PEC		0x80187 188 189static s32 amd8111_access(struct i2c_adapter *adap, u16 addr,190		unsigned short flags, char read_write, u8 command, int size,191		union i2c_smbus_data *data)192{193	struct amd_smbus *smbus = adap->algo_data;194	unsigned char protocol, len, pec, temp[2];195	int i, status;196 197	protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ198						  : AMD_SMB_PRTCL_WRITE;199	pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;200 201	switch (size) {202	case I2C_SMBUS_QUICK:203		protocol |= AMD_SMB_PRTCL_QUICK;204		read_write = I2C_SMBUS_WRITE;205		break;206 207	case I2C_SMBUS_BYTE:208		if (read_write == I2C_SMBUS_WRITE) {209			status = amd_ec_write(smbus, AMD_SMB_CMD,210						command);211			if (status)212				return status;213		}214		protocol |= AMD_SMB_PRTCL_BYTE;215		break;216 217	case I2C_SMBUS_BYTE_DATA:218		status = amd_ec_write(smbus, AMD_SMB_CMD, command);219		if (status)220			return status;221		if (read_write == I2C_SMBUS_WRITE) {222			status = amd_ec_write(smbus, AMD_SMB_DATA,223						data->byte);224			if (status)225				return status;226		}227		protocol |= AMD_SMB_PRTCL_BYTE_DATA;228		break;229 230	case I2C_SMBUS_WORD_DATA:231		status = amd_ec_write(smbus, AMD_SMB_CMD, command);232		if (status)233			return status;234		if (read_write == I2C_SMBUS_WRITE) {235			status = amd_ec_write(smbus, AMD_SMB_DATA,236						data->word & 0xff);237			if (status)238				return status;239			status = amd_ec_write(smbus, AMD_SMB_DATA + 1,240						data->word >> 8);241			if (status)242				return status;243		}244		protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;245		break;246 247	case I2C_SMBUS_BLOCK_DATA:248		status = amd_ec_write(smbus, AMD_SMB_CMD, command);249		if (status)250			return status;251		if (read_write == I2C_SMBUS_WRITE) {252			len = min_t(u8, data->block[0],253					I2C_SMBUS_BLOCK_MAX);254			status = amd_ec_write(smbus, AMD_SMB_BCNT, len);255			if (status)256				return status;257			for (i = 0; i < len; i++) {258				status =259					amd_ec_write(smbus, AMD_SMB_DATA + i,260						data->block[i + 1]);261				if (status)262					return status;263			}264		}265		protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;266		break;267 268	case I2C_SMBUS_I2C_BLOCK_DATA:269		len = min_t(u8, data->block[0],270				I2C_SMBUS_BLOCK_MAX);271		status = amd_ec_write(smbus, AMD_SMB_CMD, command);272		if (status)273			return status;274		status = amd_ec_write(smbus, AMD_SMB_BCNT, len);275		if (status)276			return status;277		if (read_write == I2C_SMBUS_WRITE)278			for (i = 0; i < len; i++) {279				status =280					amd_ec_write(smbus, AMD_SMB_DATA + i,281						data->block[i + 1]);282				if (status)283					return status;284			}285		protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;286		break;287 288	case I2C_SMBUS_PROC_CALL:289		status = amd_ec_write(smbus, AMD_SMB_CMD, command);290		if (status)291			return status;292		status = amd_ec_write(smbus, AMD_SMB_DATA,293					data->word & 0xff);294		if (status)295			return status;296		status = amd_ec_write(smbus, AMD_SMB_DATA + 1,297					data->word >> 8);298		if (status)299			return status;300		protocol = AMD_SMB_PRTCL_PROC_CALL | pec;301		read_write = I2C_SMBUS_READ;302		break;303 304	case I2C_SMBUS_BLOCK_PROC_CALL:305		len = min_t(u8, data->block[0],306				I2C_SMBUS_BLOCK_MAX - 1);307		status = amd_ec_write(smbus, AMD_SMB_CMD, command);308		if (status)309			return status;310		status = amd_ec_write(smbus, AMD_SMB_BCNT, len);311		if (status)312			return status;313		for (i = 0; i < len; i++) {314			status = amd_ec_write(smbus, AMD_SMB_DATA + i,315						data->block[i + 1]);316			if (status)317				return status;318		}319		protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;320		read_write = I2C_SMBUS_READ;321		break;322 323	default:324		dev_warn(&adap->dev, "Unsupported transaction %d\n", size);325		return -EOPNOTSUPP;326	}327 328	status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);329	if (status)330		return status;331	status = amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);332	if (status)333		return status;334 335	status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);336	if (status)337		return status;338 339	if (~temp[0] & AMD_SMB_STS_DONE) {340		udelay(500);341		status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);342		if (status)343			return status;344	}345 346	if (~temp[0] & AMD_SMB_STS_DONE) {347		msleep(1);348		status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0);349		if (status)350			return status;351	}352 353	if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))354		return -EIO;355 356	if (read_write == I2C_SMBUS_WRITE)357		return 0;358 359	switch (size) {360	case I2C_SMBUS_BYTE:361	case I2C_SMBUS_BYTE_DATA:362		status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);363		if (status)364			return status;365		break;366 367	case I2C_SMBUS_WORD_DATA:368	case I2C_SMBUS_PROC_CALL:369		status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);370		if (status)371			return status;372		status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);373		if (status)374			return status;375		data->word = (temp[1] << 8) | temp[0];376		break;377 378	case I2C_SMBUS_BLOCK_DATA:379	case I2C_SMBUS_BLOCK_PROC_CALL:380		status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);381		if (status)382			return status;383		len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);384		fallthrough;385	case I2C_SMBUS_I2C_BLOCK_DATA:386		for (i = 0; i < len; i++) {387			status = amd_ec_read(smbus, AMD_SMB_DATA + i,388						data->block + i + 1);389			if (status)390				return status;391		}392		data->block[0] = len;393		break;394	}395 396	return 0;397}398 399 400static u32 amd8111_func(struct i2c_adapter *adapter)401{402	return	I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |403		I2C_FUNC_SMBUS_BYTE_DATA |404		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |405		I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |406		I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC;407}408 409static const struct i2c_algorithm smbus_algorithm = {410	.smbus_xfer = amd8111_access,411	.functionality = amd8111_func,412};413 414 415static const struct pci_device_id amd8111_ids[] = {416	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) },417	{ 0, }418};419 420MODULE_DEVICE_TABLE (pci, amd8111_ids);421 422static int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)423{424	struct amd_smbus *smbus;425	int error;426 427	if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))428		return -ENODEV;429 430	smbus = kzalloc(sizeof(struct amd_smbus), GFP_KERNEL);431	if (!smbus)432		return -ENOMEM;433 434	smbus->dev = dev;435	smbus->base = pci_resource_start(dev, 0);436	smbus->size = pci_resource_len(dev, 0);437 438	error = acpi_check_resource_conflict(&dev->resource[0]);439	if (error) {440		error = -ENODEV;441		goto out_kfree;442	}443 444	if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) {445		error = -EBUSY;446		goto out_kfree;447	}448 449	smbus->adapter.owner = THIS_MODULE;450	snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),451		"SMBus2 AMD8111 adapter at %04x", smbus->base);452	smbus->adapter.class = I2C_CLASS_HWMON;453	smbus->adapter.algo = &smbus_algorithm;454	smbus->adapter.algo_data = smbus;455 456	/* set up the sysfs linkage to our parent device */457	smbus->adapter.dev.parent = &dev->dev;458 459	pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);460	error = i2c_add_adapter(&smbus->adapter);461	if (error)462		goto out_release_region;463 464	pci_set_drvdata(dev, smbus);465	return 0;466 467 out_release_region:468	release_region(smbus->base, smbus->size);469 out_kfree:470	kfree(smbus);471	return error;472}473 474static void amd8111_remove(struct pci_dev *dev)475{476	struct amd_smbus *smbus = pci_get_drvdata(dev);477 478	i2c_del_adapter(&smbus->adapter);479	release_region(smbus->base, smbus->size);480	kfree(smbus);481}482 483static struct pci_driver amd8111_driver = {484	.name		= "amd8111_smbus2",485	.id_table	= amd8111_ids,486	.probe		= amd8111_probe,487	.remove		= amd8111_remove,488};489 490module_pci_driver(amd8111_driver);491