.. | .. |
---|
1 | | -/* |
---|
2 | | - * ChromeOS EC multi-function device (SPI) |
---|
3 | | - * |
---|
4 | | - * Copyright (C) 2012 Google, Inc |
---|
5 | | - * |
---|
6 | | - * This software is licensed under the terms of the GNU General Public |
---|
7 | | - * License version 2, as published by the Free Software Foundation, and |
---|
8 | | - * may be copied, distributed, and modified under those terms. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, |
---|
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | | - * GNU General Public License for more details. |
---|
14 | | - */ |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
| 2 | +// SPI interface for ChromeOS Embedded Controller |
---|
| 3 | +// |
---|
| 4 | +// Copyright (C) 2012 Google, Inc |
---|
15 | 5 | |
---|
16 | 6 | #include <linux/delay.h> |
---|
17 | 7 | #include <linux/kernel.h> |
---|
18 | 8 | #include <linux/module.h> |
---|
19 | | -#include <linux/mfd/cros_ec.h> |
---|
20 | | -#include <linux/mfd/cros_ec_commands.h> |
---|
21 | 9 | #include <linux/of.h> |
---|
| 10 | +#include <linux/platform_data/cros_ec_commands.h> |
---|
| 11 | +#include <linux/platform_data/cros_ec_proto.h> |
---|
22 | 12 | #include <linux/platform_device.h> |
---|
23 | 13 | #include <linux/slab.h> |
---|
24 | 14 | #include <linux/spi/spi.h> |
---|
| 15 | +#include <uapi/linux/sched/types.h> |
---|
25 | 16 | |
---|
| 17 | +#include "cros_ec.h" |
---|
26 | 18 | |
---|
27 | 19 | /* The header byte, which follows the preamble */ |
---|
28 | 20 | #define EC_MSG_HEADER 0xec |
---|
.. | .. |
---|
77 | 69 | * is sent when we want to turn on CS at the start of a transaction. |
---|
78 | 70 | * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that |
---|
79 | 71 | * is sent when we want to turn off CS at the end of a transaction. |
---|
| 72 | + * @high_pri_worker: Used to schedule high priority work. |
---|
80 | 73 | */ |
---|
81 | 74 | struct cros_ec_spi { |
---|
82 | 75 | struct spi_device *spi; |
---|
83 | 76 | s64 last_transfer_ns; |
---|
84 | 77 | unsigned int start_of_msg_delay; |
---|
85 | 78 | unsigned int end_of_msg_delay; |
---|
| 79 | + struct kthread_worker *high_pri_worker; |
---|
| 80 | +}; |
---|
| 81 | + |
---|
| 82 | +typedef int (*cros_ec_xfer_fn_t) (struct cros_ec_device *ec_dev, |
---|
| 83 | + struct cros_ec_command *ec_msg); |
---|
| 84 | + |
---|
| 85 | +/** |
---|
| 86 | + * struct cros_ec_xfer_work_params - params for our high priority workers |
---|
| 87 | + * |
---|
| 88 | + * @work: The work_struct needed to queue work |
---|
| 89 | + * @fn: The function to use to transfer |
---|
| 90 | + * @ec_dev: ChromeOS EC device |
---|
| 91 | + * @ec_msg: Message to transfer |
---|
| 92 | + * @ret: The return value of the function |
---|
| 93 | + */ |
---|
| 94 | + |
---|
| 95 | +struct cros_ec_xfer_work_params { |
---|
| 96 | + struct kthread_work work; |
---|
| 97 | + cros_ec_xfer_fn_t fn; |
---|
| 98 | + struct cros_ec_device *ec_dev; |
---|
| 99 | + struct cros_ec_command *ec_msg; |
---|
| 100 | + int ret; |
---|
86 | 101 | }; |
---|
87 | 102 | |
---|
88 | 103 | static void debug_packet(struct device *dev, const char *name, u8 *ptr, |
---|
.. | .. |
---|
112 | 127 | */ |
---|
113 | 128 | spi_message_init(&msg); |
---|
114 | 129 | memset(&trans, 0, sizeof(trans)); |
---|
115 | | - trans.delay_usecs = ec_spi->end_of_msg_delay; |
---|
| 130 | + trans.delay.value = ec_spi->end_of_msg_delay; |
---|
| 131 | + trans.delay.unit = SPI_DELAY_UNIT_USECS; |
---|
116 | 132 | spi_message_add_tail(&trans, &msg); |
---|
117 | 133 | |
---|
118 | 134 | ret = spi_sync_locked(ec_spi->spi, &msg); |
---|
.. | .. |
---|
132 | 148 | * receive_n_bytes - receive n bytes from the EC. |
---|
133 | 149 | * |
---|
134 | 150 | * Assumes buf is a pointer into the ec_dev->din buffer |
---|
| 151 | + * |
---|
| 152 | + * @ec_dev: ChromeOS EC device. |
---|
| 153 | + * @buf: Pointer to the buffer receiving the data. |
---|
| 154 | + * @n: Number of bytes received. |
---|
135 | 155 | */ |
---|
136 | 156 | static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n) |
---|
137 | 157 | { |
---|
.. | .. |
---|
360 | 380 | } |
---|
361 | 381 | |
---|
362 | 382 | /** |
---|
363 | | - * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply |
---|
| 383 | + * do_cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply |
---|
364 | 384 | * |
---|
365 | 385 | * @ec_dev: ChromeOS EC device |
---|
366 | 386 | * @ec_msg: Message to transfer |
---|
367 | 387 | */ |
---|
368 | | -static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, |
---|
369 | | - struct cros_ec_command *ec_msg) |
---|
| 388 | +static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, |
---|
| 389 | + struct cros_ec_command *ec_msg) |
---|
370 | 390 | { |
---|
371 | 391 | struct ec_host_response *response; |
---|
372 | 392 | struct cros_ec_spi *ec_spi = ec_dev->priv; |
---|
.. | .. |
---|
401 | 421 | spi_message_init(&msg); |
---|
402 | 422 | if (ec_spi->start_of_msg_delay) { |
---|
403 | 423 | memset(&trans_delay, 0, sizeof(trans_delay)); |
---|
404 | | - trans_delay.delay_usecs = ec_spi->start_of_msg_delay; |
---|
| 424 | + trans_delay.delay.value = ec_spi->start_of_msg_delay; |
---|
| 425 | + trans_delay.delay.unit = SPI_DELAY_UNIT_USECS; |
---|
405 | 426 | spi_message_add_tail(&trans_delay, &msg); |
---|
406 | 427 | } |
---|
407 | 428 | |
---|
.. | .. |
---|
503 | 524 | } |
---|
504 | 525 | |
---|
505 | 526 | /** |
---|
506 | | - * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply |
---|
| 527 | + * do_cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply |
---|
507 | 528 | * |
---|
508 | 529 | * @ec_dev: ChromeOS EC device |
---|
509 | 530 | * @ec_msg: Message to transfer |
---|
510 | 531 | */ |
---|
511 | | -static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, |
---|
512 | | - struct cros_ec_command *ec_msg) |
---|
| 532 | +static int do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, |
---|
| 533 | + struct cros_ec_command *ec_msg) |
---|
513 | 534 | { |
---|
514 | 535 | struct cros_ec_spi *ec_spi = ec_dev->priv; |
---|
515 | 536 | struct spi_transfer trans; |
---|
.. | .. |
---|
621 | 642 | return ret; |
---|
622 | 643 | } |
---|
623 | 644 | |
---|
| 645 | +static void cros_ec_xfer_high_pri_work(struct kthread_work *work) |
---|
| 646 | +{ |
---|
| 647 | + struct cros_ec_xfer_work_params *params; |
---|
| 648 | + |
---|
| 649 | + params = container_of(work, struct cros_ec_xfer_work_params, work); |
---|
| 650 | + params->ret = params->fn(params->ec_dev, params->ec_msg); |
---|
| 651 | +} |
---|
| 652 | + |
---|
| 653 | +static int cros_ec_xfer_high_pri(struct cros_ec_device *ec_dev, |
---|
| 654 | + struct cros_ec_command *ec_msg, |
---|
| 655 | + cros_ec_xfer_fn_t fn) |
---|
| 656 | +{ |
---|
| 657 | + struct cros_ec_spi *ec_spi = ec_dev->priv; |
---|
| 658 | + struct cros_ec_xfer_work_params params = { |
---|
| 659 | + .work = KTHREAD_WORK_INIT(params.work, |
---|
| 660 | + cros_ec_xfer_high_pri_work), |
---|
| 661 | + .ec_dev = ec_dev, |
---|
| 662 | + .ec_msg = ec_msg, |
---|
| 663 | + .fn = fn, |
---|
| 664 | + }; |
---|
| 665 | + |
---|
| 666 | + /* |
---|
| 667 | + * This looks a bit ridiculous. Why do the work on a |
---|
| 668 | + * different thread if we're just going to block waiting for |
---|
| 669 | + * the thread to finish? The key here is that the thread is |
---|
| 670 | + * running at high priority but the calling context might not |
---|
| 671 | + * be. We need to be at high priority to avoid getting |
---|
| 672 | + * context switched out for too long and the EC giving up on |
---|
| 673 | + * the transfer. |
---|
| 674 | + */ |
---|
| 675 | + kthread_queue_work(ec_spi->high_pri_worker, ¶ms.work); |
---|
| 676 | + kthread_flush_work(¶ms.work); |
---|
| 677 | + |
---|
| 678 | + return params.ret; |
---|
| 679 | +} |
---|
| 680 | + |
---|
| 681 | +static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, |
---|
| 682 | + struct cros_ec_command *ec_msg) |
---|
| 683 | +{ |
---|
| 684 | + return cros_ec_xfer_high_pri(ec_dev, ec_msg, do_cros_ec_pkt_xfer_spi); |
---|
| 685 | +} |
---|
| 686 | + |
---|
| 687 | +static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, |
---|
| 688 | + struct cros_ec_command *ec_msg) |
---|
| 689 | +{ |
---|
| 690 | + return cros_ec_xfer_high_pri(ec_dev, ec_msg, do_cros_ec_cmd_xfer_spi); |
---|
| 691 | +} |
---|
| 692 | + |
---|
624 | 693 | static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) |
---|
625 | 694 | { |
---|
626 | 695 | struct device_node *np = dev->of_node; |
---|
.. | .. |
---|
636 | 705 | ec_spi->end_of_msg_delay = val; |
---|
637 | 706 | } |
---|
638 | 707 | |
---|
| 708 | +static void cros_ec_spi_high_pri_release(void *worker) |
---|
| 709 | +{ |
---|
| 710 | + kthread_destroy_worker(worker); |
---|
| 711 | +} |
---|
| 712 | + |
---|
| 713 | +static int cros_ec_spi_devm_high_pri_alloc(struct device *dev, |
---|
| 714 | + struct cros_ec_spi *ec_spi) |
---|
| 715 | +{ |
---|
| 716 | + int err; |
---|
| 717 | + |
---|
| 718 | + ec_spi->high_pri_worker = |
---|
| 719 | + kthread_create_worker(0, "cros_ec_spi_high_pri"); |
---|
| 720 | + |
---|
| 721 | + if (IS_ERR(ec_spi->high_pri_worker)) { |
---|
| 722 | + err = PTR_ERR(ec_spi->high_pri_worker); |
---|
| 723 | + dev_err(dev, "Can't create cros_ec high pri worker: %d\n", err); |
---|
| 724 | + return err; |
---|
| 725 | + } |
---|
| 726 | + |
---|
| 727 | + err = devm_add_action_or_reset(dev, cros_ec_spi_high_pri_release, |
---|
| 728 | + ec_spi->high_pri_worker); |
---|
| 729 | + if (err) |
---|
| 730 | + return err; |
---|
| 731 | + |
---|
| 732 | + sched_set_fifo(ec_spi->high_pri_worker->task); |
---|
| 733 | + |
---|
| 734 | + return 0; |
---|
| 735 | +} |
---|
| 736 | + |
---|
639 | 737 | static int cros_ec_spi_probe(struct spi_device *spi) |
---|
640 | 738 | { |
---|
641 | 739 | struct device *dev = &spi->dev; |
---|
.. | .. |
---|
644 | 742 | int err; |
---|
645 | 743 | |
---|
646 | 744 | spi->bits_per_word = 8; |
---|
647 | | - spi->mode = SPI_MODE_0; |
---|
| 745 | + spi->rt = true; |
---|
648 | 746 | err = spi_setup(spi); |
---|
649 | 747 | if (err < 0) |
---|
650 | 748 | return err; |
---|
.. | .. |
---|
674 | 772 | |
---|
675 | 773 | ec_spi->last_transfer_ns = ktime_get_ns(); |
---|
676 | 774 | |
---|
| 775 | + err = cros_ec_spi_devm_high_pri_alloc(dev, ec_spi); |
---|
| 776 | + if (err) |
---|
| 777 | + return err; |
---|
| 778 | + |
---|
677 | 779 | err = cros_ec_register(ec_dev); |
---|
678 | 780 | if (err) { |
---|
679 | 781 | dev_err(dev, "cannot register EC\n"); |
---|
.. | .. |
---|
687 | 789 | |
---|
688 | 790 | static int cros_ec_spi_remove(struct spi_device *spi) |
---|
689 | 791 | { |
---|
690 | | - struct cros_ec_device *ec_dev; |
---|
| 792 | + struct cros_ec_device *ec_dev = spi_get_drvdata(spi); |
---|
691 | 793 | |
---|
692 | | - ec_dev = spi_get_drvdata(spi); |
---|
693 | | - cros_ec_remove(ec_dev); |
---|
694 | | - |
---|
695 | | - return 0; |
---|
| 794 | + return cros_ec_unregister(ec_dev); |
---|
696 | 795 | } |
---|
697 | 796 | |
---|
698 | 797 | #ifdef CONFIG_PM_SLEEP |
---|
.. | .. |
---|
729 | 828 | static struct spi_driver cros_ec_driver_spi = { |
---|
730 | 829 | .driver = { |
---|
731 | 830 | .name = "cros-ec-spi", |
---|
732 | | - .of_match_table = of_match_ptr(cros_ec_spi_of_match), |
---|
| 831 | + .of_match_table = cros_ec_spi_of_match, |
---|
733 | 832 | .pm = &cros_ec_spi_pm_ops, |
---|
734 | 833 | }, |
---|
735 | 834 | .probe = cros_ec_spi_probe, |
---|
.. | .. |
---|
740 | 839 | module_spi_driver(cros_ec_driver_spi); |
---|
741 | 840 | |
---|
742 | 841 | MODULE_LICENSE("GPL v2"); |
---|
743 | | -MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); |
---|
| 842 | +MODULE_DESCRIPTION("SPI interface for ChromeOS Embedded Controller"); |
---|