aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/files/0017-Quark-I2C-quark.patch
blob: a730cca25b5465f04a0f0e3276fcd15a007725d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
From xxxx Mon Sep 17 00:00:00 2001
From: Josef Ahmad <josef.ahmad@linux.intel.com>
Date: Tue, 13 Aug 2013 10:22:38 +0100
Subject: [PATCH 17/21] Quark I2C

---
 drivers/i2c/busses/Kconfig                 |   19 +++---
 drivers/i2c/busses/i2c-designware-core.c   |   99 ++++++++++++++++++++++++++--
 drivers/i2c/busses/i2c-designware-core.h   |   12 ++++
 drivers/i2c/busses/i2c-designware-pcidrv.c |   18 +++--
 4 files changed, 123 insertions(+), 25 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..010e4fd 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -22,7 +22,7 @@ config I2C_ALI1535
 
 config I2C_ALI1563
 	tristate "ALI 1563"
-	depends on PCI && EXPERIMENTAL
+	depends on PCI
 	help
 	  If you say yes to this option, support will be included for the SMB
 	  Host controller on Acer Labs Inc. (ALI) M1563 South Bridges.  The SMB
@@ -56,7 +56,7 @@ config I2C_AMD756
 
 config I2C_AMD756_S4882
 	tristate "SMBus multiplexing on the Tyan S4882"
-	depends on I2C_AMD756 && X86 && EXPERIMENTAL
+	depends on I2C_AMD756 && X86
 	help
 	  Enabling this option will add specific SMBus support for the Tyan
 	  S4882 motherboard.  On this 4-CPU board, the SMBus is multiplexed
@@ -164,7 +164,7 @@ config I2C_NFORCE2
 
 config I2C_NFORCE2_S4985
 	tristate "SMBus multiplexing on the Tyan S4985"
-	depends on I2C_NFORCE2 && X86 && EXPERIMENTAL
+	depends on I2C_NFORCE2 && X86
 	help
 	  Enabling this option will add specific SMBus support for the Tyan
 	  S4985 motherboard.  On this 4-CPU board, the SMBus is multiplexed
@@ -215,7 +215,7 @@ config I2C_SIS96X
 
 config I2C_VIA
 	tristate "VIA VT82C586B"
-	depends on PCI && EXPERIMENTAL
+	depends on PCI
 	select I2C_ALGOBIT
 	help
 	  If you say yes to this option, support will be included for the VIA
@@ -267,7 +267,7 @@ comment "Mac SMBus host controller drivers"
 
 config I2C_HYDRA
 	tristate "CHRP Apple Hydra Mac I/O I2C interface"
-	depends on PCI && PPC_CHRP && EXPERIMENTAL
+	depends on PCI && PPC_CHRP
 	select I2C_ALGOBIT
 	help
 	  This supports the use of the I2C interface in the Apple Hydra Mac
@@ -293,7 +293,7 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)"
 
 config I2C_AT91
 	tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
-	depends on ARCH_AT91 && EXPERIMENTAL
+	depends on ARCH_AT91
 	help
 	  This supports the use of the I2C interface on Atmel AT91
 	  processors.
@@ -386,7 +386,7 @@ config I2C_DESIGNWARE_PLATFORM
 
 config I2C_DESIGNWARE_PCI
 	tristate "Synopsys DesignWare PCI"
-	depends on PCI
+	depends on PCI && !INTEL_QUARK_X1000_SOC
 	select I2C_DESIGNWARE_CORE
 	help
 	  If you say yes to this option, support will be included for the
@@ -519,7 +519,6 @@ config I2C_NUC900
 
 config I2C_OCORES
 	tristate "OpenCores I2C Controller"
-	depends on EXPERIMENTAL
 	help
 	  If you say yes to this option, support will be included for the
 	  OpenCores I2C controller. For details see
@@ -712,7 +711,7 @@ config I2C_OCTEON
 
 config I2C_XILINX
 	tristate "Xilinx I2C Controller"
-	depends on EXPERIMENTAL && HAS_IOMEM
+	depends on HAS_IOMEM
 	help
 	  If you say yes to this option, support will be included for the
 	  Xilinx I2C controller.
@@ -803,7 +802,7 @@ config I2C_PARPORT_LIGHT
 
 config I2C_TAOS_EVM
 	tristate "TAOS evaluation module"
-	depends on EXPERIMENTAL
+	depends on TTY
 	select SERIO
 	select SERIO_SERPORT
 	default n
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index f5258c2..2a2d1c9 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -164,6 +164,29 @@ static char *abort_sources[] = {
 		"lost arbitration",
 };
 
+/*
+ * Bitmask for struct i2c_dw_data_cmd's `cmd' field.
+ * - DW_IC_CMD_READ:  read/~write operation
+ * - DW_IC_CMD_STOP:  stop condition generation (only for devices requiring
+ *   explicit transaction termination)
+ * - DW_IC_CMD_RESTART:  (re)start condition generation (only for devices
+ *   requiring explicit transaction termination)
+ */
+#define DW_IC_CMD_READ			0x01
+#define DW_IC_CMD_STOP			0x02
+#define DW_IC_CMD_RESTART		0x04
+
+/*
+ * Define the IC_DATA_CMD format.
+ */
+static union i2c_dw_data_cmd {
+	struct fields {
+		u8 data;
+		u8 cmd;
+	} fields;
+	u16 value;
+} data_cmd;
+
 u32 dw_readl(struct dw_i2c_dev *dev, int offset)
 {
 	u32 value;
@@ -344,6 +367,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
 	struct i2c_msg *msgs = dev->msgs;
 	u32 ic_con;
 
+	/* Disable interrupts */
+	i2c_dw_disable_int(dev);
+
 	/* Disable the adapter */
 	dw_writel(dev, 0, DW_IC_ENABLE);
 
@@ -380,6 +406,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 	u32 addr = msgs[dev->msg_write_idx].addr;
 	u32 buf_len = dev->tx_buf_len;
 	u8 *buf = dev->tx_buf;
+	int segment_start = 0;
 
 	intr_mask = DW_IC_INTR_DEFAULT_MASK;
 
@@ -403,21 +430,65 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 			break;
 		}
 
+		segment_start = 0;
 		if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
 			/* new i2c_msg */
 			buf = msgs[dev->msg_write_idx].buf;
 			buf_len = msgs[dev->msg_write_idx].len;
+			segment_start = 1;
 		}
 
 		tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
 		rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
 
+		/*
+		 * The maximum number of read requests that can be put into TX
+		 * FIFO depends on the number read operations already pending
+		 * in RX FIFO + the number of outstanding read operations still
+		 * queued in the TX FIFO.
+		 * This prevents RX FIFO overrun.
+		 */
+		rx_limit -= dev->rx_outstanding;
+
 		while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
+			data_cmd.fields.data = 0x00;
+			data_cmd.fields.cmd = 0x00;
+
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
-				dw_writel(dev, 0x100, DW_IC_DATA_CMD);
+				/* Master-receiver */
+				data_cmd.fields.cmd = DW_IC_CMD_READ;
 				rx_limit--;
-			} else
-				dw_writel(dev, *buf++, DW_IC_DATA_CMD);
+				dev->rx_outstanding++;
+			} else {
+				/* Master-transmitter */
+				data_cmd.fields.data = *buf;
+				buf++;
+			}
+
+			if (1 == dev->explicit_stop
+			    && 1 == segment_start) {
+				/*
+				 * First byte of a transaction segment for a
+				 * device requiring explicit transaction
+				 * termination: generate (re)start symbol.
+				 */
+				segment_start = 0;
+				data_cmd.fields.cmd |= DW_IC_CMD_RESTART;
+			}
+
+			if (1 == dev->explicit_stop
+			    && dev->msg_write_idx == dev->msgs_num - 1
+			    && 1 == buf_len) {
+				/*
+				 * Last byte of last transction segment for a
+				 * device requiring explicit transaction
+				 * termination: generate stop symbol.
+				 */
+				data_cmd.fields.cmd |= DW_IC_CMD_STOP;
+			}
+
+			dw_writel(dev, data_cmd.value, DW_IC_DATA_CMD);
+
 			tx_limit--; buf_len--;
 		}
 
@@ -468,8 +539,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -527,6 +600,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
@@ -625,8 +699,6 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
 		dw_readl(dev, DW_IC_CLR_RX_DONE);
 	if (stat & DW_IC_INTR_ACTIVITY)
 		dw_readl(dev, DW_IC_CLR_ACTIVITY);
-	if (stat & DW_IC_INTR_STOP_DET)
-		dw_readl(dev, DW_IC_CLR_STOP_DET);
 	if (stat & DW_IC_INTR_START_DET)
 		dw_readl(dev, DW_IC_CLR_START_DET);
 	if (stat & DW_IC_INTR_GEN_CALL)
@@ -677,8 +749,21 @@ irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
 	 * the current transmit status.
 	 */
 
+	/*
+	 * Process stop condition after the last transaction segment is
+	 * transmitted (and received if appropriate).
+	 */
+	if (dev->msgs_num == dev->msg_write_idx
+		&& (DW_IC_INTR_STOP_DET & dw_readl(dev, DW_IC_INTR_STAT))
+		&& 0 == dw_readl(dev, DW_IC_TXFLR)
+		&& 0 == dw_readl(dev, DW_IC_RXFLR)
+		&& 0 == dev->rx_outstanding) {
+			dw_readl(dev, DW_IC_CLR_STOP_DET);
+			complete(&dev->cmd_complete);
+	}
+
 tx_aborted:
-	if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
+	if ((stat & (DW_IC_INTR_TX_ABRT)) || dev->msg_err)
 		complete(&dev->cmd_complete);
 
 	return IRQ_HANDLED;
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 9c1840e..691aff8 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -34,6 +34,14 @@
 #define DW_IC_CON_RESTART_EN		0x20
 #define DW_IC_CON_SLAVE_DISABLE		0x40
 
+struct dw_pci_controller {
+	u32 bus_num;
+	u32 bus_cfg;
+	u32 tx_fifo_depth;
+	u32 rx_fifo_depth;
+	u32 clk_khz;
+	u8  explicit_stop;
+};
 
 /**
  * struct dw_i2c_dev - private i2c-designware data
@@ -60,6 +68,8 @@
  * @adapter: i2c subsystem adapter node
  * @tx_fifo_depth: depth of the hardware tx fifo
  * @rx_fifo_depth: depth of the hardware rx fifo
+ * @rx_outstanding: current outstanding master-receiver bytes in TX FIFO
+ * @explicit_stop: set to 1 if hardware requires explicit stop bit transmission
  */
 struct dw_i2c_dev {
 	struct device		*dev;
@@ -88,6 +98,8 @@ struct dw_i2c_dev {
 	u32			master_cfg;
 	unsigned int		tx_fifo_depth;
 	unsigned int		rx_fifo_depth;
+	int			rx_outstanding;
+	u8			explicit_stop;
 };
 
 #define ACCESS_SWAP		0x00000001
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index 6add851..62ad7dc 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -56,14 +56,6 @@ enum dw_pci_ctl_id_t {
 	medfield_5,
 };
 
-struct dw_pci_controller {
-	u32 bus_num;
-	u32 bus_cfg;
-	u32 tx_fifo_depth;
-	u32 rx_fifo_depth;
-	u32 clk_khz;
-};
-
 #define INTEL_MID_STD_CFG  (DW_IC_CON_MASTER |			\
 				DW_IC_CON_SLAVE_DISABLE |	\
 				DW_IC_CON_RESTART_EN)
@@ -75,6 +67,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[moorestown_1] = {
 		.bus_num     = 1,
@@ -82,6 +75,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[moorestown_2] = {
 		.bus_num     = 2,
@@ -89,6 +83,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_0] = {
 		.bus_num     = 0,
@@ -96,6 +91,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_1] = {
 		.bus_num     = 1,
@@ -103,6 +99,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_2] = {
 		.bus_num     = 2,
@@ -110,6 +107,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_3] = {
 		.bus_num     = 3,
@@ -117,6 +115,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_4] = {
 		.bus_num     = 4,
@@ -124,6 +123,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 	[medfield_5] = {
 		.bus_num     = 5,
@@ -131,6 +131,7 @@ static struct  dw_pci_controller  dw_pci_controllers[] = {
 		.tx_fifo_depth = 32,
 		.rx_fifo_depth = 32,
 		.clk_khz      = 25000,
+		.explicit_stop = 0,
 	},
 };
 static struct i2c_algorithm i2c_dw_algo = {
@@ -282,6 +283,7 @@ const struct pci_device_id *id)
 
 	dev->tx_fifo_depth = controller->tx_fifo_depth;
 	dev->rx_fifo_depth = controller->rx_fifo_depth;
+	dev->explicit_stop = controller->explicit_stop;
 	r = i2c_dw_init(dev);
 	if (r)
 		goto err_iounmap;
-- 
1.7.4.1