blob: 810431e30cd258963ad8eaeebd60b13e14473480 [file] [log] [blame]
Oren Weil3ce72722011-05-15 13:43:43 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba912012-02-09 19:25:53 +02004 * Copyright (c) 2003-2012, Intel Corporation.
Oren Weil3ce72722011-05-15 13:43:43 +03005 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17#include <linux/pci.h>
Tomas Winkler4f3afe12012-05-09 16:38:59 +030018#include <linux/mei.h>
Tomas Winkler47a73802012-12-25 19:06:03 +020019
20#include "mei_dev.h"
Oren Weil3ce72722011-05-15 13:43:43 +030021#include "interface.h"
22
23
24
25/**
26 * mei_set_csr_register - writes H_CSR register to the mei device,
27 * and ignores the H_IS bit for it is write-one-to-zero.
28 *
29 * @dev: the device structure
30 */
31void mei_hcsr_set(struct mei_device *dev)
32{
33 if ((dev->host_hw_state & H_IS) == H_IS)
34 dev->host_hw_state &= ~H_IS;
35 mei_reg_write(dev, H_CSR, dev->host_hw_state);
36 dev->host_hw_state = mei_hcsr_read(dev);
37}
38
39/**
40 * mei_csr_enable_interrupts - enables mei device interrupts
41 *
42 * @dev: the device structure
43 */
44void mei_enable_interrupts(struct mei_device *dev)
45{
46 dev->host_hw_state |= H_IE;
47 mei_hcsr_set(dev);
48}
49
50/**
51 * mei_csr_disable_interrupts - disables mei device interrupts
52 *
53 * @dev: the device structure
54 */
55void mei_disable_interrupts(struct mei_device *dev)
56{
57 dev->host_hw_state &= ~H_IE;
58 mei_hcsr_set(dev);
59}
60
61/**
Tomas Winkler726917f2012-06-25 23:46:28 +030062 * mei_hbuf_filled_slots - gets number of device filled buffer slots
Oren Weil3ce72722011-05-15 13:43:43 +030063 *
64 * @device: the device structure
65 *
66 * returns number of filled slots
67 */
Tomas Winkler726917f2012-06-25 23:46:28 +030068static unsigned char mei_hbuf_filled_slots(struct mei_device *dev)
Oren Weil3ce72722011-05-15 13:43:43 +030069{
70 char read_ptr, write_ptr;
71
Tomas Winkler726917f2012-06-25 23:46:28 +030072 dev->host_hw_state = mei_hcsr_read(dev);
73
Oren Weil3ce72722011-05-15 13:43:43 +030074 read_ptr = (char) ((dev->host_hw_state & H_CBRP) >> 8);
75 write_ptr = (char) ((dev->host_hw_state & H_CBWP) >> 16);
76
77 return (unsigned char) (write_ptr - read_ptr);
78}
79
80/**
Tomas Winkler726917f2012-06-25 23:46:28 +030081 * mei_hbuf_is_empty - checks if host buffer is empty.
Oren Weil3ce72722011-05-15 13:43:43 +030082 *
83 * @dev: the device structure
84 *
Tomas Winkler726917f2012-06-25 23:46:28 +030085 * returns true if empty, false - otherwise.
Oren Weil3ce72722011-05-15 13:43:43 +030086 */
Tomas Winkler726917f2012-06-25 23:46:28 +030087bool mei_hbuf_is_empty(struct mei_device *dev)
Oren Weil3ce72722011-05-15 13:43:43 +030088{
Tomas Winkler726917f2012-06-25 23:46:28 +030089 return mei_hbuf_filled_slots(dev) == 0;
Oren Weil3ce72722011-05-15 13:43:43 +030090}
91
92/**
Tomas Winkler726917f2012-06-25 23:46:28 +030093 * mei_hbuf_empty_slots - counts write empty slots.
Oren Weil3ce72722011-05-15 13:43:43 +030094 *
95 * @dev: the device structure
96 *
97 * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise empty slots count
98 */
Tomas Winkler726917f2012-06-25 23:46:28 +030099int mei_hbuf_empty_slots(struct mei_device *dev)
Oren Weil3ce72722011-05-15 13:43:43 +0300100{
Tomas Winkler24aadc82012-06-25 23:46:27 +0300101 unsigned char filled_slots, empty_slots;
Oren Weil3ce72722011-05-15 13:43:43 +0300102
Tomas Winkler726917f2012-06-25 23:46:28 +0300103 filled_slots = mei_hbuf_filled_slots(dev);
Tomas Winkler24aadc82012-06-25 23:46:27 +0300104 empty_slots = dev->hbuf_depth - filled_slots;
Oren Weil3ce72722011-05-15 13:43:43 +0300105
106 /* check for overflow */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300107 if (filled_slots > dev->hbuf_depth)
Oren Weil3ce72722011-05-15 13:43:43 +0300108 return -EOVERFLOW;
109
110 return empty_slots;
111}
112
113/**
114 * mei_write_message - writes a message to mei device.
115 *
116 * @dev: the device structure
Tomas Winkler438763f2012-12-25 19:05:59 +0200117 * @hader: mei HECI header of message
118 * @buf: message payload will be written
Oren Weil3ce72722011-05-15 13:43:43 +0300119 *
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200120 * This function returns -EIO if write has failed
Oren Weil3ce72722011-05-15 13:43:43 +0300121 */
Tomas Winkler169d1332012-06-19 09:13:35 +0300122int mei_write_message(struct mei_device *dev, struct mei_msg_hdr *header,
Tomas Winkler438763f2012-12-25 19:05:59 +0200123 unsigned char *buf)
Oren Weil3ce72722011-05-15 13:43:43 +0300124{
Tomas Winkler169d1332012-06-19 09:13:35 +0300125 unsigned long rem, dw_cnt;
Tomas Winkler438763f2012-12-25 19:05:59 +0200126 unsigned long length = header->length;
Tomas Winkler169d1332012-06-19 09:13:35 +0300127 u32 *reg_buf = (u32 *)buf;
128 int i;
129 int empty_slots;
Oren Weil3ce72722011-05-15 13:43:43 +0300130
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200131 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header));
Oren Weil3ce72722011-05-15 13:43:43 +0300132
Tomas Winkler726917f2012-06-25 23:46:28 +0300133 empty_slots = mei_hbuf_empty_slots(dev);
Tomas Winkler169d1332012-06-19 09:13:35 +0300134 dev_dbg(&dev->pdev->dev, "empty slots = %hu.\n", empty_slots);
Oren Weil3ce72722011-05-15 13:43:43 +0300135
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300136 dw_cnt = mei_data2slots(length);
Tomas Winkler169d1332012-06-19 09:13:35 +0300137 if (empty_slots < 0 || dw_cnt > empty_slots)
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200138 return -EIO;
Oren Weil3ce72722011-05-15 13:43:43 +0300139
140 mei_reg_write(dev, H_CB_WW, *((u32 *) header));
141
Tomas Winkler169d1332012-06-19 09:13:35 +0300142 for (i = 0; i < length / 4; i++)
143 mei_reg_write(dev, H_CB_WW, reg_buf[i]);
144
145 rem = length & 0x3;
146 if (rem > 0) {
147 u32 reg = 0;
148 memcpy(&reg, &buf[length - rem], rem);
149 mei_reg_write(dev, H_CB_WW, reg);
Oren Weil3ce72722011-05-15 13:43:43 +0300150 }
151
Tomas Winkler169d1332012-06-19 09:13:35 +0300152 dev->host_hw_state = mei_hcsr_read(dev);
Oren Weil3ce72722011-05-15 13:43:43 +0300153 dev->host_hw_state |= H_IG;
154 mei_hcsr_set(dev);
155 dev->me_hw_state = mei_mecsr_read(dev);
156 if ((dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200157 return -EIO;
Oren Weil3ce72722011-05-15 13:43:43 +0300158
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200159 return 0;
Oren Weil3ce72722011-05-15 13:43:43 +0300160}
161
162/**
163 * mei_count_full_read_slots - counts read full slots.
164 *
165 * @dev: the device structure
166 *
167 * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise filled slots count
168 */
169int mei_count_full_read_slots(struct mei_device *dev)
170{
171 char read_ptr, write_ptr;
172 unsigned char buffer_depth, filled_slots;
173
174 dev->me_hw_state = mei_mecsr_read(dev);
175 buffer_depth = (unsigned char)((dev->me_hw_state & ME_CBD_HRA) >> 24);
176 read_ptr = (char) ((dev->me_hw_state & ME_CBRP_HRA) >> 8);
177 write_ptr = (char) ((dev->me_hw_state & ME_CBWP_HRA) >> 16);
178 filled_slots = (unsigned char) (write_ptr - read_ptr);
179
180 /* check for overflow */
181 if (filled_slots > buffer_depth)
182 return -EOVERFLOW;
183
184 dev_dbg(&dev->pdev->dev, "filled_slots =%08x\n", filled_slots);
185 return (int)filled_slots;
186}
187
188/**
189 * mei_read_slots - reads a message from mei device.
190 *
191 * @dev: the device structure
192 * @buffer: message buffer will be written
193 * @buffer_length: message size will be read
194 */
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200195void mei_read_slots(struct mei_device *dev, unsigned char *buffer,
196 unsigned long buffer_length)
Oren Weil3ce72722011-05-15 13:43:43 +0300197{
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200198 u32 *reg_buf = (u32 *)buffer;
Oren Weil3ce72722011-05-15 13:43:43 +0300199
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200200 for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32))
201 *reg_buf++ = mei_mecbrw_read(dev);
Oren Weil3ce72722011-05-15 13:43:43 +0300202
203 if (buffer_length > 0) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200204 u32 reg = mei_mecbrw_read(dev);
205 memcpy(reg_buf, &reg, buffer_length);
Oren Weil3ce72722011-05-15 13:43:43 +0300206 }
207
208 dev->host_hw_state |= H_IG;
209 mei_hcsr_set(dev);
210}
211
212/**
213 * mei_flow_ctrl_creds - checks flow_control credentials.
214 *
215 * @dev: the device structure
216 * @cl: private data of the file object
217 *
218 * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
219 * -ENOENT if mei_cl is not present
220 * -EINVAL if single_recv_buf == 0
221 */
222int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl)
223{
224 int i;
225
Tomas Winklercf9673d2011-06-06 10:44:33 +0300226 if (!dev->me_clients_num)
Oren Weil3ce72722011-05-15 13:43:43 +0300227 return 0;
228
229 if (cl->mei_flow_ctrl_creds > 0)
230 return 1;
231
Tomas Winklercf9673d2011-06-06 10:44:33 +0300232 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weil3ce72722011-05-15 13:43:43 +0300233 struct mei_me_client *me_cl = &dev->me_clients[i];
234 if (me_cl->client_id == cl->me_client_id) {
235 if (me_cl->mei_flow_ctrl_creds) {
236 if (WARN_ON(me_cl->props.single_recv_buf == 0))
237 return -EINVAL;
238 return 1;
239 } else {
240 return 0;
241 }
242 }
243 }
244 return -ENOENT;
245}
246
247/**
248 * mei_flow_ctrl_reduce - reduces flow_control.
249 *
250 * @dev: the device structure
251 * @cl: private data of the file object
252 * @returns
253 * 0 on success
254 * -ENOENT when me client is not found
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700255 * -EINVAL when ctrl credits are <= 0
Oren Weil3ce72722011-05-15 13:43:43 +0300256 */
257int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl)
258{
259 int i;
260
Tomas Winklercf9673d2011-06-06 10:44:33 +0300261 if (!dev->me_clients_num)
Oren Weil3ce72722011-05-15 13:43:43 +0300262 return -ENOENT;
263
Tomas Winklercf9673d2011-06-06 10:44:33 +0300264 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weil3ce72722011-05-15 13:43:43 +0300265 struct mei_me_client *me_cl = &dev->me_clients[i];
266 if (me_cl->client_id == cl->me_client_id) {
267 if (me_cl->props.single_recv_buf != 0) {
268 if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
269 return -EINVAL;
270 dev->me_clients[i].mei_flow_ctrl_creds--;
271 } else {
272 if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
273 return -EINVAL;
274 cl->mei_flow_ctrl_creds--;
275 }
276 return 0;
277 }
278 }
279 return -ENOENT;
280}
281
282/**
283 * mei_send_flow_control - sends flow control to fw.
284 *
285 * @dev: the device structure
286 * @cl: private data of the file object
287 *
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200288 * This function returns -EIO on write failure
Oren Weil3ce72722011-05-15 13:43:43 +0300289 */
290int mei_send_flow_control(struct mei_device *dev, struct mei_cl *cl)
291{
292 struct mei_msg_hdr *mei_hdr;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200293 struct hbm_flow_control *flow_ctrl;
294 const size_t len = sizeof(struct hbm_flow_control);
Oren Weil3ce72722011-05-15 13:43:43 +0300295
Tomas Winkler5bd64712012-11-18 15:13:14 +0200296 mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
Oren Weil3ce72722011-05-15 13:43:43 +0300297
Tomas Winkler5bd64712012-11-18 15:13:14 +0200298 flow_ctrl = (struct hbm_flow_control *)&dev->wr_msg_buf[1];
299 memset(flow_ctrl, 0, len);
300 flow_ctrl->hbm_cmd = MEI_FLOW_CONTROL_CMD;
301 flow_ctrl->host_addr = cl->host_client_id;
302 flow_ctrl->me_addr = cl->me_client_id;
303 /* FIXME: reserved !? */
304 memset(flow_ctrl->reserved, 0, sizeof(flow_ctrl->reserved));
Oren Weil3ce72722011-05-15 13:43:43 +0300305 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200306 cl->host_client_id, cl->me_client_id);
307
Tomas Winkler438763f2012-12-25 19:05:59 +0200308 return mei_write_message(dev, mei_hdr, (unsigned char *) flow_ctrl);
Oren Weil3ce72722011-05-15 13:43:43 +0300309}
310
311/**
312 * mei_other_client_is_connecting - checks if other
313 * client with the same client id is connected.
314 *
315 * @dev: the device structure
316 * @cl: private data of the file object
317 *
318 * returns 1 if other client is connected, 0 - otherwise.
319 */
320int mei_other_client_is_connecting(struct mei_device *dev,
321 struct mei_cl *cl)
322{
323 struct mei_cl *cl_pos = NULL;
324 struct mei_cl *cl_next = NULL;
325
326 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
327 if ((cl_pos->state == MEI_FILE_CONNECTING) &&
328 (cl_pos != cl) &&
329 cl->me_client_id == cl_pos->me_client_id)
330 return 1;
331
332 }
333 return 0;
334}
335
336/**
337 * mei_disconnect - sends disconnect message to fw.
338 *
339 * @dev: the device structure
340 * @cl: private data of the file object
341 *
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200342 * This function returns -EIO on write failure
Oren Weil3ce72722011-05-15 13:43:43 +0300343 */
344int mei_disconnect(struct mei_device *dev, struct mei_cl *cl)
345{
Tomas Winkler438763f2012-12-25 19:05:59 +0200346 struct mei_msg_hdr *hdr;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200347 struct hbm_client_connect_request *req;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200348 const size_t len = sizeof(struct hbm_client_connect_request);
Oren Weil3ce72722011-05-15 13:43:43 +0300349
Tomas Winkler438763f2012-12-25 19:05:59 +0200350 hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
Oren Weil3ce72722011-05-15 13:43:43 +0300351
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200352 req = (struct hbm_client_connect_request *)&dev->wr_msg_buf[1];
Tomas Winkler5bd64712012-11-18 15:13:14 +0200353 memset(req, 0, len);
354 req->hbm_cmd = CLIENT_DISCONNECT_REQ_CMD;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200355 req->host_addr = cl->host_client_id;
356 req->me_addr = cl->me_client_id;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200357 req->reserved = 0;
Oren Weil3ce72722011-05-15 13:43:43 +0300358
Tomas Winkler438763f2012-12-25 19:05:59 +0200359 return mei_write_message(dev, hdr, (unsigned char *)req);
Oren Weil3ce72722011-05-15 13:43:43 +0300360}
361
362/**
363 * mei_connect - sends connect message to fw.
364 *
365 * @dev: the device structure
366 * @cl: private data of the file object
367 *
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200368 * This function returns -EIO on write failure
Oren Weil3ce72722011-05-15 13:43:43 +0300369 */
370int mei_connect(struct mei_device *dev, struct mei_cl *cl)
371{
Tomas Winkler438763f2012-12-25 19:05:59 +0200372 struct mei_msg_hdr *hdr;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200373 struct hbm_client_connect_request *req;
374 const size_t len = sizeof(struct hbm_client_connect_request);
Oren Weil3ce72722011-05-15 13:43:43 +0300375
Tomas Winkler438763f2012-12-25 19:05:59 +0200376 hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
Oren Weil3ce72722011-05-15 13:43:43 +0300377
Tomas Winkler5bd64712012-11-18 15:13:14 +0200378 req = (struct hbm_client_connect_request *) &dev->wr_msg_buf[1];
379 req->hbm_cmd = CLIENT_CONNECT_REQ_CMD;
380 req->host_addr = cl->host_client_id;
381 req->me_addr = cl->me_client_id;
382 req->reserved = 0;
Oren Weil3ce72722011-05-15 13:43:43 +0300383
Tomas Winkler438763f2012-12-25 19:05:59 +0200384 return mei_write_message(dev, hdr, (unsigned char *) req);
Oren Weil3ce72722011-05-15 13:43:43 +0300385}