diff options
Diffstat (limited to 'drivers/mfd/cros_ec_spi.c')
-rw-r--r-- | drivers/mfd/cros_ec_spi.c | 67 |
1 files changed, 50 insertions, 17 deletions
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 84af8d7a4295..0b8d32829166 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c | |||
@@ -39,14 +39,22 @@ | |||
39 | #define EC_MSG_PREAMBLE_COUNT 32 | 39 | #define EC_MSG_PREAMBLE_COUNT 32 |
40 | 40 | ||
41 | /* | 41 | /* |
42 | * We must get a response from the EC in 5ms. This is a very long | 42 | * Allow for a long time for the EC to respond. We support i2c |
43 | * time, but the flash write command can take 2-3ms. The EC command | 43 | * tunneling and support fairly long messages for the tunnel (249 |
44 | * processing is currently not very fast (about 500us). We could | 44 | * bytes long at the moment). If we're talking to a 100 kHz device |
45 | * look at speeding this up and making the flash write command a | 45 | * on the other end and need to transfer ~256 bytes, then we need: |
46 | * 'slow' command, requiring a GET_STATUS wait loop, like flash | 46 | * 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms |
47 | * erase. | 47 | * |
48 | */ | 48 | * We'll wait 4 times that to handle clock stretching and other |
49 | #define EC_MSG_DEADLINE_MS 5 | 49 | * paranoia. |
50 | * | ||
51 | * It's pretty unlikely that we'll really see a 249 byte tunnel in | ||
52 | * anything other than testing. If this was more common we might | ||
53 | * consider having slow commands like this require a GET_STATUS | ||
54 | * wait loop. The 'flash write' command would be another candidate | ||
55 | * for this, clocking in at 2-3ms. | ||
56 | */ | ||
57 | #define EC_MSG_DEADLINE_MS 100 | ||
50 | 58 | ||
51 | /* | 59 | /* |
52 | * Time between raising the SPI chip select (for the end of a | 60 | * Time between raising the SPI chip select (for the end of a |
@@ -65,11 +73,13 @@ | |||
65 | * if no record | 73 | * if no record |
66 | * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that | 74 | * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that |
67 | * is sent when we want to turn off CS at the end of a transaction. | 75 | * is sent when we want to turn off CS at the end of a transaction. |
76 | * @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time | ||
68 | */ | 77 | */ |
69 | struct cros_ec_spi { | 78 | struct cros_ec_spi { |
70 | struct spi_device *spi; | 79 | struct spi_device *spi; |
71 | s64 last_transfer_ns; | 80 | s64 last_transfer_ns; |
72 | unsigned int end_of_msg_delay; | 81 | unsigned int end_of_msg_delay; |
82 | struct mutex lock; | ||
73 | }; | 83 | }; |
74 | 84 | ||
75 | static void debug_packet(struct device *dev, const char *name, u8 *ptr, | 85 | static void debug_packet(struct device *dev, const char *name, u8 *ptr, |
@@ -111,7 +121,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, | |||
111 | 121 | ||
112 | /* Receive data until we see the header byte */ | 122 | /* Receive data until we see the header byte */ |
113 | deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); | 123 | deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); |
114 | do { | 124 | while (true) { |
125 | unsigned long start_jiffies = jiffies; | ||
126 | |||
115 | memset(&trans, 0, sizeof(trans)); | 127 | memset(&trans, 0, sizeof(trans)); |
116 | trans.cs_change = 1; | 128 | trans.cs_change = 1; |
117 | trans.rx_buf = ptr = ec_dev->din; | 129 | trans.rx_buf = ptr = ec_dev->din; |
@@ -132,12 +144,19 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, | |||
132 | break; | 144 | break; |
133 | } | 145 | } |
134 | } | 146 | } |
147 | if (ptr != end) | ||
148 | break; | ||
135 | 149 | ||
136 | if (time_after(jiffies, deadline)) { | 150 | /* |
151 | * Use the time at the start of the loop as a timeout. This | ||
152 | * gives us one last shot at getting the transfer and is useful | ||
153 | * in case we got context switched out for a while. | ||
154 | */ | ||
155 | if (time_after(start_jiffies, deadline)) { | ||
137 | dev_warn(ec_dev->dev, "EC failed to respond in time\n"); | 156 | dev_warn(ec_dev->dev, "EC failed to respond in time\n"); |
138 | return -ETIMEDOUT; | 157 | return -ETIMEDOUT; |
139 | } | 158 | } |
140 | } while (ptr == end); | 159 | } |
141 | 160 | ||
142 | /* | 161 | /* |
143 | * ptr now points to the header byte. Copy any valid data to the | 162 | * ptr now points to the header byte. Copy any valid data to the |
@@ -208,6 +227,13 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
208 | int ret = 0, final_ret; | 227 | int ret = 0, final_ret; |
209 | struct timespec ts; | 228 | struct timespec ts; |
210 | 229 | ||
230 | /* | ||
231 | * We have the shared ec_dev buffer plus we do lots of separate spi_sync | ||
232 | * calls, so we need to make sure only one person is using this at a | ||
233 | * time. | ||
234 | */ | ||
235 | mutex_lock(&ec_spi->lock); | ||
236 | |||
211 | len = cros_ec_prepare_tx(ec_dev, ec_msg); | 237 | len = cros_ec_prepare_tx(ec_dev, ec_msg); |
212 | dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); | 238 | dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); |
213 | 239 | ||
@@ -219,7 +245,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
219 | ktime_get_ts(&ts); | 245 | ktime_get_ts(&ts); |
220 | delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; | 246 | delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; |
221 | if (delay < EC_SPI_RECOVERY_TIME_NS) | 247 | if (delay < EC_SPI_RECOVERY_TIME_NS) |
222 | ndelay(delay); | 248 | ndelay(EC_SPI_RECOVERY_TIME_NS - delay); |
223 | } | 249 | } |
224 | 250 | ||
225 | /* Transmit phase - send our message */ | 251 | /* Transmit phase - send our message */ |
@@ -260,7 +286,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
260 | ret = final_ret; | 286 | ret = final_ret; |
261 | if (ret < 0) { | 287 | if (ret < 0) { |
262 | dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); | 288 | dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); |
263 | return ret; | 289 | goto exit; |
264 | } | 290 | } |
265 | 291 | ||
266 | /* check response error code */ | 292 | /* check response error code */ |
@@ -269,14 +295,16 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
269 | dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", | 295 | dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", |
270 | ec_msg->cmd, ptr[0]); | 296 | ec_msg->cmd, ptr[0]); |
271 | debug_packet(ec_dev->dev, "in_err", ptr, len); | 297 | debug_packet(ec_dev->dev, "in_err", ptr, len); |
272 | return -EINVAL; | 298 | ret = -EINVAL; |
299 | goto exit; | ||
273 | } | 300 | } |
274 | len = ptr[1]; | 301 | len = ptr[1]; |
275 | sum = ptr[0] + ptr[1]; | 302 | sum = ptr[0] + ptr[1]; |
276 | if (len > ec_msg->in_len) { | 303 | if (len > ec_msg->in_len) { |
277 | dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", | 304 | dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", |
278 | len, ec_msg->in_len); | 305 | len, ec_msg->in_len); |
279 | return -ENOSPC; | 306 | ret = -ENOSPC; |
307 | goto exit; | ||
280 | } | 308 | } |
281 | 309 | ||
282 | /* copy response packet payload and compute checksum */ | 310 | /* copy response packet payload and compute checksum */ |
@@ -293,10 +321,14 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
293 | dev_err(ec_dev->dev, | 321 | dev_err(ec_dev->dev, |
294 | "bad packet checksum, expected %02x, got %02x\n", | 322 | "bad packet checksum, expected %02x, got %02x\n", |
295 | sum, ptr[len + 2]); | 323 | sum, ptr[len + 2]); |
296 | return -EBADMSG; | 324 | ret = -EBADMSG; |
325 | goto exit; | ||
297 | } | 326 | } |
298 | 327 | ||
299 | return 0; | 328 | ret = 0; |
329 | exit: | ||
330 | mutex_unlock(&ec_spi->lock); | ||
331 | return ret; | ||
300 | } | 332 | } |
301 | 333 | ||
302 | static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) | 334 | static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) |
@@ -327,6 +359,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) | |||
327 | if (ec_spi == NULL) | 359 | if (ec_spi == NULL) |
328 | return -ENOMEM; | 360 | return -ENOMEM; |
329 | ec_spi->spi = spi; | 361 | ec_spi->spi = spi; |
362 | mutex_init(&ec_spi->lock); | ||
330 | ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); | 363 | ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); |
331 | if (!ec_dev) | 364 | if (!ec_dev) |
332 | return -ENOMEM; | 365 | return -ENOMEM; |