Merge tag 'platform-drivers-x86-v4.20-1' of git://git.infradead.org/linux-platform...
[sfrench/cifs-2.6.git] / drivers / platform / chrome / cros_ec_lpc_reg.c
1 /*
2  * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller
3  *
4  * Copyright (C) 2016 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  *
15  * This driver uses the Chrome OS EC byte-level message-based protocol for
16  * communicating the keyboard state (which keys are pressed) from a keyboard EC
17  * to the AP over some bus (such as i2c, lpc, spi).  The EC does debouncing,
18  * but everything else (including deghosting) is done here.  The main
19  * motivation for this is to keep the EC firmware as simple as possible, since
20  * it cannot be easily upgraded and EC flash/IRAM space is relatively
21  * expensive.
22  */
23
24 #include <linux/io.h>
25 #include <linux/mfd/cros_ec.h>
26 #include <linux/mfd/cros_ec_commands.h>
27
28 #include "cros_ec_lpc_mec.h"
29
30 static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
31 {
32         int i;
33         int sum = 0;
34
35         for (i = 0; i < length; ++i) {
36                 dest[i] = inb(offset + i);
37                 sum += dest[i];
38         }
39
40         /* Return checksum of all bytes read */
41         return sum;
42 }
43
44 static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
45 {
46         int i;
47         int sum = 0;
48
49         for (i = 0; i < length; ++i) {
50                 outb(msg[i], offset + i);
51                 sum += msg[i];
52         }
53
54         /* Return checksum of all bytes written */
55         return sum;
56 }
57
58 #ifdef CONFIG_CROS_EC_LPC_MEC
59
60 u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
61 {
62         if (length == 0)
63                 return 0;
64
65         /* Access desired range through EMI interface */
66         if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
67                 /* Ensure we don't straddle EMI region */
68                 if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
69                         return 0;
70
71                 return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length,
72                                                 dest);
73         }
74
75         if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
76                     offset < MEC_EMI_RANGE_START))
77                 return 0;
78
79         return lpc_read_bytes(offset, length, dest);
80 }
81
82 u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
83 {
84         if (length == 0)
85                 return 0;
86
87         /* Access desired range through EMI interface */
88         if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
89                 /* Ensure we don't straddle EMI region */
90                 if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
91                         return 0;
92
93                 return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length,
94                                                 msg);
95         }
96
97         if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
98                     offset < MEC_EMI_RANGE_START))
99                 return 0;
100
101         return lpc_write_bytes(offset, length, msg);
102 }
103
104 void cros_ec_lpc_reg_init(void)
105 {
106         cros_ec_lpc_mec_init();
107 }
108
109 void cros_ec_lpc_reg_destroy(void)
110 {
111         cros_ec_lpc_mec_destroy();
112 }
113
114 #else /* CONFIG_CROS_EC_LPC_MEC */
115
116 u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
117 {
118         return lpc_read_bytes(offset, length, dest);
119 }
120
121 u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
122 {
123         return lpc_write_bytes(offset, length, msg);
124 }
125
126 void cros_ec_lpc_reg_init(void)
127 {
128 }
129
130 void cros_ec_lpc_reg_destroy(void)
131 {
132 }
133
134 #endif /* CONFIG_CROS_EC_LPC_MEC */