~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/u-boot/drivers/misc/cros_ec_lpc.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Chromium OS cros_ec driver - LPC interface
 
3
 *
 
4
 * Copyright (c) 2012 The Chromium OS Authors.
 
5
 *
 
6
 * SPDX-License-Identifier:     GPL-2.0+
 
7
 */
 
8
 
 
9
/*
 
10
 * The Matrix Keyboard Protocol driver handles talking to the keyboard
 
11
 * controller chip. Mostly this is for keyboard functions, but some other
 
12
 * things have slipped in, so we provide generic services to talk to the
 
13
 * KBC.
 
14
 */
 
15
 
 
16
#include <common.h>
 
17
#include <command.h>
 
18
#include <cros_ec.h>
 
19
#include <asm/io.h>
 
20
 
 
21
#ifdef DEBUG_TRACE
 
22
#define debug_trace(fmt, b...)  debug(fmt, ##b)
 
23
#else
 
24
#define debug_trace(fmt, b...)
 
25
#endif
 
26
 
 
27
static int wait_for_sync(struct cros_ec_dev *dev)
 
28
{
 
29
        unsigned long start;
 
30
 
 
31
        start = get_timer(0);
 
32
        while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
 
33
                if (get_timer(start) > 1000) {
 
34
                        debug("%s: Timeout waiting for CROS_EC sync\n",
 
35
                              __func__);
 
36
                        return -1;
 
37
                }
 
38
        }
 
39
 
 
40
        return 0;
 
41
}
 
42
 
 
43
int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
 
44
                     const uint8_t *dout, int dout_len,
 
45
                     uint8_t **dinp, int din_len)
 
46
{
 
47
        const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
 
48
        const int data_addr = EC_LPC_ADDR_HOST_DATA;
 
49
        const int args_addr = EC_LPC_ADDR_HOST_ARGS;
 
50
        const int param_addr = EC_LPC_ADDR_HOST_PARAM;
 
51
 
 
52
        struct ec_lpc_host_args args;
 
53
        uint8_t *d;
 
54
        int csum;
 
55
        int i;
 
56
 
 
57
        if (dout_len > EC_HOST_PARAM_SIZE) {
 
58
                debug("%s: Cannot send %d bytes\n", __func__, dout_len);
 
59
                return -1;
 
60
        }
 
61
 
 
62
        /* Fill in args */
 
63
        args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
 
64
        args.command_version = cmd_version;
 
65
        args.data_size = dout_len;
 
66
 
 
67
        /* Calculate checksum */
 
68
        csum = cmd + args.flags + args.command_version + args.data_size;
 
69
        for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++)
 
70
                csum += *d;
 
71
 
 
72
        args.checksum = (uint8_t)csum;
 
73
 
 
74
        if (wait_for_sync(dev)) {
 
75
                debug("%s: Timeout waiting ready\n", __func__);
 
76
                return -1;
 
77
        }
 
78
 
 
79
        /* Write args */
 
80
        for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
 
81
                outb(*d, args_addr + i);
 
82
 
 
83
        /* Write data, if any */
 
84
        debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version);
 
85
        for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) {
 
86
                outb(*d, param_addr + i);
 
87
                debug_trace("%02x ", *d);
 
88
        }
 
89
 
 
90
        outb(cmd, cmd_addr);
 
91
        debug_trace("\n");
 
92
 
 
93
        if (wait_for_sync(dev)) {
 
94
                debug("%s: Timeout waiting for response\n", __func__);
 
95
                return -1;
 
96
        }
 
97
 
 
98
        /* Check result */
 
99
        i = inb(data_addr);
 
100
        if (i) {
 
101
                debug("%s: CROS_EC result code %d\n", __func__, i);
 
102
                return -i;
 
103
        }
 
104
 
 
105
        /* Read back args */
 
106
        for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
 
107
                *d = inb(args_addr + i);
 
108
 
 
109
        /*
 
110
         * If EC didn't modify args flags, then somehow we sent a new-style
 
111
         * command to an old EC, which means it would have read its params
 
112
         * from the wrong place.
 
113
         */
 
114
        if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
 
115
                debug("%s: CROS_EC protocol mismatch\n", __func__);
 
116
                return -EC_RES_INVALID_RESPONSE;
 
117
        }
 
118
 
 
119
        if (args.data_size > din_len) {
 
120
                debug("%s: CROS_EC returned too much data %d > %d\n",
 
121
                      __func__, args.data_size, din_len);
 
122
                return -EC_RES_INVALID_RESPONSE;
 
123
        }
 
124
 
 
125
        /* Read data, if any */
 
126
        for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) {
 
127
                *d = inb(param_addr + i);
 
128
                debug_trace("%02x ", *d);
 
129
        }
 
130
        debug_trace("\n");
 
131
 
 
132
        /* Verify checksum */
 
133
        csum = cmd + args.flags + args.command_version + args.data_size;
 
134
        for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++)
 
135
                csum += *d;
 
136
 
 
137
        if (args.checksum != (uint8_t)csum) {
 
138
                debug("%s: CROS_EC response has invalid checksum\n", __func__);
 
139
                return -EC_RES_INVALID_CHECKSUM;
 
140
        }
 
141
        *dinp = dev->din;
 
142
 
 
143
        /* Return actual amount of data received */
 
144
        return args.data_size;
 
145
}
 
146
 
 
147
/**
 
148
 * Initialize LPC protocol.
 
149
 *
 
150
 * @param dev           CROS_EC device
 
151
 * @param blob          Device tree blob
 
152
 * @return 0 if ok, -1 on error
 
153
 */
 
154
int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob)
 
155
{
 
156
        int byte, i;
 
157
 
 
158
        /* See if we can find an EC at the other end */
 
159
        byte = 0xff;
 
160
        byte &= inb(EC_LPC_ADDR_HOST_CMD);
 
161
        byte &= inb(EC_LPC_ADDR_HOST_DATA);
 
162
        for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++)
 
163
                byte &= inb(EC_LPC_ADDR_HOST_PARAM + i);
 
164
        if (byte == 0xff) {
 
165
                debug("%s: CROS_EC device not found on LPC bus\n",
 
166
                        __func__);
 
167
                return -1;
 
168
        }
 
169
 
 
170
        return 0;
 
171
}
 
172
 
 
173
/*
 
174
 * Test if LPC command args are supported.
 
175
 *
 
176
 * The cheapest way to do this is by looking for the memory-mapped
 
177
 * flag.  This is faster than sending a new-style 'hello' command and
 
178
 * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
 
179
 * in args when it responds.
 
180
 */
 
181
int cros_ec_lpc_check_version(struct cros_ec_dev *dev)
 
182
{
 
183
        if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
 
184
                        inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1)
 
185
                                == 'C' &&
 
186
                        (inb(EC_LPC_ADDR_MEMMAP +
 
187
                                EC_MEMMAP_HOST_CMD_FLAGS) &
 
188
                                EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) {
 
189
                return 0;
 
190
        }
 
191
 
 
192
        printf("%s: ERROR: old EC interface not supported\n", __func__);
 
193
        return -1;
 
194
}