diff options
Diffstat (limited to 'drivers/staging/mei')
-rw-r--r-- | drivers/staging/mei/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/mei/TODO | 10 | ||||
-rw-r--r-- | drivers/staging/mei/init.c | 181 | ||||
-rw-r--r-- | drivers/staging/mei/interface.c | 13 | ||||
-rw-r--r-- | drivers/staging/mei/interface.h | 8 | ||||
-rw-r--r-- | drivers/staging/mei/interrupt.c | 128 | ||||
-rw-r--r-- | drivers/staging/mei/iorw.c | 32 | ||||
-rw-r--r-- | drivers/staging/mei/main.c | 117 | ||||
-rw-r--r-- | drivers/staging/mei/mei_dev.h | 155 | ||||
-rw-r--r-- | drivers/staging/mei/wd.c | 254 |
10 files changed, 573 insertions, 327 deletions
diff --git a/drivers/staging/mei/Kconfig b/drivers/staging/mei/Kconfig index 3f3f170..47d78a7 100644 --- a/drivers/staging/mei/Kconfig +++ b/drivers/staging/mei/Kconfig @@ -1,6 +1,6 @@ config INTEL_MEI tristate "Intel Management Engine Interface (Intel MEI)" - depends on X86 && PCI && EXPERIMENTAL + depends on X86 && PCI && EXPERIMENTAL && WATCHDOG_CORE help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. diff --git a/drivers/staging/mei/TODO b/drivers/staging/mei/TODO index 3b6a667..7d9a13b 100644 --- a/drivers/staging/mei/TODO +++ b/drivers/staging/mei/TODO @@ -1,14 +1,4 @@ TODO: - - Create in-kernel Client API. Examples of in-kernel clients are watchdog and AMTHI. - - ME Watchdog Driver to expose standard Linux watchdog interface - - Rewrite AMTHI to use in-kernel client interface - - Cleanup init and probe functions - - Review BUG/BUG_ON usage - - Cleanup and reorganize header files - - Rewrite client data structure - - Make state machine more readable - - Add mei.txt with driver explanation and it's driver - - Fix Kconfig - Cleanup and split the timer function Upon Unstaging: - move mei.h to include/linux/mei.h diff --git a/drivers/staging/mei/init.c b/drivers/staging/mei/init.c index 685fcf6..a78e63b 100644 --- a/drivers/staging/mei/init.c +++ b/drivers/staging/mei/init.c @@ -29,54 +29,29 @@ const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac, 0x81, 0x4c); /** - * mei_initialize_list - Sets up a queue list. + * mei_io_list_init - Sets up a queue list. * - * @list: An instance of our list structure + * @list: An instance io list structure * @dev: the device structure */ -void mei_initialize_list(struct mei_io_list *list, struct mei_device *dev) +void mei_io_list_init(struct mei_io_list *list) { /* initialize our queue list */ INIT_LIST_HEAD(&list->mei_cb.cb_list); list->status = 0; - list->device_extension = dev; } /** - * mei_flush_queues - flushes queue lists belonging to cl. - * - * @dev: the device structure - * @cl: private data of the file object - */ -void mei_flush_queues(struct mei_device *dev, struct mei_cl *cl) -{ - int i; - - if (!dev || !cl) - return; - - for (i = 0; i < MEI_IO_LISTS_NUMBER; i++) { - dev_dbg(&dev->pdev->dev, "remove list entry belonging to cl\n"); - mei_flush_list(dev->io_list_array[i], cl); - } -} - - -/** - * mei_flush_list - removes list entry belonging to cl. + * mei_io_list_flush - removes list entry belonging to cl. * * @list: An instance of our list structure * @cl: private data of the file object */ -void mei_flush_list(struct mei_io_list *list, struct mei_cl *cl) +void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl) { - struct mei_cl *cl_tmp; struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb_next = NULL; - if (!list || !cl) - return; - if (list->status != 0) return; @@ -86,14 +61,36 @@ void mei_flush_list(struct mei_io_list *list, struct mei_cl *cl) list_for_each_entry_safe(cb_pos, cb_next, &list->mei_cb.cb_list, cb_list) { if (cb_pos) { - cl_tmp = (struct mei_cl *) - cb_pos->file_private; - if (cl_tmp && - mei_fe_same_id(cl, cl_tmp)) + struct mei_cl *cl_tmp; + cl_tmp = (struct mei_cl *)cb_pos->file_private; + if (mei_cl_cmp_id(cl, cl_tmp)) list_del(&cb_pos->cb_list); } } } +/** + * mei_cl_flush_queues - flushes queue lists belonging to cl. + * + * @dev: the device structure + * @cl: private data of the file object + */ +int mei_cl_flush_queues(struct mei_cl *cl) +{ + if (!cl || !cl->dev) + return -EINVAL; + + dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); + mei_io_list_flush(&cl->dev->read_list, cl); + mei_io_list_flush(&cl->dev->write_list, cl); + mei_io_list_flush(&cl->dev->write_waiting_list, cl); + mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); + mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); + mei_io_list_flush(&cl->dev->amthi_cmd_list, cl); + mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl); + return 0; +} + + /** * mei_reset_iamthif_params - initializes mei device iamthif @@ -106,8 +103,8 @@ static void mei_reset_iamthif_params(struct mei_device *dev) dev->iamthif_current_cb = NULL; dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; - dev->iamthif_canceled = 0; - dev->iamthif_ioctl = 0; + dev->iamthif_canceled = false; + dev->iamthif_ioctl = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; } @@ -119,9 +116,8 @@ static void mei_reset_iamthif_params(struct mei_device *dev) * * returns The mei_device_device pointer on success, NULL on failure. */ -struct mei_device *init_mei_device(struct pci_dev *pdev) +struct mei_device *mei_device_init(struct pci_dev *pdev) { - int i; struct mei_device *dev; dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL); @@ -129,13 +125,6 @@ struct mei_device *init_mei_device(struct pci_dev *pdev) return NULL; /* setup our list array */ - dev->io_list_array[0] = &dev->read_list; - dev->io_list_array[1] = &dev->write_list; - dev->io_list_array[2] = &dev->write_waiting_list; - dev->io_list_array[3] = &dev->ctrl_wr_list; - dev->io_list_array[4] = &dev->ctrl_rd_list; - dev->io_list_array[5] = &dev->amthi_cmd_list; - dev->io_list_array[6] = &dev->amthi_read_complete_list; INIT_LIST_HEAD(&dev->file_list); INIT_LIST_HEAD(&dev->wd_cl.link); INIT_LIST_HEAD(&dev->iamthif_cl.link); @@ -143,9 +132,18 @@ struct mei_device *init_mei_device(struct pci_dev *pdev) init_waitqueue_head(&dev->wait_recvd_msg); init_waitqueue_head(&dev->wait_stop_wd); dev->mei_state = MEI_INITIALIZING; + dev->reset_count = 0; dev->iamthif_state = MEI_IAMTHIF_IDLE; - for (i = 0; i < MEI_IO_LISTS_NUMBER; i++) - mei_initialize_list(dev->io_list_array[i], dev); + dev->wd_interface_reg = false; + + + mei_io_list_init(&dev->read_list); + mei_io_list_init(&dev->write_list); + mei_io_list_init(&dev->write_waiting_list); + mei_io_list_init(&dev->ctrl_wr_list); + mei_io_list_init(&dev->ctrl_rd_list); + mei_io_list_init(&dev->amthi_cmd_list); + mei_io_list_init(&dev->amthi_read_complete_list); dev->pdev = pdev; return dev; } @@ -173,7 +171,7 @@ int mei_hw_init(struct mei_device *dev) if ((dev->host_hw_state & H_IS) == H_IS) mei_reg_write(dev, H_CSR, dev->host_hw_state); - dev->recvd_msg = 0; + dev->recvd_msg = false; dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); mei_reset(dev, 1); @@ -223,7 +221,7 @@ int mei_hw_init(struct mei_device *dev) goto out; } - dev->recvd_msg = 0; + dev->recvd_msg = false; dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", dev->host_hw_state, dev->me_hw_state); dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n"); @@ -267,7 +265,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) bool unexpected; if (dev->mei_state == MEI_RECOVERING_FROM_RESET) { - dev->need_reset = 1; + dev->need_reset = true; return; } @@ -291,7 +289,15 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n", dev->host_hw_state); - dev->need_reset = 0; + dev->need_reset = false; + + dev->reset_count++; + if (dev->reset_count > MEI_MAX_CONSEC_RESET) { + dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + dev->mei_state = MEI_DISABLED; + return; + } + if (dev->mei_state != MEI_INITIALIZING) { if (dev->mei_state != MEI_DISABLED && @@ -318,10 +324,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->extra_write_index = 0; } - dev->num_mei_me_clients = 0; + dev->me_clients_num = 0; dev->rd_msg_hdr = 0; - dev->stop = 0; - dev->wd_pending = 0; + dev->stop = false; + dev->wd_pending = false; /* update the state of the registers after reset */ dev->host_hw_state = mei_hcsr_read(dev); @@ -363,7 +369,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) * * returns none. */ -void host_start_message(struct mei_device *dev) +void mei_host_start_message(struct mei_device *dev) { struct mei_msg_hdr *mei_hdr; struct hbm_host_version_request *host_start_req; @@ -382,7 +388,7 @@ void host_start_message(struct mei_device *dev) host_start_req->cmd.cmd = HOST_START_REQ_CMD; host_start_req->host_version.major_version = HBM_MAJOR_VERSION; host_start_req->host_version.minor_version = HBM_MINOR_VERSION; - dev->recvd_msg = 0; + dev->recvd_msg = false; if (!mei_write_message(dev, mei_hdr, (unsigned char *) (host_start_req), mei_hdr->length)) { @@ -402,7 +408,7 @@ void host_start_message(struct mei_device *dev) * * returns none. */ -void host_enum_clients_message(struct mei_device *dev) +void mei_host_enum_clients_message(struct mei_device *dev) { struct mei_msg_hdr *mei_hdr; struct hbm_host_enum_request *host_enum_req; @@ -437,16 +443,16 @@ void host_enum_clients_message(struct mei_device *dev) * * returns none. */ -void allocate_me_clients_storage(struct mei_device *dev) +void mei_allocate_me_clients_storage(struct mei_device *dev) { struct mei_me_client *clients; int b; /* count how many ME clients we have */ for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) - dev->num_mei_me_clients++; + dev->me_clients_num++; - if (dev->num_mei_me_clients <= 0) + if (dev->me_clients_num <= 0) return ; @@ -455,9 +461,9 @@ void allocate_me_clients_storage(struct mei_device *dev) dev->me_clients = NULL; } dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", - dev->num_mei_me_clients * sizeof(struct mei_me_client)); + dev->me_clients_num * sizeof(struct mei_me_client)); /* allocate storage for ME clients representation */ - clients = kcalloc(dev->num_mei_me_clients, + clients = kcalloc(dev->me_clients_num, sizeof(struct mei_me_client), GFP_KERNEL); if (!clients) { dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); @@ -473,9 +479,12 @@ void allocate_me_clients_storage(struct mei_device *dev) * * @dev: the device structure * - * returns none. + * returns: + * < 0 - Error. + * = 0 - no more clients. + * = 1 - still have clients to send properties request. */ -void host_client_properties(struct mei_device *dev) +int mei_host_client_properties(struct mei_device *dev) { struct mei_msg_hdr *mei_header; struct hbm_props_request *host_cli_req; @@ -507,27 +516,15 @@ void host_client_properties(struct mei_device *dev) dev->mei_state = MEI_RESETING; dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); mei_reset(dev, 1); - return; + return -EIO; } dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; dev->me_client_index = b; - return; + return 1; } - - /* - * Clear Map for indicating now ME clients - * with associated host client - */ - bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); - dev->write_hang = -1; - dev->open_handle_count = 0; - bitmap_set(dev->host_clients_map, 0, 3); - dev->mei_state = MEI_ENABLED; - - mei_wd_host_init(dev); - return; + return 0; } /** @@ -536,7 +533,7 @@ void host_client_properties(struct mei_device *dev) * @priv: private file structure to be initialized * @file: the file structure */ -void mei_init_file_private(struct mei_cl *priv, struct mei_device *dev) +void mei_cl_init(struct mei_cl *priv, struct mei_device *dev) { memset(priv, 0, sizeof(struct mei_cl)); init_waitqueue_head(&priv->wait); @@ -552,7 +549,7 @@ int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid) { int i, res = -1; - for (i = 0; i < dev->num_mei_me_clients; ++i) + for (i = 0; i < dev->me_clients_num; ++i) if (uuid_le_cmp(cuuid, dev->me_clients[i].props.protocol_name) == 0) { res = i; @@ -601,12 +598,12 @@ u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv, * @dev: the device structure * */ -void host_init_iamthif(struct mei_device *dev) +void mei_host_init_iamthif(struct mei_device *dev) { u8 i; unsigned char *msg_buf; - mei_init_file_private(&dev->iamthif_cl, dev); + mei_cl_init(&dev->iamthif_cl, dev); dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; /* find ME amthi client */ @@ -656,17 +653,17 @@ void host_init_iamthif(struct mei_device *dev) * * returns The allocated file or NULL on failure */ -struct mei_cl *mei_alloc_file_private(struct mei_device *dev) +struct mei_cl *mei_cl_allocate(struct mei_device *dev) { - struct mei_cl *priv; + struct mei_cl *cl; - priv = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); - if (!priv) + cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); + if (!cl) return NULL; - mei_init_file_private(priv, dev); + mei_cl_init(cl, dev); - return priv; + return cl; } @@ -701,7 +698,7 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) cb->file_private = cl; cb->major_file_operations = MEI_CLOSE; if (dev->mei_host_buffer_is_empty) { - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (mei_disconnect(dev, cl)) { mdelay(10); /* Wait for hardware disconnection ready */ list_add_tail(&cb->cb_list, @@ -739,8 +736,8 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); } - mei_flush_list(&dev->ctrl_rd_list, cl); - mei_flush_list(&dev->ctrl_wr_list, cl); + mei_io_list_flush(&dev->ctrl_rd_list, cl); + mei_io_list_flush(&dev->ctrl_wr_list, cl); free: mei_free_cb_private(cb); return rets; diff --git a/drivers/staging/mei/interface.c b/drivers/staging/mei/interface.c index 4959aae..a65dacf 100644 --- a/drivers/staging/mei/interface.c +++ b/drivers/staging/mei/interface.c @@ -179,7 +179,6 @@ int mei_write_message(struct mei_device *dev, if ((dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA) return 0; - dev->write_hang = 0; return 1; } @@ -256,13 +255,13 @@ int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl) { int i; - if (!dev->num_mei_me_clients) + if (!dev->me_clients_num) return 0; if (cl->mei_flow_ctrl_creds > 0) return 1; - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { struct mei_me_client *me_cl = &dev->me_clients[i]; if (me_cl->client_id == cl->me_client_id) { if (me_cl->mei_flow_ctrl_creds) { @@ -291,10 +290,10 @@ int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl) { int i; - if (!dev->num_mei_me_clients) + if (!dev->me_clients_num) return -ENOENT; - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { struct mei_me_client *me_cl = &dev->me_clients[i]; if (me_cl->client_id == cl->me_client_id) { if (me_cl->props.single_recv_buf != 0) { @@ -333,7 +332,7 @@ int mei_send_flow_control(struct mei_device *dev, struct mei_cl *cl) mei_hdr->reserved = 0; mei_flow_control = (struct hbm_flow_control *) &dev->wr_msg_buf[1]; - memset(mei_flow_control, 0, sizeof(mei_flow_control)); + memset(mei_flow_control, 0, sizeof(*mei_flow_control)); mei_flow_control->host_addr = cl->host_client_id; mei_flow_control->me_addr = cl->me_client_id; mei_flow_control->cmd.cmd = MEI_FLOW_CONTROL_CMD; @@ -397,7 +396,7 @@ int mei_disconnect(struct mei_device *dev, struct mei_cl *cl) mei_cli_disconnect = (struct hbm_client_disconnect_request *) &dev->wr_msg_buf[1]; - memset(mei_cli_disconnect, 0, sizeof(mei_cli_disconnect)); + memset(mei_cli_disconnect, 0, sizeof(*mei_cli_disconnect)); mei_cli_disconnect->host_addr = cl->host_client_id; mei_cli_disconnect->me_addr = cl->me_client_id; mei_cli_disconnect->cmd.cmd = CLIENT_DISCONNECT_REQ_CMD; diff --git a/drivers/staging/mei/interface.h b/drivers/staging/mei/interface.h index d0bf5cf..7bd38ae 100644 --- a/drivers/staging/mei/interface.h +++ b/drivers/staging/mei/interface.h @@ -23,7 +23,9 @@ #include "mei_dev.h" -#define AMT_WD_VALUE 120 /* seconds */ +#define AMT_WD_DEFAULT_TIMEOUT 120 /* seconds */ +#define AMT_WD_MIN_TIMEOUT 120 /* seconds */ +#define AMT_WD_MAX_TIMEOUT 65535 /* seconds */ #define MEI_WATCHDOG_DATA_SIZE 16 #define MEI_START_WD_DATA_SIZE 20 @@ -48,8 +50,8 @@ int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl); int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev, bool preserve); -void mei_wd_host_init(struct mei_device *dev); -void mei_wd_start_setup(struct mei_device *dev); +bool mei_wd_host_init(struct mei_device *dev); +void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout); int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl); diff --git a/drivers/staging/mei/interrupt.c b/drivers/staging/mei/interrupt.c index d1b9214..42b7c9a 100644 --- a/drivers/staging/mei/interrupt.c +++ b/drivers/staging/mei/interrupt.c @@ -94,7 +94,7 @@ static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos) dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); } else { - run_next_iamthif_cmd(dev); + mei_run_next_iamthif_cmd(dev); } dev_dbg(&dev->pdev->dev, "completing amthi call back.\n"); @@ -195,7 +195,7 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, { struct mei_cl *cl; struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; - unsigned char *buffer; + unsigned char *buffer = NULL; dev_dbg(&dev->pdev->dev, "start client msg\n"); if (!(dev->read_list.status == 0 && @@ -280,7 +280,7 @@ static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots) } else { dev_dbg(&dev->pdev->dev, "iamthif flow control success\n"); dev->iamthif_state = MEI_IAMTHIF_READING; - dev->iamthif_flow_control_pending = 0; + dev->iamthif_flow_control_pending = false; dev->iamthif_msg_buf_index = 0; dev->iamthif_msg_buf_size = 0; dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER; @@ -396,7 +396,19 @@ static void mei_client_connect_response(struct mei_device *dev, dev->wd_due_counter = (dev->wd_timeout) ? 1 : 0; dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); - host_init_iamthif(dev); + + /* Registering watchdog interface device once we got connection + to the WD Client + */ + if (watchdog_register_device(&amt_wd_dev)) { + printk(KERN_ERR "mei: unable to register watchdog device.\n"); + dev->wd_interface_reg = false; + } else { + dev_dbg(&dev->pdev->dev, "successfully register watchdog interface.\n"); + dev->wd_interface_reg = true; + } + + mei_host_init_iamthif(dev); return; } @@ -499,7 +511,7 @@ static void add_single_flow_creds(struct mei_device *dev, struct mei_me_client *client; int i; - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { client = &dev->me_clients[i]; if (client && flow->me_addr == client->client_id) { if (client->props.single_recv_buf) { @@ -593,7 +605,7 @@ static void mei_client_disconnect_request(struct mei_device *dev, cl_pos->timer_count = 0; if (cl_pos == &dev->wd_cl) { dev->wd_due_counter = 0; - dev->wd_pending = 0; + dev->wd_pending = false; } else if (cl_pos == &dev->iamthif_cl) dev->iamthif_timer = 0; @@ -641,6 +653,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, struct hbm_host_enum_response *enum_res; struct hbm_client_disconnect_request *disconnect_req; struct hbm_host_stop_request *host_stop_req; + int res; unsigned char *buffer; @@ -659,9 +672,9 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, if (dev->mei_state == MEI_INIT_CLIENTS && dev->init_clients_state == MEI_START_MESSAGE) { dev->init_clients_timer = 0; - host_enum_clients_message(dev); + mei_host_enum_clients_message(dev); } else { - dev->recvd_msg = 0; + dev->recvd_msg = false; dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n"); mei_reset(dev, 1); return; @@ -690,7 +703,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, return; } - dev->recvd_msg = 1; + dev->recvd_msg = true; dev_dbg(&dev->pdev->dev, "host start response message received.\n"); break; @@ -734,7 +747,39 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, MEI_CLIENT_PROPERTIES_MESSAGE) { dev->me_client_index++; dev->me_client_presentation_num++; - host_client_properties(dev); + + /** Send Client Propeties request **/ + res = mei_host_client_properties(dev); + if (res < 0) { + dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed"); + return; + } else if (!res) { + /* + * No more clients to send to. + * Clear Map for indicating now ME clients + * with associated host client + */ + bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); + dev->open_handle_count = 0; + + /* + * Reserving the first three client IDs + * Client Id 0 - Reserved for MEI Bus Message communications + * Client Id 1 - Reserved for Watchdog + * Client ID 2 - Reserved for AMTHI + */ + bitmap_set(dev->host_clients_map, 0, 3); + dev->mei_state = MEI_ENABLED; + dev->reset_count = 0; + + /* if wd initialization fails, initialization the AMTHI client, + * otherwise the AMTHI client will be initialized after the WD client connect response + * will be received + */ + if (mei_wd_host_init(dev)) + mei_host_init_iamthif(dev); + } + } else { dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message"); mei_reset(dev, 1); @@ -755,10 +800,10 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, dev->init_clients_timer = 0; dev->me_client_presentation_num = 0; dev->me_client_index = 0; - allocate_me_clients_storage(dev); + mei_allocate_me_clients_storage(dev); dev->init_clients_state = MEI_CLIENT_PROPERTIES_MESSAGE; - host_client_properties(dev); + mei_host_client_properties(dev); } else { dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n"); mei_reset(dev, 1); @@ -1028,7 +1073,7 @@ static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots, cb_pos->information = dev->iamthif_msg_buf_index; cl->status = 0; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; - dev->iamthif_flow_control_pending = 1; + dev->iamthif_flow_control_pending = true; /* save iamthif cb sent to amthi client */ dev->iamthif_current_cb = cb_pos; list_move_tail(&cb_pos->cb_list, @@ -1192,7 +1237,6 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); return 0; } - dev->write_hang = -1; *slots = mei_count_empty_write_slots(dev); /* complete all waiting for write CB */ dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); @@ -1232,7 +1276,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, } if (dev->stop && !dev->wd_pending) { - dev->wd_stopped = 1; + dev->wd_stopped = true; wake_up_interruptible(&dev->wait_stop_wd); return 0; } @@ -1256,7 +1300,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) return -ENODEV; - dev->wd_pending = 0; + dev->wd_pending = false; if (dev->wd_timeout) { *slots -= (sizeof(struct mei_msg_hdr) + @@ -1382,7 +1426,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, * * NOTE: This function is called by timer interrupt work */ -void mei_wd_timer(struct work_struct *work) +void mei_timer(struct work_struct *work) { unsigned long timeout; struct mei_cl *cl_pos = NULL; @@ -1392,7 +1436,7 @@ void mei_wd_timer(struct work_struct *work) struct mei_cl_cb *cb_next = NULL; struct mei_device *dev = container_of(work, - struct mei_device, wd_work.work); + struct mei_device, timer_work.work); mutex_lock(&dev->device_lock); @@ -1419,41 +1463,14 @@ void mei_wd_timer(struct work_struct *work) } } - if (dev->wd_cl.state != MEI_FILE_CONNECTED) - goto out; - - /* Watchdog */ - if (dev->wd_due_counter && !dev->wd_bypass) { - if (--dev->wd_due_counter == 0) { - if (dev->mei_host_buffer_is_empty && - mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { - dev->mei_host_buffer_is_empty = 0; - dev_dbg(&dev->pdev->dev, "send watchdog.\n"); - - if (mei_wd_send(dev)) - dev_dbg(&dev->pdev->dev, "wd send failed.\n"); - else - if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) - goto out; - - if (dev->wd_timeout) - dev->wd_due_counter = 2; - else - dev->wd_due_counter = 0; - - } else - dev->wd_pending = 1; - - } - } if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { dev_dbg(&dev->pdev->dev, "reseting because of hang to amthi.\n"); mei_reset(dev, 1); dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; - dev->iamthif_canceled = 0; - dev->iamthif_ioctl = 1; + dev->iamthif_canceled = false; + dev->iamthif_ioctl = true; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; @@ -1462,7 +1479,7 @@ void mei_wd_timer(struct work_struct *work) dev->iamthif_file_object = NULL; dev->iamthif_current_cb = NULL; - run_next_iamthif_cmd(dev); + mei_run_next_iamthif_cmd(dev); } } @@ -1506,12 +1523,13 @@ void mei_wd_timer(struct work_struct *work) dev->iamthif_file_object = NULL; dev->iamthif_current_cb = NULL; dev->iamthif_timer = 0; - run_next_iamthif_cmd(dev); + mei_run_next_iamthif_cmd(dev); } } out: - schedule_delayed_work(&dev->wd_work, 2 * HZ); + if (dev->mei_state != MEI_DISABLED) + schedule_delayed_work(&dev->timer_work, 2 * HZ); mutex_unlock(&dev->device_lock); } @@ -1539,8 +1557,14 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */ mutex_lock(&dev->device_lock); - mei_initialize_list(&complete_list, dev); + mei_io_list_init(&complete_list); dev->host_hw_state = mei_hcsr_read(dev); + + /* Ack the interrupt here + * In case of MSI we don't go throuhg the quick handler */ + if (pci_dev_msi_enabled(dev->pdev)) + mei_reg_write(dev, H_CSR, dev->host_hw_state); + dev->me_hw_state = mei_mecsr_read(dev); /* check if ME wants a reset */ @@ -1564,7 +1588,7 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) /* link is established * start sending messages. */ - host_start_message(dev); + mei_host_start_message(dev); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } else { diff --git a/drivers/staging/mei/iorw.c b/drivers/staging/mei/iorw.c index 697a277..8a61d12 100644 --- a/drivers/staging/mei/iorw.c +++ b/drivers/staging/mei/iorw.c @@ -121,7 +121,7 @@ int mei_ioctl_connect_client(struct file *file, clear_bit(cl->host_client_id, dev->host_clients_map); list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { - if (mei_fe_same_id(cl, cl_pos)) { + if (mei_cl_cmp_id(cl, cl_pos)) { dev_dbg(&dev->pdev->dev, "remove file private data node host" " client = %d, ME client = %d.\n", @@ -161,7 +161,7 @@ int mei_ioctl_connect_client(struct file *file, if (dev->mei_host_buffer_is_empty && !mei_other_client_is_connecting(dev, cl)) { dev_dbg(&dev->pdev->dev, "Sending Connect Message\n"); - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (!mei_connect(dev, cl)) { dev_dbg(&dev->pdev->dev, "Sending connect message - failed\n"); rets = -ENODEV; @@ -204,8 +204,8 @@ int mei_ioctl_connect_client(struct file *file, } rets = -EFAULT; - mei_flush_list(&dev->ctrl_rd_list, cl); - mei_flush_list(&dev->ctrl_wr_list, cl); + mei_io_list_flush(&dev->ctrl_rd_list, cl); + mei_io_list_flush(&dev->ctrl_wr_list, cl); goto end; } rets = 0; @@ -277,13 +277,13 @@ int amthi_read(struct mei_device *dev, struct file *file, return -ETIMEDOUT; } - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { if (dev->me_clients[i].client_id == dev->iamthif_cl.me_client_id) break; } - if (i == dev->num_mei_me_clients) { + if (i == dev->me_clients_num) { dev_dbg(&dev->pdev->dev, "amthi client not found.\n"); return -ENODEV; } @@ -409,7 +409,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n", cl->host_client_id, cl->me_client_id); - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { if (dev->me_clients[i].client_id == cl->me_client_id) break; @@ -420,7 +420,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) goto unlock; } - if (i == dev->num_mei_me_clients) { + if (i == dev->me_clients_num) { rets = -ENODEV; goto unlock; } @@ -439,7 +439,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) cb->file_private = (void *) cl; cl->read_cb = cb; if (dev->mei_host_buffer_is_empty) { - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (!mei_send_flow_control(dev, cl)) { rets = -ENODEV; goto unlock; @@ -478,8 +478,8 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; dev->iamthif_file_object = cb->file_object; - dev->iamthif_canceled = 0; - dev->iamthif_ioctl = 1; + dev->iamthif_canceled = false; + dev->iamthif_ioctl = true; dev->iamthif_msg_buf_size = cb->request_buffer.size; memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, cb->request_buffer.size); @@ -490,7 +490,7 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) if (ret && dev->mei_host_buffer_is_empty) { ret = 0; - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (cb->request_buffer.size > (((dev->host_hw_state & H_CBD) >> 24) * sizeof(u32)) -sizeof(struct mei_msg_hdr)) { @@ -515,7 +515,7 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) if (mei_hdr.msg_complete) { if (mei_flow_ctrl_reduce(dev, &dev->iamthif_cl)) return -ENODEV; - dev->iamthif_flow_control_pending = 1; + dev->iamthif_flow_control_pending = true; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; dev_dbg(&dev->pdev->dev, "add amthi cb to write waiting list\n"); dev->iamthif_current_cb = cb; @@ -547,7 +547,7 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) * * returns 0 on success, <0 on failure. */ -void run_next_iamthif_cmd(struct mei_device *dev) +void mei_run_next_iamthif_cmd(struct mei_device *dev) { struct mei_cl *cl_tmp; struct mei_cl_cb *cb_pos = NULL; @@ -559,8 +559,8 @@ void run_next_iamthif_cmd(struct mei_device *dev) dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; - dev->iamthif_canceled = 0; - dev->iamthif_ioctl = 1; + dev->iamthif_canceled = false; + dev->iamthif_ioctl = true; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; diff --git a/drivers/staging/mei/main.c b/drivers/staging/mei/main.c index bfd1b46..44ed7a8 100644 --- a/drivers/staging/mei/main.c +++ b/drivers/staging/mei/main.c @@ -14,8 +14,6 @@ * */ - - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -30,7 +28,6 @@ #include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> -#include <linux/version.h> #include <linux/sched.h> #include <linux/uuid.h> #include <linux/compat.h> @@ -61,7 +58,7 @@ static struct cdev mei_cdev; static int mei_major; /* The device pointer */ /* Currently this driver works as long as there is only a single AMT device. */ -static struct pci_dev *mei_device; +struct pci_dev *mei_device; static struct class *mei_class; @@ -109,6 +106,27 @@ MODULE_DEVICE_TABLE(pci, mei_pci_tbl); static DEFINE_MUTEX(mei_mutex); /** + * mei_quirk_probe - probe for devices that doesn't valid ME interface + * @pdev: PCI device structure + * @ent: entry into pci_device_table + * + * returns true if ME Interface is valid, false otherwise + */ +static bool __devinit mei_quirk_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + u32 reg; + if (ent->device == MEI_DEV_ID_PBG_1) { + pci_read_config_dword(pdev, 0x48, ®); + /* make sure that bit 9 is up and bit 10 is down */ + if ((reg & 0x600) == 0x200) { + dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); + return false; + } + } + return true; +} +/** * mei_probe - Device Initialization Routine * * @pdev: PCI device structure @@ -123,6 +141,12 @@ static int __devinit mei_probe(struct pci_dev *pdev, int err; mutex_lock(&mei_mutex); + + if (!mei_quirk_probe(pdev, ent)) { + err = -ENODEV; + goto end; + } + if (mei_device) { err = -EEXIST; goto end; @@ -142,7 +166,7 @@ static int __devinit mei_probe(struct pci_dev *pdev, goto disable_device; } /* allocates and initializes the mei dev structure */ - dev = init_mei_device(pdev); + dev = mei_device_init(pdev); if (!dev) { err = -ENOMEM; goto release_regions; @@ -154,17 +178,26 @@ static int __devinit mei_probe(struct pci_dev *pdev, err = -ENOMEM; goto free_device; } - /* request and enable interrupt */ - err = request_threaded_irq(pdev->irq, + pci_enable_msi(pdev); + + /* request and enable interrupt */ + if (pci_dev_msi_enabled(pdev)) + err = request_threaded_irq(pdev->irq, + NULL, + mei_interrupt_thread_handler, + 0, mei_driver_name, dev); + else + err = request_threaded_irq(pdev->irq, mei_interrupt_quick_handler, mei_interrupt_thread_handler, IRQF_SHARED, mei_driver_name, dev); + if (err) { printk(KERN_ERR "mei: request_threaded_irq failure. irq = %d\n", pdev->irq); goto unmap_memory; } - INIT_DELAYED_WORK(&dev->wd_work, mei_wd_timer); + INIT_DELAYED_WORK(&dev->timer_work, mei_timer); if (mei_hw_init(dev)) { printk(KERN_ERR "mei: Init hw failure.\n"); err = -ENODEV; @@ -172,7 +205,7 @@ static int __devinit mei_probe(struct pci_dev *pdev, } mei_device = pdev; pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->wd_work, HZ); + schedule_delayed_work(&dev->timer_work, HZ); mutex_unlock(&mei_mutex); @@ -186,6 +219,7 @@ release_irq: mei_disable_interrupts(dev); flush_scheduled_work(); free_irq(pdev->irq, dev); + pci_disable_msi(pdev); unmap_memory: pci_iounmap(pdev, dev->mem_addr); free_device: @@ -234,13 +268,17 @@ static void __devexit mei_remove(struct pci_dev *pdev) mei_disconnect_host_client(dev, &dev->wd_cl); } + /* Unregistering watchdog device */ + if (dev->wd_interface_reg) + watchdog_unregister_device(&amt_wd_dev); + /* remove entry if already in list */ dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); mei_remove_client_from_file_list(dev, dev->wd_cl.host_client_id); mei_remove_client_from_file_list(dev, dev->iamthif_cl.host_client_id); dev->iamthif_current_cb = NULL; - dev->num_mei_me_clients = 0; + dev->me_clients_num = 0; mutex_unlock(&dev->device_lock); @@ -250,6 +288,7 @@ static void __devexit mei_remove(struct pci_dev *pdev) mei_disable_interrupts(dev); free_irq(pdev->irq, dev); + pci_disable_msi(pdev); pci_set_drvdata(pdev, NULL); if (dev->mem_addr) @@ -362,7 +401,6 @@ static struct mei_cl_cb *find_read_list_entry( { struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb_next = NULL; - struct mei_cl *cl_list_temp; if (!dev->read_list.status && !list_empty(&dev->read_list.mei_cb.cb_list)) { @@ -370,14 +408,11 @@ static struct mei_cl_cb *find_read_list_entry( dev_dbg(&dev->pdev->dev, "remove read_list CB\n"); list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.mei_cb.cb_list, cb_list) { + struct mei_cl *cl_temp; + cl_temp = (struct mei_cl *)cb_pos->file_private; - cl_list_temp = (struct mei_cl *) - cb_pos->file_private; - - if (cl_list_temp && - mei_fe_same_id(cl, cl_list_temp)) + if (mei_cl_cmp_id(cl, cl_temp)) return cb_pos; - } } return NULL; @@ -407,9 +442,9 @@ static int mei_open(struct inode *inode, struct file *file) mutex_lock(&dev->device_lock); err = -ENOMEM; - cl = mei_alloc_file_private(dev); + cl = mei_cl_allocate(dev); if (!cl) - goto out; + goto out_unlock; err = -ENODEV; if (dev->mei_state != MEI_ENABLED) { @@ -478,7 +513,7 @@ static int mei_release(struct inode *inode, struct file *file) cl->me_client_id); rets = mei_disconnect_host_client(dev, cl); } - mei_flush_queues(dev, cl); + mei_cl_flush_queues(cl); dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", cl->host_client_id, cl->me_client_id); @@ -519,10 +554,10 @@ static int mei_release(struct inode *inode, struct file *file) dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n", dev->iamthif_state); - dev->iamthif_canceled = 1; + dev->iamthif_canceled = true; if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n"); - run_next_iamthif_cmd(dev); + mei_run_next_iamthif_cmd(dev); } } @@ -800,7 +835,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENODEV; goto unlock_dev; } - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { if (dev->me_clients[i].client_id == dev->iamthif_cl.me_client_id) break; @@ -810,7 +845,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENODEV; goto unlock_dev; } - if (i == dev->num_mei_me_clients || + if (i == dev->me_clients_num || (dev->me_clients[i].client_id != dev->iamthif_cl.me_client_id)) { rets = -ENODEV; @@ -868,7 +903,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, cl->me_client_id); goto unlock_dev; } - for (i = 0; i < dev->num_mei_me_clients; i++) { + for (i = 0; i < dev->me_clients_num; i++) { if (dev->me_clients[i].client_id == cl->me_client_id) break; @@ -877,7 +912,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENODEV; goto unlock_dev; } - if (i == dev->num_mei_me_clients) { + if (i == dev->me_clients_num) { rets = -ENODEV; goto unlock_dev; } @@ -893,7 +928,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets && dev->mei_host_buffer_is_empty) { rets = 0; - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (length > ((((dev->host_hw_state & H_CBD) >> 24) * sizeof(u32)) - sizeof(struct mei_msg_hdr))) { @@ -1066,7 +1101,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) dev->iamthif_file_object == file) { mask |= (POLLIN | POLLRDNORM); dev_dbg(&dev->pdev->dev, "run next amthi cb\n"); - run_next_iamthif_cmd(dev); + mei_run_next_iamthif_cmd(dev); } goto out; } @@ -1103,7 +1138,7 @@ static int mei_pci_suspend(struct device *device) mutex_unlock(&dev->device_lock); free_irq(pdev->irq, dev); - + pci_disable_msi(pdev); return err; } @@ -1118,11 +1153,20 @@ static int mei_pci_resume(struct device *device) if (!dev) return -ENODEV; - /* request and enable interrupt */ - err = request_threaded_irq(pdev->irq, + pci_enable_msi(pdev); + + /* request and enable interrupt */ + if (pci_dev_msi_enabled(pdev)) + err = request_threaded_irq(pdev->irq, + NULL, + mei_interrupt_thread_handler, + 0, mei_driver_name, dev); + else + err = request_threaded_irq(pdev->irq, mei_interrupt_quick_handler, mei_interrupt_thread_handler, IRQF_SHARED, mei_driver_name, dev); + if (err) { printk(KERN_ERR "mei: Request_irq failure. irq = %d\n", pdev->irq); @@ -1134,12 +1178,9 @@ static int mei_pci_resume(struct device *device) mei_reset(dev, 1); mutex_unlock(&dev->device_lock); - /* Start watchdog if stopped in suspend */ - if (dev->wd_timeout) { - mei_wd_start_setup(dev); - dev->wd_due_counter = 1; - schedule_delayed_work(&dev->wd_work, HZ); - } + /* Start timer if stopped in suspend */ + schedule_delayed_work(&dev->timer_work, HZ); + return err; } static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); @@ -1333,9 +1374,9 @@ module_init(mei_init_module); */ static void __exit mei_exit_module(void) { - pci_unregister_driver(&mei_driver); mei_sysfs_device_remove(); mei_unregister_cdev(); + pci_unregister_driver(&mei_driver); pr_debug("mei: Driver unloaded successfully.\n"); } diff --git a/drivers/staging/mei/mei_dev.h b/drivers/staging/mei/mei_dev.h index 6f3ec06..264bf23 100644 --- a/drivers/staging/mei/mei_dev.h +++ b/drivers/staging/mei/mei_dev.h @@ -18,6 +18,7 @@ #define _MEI_DEV_H_ #include <linux/types.h> +#include <linux/watchdog.h> #include "mei.h" #include "hw.h" @@ -37,6 +38,17 @@ #define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) /* + * MEI PCI Device object + */ +extern struct pci_dev *mei_device; + +/* + * AMT Watchdog Device + */ +#define INTEL_AMT_WATCHDOG_ID "INTCAMT" +extern struct watchdog_device amt_wd_dev; + +/* * AMTHI Client UUID */ extern const uuid_le mei_amthi_guid; @@ -52,6 +64,11 @@ extern const uuid_le mei_wd_guid; extern const u8 mei_wd_state_independence_msg[3][4]; /* + * maximum number of consecutive resets + */ +#define MEI_MAX_CONSEC_RESET 3 + +/* * Number of File descriptors/handles * that can be opened to the driver. * @@ -62,11 +79,6 @@ extern const u8 mei_wd_state_independence_msg[3][4]; #define MEI_MAX_OPEN_HANDLE_COUNT 253 /* - * Number of queue lists used by this driver - */ -#define MEI_IO_LISTS_NUMBER 7 - -/* * Number of Maximum MEI Clients */ #define MEI_CLIENTS_MAX 255 @@ -169,17 +181,19 @@ struct mei_cl { struct mei_io_list { struct mei_cl_cb mei_cb; int status; - struct mei_device *device_extension; }; -/* MEI private device struct */ +/** + * mei_device - MEI private device struct + * + * @reset_count - limits the number of consecutive resets + */ struct mei_device { struct pci_dev *pdev; /* pointer to pci device struct */ /* * lists of queues */ /* array of pointers to aio lists */ - struct mei_io_list *io_list_array[MEI_IO_LISTS_NUMBER]; struct mei_io_list read_list; /* driver read queue */ struct mei_io_list write_list; /* driver write queue */ struct mei_io_list write_waiting_list; /* write waiting queue */ @@ -193,6 +207,7 @@ struct mei_device { * list of files */ struct list_head file_list; + long open_handle_count; /* * memory of device */ @@ -203,8 +218,8 @@ struct mei_device { * lock for the device */ struct mutex device_lock; /* device lock */ - int recvd_msg; - struct delayed_work wd_work; /* watch dog deleye work */ + struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */ + bool recvd_msg; /* * hw states of host and fw(ME) */ @@ -219,10 +234,12 @@ struct mei_device { /* * mei device states */ + unsigned long reset_count; enum mei_states mei_state; enum mei_init_clients_states init_clients_state; u16 init_clients_timer; - int stop; + bool stop; + bool need_reset; u32 extra_write_index; u32 rd_msg_buf[128]; /* used for control messages */ @@ -232,81 +249,107 @@ struct mei_device { struct hbm_version version; - int mei_host_buffer_is_empty; - struct mei_cl wd_cl; struct mei_me_client *me_clients; /* Note: memory has to be allocated */ DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); - u8 num_mei_me_clients; + u8 me_clients_num; u8 me_client_presentation_num; u8 me_client_index; + bool mei_host_buffer_is_empty; - int wd_pending; - int wd_stopped; + struct mei_cl wd_cl; + bool wd_pending; + bool wd_stopped; + bool wd_bypass; /* if false, don't refresh watchdog ME client */ u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */ + u16 wd_due_counter; unsigned char wd_data[MEI_START_WD_DATA_SIZE]; - u16 wd_due_counter; - bool wd_bypass; /* if false, don't refresh watchdog ME client */ struct file *iamthif_file_object; struct mei_cl iamthif_cl; - int iamthif_ioctl; - int iamthif_canceled; + struct mei_cl_cb *iamthif_current_cb; int iamthif_mtu; unsigned long iamthif_timer; u32 iamthif_stall_timer; unsigned char *iamthif_msg_buf; /* Note: memory has to be allocated */ u32 iamthif_msg_buf_size; u32 iamthif_msg_buf_index; - int iamthif_flow_control_pending; enum iamthif_states iamthif_state; - struct mei_cl_cb *iamthif_current_cb; - u8 write_hang; - int need_reset; - long open_handle_count; + bool iamthif_flow_control_pending; + bool iamthif_ioctl; + bool iamthif_canceled; + bool wd_interface_reg; }; /* * mei init function prototypes */ -struct mei_device *init_mei_device(struct pci_dev *pdev); +struct mei_device *mei_device_init(struct pci_dev *pdev); void mei_reset(struct mei_device *dev, int interrupts); int mei_hw_init(struct mei_device *dev); int mei_task_initialize_clients(void *data); int mei_initialize_clients(struct mei_device *dev); -struct mei_cl *mei_alloc_file_private(struct mei_device *dev); int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl); -void mei_initialize_list(struct mei_io_list *list, - struct mei_device *dev); -void mei_flush_list(struct mei_io_list *list, struct mei_cl *cl); -void mei_flush_queues(struct mei_device *dev, struct mei_cl *cl); -void mei_remove_client_from_file_list(struct mei_device *dev, - u8 host_client_id); -void host_init_iamthif(struct mei_device *dev); -void mei_init_file_private(struct mei_cl *priv, struct mei_device *dev); -void allocate_me_clients_storage(struct mei_device *dev); - -void host_start_message(struct mei_device *dev); -void host_enum_clients_message(struct mei_device *dev); -void host_client_properties(struct mei_device *dev); +void mei_remove_client_from_file_list(struct mei_device *dev, u8 host_client_id); +void mei_host_init_iamthif(struct mei_device *dev); +void mei_allocate_me_clients_storage(struct mei_device *dev); + u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv, const uuid_le *cguid, u8 client_id); /* - * interrupt functions prototype + * MEI IO List Functions + */ +void mei_io_list_init(struct mei_io_list *list); +void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl); + +/* + * MEI ME Client Functions + */ + +struct mei_cl *mei_cl_allocate(struct mei_device *dev); +void mei_cl_init(struct mei_cl *cl, struct mei_device *dev); +int mei_cl_flush_queues(struct mei_cl *cl); +/** + * mei_cl_cmp_id - tells if file private data have same id + * + * @fe1: private data of 1. file object + * @fe2: private data of 2. file object + * + * returns true - if ids are the same and not NULL + */ +static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, + const struct mei_cl *cl2) +{ + return cl1 && cl2 && + (cl1->host_client_id == cl2->host_client_id) && + (cl1->me_client_id == cl2->me_client_id); +} + + + +/* + * MEI Host Client Functions + */ +void mei_host_start_message(struct mei_device *dev); +void mei_host_enum_clients_message(struct mei_device *dev); +int mei_host_client_properties(struct mei_device *dev); + +/* + * MEI interrupt functions prototype */ irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id); -irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id); -void mei_wd_timer(struct work_struct *work); +irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id); +void mei_timer(struct work_struct *work); /* - * input output function prototype + * MEI input output function prototype */ int mei_ioctl_connect_client(struct file *file, struct mei_connect_client_data *data); @@ -321,7 +364,7 @@ int amthi_read(struct mei_device *dev, struct file *file, struct mei_cl_cb *find_amthi_read_list_entry(struct mei_device *dev, struct file *file); -void run_next_iamthif_cmd(struct mei_device *dev); +void mei_run_next_iamthif_cmd(struct mei_device *dev); void mei_free_cb_private(struct mei_cl_cb *priv_cb); @@ -337,10 +380,9 @@ int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid); * @dev: the device structure * @offset: offset from which to read the data * - * returns the byte read. + * returns register value (u32) */ -static inline u32 mei_reg_read(struct mei_device *dev, - unsigned long offset) +static inline u32 mei_reg_read(struct mei_device *dev, unsigned long offset) { return ioread32(dev->mem_addr + offset); } @@ -350,7 +392,7 @@ static inline u32 mei_reg_read(struct mei_device *dev, * * @dev: the device structure * @offset: offset from which to write the data - * @value: the byte to write + * @value: register value to write (u32) */ static inline void mei_reg_write(struct mei_device *dev, unsigned long offset, u32 value) @@ -404,19 +446,4 @@ void mei_csr_clear_his(struct mei_device *dev); void mei_enable_interrupts(struct mei_device *dev); void mei_disable_interrupts(struct mei_device *dev); -/** - * mei_fe_same_id - tells if file private data have same id - * - * @fe1: private data of 1. file object - * @fe2: private data of 2. file object - * - * returns !=0 - if ids are the same, 0 - if differ. - */ -static inline int mei_fe_same_id(const struct mei_cl *fe1, - const struct mei_cl *fe2) -{ - return ((fe1->host_client_id == fe2->host_client_id) && - (fe1->me_client_id == fe2->me_client_id)); -} - #endif diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c index fff53d0..ffca7ca 100644 --- a/drivers/staging/mei/wd.c +++ b/drivers/staging/mei/wd.c @@ -19,22 +19,13 @@ #include <linux/device.h> #include <linux/pci.h> #include <linux/sched.h> +#include <linux/watchdog.h> #include "mei_dev.h" #include "hw.h" #include "interface.h" #include "mei.h" -/* - * MEI Watchdog Module Parameters - */ -static u16 watchdog_timeout = AMT_WD_VALUE; -module_param(watchdog_timeout, ushort, 0); -MODULE_PARM_DESC(watchdog_timeout, - "Intel(R) AMT Watchdog timeout value in seconds. (default=" - __MODULE_STRING(AMT_WD_VALUE) - ", disable=0)"); - static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; @@ -50,12 +41,12 @@ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, 0x32, 0xAB); -void mei_wd_start_setup(struct mei_device *dev) +void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) { - dev_dbg(&dev->pdev->dev, "dev->wd_timeout=%d.\n", dev->wd_timeout); + dev_dbg(&dev->pdev->dev, "timeout=%d.\n", timeout); memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE); memcpy(dev->wd_data + MEI_WD_PARAMS_SIZE, - &dev->wd_timeout, sizeof(u16)); + &timeout, sizeof(u16)); } /** @@ -63,39 +54,39 @@ void mei_wd_start_setup(struct mei_device *dev) * * @dev: the device structure */ -void mei_wd_host_init(struct mei_device *dev) +bool mei_wd_host_init(struct mei_device *dev) { - mei_init_file_private(&dev->wd_cl, dev); + bool ret = false; + + mei_cl_init(&dev->wd_cl, dev); /* look for WD client and connect to it */ dev->wd_cl.state = MEI_FILE_DISCONNECTED; - dev->wd_timeout = watchdog_timeout; - - if (dev->wd_timeout > 0) { - mei_wd_start_setup(dev); - /* find ME WD client */ - mei_find_me_client_update_filext(dev, &dev->wd_cl, - &mei_wd_guid, MEI_WD_HOST_CLIENT_ID); - - dev_dbg(&dev->pdev->dev, "check wd_cl\n"); - if (MEI_FILE_CONNECTING == dev->wd_cl.state) { - if (!mei_connect(dev, &dev->wd_cl)) { - dev_dbg(&dev->pdev->dev, "Failed to connect to WD client\n"); - dev->wd_cl.state = MEI_FILE_DISCONNECTED; - dev->wd_cl.host_client_id = 0; - host_init_iamthif(dev) ; - } else { - dev->wd_cl.timer_count = CONNECT_TIMEOUT; - } + dev->wd_timeout = AMT_WD_DEFAULT_TIMEOUT; + + /* find ME WD client */ + mei_find_me_client_update_filext(dev, &dev->wd_cl, + &mei_wd_guid, MEI_WD_HOST_CLIENT_ID); + + dev_dbg(&dev->pdev->dev, "check wd_cl\n"); + if (MEI_FILE_CONNECTING == dev->wd_cl.state) { + if (!mei_connect(dev, &dev->wd_cl)) { + dev_dbg(&dev->pdev->dev, "Failed to connect to WD client\n"); + dev->wd_cl.state = MEI_FILE_DISCONNECTED; + dev->wd_cl.host_client_id = 0; + ret = false; + goto end; } else { - dev_dbg(&dev->pdev->dev, "Failed to find WD client\n"); - host_init_iamthif(dev) ; + dev->wd_cl.timer_count = CONNECT_TIMEOUT; } } else { - dev->wd_bypass = true; - dev_dbg(&dev->pdev->dev, "WD requested to be disabled\n"); - host_init_iamthif(dev) ; + dev_dbg(&dev->pdev->dev, "Failed to find WD client\n"); + ret = false; + goto end; } + +end: + return ret; } /** @@ -129,19 +120,29 @@ int mei_wd_send(struct mei_device *dev) return -EIO; } +/** + * mei_wd_stop - sends watchdog stop message to fw. + * + * @dev: the device structure + * @preserve: indicate if to keep the timeout value + * + * returns 0 if success, + * -EIO when message send fails + * -EINVAL when invalid message is to be sent + */ int mei_wd_stop(struct mei_device *dev, bool preserve) { int ret; u16 wd_timeout = dev->wd_timeout; - cancel_delayed_work(&dev->wd_work); + cancel_delayed_work(&dev->timer_work); if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout) return 0; dev->wd_timeout = 0; dev->wd_due_counter = 0; memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE); - dev->stop = 1; + dev->stop = true; ret = mei_flow_ctrl_creds(dev, &dev->wd_cl); if (ret < 0) @@ -149,7 +150,7 @@ int mei_wd_stop(struct mei_device *dev, bool preserve) if (ret && dev->mei_host_buffer_is_empty) { ret = 0; - dev->mei_host_buffer_is_empty = 0; + dev->mei_host_buffer_is_empty = false; if (!mei_wd_send(dev)) { ret = mei_flow_ctrl_reduce(dev, &dev->wd_cl); @@ -159,11 +160,11 @@ int mei_wd_stop(struct mei_device *dev, bool preserve) dev_dbg(&dev->pdev->dev, "send stop WD failed\n"); } - dev->wd_pending = 0; + dev->wd_pending = false; } else { - dev->wd_pending = 1; + dev->wd_pending = true; } - dev->wd_stopped = 0; + dev->wd_stopped = false; mutex_unlock(&dev->device_lock); ret = wait_event_interruptible_timeout(dev->wait_stop_wd, @@ -186,3 +187,168 @@ out: return ret; } +/* + * mei_wd_ops_start - wd start command from the watchdog core. + * + * @wd_dev - watchdog device struct + * + * returns 0 if success, negative errno code for failure + */ +static int mei_wd_ops_start(struct watchdog_device *wd_dev) +{ + int err = -ENODEV; + struct mei_device *dev; + + dev = pci_get_drvdata(mei_device); + if (!dev) + return -ENODEV; + + mutex_lock(&dev->device_lock); + + if (dev->mei_state != MEI_ENABLED) { + dev_dbg(&dev->pdev->dev, "mei_state != MEI_ENABLED mei_state= %d\n", + dev->mei_state); + goto end_unlock; + } + + if (dev->wd_cl.state != MEI_FILE_CONNECTED) { + dev_dbg(&dev->pdev->dev, "MEI Driver is not connected to Watchdog Client\n"); + goto end_unlock; + } + + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + err = 0; +end_unlock: + mutex_unlock(&dev->device_lock); + return err; +} + +/* + * mei_wd_ops_stop - wd stop command from the watchdog core. + * + * @wd_dev - watchdog device struct + * + * returns 0 if success, negative errno code for failure + */ +static int mei_wd_ops_stop(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + dev = pci_get_drvdata(mei_device); + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->device_lock); + mei_wd_stop(dev, false); + mutex_unlock(&dev->device_lock); + + return 0; +} + +/* + * mei_wd_ops_ping - wd ping command from the watchdog core. + * + * @wd_dev - watchdog device struct + * + * returns 0 if success, negative errno code for failure + */ +static int mei_wd_ops_ping(struct watchdog_device *wd_dev) +{ + int ret = 0; + struct mei_device *dev; + dev = pci_get_drvdata(mei_device); + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->device_lock); + + if (dev->wd_cl.state != MEI_FILE_CONNECTED) { + dev_dbg(&dev->pdev->dev, "wd is not connected.\n"); + ret = -ENODEV; + goto end; + } + + /* Check if we can send the ping to HW*/ + if (dev->mei_host_buffer_is_empty && + mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { + + dev->mei_host_buffer_is_empty = false; + dev_dbg(&dev->pdev->dev, "sending watchdog ping\n"); + + if (mei_wd_send(dev)) { + dev_dbg(&dev->pdev->dev, "wd send failed.\n"); + ret = -EIO; + goto end; + } + + if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) { + dev_dbg(&dev->pdev->dev, "mei_flow_ctrl_reduce() failed.\n"); + ret = -EIO; + goto end; + } + + } else { + dev->wd_pending = true; + } + +end: + mutex_unlock(&dev->device_lock); + return ret; +} + +/* + * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. + * + * @wd_dev - watchdog device struct + * @timeout - timeout value to set + * + * returns 0 if success, negative errno code for failure + */ +static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout) +{ + struct mei_device *dev; + dev = pci_get_drvdata(mei_device); + + if (!dev) + return -ENODEV; + + /* Check Timeout value */ + if (timeout < AMT_WD_MIN_TIMEOUT || timeout > AMT_WD_MAX_TIMEOUT) + return -EINVAL; + + mutex_lock(&dev->device_lock); + + dev->wd_timeout = timeout; + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + mutex_unlock(&dev->device_lock); + + return 0; +} + +/* + * Watchdog Device structs + */ +const struct watchdog_ops wd_ops = { + .owner = THIS_MODULE, + .start = mei_wd_ops_start, + .stop = mei_wd_ops_stop, + .ping = mei_wd_ops_ping, + .set_timeout = mei_wd_ops_set_timeout, +}; +const struct watchdog_info wd_info = { + .identity = INTEL_AMT_WATCHDOG_ID, + .options = WDIOF_KEEPALIVEPING, +}; + +struct watchdog_device amt_wd_dev = { + .info = &wd_info, + .ops = &wd_ops, + .timeout = AMT_WD_DEFAULT_TIMEOUT, + .min_timeout = AMT_WD_MIN_TIMEOUT, + .max_timeout = AMT_WD_MAX_TIMEOUT, +}; + + |