|
19 | 19 |
|
20 | 20 | #include <linux/clk.h>
|
21 | 21 | #include <linux/io.h>
|
| 22 | +#include <linux/iopoll.h> |
22 | 23 | #include <linux/fsl_devices.h>
|
23 | 24 | #include <linux/i2c.h>
|
24 | 25 | #include <linux/interrupt.h>
|
|
45 | 46 | #define CCR_MTX 0x10
|
46 | 47 | #define CCR_TXAK 0x08
|
47 | 48 | #define CCR_RSTA 0x04
|
| 49 | +#define CCR_RSVD 0x02 |
48 | 50 |
|
49 | 51 | #define CSR_MCF 0x80
|
50 | 52 | #define CSR_MAAS 0x40
|
@@ -97,7 +99,7 @@ struct mpc_i2c {
|
97 | 99 | u32 block;
|
98 | 100 | int rc;
|
99 | 101 | int expect_rxack;
|
100 |
| - |
| 102 | + bool has_errata_A004447; |
101 | 103 | };
|
102 | 104 |
|
103 | 105 | struct mpc_i2c_divider {
|
@@ -136,6 +138,75 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
|
136 | 138 | }
|
137 | 139 | }
|
138 | 140 |
|
| 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 | + |
139 | 210 | #if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
|
140 | 211 | static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
|
141 | 212 | {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
|
@@ -670,7 +741,10 @@ static int fsl_i2c_bus_recovery(struct i2c_adapter *adap)
|
670 | 741 | {
|
671 | 742 | struct mpc_i2c *i2c = i2c_get_adapdata(adap);
|
672 | 743 |
|
673 |
| - mpc_i2c_fixup(i2c); |
| 744 | + if (i2c->has_errata_A004447) |
| 745 | + mpc_i2c_fixup_A004447(i2c); |
| 746 | + else |
| 747 | + mpc_i2c_fixup(i2c); |
674 | 748 |
|
675 | 749 | return 0;
|
676 | 750 | }
|
@@ -767,6 +841,9 @@ static int fsl_i2c_probe(struct platform_device *op)
|
767 | 841 | }
|
768 | 842 | dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
|
769 | 843 |
|
| 844 | + if (of_property_read_bool(op->dev.of_node, "fsl,i2c-erratum-a004447")) |
| 845 | + i2c->has_errata_A004447 = true; |
| 846 | + |
770 | 847 | i2c->adap = mpc_ops;
|
771 | 848 | scnprintf(i2c->adap.name, sizeof(i2c->adap.name),
|
772 | 849 | "MPC adapter (%s)", of_node_full_name(op->dev.of_node));
|
|
0 commit comments