Convert the drivers in drivers/i2c/busses/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Ben Dooks <ben-linux@fluff.org> Acked-by: Jochen Friedrich <jochen@scram.de> Acked-by: Peter Korsgaard <jacmet@sunsite.dk> Acked-by: Wolfram Sang <w.sang@pengutronix.de> Cc: Manuel Lauss <manuel.lauss@googlemail.com> Cc: Barry Song <21cnbao@gmail.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Yong Zhang <yong.zhang0@gmail.com> Cc: Lucas De Marchi <lucas.demarchi@profusion.mobi> Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Axel Lin <axel.lin@gmail.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
		
			
				
	
	
		
			531 lines
		
	
	
	
		
			13 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			531 lines
		
	
	
	
		
			13 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/* ------------------------------------------------------------------------- */
 | 
						|
/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x       */
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd
 | 
						|
 *                    <Peter dot Milne at D hyphen TACQ dot com>
 | 
						|
 *
 | 
						|
 * With acknowledgements to i2c-algo-ibm_ocp.c by 
 | 
						|
 * Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com
 | 
						|
 *
 | 
						|
 * And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund:
 | 
						|
 *
 | 
						|
 * Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund
 | 
						|
 *  
 | 
						|
 * And which acknowledged Kyösti Mälkki <kmalkki@cc.hut.fi>,
 | 
						|
 * Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com>
 | 
						|
 *
 | 
						|
 * Major cleanup by Deepak Saxena <dsaxena@plexity.net>, 01/2005:
 | 
						|
 *
 | 
						|
 * - Use driver model to pass per-chip info instead of hardcoding and #ifdefs
 | 
						|
 * - Use ioremap/__raw_readl/__raw_writel instead of direct dereference
 | 
						|
 * - Make it work with IXP46x chips
 | 
						|
 * - Cleanup function names, coding style, etc
 | 
						|
 *
 | 
						|
 * - writing to slave address causes latchup on iop331.
 | 
						|
 *	fix: driver refuses to address self.
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation, version 2.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/errno.h>
 | 
						|
#include <linux/platform_device.h>
 | 
						|
#include <linux/i2c.h>
 | 
						|
#include <linux/io.h>
 | 
						|
 | 
						|
#include "i2c-iop3xx.h"
 | 
						|
 | 
						|
/* global unit counter */
 | 
						|
static int i2c_id;
 | 
						|
 | 
						|
static inline unsigned char 
 | 
						|
iic_cook_addr(struct i2c_msg *msg) 
 | 
						|
{
 | 
						|
	unsigned char addr;
 | 
						|
 | 
						|
	addr = (msg->addr << 1);
 | 
						|
 | 
						|
	if (msg->flags & I2C_M_RD)
 | 
						|
		addr |= 1;
 | 
						|
 | 
						|
	return addr;   
 | 
						|
}
 | 
						|
 | 
						|
static void 
 | 
						|
iop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap)
 | 
						|
{
 | 
						|
	/* Follows devman 9.3 */
 | 
						|
	__raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	__raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET);
 | 
						|
	__raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
} 
 | 
						|
 | 
						|
static void 
 | 
						|
iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
 | 
						|
{
 | 
						|
	u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE;
 | 
						|
 | 
						|
	/* 
 | 
						|
	 * Every time unit enable is asserted, GPOD needs to be cleared
 | 
						|
	 * on IOP3XX to avoid data corruption on the bus.
 | 
						|
	 */
 | 
						|
#if defined(CONFIG_ARCH_IOP32X) || defined(CONFIG_ARCH_IOP33X)
 | 
						|
	if (iop3xx_adap->id == 0) {
 | 
						|
		gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW);
 | 
						|
		gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW);
 | 
						|
	} else {
 | 
						|
		gpio_line_set(IOP3XX_GPIO_LINE(5), GPIO_LOW);
 | 
						|
		gpio_line_set(IOP3XX_GPIO_LINE(4), GPIO_LOW);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	/* NB SR bits not same position as CR IE bits :-( */
 | 
						|
	iop3xx_adap->SR_enabled = 
 | 
						|
		IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD |
 | 
						|
		IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY;
 | 
						|
 | 
						|
	cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
 | 
						|
		IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE;
 | 
						|
 | 
						|
	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
}
 | 
						|
 | 
						|
static void 
 | 
						|
iop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap)
 | 
						|
{
 | 
						|
	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	
 | 
						|
	cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE | 
 | 
						|
		IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN);
 | 
						|
 | 
						|
	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
 * NB: the handler has to clear the source of the interrupt! 
 | 
						|
 * Then it passes the SR flags of interest to BH via adap data
 | 
						|
 */
 | 
						|
static irqreturn_t 
 | 
						|
iop3xx_i2c_irq_handler(int this_irq, void *dev_id) 
 | 
						|
{
 | 
						|
	struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id;
 | 
						|
	u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET);
 | 
						|
 | 
						|
	if ((sr &= iop3xx_adap->SR_enabled)) {
 | 
						|
		__raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET);
 | 
						|
		iop3xx_adap->SR_received |= sr;
 | 
						|
		wake_up_interruptible(&iop3xx_adap->waitq);
 | 
						|
	}
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
/* check all error conditions, clear them , report most important */
 | 
						|
static int 
 | 
						|
iop3xx_i2c_error(u32 sr)
 | 
						|
{
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	if ((sr & IOP3XX_ISR_BERRD)) {
 | 
						|
		if ( !rc ) rc = -I2C_ERR_BERR;
 | 
						|
	}
 | 
						|
	if ((sr & IOP3XX_ISR_ALD)) {
 | 
						|
		if ( !rc ) rc = -I2C_ERR_ALD;		
 | 
						|
	}
 | 
						|
	return rc;	
 | 
						|
}
 | 
						|
 | 
						|
static inline u32 
 | 
						|
iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	u32 sr;
 | 
						|
 | 
						|
	spin_lock_irqsave(&iop3xx_adap->lock, flags);
 | 
						|
	sr = iop3xx_adap->SR_received;
 | 
						|
	iop3xx_adap->SR_received = 0;
 | 
						|
	spin_unlock_irqrestore(&iop3xx_adap->lock, flags);
 | 
						|
 | 
						|
	return sr;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sleep until interrupted, then recover and analyse the SR
 | 
						|
 * saved by handler
 | 
						|
 */
 | 
						|
typedef int (* compare_func)(unsigned test, unsigned mask);
 | 
						|
/* returns 1 on correct comparison */
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, 
 | 
						|
			  unsigned flags, unsigned* status,
 | 
						|
			  compare_func compare)
 | 
						|
{
 | 
						|
	unsigned sr = 0;
 | 
						|
	int interrupted;
 | 
						|
	int done;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	do {
 | 
						|
		interrupted = wait_event_interruptible_timeout (
 | 
						|
			iop3xx_adap->waitq,
 | 
						|
			(done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
 | 
						|
			1 * HZ;
 | 
						|
			);
 | 
						|
		if ((rc = iop3xx_i2c_error(sr)) < 0) {
 | 
						|
			*status = sr;
 | 
						|
			return rc;
 | 
						|
		} else if (!interrupted) {
 | 
						|
			*status = sr;
 | 
						|
			return -ETIMEDOUT;
 | 
						|
		}
 | 
						|
	} while(!done);
 | 
						|
 | 
						|
	*status = sr;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Concrete compare_funcs 
 | 
						|
 */
 | 
						|
static int 
 | 
						|
all_bits_clear(unsigned test, unsigned mask)
 | 
						|
{
 | 
						|
	return (test & mask) == 0;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
any_bits_set(unsigned test, unsigned mask)
 | 
						|
{
 | 
						|
	return (test & mask) != 0;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
 | 
						|
{
 | 
						|
	return iop3xx_i2c_wait_event( 
 | 
						|
		iop3xx_adap, 
 | 
						|
	        IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
 | 
						|
		status, any_bits_set);
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
 | 
						|
{
 | 
						|
	return iop3xx_i2c_wait_event( 
 | 
						|
		iop3xx_adap, 
 | 
						|
		IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
 | 
						|
		status,	any_bits_set);
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
 | 
						|
{
 | 
						|
	return iop3xx_i2c_wait_event( 
 | 
						|
		iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear);
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, 
 | 
						|
				struct i2c_msg* msg)
 | 
						|
{
 | 
						|
	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	int status;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	/* avoid writing to my slave address (hangs on 80331),
 | 
						|
	 * forbidden in Intel developer manual
 | 
						|
	 */
 | 
						|
	if (msg->addr == MYSAR) {
 | 
						|
		return -EBUSY;
 | 
						|
	}
 | 
						|
 | 
						|
	__raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET);
 | 
						|
	
 | 
						|
	cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
 | 
						|
	cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE;
 | 
						|
 | 
						|
	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, 
 | 
						|
				int stop)
 | 
						|
{
 | 
						|
	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	int status;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	__raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET);
 | 
						|
	cr &= ~IOP3XX_ICR_MSTART;
 | 
						|
	if (stop) {
 | 
						|
		cr |= IOP3XX_ICR_MSTOP;
 | 
						|
	} else {
 | 
						|
		cr &= ~IOP3XX_ICR_MSTOP;
 | 
						|
	}
 | 
						|
	cr |= IOP3XX_ICR_TBYTE;
 | 
						|
	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
 | 
						|
 | 
						|
	return rc;
 | 
						|
} 
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, 
 | 
						|
				int stop)
 | 
						|
{
 | 
						|
	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
	int status;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	cr &= ~IOP3XX_ICR_MSTART;
 | 
						|
 | 
						|
	if (stop) {
 | 
						|
		cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK;
 | 
						|
	} else {
 | 
						|
		cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
 | 
						|
	}
 | 
						|
	cr |= IOP3XX_ICR_TBYTE;
 | 
						|
	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
 | 
						|
 | 
						|
	rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status);
 | 
						|
 | 
						|
	*byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
 | 
						|
{
 | 
						|
	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
 | 
						|
	int ii;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	for (ii = 0; rc == 0 && ii != count; ++ii) 
 | 
						|
		rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
 | 
						|
{
 | 
						|
	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
 | 
						|
	int ii;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	for (ii = 0; rc == 0 && ii != count; ++ii)
 | 
						|
		rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
 | 
						|
	
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Description:  This function implements combined transactions.  Combined
 | 
						|
 * transactions consist of combinations of reading and writing blocks of data.
 | 
						|
 * FROM THE SAME ADDRESS
 | 
						|
 * Each transfer (i.e. a read or a write) is separated by a repeated start
 | 
						|
 * condition.
 | 
						|
 */
 | 
						|
static int 
 | 
						|
iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) 
 | 
						|
{
 | 
						|
	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg);
 | 
						|
	if (rc < 0) {
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((pmsg->flags&I2C_M_RD)) {
 | 
						|
		return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len);
 | 
						|
	} else {
 | 
						|
		return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * master_xfer() - main read/write entry
 | 
						|
 */
 | 
						|
static int 
 | 
						|
iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, 
 | 
						|
				int num)
 | 
						|
{
 | 
						|
	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
 | 
						|
	int im = 0;
 | 
						|
	int ret = 0;
 | 
						|
	int status;
 | 
						|
 | 
						|
	iop3xx_i2c_wait_idle(iop3xx_adap, &status);
 | 
						|
	iop3xx_i2c_reset(iop3xx_adap);
 | 
						|
	iop3xx_i2c_enable(iop3xx_adap);
 | 
						|
 | 
						|
	for (im = 0; ret == 0 && im != num; im++) {
 | 
						|
		ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]);
 | 
						|
	}
 | 
						|
 | 
						|
	iop3xx_i2c_transaction_cleanup(iop3xx_adap);
 | 
						|
	
 | 
						|
	if(ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return im;   
 | 
						|
}
 | 
						|
 | 
						|
static u32 
 | 
						|
iop3xx_i2c_func(struct i2c_adapter *adap)
 | 
						|
{
 | 
						|
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 | 
						|
}
 | 
						|
 | 
						|
static const struct i2c_algorithm iop3xx_i2c_algo = {
 | 
						|
	.master_xfer	= iop3xx_i2c_master_xfer,
 | 
						|
	.functionality	= iop3xx_i2c_func,
 | 
						|
};
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_remove(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	struct i2c_adapter *padapter = platform_get_drvdata(pdev);
 | 
						|
	struct i2c_algo_iop3xx_data *adapter_data = 
 | 
						|
		(struct i2c_algo_iop3xx_data *)padapter->algo_data;
 | 
						|
	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
	unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Disable the actual HW unit
 | 
						|
	 */
 | 
						|
	cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
 | 
						|
		IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE);
 | 
						|
	__raw_writel(cr, adapter_data->ioaddr + CR_OFFSET);
 | 
						|
 | 
						|
	iounmap(adapter_data->ioaddr);
 | 
						|
	release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
 | 
						|
	kfree(adapter_data);
 | 
						|
	kfree(padapter);
 | 
						|
 | 
						|
	platform_set_drvdata(pdev, NULL);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int 
 | 
						|
iop3xx_i2c_probe(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	struct resource *res;
 | 
						|
	int ret, irq;
 | 
						|
	struct i2c_adapter *new_adapter;
 | 
						|
	struct i2c_algo_iop3xx_data *adapter_data;
 | 
						|
 | 
						|
	new_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
 | 
						|
	if (!new_adapter) {
 | 
						|
		ret = -ENOMEM;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	adapter_data = kzalloc(sizeof(struct i2c_algo_iop3xx_data), GFP_KERNEL);
 | 
						|
	if (!adapter_data) {
 | 
						|
		ret = -ENOMEM;
 | 
						|
		goto free_adapter;
 | 
						|
	}
 | 
						|
 | 
						|
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
	if (!res) {
 | 
						|
		ret = -ENODEV;
 | 
						|
		goto free_both;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) {
 | 
						|
		ret = -EBUSY;
 | 
						|
		goto free_both;
 | 
						|
	}
 | 
						|
 | 
						|
	/* set the adapter enumeration # */
 | 
						|
	adapter_data->id = i2c_id++;
 | 
						|
 | 
						|
	adapter_data->ioaddr = ioremap(res->start, IOP3XX_I2C_IO_SIZE);
 | 
						|
	if (!adapter_data->ioaddr) {
 | 
						|
		ret = -ENOMEM;
 | 
						|
		goto release_region;
 | 
						|
	}
 | 
						|
 | 
						|
	irq = platform_get_irq(pdev, 0);
 | 
						|
	if (irq < 0) {
 | 
						|
		ret = -ENXIO;
 | 
						|
		goto unmap;
 | 
						|
	}
 | 
						|
	ret = request_irq(irq, iop3xx_i2c_irq_handler, 0,
 | 
						|
				pdev->name, adapter_data);
 | 
						|
 | 
						|
	if (ret) {
 | 
						|
		ret = -EIO;
 | 
						|
		goto unmap;
 | 
						|
	}
 | 
						|
 | 
						|
	memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
 | 
						|
	new_adapter->owner = THIS_MODULE;
 | 
						|
	new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 | 
						|
	new_adapter->dev.parent = &pdev->dev;
 | 
						|
	new_adapter->nr = pdev->id;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Default values...should these come in from board code?
 | 
						|
	 */
 | 
						|
	new_adapter->timeout = HZ;
 | 
						|
	new_adapter->algo = &iop3xx_i2c_algo;
 | 
						|
 | 
						|
	init_waitqueue_head(&adapter_data->waitq);
 | 
						|
	spin_lock_init(&adapter_data->lock);
 | 
						|
 | 
						|
	iop3xx_i2c_reset(adapter_data);
 | 
						|
	iop3xx_i2c_enable(adapter_data);
 | 
						|
 | 
						|
	platform_set_drvdata(pdev, new_adapter);
 | 
						|
	new_adapter->algo_data = adapter_data;
 | 
						|
 | 
						|
	i2c_add_numbered_adapter(new_adapter);
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
unmap:
 | 
						|
	iounmap(adapter_data->ioaddr);
 | 
						|
 | 
						|
release_region:
 | 
						|
	release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
 | 
						|
 | 
						|
free_both:
 | 
						|
	kfree(adapter_data);
 | 
						|
 | 
						|
free_adapter:
 | 
						|
	kfree(new_adapter);
 | 
						|
 | 
						|
out:
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static struct platform_driver iop3xx_i2c_driver = {
 | 
						|
	.probe		= iop3xx_i2c_probe,
 | 
						|
	.remove		= iop3xx_i2c_remove,
 | 
						|
	.driver		= {
 | 
						|
		.owner	= THIS_MODULE,
 | 
						|
		.name	= "IOP3xx-I2C",
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
module_platform_driver(iop3xx_i2c_driver);
 | 
						|
 | 
						|
MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>");
 | 
						|
MODULE_DESCRIPTION("IOP3xx iic algorithm and driver");
 | 
						|
MODULE_LICENSE("GPL");
 | 
						|
MODULE_ALIAS("platform:IOP3xx-I2C");
 |