From ef5a12fddf73716159f17d61391ad5b0e022bc38 Mon Sep 17 00:00:00 2001
From: Alex Deucher <alexdeucher@gmail.com>
Date: Fri, 5 Feb 2010 16:48:23 -0500
Subject: [PATCH] drm/radeon/kms: hw i2c fixes

- handle bus probing correctly
- use meaningful error numbers
- abort if transaction fails

This gets i2cdetect working as well with hw i2c as
sw.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
---
 drivers/gpu/drm/radeon/radeon_i2c.c |   95 ++++++++++++++++++++++++++++++-----
 1 files changed, 82 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 14a68b4..825c921 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -216,11 +216,43 @@ static int r100_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 			break;
 		default:
 			DRM_ERROR("gpio not supported with hw i2c\n");
-			ret = -1;
+			ret = -EINVAL;
 			goto done;
 		}
 	}
 
+	/* check for bus probe */
+	p = &msgs[0];
+	if ((num == 1) && (p->len == 0)) {
+		WREG32(i2c_cntl_0, (RADEON_I2C_DONE |
+				    RADEON_I2C_NACK |
+				    RADEON_I2C_HALT |
+				    RADEON_I2C_SOFT_RST));
+		WREG32(i2c_data, (p->addr << 1) & 0xff);
+		WREG32(i2c_data, 0);
+		WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) |
+				    (1 << RADEON_I2C_ADDR_COUNT_SHIFT) |
+				    RADEON_I2C_EN |
+				    (48 << RADEON_I2C_TIME_LIMIT_SHIFT)));
+		WREG32(i2c_cntl_0, reg);
+		for (k = 0; k < 32; k++) {
+			udelay(10);
+			tmp = RREG32(i2c_cntl_0);
+			if (tmp & RADEON_I2C_GO)
+				continue;
+			tmp = RREG32(i2c_cntl_0);
+			if (tmp & RADEON_I2C_DONE)
+				break;
+			else {
+				DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+				WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+				ret = -EIO;
+				goto done;
+			}
+		}
+		goto done;
+	}
+
 	for (i = 0; i < num; i++) {
 		p = &msgs[i];
 		for (j = 0; j < p->len; j++) {
@@ -245,7 +277,8 @@ static int r100_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 						break;
 					else {
 						DRM_DEBUG("i2c read error 0x%08x\n", tmp);
-						ret = -1;
+						WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+						ret = -EIO;
 						goto done;
 					}
 				}
@@ -272,7 +305,8 @@ static int r100_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 						break;
 					else {
 						DRM_DEBUG("i2c write error 0x%08x\n", tmp);
-						ret = -1;
+						WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+						ret = -EIO;
 						goto done;
 					}
 				}
@@ -361,11 +395,10 @@ static int r500_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 	}
 	if (i == 50) {
 		DRM_ERROR("failed to get i2c bus\n");
-		ret = -1;
+		ret = -EBUSY;
 		goto done;
 	}
 
-
 	if (rdev->family == CHIP_R520)
 		prescale = (127 << 8) + ((rdev->clock.default_sclk * 10) / (4 * 127 * i2c_clock));
 	else
@@ -384,7 +417,44 @@ static int r500_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 		break;
 	default:
 		DRM_ERROR("gpio not supported with hw i2c\n");
-		ret = -1;
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* check for bus probe */
+	p = &msgs[0];
+	if ((num == 1) && (p->len == 0)) {
+		WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE |
+					      AVIVO_DC_I2C_NACK |
+					      AVIVO_DC_I2C_HALT));
+		WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET);
+		udelay(1);
+		WREG32(AVIVO_DC_I2C_RESET, 0);
+
+		WREG32(AVIVO_DC_I2C_DATA, (p->addr << 1) & 0xff);
+		WREG32(AVIVO_DC_I2C_DATA, 0);
+
+		WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48));
+		WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) |
+					       AVIVO_DC_I2C_DATA_COUNT(1) |
+					       (prescale << 16)));
+		WREG32(AVIVO_DC_I2C_CONTROL1, reg);
+		WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO);
+		for (j = 0; j < 200; j++) {
+			udelay(50);
+			tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+			if (tmp & AVIVO_DC_I2C_GO)
+				continue;
+			tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+			if (tmp & AVIVO_DC_I2C_DONE)
+				break;
+			else {
+				DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+				WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+				ret = -EIO;
+				goto done;
+			}
+		}
 		goto done;
 	}
 
@@ -422,7 +492,8 @@ static int r500_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 						break;
 					else {
 						DRM_DEBUG("i2c read error 0x%08x\n", tmp);
-						ret = -1;
+						WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+						ret = -EIO;
 						goto done;
 					}
 				}
@@ -464,7 +535,8 @@ static int r500_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
 						break;
 					else {
 						DRM_DEBUG("i2c write error 0x%08x\n", tmp);
-						ret = -1;
+						WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+						ret = -EIO;
 						goto done;
 					}
 				}
@@ -499,10 +571,7 @@ static int radeon_sw_i2c_xfer(struct i2c_adapter *i2c_adap,
 	int ret;
 
 	radeon_i2c_do_lock(i2c, 1);
-	if (i2c_transfer(&i2c->algo.radeon.bit_adapter, msgs, num) == num)
-		ret = num;
-	else
-		ret = -1;
+	ret = i2c_transfer(&i2c->algo.radeon.bit_adapter, msgs, num);
 	radeon_i2c_do_lock(i2c, 0);
 
 	return ret;
@@ -580,7 +649,7 @@ static int radeon_i2c_xfer(struct i2c_adapter *i2c_adap,
 		break;
 	default:
 		DRM_ERROR("i2c: unhandled radeon chip\n");
-		ret = -1;
+		ret = -EIO;
 		break;
 	}
 
-- 
1.5.6.3

