PATCH - s3c2410 clock control patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch makes it possible for the I2C unit
to disable the clock fed into it whilst it is
not being used. This should reduce the power
consumption, with only minimal extra time during
the start and end of a transfer.

This patch also ensures that the clock is in
the right state when going in and out of
suspend.

Also included is a minor bugfix to place a minor
delay if going to next part of a write, the
controller can generate incorrect bus data if
a small number of register reads inserted
before returning from the IRQ handler

Signed-off-by: Ben Dooks <ben-linux at fluff.org>
-------------- next part --------------
diff -urpN -X ../dontdiff linux-2.6.16-rmk/drivers/i2c/busses/Kconfig linux-2.6.16-rmk-bjd1/drivers/i2c/busses/Kconfig
--- linux-2.6.16-rmk/drivers/i2c/busses/Kconfig	2006-03-20 05:53:29.000000000 +0000
+++ linux-2.6.16-rmk-bjd1/drivers/i2c/busses/Kconfig	2006-03-21 22:44:26.000000000 +0000
@@ -343,6 +343,14 @@ config I2C_S3C2410
 	  Say Y here to include support for I2C controller in the
 	  Samsung S3C2410 based System-on-Chip devices.
 
+config I2C_S3C2410_CLKIDLE
+	bool "Stop I2C clock during IDLE"
+	depends on I2C_S3C2410
+	help
+	  Say Y here to enable support for disabling the I2C unit
+	  clock when the bus is idle. This is useful to save power
+	  if not using the unit as a bus slave.
+
 config I2C_SAVAGE4
 	tristate "S3 Savage 4"
 	depends on I2C && PCI && EXPERIMENTAL
diff -urpN -X ../dontdiff linux-2.6.16-rmk/drivers/i2c/busses/i2c-s3c2410.c linux-2.6.16-rmk-bjd1/drivers/i2c/busses/i2c-s3c2410.c
--- linux-2.6.16-rmk/drivers/i2c/busses/i2c-s3c2410.c	2006-03-20 05:53:29.000000000 +0000
+++ linux-2.6.16-rmk-bjd1/drivers/i2c/busses/i2c-s3c2410.c	2006-03-21 22:44:26.000000000 +0000
@@ -44,6 +44,14 @@
 #include <asm/arch/regs-iic.h>
 #include <asm/arch/iic.h>
 
+/* configuration */
+
+#ifdef CONFIG_I2C_S3C2410_CLKIDLE
+static int clock_idle = 1;
+#else
+static const int clock_idle = 0;
+#endif
+
 /* i2c controller state */
 
 enum s3c24xx_i2c_state {
@@ -96,6 +104,11 @@ static inline int s3c24xx_i2c_is2440(str
 	return !strcmp(pdev->name, "s3c2440-i2c");
 }
 
+static inline int s3c24xx_i2c_clkidle(struct s3c24xx_i2c *i2c)
+{
+	return clock_idle;
+}
+
 
 /* s3c24xx_i2c_get_platformdata
  *
@@ -214,7 +227,7 @@ static inline void s3c24xx_i2c_stop(stru
 {
 	unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
 
-	dev_dbg(i2c->dev, "STOP\n");
+	dev_dbg(i2c->dev, "STOP (ret=%d)\n", ret);
 
 	/* stop the transfer */
 	iicstat &= ~ S3C2410_IICSTAT_START;
@@ -269,6 +282,7 @@ static int i2s_s3c_irq_nextbyte(struct s
 	unsigned long tmp;
 	unsigned char byte;
 	int ret = 0;
+	int i;
 
 	switch (i2c->state) {
 
@@ -324,7 +338,15 @@ static int i2s_s3c_irq_nextbyte(struct s
 		if (!is_msgend(i2c)) {
 			byte = i2c->msg->buf[i2c->msg_ptr++];
 			writeb(byte, i2c->regs + S3C2410_IICDS);
-			
+
+			/* sometimes write operations fail if the write is
+			 * let go too quickly, slow down the proceedings
+			 * slightly.
+			 */
+
+			for (i = 0; i < 6; i++) 
+				tmp += readl(i2c->regs + S3C2410_IICSTAT);
+
 		} else if (!is_lastmsg(i2c)) {
 			/* we need to go to the next i2c message */
 
@@ -492,6 +514,9 @@ static int s3c24xx_i2c_doxfer(struct s3c
 	unsigned long timeout;
 	int ret;
 
+	if (s3c24xx_i2c_clkidle(i2c))
+		clk_enable(i2c->clk);
+
 	ret = s3c24xx_i2c_set_master(i2c);
 	if (ret != 0) {
 		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
@@ -528,6 +553,9 @@ static int s3c24xx_i2c_doxfer(struct s3c
 	msleep(1);
 
  out:
+	if (s3c24xx_i2c_clkidle(i2c))
+		clk_disable(i2c->clk);
+
 	return ret;
 }
 
@@ -852,8 +880,13 @@ static int s3c24xx_i2c_probe(struct plat
 	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
 
  out:
-	if (ret < 0)
+	if (ret < 0) {
 		s3c24xx_i2c_free(i2c);
+		clk_disable(i2c->clk);
+	} else {
+		if (s3c24xx_i2c_clkidle(i2c))
+			clk_disable(i2c->clk);
+	}
 
 	return ret;
 }
@@ -870,6 +903,9 @@ static int s3c24xx_i2c_remove(struct pla
 	if (i2c != NULL) {
 		s3c24xx_i2c_free(i2c);
 		platform_set_drvdata(pdev, NULL);
+
+		if (!s3c24xx_i2c_clkidle(i2c))
+			clk_disable(i2c->clk);
 	}
 
 	return 0;
@@ -880,9 +916,14 @@ static int s3c24xx_i2c_resume(struct pla
 {
 	struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);
 
-	if (i2c != NULL)
+	if (i2c != NULL) {
+		clk_enable(i2c->clk);
 		s3c24xx_i2c_init(i2c);
 
+		if (!s3c24xx_i2c_clkidle(i2c))
+			clk_disable(i2c->clk);
+	}
+
 	return 0;
 }
 


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux