diff options
author | Doug Anderson <dianders@chromium.org> | 2014-04-30 13:44:05 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-06-03 03:11:46 -0400 |
commit | 362196e5d0832ce3c4e1df376c02cff812a2391b (patch) | |
tree | 3ef8f401d423fe0011e1bdfe573fea914feded82 /drivers/mfd | |
parent | 1fe368665b1499041919d78467147849989af7c9 (diff) |
mfd: cros_ec: spi: Add mutex to cros_ec_spi
The main transfer function for cros_ec_spi can be called by more than
one client at a time. Make sure that those clients don't stomp on
each other by locking the bus for the duration of the transfer
function.
Signed-off-by: Doug Anderson <dianders@chromium.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Andrew Bresticker <abrestic@chromium.org>
Tested-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/cros_ec_spi.c | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index c185eb67943f..a2a605de79a7 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c | |||
@@ -65,11 +65,13 @@ | |||
65 | * if no record | 65 | * if no record |
66 | * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that | 66 | * @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. | 67 | * is sent when we want to turn off CS at the end of a transaction. |
68 | * @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time | ||
68 | */ | 69 | */ |
69 | struct cros_ec_spi { | 70 | struct cros_ec_spi { |
70 | struct spi_device *spi; | 71 | struct spi_device *spi; |
71 | s64 last_transfer_ns; | 72 | s64 last_transfer_ns; |
72 | unsigned int end_of_msg_delay; | 73 | unsigned int end_of_msg_delay; |
74 | struct mutex lock; | ||
73 | }; | 75 | }; |
74 | 76 | ||
75 | static void debug_packet(struct device *dev, const char *name, u8 *ptr, | 77 | static void debug_packet(struct device *dev, const char *name, u8 *ptr, |
@@ -208,6 +210,13 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
208 | int ret = 0, final_ret; | 210 | int ret = 0, final_ret; |
209 | struct timespec ts; | 211 | struct timespec ts; |
210 | 212 | ||
213 | /* | ||
214 | * We have the shared ec_dev buffer plus we do lots of separate spi_sync | ||
215 | * calls, so we need to make sure only one person is using this at a | ||
216 | * time. | ||
217 | */ | ||
218 | mutex_lock(&ec_spi->lock); | ||
219 | |||
211 | len = cros_ec_prepare_tx(ec_dev, ec_msg); | 220 | len = cros_ec_prepare_tx(ec_dev, ec_msg); |
212 | dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); | 221 | dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); |
213 | 222 | ||
@@ -260,7 +269,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
260 | ret = final_ret; | 269 | ret = final_ret; |
261 | if (ret < 0) { | 270 | if (ret < 0) { |
262 | dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); | 271 | dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); |
263 | return ret; | 272 | goto exit; |
264 | } | 273 | } |
265 | 274 | ||
266 | /* check response error code */ | 275 | /* check response error code */ |
@@ -269,14 +278,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", | 278 | dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", |
270 | ec_msg->cmd, ptr[0]); | 279 | ec_msg->cmd, ptr[0]); |
271 | debug_packet(ec_dev->dev, "in_err", ptr, len); | 280 | debug_packet(ec_dev->dev, "in_err", ptr, len); |
272 | return -EINVAL; | 281 | ret = -EINVAL; |
282 | goto exit; | ||
273 | } | 283 | } |
274 | len = ptr[1]; | 284 | len = ptr[1]; |
275 | sum = ptr[0] + ptr[1]; | 285 | sum = ptr[0] + ptr[1]; |
276 | if (len > ec_msg->in_len) { | 286 | if (len > ec_msg->in_len) { |
277 | dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", | 287 | dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", |
278 | len, ec_msg->in_len); | 288 | len, ec_msg->in_len); |
279 | return -ENOSPC; | 289 | ret = -ENOSPC; |
290 | goto exit; | ||
280 | } | 291 | } |
281 | 292 | ||
282 | /* copy response packet payload and compute checksum */ | 293 | /* copy response packet payload and compute checksum */ |
@@ -293,10 +304,14 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, | |||
293 | dev_err(ec_dev->dev, | 304 | dev_err(ec_dev->dev, |
294 | "bad packet checksum, expected %02x, got %02x\n", | 305 | "bad packet checksum, expected %02x, got %02x\n", |
295 | sum, ptr[len + 2]); | 306 | sum, ptr[len + 2]); |
296 | return -EBADMSG; | 307 | ret = -EBADMSG; |
308 | goto exit; | ||
297 | } | 309 | } |
298 | 310 | ||
299 | return 0; | 311 | ret = 0; |
312 | exit: | ||
313 | mutex_unlock(&ec_spi->lock); | ||
314 | return ret; | ||
300 | } | 315 | } |
301 | 316 | ||
302 | static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) | 317 | static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) |
@@ -327,6 +342,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) | |||
327 | if (ec_spi == NULL) | 342 | if (ec_spi == NULL) |
328 | return -ENOMEM; | 343 | return -ENOMEM; |
329 | ec_spi->spi = spi; | 344 | ec_spi->spi = spi; |
345 | mutex_init(&ec_spi->lock); | ||
330 | ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); | 346 | ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); |
331 | if (!ec_dev) | 347 | if (!ec_dev) |
332 | return -ENOMEM; | 348 | return -ENOMEM; |