diff options
author | Kevin Hilman <khilman@mvista.com> | 2007-02-06 23:46:47 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-18 05:59:50 -0500 |
commit | 14d178a143568f3651a40af6defadd44fb0b6b81 (patch) | |
tree | 491c7ad0258ac5852131d23815dbaf13d0ecaaa9 | |
parent | 41762b8ca9e16c7443d8348ec53daddbe940cdcc (diff) |
[ARM] 4140/1: AACI stability add ac97 timeout and retries
Add timeouts to hardware read/write/probe functions in order
to avoid lockups on buggy/broken hardware.
Signed-off-by: Kevin Hilman <khilman@mvista.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | sound/arm/aaci.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index a032aee82adf..5190d7acdb9f 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c | |||
@@ -65,10 +65,12 @@ static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97) | |||
65 | * SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR | 65 | * SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR |
66 | * register. | 66 | * register. |
67 | */ | 67 | */ |
68 | static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) | 68 | static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, |
69 | unsigned short val) | ||
69 | { | 70 | { |
70 | struct aaci *aaci = ac97->private_data; | 71 | struct aaci *aaci = ac97->private_data; |
71 | u32 v; | 72 | u32 v; |
73 | int timeout = 5000; | ||
72 | 74 | ||
73 | if (ac97->num >= 4) | 75 | if (ac97->num >= 4) |
74 | return; | 76 | return; |
@@ -89,7 +91,11 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned | |||
89 | */ | 91 | */ |
90 | do { | 92 | do { |
91 | v = readl(aaci->base + AACI_SLFR); | 93 | v = readl(aaci->base + AACI_SLFR); |
92 | } while (v & (SLFR_1TXB|SLFR_2TXB)); | 94 | } while ((v & (SLFR_1TXB|SLFR_2TXB)) && timeout--); |
95 | |||
96 | if (!timeout) | ||
97 | dev_err(&aaci->dev->dev, | ||
98 | "timeout waiting for write to complete\n"); | ||
93 | 99 | ||
94 | mutex_unlock(&aaci->ac97_sem); | 100 | mutex_unlock(&aaci->ac97_sem); |
95 | } | 101 | } |
@@ -101,6 +107,8 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | |||
101 | { | 107 | { |
102 | struct aaci *aaci = ac97->private_data; | 108 | struct aaci *aaci = ac97->private_data; |
103 | u32 v; | 109 | u32 v; |
110 | int timeout = 5000; | ||
111 | int retries = 10; | ||
104 | 112 | ||
105 | if (ac97->num >= 4) | 113 | if (ac97->num >= 4) |
106 | return ~0; | 114 | return ~0; |
@@ -119,7 +127,13 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | |||
119 | */ | 127 | */ |
120 | do { | 128 | do { |
121 | v = readl(aaci->base + AACI_SLFR); | 129 | v = readl(aaci->base + AACI_SLFR); |
122 | } while (v & SLFR_1TXB); | 130 | } while ((v & SLFR_1TXB) && timeout--); |
131 | |||
132 | if (!timeout) { | ||
133 | dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n"); | ||
134 | v = ~0; | ||
135 | goto out; | ||
136 | } | ||
123 | 137 | ||
124 | /* | 138 | /* |
125 | * Give the AC'97 codec more than enough time | 139 | * Give the AC'97 codec more than enough time |
@@ -130,21 +144,35 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | |||
130 | /* | 144 | /* |
131 | * Wait for slot 2 to indicate data. | 145 | * Wait for slot 2 to indicate data. |
132 | */ | 146 | */ |
147 | timeout = 5000; | ||
133 | do { | 148 | do { |
134 | cond_resched(); | 149 | cond_resched(); |
135 | v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV); | 150 | v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV); |
136 | } while (v != (SLFR_1RXV|SLFR_2RXV)); | 151 | } while ((v != (SLFR_1RXV|SLFR_2RXV)) && timeout--); |
137 | 152 | ||
138 | v = readl(aaci->base + AACI_SL1RX) >> 12; | 153 | if (!timeout) { |
139 | if (v == reg) { | 154 | dev_err(&aaci->dev->dev, "timeout on RX valid\n"); |
140 | v = readl(aaci->base + AACI_SL2RX) >> 4; | ||
141 | } else { | ||
142 | dev_err(&aaci->dev->dev, | ||
143 | "wrong ac97 register read back (%x != %x)\n", | ||
144 | v, reg); | ||
145 | v = ~0; | 155 | v = ~0; |
156 | goto out; | ||
146 | } | 157 | } |
147 | 158 | ||
159 | do { | ||
160 | v = readl(aaci->base + AACI_SL1RX) >> 12; | ||
161 | if (v == reg) { | ||
162 | v = readl(aaci->base + AACI_SL2RX) >> 4; | ||
163 | break; | ||
164 | } else if (--retries) { | ||
165 | dev_warn(&aaci->dev->dev, | ||
166 | "ac97 read back fail. retry\n"); | ||
167 | continue; | ||
168 | } else { | ||
169 | dev_warn(&aaci->dev->dev, | ||
170 | "wrong ac97 register read back (%x != %x)\n", | ||
171 | v, reg); | ||
172 | v = ~0; | ||
173 | } | ||
174 | } while (retries); | ||
175 | out: | ||
148 | mutex_unlock(&aaci->ac97_sem); | 176 | mutex_unlock(&aaci->ac97_sem); |
149 | return v; | 177 | return v; |
150 | } | 178 | } |