Skip to content

Commit 8f0cdec

Browse files
cpackham-atlnzwsakernel
authored andcommitted
i2c: mpc: implement erratum A-004447 workaround
The P2040/P2041 has an erratum where the normal i2c recovery mechanism does not work. Implement the alternative recovery mechanism documented in the P2040 Chip Errata Rev Q. Signed-off-by: Chris Packham <[email protected]> Signed-off-by: Wolfram Sang <[email protected]>
1 parent 19ae697 commit 8f0cdec

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

drivers/i2c/busses/i2c-mpc.c

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <linux/clk.h>
2121
#include <linux/io.h>
22+
#include <linux/iopoll.h>
2223
#include <linux/fsl_devices.h>
2324
#include <linux/i2c.h>
2425
#include <linux/interrupt.h>
@@ -45,6 +46,7 @@
4546
#define CCR_MTX 0x10
4647
#define CCR_TXAK 0x08
4748
#define CCR_RSTA 0x04
49+
#define CCR_RSVD 0x02
4850

4951
#define CSR_MCF 0x80
5052
#define CSR_MAAS 0x40
@@ -97,7 +99,7 @@ struct mpc_i2c {
9799
u32 block;
98100
int rc;
99101
int expect_rxack;
100-
102+
bool has_errata_A004447;
101103
};
102104

103105
struct mpc_i2c_divider {
@@ -136,6 +138,75 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
136138
}
137139
}
138140

141+
static int i2c_mpc_wait_sr(struct mpc_i2c *i2c, int mask)
142+
{
143+
void __iomem *addr = i2c->base + MPC_I2C_SR;
144+
u8 val;
145+
146+
return readb_poll_timeout(addr, val, val & mask, 0, 100);
147+
}
148+
149+
/*
150+
* Workaround for Erratum A004447. From the P2040CE Rev Q
151+
*
152+
* 1. Set up the frequency divider and sampling rate.
153+
* 2. I2CCR - a0h
154+
* 3. Poll for I2CSR[MBB] to get set.
155+
* 4. If I2CSR[MAL] is set (an indication that SDA is stuck low), then go to
156+
* step 5. If MAL is not set, then go to step 13.
157+
* 5. I2CCR - 00h
158+
* 6. I2CCR - 22h
159+
* 7. I2CCR - a2h
160+
* 8. Poll for I2CSR[MBB] to get set.
161+
* 9. Issue read to I2CDR.
162+
* 10. Poll for I2CSR[MIF] to be set.
163+
* 11. I2CCR - 82h
164+
* 12. Workaround complete. Skip the next steps.
165+
* 13. Issue read to I2CDR.
166+
* 14. Poll for I2CSR[MIF] to be set.
167+
* 15. I2CCR - 80h
168+
*/
169+
static void mpc_i2c_fixup_A004447(struct mpc_i2c *i2c)
170+
{
171+
int ret;
172+
u32 val;
173+
174+
writeccr(i2c, CCR_MEN | CCR_MSTA);
175+
ret = i2c_mpc_wait_sr(i2c, CSR_MBB);
176+
if (ret) {
177+
dev_err(i2c->dev, "timeout waiting for CSR_MBB\n");
178+
return;
179+
}
180+
181+
val = readb(i2c->base + MPC_I2C_SR);
182+
183+
if (val & CSR_MAL) {
184+
writeccr(i2c, 0x00);
185+
writeccr(i2c, CCR_MSTA | CCR_RSVD);
186+
writeccr(i2c, CCR_MEN | CCR_MSTA | CCR_RSVD);
187+
ret = i2c_mpc_wait_sr(i2c, CSR_MBB);
188+
if (ret) {
189+
dev_err(i2c->dev, "timeout waiting for CSR_MBB\n");
190+
return;
191+
}
192+
val = readb(i2c->base + MPC_I2C_DR);
193+
ret = i2c_mpc_wait_sr(i2c, CSR_MIF);
194+
if (ret) {
195+
dev_err(i2c->dev, "timeout waiting for CSR_MIF\n");
196+
return;
197+
}
198+
writeccr(i2c, CCR_MEN | CCR_RSVD);
199+
} else {
200+
val = readb(i2c->base + MPC_I2C_DR);
201+
ret = i2c_mpc_wait_sr(i2c, CSR_MIF);
202+
if (ret) {
203+
dev_err(i2c->dev, "timeout waiting for CSR_MIF\n");
204+
return;
205+
}
206+
writeccr(i2c, CCR_MEN);
207+
}
208+
}
209+
139210
#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
140211
static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
141212
{20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
@@ -670,7 +741,10 @@ static int fsl_i2c_bus_recovery(struct i2c_adapter *adap)
670741
{
671742
struct mpc_i2c *i2c = i2c_get_adapdata(adap);
672743

673-
mpc_i2c_fixup(i2c);
744+
if (i2c->has_errata_A004447)
745+
mpc_i2c_fixup_A004447(i2c);
746+
else
747+
mpc_i2c_fixup(i2c);
674748

675749
return 0;
676750
}
@@ -767,6 +841,9 @@ static int fsl_i2c_probe(struct platform_device *op)
767841
}
768842
dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
769843

844+
if (of_property_read_bool(op->dev.of_node, "fsl,i2c-erratum-a004447"))
845+
i2c->has_errata_A004447 = true;
846+
770847
i2c->adap = mpc_ops;
771848
scnprintf(i2c->adap.name, sizeof(i2c->adap.name),
772849
"MPC adapter (%s)", of_node_full_name(op->dev.of_node));

0 commit comments

Comments
 (0)