1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Part of Intel(R) Manageability Engine Interface Linux driver 8 * 9 * Copyright (c) 2003 - 2008 Intel Corp. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions, and the following disclaimer, 17 * without modification. 18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 19 * substantially similar to the "NO WARRANTY" disclaimer below 20 * ("Disclaimer") and any redistribution must be conditioned upon 21 * including a substantially similar Disclaimer requirement for further 22 * binary redistribution. 23 * 3. Neither the names of the above-listed copyright holders nor the names 24 * of any contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * Alternatively, this software may be distributed under the terms of the 28 * GNU General Public License ("GPL") version 2 as published by the Free 29 * Software Foundation. 30 * 31 * NO WARRANTY 32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 * POSSIBILITY OF SUCH DAMAGES. 43 * 44 */ 45 46 47 #include <sys/types.h> 48 #include <sys/cmn_err.h> 49 #include <sys/conf.h> 50 #include <sys/ddi.h> 51 #include <sys/ddi_impldefs.h> 52 #include <sys/devops.h> 53 #include <sys/instance.h> 54 #include <sys/modctl.h> 55 #include <sys/open.h> 56 #include <sys/stat.h> 57 #include <sys/sunddi.h> 58 #include <sys/sunndi.h> 59 #include <sys/systm.h> 60 #include <sys/mkdev.h> 61 #include <sys/list.h> 62 #include "heci_data_structures.h" 63 #include "heci.h" 64 #include "heci_interface.h" 65 66 67 68 static const uint8_t interface_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10}; 69 static const uint8_t interface_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10}; 70 71 /* 72 * read_heci_register - Read a byte from the heci device 73 * 74 * @device: the device structure 75 * @offset: offset from which to read the data 76 * 77 * @return the byte read. 78 */ 79 uint32_t 80 read_heci_register(struct iamt_heci_device *device, 81 unsigned long offset) 82 { 83 return (ddi_get32(device->io_handle, 84 (uint32_t *)(long)(device->mem_addr + offset))); 85 } 86 87 /* 88 * write_heci_register - Write 4 bytes to the heci device 89 * 90 * @device: the device structure 91 * @offset: offset from which to write the data 92 * @value: the byte to write 93 */ 94 void 95 write_heci_register(struct iamt_heci_device *device, unsigned long offset, 96 uint32_t value) 97 { 98 ddi_put32(device->io_handle, 99 (uint32_t *)(long)(device->mem_addr + offset), value); 100 } 101 102 103 /* 104 * heci_set_csr_register - write H_CSR register to the heci device 105 * 106 * @dev: device object for our driver 107 */ 108 void 109 heci_set_csr_register(struct iamt_heci_device *dev) 110 { 111 write_heci_register(dev, H_CSR, dev->host_hw_state); 112 dev->host_hw_state = read_heci_register(dev, H_CSR); 113 } 114 115 /* 116 * heci_csr_enable_interrupts - enable heci device interrupts 117 * 118 * @dev: device object for our driver 119 */ 120 void 121 heci_csr_enable_interrupts(struct iamt_heci_device *dev) 122 { 123 dev->host_hw_state |= H_IE; 124 heci_set_csr_register(dev); 125 } 126 127 /* 128 * heci_csr_disable_interrupts - disable heci device interrupts 129 * 130 * @dev: device object for our driver 131 */ 132 void 133 heci_csr_disable_interrupts(struct iamt_heci_device *dev) 134 { 135 dev->host_hw_state &= ~H_IE; 136 heci_set_csr_register(dev); 137 } 138 139 140 /* 141 * _host_get_filled_slots - get number of device filled buffer slots 142 * 143 * @device: the device structure 144 * 145 * @return numer of filled slots 146 */ 147 static unsigned char 148 _host_get_filled_slots(struct iamt_heci_device *dev) 149 { 150 char read_ptr, write_ptr; 151 152 read_ptr = (char)((dev->host_hw_state & H_CBRP) >> 8); 153 write_ptr = (char)((dev->host_hw_state & H_CBWP) >> 16); 154 155 return ((unsigned char)(write_ptr - read_ptr)); 156 } 157 158 /* 159 * host_buffer_is_empty - check if host buffer is empty. 160 * 161 * @dev: device object for our driver 162 * 163 * @return 1 if empty, 0 - otherwise. 164 */ 165 int 166 host_buffer_is_empty(struct iamt_heci_device *dev) 167 { 168 unsigned char filled_slots; 169 170 dev->host_hw_state = read_heci_register(dev, H_CSR); 171 filled_slots = _host_get_filled_slots(dev); 172 173 if (filled_slots > 0) 174 return (0); 175 176 return (1); 177 } 178 179 /* 180 * count_empty_write_slots - count write empty slots. 181 * 182 * @dev: device object for our driver 183 * 184 * @return -1(ESLOTS_OVERFLOW) if overflow, otherwise empty slots count 185 */ 186 int32_t 187 count_empty_write_slots(struct iamt_heci_device *dev) 188 { 189 unsigned char buffer_depth, filled_slots, empty_slots; 190 191 buffer_depth = (unsigned char)((dev->host_hw_state & H_CBD) >> 24); 192 filled_slots = _host_get_filled_slots(dev); 193 empty_slots = buffer_depth - filled_slots; 194 195 if (filled_slots > buffer_depth) { 196 /* overflow */ 197 return (-ESLOTS_OVERFLOW); 198 } 199 200 return ((int32_t)empty_slots); 201 } 202 203 /* 204 * heci_write_message - write a message to heci device. 205 * 206 * @dev: device object for our driver 207 * @heci_hdr: header of message 208 * @write_buffer: message buffer will be write 209 * @write_length: message size will be write 210 * 211 * @return 1 if success, 0 - otherwise. 212 */ 213 int 214 heci_write_message(struct iamt_heci_device *dev, 215 struct heci_msg_hdr *header, 216 unsigned char *write_buffer, 217 unsigned long write_length) 218 { 219 uint32_t temp_msg = 0; 220 unsigned long bytes_written = 0; 221 unsigned char buffer_depth, filled_slots, empty_slots; 222 unsigned long dw_to_write; 223 224 dev->host_hw_state = read_heci_register(dev, H_CSR); 225 DBG("host_hw_state = 0x%08x.\n", dev->host_hw_state); 226 DBG("heci_write_message header=%08x.\n", *((uint32_t *)(void *)header)); 227 buffer_depth = (unsigned char)((dev->host_hw_state & H_CBD) >> 24); 228 filled_slots = _host_get_filled_slots(dev); 229 empty_slots = buffer_depth - filled_slots; 230 DBG("filled = %u, empty = %u.\n", filled_slots, empty_slots); 231 232 dw_to_write = ((write_length + 3) / 4); 233 234 if (dw_to_write > empty_slots) 235 return (0); 236 237 write_heci_register(dev, H_CB_WW, *((uint32_t *)(void *)header)); 238 239 while (write_length >= 4) { 240 write_heci_register(dev, H_CB_WW, 241 *(uint32_t *)(void *)(write_buffer + bytes_written)); 242 bytes_written += 4; 243 write_length -= 4; 244 } 245 246 if (write_length > 0) { 247 (void) memcpy(&temp_msg, &write_buffer[bytes_written], 248 write_length); 249 write_heci_register(dev, H_CB_WW, temp_msg); 250 } 251 252 dev->host_hw_state |= H_IG; 253 write_heci_register(dev, H_CSR, dev->host_hw_state); 254 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA); 255 if ((dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA) 256 return (0); 257 258 dev->write_hang = 0; 259 return (1); 260 } 261 262 /* 263 * count_full_read_slots - count read full slots. 264 * 265 * @dev: device object for our driver 266 * 267 * @return -1(ESLOTS_OVERFLOW) if overflow, otherwise filled slots count 268 */ 269 int32_t 270 count_full_read_slots(struct iamt_heci_device *dev) 271 { 272 char read_ptr, write_ptr; 273 unsigned char buffer_depth, filled_slots; 274 275 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA); 276 buffer_depth = (unsigned char)((dev->me_hw_state & ME_CBD_HRA) >> 24); 277 read_ptr = (char)((dev->me_hw_state & ME_CBRP_HRA) >> 8); 278 write_ptr = (char)((dev->me_hw_state & ME_CBWP_HRA) >> 16); 279 filled_slots = (unsigned char)(write_ptr - read_ptr); 280 281 if (filled_slots > buffer_depth) { 282 /* overflow */ 283 return (-ESLOTS_OVERFLOW); 284 } 285 286 DBG("filled_slots =%08x \n", filled_slots); 287 return ((int32_t)filled_slots); 288 } 289 290 /* 291 * heci_read_slots - read a message from heci device. 292 * 293 * @dev: device object for our driver 294 * @buffer: message buffer will be write 295 * @buffer_length: message size will be read 296 */ 297 void 298 heci_read_slots(struct iamt_heci_device *dev, 299 unsigned char *buffer, unsigned long buffer_length) 300 { 301 uint32_t i = 0; 302 unsigned char temp_buf[sizeof (uint32_t)]; 303 304 while (buffer_length >= sizeof (uint32_t)) { 305 ((uint32_t *)(void *)buffer)[i] = 306 read_heci_register(dev, ME_CB_RW); 307 DBG("buffer[%d]= %d\n", i, ((uint32_t *)(void *)buffer)[i]); 308 i++; 309 buffer_length -= sizeof (uint32_t); 310 } 311 312 if (buffer_length > 0) { 313 *((uint32_t *)(void *)&temp_buf) = 314 read_heci_register(dev, ME_CB_RW); 315 (void) memcpy(&buffer[i * 4], temp_buf, buffer_length); 316 } 317 318 dev->host_hw_state |= H_IG; 319 heci_set_csr_register(dev); 320 } 321 322 /* 323 * flow_ctrl_creds - check flow_control credentials. 324 * 325 * @dev: device object for our driver 326 * @file_ext: private data of the file object 327 * 328 * @return 1 if flow_ctrl_creds >0, 0 - otherwise. 329 */ 330 int 331 flow_ctrl_creds(struct iamt_heci_device *dev, 332 struct heci_file_private *file_ext) 333 { 334 uint8_t i; 335 336 if (!dev->num_heci_me_clients) 337 return (0); 338 339 if (file_ext == NULL) 340 return (0); 341 342 if (file_ext->flow_ctrl_creds > 0) 343 return (1); 344 345 for (i = 0; i < dev->num_heci_me_clients; i++) { 346 if (dev->me_clients[i].client_id == file_ext->me_client_id) { 347 if (dev->me_clients[i].flow_ctrl_creds > 0) { 348 ASSERT(dev->me_clients[i].props.single_recv_buf 349 != 0); 350 return (1); 351 } 352 return (0); 353 } 354 } 355 ASSERT(0); 356 return (0); 357 } 358 359 /* 360 * flow_ctrl_reduce - reduce flow_control. 361 * 362 * @dev: device object for our driver 363 * @file_ext: private data of the file object 364 */ 365 void 366 flow_ctrl_reduce(struct iamt_heci_device *dev, 367 struct heci_file_private *file_ext) 368 { 369 uint8_t i; 370 371 if (!dev->num_heci_me_clients) 372 return; 373 374 for (i = 0; i < dev->num_heci_me_clients; i++) { 375 if (dev->me_clients[i].client_id == file_ext->me_client_id) { 376 if (dev->me_clients[i].props.single_recv_buf != 0) { 377 ASSERT(dev->me_clients[i].flow_ctrl_creds > 0); 378 dev->me_clients[i].flow_ctrl_creds--; 379 } else { 380 ASSERT(file_ext->flow_ctrl_creds > 0); 381 file_ext->flow_ctrl_creds--; 382 } 383 return; 384 } 385 } 386 ASSERT(0); 387 } 388 389 /* 390 * heci_send_flow_control - send flow control to fw. 391 * 392 * @dev: device object for our driver 393 * @file_ext: private data of the file object 394 * 395 * @return 1 if success, 0 - otherwise. 396 */ 397 int 398 heci_send_flow_control(struct iamt_heci_device *dev, 399 struct heci_file_private *file_ext) 400 { 401 struct heci_msg_hdr *heci_hdr; 402 struct hbm_flow_control *heci_flow_control; 403 404 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 405 heci_hdr->host_addr = 0; 406 heci_hdr->me_addr = 0; 407 heci_hdr->length = sizeof (struct hbm_flow_control); 408 heci_hdr->msg_complete = 1; 409 heci_hdr->reserved = 0; 410 411 heci_flow_control = (struct hbm_flow_control *)&dev->wr_msg_buf[1]; 412 (void) memset(heci_flow_control, 0, sizeof (heci_flow_control)); 413 heci_flow_control->host_addr = file_ext->host_client_id; 414 heci_flow_control->me_addr = file_ext->me_client_id; 415 heci_flow_control->cmd.cmd = HECI_FLOW_CONTROL_CMD; 416 (void) memset(heci_flow_control->reserved, 0, 417 sizeof (heci_flow_control->reserved)); 418 DBG("sending flow control host client = %d, me client = %d\n", 419 file_ext->host_client_id, file_ext->me_client_id); 420 if (!heci_write_message(dev, heci_hdr, 421 (unsigned char *)heci_flow_control, 422 sizeof (struct hbm_flow_control))) 423 return (0); 424 425 return (1); 426 427 } 428 429 /* 430 * other_client_is_connecting - check if other 431 * client with the same client id is connected. 432 * 433 * @dev: device object for our driver 434 * @file_ext: private data of the file object 435 * 436 * @return 1 if other client is connected, 0 - otherwise. 437 */ 438 int 439 other_client_is_connecting(struct iamt_heci_device *dev, 440 struct heci_file_private *file_ext) 441 { 442 443 struct heci_file_private *file_pos = NULL; 444 struct heci_file_private *file_next = NULL; 445 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link, 446 struct heci_file_private) { 447 if ((file_pos->state == HECI_FILE_CONNECTING) && 448 (file_pos != file_ext) && 449 file_ext->me_client_id == file_pos->me_client_id) 450 return (1); 451 452 } 453 return (0); 454 } 455 456 /* 457 * heci_send_wd - send watch dog message to fw. 458 * 459 * @dev: device object for our driver 460 * 461 * @return 1 if success, 0 - otherwise. 462 */ 463 int 464 heci_send_wd(struct iamt_heci_device *dev) 465 { 466 struct heci_msg_hdr *heci_hdr; 467 468 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 469 heci_hdr->host_addr = dev->wd_file_ext.host_client_id; 470 heci_hdr->me_addr = dev->wd_file_ext.me_client_id; 471 heci_hdr->msg_complete = 1; 472 heci_hdr->reserved = 0; 473 474 if (!memcmp(dev->wd_data, interface_start_wd_params, 475 HECI_WD_PARAMS_SIZE)) { 476 heci_hdr->length = HECI_START_WD_DATA_SIZE; 477 } else { 478 ASSERT(!memcmp(dev->wd_data, interface_stop_wd_params, 479 HECI_WD_PARAMS_SIZE)); 480 heci_hdr->length = HECI_WD_PARAMS_SIZE; 481 } 482 483 if (!heci_write_message(dev, heci_hdr, dev->wd_data, heci_hdr->length)) 484 return (0); 485 486 return (1); 487 } 488 489 /* 490 * heci_disconnect - send disconnect message to fw. 491 * 492 * @dev: device object for our driver 493 * @file_ext: private data of the file object 494 * 495 * @return 1 if success, 0 - otherwise. 496 */ 497 int 498 heci_disconnect(struct iamt_heci_device *dev, 499 struct heci_file_private *file_ext) 500 { 501 struct heci_msg_hdr *heci_hdr; 502 struct hbm_client_disconnect_request *heci_cli_disconnect; 503 504 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 505 heci_hdr->host_addr = 0; 506 heci_hdr->me_addr = 0; 507 heci_hdr->length = sizeof (struct hbm_client_disconnect_request); 508 heci_hdr->msg_complete = 1; 509 heci_hdr->reserved = 0; 510 511 heci_cli_disconnect = 512 (struct hbm_client_disconnect_request *)&dev->wr_msg_buf[1]; 513 (void) memset(heci_cli_disconnect, 0, sizeof (heci_cli_disconnect)); 514 heci_cli_disconnect->host_addr = file_ext->host_client_id; 515 heci_cli_disconnect->me_addr = file_ext->me_client_id; 516 heci_cli_disconnect->cmd.cmd = CLIENT_DISCONNECT_REQ_CMD; 517 heci_cli_disconnect->reserved[0] = 0; 518 519 if (!heci_write_message(dev, heci_hdr, 520 (unsigned char *)heci_cli_disconnect, 521 sizeof (struct hbm_client_disconnect_request))) 522 return (0); 523 524 return (1); 525 } 526 527 /* 528 * heci_connect - send connect message to fw. 529 * 530 * @dev: device object for our driver 531 * @file_ext: private data of the file object 532 * 533 * @return 1 if success, 0 - otherwise. 534 */ 535 int 536 heci_connect(struct iamt_heci_device *dev, 537 struct heci_file_private *file_ext) 538 { 539 struct heci_msg_hdr *heci_hdr; 540 struct hbm_client_connect_request *heci_cli_connect; 541 542 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 543 heci_hdr->host_addr = 0; 544 heci_hdr->me_addr = 0; 545 heci_hdr->length = sizeof (struct hbm_client_connect_request); 546 heci_hdr->msg_complete = 1; 547 heci_hdr->reserved = 0; 548 549 heci_cli_connect = 550 (struct hbm_client_connect_request *)&dev->wr_msg_buf[1]; 551 heci_cli_connect->host_addr = file_ext->host_client_id; 552 heci_cli_connect->me_addr = file_ext->me_client_id; 553 heci_cli_connect->cmd.cmd = CLIENT_CONNECT_REQ_CMD; 554 heci_cli_connect->reserved = 0; 555 556 if (!heci_write_message(dev, heci_hdr, 557 (unsigned char *)heci_cli_connect, 558 sizeof (struct hbm_client_connect_request))) 559 return (0); 560 561 return (1); 562 } 563