/drivers/macintosh/

>logblamecommitdiffstats
path: root/drivers/i2c/busses/i2c-ali1563.c
blob: 6b68074e518a072d1239c1e1f8ba61287cc4b456 (plain) (tree)
1
2
3
4
5



                                                               
                                                              






















































                                                                           
                                    
 
                                            

                                   
                                                                











                                                                               
                                                                           



















                                                                               
 

                                                                            

                                                     











                                                                       
                                                        






                                                                









































                                                                               
                                                     



























































































                                                                                    
                                                                                          

                                        
 


                                         

                                                                       


















                                                                         
                                                   



























                                                                              




                                                        
 






                                                                    

















                                                                                

                                                             

                                                                              

                         
                                                                             










                                                        
                                                       





                                             
                                       









                                                                      
                          



                                                                     






                                                                        
















                                                                 
                                          



















                                                        
/**
 *	i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
 *
 *	Copyright (C) 2004 Patrick Mochel
 *		      2005 Rudolf Marek <r.marek@assembler.cz>
 *
 *	The 1563 southbridge is deceptively similar to the 1533, with a
 *	few notable exceptions. One of those happens to be the fact they
 *	upgraded the i2c core to be 2.0 compliant, and happens to be almost
 *	identical to the i2c controller found in the Intel 801 south
 *	bridges.
 *
 *	This driver is based on a mix of the 15x3, 1535, and i801 drivers,
 *	with a little help from the ALi 1563 spec.
 *
 *	This file is released under the GPLv2
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>

#define ALI1563_MAX_TIMEOUT	500
#define	ALI1563_SMBBA		0x80
#define ALI1563_SMB_IOEN	1
#define ALI1563_SMB_HOSTEN	2
#define ALI1563_SMB_IOSIZE	16

#define SMB_HST_STS	(ali1563_smba + 0)
#define SMB_HST_CNTL1	(ali1563_smba + 1)
#define SMB_HST_CNTL2	(ali1563_smba + 2)
#define SMB_HST_CMD	(ali1563_smba + 3)
#define SMB_HST_ADD	(ali1563_smba + 4)
#define SMB_HST_DAT0	(ali1563_smba + 5)
#define SMB_HST_DAT1	(ali1563_smba + 6)
#define SMB_BLK_DAT	(ali1563_smba + 7)

#define HST_STS_BUSY	0x01
#define HST_STS_INTR	0x02
#define HST_STS_DEVERR	0x04
#define HST_STS_BUSERR	0x08
#define HST_STS_FAIL	0x10
#define HST_STS_DONE	0x80
#define HST_STS_BAD	0x1c


#define HST_CNTL1_TIMEOUT	0x80
#define HST_CNTL1_LAST		0x40

#define HST_CNTL2_KILL		0x04
#define HST_CNTL2_START		0x40
#define HST_CNTL2_QUICK		0x00
#define HST_CNTL2_BYTE		0x01
#define HST_CNTL2_BYTE_DATA	0x02
#define HST_CNTL2_WORD_DATA	0x03
#define HST_CNTL2_BLOCK		0x05


#define HST_CNTL2_SIZEMASK	0x38

static struct pci_driver ali1563_pci_driver;
static unsigned short ali1563_smba;

static int ali1563_transaction(struct i2c_adapter * a, int size)
{
	u32 data;
	int timeout;

	dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	data = inb_p(SMB_HST_STS);
	if (data & HST_STS_BAD) {
		dev_err(&a->dev, "ali1563: Trying to reset busy device\n");
		outb_p(data | HST_STS_BAD,SMB_HST_STS);
		data = inb_p(SMB_HST_STS);
		if (data & HST_STS_BAD)
			return -EBUSY;
	}
	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);

	timeout = ALI1563_MAX_TIMEOUT;
	do
		msleep(1);
	while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout);

	dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	if (timeout && !(data & HST_STS_BAD))
		return 0;

	if (!timeout) {
		dev_err(&a->dev, "Timeout - Trying to KILL transaction!\n");
		/* Issue 'kill' to host controller */
		outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2);
		data = inb_p(SMB_HST_STS);
 	}

	/* device error - no response, ignore the autodetection case */
	if ((data & HST_STS_DEVERR) && (size != HST_CNTL2_QUICK)) {
		dev_err(&a->dev, "Device error!\n");
	}

	/* bus collision */
	if (data & HST_STS_BUSERR) {
		dev_err(&a->dev, "Bus collision!\n");
		/* Issue timeout, hoping it helps */
		outb_p(HST_CNTL1_TIMEOUT,SMB_HST_CNTL1);
	}

	if (data & HST_STS_FAIL) {
		dev_err(&a->dev, "Cleaning fail after KILL!\n");
		outb_p(0x0,SMB_HST_CNTL2);
	}

	return -1;
}

static int ali1563_block_start(struct i2c_adapter * a)
{
	u32 data;
	int timeout;

	dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	data = inb_p(SMB_HST_STS);
	if (data & HST_STS_BAD) {
		dev_warn(&a->dev,"ali1563: Trying to reset busy device\n");
		outb_p(data | HST_STS_BAD,SMB_HST_STS);
		data = inb_p(SMB_HST_STS);
		if (data & HST_STS_BAD)
			return -EBUSY;
	}

	/* Clear byte-ready bit */
	outb_p(data | HST_STS_DONE, SMB_HST_STS);

	/* Start transaction and wait for byte-ready bit to be set */
	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);

	timeout = ALI1563_MAX_TIMEOUT;
	do
		msleep(1);
	while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout);

	dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	if (timeout && !(data & HST_STS_BAD))
		return 0;
	dev_err(&a->dev, "SMBus Error: %s%s%s%s%s\n",
		timeout ? "Timeout " : "",
		data & HST_STS_FAIL ? "Transaction Failed " : "",
		data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
		data & HST_STS_DEVERR ? "Device Error " : "",
		!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
	return -1;
}

static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw)
{
	int i, len;
	int error = 0;

	/* Do we need this? */
	outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);

	if (rw == I2C_SMBUS_WRITE) {
		len = data->block[0];
		if (len < 1)
			len = 1;
		else if (len > 32)
			len = 32;
		outb_p(len,SMB_HST_DAT0);
		outb_p(data->block[1],SMB_BLK_DAT);
	} else
		len = 32;

	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2);

	for (i = 0; i < len; i++) {
		if (rw == I2C_SMBUS_WRITE) {
			outb_p(data->block[i + 1], SMB_BLK_DAT);
			if ((error = ali1563_block_start(a)))
				break;
		} else {
			if ((error = ali1563_block_start(a)))
				break;
			if (i == 0) {
				len = inb_p(SMB_HST_DAT0);
				if (len < 1)
					len = 1;
				else if (len > 32)