Newer
Older
/*
* drivers/i2c/busses/i2c-bfin-twi.c
*
* Description: Driver for Blackfin Two Wire Interface
*
* Author: sonicz <sonic.zhang@analog.com>
*
* Copyright (c) 2005-2007 Analog Devices, Inc.
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/blackfin.h>
#include <asm/irq.h>
#define POLL_TIMEOUT (2 * HZ)
/* SMBus mode*/
Sonic Zhang
committed
#define TWI_I2C_MODE_STANDARD 1
#define TWI_I2C_MODE_STANDARDSUB 2
#define TWI_I2C_MODE_COMBINED 3
#define TWI_I2C_MODE_REPEAT 4
struct bfin_twi_iface {
int irq;
spinlock_t lock;
char read_write;
u8 command;
u8 *transPtr;
int readNum;
int writeNum;
int cur_mode;
int manual_stop;
int result;
int timeout_count;
struct timer_list timeout_timer;
struct i2c_adapter adap;
struct completion complete;
Sonic Zhang
committed
struct i2c_msg *pmsg;
int msg_num;
int cur_msg;
Bryan Wu
committed
void __iomem *regs_base;
Bryan Wu
committed
#define DEFINE_TWI_REG(reg, off) \
static inline u16 read_##reg(struct bfin_twi_iface *iface) \
{ return bfin_read16(iface->regs_base + (off)); } \
static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \
{ bfin_write16(iface->regs_base + (off), v); }
DEFINE_TWI_REG(CLKDIV, 0x00)
DEFINE_TWI_REG(CONTROL, 0x04)
DEFINE_TWI_REG(SLAVE_CTL, 0x08)
DEFINE_TWI_REG(SLAVE_STAT, 0x0C)
DEFINE_TWI_REG(SLAVE_ADDR, 0x10)
DEFINE_TWI_REG(MASTER_CTL, 0x14)
DEFINE_TWI_REG(MASTER_STAT, 0x18)
DEFINE_TWI_REG(MASTER_ADDR, 0x1C)
DEFINE_TWI_REG(INT_STAT, 0x20)
DEFINE_TWI_REG(INT_MASK, 0x24)
DEFINE_TWI_REG(FIFO_CTL, 0x28)
DEFINE_TWI_REG(FIFO_STAT, 0x2C)
DEFINE_TWI_REG(XMT_DATA8, 0x80)
DEFINE_TWI_REG(XMT_DATA16, 0x84)
DEFINE_TWI_REG(RCV_DATA8, 0x88)
DEFINE_TWI_REG(RCV_DATA16, 0x8C)
static const u16 pin_req[2][3] = {
{P_TWI0_SCL, P_TWI0_SDA, 0},
{P_TWI1_SCL, P_TWI1_SDA, 0},
};
static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
{
Bryan Wu
committed
unsigned short twi_int_status = read_INT_STAT(iface);
unsigned short mast_stat = read_MASTER_STAT(iface);
if (twi_int_status & XMTSERV) {
/* Transmit next data */
if (iface->writeNum > 0) {
Bryan Wu
committed
write_XMT_DATA8(iface, *(iface->transPtr++));
iface->writeNum--;
}
/* start receive immediately after complete sending in
* combine mode.
*/
Sonic Zhang
committed
else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | MDIR | RSTART);
Sonic Zhang
committed
else if (iface->manual_stop)
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP);
Sonic Zhang
committed
else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num)
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART);
Bryan Wu
committed
write_INT_STAT(iface, XMTSERV);
SSYNC();
}
if (twi_int_status & RCVSERV) {
if (iface->readNum > 0) {
/* Receive next data */
Bryan Wu
committed
*(iface->transPtr) = read_RCV_DATA8(iface);
if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
/* Change combine mode into sub mode after
* read first data.
*/
iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
/* Get read number from first byte in block
* combine mode.
*/
if (iface->readNum == 1 && iface->manual_stop)
iface->readNum = *iface->transPtr + 1;
}
iface->transPtr++;
iface->readNum--;
} else if (iface->manual_stop) {
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP);
Sonic Zhang
committed
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) {
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART);
Sonic Zhang
committed
SSYNC();
Bryan Wu
committed
write_INT_STAT(iface, RCVSERV);
SSYNC();
}
if (twi_int_status & MERR) {
Bryan Wu
committed
write_INT_STAT(iface, MERR);
write_INT_MASK(iface, 0);
write_MASTER_STAT(iface, 0x3e);
write_MASTER_CTL(iface, 0);
Sonic Zhang
committed
iface->result = -EIO;
/* if both err and complete int stats are set, return proper
* results.
*/
if (twi_int_status & MCOMP) {
Bryan Wu
committed
write_INT_STAT(iface, MCOMP);
write_INT_MASK(iface, 0);
write_MASTER_CTL(iface, 0);
SSYNC();
/* If it is a quick transfer, only address bug no data,
* not an err, return 1.
*/
if (iface->writeNum == 0 && (mast_stat & BUFRDERR))
iface->result = 1;
/* If address not acknowledged return -1,
* else return 0.
*/
else if (!(mast_stat & ANAK))
iface->result = 0;
}
complete(&iface->complete);
return;
}
if (twi_int_status & MCOMP) {
Bryan Wu
committed
write_INT_STAT(iface, MCOMP);
SSYNC();
if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
if (iface->readNum == 0) {
/* set the read number to 1 and ask for manual
* stop in block combine mode
*/
iface->readNum = 1;
iface->manual_stop = 1;
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | (0xff << 6));
} else {
/* set the readd number in other
* combine mode.
*/
Bryan Wu
committed
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) &
Bryan Wu
committed
(iface->readNum << 6));
}
/* remove restart bit and enable master receive */
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART);
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | MEN | MDIR);
Sonic Zhang
committed
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) {
iface->cur_msg++;
iface->transPtr = iface->pmsg[iface->cur_msg].buf;
iface->writeNum = iface->readNum =
iface->pmsg[iface->cur_msg].len;
/* Set Transmit device address */
Bryan Wu
committed
write_MASTER_ADDR(iface,
Sonic Zhang
committed
iface->pmsg[iface->cur_msg].addr);
if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else {
iface->read_write = I2C_SMBUS_WRITE;
/* Transmit first data */
if (iface->writeNum > 0) {
Bryan Wu
committed
write_XMT_DATA8(iface,
Sonic Zhang
committed
*(iface->transPtr++));
iface->writeNum--;
SSYNC();
}
}
if (iface->pmsg[iface->cur_msg].len <= 255)
Bryan Wu
committed
write_MASTER_CTL(iface,
Sonic Zhang
committed
iface->pmsg[iface->cur_msg].len << 6);
else {
Bryan Wu
committed
write_MASTER_CTL(iface, 0xff << 6);
Sonic Zhang
committed
iface->manual_stop = 1;
}
/* remove restart bit and enable master receive */
Bryan Wu
committed
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART);
write_MASTER_CTL(iface, read_MASTER_CTL(iface) |
Sonic Zhang
committed
MEN | ((iface->read_write == I2C_SMBUS_READ) ?
MDIR : 0));
SSYNC();
Bryan Wu
committed
write_INT_MASK(iface, 0);
write_MASTER_CTL(iface, 0);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
SSYNC();
complete(&iface->complete);
}
}
}
/* Interrupt handler */
static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
{
struct bfin_twi_iface *iface = dev_id;
unsigned long flags;
spin_lock_irqsave(&iface->lock, flags);
del_timer(&iface->timeout_timer);
bfin_twi_handle_interrupt(iface);
spin_unlock_irqrestore(&iface->lock, flags);
return IRQ_HANDLED;
}
static void bfin_twi_timeout(unsigned long data)
{
struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data;
unsigned long flags;
spin_lock_irqsave(&iface->lock, flags);
bfin_twi_handle_interrupt(iface);
if (iface->result == 0) {
iface->timeout_count--;
if (iface->timeout_count > 0) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
} else {
iface->result = -1;
complete(&iface->complete);
}
}
spin_unlock_irqrestore(&iface->lock, flags);
}
/*
* Generic i2c master transfer entrypoint
*/
static int bfin_twi_master_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct bfin_twi_iface *iface = adap->algo_data;
struct i2c_msg *pmsg;
int rc = 0;
Bryan Wu
committed
if (!(read_CONTROL(iface) & TWI_ENA))
Bryan Wu
committed
while (read_MASTER_STAT(iface) & BUSBUSY)
Sonic Zhang
committed
iface->pmsg = msgs;
iface->msg_num = num;
iface->cur_msg = 0;
Sonic Zhang
committed
pmsg = &msgs[0];
if (pmsg->flags & I2C_M_TEN) {
dev_err(&adap->dev, "10 bits addr not supported!\n");
return -EINVAL;
}
Sonic Zhang
committed
iface->cur_mode = TWI_I2C_MODE_REPEAT;
iface->manual_stop = 0;
iface->transPtr = pmsg->buf;
iface->writeNum = iface->readNum = pmsg->len;
iface->result = 0;
iface->timeout_count = 10;
/* Set Transmit device address */
Bryan Wu
committed
write_MASTER_ADDR(iface, pmsg->addr);
Sonic Zhang
committed
/* FIFO Initiation. Data in FIFO should be
* discarded before start a new operation.
*/
Bryan Wu
committed
write_FIFO_CTL(iface, 0x3);
Sonic Zhang
committed
SSYNC();
Bryan Wu
committed
write_FIFO_CTL(iface, 0);
Sonic Zhang
committed
SSYNC();
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else {
iface->read_write = I2C_SMBUS_WRITE;
/* Transmit first data */
if (iface->writeNum > 0) {
Bryan Wu
committed
write_XMT_DATA8(iface, *(iface->transPtr++));
Sonic Zhang
committed
iface->writeNum--;
SSYNC();
Sonic Zhang
committed
}
Sonic Zhang
committed
/* clear int stat */
Bryan Wu
committed
write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
Sonic Zhang
committed
/* Interrupt mask . Enable XMT, RCV interrupt */
Bryan Wu
committed
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
Sonic Zhang
committed
SSYNC();
Sonic Zhang
committed
if (pmsg->len <= 255)
Bryan Wu
committed
write_MASTER_CTL(iface, pmsg->len << 6);
Sonic Zhang
committed
else {
Bryan Wu
committed
write_MASTER_CTL(iface, 0xff << 6);
Sonic Zhang
committed
iface->manual_stop = 1;
}
Sonic Zhang
committed
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
Sonic Zhang
committed
/* Master enable */
Bryan Wu
committed
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
Sonic Zhang
committed
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
SSYNC();
wait_for_completion(&iface->complete);
rc = iface->result;
Sonic Zhang
committed
if (rc == 1)
return num;
else
return rc;
}
/*
* SMBus type transfer entrypoint
*/
int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
struct bfin_twi_iface *iface = adap->algo_data;
int rc = 0;
Bryan Wu
committed
if (!(read_CONTROL(iface) & TWI_ENA))
Bryan Wu
committed
while (read_MASTER_STAT(iface) & BUSBUSY)
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
yield();
iface->writeNum = 0;
iface->readNum = 0;
/* Prepare datas & select mode */
switch (size) {
case I2C_SMBUS_QUICK:
iface->transPtr = NULL;
iface->cur_mode = TWI_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE:
if (data == NULL)
iface->transPtr = NULL;
else {
if (read_write == I2C_SMBUS_READ)
iface->readNum = 1;
else
iface->writeNum = 1;
iface->transPtr = &data->byte;
}
iface->cur_mode = TWI_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ) {
iface->readNum = 1;
iface->cur_mode = TWI_I2C_MODE_COMBINED;
} else {
iface->writeNum = 1;
iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
}
iface->transPtr = &data->byte;
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ) {
iface->readNum = 2;
iface->cur_mode = TWI_I2C_MODE_COMBINED;
} else {
iface->writeNum = 2;
iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
}
iface->transPtr = (u8 *)&data->word;
break;
case I2C_SMBUS_PROC_CALL:
iface->writeNum = 2;
iface->readNum = 2;
iface->cur_mode = TWI_I2C_MODE_COMBINED;
iface->transPtr = (u8 *)&data->word;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
iface->readNum = 0;
iface->cur_mode = TWI_I2C_MODE_COMBINED;
} else {
iface->writeNum = data->block[0] + 1;
iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
}
iface->transPtr = data->block;
break;
default:
return -1;
}
iface->result = 0;
iface->manual_stop = 0;
iface->read_write = read_write;
iface->command = command;
iface->timeout_count = 10;
/* FIFO Initiation. Data in FIFO should be discarded before
* start a new operation.
*/
Bryan Wu
committed
write_FIFO_CTL(iface, 0x3);
Bryan Wu
committed
write_FIFO_CTL(iface, 0);
Bryan Wu
committed
write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
/* Set Transmit device address */
Bryan Wu
committed
write_MASTER_ADDR(iface, addr);
SSYNC();
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
switch (iface->cur_mode) {
case TWI_I2C_MODE_STANDARDSUB:
Bryan Wu
committed
write_XMT_DATA8(iface, iface->command);
write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
SSYNC();
if (iface->writeNum + 1 <= 255)
Bryan Wu
committed
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
Bryan Wu
committed
write_MASTER_CTL(iface, 0xff << 6);
iface->manual_stop = 1;
}
/* Master enable */
Bryan Wu
committed
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
break;
case TWI_I2C_MODE_COMBINED:
Bryan Wu
committed
write_XMT_DATA8(iface, iface->command);
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
SSYNC();
if (iface->writeNum > 0)
Bryan Wu
committed
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
Bryan Wu
committed
write_MASTER_CTL(iface, 0x1 << 6);
Bryan Wu
committed
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
break;
default:
Bryan Wu
committed
write_MASTER_CTL(iface, 0);
if (size != I2C_SMBUS_QUICK) {
/* Don't access xmit data register when this is a
* read operation.
*/
if (iface->read_write != I2C_SMBUS_READ) {
if (iface->writeNum > 0) {
Bryan Wu
committed
write_XMT_DATA8(iface,
*(iface->transPtr++));
Bryan Wu
committed
write_MASTER_CTL(iface,
iface->writeNum << 6);
Bryan Wu
committed
write_MASTER_CTL(iface,
0xff << 6);
iface->manual_stop = 1;
}
iface->writeNum--;
} else {
Bryan Wu
committed
write_XMT_DATA8(iface, iface->command);
write_MASTER_CTL(iface, 1 << 6);
}
} else {
if (iface->readNum > 0 && iface->readNum <= 255)
Bryan Wu
committed
write_MASTER_CTL(iface,
iface->readNum << 6);
Bryan Wu
committed
write_MASTER_CTL(iface, 0xff << 6);
iface->manual_stop = 1;
} else {
del_timer(&iface->timeout_timer);
break;
}
}
}
Bryan Wu
committed
write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
SSYNC();
/* Master enable */
Bryan Wu
committed
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
break;
}
SSYNC();
wait_for_completion(&iface->complete);
rc = (iface->result >= 0) ? 0 : -1;
return rc;
}
/*
* Return what the adapter supports
*/
static u32 bfin_twi_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_I2C;
}
static struct i2c_algorithm bfin_twi_algorithm = {
.master_xfer = bfin_twi_master_xfer,
.smbus_xfer = bfin_twi_smbus_xfer,
.functionality = bfin_twi_functionality,
};
static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state)
{
Bryan Wu
committed
struct bfin_twi_iface *iface = platform_get_drvdata(dev);
Bryan Wu
committed
write_CONTROL(iface, read_CONTROL(iface) & ~TWI_ENA);
SSYNC();
return 0;
}
static int i2c_bfin_twi_resume(struct platform_device *dev)
{
Bryan Wu
committed
struct bfin_twi_iface *iface = platform_get_drvdata(dev);
Bryan Wu
committed
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
Bryan Wu
committed
static int i2c_bfin_twi_probe(struct platform_device *pdev)
Bryan Wu
committed
struct bfin_twi_iface *iface;
Bryan Wu
committed
struct resource *res;
Bryan Wu
committed
iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
if (!iface) {
dev_err(&pdev->dev, "Cannot allocate memory\n");
rc = -ENOMEM;
goto out_error_nomem;
}
spin_lock_init(&(iface->lock));
init_completion(&(iface->complete));
Bryan Wu
committed
/* Find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
rc = -ENOENT;
goto out_error_get_res;
}
iface->regs_base = ioremap(res->start, res->end - res->start + 1);
if (iface->regs_base == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
rc = -ENXIO;
goto out_error_ioremap;
}
iface->irq = platform_get_irq(pdev, 0);
if (iface->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
rc = -ENOENT;
goto out_error_no_irq;
}
init_timer(&(iface->timeout_timer));
iface->timeout_timer.function = bfin_twi_timeout;
iface->timeout_timer.data = (unsigned long)iface;
p_adap = &iface->adap;
p_adap->id = I2C_HW_BLACKFIN;
Bryan Wu
committed
p_adap->nr = pdev->id;
strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
p_adap->algo = &bfin_twi_algorithm;
p_adap->algo_data = iface;
p_adap->class = I2C_CLASS_ALL;
Bryan Wu
committed
p_adap->dev.parent = &pdev->dev;
rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
if (rc) {
dev_err(&pdev->dev, "Can't setup pin mux!\n");
goto out_error_pin_mux;
}
rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
Bryan Wu
committed
IRQF_DISABLED, pdev->name, iface);
Bryan Wu
committed
dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
rc = -ENODEV;
goto out_error_req_irq;
}
/* Set TWI internal clock as 10MHz */
Bryan Wu
committed
write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
/* Set Twi interface clock as specified */
Bryan Wu
committed
write_CLKDIV(iface, ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
<< 8) | ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
Bryan Wu
committed
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
rc = i2c_add_numbered_adapter(p_adap);
Bryan Wu
committed
if (rc < 0) {
dev_err(&pdev->dev, "Can't add i2c adapter!\n");
goto out_error_add_adapter;
}
platform_set_drvdata(pdev, iface);
Bryan Wu
committed
dev_info(&pdev->dev, "Blackfin I2C TWI controller, regs_base@%p\n",
iface->regs_base);
return 0;
out_error_add_adapter:
free_irq(iface->irq, iface);
out_error_req_irq:
out_error_no_irq:
peripheral_free_list(pin_req[pdev->id]);
out_error_pin_mux:
Bryan Wu
committed
iounmap(iface->regs_base);
out_error_ioremap:
out_error_get_res:
kfree(iface);
out_error_nomem:
return rc;
}
static int i2c_bfin_twi_remove(struct platform_device *pdev)
{
struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
i2c_del_adapter(&(iface->adap));
free_irq(iface->irq, iface);
peripheral_free_list(pin_req[pdev->id]);
Bryan Wu
committed
iounmap(iface->regs_base);
kfree(iface);
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
return 0;
}
static struct platform_driver i2c_bfin_twi_driver = {
.probe = i2c_bfin_twi_probe,
.remove = i2c_bfin_twi_remove,
.suspend = i2c_bfin_twi_suspend,
.resume = i2c_bfin_twi_resume,
.driver = {
.name = "i2c-bfin-twi",
.owner = THIS_MODULE,
},
};
static int __init i2c_bfin_twi_init(void)
{
return platform_driver_register(&i2c_bfin_twi_driver);
}
static void __exit i2c_bfin_twi_exit(void)
{
platform_driver_unregister(&i2c_bfin_twi_driver);
}
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for Blackfin TWI");
MODULE_LICENSE("GPL");
module_init(i2c_bfin_twi_init);
module_exit(i2c_bfin_twi_exit);