rcu: Fix thinko, actually initialize full tree
[safe/jmp/linux-2.6] / drivers / i2c / busses / i2c-bfin-twi.c
index 4ea31c0..b309ac2 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/io.h>
 #include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/spinlock.h>
@@ -49,6 +50,8 @@ struct bfin_twi_iface {
        struct i2c_msg          *pmsg;
        int                     msg_num;
        int                     cur_msg;
+       u16                     saved_clkdiv;
+       u16                     saved_control;
        void __iomem            *regs_base;
 };
 
@@ -102,9 +105,14 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        write_MASTER_CTL(iface,
                                read_MASTER_CTL(iface) | STOP);
                else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
-                               iface->cur_msg+1 < iface->msg_num)
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | RSTART);
+                        iface->cur_msg + 1 < iface->msg_num) {
+                       if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+                               write_MASTER_CTL(iface,
+                                       read_MASTER_CTL(iface) | RSTART | MDIR);
+                       else
+                               write_MASTER_CTL(iface,
+                                       (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+               }
                SSYNC();
                /* Clear status */
                write_INT_STAT(iface, XMTSERV);
@@ -132,9 +140,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                                read_MASTER_CTL(iface) | STOP);
                        SSYNC();
                } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
-                               iface->cur_msg+1 < iface->msg_num) {
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | RSTART);
+                          iface->cur_msg + 1 < iface->msg_num) {
+                       if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+                               write_MASTER_CTL(iface,
+                                       read_MASTER_CTL(iface) | RSTART | MDIR);
+                       else
+                               write_MASTER_CTL(iface,
+                                       (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
                        SSYNC();
                }
                /* Clear interrupt source */
@@ -194,8 +206,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        /* remove restart bit and enable master receive */
                        write_MASTER_CTL(iface,
                                read_MASTER_CTL(iface) & ~RSTART);
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | MEN | MDIR);
                        SSYNC();
                } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
                                iface->cur_msg+1 < iface->msg_num) {
@@ -220,18 +230,19 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        }
 
                        if (iface->pmsg[iface->cur_msg].len <= 255)
-                               write_MASTER_CTL(iface,
-                               iface->pmsg[iface->cur_msg].len << 6);
+                                       write_MASTER_CTL(iface,
+                                       (read_MASTER_CTL(iface) &
+                                       (~(0xff << 6))) |
+                               (iface->pmsg[iface->cur_msg].len << 6));
                        else {
-                               write_MASTER_CTL(iface, 0xff << 6);
+                               write_MASTER_CTL(iface,
+                                       (read_MASTER_CTL(iface) |
+                                       (0xff << 6)));
                                iface->manual_stop = 1;
                        }
                        /* remove restart bit and enable master receive */
                        write_MASTER_CTL(iface,
                                read_MASTER_CTL(iface) & ~RSTART);
-                       write_MASTER_CTL(iface, read_MASTER_CTL(iface) |
-                               MEN | ((iface->read_write == I2C_SMBUS_READ) ?
-                               MDIR : 0));
                        SSYNC();
                } else {
                        iface->result = 1;
@@ -308,6 +319,7 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
        iface->writeNum = iface->readNum = pmsg->len;
        iface->result = 0;
        iface->timeout_count = 10;
+       init_completion(&(iface->complete));
        /* Set Transmit device address */
        write_MASTER_ADDR(iface, pmsg->addr);
 
@@ -438,6 +450,16 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
                }
                iface->transPtr = data->block;
                break;
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+               if (read_write == I2C_SMBUS_READ) {
+                       iface->readNum = data->block[0];
+                       iface->cur_mode = TWI_I2C_MODE_COMBINED;
+               } else {
+                       iface->writeNum = data->block[0];
+                       iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+               }
+               iface->transPtr = (u8 *)&data->block[1];
+               break;
        default:
                return -1;
        }
@@ -447,6 +469,7 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
        iface->read_write = read_write;
        iface->command = command;
        iface->timeout_count = 10;
+       init_completion(&(iface->complete));
 
        /* FIFO Initiation. Data in FIFO should be discarded before
         * start a new operation.
@@ -560,35 +583,46 @@ 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;
+              I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
 }
 
-
 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)
+static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state)
 {
-       struct bfin_twi_iface *iface = platform_get_drvdata(dev);
+       struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+
+       iface->saved_clkdiv = read_CLKDIV(iface);
+       iface->saved_control = read_CONTROL(iface);
+
+       free_irq(iface->irq, iface);
 
        /* Disable TWI */
-       write_CONTROL(iface, read_CONTROL(iface) & ~TWI_ENA);
-       SSYNC();
+       write_CONTROL(iface, iface->saved_control & ~TWI_ENA);
 
        return 0;
 }
 
-static int i2c_bfin_twi_resume(struct platform_device *dev)
+static int i2c_bfin_twi_resume(struct platform_device *pdev)
 {
-       struct bfin_twi_iface *iface = platform_get_drvdata(dev);
+       struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
 
-       /* Enable TWI */
-       write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
-       SSYNC();
+       int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
+               IRQF_DISABLED, pdev->name, iface);
+       if (rc) {
+               dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
+               return -ENODEV;
+       }
+
+       /* Resume TWI interface clock as specified */
+       write_CLKDIV(iface, iface->saved_clkdiv);
+
+       /* Resume TWI */
+       write_CONTROL(iface, iface->saved_control);
 
        return 0;
 }
@@ -599,6 +633,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        struct i2c_adapter *p_adap;
        struct resource *res;
        int rc;
+       unsigned int clkhilow;
 
        iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
        if (!iface) {
@@ -608,7 +643,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        }
 
        spin_lock_init(&(iface->lock));
-       init_completion(&(iface->complete));
 
        /* Find and map our resources */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -618,7 +652,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
                goto out_error_get_res;
        }
 
-       iface->regs_base = ioremap(res->start, res->end - res->start + 1);
+       iface->regs_base = ioremap(res->start, resource_size(res));
        if (iface->regs_base == NULL) {
                dev_err(&pdev->dev, "Cannot map IO\n");
                rc = -ENXIO;
@@ -637,12 +671,11 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        iface->timeout_timer.data = (unsigned long)iface;
 
        p_adap = &iface->adap;
-       p_adap->id = I2C_HW_BLACKFIN;
        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;
+       p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        p_adap->dev.parent = &pdev->dev;
 
        rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
@@ -662,10 +695,14 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        /* Set TWI internal clock as 10MHz */
        write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
 
+       /*
+        * We will not end up with a CLKDIV=0 because no one will specify
+        * 20kHz SCL or less in Kconfig now. (5 * 1024 / 20 = 0x100)
+        */
+       clkhilow = 5 * 1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ;
+
        /* Set Twi interface clock as specified */
-       write_CLKDIV(iface, ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
-                       << 8) | ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
-                       & 0xFF));
+       write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
 
        /* Enable TWI */
        write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
@@ -740,3 +777,4 @@ module_exit(i2c_bfin_twi_exit);
 MODULE_AUTHOR("Bryan Wu, Sonic Zhang");
 MODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Contoller Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c-bfin-twi");