3232#include <linux/hwmon.h>
3333#include <linux/workqueue.h>
3434#include <linux/err.h>
35+ #include <linux/bits.h>
3536
3637/* data port used by Apple SMC */
3738#define APPLESMC_DATA_PORT 0x300
4243
4344#define APPLESMC_MAX_DATA_LENGTH 32
4445
45- /* wait up to 128 ms for a status change. */
46- #define APPLESMC_MIN_WAIT 0x0010
47- #define APPLESMC_RETRY_WAIT 0x0100
48- #define APPLESMC_MAX_WAIT 0x20000
46+ /* Apple SMC status bits */
47+ #define SMC_STATUS_AWAITING_DATA BIT(0) /* SMC has data waiting to be read */
48+ #define SMC_STATUS_IB_CLOSED BIT(1) /* Will ignore any input */
49+ #define SMC_STATUS_BUSY BIT(2) /* Command in progress */
50+
51+ /* Initial wait is 8us */
52+ #define APPLESMC_MIN_WAIT 0x0008
4953
5054#define APPLESMC_READ_CMD 0x10
5155#define APPLESMC_WRITE_CMD 0x11
@@ -151,65 +155,84 @@ static unsigned int key_at_index;
151155static struct workqueue_struct * applesmc_led_wq ;
152156
153157/*
154- * wait_read - Wait for a byte to appear on SMC port. Callers must
155- * hold applesmc_lock.
158+ * Wait for specific status bits with a mask on the SMC.
159+ * Used before all transactions.
160+ * This does 10 fast loops of 8us then exponentially backs off for a
161+ * minimum total wait of 262ms. Depending on usleep_range this could
162+ * run out past 500ms.
156163 */
157- static int wait_read (void )
164+
165+ static int wait_status (u8 val , u8 mask )
158166{
159- unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ ) / USEC_PER_SEC ;
160167 u8 status ;
161168 int us ;
169+ int i ;
162170
163- for ( us = APPLESMC_MIN_WAIT ; us < APPLESMC_MAX_WAIT ; us <<= 1 ) {
164- usleep_range ( us , us * 16 );
171+ us = APPLESMC_MIN_WAIT ;
172+ for ( i = 0 ; i < 24 ; i ++ ) {
165173 status = inb (APPLESMC_CMD_PORT );
166- /* read: wait for smc to settle */
167- if (status & 0x01 )
174+ if ((status & mask ) == val )
168175 return 0 ;
169- /* timeout: give up */
170- if (time_after ( jiffies , end ) )
171- break ;
176+ usleep_range ( us , us * 2 );
177+ if (i > 9 )
178+ us <<= 1 ;
172179 }
173-
174- pr_warn ("wait_read() fail: 0x%02x\n" , status );
175180 return - EIO ;
176181}
177182
178- /*
179- * send_byte - Write to SMC port, retrying when necessary. Callers
180- * must hold applesmc_lock.
181- */
183+ /* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
184+
182185static int send_byte (u8 cmd , u16 port )
183186{
184- u8 status ;
185- int us ;
186- unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ ) / USEC_PER_SEC ;
187+ int status ;
188+
189+ status = wait_status (0 , SMC_STATUS_IB_CLOSED );
190+ if (status )
191+ return status ;
192+ /*
193+ * This needs to be a separate read looking for bit 0x04
194+ * after bit 0x02 falls. If consolidated with the wait above
195+ * this extra read may not happen if status returns both
196+ * simultaneously and this would appear to be required.
197+ */
198+ status = wait_status (SMC_STATUS_BUSY , SMC_STATUS_BUSY );
199+ if (status )
200+ return status ;
187201
188202 outb (cmd , port );
189- for (us = APPLESMC_MIN_WAIT ; us < APPLESMC_MAX_WAIT ; us <<= 1 ) {
190- usleep_range (us , us * 16 );
191- status = inb (APPLESMC_CMD_PORT );
192- /* write: wait for smc to settle */
193- if (status & 0x02 )
194- continue ;
195- /* ready: cmd accepted, return */
196- if (status & 0x04 )
197- return 0 ;
198- /* timeout: give up */
199- if (time_after (jiffies , end ))
200- break ;
201- /* busy: long wait and resend */
202- udelay (APPLESMC_RETRY_WAIT );
203- outb (cmd , port );
204- }
205-
206- pr_warn ("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n" , cmd , port , status );
207- return - EIO ;
203+ return 0 ;
208204}
209205
206+ /* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
207+
210208static int send_command (u8 cmd )
211209{
212- return send_byte (cmd , APPLESMC_CMD_PORT );
210+ int ret ;
211+
212+ ret = wait_status (0 , SMC_STATUS_IB_CLOSED );
213+ if (ret )
214+ return ret ;
215+ outb (cmd , APPLESMC_CMD_PORT );
216+ return 0 ;
217+ }
218+
219+ /*
220+ * Based on logic from the Apple driver. This is issued before any interaction
221+ * If busy is stuck high, issue a read command to reset the SMC state machine.
222+ * If busy is stuck high after the command then the SMC is jammed.
223+ */
224+
225+ static int smc_sane (void )
226+ {
227+ int ret ;
228+
229+ ret = wait_status (0 , SMC_STATUS_BUSY );
230+ if (!ret )
231+ return ret ;
232+ ret = send_command (APPLESMC_READ_CMD );
233+ if (ret )
234+ return ret ;
235+ return wait_status (0 , SMC_STATUS_BUSY );
213236}
214237
215238static int send_argument (const char * key )
@@ -226,6 +249,11 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
226249{
227250 u8 status , data = 0 ;
228251 int i ;
252+ int ret ;
253+
254+ ret = smc_sane ();
255+ if (ret )
256+ return ret ;
229257
230258 if (send_command (cmd ) || send_argument (key )) {
231259 pr_warn ("%.4s: read arg fail\n" , key );
@@ -239,7 +267,8 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
239267 }
240268
241269 for (i = 0 ; i < len ; i ++ ) {
242- if (wait_read ()) {
270+ if (wait_status (SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY ,
271+ SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY )) {
243272 pr_warn ("%.4s: read data[%d] fail\n" , key , i );
244273 return - EIO ;
245274 }
@@ -250,19 +279,24 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
250279 for (i = 0 ; i < 16 ; i ++ ) {
251280 udelay (APPLESMC_MIN_WAIT );
252281 status = inb (APPLESMC_CMD_PORT );
253- if (!(status & 0x01 ))
282+ if (!(status & SMC_STATUS_AWAITING_DATA ))
254283 break ;
255284 data = inb (APPLESMC_DATA_PORT );
256285 }
257286 if (i )
258287 pr_warn ("flushed %d bytes, last value is: %d\n" , i , data );
259288
260- return 0 ;
289+ return wait_status ( 0 , SMC_STATUS_BUSY ) ;
261290}
262291
263292static int write_smc (u8 cmd , const char * key , const u8 * buffer , u8 len )
264293{
265294 int i ;
295+ int ret ;
296+
297+ ret = smc_sane ();
298+ if (ret )
299+ return ret ;
266300
267301 if (send_command (cmd ) || send_argument (key )) {
268302 pr_warn ("%s: write arg fail\n" , key );
@@ -281,7 +315,7 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
281315 }
282316 }
283317
284- return 0 ;
318+ return wait_status ( 0 , SMC_STATUS_BUSY ) ;
285319}
286320
287321static int read_register_count (unsigned int * count )
0 commit comments