1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for the OLPC XO-1.75 Embedded Controller.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * The EC protocol is documented at:
6*4882a593Smuzhiyun * http://wiki.laptop.org/go/XO_1.75_HOST_to_EC_Protocol
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Copyright (C) 2010 One Laptop per Child Foundation.
9*4882a593Smuzhiyun * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/completion.h>
13*4882a593Smuzhiyun #include <linux/ctype.h>
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
16*4882a593Smuzhiyun #include <linux/input.h>
17*4882a593Smuzhiyun #include <linux/kfifo.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/olpc-ec.h>
20*4882a593Smuzhiyun #include <linux/platform_device.h>
21*4882a593Smuzhiyun #include <linux/power_supply.h>
22*4882a593Smuzhiyun #include <linux/reboot.h>
23*4882a593Smuzhiyun #include <linux/slab.h>
24*4882a593Smuzhiyun #include <linux/spinlock.h>
25*4882a593Smuzhiyun #include <linux/spi/spi.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun struct ec_cmd_t {
28*4882a593Smuzhiyun u8 cmd;
29*4882a593Smuzhiyun u8 bytes_returned;
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun enum ec_chan_t {
33*4882a593Smuzhiyun CHAN_NONE = 0,
34*4882a593Smuzhiyun CHAN_SWITCH,
35*4882a593Smuzhiyun CHAN_CMD_RESP,
36*4882a593Smuzhiyun CHAN_KEYBOARD,
37*4882a593Smuzhiyun CHAN_TOUCHPAD,
38*4882a593Smuzhiyun CHAN_EVENT,
39*4882a593Smuzhiyun CHAN_DEBUG,
40*4882a593Smuzhiyun CHAN_CMD_ERROR,
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /*
44*4882a593Smuzhiyun * EC events
45*4882a593Smuzhiyun */
46*4882a593Smuzhiyun #define EVENT_AC_CHANGE 1 /* AC plugged/unplugged */
47*4882a593Smuzhiyun #define EVENT_BATTERY_STATUS 2 /* Battery low/full/error/gone */
48*4882a593Smuzhiyun #define EVENT_BATTERY_CRITICAL 3 /* Battery critical voltage */
49*4882a593Smuzhiyun #define EVENT_BATTERY_SOC_CHANGE 4 /* 1% SOC Change */
50*4882a593Smuzhiyun #define EVENT_BATTERY_ERROR 5 /* Abnormal error, query for cause */
51*4882a593Smuzhiyun #define EVENT_POWER_PRESSED 6 /* Power button was pressed */
52*4882a593Smuzhiyun #define EVENT_POWER_PRESS_WAKE 7 /* Woken up with a power button */
53*4882a593Smuzhiyun #define EVENT_TIMED_HOST_WAKE 8 /* Host wake timer */
54*4882a593Smuzhiyun #define EVENT_OLS_HIGH_LIMIT 9 /* OLS crossed dark threshold */
55*4882a593Smuzhiyun #define EVENT_OLS_LOW_LIMIT 10 /* OLS crossed light threshold */
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /*
58*4882a593Smuzhiyun * EC commands
59*4882a593Smuzhiyun * (from http://dev.laptop.org/git/users/rsmith/ec-1.75/tree/ec_cmd.h)
60*4882a593Smuzhiyun */
61*4882a593Smuzhiyun #define CMD_GET_API_VERSION 0x08 /* out: u8 */
62*4882a593Smuzhiyun #define CMD_READ_VOLTAGE 0x10 /* out: u16, *9.76/32, mV */
63*4882a593Smuzhiyun #define CMD_READ_CURRENT 0x11 /* out: s16, *15.625/120, mA */
64*4882a593Smuzhiyun #define CMD_READ_ACR 0x12 /* out: s16, *6250/15, uAh */
65*4882a593Smuzhiyun #define CMD_READ_BATT_TEMPERATURE 0x13 /* out: u16, *100/256, deg C */
66*4882a593Smuzhiyun #define CMD_READ_AMBIENT_TEMPERATURE 0x14 /* unimplemented, no hardware */
67*4882a593Smuzhiyun #define CMD_READ_BATTERY_STATUS 0x15 /* out: u8, bitmask */
68*4882a593Smuzhiyun #define CMD_READ_SOC 0x16 /* out: u8, percentage */
69*4882a593Smuzhiyun #define CMD_READ_GAUGE_ID 0x17 /* out: u8 * 8 */
70*4882a593Smuzhiyun #define CMD_READ_GAUGE_DATA 0x18 /* in: u8 addr, out: u8 data */
71*4882a593Smuzhiyun #define CMD_READ_BOARD_ID 0x19 /* out: u16 (platform id) */
72*4882a593Smuzhiyun #define CMD_READ_BATT_ERR_CODE 0x1f /* out: u8, error bitmask */
73*4882a593Smuzhiyun #define CMD_SET_DCON_POWER 0x26 /* in: u8 */
74*4882a593Smuzhiyun #define CMD_RESET_EC 0x28 /* none */
75*4882a593Smuzhiyun #define CMD_READ_BATTERY_TYPE 0x2c /* out: u8 */
76*4882a593Smuzhiyun #define CMD_SET_AUTOWAK 0x33 /* out: u8 */
77*4882a593Smuzhiyun #define CMD_SET_EC_WAKEUP_TIMER 0x36 /* in: u32, out: ? */
78*4882a593Smuzhiyun #define CMD_READ_EXT_SCI_MASK 0x37 /* ? */
79*4882a593Smuzhiyun #define CMD_WRITE_EXT_SCI_MASK 0x38 /* ? */
80*4882a593Smuzhiyun #define CMD_CLEAR_EC_WAKEUP_TIMER 0x39 /* none */
81*4882a593Smuzhiyun #define CMD_ENABLE_RUNIN_DISCHARGE 0x3B /* none */
82*4882a593Smuzhiyun #define CMD_DISABLE_RUNIN_DISCHARGE 0x3C /* none */
83*4882a593Smuzhiyun #define CMD_READ_MPPT_ACTIVE 0x3d /* out: u8 */
84*4882a593Smuzhiyun #define CMD_READ_MPPT_LIMIT 0x3e /* out: u8 */
85*4882a593Smuzhiyun #define CMD_SET_MPPT_LIMIT 0x3f /* in: u8 */
86*4882a593Smuzhiyun #define CMD_DISABLE_MPPT 0x40 /* none */
87*4882a593Smuzhiyun #define CMD_ENABLE_MPPT 0x41 /* none */
88*4882a593Smuzhiyun #define CMD_READ_VIN 0x42 /* out: u16 */
89*4882a593Smuzhiyun #define CMD_EXT_SCI_QUERY 0x43 /* ? */
90*4882a593Smuzhiyun #define RSP_KEYBOARD_DATA 0x48 /* ? */
91*4882a593Smuzhiyun #define RSP_TOUCHPAD_DATA 0x49 /* ? */
92*4882a593Smuzhiyun #define CMD_GET_FW_VERSION 0x4a /* out: u8 * 16 */
93*4882a593Smuzhiyun #define CMD_POWER_CYCLE 0x4b /* none */
94*4882a593Smuzhiyun #define CMD_POWER_OFF 0x4c /* none */
95*4882a593Smuzhiyun #define CMD_RESET_EC_SOFT 0x4d /* none */
96*4882a593Smuzhiyun #define CMD_READ_GAUGE_U16 0x4e /* ? */
97*4882a593Smuzhiyun #define CMD_ENABLE_MOUSE 0x4f /* ? */
98*4882a593Smuzhiyun #define CMD_ECHO 0x52 /* in: u8 * 5, out: u8 * 5 */
99*4882a593Smuzhiyun #define CMD_GET_FW_DATE 0x53 /* out: u8 * 16 */
100*4882a593Smuzhiyun #define CMD_GET_FW_USER 0x54 /* out: u8 * 16 */
101*4882a593Smuzhiyun #define CMD_TURN_OFF_POWER 0x55 /* none (same as 0x4c) */
102*4882a593Smuzhiyun #define CMD_READ_OLS 0x56 /* out: u16 */
103*4882a593Smuzhiyun #define CMD_OLS_SMT_LEDON 0x57 /* none */
104*4882a593Smuzhiyun #define CMD_OLS_SMT_LEDOFF 0x58 /* none */
105*4882a593Smuzhiyun #define CMD_START_OLS_ASSY 0x59 /* none */
106*4882a593Smuzhiyun #define CMD_STOP_OLS_ASSY 0x5a /* none */
107*4882a593Smuzhiyun #define CMD_OLS_SMTTEST_STOP 0x5b /* none */
108*4882a593Smuzhiyun #define CMD_READ_VIN_SCALED 0x5c /* out: u16 */
109*4882a593Smuzhiyun #define CMD_READ_BAT_MIN_W 0x5d /* out: u16 */
110*4882a593Smuzhiyun #define CMD_READ_BAR_MAX_W 0x5e /* out: u16 */
111*4882a593Smuzhiyun #define CMD_RESET_BAT_MINMAX_W 0x5f /* none */
112*4882a593Smuzhiyun #define CMD_READ_LOCATION 0x60 /* in: u16 addr, out: u8 data */
113*4882a593Smuzhiyun #define CMD_WRITE_LOCATION 0x61 /* in: u16 addr, u8 data */
114*4882a593Smuzhiyun #define CMD_KEYBOARD_CMD 0x62 /* in: u8, out: ? */
115*4882a593Smuzhiyun #define CMD_TOUCHPAD_CMD 0x63 /* in: u8, out: ? */
116*4882a593Smuzhiyun #define CMD_GET_FW_HASH 0x64 /* out: u8 * 16 */
117*4882a593Smuzhiyun #define CMD_SUSPEND_HINT 0x65 /* in: u8 */
118*4882a593Smuzhiyun #define CMD_ENABLE_WAKE_TIMER 0x66 /* in: u8 */
119*4882a593Smuzhiyun #define CMD_SET_WAKE_TIMER 0x67 /* in: 32 */
120*4882a593Smuzhiyun #define CMD_ENABLE_WAKE_AUTORESET 0x68 /* in: u8 */
121*4882a593Smuzhiyun #define CMD_OLS_SET_LIMITS 0x69 /* in: u16, u16 */
122*4882a593Smuzhiyun #define CMD_OLS_GET_LIMITS 0x6a /* out: u16, u16 */
123*4882a593Smuzhiyun #define CMD_OLS_SET_CEILING 0x6b /* in: u16 */
124*4882a593Smuzhiyun #define CMD_OLS_GET_CEILING 0x6c /* out: u16 */
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /*
127*4882a593Smuzhiyun * Accepted EC commands, and how many bytes they return. There are plenty
128*4882a593Smuzhiyun * of EC commands that are no longer implemented, or are implemented only on
129*4882a593Smuzhiyun * certain older boards.
130*4882a593Smuzhiyun */
131*4882a593Smuzhiyun static const struct ec_cmd_t olpc_xo175_ec_cmds[] = {
132*4882a593Smuzhiyun { CMD_GET_API_VERSION, 1 },
133*4882a593Smuzhiyun { CMD_READ_VOLTAGE, 2 },
134*4882a593Smuzhiyun { CMD_READ_CURRENT, 2 },
135*4882a593Smuzhiyun { CMD_READ_ACR, 2 },
136*4882a593Smuzhiyun { CMD_READ_BATT_TEMPERATURE, 2 },
137*4882a593Smuzhiyun { CMD_READ_BATTERY_STATUS, 1 },
138*4882a593Smuzhiyun { CMD_READ_SOC, 1 },
139*4882a593Smuzhiyun { CMD_READ_GAUGE_ID, 8 },
140*4882a593Smuzhiyun { CMD_READ_GAUGE_DATA, 1 },
141*4882a593Smuzhiyun { CMD_READ_BOARD_ID, 2 },
142*4882a593Smuzhiyun { CMD_READ_BATT_ERR_CODE, 1 },
143*4882a593Smuzhiyun { CMD_SET_DCON_POWER, 0 },
144*4882a593Smuzhiyun { CMD_RESET_EC, 0 },
145*4882a593Smuzhiyun { CMD_READ_BATTERY_TYPE, 1 },
146*4882a593Smuzhiyun { CMD_ENABLE_RUNIN_DISCHARGE, 0 },
147*4882a593Smuzhiyun { CMD_DISABLE_RUNIN_DISCHARGE, 0 },
148*4882a593Smuzhiyun { CMD_READ_MPPT_ACTIVE, 1 },
149*4882a593Smuzhiyun { CMD_READ_MPPT_LIMIT, 1 },
150*4882a593Smuzhiyun { CMD_SET_MPPT_LIMIT, 0 },
151*4882a593Smuzhiyun { CMD_DISABLE_MPPT, 0 },
152*4882a593Smuzhiyun { CMD_ENABLE_MPPT, 0 },
153*4882a593Smuzhiyun { CMD_READ_VIN, 2 },
154*4882a593Smuzhiyun { CMD_GET_FW_VERSION, 16 },
155*4882a593Smuzhiyun { CMD_POWER_CYCLE, 0 },
156*4882a593Smuzhiyun { CMD_POWER_OFF, 0 },
157*4882a593Smuzhiyun { CMD_RESET_EC_SOFT, 0 },
158*4882a593Smuzhiyun { CMD_ECHO, 5 },
159*4882a593Smuzhiyun { CMD_GET_FW_DATE, 16 },
160*4882a593Smuzhiyun { CMD_GET_FW_USER, 16 },
161*4882a593Smuzhiyun { CMD_TURN_OFF_POWER, 0 },
162*4882a593Smuzhiyun { CMD_READ_OLS, 2 },
163*4882a593Smuzhiyun { CMD_OLS_SMT_LEDON, 0 },
164*4882a593Smuzhiyun { CMD_OLS_SMT_LEDOFF, 0 },
165*4882a593Smuzhiyun { CMD_START_OLS_ASSY, 0 },
166*4882a593Smuzhiyun { CMD_STOP_OLS_ASSY, 0 },
167*4882a593Smuzhiyun { CMD_OLS_SMTTEST_STOP, 0 },
168*4882a593Smuzhiyun { CMD_READ_VIN_SCALED, 2 },
169*4882a593Smuzhiyun { CMD_READ_BAT_MIN_W, 2 },
170*4882a593Smuzhiyun { CMD_READ_BAR_MAX_W, 2 },
171*4882a593Smuzhiyun { CMD_RESET_BAT_MINMAX_W, 0 },
172*4882a593Smuzhiyun { CMD_READ_LOCATION, 1 },
173*4882a593Smuzhiyun { CMD_WRITE_LOCATION, 0 },
174*4882a593Smuzhiyun { CMD_GET_FW_HASH, 16 },
175*4882a593Smuzhiyun { CMD_SUSPEND_HINT, 0 },
176*4882a593Smuzhiyun { CMD_ENABLE_WAKE_TIMER, 0 },
177*4882a593Smuzhiyun { CMD_SET_WAKE_TIMER, 0 },
178*4882a593Smuzhiyun { CMD_ENABLE_WAKE_AUTORESET, 0 },
179*4882a593Smuzhiyun { CMD_OLS_SET_LIMITS, 0 },
180*4882a593Smuzhiyun { CMD_OLS_GET_LIMITS, 4 },
181*4882a593Smuzhiyun { CMD_OLS_SET_CEILING, 0 },
182*4882a593Smuzhiyun { CMD_OLS_GET_CEILING, 2 },
183*4882a593Smuzhiyun { CMD_READ_EXT_SCI_MASK, 2 },
184*4882a593Smuzhiyun { CMD_WRITE_EXT_SCI_MASK, 0 },
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun { }
187*4882a593Smuzhiyun };
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun #define EC_MAX_CMD_DATA_LEN 5
190*4882a593Smuzhiyun #define EC_MAX_RESP_LEN 16
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun #define LOG_BUF_SIZE 128
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun #define PM_WAKEUP_TIME 1000
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun #define EC_ALL_EVENTS GENMASK(15, 0)
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun enum ec_state_t {
199*4882a593Smuzhiyun CMD_STATE_IDLE = 0,
200*4882a593Smuzhiyun CMD_STATE_WAITING_FOR_SWITCH,
201*4882a593Smuzhiyun CMD_STATE_CMD_IN_TX_FIFO,
202*4882a593Smuzhiyun CMD_STATE_CMD_SENT,
203*4882a593Smuzhiyun CMD_STATE_RESP_RECEIVED,
204*4882a593Smuzhiyun CMD_STATE_ERROR_RECEIVED,
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun struct olpc_xo175_ec_cmd {
208*4882a593Smuzhiyun u8 command;
209*4882a593Smuzhiyun u8 nr_args;
210*4882a593Smuzhiyun u8 data_len;
211*4882a593Smuzhiyun u8 args[EC_MAX_CMD_DATA_LEN];
212*4882a593Smuzhiyun };
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun struct olpc_xo175_ec_resp {
215*4882a593Smuzhiyun u8 channel;
216*4882a593Smuzhiyun u8 byte;
217*4882a593Smuzhiyun };
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun struct olpc_xo175_ec {
220*4882a593Smuzhiyun bool suspended;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun /* SPI related stuff. */
223*4882a593Smuzhiyun struct spi_device *spi;
224*4882a593Smuzhiyun struct spi_transfer xfer;
225*4882a593Smuzhiyun struct spi_message msg;
226*4882a593Smuzhiyun union {
227*4882a593Smuzhiyun struct olpc_xo175_ec_cmd cmd;
228*4882a593Smuzhiyun struct olpc_xo175_ec_resp resp;
229*4882a593Smuzhiyun } tx_buf, rx_buf;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun /* GPIO for the CMD signals. */
232*4882a593Smuzhiyun struct gpio_desc *gpio_cmd;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* Command handling related state. */
235*4882a593Smuzhiyun spinlock_t cmd_state_lock;
236*4882a593Smuzhiyun int cmd_state;
237*4882a593Smuzhiyun bool cmd_running;
238*4882a593Smuzhiyun struct completion cmd_done;
239*4882a593Smuzhiyun struct olpc_xo175_ec_cmd cmd;
240*4882a593Smuzhiyun u8 resp_data[EC_MAX_RESP_LEN];
241*4882a593Smuzhiyun int expected_resp_len;
242*4882a593Smuzhiyun int resp_len;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun /* Power button. */
245*4882a593Smuzhiyun struct input_dev *pwrbtn;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun /* Debug handling. */
248*4882a593Smuzhiyun char logbuf[LOG_BUF_SIZE];
249*4882a593Smuzhiyun int logbuf_len;
250*4882a593Smuzhiyun };
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun static struct platform_device *olpc_ec;
253*4882a593Smuzhiyun
olpc_xo175_ec_resp_len(u8 cmd)254*4882a593Smuzhiyun static int olpc_xo175_ec_resp_len(u8 cmd)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun const struct ec_cmd_t *p;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun for (p = olpc_xo175_ec_cmds; p->cmd; p++) {
259*4882a593Smuzhiyun if (p->cmd == cmd)
260*4882a593Smuzhiyun return p->bytes_returned;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return -EINVAL;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
olpc_xo175_ec_flush_logbuf(struct olpc_xo175_ec * priv)266*4882a593Smuzhiyun static void olpc_xo175_ec_flush_logbuf(struct olpc_xo175_ec *priv)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun dev_dbg(&priv->spi->dev, "got debug string [%*pE]\n",
269*4882a593Smuzhiyun priv->logbuf_len, priv->logbuf);
270*4882a593Smuzhiyun priv->logbuf_len = 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun static void olpc_xo175_ec_complete(void *arg);
274*4882a593Smuzhiyun
olpc_xo175_ec_send_command(struct olpc_xo175_ec * priv,void * cmd,size_t cmdlen)275*4882a593Smuzhiyun static void olpc_xo175_ec_send_command(struct olpc_xo175_ec *priv, void *cmd,
276*4882a593Smuzhiyun size_t cmdlen)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun int ret;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun memcpy(&priv->tx_buf, cmd, cmdlen);
281*4882a593Smuzhiyun priv->xfer.len = cmdlen;
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun priv->msg.complete = olpc_xo175_ec_complete;
286*4882a593Smuzhiyun priv->msg.context = priv;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun ret = spi_async(priv->spi, &priv->msg);
289*4882a593Smuzhiyun if (ret)
290*4882a593Smuzhiyun dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
olpc_xo175_ec_read_packet(struct olpc_xo175_ec * priv)293*4882a593Smuzhiyun static void olpc_xo175_ec_read_packet(struct olpc_xo175_ec *priv)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun u8 nonce[] = {0xA5, 0x5A};
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun olpc_xo175_ec_send_command(priv, nonce, sizeof(nonce));
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
olpc_xo175_ec_complete(void * arg)300*4882a593Smuzhiyun static void olpc_xo175_ec_complete(void *arg)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun struct olpc_xo175_ec *priv = arg;
303*4882a593Smuzhiyun struct device *dev = &priv->spi->dev;
304*4882a593Smuzhiyun struct power_supply *psy;
305*4882a593Smuzhiyun unsigned long flags;
306*4882a593Smuzhiyun u8 channel;
307*4882a593Smuzhiyun u8 byte;
308*4882a593Smuzhiyun int ret;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun ret = priv->msg.status;
311*4882a593Smuzhiyun if (ret) {
312*4882a593Smuzhiyun dev_err(dev, "SPI transfer failed: %d\n", ret);
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
315*4882a593Smuzhiyun if (priv->cmd_running) {
316*4882a593Smuzhiyun priv->resp_len = 0;
317*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
318*4882a593Smuzhiyun complete(&priv->cmd_done);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun if (ret != -EINTR)
323*4882a593Smuzhiyun olpc_xo175_ec_read_packet(priv);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun return;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun channel = priv->rx_buf.resp.channel;
329*4882a593Smuzhiyun byte = priv->rx_buf.resp.byte;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun switch (channel) {
332*4882a593Smuzhiyun case CHAN_NONE:
333*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun if (!priv->cmd_running) {
336*4882a593Smuzhiyun /* We can safely ignore these */
337*4882a593Smuzhiyun dev_err(dev, "spurious FIFO read packet\n");
338*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
339*4882a593Smuzhiyun return;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_CMD_SENT;
343*4882a593Smuzhiyun if (!priv->expected_resp_len)
344*4882a593Smuzhiyun complete(&priv->cmd_done);
345*4882a593Smuzhiyun olpc_xo175_ec_read_packet(priv);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
348*4882a593Smuzhiyun return;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun case CHAN_SWITCH:
351*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (!priv->cmd_running) {
354*4882a593Smuzhiyun /* Just go with the flow */
355*4882a593Smuzhiyun dev_err(dev, "spurious SWITCH packet\n");
356*4882a593Smuzhiyun memset(&priv->cmd, 0, sizeof(priv->cmd));
357*4882a593Smuzhiyun priv->cmd.command = CMD_ECHO;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_CMD_IN_TX_FIFO;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /* Throw command into TxFIFO */
363*4882a593Smuzhiyun gpiod_set_value_cansleep(priv->gpio_cmd, 0);
364*4882a593Smuzhiyun olpc_xo175_ec_send_command(priv, &priv->cmd, sizeof(priv->cmd));
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
367*4882a593Smuzhiyun return;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun case CHAN_CMD_RESP:
370*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun if (!priv->cmd_running) {
373*4882a593Smuzhiyun dev_err(dev, "spurious response packet\n");
374*4882a593Smuzhiyun } else if (priv->resp_len >= priv->expected_resp_len) {
375*4882a593Smuzhiyun dev_err(dev, "too many response packets\n");
376*4882a593Smuzhiyun } else {
377*4882a593Smuzhiyun priv->resp_data[priv->resp_len++] = byte;
378*4882a593Smuzhiyun if (priv->resp_len == priv->expected_resp_len) {
379*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_RESP_RECEIVED;
380*4882a593Smuzhiyun complete(&priv->cmd_done);
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
385*4882a593Smuzhiyun break;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun case CHAN_CMD_ERROR:
388*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun if (!priv->cmd_running) {
391*4882a593Smuzhiyun dev_err(dev, "spurious cmd error packet\n");
392*4882a593Smuzhiyun } else {
393*4882a593Smuzhiyun priv->resp_data[0] = byte;
394*4882a593Smuzhiyun priv->resp_len = 1;
395*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
396*4882a593Smuzhiyun complete(&priv->cmd_done);
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
399*4882a593Smuzhiyun break;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun case CHAN_KEYBOARD:
402*4882a593Smuzhiyun dev_warn(dev, "keyboard is not supported\n");
403*4882a593Smuzhiyun break;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun case CHAN_TOUCHPAD:
406*4882a593Smuzhiyun dev_warn(dev, "touchpad is not supported\n");
407*4882a593Smuzhiyun break;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun case CHAN_EVENT:
410*4882a593Smuzhiyun dev_dbg(dev, "got event %.2x\n", byte);
411*4882a593Smuzhiyun switch (byte) {
412*4882a593Smuzhiyun case EVENT_AC_CHANGE:
413*4882a593Smuzhiyun psy = power_supply_get_by_name("olpc_ac");
414*4882a593Smuzhiyun if (psy) {
415*4882a593Smuzhiyun power_supply_changed(psy);
416*4882a593Smuzhiyun power_supply_put(psy);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun break;
419*4882a593Smuzhiyun case EVENT_BATTERY_STATUS:
420*4882a593Smuzhiyun case EVENT_BATTERY_CRITICAL:
421*4882a593Smuzhiyun case EVENT_BATTERY_SOC_CHANGE:
422*4882a593Smuzhiyun case EVENT_BATTERY_ERROR:
423*4882a593Smuzhiyun psy = power_supply_get_by_name("olpc_battery");
424*4882a593Smuzhiyun if (psy) {
425*4882a593Smuzhiyun power_supply_changed(psy);
426*4882a593Smuzhiyun power_supply_put(psy);
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun break;
429*4882a593Smuzhiyun case EVENT_POWER_PRESSED:
430*4882a593Smuzhiyun input_report_key(priv->pwrbtn, KEY_POWER, 1);
431*4882a593Smuzhiyun input_sync(priv->pwrbtn);
432*4882a593Smuzhiyun input_report_key(priv->pwrbtn, KEY_POWER, 0);
433*4882a593Smuzhiyun input_sync(priv->pwrbtn);
434*4882a593Smuzhiyun fallthrough;
435*4882a593Smuzhiyun case EVENT_POWER_PRESS_WAKE:
436*4882a593Smuzhiyun case EVENT_TIMED_HOST_WAKE:
437*4882a593Smuzhiyun pm_wakeup_event(priv->pwrbtn->dev.parent,
438*4882a593Smuzhiyun PM_WAKEUP_TIME);
439*4882a593Smuzhiyun break;
440*4882a593Smuzhiyun default:
441*4882a593Smuzhiyun dev_dbg(dev, "ignored unknown event %.2x\n", byte);
442*4882a593Smuzhiyun break;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun break;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun case CHAN_DEBUG:
447*4882a593Smuzhiyun if (byte == '\n') {
448*4882a593Smuzhiyun olpc_xo175_ec_flush_logbuf(priv);
449*4882a593Smuzhiyun } else if (isprint(byte)) {
450*4882a593Smuzhiyun priv->logbuf[priv->logbuf_len++] = byte;
451*4882a593Smuzhiyun if (priv->logbuf_len == LOG_BUF_SIZE)
452*4882a593Smuzhiyun olpc_xo175_ec_flush_logbuf(priv);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun break;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun default:
457*4882a593Smuzhiyun dev_warn(dev, "unknown channel: %d, %.2x\n", channel, byte);
458*4882a593Smuzhiyun break;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /* Most non-command packets get the TxFIFO refilled and an ACK. */
462*4882a593Smuzhiyun olpc_xo175_ec_read_packet(priv);
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun /*
466*4882a593Smuzhiyun * This function is protected with a mutex. We can safely assume that
467*4882a593Smuzhiyun * there will be only one instance of this function running at a time.
468*4882a593Smuzhiyun * One of the ways in which we enforce this is by waiting until we get
469*4882a593Smuzhiyun * all response bytes back from the EC, rather than just the number that
470*4882a593Smuzhiyun * the caller requests (otherwise, we might start a new command while an
471*4882a593Smuzhiyun * old command's response bytes are still incoming).
472*4882a593Smuzhiyun */
olpc_xo175_ec_cmd(u8 cmd,u8 * inbuf,size_t inlen,u8 * resp,size_t resp_len,void * ec_cb_arg)473*4882a593Smuzhiyun static int olpc_xo175_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *resp,
474*4882a593Smuzhiyun size_t resp_len, void *ec_cb_arg)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun struct olpc_xo175_ec *priv = ec_cb_arg;
477*4882a593Smuzhiyun struct device *dev = &priv->spi->dev;
478*4882a593Smuzhiyun unsigned long flags;
479*4882a593Smuzhiyun size_t nr_bytes;
480*4882a593Smuzhiyun int ret = 0;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun dev_dbg(dev, "CMD %x, %zd bytes expected\n", cmd, resp_len);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun if (inlen > 5) {
485*4882a593Smuzhiyun dev_err(dev, "command len %zd too big!\n", resp_len);
486*4882a593Smuzhiyun return -EOVERFLOW;
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun /* Suspending in the middle of an EC command hoses things badly! */
490*4882a593Smuzhiyun if (WARN_ON(priv->suspended))
491*4882a593Smuzhiyun return -EBUSY;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun /* Ensure a valid command and return bytes */
494*4882a593Smuzhiyun ret = olpc_xo175_ec_resp_len(cmd);
495*4882a593Smuzhiyun if (ret < 0) {
496*4882a593Smuzhiyun dev_err_ratelimited(dev, "unknown command 0x%x\n", cmd);
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun /*
499*4882a593Smuzhiyun * Assume the best in our callers, and allow unknown commands
500*4882a593Smuzhiyun * through. I'm not the charitable type, but it was beaten
501*4882a593Smuzhiyun * into me. Just maintain a minimum standard of sanity.
502*4882a593Smuzhiyun */
503*4882a593Smuzhiyun if (resp_len > sizeof(priv->resp_data)) {
504*4882a593Smuzhiyun dev_err(dev, "response too big: %zd!\n", resp_len);
505*4882a593Smuzhiyun return -EOVERFLOW;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun nr_bytes = resp_len;
508*4882a593Smuzhiyun } else {
509*4882a593Smuzhiyun nr_bytes = (size_t)ret;
510*4882a593Smuzhiyun ret = 0;
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun resp_len = min(resp_len, nr_bytes);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun /* Initialize the state machine */
517*4882a593Smuzhiyun init_completion(&priv->cmd_done);
518*4882a593Smuzhiyun priv->cmd_running = true;
519*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_WAITING_FOR_SWITCH;
520*4882a593Smuzhiyun memset(&priv->cmd, 0, sizeof(priv->cmd));
521*4882a593Smuzhiyun priv->cmd.command = cmd;
522*4882a593Smuzhiyun priv->cmd.nr_args = inlen;
523*4882a593Smuzhiyun priv->cmd.data_len = 0;
524*4882a593Smuzhiyun memcpy(priv->cmd.args, inbuf, inlen);
525*4882a593Smuzhiyun priv->expected_resp_len = nr_bytes;
526*4882a593Smuzhiyun priv->resp_len = 0;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun /* Tickle the cmd gpio to get things started */
529*4882a593Smuzhiyun gpiod_set_value_cansleep(priv->gpio_cmd, 1);
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun /* The irq handler should do the rest */
534*4882a593Smuzhiyun if (!wait_for_completion_timeout(&priv->cmd_done,
535*4882a593Smuzhiyun msecs_to_jiffies(4000))) {
536*4882a593Smuzhiyun dev_err(dev, "EC cmd error: timeout in STATE %d\n",
537*4882a593Smuzhiyun priv->cmd_state);
538*4882a593Smuzhiyun gpiod_set_value_cansleep(priv->gpio_cmd, 0);
539*4882a593Smuzhiyun spi_slave_abort(priv->spi);
540*4882a593Smuzhiyun olpc_xo175_ec_read_packet(priv);
541*4882a593Smuzhiyun return -ETIMEDOUT;
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun spin_lock_irqsave(&priv->cmd_state_lock, flags);
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun /* Deal with the results. */
547*4882a593Smuzhiyun if (priv->cmd_state == CMD_STATE_ERROR_RECEIVED) {
548*4882a593Smuzhiyun /* EC-provided error is in the single response byte */
549*4882a593Smuzhiyun dev_err(dev, "command 0x%x returned error 0x%x\n",
550*4882a593Smuzhiyun cmd, priv->resp_data[0]);
551*4882a593Smuzhiyun ret = -EREMOTEIO;
552*4882a593Smuzhiyun } else if (priv->resp_len != nr_bytes) {
553*4882a593Smuzhiyun dev_err(dev, "command 0x%x returned %d bytes, expected %zd bytes\n",
554*4882a593Smuzhiyun cmd, priv->resp_len, nr_bytes);
555*4882a593Smuzhiyun ret = -EREMOTEIO;
556*4882a593Smuzhiyun } else {
557*4882a593Smuzhiyun /*
558*4882a593Smuzhiyun * We may have 8 bytes in priv->resp, but we only care about
559*4882a593Smuzhiyun * what we've been asked for. If the caller asked for only 2
560*4882a593Smuzhiyun * bytes, give them that. We've guaranteed that
561*4882a593Smuzhiyun * resp_len <= priv->resp_len and priv->resp_len == nr_bytes.
562*4882a593Smuzhiyun */
563*4882a593Smuzhiyun memcpy(resp, priv->resp_data, resp_len);
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun /* This should already be low, but just in case. */
567*4882a593Smuzhiyun gpiod_set_value_cansleep(priv->gpio_cmd, 0);
568*4882a593Smuzhiyun priv->cmd_running = false;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun return ret;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun
olpc_xo175_ec_set_event_mask(unsigned int mask)575*4882a593Smuzhiyun static int olpc_xo175_ec_set_event_mask(unsigned int mask)
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun u8 args[2];
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun args[0] = mask >> 0;
580*4882a593Smuzhiyun args[1] = mask >> 8;
581*4882a593Smuzhiyun return olpc_ec_cmd(CMD_WRITE_EXT_SCI_MASK, args, 2, NULL, 0);
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun
olpc_xo175_ec_power_off(void)584*4882a593Smuzhiyun static void olpc_xo175_ec_power_off(void)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun while (1) {
587*4882a593Smuzhiyun olpc_ec_cmd(CMD_POWER_OFF, NULL, 0, NULL, 0);
588*4882a593Smuzhiyun mdelay(1000);
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun
olpc_xo175_ec_suspend(struct device * dev)592*4882a593Smuzhiyun static int __maybe_unused olpc_xo175_ec_suspend(struct device *dev)
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
595*4882a593Smuzhiyun static struct {
596*4882a593Smuzhiyun u8 suspend;
597*4882a593Smuzhiyun u32 suspend_count;
598*4882a593Smuzhiyun } __packed hintargs;
599*4882a593Smuzhiyun static unsigned int suspend_count;
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun /*
602*4882a593Smuzhiyun * SOC_SLEEP is not wired to the EC on B3 and earlier boards.
603*4882a593Smuzhiyun * This command lets the EC know instead. The suspend count doesn't seem
604*4882a593Smuzhiyun * to be used anywhere but in the EC debug output.
605*4882a593Smuzhiyun */
606*4882a593Smuzhiyun hintargs.suspend = 1;
607*4882a593Smuzhiyun hintargs.suspend_count = suspend_count++;
608*4882a593Smuzhiyun olpc_ec_cmd(CMD_SUSPEND_HINT, (void *)&hintargs, sizeof(hintargs),
609*4882a593Smuzhiyun NULL, 0);
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun /*
612*4882a593Smuzhiyun * After we've sent the suspend hint, don't allow further EC commands
613*4882a593Smuzhiyun * to be run until we've resumed. Userspace tasks should be frozen,
614*4882a593Smuzhiyun * but kernel threads and interrupts could still schedule EC commands.
615*4882a593Smuzhiyun */
616*4882a593Smuzhiyun priv->suspended = true;
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun
olpc_xo175_ec_resume_noirq(struct device * dev)621*4882a593Smuzhiyun static int __maybe_unused olpc_xo175_ec_resume_noirq(struct device *dev)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun priv->suspended = false;
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun return 0;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun
olpc_xo175_ec_resume(struct device * dev)630*4882a593Smuzhiyun static int __maybe_unused olpc_xo175_ec_resume(struct device *dev)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun u8 x = 0;
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun /*
635*4882a593Smuzhiyun * The resume hint is only needed if no other commands are
636*4882a593Smuzhiyun * being sent during resume. all it does is tell the EC
637*4882a593Smuzhiyun * the SoC is definitely awake.
638*4882a593Smuzhiyun */
639*4882a593Smuzhiyun olpc_ec_cmd(CMD_SUSPEND_HINT, &x, 1, NULL, 0);
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun /* Enable all EC events while we're awake */
642*4882a593Smuzhiyun olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun return 0;
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun static struct olpc_ec_driver olpc_xo175_ec_driver = {
648*4882a593Smuzhiyun .ec_cmd = olpc_xo175_ec_cmd,
649*4882a593Smuzhiyun };
650*4882a593Smuzhiyun
olpc_xo175_ec_remove(struct spi_device * spi)651*4882a593Smuzhiyun static int olpc_xo175_ec_remove(struct spi_device *spi)
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun if (pm_power_off == olpc_xo175_ec_power_off)
654*4882a593Smuzhiyun pm_power_off = NULL;
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun spi_slave_abort(spi);
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun platform_device_unregister(olpc_ec);
659*4882a593Smuzhiyun olpc_ec = NULL;
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun return 0;
662*4882a593Smuzhiyun }
663*4882a593Smuzhiyun
olpc_xo175_ec_probe(struct spi_device * spi)664*4882a593Smuzhiyun static int olpc_xo175_ec_probe(struct spi_device *spi)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun struct olpc_xo175_ec *priv;
667*4882a593Smuzhiyun int ret;
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun if (olpc_ec) {
670*4882a593Smuzhiyun dev_err(&spi->dev, "OLPC EC already registered.\n");
671*4882a593Smuzhiyun return -EBUSY;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
675*4882a593Smuzhiyun if (!priv)
676*4882a593Smuzhiyun return -ENOMEM;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun priv->gpio_cmd = devm_gpiod_get(&spi->dev, "cmd", GPIOD_OUT_LOW);
679*4882a593Smuzhiyun if (IS_ERR(priv->gpio_cmd)) {
680*4882a593Smuzhiyun dev_err(&spi->dev, "failed to get cmd gpio: %ld\n",
681*4882a593Smuzhiyun PTR_ERR(priv->gpio_cmd));
682*4882a593Smuzhiyun return PTR_ERR(priv->gpio_cmd);
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun priv->spi = spi;
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun spin_lock_init(&priv->cmd_state_lock);
688*4882a593Smuzhiyun priv->cmd_state = CMD_STATE_IDLE;
689*4882a593Smuzhiyun init_completion(&priv->cmd_done);
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun priv->logbuf_len = 0;
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun /* Set up power button input device */
694*4882a593Smuzhiyun priv->pwrbtn = devm_input_allocate_device(&spi->dev);
695*4882a593Smuzhiyun if (!priv->pwrbtn)
696*4882a593Smuzhiyun return -ENOMEM;
697*4882a593Smuzhiyun priv->pwrbtn->name = "Power Button";
698*4882a593Smuzhiyun priv->pwrbtn->dev.parent = &spi->dev;
699*4882a593Smuzhiyun input_set_capability(priv->pwrbtn, EV_KEY, KEY_POWER);
700*4882a593Smuzhiyun ret = input_register_device(priv->pwrbtn);
701*4882a593Smuzhiyun if (ret) {
702*4882a593Smuzhiyun dev_err(&spi->dev, "error registering input device: %d\n", ret);
703*4882a593Smuzhiyun return ret;
704*4882a593Smuzhiyun }
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun spi_set_drvdata(spi, priv);
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun priv->xfer.rx_buf = &priv->rx_buf;
709*4882a593Smuzhiyun priv->xfer.tx_buf = &priv->tx_buf;
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun olpc_xo175_ec_read_packet(priv);
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun olpc_ec_driver_register(&olpc_xo175_ec_driver, priv);
714*4882a593Smuzhiyun olpc_ec = platform_device_register_resndata(&spi->dev, "olpc-ec", -1,
715*4882a593Smuzhiyun NULL, 0, NULL, 0);
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun /* Enable all EC events while we're awake */
718*4882a593Smuzhiyun olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun if (pm_power_off == NULL)
721*4882a593Smuzhiyun pm_power_off = olpc_xo175_ec_power_off;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun dev_info(&spi->dev, "OLPC XO-1.75 Embedded Controller driver\n");
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun return 0;
726*4882a593Smuzhiyun }
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun static const struct dev_pm_ops olpc_xo175_ec_pm_ops = {
729*4882a593Smuzhiyun SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, olpc_xo175_ec_resume_noirq)
730*4882a593Smuzhiyun SET_RUNTIME_PM_OPS(olpc_xo175_ec_suspend, olpc_xo175_ec_resume, NULL)
731*4882a593Smuzhiyun };
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun static const struct of_device_id olpc_xo175_ec_of_match[] = {
734*4882a593Smuzhiyun { .compatible = "olpc,xo1.75-ec" },
735*4882a593Smuzhiyun { }
736*4882a593Smuzhiyun };
737*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, olpc_xo175_ec_of_match);
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun static const struct spi_device_id olpc_xo175_ec_id_table[] = {
740*4882a593Smuzhiyun { "xo1.75-ec", 0 },
741*4882a593Smuzhiyun {}
742*4882a593Smuzhiyun };
743*4882a593Smuzhiyun MODULE_DEVICE_TABLE(spi, olpc_xo175_ec_id_table);
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun static struct spi_driver olpc_xo175_ec_spi_driver = {
746*4882a593Smuzhiyun .driver = {
747*4882a593Smuzhiyun .name = "olpc-xo175-ec",
748*4882a593Smuzhiyun .of_match_table = olpc_xo175_ec_of_match,
749*4882a593Smuzhiyun .pm = &olpc_xo175_ec_pm_ops,
750*4882a593Smuzhiyun },
751*4882a593Smuzhiyun .probe = olpc_xo175_ec_probe,
752*4882a593Smuzhiyun .remove = olpc_xo175_ec_remove,
753*4882a593Smuzhiyun };
754*4882a593Smuzhiyun module_spi_driver(olpc_xo175_ec_spi_driver);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun MODULE_DESCRIPTION("OLPC XO-1.75 Embedded Controller driver");
757*4882a593Smuzhiyun MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); /* Functionality */
758*4882a593Smuzhiyun MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); /* Bugs */
759*4882a593Smuzhiyun MODULE_LICENSE("GPL");
760