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 | 25 | ||||
-rw-r--r-- | drivers/staging/mei/interface.c | 4 | ||||
-rw-r--r-- | drivers/staging/mei/interface.h | 8 | ||||
-rw-r--r-- | drivers/staging/mei/interrupt.c | 85 | ||||
-rw-r--r-- | drivers/staging/mei/main.c | 51 | ||||
-rw-r--r-- | drivers/staging/mei/mei_dev.h | 24 | ||||
-rw-r--r-- | drivers/staging/mei/wd.c | 242 |
9 files changed, 330 insertions, 121 deletions
diff --git a/drivers/staging/mei/Kconfig b/drivers/staging/mei/Kconfig index 3f3f170890ee..47d78a72db2e 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 3b6a667a5808..7d9a13b0f2dd 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 0fa8216fd0eb..8bf34794489c 100644 --- a/drivers/staging/mei/init.c +++ b/drivers/staging/mei/init.c @@ -133,6 +133,7 @@ struct mei_device *mei_device_init(struct pci_dev *pdev) init_waitqueue_head(&dev->wait_stop_wd); dev->mei_state = MEI_INITIALIZING; dev->iamthif_state = MEI_IAMTHIF_IDLE; + dev->wd_interface_reg = false; mei_io_list_init(&dev->read_list); @@ -469,9 +470,12 @@ void mei_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 mei_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; @@ -503,26 +507,15 @@ void mei_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->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; } /** diff --git a/drivers/staging/mei/interface.c b/drivers/staging/mei/interface.c index cfec92dfc1c4..a65dacf00213 100644 --- a/drivers/staging/mei/interface.c +++ b/drivers/staging/mei/interface.c @@ -332,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; @@ -396,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 d0bf5cf4f3ec..7bd38ae2c233 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 9cb186bf1874..882d106d54e5 100644 --- a/drivers/staging/mei/interrupt.c +++ b/drivers/staging/mei/interrupt.c @@ -396,6 +396,18 @@ 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"); + + /* 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; } @@ -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; @@ -734,7 +747,38 @@ 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++; - mei_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; + + /* 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); @@ -1381,7 +1425,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; @@ -1391,7 +1435,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); @@ -1418,33 +1462,6 @@ 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 = false; - 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 = true; - - } - } if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { dev_dbg(&dev->pdev->dev, "reseting because of hang to amthi.\n"); @@ -1510,7 +1527,7 @@ void mei_wd_timer(struct work_struct *work) } } out: - schedule_delayed_work(&dev->wd_work, 2 * HZ); + schedule_delayed_work(&dev->timer_work, 2 * HZ); mutex_unlock(&dev->device_lock); } @@ -1540,6 +1557,12 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) mutex_lock(&dev->device_lock); 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 */ diff --git a/drivers/staging/mei/main.c b/drivers/staging/mei/main.c index de8825fcd8ce..eb05c36f45d4 100644 --- a/drivers/staging/mei/main.c +++ b/drivers/staging/mei/main.c @@ -58,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; @@ -151,17 +151,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; @@ -169,7 +178,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); @@ -183,6 +192,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: @@ -231,6 +241,10 @@ 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); @@ -247,6 +261,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) @@ -402,7 +417,7 @@ static int mei_open(struct inode *inode, struct file *file) err = -ENOMEM; cl = mei_cl_allocate(dev); if (!cl) - goto out; + goto out_unlock; err = -ENODEV; if (dev->mei_state != MEI_ENABLED) { @@ -1096,7 +1111,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; } @@ -1111,11 +1126,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); @@ -1127,12 +1151,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); diff --git a/drivers/staging/mei/mei_dev.h b/drivers/staging/mei/mei_dev.h index d7bc10c612be..af4b1af9eeac 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; @@ -197,7 +209,7 @@ struct mei_device { * lock for the device */ struct mutex device_lock; /* device lock */ - 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) @@ -258,6 +270,8 @@ struct mei_device { bool iamthif_flow_control_pending; bool iamthif_ioctl; bool iamthif_canceled; + + bool wd_interface_reg; }; @@ -315,14 +329,14 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, */ void mei_host_start_message(struct mei_device *dev); void mei_host_enum_clients_message(struct mei_device *dev); -void mei_host_client_properties(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); +void mei_timer(struct work_struct *work); /* * MEI input output function prototype @@ -356,7 +370,7 @@ 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) { @@ -368,7 +382,7 @@ static inline u32 mei_reg_read(struct mei_device *dev, unsigned long offset) * * @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) diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c index 42f04efc90e4..ffca7ca32658 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) { + 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; - mei_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"); - mei_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"); - mei_host_init_iamthif(dev) ; + dev_dbg(&dev->pdev->dev, "Failed to find WD client\n"); + ret = false; + goto end; } + +end: + return ret; } /** @@ -129,12 +120,22 @@ 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; @@ -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, +}; + + |