From ca455fafc2ef6a26089ba082b89e5d6e89cb2f8f Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:35 -0400 Subject: mei: amthif: drop READ_COMPLETE state AMTHIF code now uses read completed queue to store replies from the FW. It is possible to send the next request as soon as a read from the FW is completed. With these changes we don't need the READ_COMPLETE state. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 34 +++++++++++----------------------- drivers/misc/mei/main.c | 2 +- drivers/misc/mei/mei_dev.h | 5 +---- 3 files changed, 13 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index a039a5df6f21..13d89043ad76 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -299,8 +299,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) /* * The previous request is still in processing, queue this one. */ - if (dev->iamthif_state > MEI_IAMTHIF_IDLE && - dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE) + if (dev->iamthif_state != MEI_IAMTHIF_IDLE) return 0; return mei_amthif_run_next_cmd(dev); @@ -309,7 +308,6 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) /** * mei_amthif_poll - the amthif poll function * - * @dev: the device structure * @file: pointer to file structure * @wait: pointer to poll_table structure * @@ -317,26 +315,19 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) * * Locking: called under "dev->device_lock" lock */ - -unsigned int mei_amthif_poll(struct mei_device *dev, - struct file *file, poll_table *wait) +unsigned int mei_amthif_poll(struct file *file, poll_table *wait) { + struct mei_cl *cl = file->private_data; + struct mei_cl_cb *cb = mei_cl_read_cb(cl, file); unsigned int mask = 0; - poll_wait(file, &dev->iamthif_cl.rx_wait, wait); - - if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_fp == file) { - + poll_wait(file, &cl->rx_wait, wait); + if (cb) mask |= POLLIN | POLLRDNORM; - mei_amthif_run_next_cmd(dev); - } return mask; } - - /** * mei_amthif_irq_write - write iamthif command in irq thread context. * @@ -393,7 +384,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, return 0; dev_dbg(dev->dev, "completed amthif read.\n "); - dev->iamthif_current_cb = NULL; dev->iamthif_stall_timer = 0; return 0; @@ -425,14 +415,16 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } if (!dev->iamthif_canceled) { - dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; - dev->iamthif_stall_timer = 0; list_add_tail(&cb->list, &cl->rd_completed); dev_dbg(dev->dev, "amthif read completed\n"); } else { - mei_amthif_run_next_cmd(dev); + mei_io_cb_free(cb); } + dev->iamthif_current_cb = NULL; + dev->iamthif_stall_timer = 0; + mei_amthif_run_next_cmd(dev); + dev_dbg(dev->dev, "completing amthif call back.\n"); wake_up_interruptible(&cl->rx_wait); } @@ -539,10 +531,6 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", dev->iamthif_state); dev->iamthif_canceled = true; - if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { - dev_dbg(dev->dev, "run next amthif iamthif cb\n"); - mei_amthif_run_next_cmd(dev); - } } if (mei_clear_lists(dev, file)) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 52635b063873..622ff19864df 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -610,7 +610,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) } if (cl == &dev->iamthif_cl) { - mask = mei_amthif_poll(dev, file, wait); + mask = mei_amthif_poll(file, wait); goto out; } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index e5e32503d4bc..fa6007707048 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -80,9 +80,7 @@ const char *mei_dev_state_str(int state); enum iamthif_states { MEI_IAMTHIF_IDLE, MEI_IAMTHIF_WRITING, - MEI_IAMTHIF_FLOW_CONTROL, MEI_IAMTHIF_READING, - MEI_IAMTHIF_READ_COMPLETE }; enum mei_file_transaction_states { @@ -572,8 +570,7 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl); int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset); -unsigned int mei_amthif_poll(struct mei_device *dev, - struct file *file, poll_table *wait); +unsigned int mei_amthif_poll(struct file *file, poll_table *wait); int mei_amthif_release(struct mei_device *dev, struct file *file); -- cgit v1.2.3-58-ga151 From 32a1dc1d02eb41196903278f40d6a4aef66ce535 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:36 -0400 Subject: mei: amthif: fix request cancel Currently, all requests cancelled by the user are immediately removed from the queues. Such removal can cause unexpected behavior in the case when a request is partially written or a reply is received after the request is cancelled. To resolve this a request is always fully processed and the result is discarded in case the request was canceled. This completes the partial fix in commit: 9d04ee1 ("mei: amthif: discard not read messages") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 111 +++++++++++++++---------------------------- drivers/misc/mei/interrupt.c | 2 - 2 files changed, 38 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 13d89043ad76..0cded8aa72c0 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -399,34 +399,44 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) { struct mei_device *dev = cl->dev; - if (cb->fop_type == MEI_FOP_WRITE) { + dev_dbg(dev->dev, "completing amthif call back.\n"); + switch (cb->fop_type) { + case MEI_FOP_WRITE: if (!cb->status) { dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; mei_io_cb_free(cb); return; } - /* - * in case of error enqueue the write cb to complete read list - * so it can be propagated to the reader - */ - list_add_tail(&cb->list, &cl->rd_completed); - wake_up_interruptible(&cl->rx_wait); - return; - } + dev->iamthif_current_cb = NULL; + dev->iamthif_state = MEI_IAMTHIF_IDLE; + dev->iamthif_fp = NULL; + if (!dev->iamthif_canceled) { + /* + * in case of error enqueue the write cb to complete + * read list so it can be propagated to the reader + */ + list_add_tail(&cb->list, &cl->rd_completed); + wake_up_interruptible(&cl->rx_wait); + } else { + mei_io_cb_free(cb); + } + break; + case MEI_FOP_READ: + if (!dev->iamthif_canceled) { + list_add_tail(&cb->list, &cl->rd_completed); + dev_dbg(dev->dev, "amthif read completed\n"); + wake_up_interruptible(&cl->rx_wait); + } else { + mei_io_cb_free(cb); + } - if (!dev->iamthif_canceled) { - list_add_tail(&cb->list, &cl->rd_completed); - dev_dbg(dev->dev, "amthif read completed\n"); - } else { - mei_io_cb_free(cb); + dev->iamthif_current_cb = NULL; + dev->iamthif_stall_timer = 0; + mei_amthif_run_next_cmd(dev); + break; + default: + WARN_ON(1); } - - dev->iamthif_current_cb = NULL; - dev->iamthif_stall_timer = 0; - mei_amthif_run_next_cmd(dev); - - dev_dbg(dev->dev, "completing amthif call back.\n"); - wake_up_interruptible(&cl->rx_wait); } /** @@ -439,32 +449,15 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) * * mei_clear_list is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed - * - * Return: true if callback removed from the list, false otherwise */ -static bool mei_clear_list(struct mei_device *dev, +static void mei_clear_list(struct mei_device *dev, const struct file *file, struct list_head *mei_cb_list) { - struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl_cb *cb, *next; - bool removed = false; - - /* list all list member */ - list_for_each_entry_safe(cb, next, mei_cb_list, list) { - /* check if list member associated with a file */ - if (file == cb->fp) { - /* check if cb equal to current iamthif cb */ - if (dev->iamthif_current_cb == cb) { - dev->iamthif_current_cb = NULL; - /* send flow control to iamthif client */ - mei_hbm_cl_flow_control_req(dev, cl); - } - /* free all allocated buffers */ + + list_for_each_entry_safe(cb, next, mei_cb_list, list) + if (file == cb->fp) mei_io_cb_free(cb); - removed = true; - } - } - return removed; } /** @@ -475,41 +468,14 @@ static bool mei_clear_list(struct mei_device *dev, * * mei_clear_lists is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed - * - * Return: true if callback removed from the list, false otherwise */ -static bool mei_clear_lists(struct mei_device *dev, const struct file *file) +static void mei_clear_lists(struct mei_device *dev, const struct file *file) { - bool removed = false; struct mei_cl *cl = &dev->iamthif_cl; - /* remove callbacks associated with a file */ mei_clear_list(dev, file, &dev->amthif_cmd_list.list); - if (mei_clear_list(dev, file, &cl->rd_completed)) - removed = true; - + mei_clear_list(dev, file, &cl->rd_completed); mei_clear_list(dev, file, &dev->ctrl_rd_list.list); - - if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list)) - removed = true; - - if (mei_clear_list(dev, file, &dev->write_waiting_list.list)) - removed = true; - - if (mei_clear_list(dev, file, &dev->write_list.list)) - removed = true; - - /* check if iamthif_current_cb not NULL */ - if (dev->iamthif_current_cb && !removed) { - /* check file and iamthif current cb association */ - if (dev->iamthif_current_cb->fp == file) { - /* remove cb */ - mei_io_cb_free(dev->iamthif_current_cb); - dev->iamthif_current_cb = NULL; - removed = true; - } - } - return removed; } /** @@ -533,8 +499,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) dev->iamthif_canceled = true; } - if (mei_clear_lists(dev, file)) - dev->iamthif_state = MEI_IAMTHIF_IDLE; + mei_clear_lists(dev, file); return 0; } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3831a7ba2531..38db1c3d4f14 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -514,8 +514,6 @@ void mei_timer(struct work_struct *work) if (--dev->iamthif_stall_timer == 0) { dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); - dev->iamthif_canceled = false; - dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; -- cgit v1.2.3-58-ga151 From bca7dbacfd4c7f1f069a70e241e39aea5bd85808 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:37 -0400 Subject: mei: amthif: drop iamthif_current_cb iamthif_current_cb was used in request cancel in amthif code. Now a canceled request is discarded only at the end of the processing and the variable lost its purpose and can be safely removed. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 5 ----- drivers/misc/mei/interrupt.c | 3 --- drivers/misc/mei/mei_dev.h | 2 -- 3 files changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 0cded8aa72c0..b602fd3b914c 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -47,7 +47,6 @@ const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, void mei_amthif_reset_params(struct mei_device *dev) { /* reset iamthif parameters. */ - dev->iamthif_current_cb = NULL; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_stall_timer = 0; @@ -213,7 +212,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) dev->iamthif_state = MEI_IAMTHIF_READING; dev->iamthif_fp = cb->fp; - dev->iamthif_current_cb = cb; return 0; err: @@ -240,7 +238,6 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) dev = cl->dev; dev->iamthif_state = MEI_IAMTHIF_WRITING; - dev->iamthif_current_cb = cb; dev->iamthif_fp = cb->fp; dev->iamthif_canceled = false; @@ -407,7 +404,6 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) mei_io_cb_free(cb); return; } - dev->iamthif_current_cb = NULL; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_fp = NULL; if (!dev->iamthif_canceled) { @@ -430,7 +426,6 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) mei_io_cb_free(cb); } - dev->iamthif_current_cb = NULL; dev->iamthif_stall_timer = 0; mei_amthif_run_next_cmd(dev); break; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 38db1c3d4f14..436ecda3935e 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -515,9 +515,6 @@ void mei_timer(struct work_struct *work) dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); - mei_io_cb_free(dev->iamthif_current_cb); - dev->iamthif_current_cb = NULL; - dev->iamthif_fp = NULL; mei_amthif_run_next_cmd(dev); } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index fa6007707048..d7b68063ace1 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -400,7 +400,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @amthif_cmd_list : amthif list for cmd waiting * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client - * @iamthif_current_cb : amthif current operation callback * @iamthif_open_count : number of opened amthif connections * @iamthif_stall_timer : timer to detect amthif hang * @iamthif_state : amthif processor state @@ -485,7 +484,6 @@ struct mei_device { /* driver managed amthif list for reading completed amthif cmd data */ const struct file *iamthif_fp; struct mei_cl iamthif_cl; - struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; u32 iamthif_stall_timer; enum iamthif_states iamthif_state; -- cgit v1.2.3-58-ga151 From 9fa0be8bb66a42a5f75e00a54732c92f3772aa6e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:38 -0400 Subject: mei: amthif: enable poll for async events Currently the poll function is bailing early for amthif client and ignores requests for async events notifications. Move async event processing before amthif to enable async events notifications on amthif client. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 622ff19864df..a8d0471f817f 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -609,17 +609,17 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) goto out; } - if (cl == &dev->iamthif_cl) { - mask = mei_amthif_poll(file, wait); - goto out; - } - if (notify_en) { poll_wait(file, &cl->ev_wait, wait); if (cl->notify_ev) mask |= POLLPRI; } + if (cl == &dev->iamthif_cl) { + mask |= mei_amthif_poll(file, wait); + goto out; + } + if (req_events & (POLLIN | POLLRDNORM)) { poll_wait(file, &cl->rx_wait, wait); -- cgit v1.2.3-58-ga151 From 22393381a489e61b87d85184c02cf070a410c80d Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:39 -0400 Subject: mei: amthif: drop mei_amthif_send_cmd Incorporate the mei_amthif_send_cmd code into its only caller: mei_amthif_run_next_cmd Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 52 +++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b602fd3b914c..9885fa40a120 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -219,38 +219,6 @@ err: return rets; } -/** - * mei_amthif_send_cmd - send amthif command to the ME - * - * @cl: the host client - * @cb: mei call back struct - * - * Return: 0 on success, <0 on failure. - */ -static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) -{ - struct mei_device *dev; - int ret; - - if (!cl->dev || !cb) - return -ENODEV; - - dev = cl->dev; - - dev->iamthif_state = MEI_IAMTHIF_WRITING; - dev->iamthif_fp = cb->fp; - dev->iamthif_canceled = false; - - ret = mei_cl_write(cl, cb, false); - if (ret < 0) - return ret; - - if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->fp); - - return 0; -} - /** * mei_amthif_run_next_cmd - send next amt command from queue * @@ -262,20 +230,32 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) { struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl_cb *cb; + int ret; dev->iamthif_canceled = false; - dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_fp = NULL; dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, typeof(*cb), list); - if (!cb) + if (!cb) { + dev->iamthif_state = MEI_IAMTHIF_IDLE; + dev->iamthif_fp = NULL; return 0; + } list_del_init(&cb->list); - return mei_amthif_send_cmd(cl, cb); + dev->iamthif_state = MEI_IAMTHIF_WRITING; + dev->iamthif_fp = cb->fp; + + ret = mei_cl_write(cl, cb, false); + if (ret < 0) + return ret; + + if (cb->completed) + cb->status = mei_amthif_read_start(cl, cb->fp); + + return 0; } /** -- cgit v1.2.3-58-ga151 From 35eda92afa467232ab2a01422b813fa4a14542c5 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:40 -0400 Subject: mei: amthif: use mei_cl_alloc_cb for allocating cbs Use mei_cl_alloc_cb wrapper instead of open code steps Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 9885fa40a120..14b454143c81 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -196,17 +196,10 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; - int rets; - - cb = mei_io_cb_init(cl, MEI_FOP_READ, file); - if (!cb) { - rets = -ENOMEM; - goto err; - } - rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl)); - if (rets) - goto err; + cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, file); + if (!cb) + return -ENOMEM; list_add_tail(&cb->list, &dev->ctrl_wr_list.list); @@ -214,9 +207,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) dev->iamthif_fp = cb->fp; return 0; -err: - mei_io_cb_free(cb); - return rets; } /** -- cgit v1.2.3-58-ga151 From aab3b1a34ae376a80ebb05a217d950df13db5156 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 24 May 2016 16:03:41 -0400 Subject: mei: drop mei_io_cb_alloc_buf mei_io_cb_alloc_buf have a single caller :mei_cl_alloc_cb. After amthif stopped using it, the code can be integrated into the caller and the function can be dropped. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 32 ++++++-------------------------- drivers/misc/mei/client.h | 2 -- 2 files changed, 6 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 641c1a566687..6658917be64f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -419,31 +419,6 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) __mei_io_list_flush(list, cl, true); } -/** - * mei_io_cb_alloc_buf - allocate callback buffer - * - * @cb: io callback structure - * @length: size of the buffer - * - * Return: 0 on success - * -EINVAL if cb is NULL - * -ENOMEM if allocation failed - */ -int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) -{ - if (!cb) - return -EINVAL; - - if (length == 0) - return 0; - - cb->buf.data = kmalloc(length, GFP_KERNEL); - if (!cb->buf.data) - return -ENOMEM; - cb->buf.size = length; - return 0; -} - /** * mei_cl_alloc_cb - a convenient wrapper for allocating read cb * @@ -464,10 +439,15 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, if (!cb) return NULL; - if (mei_io_cb_alloc_buf(cb, length)) { + if (length == 0) + return cb; + + cb->buf.data = kmalloc(length, GFP_KERNEL); + if (!cb->buf.data) { mei_io_cb_free(cb); return NULL; } + cb->buf.size = length; return cb; } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 0d7a3a1fef78..16466aa40cbd 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -85,8 +85,6 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, const struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); -int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length); - /** * mei_io_list_init - Sets up a queue list. -- cgit v1.2.3-58-ga151 From 77537ad2917bbca0a73ee6ebd44fc0ce5416d590 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:52 +0300 Subject: mei: recover after errors in runtime pm flow Schedule link reset if failed to perform runtime suspend or resume. Set active runtime pm stte on link reset to clean runtimr pm error, if present. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 3 +++ drivers/misc/mei/hw-txe.c | 3 +++ drivers/misc/mei/pci-me.c | 6 ++++++ drivers/misc/mei/pci-txe.c | 7 +++++++ 4 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index e2fb44cc5c37..c2d70d2dd112 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -18,6 +18,7 @@ #include #include +#include #include "mei_dev.h" #include "hbm.h" @@ -1063,6 +1064,8 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) } } + pm_runtime_set_active(dev->dev); + hcsr = mei_hcsr_read(dev); /* H_RST may be found lit before reset is started, * for example if preceding reset flow hasn't completed. diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 4a6c1b85f11e..e6e5e55a12ed 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -935,6 +936,8 @@ static int mei_txe_hw_start(struct mei_device *dev) return ret; } + pm_runtime_set_active(dev->dev); + /* enable input ready interrupts: * SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 64e64da6da44..718addfa9fb0 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -400,6 +400,9 @@ static int mei_me_pm_runtime_suspend(struct device *device) dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret); + if (ret && ret != -EAGAIN) + schedule_work(&dev->reset_work); + return ret; } @@ -423,6 +426,9 @@ static int mei_me_pm_runtime_resume(struct device *device) dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret); + if (ret) + schedule_work(&dev->reset_work); + return ret; } diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 30cc30683c07..58ffd30dcc91 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -347,6 +347,10 @@ static int mei_txe_pm_runtime_suspend(struct device *device) dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret); mutex_unlock(&dev->device_lock); + + if (ret && ret != -EAGAIN) + schedule_work(&dev->reset_work); + return ret; } @@ -372,6 +376,9 @@ static int mei_txe_pm_runtime_resume(struct device *device) dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret); + if (ret) + schedule_work(&dev->reset_work); + return ret; } -- cgit v1.2.3-58-ga151 From 041330d9c33e975791f4d543db3f25d9a6c474c9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 16 Jun 2016 17:58:53 +0300 Subject: mei: hbm: add missing argument in the kdoc In mei_hbm_cl_hdr buf argument was not described Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 085f3aafe6fa..b1697a329af1 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -161,6 +161,7 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len) * @dev: the device structure * @cl: client * @hbm_cmd: host bus message command + * @buf: message buffer * @len: buffer length * * Return: 0 on success, <0 on failure. -- cgit v1.2.3-58-ga151 From 1eb5bd4d83f566e5ea8521b6020d0cfcb8f1c3ef Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:54 +0300 Subject: mei: drop read complete queue emptiness check In the course of the read flow we want to wait for read completion only if the read queue is empty. However the calling list_empty(&cl->rd_completed) is a duplication as the same check was performed by mei_cl_read_cb() and the waiting is skipped if it returns not NULL. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 3 ++- drivers/misc/mei/main.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 1f33fea9299f..63411ddfb83d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -126,7 +126,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) goto out; /* wait on event only if there is no other waiter */ - if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { + /* synchronized under device mutex */ + if (!waitqueue_active(&cl->rx_wait)) { mutex_unlock(&bus->device_lock); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index a8d0471f817f..3326bde7fba1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -183,7 +183,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { + /* synchronized under device mutex */ + if (!waitqueue_active(&cl->rx_wait)) { if (file->f_flags & O_NONBLOCK) { rets = -EAGAIN; goto out; -- cgit v1.2.3-58-ga151 From 2d4d5481e2d6f93b25fcfb13a9f20bbfbf54266a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jun 2016 00:25:31 +0300 Subject: mei: fix return value on disconnection Correct errno on client disconnection is -ENODEV not -EBUSY Cc: #4.3+ Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- drivers/misc/mei/bus.c | 2 +- drivers/misc/mei/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 14b454143c81..3cf54ca051ea 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -132,7 +132,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, return -ERESTARTSYS; if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; + rets = -ENODEV; goto out; } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 63411ddfb83d..9c404dc8eada 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -143,7 +143,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; + rets = -ENODEV; goto out; } } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3326bde7fba1..d62e89c80beb 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -203,7 +203,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_lock(&dev->device_lock); if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; + rets = -ENODEV; goto out; } } -- cgit v1.2.3-58-ga151 From a03c608f25fbe2eb5f2862a4dab1791948496215 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:56 +0300 Subject: mei: move read cb to complete queue if not connected Move read cb to the completion queue if a read finds out that client is not connected. This expedite user space reader wake on error condition. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/interrupt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 436ecda3935e..412229e7bb7c 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -113,6 +113,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (!mei_cl_is_connected(cl)) { cl_dbg(dev, cl, "not connected\n"); + list_move_tail(&cb->list, &complete_list->list); cb->status = -ENODEV; goto out; } -- cgit v1.2.3-58-ga151 From 97d549b4d5fca22ebde0798cb8c16c19b4ae837a Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:57 +0300 Subject: mei: add file pointer to the host client structure Store the file associated with a client in the host client structure, this enables dropping the special amthif client file pointer from struct mei_device, and this is also a preparation for changing the way rx packet allocation for fixed_address clients Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 13 +++++++------ drivers/misc/mei/interrupt.c | 1 - drivers/misc/mei/main.c | 1 + drivers/misc/mei/mei_dev.h | 5 ++--- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 3cf54ca051ea..e346af17d9f4 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -204,7 +204,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) list_add_tail(&cb->list, &dev->ctrl_wr_list.list); dev->iamthif_state = MEI_IAMTHIF_READING; - dev->iamthif_fp = cb->fp; + cl->fp = cb->fp; return 0; } @@ -230,13 +230,13 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) typeof(*cb), list); if (!cb) { dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_fp = NULL; + cl->fp = NULL; return 0; } list_del_init(&cb->list); dev->iamthif_state = MEI_IAMTHIF_WRITING; - dev->iamthif_fp = cb->fp; + cl->fp = cb->fp; ret = mei_cl_write(cl, cb, false); if (ret < 0) @@ -375,7 +375,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) return; } dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_fp = NULL; + cl->fp = NULL; if (!dev->iamthif_canceled) { /* * in case of error enqueue the write cb to complete @@ -453,11 +453,12 @@ static void mei_clear_lists(struct mei_device *dev, const struct file *file) */ int mei_amthif_release(struct mei_device *dev, struct file *file) { + struct mei_cl *cl = file->private_data; + if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (dev->iamthif_fp == file && - dev->iamthif_state != MEI_IAMTHIF_IDLE) { + if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", dev->iamthif_state); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 412229e7bb7c..8b5e4b4c4c15 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -516,7 +516,6 @@ void mei_timer(struct work_struct *work) dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); - dev->iamthif_fp = NULL; mei_amthif_run_next_cmd(dev); } } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d62e89c80beb..d7ef5edf044a 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -71,6 +71,7 @@ static int mei_open(struct inode *inode, struct file *file) goto err_unlock; } + cl->fp = file; file->private_data = cl; mutex_unlock(&dev->device_lock); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d7b68063ace1..aaefbc87c2fc 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -200,6 +200,7 @@ struct mei_cl_cb { * @ev_async: event async notification * @status: connection status * @me_cl: fw client connected + * @fp: file associated with client * @host_client_id: host id * @mei_flow_ctrl_creds: transmit flow credentials * @timer_count: watchdog timer for operation completion @@ -223,6 +224,7 @@ struct mei_cl { struct fasync_struct *ev_async; int status; struct mei_me_client *me_cl; + const struct file *fp; u8 host_client_id; u8 mei_flow_ctrl_creds; u8 timer_count; @@ -398,7 +400,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @override_fixed_address: force allow fixed address behavior * * @amthif_cmd_list : amthif list for cmd waiting - * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client * @iamthif_open_count : number of opened amthif connections * @iamthif_stall_timer : timer to detect amthif hang @@ -481,8 +482,6 @@ struct mei_device { /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; - /* driver managed amthif list for reading completed amthif cmd data */ - const struct file *iamthif_fp; struct mei_cl iamthif_cl; long iamthif_open_count; u32 iamthif_stall_timer; -- cgit v1.2.3-58-ga151 From a808c80cdaa83939b220176fcdffca8385d88ba6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:58 +0300 Subject: mei: add read callback on demand for fixed_address clients The Fixed address clients do not work with the flow control, and the packet RX callback was allocated upon TX with anticipation of a following RX. This won't work if the clients with unsolicited Rx. Rather than preparing read callback upon a write we allocate one directly on the reciev path if one doesn't exists. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 15 ++++----------- drivers/misc/mei/interrupt.c | 10 ++++++++-- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 6658917be64f..2a09db86e50e 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1145,26 +1145,19 @@ err: * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. * * @cl: host client - * @fp: the file pointer associated with the pointer * * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. */ -static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp) +static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { - int rets; - if (WARN_ON(!cl || !cl->me_cl)) return -EINVAL; if (cl->mei_flow_ctrl_creds > 0) return 1; - if (mei_cl_is_fixed_address(cl)) { - rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp); - if (rets && rets != -EBUSY) - return rets; + if (mei_cl_is_fixed_address(cl)) return 1; - } if (mei_cl_is_single_recv_buf(cl)) { if (cl->me_cl->mei_flow_ctrl_creds > 0) @@ -1537,7 +1530,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, first_chunk = cb->buf_idx == 0; - rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1; + rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1; if (rets < 0) return rets; @@ -1643,7 +1636,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.msg_complete = 0; mei_hdr.internal = cb->internal; - rets = mei_cl_flow_ctrl_creds(cl, cb->fp); + rets = mei_cl_flow_ctrl_creds(cl); if (rets < 0) goto err; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 8b5e4b4c4c15..44ba90140725 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -107,8 +107,14 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); if (!cb) { - cl_err(dev, cl, "pending read cb not found\n"); - goto out; + if (!mei_cl_is_fixed_address(cl)) { + cl_err(dev, cl, "pending read cb not found\n"); + goto out; + } + cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp); + if (!cb) + goto out; + list_add_tail(&cb->list, &cl->rd_pending); } if (!mei_cl_is_connected(cl)) { -- cgit v1.2.3-58-ga151 From c85dba9e87376aa8e08fa7b024df562d0fda189b Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 16 Jun 2016 17:58:59 +0300 Subject: mei: amthif: drop mei_clear_lists function Open code mei_clear_lists into its only caller mei_amthif_releas and drop unused parameter 'dev' form from mei_clear_list function. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index e346af17d9f4..26568c5beb25 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -408,15 +408,14 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) * mei_clear_list - removes all callbacks associated with file * from mei_cb_list * - * @dev: device structure. * @file: file structure * @mei_cb_list: callbacks list * * mei_clear_list is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed */ -static void mei_clear_list(struct mei_device *dev, - const struct file *file, struct list_head *mei_cb_list) +static void mei_clear_list(const struct file *file, + struct list_head *mei_cb_list) { struct mei_cl_cb *cb, *next; @@ -425,24 +424,6 @@ static void mei_clear_list(struct mei_device *dev, mei_io_cb_free(cb); } -/** - * mei_clear_lists - removes all callbacks associated with file - * - * @dev: device structure - * @file: file structure - * - * mei_clear_lists is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - */ -static void mei_clear_lists(struct mei_device *dev, const struct file *file) -{ - struct mei_cl *cl = &dev->iamthif_cl; - - mei_clear_list(dev, file, &dev->amthif_cmd_list.list); - mei_clear_list(dev, file, &cl->rd_completed); - mei_clear_list(dev, file, &dev->ctrl_rd_list.list); -} - /** * mei_amthif_release - the release function * @@ -465,7 +446,9 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) dev->iamthif_canceled = true; } - mei_clear_lists(dev, file); + mei_clear_list(file, &dev->amthif_cmd_list.list); + mei_clear_list(file, &cl->rd_completed); + mei_clear_list(file, &dev->ctrl_rd_list.list); return 0; } -- cgit v1.2.3-58-ga151 From dfe5f753d06a4eaf362872a7f30aabe5f5fc968c Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:01 +0300 Subject: mei: drop redundant krealloc and checks in irq read The read callback is always prepared with MTU-sized buffer and the FW can't send more than the MTU in one message. Checking for buffer existence and krealloc to increase receive buffer size are redundant and may be safely discarded. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/interrupt.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 44ba90140725..36382d782e20 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -102,18 +102,17 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; - unsigned char *buffer = NULL; size_t buf_sz; cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); if (!cb) { if (!mei_cl_is_fixed_address(cl)) { cl_err(dev, cl, "pending read cb not found\n"); - goto out; + goto discard; } cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp); if (!cb) - goto out; + goto discard; list_add_tail(&cb->list, &cl->rd_pending); } @@ -121,14 +120,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cl_dbg(dev, cl, "not connected\n"); list_move_tail(&cb->list, &complete_list->list); cb->status = -ENODEV; - goto out; - } - - if (cb->buf.size == 0 || cb->buf.data == NULL) { - cl_err(dev, cl, "response buffer is not allocated.\n"); - list_move_tail(&cb->list, &complete_list->list); - cb->status = -ENOMEM; - goto out; + goto discard; } buf_sz = mei_hdr->length + cb->buf_idx; @@ -139,25 +131,19 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, list_move_tail(&cb->list, &complete_list->list); cb->status = -EMSGSIZE; - goto out; + goto discard; } if (cb->buf.size < buf_sz) { cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cb->buf.size, mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL); - if (!buffer) { - cb->status = -ENOMEM; - list_move_tail(&cb->list, &complete_list->list); - goto out; - } - cb->buf.data = buffer; - cb->buf.size = buf_sz; + list_move_tail(&cb->list, &complete_list->list); + cb->status = -EMSGSIZE; + goto discard; } - buffer = cb->buf.data + cb->buf_idx; - mei_read_slots(dev, buffer, mei_hdr->length); + mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length); cb->buf_idx += mei_hdr->length; @@ -169,10 +155,10 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, pm_request_autosuspend(dev->dev); } -out: - if (!buffer) - mei_irq_discard_msg(dev, mei_hdr); + return 0; +discard: + mei_irq_discard_msg(dev, mei_hdr); return 0; } -- cgit v1.2.3-58-ga151 From e51dfa5a7f89b2c96371559b0c9772fd41376464 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:02 +0300 Subject: mei: prepare read cb for fixed address clients on the receive path only. The read callbacks for the fixed address clients, that don't have flow control are built now on the receive path. In order to have a single allocation place we remove the allocation from the read request. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2a09db86e50e..05cacc3e718f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1459,17 +1459,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) return -ENOTTY; } + if (mei_cl_is_fixed_address(cl)) + return 0; + /* always allocate at least client max message */ length = max_t(size_t, length, mei_cl_mtu(cl)); cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); if (!cb) return -ENOMEM; - if (mei_cl_is_fixed_address(cl)) { - list_add_tail(&cb->list, &cl->rd_pending); - return 0; - } - rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); -- cgit v1.2.3-58-ga151 From 46978ada7dc669bf13f860798664b587dbc05ce5 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:03 +0300 Subject: mei: rx flow control counter Use RX flow control counter in the host client structure to track the number of simultaneous outstanding reads. This eliminates search in queues and makes ground for enabling for parallel read. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 1 + drivers/misc/mei/client.c | 40 +++++++++++++--------------------------- drivers/misc/mei/mei_dev.h | 4 ++-- 3 files changed, 16 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 26568c5beb25..227fd5a816a6 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -202,6 +202,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) return -ENOMEM; list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + cl->rx_flow_ctrl_creds++; dev->iamthif_state = MEI_IAMTHIF_READING; cl->fp = cb->fp; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 05cacc3e718f..ded11de90a26 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -735,6 +735,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl) mei_io_list_flush(&dev->ctrl_wr_list, cl); mei_cl_wake_all(cl); cl->mei_flow_ctrl_creds = 0; + cl->rx_flow_ctrl_creds = 0; cl->timer_count = 0; if (!cl->me_cl) @@ -1408,25 +1409,6 @@ out: return 0; } -/** - * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control - * for given host client - * - * @cl: host client - * - * Return: true, if found at least one cb. - */ -static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) -{ - struct mei_device *dev = cl->dev; - struct mei_cl_cb *cb; - - list_for_each_entry(cb, &dev->ctrl_wr_list.list, list) - if (cb->fop_type == MEI_FOP_READ && cb->cl == cl) - return true; - return false; -} - /** * mei_cl_read_start - the start read client message function. * @@ -1450,10 +1432,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) if (!mei_cl_is_connected(cl)) return -ENODEV; - /* HW currently supports only one pending read */ - if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl)) - return -EBUSY; - if (!mei_me_cl_is_active(cl->me_cl)) { cl_err(dev, cl, "no such me client\n"); return -ENOTTY; @@ -1462,12 +1440,18 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) if (mei_cl_is_fixed_address(cl)) return 0; + /* HW currently supports only one pending read */ + if (cl->rx_flow_ctrl_creds) + return -EBUSY; + /* always allocate at least client max message */ length = max_t(size_t, length, mei_cl_mtu(cl)); cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); if (!cb) return -ENOMEM; + list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); @@ -1475,16 +1459,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) goto nortpm; } + rets = 0; if (mei_hbuf_acquire(dev)) { rets = mei_hbm_cl_flow_control_req(dev, cl); if (rets < 0) goto out; - list_add_tail(&cb->list, &cl->rd_pending); - } else { - rets = 0; - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + list_move_tail(&cb->list, &cl->rd_pending); } + cl->rx_flow_ctrl_creds++; out: cl_dbg(dev, cl, "rpm: autosuspend\n"); @@ -1732,6 +1715,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) case MEI_FOP_READ: list_add_tail(&cb->list, &cl->rd_completed); + if (!mei_cl_is_fixed_address(cl) && + !WARN_ON(!cl->rx_flow_ctrl_creds)) + cl->rx_flow_ctrl_creds--; if (!mei_cl_bus_rx_event(cl)) wake_up_interruptible(&cl->rx_wait); break; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index aaefbc87c2fc..8bdb28054423 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -203,8 +203,8 @@ struct mei_cl_cb { * @fp: file associated with client * @host_client_id: host id * @mei_flow_ctrl_creds: transmit flow credentials + * @rx_flow_ctrl_creds: receive flow credentials * @timer_count: watchdog timer for operation completion - * @reserved: reserved for alignment * @notify_en: notification - enabled/disabled * @notify_ev: pending notification event * @writing_state: state of the tx @@ -227,8 +227,8 @@ struct mei_cl { const struct file *fp; u8 host_client_id; u8 mei_flow_ctrl_creds; + u8 rx_flow_ctrl_creds; u8 timer_count; - u8 reserved; u8 notify_en; u8 notify_ev; enum mei_file_transaction_states writing_state; -- cgit v1.2.3-58-ga151 From 4034b81ba38967ad0056781572a9d9a95d39f02e Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 26 Jul 2016 01:06:04 +0300 Subject: mei: use consistent naming for TX control flow credits With the introduction of the receive control flow credits prefixed with rx_ we add tx_ prefix to the variables and function used for tracking the transmit control flow credits. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 39 +++++++++++++++++++-------------------- drivers/misc/mei/hbm.c | 41 ++++++++++++++++++++--------------------- drivers/misc/mei/mei_dev.h | 8 ++++---- 3 files changed, 43 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index ded11de90a26..89425a8795a8 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -734,8 +734,8 @@ void mei_cl_set_disconnected(struct mei_cl *cl) mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); mei_cl_wake_all(cl); - cl->mei_flow_ctrl_creds = 0; cl->rx_flow_ctrl_creds = 0; + cl->tx_flow_ctrl_creds = 0; cl->timer_count = 0; if (!cl->me_cl) @@ -745,7 +745,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl) cl->me_cl->connect_count--; if (cl->me_cl->connect_count == 0) - cl->me_cl->mei_flow_ctrl_creds = 0; + cl->me_cl->tx_flow_ctrl_creds = 0; mei_me_cl_put(cl->me_cl); cl->me_cl = NULL; @@ -1140,43 +1140,42 @@ err: return ERR_PTR(ret); } - - /** - * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. + * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl. * * @cl: host client * - * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. + * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise. */ -static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) +static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl) { if (WARN_ON(!cl || !cl->me_cl)) return -EINVAL; - if (cl->mei_flow_ctrl_creds > 0) + if (cl->tx_flow_ctrl_creds > 0) return 1; if (mei_cl_is_fixed_address(cl)) return 1; if (mei_cl_is_single_recv_buf(cl)) { - if (cl->me_cl->mei_flow_ctrl_creds > 0) + if (cl->me_cl->tx_flow_ctrl_creds > 0) return 1; } return 0; } /** - * mei_cl_flow_ctrl_reduce - reduces flow_control. + * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits + * for a client * - * @cl: private data of the file object + * @cl: host client * * Return: * 0 on success * -EINVAL when ctrl credits are <= 0 */ -static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) +static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl) { if (WARN_ON(!cl || !cl->me_cl)) return -EINVAL; @@ -1185,13 +1184,13 @@ static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) return 0; if (mei_cl_is_single_recv_buf(cl)) { - if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0)) + if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0)) return -EINVAL; - cl->me_cl->mei_flow_ctrl_creds--; + cl->me_cl->tx_flow_ctrl_creds--; } else { - if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) + if (WARN_ON(cl->tx_flow_ctrl_creds <= 0)) return -EINVAL; - cl->mei_flow_ctrl_creds--; + cl->tx_flow_ctrl_creds--; } return 0; } @@ -1511,7 +1510,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, first_chunk = cb->buf_idx == 0; - rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1; + rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1; if (rets < 0) return rets; @@ -1559,7 +1558,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, cb->completed = mei_hdr.msg_complete == 1; if (first_chunk) { - if (mei_cl_flow_ctrl_reduce(cl)) + if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) return -EIO; } @@ -1617,7 +1616,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.msg_complete = 0; mei_hdr.internal = cb->internal; - rets = mei_cl_flow_ctrl_creds(cl); + rets = mei_cl_tx_flow_ctrl_creds(cl); if (rets < 0) goto err; @@ -1645,7 +1644,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) if (rets) goto err; - rets = mei_cl_flow_ctrl_reduce(cl); + rets = mei_cl_tx_flow_ctrl_creds_reduce(cl); if (rets) goto err; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index b1697a329af1..c8e8a8d22019 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -340,7 +340,7 @@ static int mei_hbm_me_cl_add(struct mei_device *dev, me_cl->props = res->client_properties; me_cl->client_id = res->me_addr; - me_cl->mei_flow_ctrl_creds = 0; + me_cl->tx_flow_ctrl_creds = 0; mei_me_cl_add(dev, me_cl); @@ -637,23 +637,22 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) } /** - * mei_hbm_add_single_flow_creds - adds single buffer credentials. + * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials. * * @dev: the device structure - * @flow: flow control. + * @fctrl: flow control response bus message * * Return: 0 on success, < 0 otherwise */ -static int mei_hbm_add_single_flow_creds(struct mei_device *dev, - struct hbm_flow_control *flow) +static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev, + struct hbm_flow_control *fctrl) { struct mei_me_client *me_cl; int rets; - me_cl = mei_me_cl_by_id(dev, flow->me_addr); + me_cl = mei_me_cl_by_id(dev, fctrl->me_addr); if (!me_cl) { - dev_err(dev->dev, "no such me client %d\n", - flow->me_addr); + dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr); return -ENOENT; } @@ -662,9 +661,9 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev, goto out; } - me_cl->mei_flow_ctrl_creds++; + me_cl->tx_flow_ctrl_creds++; dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n", - flow->me_addr, me_cl->mei_flow_ctrl_creds); + fctrl->me_addr, me_cl->tx_flow_ctrl_creds); rets = 0; out: @@ -676,24 +675,24 @@ out: * mei_hbm_cl_flow_control_res - flow control response from me * * @dev: the device structure - * @flow_control: flow control response bus message + * @fctrl: flow control response bus message */ -static void mei_hbm_cl_flow_control_res(struct mei_device *dev, - struct hbm_flow_control *flow_control) +static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev, + struct hbm_flow_control *fctrl) { struct mei_cl *cl; - if (!flow_control->host_addr) { + if (!fctrl->host_addr) { /* single receive buffer */ - mei_hbm_add_single_flow_creds(dev, flow_control); + mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl); return; } - cl = mei_hbm_cl_find_by_cmd(dev, flow_control); + cl = mei_hbm_cl_find_by_cmd(dev, fctrl); if (cl) { - cl->mei_flow_ctrl_creds++; + cl->tx_flow_ctrl_creds++; cl_dbg(dev, cl, "flow control creds = %d.\n", - cl->mei_flow_ctrl_creds); + cl->tx_flow_ctrl_creds); } } @@ -1023,7 +1022,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) struct mei_hbm_cl_cmd *cl_cmd; struct hbm_client_connect_request *disconnect_req; - struct hbm_flow_control *flow_control; + struct hbm_flow_control *fctrl; /* read the message to our buffer */ BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); @@ -1103,8 +1102,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) case MEI_FLOW_CONTROL_CMD: dev_dbg(dev->dev, "hbm: client flow control response: message received.\n"); - flow_control = (struct hbm_flow_control *) mei_msg; - mei_hbm_cl_flow_control_res(dev, flow_control); + fctrl = (struct hbm_flow_control *)mei_msg; + mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl); break; case MEI_PG_ISOLATION_ENTRY_RES_CMD: diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 8bdb28054423..7c8fa8d70e11 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -144,7 +144,7 @@ struct mei_fw_status { * @refcnt: struct reference count * @props: client properties * @client_id: me client id - * @mei_flow_ctrl_creds: flow control credits + * @tx_flow_ctrl_creds: flow control credits * @connect_count: number connections to this client * @bus_added: added to bus */ @@ -153,7 +153,7 @@ struct mei_me_client { struct kref refcnt; struct mei_client_properties props; u8 client_id; - u8 mei_flow_ctrl_creds; + u8 tx_flow_ctrl_creds; u8 connect_count; u8 bus_added; }; @@ -202,7 +202,7 @@ struct mei_cl_cb { * @me_cl: fw client connected * @fp: file associated with client * @host_client_id: host id - * @mei_flow_ctrl_creds: transmit flow credentials + * @tx_flow_ctrl_creds: transmit flow credentials * @rx_flow_ctrl_creds: receive flow credentials * @timer_count: watchdog timer for operation completion * @notify_en: notification - enabled/disabled @@ -226,7 +226,7 @@ struct mei_cl { struct mei_me_client *me_cl; const struct file *fp; u8 host_client_id; - u8 mei_flow_ctrl_creds; + u8 tx_flow_ctrl_creds; u8 rx_flow_ctrl_creds; u8 timer_count; u8 notify_en; -- cgit v1.2.3-58-ga151 From 3030dc056459439d756d81a920e135893076a348 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 26 Jul 2016 01:06:05 +0300 Subject: mei: add wrapper for queuing control commands. Enclose the boiler plate code of allocating a control/hbm command cb and enqueueing it onto ctrl_wr.list in a convenient wrapper mei_cl_enqueue_ctrl_wr_cb(). This is a preparatory patch for enabling consecutive reads. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 7 ++--- drivers/misc/mei/bus.c | 4 +-- drivers/misc/mei/client.c | 78 ++++++++++++++++++++++++++++++----------------- drivers/misc/mei/client.h | 5 +-- drivers/misc/mei/hbm.c | 4 +-- drivers/misc/mei/main.c | 2 +- 6 files changed, 61 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 227fd5a816a6..e8029235504d 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -188,20 +188,19 @@ out: * mei_amthif_read_start - queue message for sending read credential * * @cl: host client - * @file: file pointer of message recipient + * @fp: file pointer of message recipient * * Return: 0 on success, <0 on failure. */ -static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) +static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; - cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, file); + cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp); if (!cb) return -ENOMEM; - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); cl->rx_flow_ctrl_creds++; dev->iamthif_state = MEI_IAMTHIF_READING; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 9c404dc8eada..cdf13f6d2697 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -235,7 +235,7 @@ static void mei_cl_bus_event_work(struct work_struct *work) /* Prepare for the next read */ if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { mutex_lock(&bus->device_lock); - mei_cl_read_start(cldev->cl, 0, NULL); + mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); mutex_unlock(&bus->device_lock); } } @@ -325,7 +325,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev, if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { mutex_lock(&bus->device_lock); - ret = mei_cl_read_start(cldev->cl, 0, NULL); + ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); mutex_unlock(&bus->device_lock); if (ret && ret != -EBUSY) return ret; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 89425a8795a8..c924ba92c834 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -358,8 +358,9 @@ void mei_io_cb_free(struct mei_cl_cb *cb) * * Return: mei_cl_cb pointer or NULL; */ -struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - const struct file *fp) +static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, + enum mei_cb_file_ops type, + const struct file *fp) { struct mei_cl_cb *cb; @@ -430,12 +431,12 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) * Return: cb on success and NULL on failure */ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, - enum mei_cb_file_ops type, + enum mei_cb_file_ops fop_type, const struct file *fp) { struct mei_cl_cb *cb; - cb = mei_io_cb_init(cl, type, fp); + cb = mei_io_cb_init(cl, fop_type, fp); if (!cb) return NULL; @@ -452,6 +453,36 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, return cb; } +/** + * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating + * and enqueuing of the control commands cb + * + * @cl: host client + * @length: size of the buffer + * @type: operation type + * @fp: associated file pointer (might be NULL) + * + * Return: cb on success and NULL on failure + * Locking: called under "dev->device_lock" lock + */ +struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, + enum mei_cb_file_ops fop_type, + const struct file *fp) +{ + struct mei_cl_cb *cb; + + /* for RX always allocate at least client's mtu */ + if (length) + length = max_t(size_t, length, mei_cl_mtu(cl)); + + cb = mei_cl_alloc_cb(cl, length, fop_type, fp); + if (!cb) + return NULL; + + list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list); + return cb; +} + /** * mei_cl_read_cb - find this cl's callback in the read list * for a specific file @@ -848,13 +879,11 @@ static int __mei_cl_disconnect(struct mei_cl *cl) cl->state = MEI_FILE_DISCONNECTING; - cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); - rets = cb ? 0 : -ENOMEM; - if (rets) + cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL); + if (!cb) { + rets = -ENOMEM; goto out; - - cl_dbg(dev, cl, "add disconnect cb to control write list\n"); - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + } if (mei_hbuf_acquire(dev)) { rets = mei_cl_send_disconnect(cl, cb); @@ -1023,14 +1052,14 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, * * @cl: host client * @me_cl: me client - * @file: pointer to file structure + * @fp: pointer to file structure * * Locking: called under "dev->device_lock" lock * * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, - const struct file *file) + const struct file *fp) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1057,12 +1086,11 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, goto nortpm; } - cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); - rets = cb ? 0 : -ENOMEM; - if (rets) + cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp); + if (!cb) { + rets = -ENOMEM; goto out; - - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + } /* run hbuf acquire last so we don't have to undo */ if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { @@ -1265,7 +1293,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, * mei_cl_notify_request - send notification stop/start request * * @cl: host client - * @file: associate request with file + * @fp: associate request with file * @request: 1 for start or 0 for stop * * Locking: called under "dev->device_lock" lock @@ -1273,7 +1301,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, * Return: 0 on such and error otherwise. */ int mei_cl_notify_request(struct mei_cl *cl, - const struct file *file, u8 request) + const struct file *fp, u8 request) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1298,7 +1326,7 @@ int mei_cl_notify_request(struct mei_cl *cl, } fop_type = mei_cl_notify_req2fop(request); - cb = mei_io_cb_init(cl, fop_type, file); + cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp); if (!cb) { rets = -ENOMEM; goto out; @@ -1309,9 +1337,7 @@ int mei_cl_notify_request(struct mei_cl *cl, rets = -ENODEV; goto out; } - list_add_tail(&cb->list, &dev->ctrl_rd_list.list); - } else { - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list.list); } mutex_unlock(&dev->device_lock); @@ -1443,14 +1469,10 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) if (cl->rx_flow_ctrl_creds) return -EBUSY; - /* always allocate at least client max message */ - length = max_t(size_t, length, mei_cl_mtu(cl)); - cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); + cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp); if (!cb) return -ENOMEM; - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); - rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 16466aa40cbd..d2bfabecd882 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -82,8 +82,6 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) /* * MEI IO Functions */ -struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - const struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); /** @@ -116,6 +114,9 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, const struct file *fp); +struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, + enum mei_cb_file_ops type, + const struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); /* diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index c8e8a8d22019..4b9495f0394c 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -871,10 +871,10 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, cl->state = MEI_FILE_DISCONNECTING; cl->timer_count = 0; - cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL); + cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT_RSP, + NULL); if (!cb) return -ENOMEM; - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } return 0; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d7ef5edf044a..d698ba32357c 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -628,7 +628,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) if (!list_empty(&cl->rd_completed)) mask |= POLLIN | POLLRDNORM; else - mei_cl_read_start(cl, 0, file); + mei_cl_read_start(cl, mei_cl_mtu(cl), file); } out: -- cgit v1.2.3-58-ga151 From ff1586a7ea57a4758a3f10ad7c9cdb26edcbd7f3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:06 +0300 Subject: mei: enqueue consecutive reads The FW supports only one pending read per host client, in order to support issuing of consecutive reads the driver queues read requests internally and send them to the firmware after pending one has completed. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/interrupt.c | 3 +++ drivers/misc/mei/main.c | 39 +++++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 36382d782e20..bf745e03f21e 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -209,6 +209,9 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, int slots; int ret; + if (!list_empty(&cl->rd_pending)) + return 0; + msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); slots = mei_hbuf_empty_slots(dev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d698ba32357c..650061b6ab9b 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -139,9 +139,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, struct mei_cl *cl = file->private_data; struct mei_device *dev; struct mei_cl_cb *cb = NULL; + bool nonblock = !!(file->f_flags & O_NONBLOCK); int rets; - int err; - if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -177,25 +176,29 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, if (*offset > 0) *offset = 0; - err = mei_cl_read_start(cl, length, file); - if (err && err != -EBUSY) { - cl_dbg(dev, cl, "mei start read failure status = %d\n", err); - rets = err; + rets = mei_cl_read_start(cl, length, file); + if (rets && rets != -EBUSY) { + cl_dbg(dev, cl, "mei start read failure status = %d\n", rets); goto out; } - /* synchronized under device mutex */ - if (!waitqueue_active(&cl->rx_wait)) { - if (file->f_flags & O_NONBLOCK) { - rets = -EAGAIN; - goto out; - } + if (nonblock) { + rets = -EAGAIN; + goto out; + } + + if (rets == -EBUSY && + !mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) { + rets = -ENOMEM; + goto out; + } + do { mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - (!list_empty(&cl->rd_completed)) || - (!mei_cl_is_connected(cl)))) { + (!list_empty(&cl->rd_completed)) || + (!mei_cl_is_connected(cl)))) { if (signal_pending(current)) return -EINTR; @@ -207,13 +210,9 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, rets = -ENODEV; goto out; } - } - cb = mei_cl_read_cb(cl, file); - if (!cb) { - rets = 0; - goto out; - } + cb = mei_cl_read_cb(cl, file); + } while (!cb); copy_buffer: /* now copy the data to user space */ -- cgit v1.2.3-58-ga151 From 9d27e73c1897c17e535987a4758fddb7a2fe1fd3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:07 +0300 Subject: mei: amthif: drop mei_amthif_read mei_amthif_read have only one difference from mei_read, it is not calling mei_read_start(). Make mei_read_start return immediately for amthif client and drop the special mei_amthif_read function. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 100 --------------------------------------------- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/main.c | 5 --- drivers/misc/mei/mei_dev.h | 3 -- 4 files changed, 1 insertion(+), 109 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index e8029235504d..2222b60e9208 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -84,106 +84,6 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) return ret; } -/** - * mei_amthif_read - read data from AMTHIF client - * - * @dev: the device structure - * @file: pointer to file object - * @ubuf: pointer to user data in user space - * @length: data length to read - * @offset: data read offset - * - * Locking: called under "dev->device_lock" lock - * - * Return: - * returned data length on success, - * zero if no data to read, - * negative on failure. - */ -int mei_amthif_read(struct mei_device *dev, struct file *file, - char __user *ubuf, size_t length, loff_t *offset) -{ - struct mei_cl *cl = file->private_data; - struct mei_cl_cb *cb; - int rets; - int wait_ret; - - dev_dbg(dev->dev, "checking amthif data\n"); - cb = mei_cl_read_cb(cl, file); - - /* Check for if we can block or not*/ - if (cb == NULL && file->f_flags & O_NONBLOCK) - return -EAGAIN; - - - dev_dbg(dev->dev, "waiting for amthif data\n"); - while (cb == NULL) { - /* unlock the Mutex */ - mutex_unlock(&dev->device_lock); - - wait_ret = wait_event_interruptible(cl->rx_wait, - !list_empty(&cl->rd_completed) || - !mei_cl_is_connected(cl)); - - /* Locking again the Mutex */ - mutex_lock(&dev->device_lock); - - if (wait_ret) - return -ERESTARTSYS; - - if (!mei_cl_is_connected(cl)) { - rets = -ENODEV; - goto out; - } - - cb = mei_cl_read_cb(cl, file); - } - - if (cb->status) { - rets = cb->status; - dev_dbg(dev->dev, "read operation failed %d\n", rets); - goto free; - } - - dev_dbg(dev->dev, "Got amthif data\n"); - /* if the whole message will fit remove it from the list */ - if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) - list_del_init(&cb->list); - else if (cb->buf_idx <= *offset) { - /* end of the message has been reached */ - list_del_init(&cb->list); - rets = 0; - goto free; - } - /* else means that not full buffer will be read and do not - * remove message from deletion list - */ - - dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n", - cb->buf.size, cb->buf_idx); - - /* length is being truncated to PAGE_SIZE, however, - * the buf_idx may point beyond */ - length = min_t(size_t, length, (cb->buf_idx - *offset)); - - if (copy_to_user(ubuf, cb->buf.data + *offset, length)) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); - rets = -EFAULT; - } else { - rets = length; - if ((*offset + length) < cb->buf_idx) { - *offset += length; - goto out; - } - } -free: - dev_dbg(dev->dev, "free amthif cb memory.\n"); - *offset = 0; - mei_io_cb_free(cb); -out: - return rets; -} - /** * mei_amthif_read_start - queue message for sending read credential * diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c924ba92c834..45a7652820cf 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1462,7 +1462,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) return -ENOTTY; } - if (mei_cl_is_fixed_address(cl)) + if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl) return 0; /* HW currently supports only one pending read */ diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 650061b6ab9b..fa50635512e8 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -164,11 +164,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (cl == &dev->iamthif_cl) { - rets = mei_amthif_read(dev, file, ubuf, length, offset); - goto out; - } - cb = mei_cl_read_cb(cl, file); if (cb) goto copy_buffer; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 7c8fa8d70e11..2ef1ad6c391a 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -564,9 +564,6 @@ void mei_amthif_reset_params(struct mei_device *dev); int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -int mei_amthif_read(struct mei_device *dev, struct file *file, - char __user *ubuf, size_t length, loff_t *offset); - unsigned int mei_amthif_poll(struct file *file, poll_table *wait); int mei_amthif_release(struct mei_device *dev, struct file *file); -- cgit v1.2.3-58-ga151 From 80293c47a6774848c72a2f79816b7de1769c5e85 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:08 +0300 Subject: mei: drop unused file transaction states Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei_dev.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 2ef1ad6c391a..397ae2b45c9e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -87,9 +87,6 @@ enum mei_file_transaction_states { MEI_IDLE, MEI_WRITING, MEI_WRITE_COMPLETE, - MEI_FLOW_CONTROL, - MEI_READING, - MEI_READ_COMPLETE }; /** -- cgit v1.2.3-58-ga151 From e728ae271f4cf71218ec06a6daf61b79466cb466 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 26 Jul 2016 01:06:09 +0300 Subject: mei: amthif: fix deadlock in initialization during a reset The device lock was unnecessary obtained in bus rescan work before the amthif client search. That causes incorrect lock ordering and task hang: ... [88004.613213] INFO: task kworker/1:14:21832 blocked for more than 120 seconds. ... [88004.645934] Workqueue: events mei_cl_bus_rescan_work ... The correct lock order is cl_bus_lock device_lock me_clients_rwsem Move device_lock into amthif init function that called after me_clients_rwsem is released. This fixes regression introduced by commit: commit 025fb792bac3 ("mei: split amthif client init from end of clients enumeration") Cc: # 4.6+ Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 12 +++++++++--- drivers/misc/mei/bus.c | 2 -- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2222b60e9208..082462ea90c9 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -66,8 +66,12 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) struct mei_cl *cl = &dev->iamthif_cl; int ret; - if (mei_cl_is_connected(cl)) - return 0; + mutex_lock(&dev->device_lock); + + if (mei_cl_is_connected(cl)) { + ret = 0; + goto out; + } dev->iamthif_state = MEI_IAMTHIF_IDLE; @@ -76,11 +80,13 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) ret = mei_cl_link(cl); if (ret < 0) { dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); - return ret; + goto out; } ret = mei_cl_connect(cl, me_cl, NULL); +out: + mutex_unlock(&dev->device_lock); return ret; } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index cdf13f6d2697..8cac7ef9ad0d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -984,12 +984,10 @@ void mei_cl_bus_rescan_work(struct work_struct *work) container_of(work, struct mei_device, bus_rescan_work); struct mei_me_client *me_cl; - mutex_lock(&bus->device_lock); me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); if (me_cl) mei_amthif_host_init(bus, me_cl); mei_me_cl_put(me_cl); - mutex_unlock(&bus->device_lock); mei_cl_bus_rescan(bus); } -- cgit v1.2.3-58-ga151 From 9d32f82e040a823a584446cd9a7066390bf911fa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Jun 2016 13:38:24 +0200 Subject: mic: remove unused function arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building with W=1, the __scif_rma_destroy_tcw function causes a harmless warning about an argument variable that is modified but not used: drivers/misc/mic/scif/scif_dma.c: In function ‘__scif_rma_destroy_tcw’: drivers/misc/mic/scif/scif_dma.c:118:27: error: parameter ‘ep’ set but not used [-Werror=unused-but-set-parameter] In this case, we can just remove the argument, since all callers are in the same file. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index cd01a0efda6b..64d5760d069a 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -115,7 +115,6 @@ int scif_reserve_dma_chan(struct scif_endpt *ep) */ static void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, - struct scif_endpt *ep, u64 start, u64 len) { struct list_head *item, *tmp; @@ -128,7 +127,6 @@ void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, list_for_each_safe(item, tmp, &mmn->tc_reg_list) { window = list_entry(item, struct scif_window, list); - ep = (struct scif_endpt *)window->ep; if (!len) break; start_va = window->va_for_temp; @@ -146,7 +144,7 @@ static void scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, u64 start, u64 len) struct scif_endpt *ep = mmn->ep; spin_lock(&ep->rma_info.tc_lock); - __scif_rma_destroy_tcw(mmn, ep, start, len); + __scif_rma_destroy_tcw(mmn, start, len); spin_unlock(&ep->rma_info.tc_lock); } @@ -169,7 +167,7 @@ static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep) spin_lock(&ep->rma_info.tc_lock); list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { mmn = list_entry(item, struct scif_mmu_notif, list); - __scif_rma_destroy_tcw(mmn, ep, 0, ULONG_MAX); + __scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); } spin_unlock(&ep->rma_info.tc_lock); } -- cgit v1.2.3-58-ga151 From acde785e492d6ce838f50937f7547124e0837a55 Mon Sep 17 00:00:00 2001 From: Muhammad Falak R Wani Date: Sat, 21 May 2016 18:41:38 +0530 Subject: misc: mic: scif: use vma_pages(). Replace explicit computation of vma page count by a call to vma_pages() Signed-off-by: Muhammad Falak R Wani Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_mmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_mmap.c b/drivers/misc/mic/scif/scif_mmap.c index 49cb8f7b4672..928211677079 100644 --- a/drivers/misc/mic/scif/scif_mmap.c +++ b/drivers/misc/mic/scif/scif_mmap.c @@ -552,7 +552,7 @@ static void scif_munmap(struct vm_area_struct *vma) { struct scif_endpt *ep; struct vma_pvt *vmapvt = vma->vm_private_data; - int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + int nr_pages = vma_pages(vma); s64 offset; struct scif_rma_req req; struct scif_window *window = NULL; @@ -614,7 +614,7 @@ int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd) struct scif_window *window = NULL; struct scif_endpt *ep = (struct scif_endpt *)epd; s64 start_offset = vma->vm_pgoff << PAGE_SHIFT; - int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + int nr_pages = vma_pages(vma); int err; struct vma_pvt *vmapvt; -- cgit v1.2.3-58-ga151 From 069b188f43d9a44422eb35a8f95533d2c44ad315 Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Thu, 28 Jul 2016 15:07:04 -0400 Subject: tile-srom: avoid krealloc(... __GFP_ZERO) pattern Joe Perches points out [1] that this pattern isn't currently safe. This driver doesn't really need the zeroing semantic anyway; by restructuring the code slightly we can initialize all the fields of the structure up front instead. [1] https://lkml.kernel.org/r/1469729491.3998.58.camel@perches.com Signed-off-by: Chris Metcalf Signed-off-by: Greg Kroah-Hartman --- drivers/char/tile-srom.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 69f6b4acc377..398800edb2cc 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -331,13 +331,11 @@ static const struct file_operations srom_fops = { /** * srom_setup_minor() - Initialize per-minor information. * @srom: Per-device SROM state. - * @index: Device to set up. + * @devhdl: Partition device handle. */ -static int srom_setup_minor(struct srom_dev *srom, int index) +static int srom_setup_minor(struct srom_dev *srom, int devhdl) { - struct device *dev; - int devhdl = srom->hv_devhdl; - + srom->hv_devhdl = devhdl; mutex_init(&srom->lock); if (_srom_read(devhdl, &srom->total_size, @@ -350,9 +348,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index) SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0) return -EIO; - dev = device_create(srom_class, &srom_parent->dev, - MKDEV(srom_major, index), srom, "%d", index); - return PTR_ERR_OR_ZERO(dev); + return 0; } /** srom_init() - Initialize the driver's module. */ @@ -365,7 +361,7 @@ static int srom_init(void) * Start with a plausible number of partitions; the krealloc() call * below will yield about log(srom_devs) additional allocations. */ - srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); + srom_devices = kmalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); /* Discover the number of srom partitions. */ for (i = 0; ; i++) { @@ -373,7 +369,7 @@ static int srom_init(void) char buf[20]; struct srom_dev *new_srom_devices = krealloc(srom_devices, (i+1) * sizeof(struct srom_dev), - GFP_KERNEL | __GFP_ZERO); + GFP_KERNEL); if (!new_srom_devices) { result = -ENOMEM; goto fail_mem; @@ -387,7 +383,9 @@ static int srom_init(void) i, devhdl); break; } - srom_devices[i].hv_devhdl = devhdl; + result = srom_setup_minor(&srom_devices[i], devhdl); + if (result != 0) + goto fail_mem; } srom_devs = i; @@ -431,9 +429,13 @@ static int srom_init(void) srom_class->dev_groups = srom_dev_groups; srom_class->devnode = srom_devnode; - /* Do per-partition initialization */ + /* Create per-partition devices */ for (i = 0; i < srom_devs; i++) { - result = srom_setup_minor(srom_devices + i, i); + struct device *dev = + device_create(srom_class, &srom_parent->dev, + MKDEV(srom_major, i), srom_devices + i, + "%d", i); + result = PTR_ERR_OR_ZERO(dev); if (result < 0) goto fail_class; } -- cgit v1.2.3-58-ga151 From a9f61ca793becabdefab03b77568d6c6f8c1bc79 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 3 Jun 2016 17:09:22 -0700 Subject: Drivers: hv: avoid vfree() on crash When we crash from NMI context (e.g. after NMI injection from host when 'sysctl -w kernel.unknown_nmi_panic=1' is set) we hit kernel BUG at mm/vmalloc.c:1530! as vfree() is denied. While the issue could be solved with in_nmi() check instead I opted for skipping vfree on all sorts of crashes to reduce the amount of work which can cause consequent crashes. We don't really need to free anything on crash. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 8 +++++--- drivers/hv/hyperv_vmbus.h | 2 +- drivers/hv/vmbus_drv.c | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index a1c086ba3b9a..60dbd6cb4640 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -278,7 +278,7 @@ cleanup: * * This routine is called normally during driver unloading or exiting. */ -void hv_cleanup(void) +void hv_cleanup(bool crash) { union hv_x64_msr_hypercall_contents hypercall_msr; @@ -288,7 +288,8 @@ void hv_cleanup(void) if (hv_context.hypercall_page) { hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - vfree(hv_context.hypercall_page); + if (!crash) + vfree(hv_context.hypercall_page); hv_context.hypercall_page = NULL; } @@ -308,7 +309,8 @@ void hv_cleanup(void) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - vfree(hv_context.tsc_page); + if (!crash) + vfree(hv_context.tsc_page); hv_context.tsc_page = NULL; } #endif diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 718b5c72f0c8..dfa9fac100d8 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -495,7 +495,7 @@ struct hv_ring_buffer_debug_info { extern int hv_init(void); -extern void hv_cleanup(void); +extern void hv_cleanup(bool crash); extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e82f7e1c217c..fedf6298ec0b 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -874,7 +874,7 @@ err_alloc: bus_unregister(&hv_bus); err_cleanup: - hv_cleanup(); + hv_cleanup(false); return ret; } @@ -1326,7 +1326,7 @@ static void hv_kexec_handler(void) vmbus_initiate_unload(false); for_each_online_cpu(cpu) smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); - hv_cleanup(); + hv_cleanup(false); }; static void hv_crash_handler(struct pt_regs *regs) @@ -1338,7 +1338,7 @@ static void hv_crash_handler(struct pt_regs *regs) * for kdump. */ hv_synic_cleanup(NULL); - hv_cleanup(); + hv_cleanup(true); }; static int __init hv_acpi_init(void) @@ -1398,7 +1398,7 @@ static void __exit vmbus_exit(void) &hyperv_panic_block); } bus_unregister(&hv_bus); - hv_cleanup(); + hv_cleanup(false); for_each_online_cpu(cpu) { tasklet_kill(hv_context.event_dpc[cpu]); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); -- cgit v1.2.3-58-ga151 From 4d63763296ab7865a98bc29cc7d77145815ef89f Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 3 Jun 2016 17:09:23 -0700 Subject: Drivers: hv: get rid of redundant messagecount in create_gpadl_header() We use messagecount only once in vmbus_establish_gpadl() to check if it is safe to iterate through the submsglist. We can just initialize the list header in all cases in create_gpadl_header() instead. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 56dd261f7142..2b109e8a8c97 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -238,8 +238,7 @@ EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); * create_gpadl_header - Creates a gpadl for the specified buffer */ static int create_gpadl_header(void *kbuffer, u32 size, - struct vmbus_channel_msginfo **msginfo, - u32 *messagecount) + struct vmbus_channel_msginfo **msginfo) { int i; int pagecount; @@ -283,7 +282,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys( kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; *msginfo = msgheader; - *messagecount = 1; pfnsum = pfncount; pfnleft = pagecount - pfncount; @@ -323,7 +321,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, } msgbody->msgsize = msgsize; - (*messagecount)++; gpadl_body = (struct vmbus_channel_gpadl_body *)msgbody->msg; @@ -352,6 +349,8 @@ static int create_gpadl_header(void *kbuffer, u32 size, msgheader = kzalloc(msgsize, GFP_KERNEL); if (msgheader == NULL) goto nomem; + + INIT_LIST_HEAD(&msgheader->submsglist); msgheader->msgsize = msgsize; gpadl_header = (struct vmbus_channel_gpadl_header *) @@ -366,7 +365,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; *msginfo = msgheader; - *messagecount = 1; } return 0; @@ -391,7 +389,6 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_msginfo *submsginfo; - u32 msgcount; struct list_head *curr; u32 next_gpadl_handle; unsigned long flags; @@ -400,7 +397,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, next_gpadl_handle = (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1); - ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); + ret = create_gpadl_header(kbuffer, size, &msginfo); if (ret) return ret; @@ -423,24 +420,21 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, if (ret != 0) goto cleanup; - if (msgcount > 1) { - list_for_each(curr, &msginfo->submsglist) { + list_for_each(curr, &msginfo->submsglist) { + submsginfo = (struct vmbus_channel_msginfo *)curr; + gpadl_body = + (struct vmbus_channel_gpadl_body *)submsginfo->msg; - submsginfo = (struct vmbus_channel_msginfo *)curr; - gpadl_body = - (struct vmbus_channel_gpadl_body *)submsginfo->msg; + gpadl_body->header.msgtype = + CHANNELMSG_GPADL_BODY; + gpadl_body->gpadl = next_gpadl_handle; - gpadl_body->header.msgtype = - CHANNELMSG_GPADL_BODY; - gpadl_body->gpadl = next_gpadl_handle; + ret = vmbus_post_msg(gpadl_body, + submsginfo->msgsize - + sizeof(*submsginfo)); + if (ret != 0) + goto cleanup; - ret = vmbus_post_msg(gpadl_body, - submsginfo->msgsize - - sizeof(*submsginfo)); - if (ret != 0) - goto cleanup; - - } } wait_for_completion(&msginfo->waitevent); -- cgit v1.2.3-58-ga151 From 7cc80c98070ccc7940fc28811c92cca0a681015d Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 3 Jun 2016 17:09:24 -0700 Subject: Drivers: hv: don't leak memory in vmbus_establish_gpadl() In some cases create_gpadl_header() allocates submessages but we never free them. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 2b109e8a8c97..a68830c848e4 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -388,7 +388,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, struct vmbus_channel_gpadl_header *gpadlmsg; struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_msginfo *submsginfo; + struct vmbus_channel_msginfo *submsginfo, *tmp; struct list_head *curr; u32 next_gpadl_handle; unsigned long flags; @@ -445,6 +445,10 @@ cleanup: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&msginfo->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist, + msglistentry) { + kfree(submsginfo); + } kfree(msginfo); return ret; -- cgit v1.2.3-58-ga151 From 396e287fa2ff46e83ae016cdcb300c3faa3b02f6 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 9 Jun 2016 17:08:56 -0700 Subject: Drivers: hv: get rid of timeout in vmbus_open() vmbus_teardown_gpadl() can result in infinite wait when it is called on 5 second timeout in vmbus_open(). The issue is caused by the fact that gpadl teardown operation won't ever succeed for an opened channel and the timeout isn't always enough. As a guest, we can always trust the host to respond to our request (and there is nothing we can do if it doesn't). Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index a68830c848e4..9a88c63f6e0d 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -73,7 +73,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, void *in, *out; unsigned long flags; int ret, err = 0; - unsigned long t; struct page *page; spin_lock_irqsave(&newchannel->lock, flags); @@ -183,11 +182,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto error1; } - t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); - if (t == 0) { - err = -ETIMEDOUT; - goto error1; - } + wait_for_completion(&open_info->waitevent); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&open_info->msglistentry); -- cgit v1.2.3-58-ga151 From e0fa3e5e7df61eb2c339c9f0067c202c0cdeec2c Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 9 Jun 2016 17:08:57 -0700 Subject: Drivers: hv: utils: fix a race on userspace daemons registration Background: userspace daemons registration protocol for Hyper-V utilities drivers has two steps: 1) daemon writes its own version to kernel 2) kernel reads it and replies with module version at this point we consider the handshake procedure being completed and we do hv_poll_channel() transitioning the utility device to HVUTIL_READY state. At this point we're ready to handle messages from kernel. When hvutil_transport is in HVUTIL_TRANSPORT_CHARDEV mode we have a single buffer for outgoing message. hvutil_transport_send() puts to this buffer and till the buffer is cleared with hvt_op_read() returns -EFAULT to all consequent calls. Host<->guest protocol guarantees there is no more than one request at a time and we will not get new requests till we reply to the previous one so this single message buffer is enough. Now to the race. When we finish negotiation procedure and send kernel module version to userspace with hvutil_transport_send() it goes into the above mentioned buffer and if the daemon is slow enough to read it from there we can get a collision when a request from the host comes, we won't be able to put anything to the buffer so the request will be lost. To solve the issue we need to know when the negotiation is really done (when the version message is read by the daemon) and transition to HVUTIL_READY state after this happens. Implement a callback on read to support this. Old style netlink communication is not affected by the change, we don't really know when these messages are delivered but we don't have a single message buffer there. Reported-by: Barry Davis Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_fcopy.c | 14 ++++++++++---- drivers/hv/hv_kvp.c | 27 ++++++++++++++++----------- drivers/hv/hv_snapshot.c | 16 +++++++++++----- drivers/hv/hv_utils_transport.c | 15 ++++++++++++++- drivers/hv/hv_utils_transport.h | 4 +++- 5 files changed, 54 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 23c70799ad8a..8b2ba98831ec 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -83,6 +83,12 @@ static void fcopy_timeout_func(struct work_struct *dummy) hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); } +static void fcopy_register_done(void) +{ + pr_debug("FCP: userspace daemon registered\n"); + hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); +} + static int fcopy_handle_handshake(u32 version) { u32 our_ver = FCOPY_CURRENT_VERSION; @@ -94,7 +100,8 @@ static int fcopy_handle_handshake(u32 version) break; case FCOPY_VERSION_1: /* Daemon expects us to reply with our own version */ - if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver))) + if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver), + fcopy_register_done)) return -EFAULT; dm_reg_value = version; break; @@ -107,8 +114,7 @@ static int fcopy_handle_handshake(u32 version) */ return -EINVAL; } - pr_debug("FCP: userspace daemon ver. %d registered\n", version); - hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); + pr_debug("FCP: userspace daemon ver. %d connected\n", version); return 0; } @@ -161,7 +167,7 @@ static void fcopy_send_data(struct work_struct *dummy) } fcopy_transaction.state = HVUTIL_USERSPACE_REQ; - rc = hvutil_transport_send(hvt, out_src, out_len); + rc = hvutil_transport_send(hvt, out_src, out_len, NULL); if (rc) { pr_debug("FCP: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&fcopy_timeout_work)) { diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index cb1a9160aab1..5e1fdc8d32ab 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -102,6 +102,17 @@ static void kvp_poll_wrapper(void *channel) hv_kvp_onchannelcallback(channel); } +static void kvp_register_done(void) +{ + /* + * If we're still negotiating with the host cancel the timeout + * work to not poll the channel twice. + */ + pr_debug("KVP: userspace daemon registered\n"); + cancel_delayed_work_sync(&kvp_host_handshake_work); + hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); +} + static void kvp_register(int reg_value) { @@ -116,7 +127,8 @@ kvp_register(int reg_value) kvp_msg->kvp_hdr.operation = reg_value; strcpy(version, HV_DRV_VERSION); - hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg)); + hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), + kvp_register_done); kfree(kvp_msg); } } @@ -158,17 +170,10 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) /* * We have a compatible daemon; complete the handshake. */ - pr_debug("KVP: userspace daemon ver. %d registered\n", - KVP_OP_REGISTER); + pr_debug("KVP: userspace daemon ver. %d connected\n", + msg->kvp_hdr.operation); kvp_register(dm_reg_value); - /* - * If we're still negotiating with the host cancel the timeout - * work to not poll the channel twice. - */ - cancel_delayed_work_sync(&kvp_host_handshake_work); - hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); - return 0; } @@ -455,7 +460,7 @@ kvp_send_key(struct work_struct *dummy) } kvp_transaction.state = HVUTIL_USERSPACE_REQ; - rc = hvutil_transport_send(hvt, message, sizeof(*message)); + rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL); if (rc) { pr_debug("KVP: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&kvp_timeout_work)) { diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 3fba14e88f03..fde45865579a 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -95,6 +95,12 @@ static void vss_timeout_func(struct work_struct *dummy) hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); } +static void vss_register_done(void) +{ + hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); + pr_debug("VSS: userspace daemon registered\n"); +} + static int vss_handle_handshake(struct hv_vss_msg *vss_msg) { u32 our_ver = VSS_OP_REGISTER1; @@ -105,16 +111,16 @@ static int vss_handle_handshake(struct hv_vss_msg *vss_msg) dm_reg_value = VSS_OP_REGISTER; break; case VSS_OP_REGISTER1: - /* Daemon expects us to reply with our own version*/ - if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver))) + /* Daemon expects us to reply with our own version */ + if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver), + vss_register_done)) return -EFAULT; dm_reg_value = VSS_OP_REGISTER1; break; default: return -EINVAL; } - hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); - pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value); + pr_debug("VSS: userspace daemon ver. %d connected\n", dm_reg_value); return 0; } @@ -168,7 +174,7 @@ static void vss_send_op(struct work_struct *dummy) vss_msg->vss_hdr.operation = op; vss_transaction.state = HVUTIL_USERSPACE_REQ; - rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg)); + rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL); if (rc) { pr_warn("VSS: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&vss_timeout_work)) { diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c index 9a9983fa4531..c235a9515267 100644 --- a/drivers/hv/hv_utils_transport.c +++ b/drivers/hv/hv_utils_transport.c @@ -72,6 +72,10 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf, hvt->outmsg = NULL; hvt->outmsg_len = 0; + if (hvt->on_read) + hvt->on_read(); + hvt->on_read = NULL; + out_unlock: mutex_unlock(&hvt->lock); return ret; @@ -219,7 +223,8 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) mutex_unlock(&hvt->lock); } -int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) +int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len, + void (*on_read_cb)(void)) { struct cn_msg *cn_msg; int ret = 0; @@ -237,6 +242,13 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) memcpy(cn_msg->data, msg, len); ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC); kfree(cn_msg); + /* + * We don't know when netlink messages are delivered but unlike + * in CHARDEV mode we're not blocked and we can send next + * messages right away. + */ + if (on_read_cb) + on_read_cb(); return ret; } /* HVUTIL_TRANSPORT_CHARDEV */ @@ -255,6 +267,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) if (hvt->outmsg) { memcpy(hvt->outmsg, msg, len); hvt->outmsg_len = len; + hvt->on_read = on_read_cb; wake_up_interruptible(&hvt->outmsg_q); } else ret = -ENOMEM; diff --git a/drivers/hv/hv_utils_transport.h b/drivers/hv/hv_utils_transport.h index 06254a165a18..d98f5225c3e6 100644 --- a/drivers/hv/hv_utils_transport.h +++ b/drivers/hv/hv_utils_transport.h @@ -36,6 +36,7 @@ struct hvutil_transport { struct list_head list; /* hvt_list */ int (*on_msg)(void *, int); /* callback on new user message */ void (*on_reset)(void); /* callback when userspace drops */ + void (*on_read)(void); /* callback on message read */ u8 *outmsg; /* message to the userspace */ int outmsg_len; /* its length */ wait_queue_head_t outmsg_q; /* poll/read wait queue */ @@ -46,7 +47,8 @@ struct hvutil_transport *hvutil_transport_init(const char *name, u32 cn_idx, u32 cn_val, int (*on_msg)(void *, int), void (*on_reset)(void)); -int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len); +int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len, + void (*on_read_cb)(void)); void hvutil_transport_destroy(struct hvutil_transport *hvt); #endif /* _HV_UTILS_TRANSPORT_H */ -- cgit v1.2.3-58-ga151 From 638fea33aee858cc665297a76f0039e95a28ce0c Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 9 Jun 2016 18:47:24 -0700 Subject: Drivers: hv: vmbus: fix the race when querying & updating the percpu list There is a rare race when we remove an entry from the global list hv_context.percpu_list[cpu] in hv_process_channel_removal() -> percpu_channel_deq() -> list_del(): at this time, if vmbus_on_event() -> process_chn_event() -> pcpu_relid2channel() is trying to query the list, we can get the kernel fault. Similarly, we also have the issue in the code path: vmbus_process_offer() -> percpu_channel_enq(). We can resolve the issue by disabling the tasklet when updating the list. The patch also moves vmbus_release_relid() to a later place where the channel has been removed from the per-cpu and the global lists. Reported-by: Rolf Neugebauer Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 6 ++---- drivers/hv/channel_mgmt.c | 32 ++++++++++++++++++++++++++++---- include/linux/hyperv.h | 3 +++ 3 files changed, 33 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9a88c63f6e0d..e47d37d21d56 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -505,7 +505,6 @@ static void reset_channel_cb(void *arg) static int vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; - struct tasklet_struct *tasklet; int ret; /* @@ -517,8 +516,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel) * To resolve the race, we can serialize them by disabling the * tasklet when the latter is running here. */ - tasklet = hv_context.event_dpc[channel->target_cpu]; - tasklet_disable(tasklet); + hv_event_tasklet_disable(channel); /* * In case a device driver's probe() fails (e.g., @@ -584,7 +582,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel) get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); out: - tasklet_enable(tasklet); + hv_event_tasklet_enable(channel); return ret; } diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b6c1211b4df7..8818b92e5912 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -21,6 +21,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -303,16 +304,32 @@ static void vmbus_release_relid(u32 relid) vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); } +void hv_event_tasklet_disable(struct vmbus_channel *channel) +{ + struct tasklet_struct *tasklet; + tasklet = hv_context.event_dpc[channel->target_cpu]; + tasklet_disable(tasklet); +} + +void hv_event_tasklet_enable(struct vmbus_channel *channel) +{ + struct tasklet_struct *tasklet; + tasklet = hv_context.event_dpc[channel->target_cpu]; + tasklet_enable(tasklet); + + /* In case there is any pending event */ + tasklet_schedule(tasklet); +} + void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) { unsigned long flags; struct vmbus_channel *primary_channel; - vmbus_release_relid(relid); - BUG_ON(!channel->rescind); BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); + hv_event_tasklet_disable(channel); if (channel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(channel->target_cpu, @@ -321,6 +338,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) percpu_channel_deq(channel); put_cpu(); } + hv_event_tasklet_enable(channel); if (channel->primary_channel == NULL) { list_del(&channel->listentry); @@ -341,6 +359,8 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) cpumask_clear_cpu(channel->target_cpu, &primary_channel->alloced_cpus_in_node); + vmbus_release_relid(relid); + free_channel(channel); } @@ -409,6 +429,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) init_vp_index(newchannel, dev_type); + hv_event_tasklet_disable(newchannel); if (newchannel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(newchannel->target_cpu, @@ -418,6 +439,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) percpu_channel_enq(newchannel); put_cpu(); } + hv_event_tasklet_enable(newchannel); /* * This state is used to indicate a successful open @@ -463,12 +485,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) return; err_deq_chan: - vmbus_release_relid(newchannel->offermsg.child_relid); - mutex_lock(&vmbus_connection.channel_mutex); list_del(&newchannel->listentry); mutex_unlock(&vmbus_connection.channel_mutex); + hv_event_tasklet_disable(newchannel); if (newchannel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(newchannel->target_cpu, @@ -477,6 +498,9 @@ err_deq_chan: percpu_channel_deq(newchannel); put_cpu(); } + hv_event_tasklet_enable(newchannel); + + vmbus_release_relid(newchannel->offermsg.child_relid); err_free_chan: free_channel(newchannel); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b10954a66939..50f493eedeb5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1357,6 +1357,9 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, struct icmsg_negotiate *, u8 *, int, int); +void hv_event_tasklet_disable(struct vmbus_channel *channel); +void hv_event_tasklet_enable(struct vmbus_channel *channel); + void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); /* -- cgit v1.2.3-58-ga151 From ccef9bcc02ee63ac171ea9f0d51e04b3e55b3a12 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 1 Jul 2016 16:26:35 -0700 Subject: Drivers: hv: vmbus: Enable explicit signaling policy for NIC channels For synthetic NIC channels, enable explicit signaling policy as netvsc wants to explicitly control when the host is to be signaled. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 18 ++++-------------- drivers/hv/channel_mgmt.c | 2 ++ drivers/hv/hyperv_vmbus.h | 3 ++- drivers/hv/ring_buffer.c | 15 ++++++++++++--- 4 files changed, 20 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index e47d37d21d56..9a49505a8e06 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -650,7 +650,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs, - &signal, lock); + &signal, lock, channel->signal_policy); /* * Signalling the host is conditional on many factors: @@ -671,11 +671,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, * mechanism which can hurt the performance otherwise. */ - if (channel->signal_policy) - signal = true; - else - kick_q = true; - if (((ret == 0) && kick_q && signal) || (ret && !is_hvsock_channel(channel))) vmbus_setevent(channel); @@ -768,7 +763,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, - &signal, lock); + &signal, lock, channel->signal_policy); /* * Signalling the host is conditional on many factors: @@ -786,11 +781,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, * enough condition that it should not matter. */ - if (channel->signal_policy) - signal = true; - else - kick_q = true; - if (((ret == 0) && kick_q && signal) || (ret)) vmbus_setevent(channel); @@ -852,7 +842,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, - &signal, lock); + &signal, lock, channel->signal_policy); if (ret == 0 && signal) vmbus_setevent(channel); @@ -917,7 +907,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, - &signal, lock); + &signal, lock, channel->signal_policy); if (ret == 0 && signal) vmbus_setevent(channel); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 8818b92e5912..d8b64ba45b1d 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -426,6 +426,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) } dev_type = hv_get_dev_type(&newchannel->offermsg.offer.if_type); + if (dev_type == HV_NIC) + set_channel_signal_state(newchannel, HV_SIGNAL_POLICY_EXPLICIT); init_vp_index(newchannel, dev_type); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index dfa9fac100d8..ddcc3485520d 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -529,7 +529,8 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, struct kvec *kv_list, - u32 kv_count, bool *signal, bool lock); + u32 kv_count, bool *signal, bool lock, + enum hv_signal_policy policy); int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, u32 buflen, u32 *buffer_actual_len, diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index fe586bf74e17..e3edcaee7ab3 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -66,12 +66,20 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi) * arrived. */ -static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) +static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi, + enum hv_signal_policy policy) { virt_mb(); if (READ_ONCE(rbi->ring_buffer->interrupt_mask)) return false; + /* + * When the client wants to control signaling, + * we only honour the host interrupt mask. + */ + if (policy == HV_SIGNAL_POLICY_EXPLICIT) + return true; + /* check interrupt_mask before read_index */ virt_rmb(); /* @@ -264,7 +272,8 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) /* Write to the ring buffer. */ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, - struct kvec *kv_list, u32 kv_count, bool *signal, bool lock) + struct kvec *kv_list, u32 kv_count, bool *signal, bool lock, + enum hv_signal_policy policy) { int i = 0; u32 bytes_avail_towrite; @@ -326,7 +335,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, if (lock) spin_unlock_irqrestore(&outring_info->ring_lock, flags); - *signal = hv_need_to_signal(old_write, outring_info); + *signal = hv_need_to_signal(old_write, outring_info, policy); return 0; } -- cgit v1.2.3-58-ga151 From 8de0d7e951826d7592e0ba1da655b175c4aa0923 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 1 Jul 2016 16:26:36 -0700 Subject: Drivers: hv: vmbus: Reduce the delay between retries in vmbus_post_msg() The current delay between retries is unnecessarily high and is negatively affecting the time it takes to boot the system. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index fcf8a02dc0ea..78e6368a4423 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -439,7 +439,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) union hv_connection_id conn_id; int ret = 0; int retries = 0; - u32 msec = 1; + u32 usec = 1; conn_id.asu32 = 0; conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; @@ -472,9 +472,9 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - msleep(msec); - if (msec < 2048) - msec *= 2; + udelay(usec); + if (usec < 2048) + usec *= 2; } return ret; } -- cgit v1.2.3-58-ga151 From 3724287c0ec472815ebe5ae3790f77965c6aa557 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 1 Jul 2016 16:26:37 -0700 Subject: Drivers: hv: vmbus: Implement a mechanism to tag the channel for low latency On Hyper-V, performance critical channels use the monitor mechanism to signal the host when the guest posts mesages for the host. This mechanism minimizes the hypervisor intercepts and also makes the host more efficient in that each time the host is woken up, it processes a batch of messages as opposed to just one. The goal here is improve the throughput and this is at the expense of increased latency. Implement a mechanism to let the client driver decide if latency is important. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 7 ++++++- include/linux/hyperv.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9a49505a8e06..884cebf5f9b9 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -43,7 +43,12 @@ static void vmbus_setevent(struct vmbus_channel *channel) { struct hv_monitor_page *monitorpage; - if (channel->offermsg.monitor_allocated) { + /* + * For channels marked as in "low latency" mode + * bypass the monitor page mechanism. + */ + if ((channel->offermsg.monitor_allocated) && + (!channel->low_latency)) { /* Each u32 represents 32 channels */ sync_set_bit(channel->offermsg.child_relid & 31, (unsigned long *) vmbus_connection.send_int_page + diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 50f493eedeb5..755e8f566a47 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -850,6 +850,31 @@ struct vmbus_channel { * ring lock to preserve the current behavior. */ bool acquire_ring_lock; + /* + * For performance critical channels (storage, networking + * etc,), Hyper-V has a mechanism to enhance the throughput + * at the expense of latency: + * When the host is to be signaled, we just set a bit in a shared page + * and this bit will be inspected by the hypervisor within a certain + * window and if the bit is set, the host will be signaled. The window + * of time is the monitor latency - currently around 100 usecs. This + * mechanism improves throughput by: + * + * A) Making the host more efficient - each time it wakes up, + * potentially it will process morev number of packets. The + * monitor latency allows a batch to build up. + * B) By deferring the hypercall to signal, we will also minimize + * the interrupts. + * + * Clearly, these optimizations improve throughput at the expense of + * latency. Furthermore, since the channel is shared for both + * control and data messages, control messages currently suffer + * unnecessary latency adversley impacting performance and boot + * time. To fix this issue, permit tagging the channel as being + * in "low latency" mode. In this mode, we will bypass the monitor + * mechanism. + */ + bool low_latency; }; @@ -891,6 +916,16 @@ static inline void set_channel_pending_send_size(struct vmbus_channel *c, c->outbound.ring_buffer->pending_send_sz = size; } +static inline void set_low_latency_mode(struct vmbus_channel *c) +{ + c->low_latency = true; +} + +static inline void clear_low_latency_mode(struct vmbus_channel *c) +{ + c->low_latency = false; +} + void vmbus_onmessage(void *context); int vmbus_request_offers(void); -- cgit v1.2.3-58-ga151 From 7cf3b79ec85ee1a5bbaaf936bb1d050dc652983b Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 24 Aug 2016 16:23:09 -0700 Subject: Drivers: hv: balloon: keep track of where ha_region starts Windows 2012 (non-R2) does not specify hot add region in hot add requests and the logic in hot_add_req() is trying to find a 128Mb-aligned region covering the request. It may also happen that host's requests are not 128Mb aligned and the created ha_region will start before the first specified PFN. We can't online these non-present pages but we don't remember the real start of the region. This is a regression introduced by the commit 5abbbb75d733 ("Drivers: hv: hv_balloon: don't lose memory when onlining order is not natural"). While the idea of keeping the 'moving window' was wrong (as there is no guarantee that hot add requests come ordered) we should still keep track of covered_start_pfn. This is not a revert, the logic is different. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index df35fb7ed5df..4ae26d60565f 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -430,13 +430,14 @@ struct dm_info_msg { * currently hot added. We hot add in multiples of 128M * chunks; it is possible that we may not be able to bring * online all the pages in the region. The range - * covered_end_pfn defines the pages that can + * covered_start_pfn:covered_end_pfn defines the pages that can * be brough online. */ struct hv_hotadd_state { struct list_head list; unsigned long start_pfn; + unsigned long covered_start_pfn; unsigned long covered_end_pfn; unsigned long ha_end_pfn; unsigned long end_pfn; @@ -682,7 +683,8 @@ static void hv_online_page(struct page *pg) list_for_each(cur, &dm_device.ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); - cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn); + cur_start_pgp = (unsigned long) + pfn_to_page(has->covered_start_pfn); cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); if (((unsigned long)pg >= cur_start_pgp) && @@ -854,6 +856,7 @@ static unsigned long process_hot_add(unsigned long pg_start, list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; ha_region->ha_end_pfn = rg_start; + ha_region->covered_start_pfn = pg_start; ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; } -- cgit v1.2.3-58-ga151 From cb7a5724c7e1bfb5766ad1c3beba14cc715991cf Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 24 Aug 2016 16:23:10 -0700 Subject: Drivers: hv: balloon: account for gaps in hot add regions I'm observing the following hot add requests from the WS2012 host: hot_add_req: start_pfn = 0x108200 count = 330752 hot_add_req: start_pfn = 0x158e00 count = 193536 hot_add_req: start_pfn = 0x188400 count = 239616 As the host doesn't specify hot add regions we're trying to create 128Mb-aligned region covering the first request, we create the 0x108000 - 0x160000 region and we add 0x108000 - 0x158e00 memory. The second request passes the pfn_covered() check, we enlarge the region to 0x108000 - 0x190000 and add 0x158e00 - 0x188200 memory. The problem emerges with the third request as it starts at 0x188400 so there is a 0x200 gap which is not covered. As the end of our region is 0x190000 now it again passes the pfn_covered() check were we just adjust the covered_end_pfn and make it 0x188400 instead of 0x188200 which means that we'll try to online 0x188200-0x188400 pages but these pages were never assigned to us and we crash. We can't react to such requests by creating new hot add regions as it may happen that the whole suggested range falls into the previously identified 128Mb-aligned area so we'll end up adding nothing or create intersecting regions and our current logic doesn't allow that. Instead, create a list of such 'gaps' and check for them in the page online callback. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 131 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 4ae26d60565f..18766f616b8d 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -441,6 +441,16 @@ struct hv_hotadd_state { unsigned long covered_end_pfn; unsigned long ha_end_pfn; unsigned long end_pfn; + /* + * A list of gaps. + */ + struct list_head gap_list; +}; + +struct hv_hotadd_gap { + struct list_head list; + unsigned long start_pfn; + unsigned long end_pfn; }; struct balloon_state { @@ -596,18 +606,46 @@ static struct notifier_block hv_memory_nb = { .priority = 0 }; +/* Check if the particular page is backed and can be onlined and online it. */ +static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) +{ + unsigned long cur_start_pgp; + unsigned long cur_end_pgp; + struct hv_hotadd_gap *gap; + + cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); -static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) + /* The page is not backed. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + return; + + /* Check for gaps. */ + list_for_each_entry(gap, &has->gap_list, list) { + cur_start_pgp = (unsigned long) + pfn_to_page(gap->start_pfn); + cur_end_pgp = (unsigned long) + pfn_to_page(gap->end_pfn); + if (((unsigned long)pg >= cur_start_pgp) && + ((unsigned long)pg < cur_end_pgp)) { + return; + } + } + + /* This frame is currently backed; online the page. */ + __online_page_set_limits(pg); + __online_page_increment_counters(pg); + __online_page_free(pg); +} + +static void hv_bring_pgs_online(struct hv_hotadd_state *has, + unsigned long start_pfn, unsigned long size) { int i; - for (i = 0; i < size; i++) { - struct page *pg; - pg = pfn_to_page(start_pfn + i); - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + for (i = 0; i < size; i++) + hv_page_online_one(has, pfn_to_page(start_pfn + i)); } static void hv_mem_hot_add(unsigned long start, unsigned long size, @@ -684,26 +722,24 @@ static void hv_online_page(struct page *pg) list_for_each(cur, &dm_device.ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); cur_start_pgp = (unsigned long) - pfn_to_page(has->covered_start_pfn); - cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + pfn_to_page(has->start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn); - if (((unsigned long)pg >= cur_start_pgp) && - ((unsigned long)pg < cur_end_pgp)) { - /* - * This frame is currently backed; online the - * page. - */ - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + /* The page belongs to a different HAS. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + continue; + + hv_page_online_one(has, pg); + break; } } -static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) +static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) { struct list_head *cur; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap; unsigned long residual, new_inc; if (list_empty(&dm_device.ha_region_list)) @@ -718,6 +754,24 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) */ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; + + /* + * If the current start pfn is not where the covered_end + * is, create a gap and update covered_end_pfn. + */ + if (has->covered_end_pfn != start_pfn) { + gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC); + if (!gap) + return -ENOMEM; + + INIT_LIST_HEAD(&gap->list); + gap->start_pfn = has->covered_end_pfn; + gap->end_pfn = start_pfn; + list_add_tail(&gap->list, &has->gap_list); + + has->covered_end_pfn = start_pfn; + } + /* * If the current hot add-request extends beyond * our current limit; extend it. @@ -734,19 +788,10 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) has->end_pfn += new_inc; } - /* - * If the current start pfn is not where the covered_end - * is, update it. - */ - - if (has->covered_end_pfn != start_pfn) - has->covered_end_pfn = start_pfn; - - return true; - + return 1; } - return false; + return 0; } static unsigned long handle_pg_range(unsigned long pg_start, @@ -785,6 +830,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, if (pgs_ol > pfn_cnt) pgs_ol = pfn_cnt; + has->covered_end_pfn += pgs_ol; + pfn_cnt -= pgs_ol; /* * Check if the corresponding memory block is already * online by checking its last previously backed page. @@ -793,10 +840,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, */ if (start_pfn > has->start_pfn && !PageReserved(pfn_to_page(start_pfn - 1))) - hv_bring_pgs_online(start_pfn, pgs_ol); + hv_bring_pgs_online(has, start_pfn, pgs_ol); - has->covered_end_pfn += pgs_ol; - pfn_cnt -= pgs_ol; } if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { @@ -834,13 +879,19 @@ static unsigned long process_hot_add(unsigned long pg_start, unsigned long rg_size) { struct hv_hotadd_state *ha_region = NULL; + int covered; if (pfn_cnt == 0) return 0; - if (!dm_device.host_specified_ha_region) - if (pfn_covered(pg_start, pfn_cnt)) + if (!dm_device.host_specified_ha_region) { + covered = pfn_covered(pg_start, pfn_cnt); + if (covered < 0) + return 0; + + if (covered) goto do_pg_range; + } /* * If the host has specified a hot-add range; deal with it first. @@ -852,6 +903,7 @@ static unsigned long process_hot_add(unsigned long pg_start, return 0; INIT_LIST_HEAD(&ha_region->list); + INIT_LIST_HEAD(&ha_region->gap_list); list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; @@ -1585,6 +1637,7 @@ static int balloon_remove(struct hv_device *dev) struct hv_dynmem_device *dm = hv_get_drvdata(dev); struct list_head *cur, *tmp; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap, *tmp_gap; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); @@ -1601,6 +1654,10 @@ static int balloon_remove(struct hv_device *dev) #endif list_for_each_safe(cur, tmp, &dm->ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); + list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) { + list_del(&gap->list); + kfree(gap); + } list_del(&has->list); kfree(has); } -- cgit v1.2.3-58-ga151 From a132c54cbcb8c239a64370b9cd9468908398bc6e Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 24 Aug 2016 16:23:11 -0700 Subject: Drivers: hv: balloon: don't wait for ol_waitevent when memhp_auto_online is enabled With the recently introduced in-kernel memory onlining (MEMORY_HOTPLUG_DEFAULT_ONLINE) these is no point in waiting for pages to come online in the driver and we can get rid of the waiting. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 18766f616b8d..34413261d189 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -673,7 +673,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, has->covered_end_pfn += processed_pfn; init_completion(&dm_device.ol_waitevent); - dm_device.ha_waiting = true; + dm_device.ha_waiting = !memhp_auto_online; mutex_unlock(&dm_device.ha_region_mutex); nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); @@ -699,12 +699,15 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, } /* - * Wait for the memory block to be onlined. - * Since the hot add has succeeded, it is ok to - * proceed even if the pages in the hot added region - * have not been "onlined" within the allowed time. + * Wait for the memory block to be onlined when memory onlining + * is done outside of kernel (memhp_auto_online). Since the hot + * add has succeeded, it is ok to proceed even if the pages in + * the hot added region have not been "onlined" within the + * allowed time. */ - wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); + if (dm_device.ha_waiting) + wait_for_completion_timeout(&dm_device.ol_waitevent, + 5*HZ); mutex_lock(&dm_device.ha_region_mutex); post_status(&dm_device); } -- cgit v1.2.3-58-ga151 From eece30b9f0046cee810a2c7caa2247f3f8dc85e2 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 24 Aug 2016 16:23:12 -0700 Subject: Drivers: hv: balloon: replace ha_region_mutex with spinlock lockdep reports possible circular locking dependency when udev is used for memory onlining: systemd-udevd/3996 is trying to acquire lock: ((memory_chain).rwsem){++++.+}, at: [] __blocking_notifier_call_chain+0x4e/0xc0 but task is already holding lock: (&dm_device.ha_region_mutex){+.+.+.}, at: [] hv_memory_notifier+0x5e/0xc0 [hv_balloon] ... which is probably a false positive because we take and release ha_region_mutex from memory notifier chain depending on the arg. No real deadlocks were reported so far (though I'm not really sure about preemptible kernels...) but we don't really need to hold the mutex for so long. We use it to protect ha_region_list (and its members) and the num_pages_onlined counter. None of these operations require us to sleep and nothing is slow, switch to using spinlock with interrupts disabled. While on it, replace list_for_each -> list_for_each_entry as we actually need entries in all these cases, drop meaningless list_empty() checks. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 98 ++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 34413261d189..d55e0e7650bf 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -547,7 +547,11 @@ struct hv_dynmem_device { */ struct task_struct *thread; - struct mutex ha_region_mutex; + /* + * Protects ha_region_list, num_pages_onlined counter and individual + * regions from ha_region_list. + */ + spinlock_t ha_lock; /* * A list of hot-add regions. @@ -571,18 +575,14 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, void *v) { struct memory_notify *mem = (struct memory_notify *)v; + unsigned long flags; switch (val) { - case MEM_GOING_ONLINE: - mutex_lock(&dm_device.ha_region_mutex); - break; - case MEM_ONLINE: + spin_lock_irqsave(&dm_device.ha_lock, flags); dm_device.num_pages_onlined += mem->nr_pages; + spin_unlock_irqrestore(&dm_device.ha_lock, flags); case MEM_CANCEL_ONLINE: - if (val == MEM_ONLINE || - mutex_is_locked(&dm_device.ha_region_mutex)) - mutex_unlock(&dm_device.ha_region_mutex); if (dm_device.ha_waiting) { dm_device.ha_waiting = false; complete(&dm_device.ol_waitevent); @@ -590,10 +590,11 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, break; case MEM_OFFLINE: - mutex_lock(&dm_device.ha_region_mutex); + spin_lock_irqsave(&dm_device.ha_lock, flags); dm_device.num_pages_onlined -= mem->nr_pages; - mutex_unlock(&dm_device.ha_region_mutex); + spin_unlock_irqrestore(&dm_device.ha_lock, flags); break; + case MEM_GOING_ONLINE: case MEM_GOING_OFFLINE: case MEM_CANCEL_OFFLINE: break; @@ -657,9 +658,12 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, unsigned long start_pfn; unsigned long processed_pfn; unsigned long total_pfn = pfn_count; + unsigned long flags; for (i = 0; i < (size/HA_CHUNK); i++) { start_pfn = start + (i * HA_CHUNK); + + spin_lock_irqsave(&dm_device.ha_lock, flags); has->ha_end_pfn += HA_CHUNK; if (total_pfn > HA_CHUNK) { @@ -671,11 +675,11 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, } has->covered_end_pfn += processed_pfn; + spin_unlock_irqrestore(&dm_device.ha_lock, flags); init_completion(&dm_device.ol_waitevent); dm_device.ha_waiting = !memhp_auto_online; - mutex_unlock(&dm_device.ha_region_mutex); nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); ret = add_memory(nid, PFN_PHYS((start_pfn)), (HA_CHUNK << PAGE_SHIFT)); @@ -692,9 +696,10 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, */ do_hot_add = false; } + spin_lock_irqsave(&dm_device.ha_lock, flags); has->ha_end_pfn -= HA_CHUNK; has->covered_end_pfn -= processed_pfn; - mutex_lock(&dm_device.ha_region_mutex); + spin_unlock_irqrestore(&dm_device.ha_lock, flags); break; } @@ -708,7 +713,6 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, if (dm_device.ha_waiting) wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); - mutex_lock(&dm_device.ha_region_mutex); post_status(&dm_device); } @@ -717,13 +721,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, static void hv_online_page(struct page *pg) { - struct list_head *cur; struct hv_hotadd_state *has; unsigned long cur_start_pgp; unsigned long cur_end_pgp; + unsigned long flags; - list_for_each(cur, &dm_device.ha_region_list) { - has = list_entry(cur, struct hv_hotadd_state, list); + spin_lock_irqsave(&dm_device.ha_lock, flags); + list_for_each_entry(has, &dm_device.ha_region_list, list) { cur_start_pgp = (unsigned long) pfn_to_page(has->start_pfn); cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn); @@ -736,21 +740,19 @@ static void hv_online_page(struct page *pg) hv_page_online_one(has, pg); break; } + spin_unlock_irqrestore(&dm_device.ha_lock, flags); } static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) { - struct list_head *cur; struct hv_hotadd_state *has; struct hv_hotadd_gap *gap; unsigned long residual, new_inc; + int ret = 0; + unsigned long flags; - if (list_empty(&dm_device.ha_region_list)) - return false; - - list_for_each(cur, &dm_device.ha_region_list) { - has = list_entry(cur, struct hv_hotadd_state, list); - + spin_lock_irqsave(&dm_device.ha_lock, flags); + list_for_each_entry(has, &dm_device.ha_region_list, list) { /* * If the pfn range we are dealing with is not in the current * "hot add block", move on. @@ -764,8 +766,10 @@ static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) */ if (has->covered_end_pfn != start_pfn) { gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC); - if (!gap) - return -ENOMEM; + if (!gap) { + ret = -ENOMEM; + break; + } INIT_LIST_HEAD(&gap->list); gap->start_pfn = has->covered_end_pfn; @@ -791,10 +795,12 @@ static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) has->end_pfn += new_inc; } - return 1; + ret = 1; + break; } + spin_unlock_irqrestore(&dm_device.ha_lock, flags); - return 0; + return ret; } static unsigned long handle_pg_range(unsigned long pg_start, @@ -803,17 +809,13 @@ static unsigned long handle_pg_range(unsigned long pg_start, unsigned long start_pfn = pg_start; unsigned long pfn_cnt = pg_count; unsigned long size; - struct list_head *cur; struct hv_hotadd_state *has; unsigned long pgs_ol = 0; unsigned long old_covered_state; + unsigned long res = 0, flags; - if (list_empty(&dm_device.ha_region_list)) - return 0; - - list_for_each(cur, &dm_device.ha_region_list) { - has = list_entry(cur, struct hv_hotadd_state, list); - + spin_lock_irqsave(&dm_device.ha_lock, flags); + list_for_each_entry(has, &dm_device.ha_region_list, list) { /* * If the pfn range we are dealing with is not in the current * "hot add block", move on. @@ -863,17 +865,20 @@ static unsigned long handle_pg_range(unsigned long pg_start, } else { pfn_cnt = size; } + spin_unlock_irqrestore(&dm_device.ha_lock, flags); hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has); + spin_lock_irqsave(&dm_device.ha_lock, flags); } /* * If we managed to online any pages that were given to us, * we declare success. */ - return has->covered_end_pfn - old_covered_state; - + res = has->covered_end_pfn - old_covered_state; + break; } + spin_unlock_irqrestore(&dm_device.ha_lock, flags); - return 0; + return res; } static unsigned long process_hot_add(unsigned long pg_start, @@ -883,6 +888,7 @@ static unsigned long process_hot_add(unsigned long pg_start, { struct hv_hotadd_state *ha_region = NULL; int covered; + unsigned long flags; if (pfn_cnt == 0) return 0; @@ -908,12 +914,15 @@ static unsigned long process_hot_add(unsigned long pg_start, INIT_LIST_HEAD(&ha_region->list); INIT_LIST_HEAD(&ha_region->gap_list); - list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; ha_region->ha_end_pfn = rg_start; ha_region->covered_start_pfn = pg_start; ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; + + spin_lock_irqsave(&dm_device.ha_lock, flags); + list_add_tail(&ha_region->list, &dm_device.ha_region_list); + spin_unlock_irqrestore(&dm_device.ha_lock, flags); } do_pg_range: @@ -940,7 +949,6 @@ static void hot_add_req(struct work_struct *dummy) resp.hdr.size = sizeof(struct dm_hot_add_response); #ifdef CONFIG_MEMORY_HOTPLUG - mutex_lock(&dm_device.ha_region_mutex); pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt; @@ -974,7 +982,6 @@ static void hot_add_req(struct work_struct *dummy) rg_start, rg_sz); dm->num_pages_added += resp.page_count; - mutex_unlock(&dm_device.ha_region_mutex); #endif /* * The result field of the response structure has the @@ -1519,7 +1526,7 @@ static int balloon_probe(struct hv_device *dev, init_completion(&dm_device.host_event); init_completion(&dm_device.config_event); INIT_LIST_HEAD(&dm_device.ha_region_list); - mutex_init(&dm_device.ha_region_mutex); + spin_lock_init(&dm_device.ha_lock); INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); dm_device.host_specified_ha_region = false; @@ -1638,9 +1645,9 @@ probe_error0: static int balloon_remove(struct hv_device *dev) { struct hv_dynmem_device *dm = hv_get_drvdata(dev); - struct list_head *cur, *tmp; - struct hv_hotadd_state *has; + struct hv_hotadd_state *has, *tmp; struct hv_hotadd_gap *gap, *tmp_gap; + unsigned long flags; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); @@ -1655,8 +1662,8 @@ static int balloon_remove(struct hv_device *dev) restore_online_page_callback(&hv_online_page); unregister_memory_notifier(&hv_memory_nb); #endif - list_for_each_safe(cur, tmp, &dm->ha_region_list) { - has = list_entry(cur, struct hv_hotadd_state, list); + spin_lock_irqsave(&dm_device.ha_lock, flags); + list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) { list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) { list_del(&gap->list); kfree(gap); @@ -1664,6 +1671,7 @@ static int balloon_remove(struct hv_device *dev) list_del(&has->list); kfree(has); } + spin_unlock_irqrestore(&dm_device.ha_lock, flags); return 0; } -- cgit v1.2.3-58-ga151 From b605c2d913589c448d4a6887262bb8e99da12009 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Wed, 24 Aug 2016 16:23:13 -0700 Subject: Drivers: hv: balloon: Use available memory value in pressure report Reports for available memory should use the si_mem_available() value. The previous freeram value does not include available page cache memory. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index d55e0e7650bf..fdf8da929cbe 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1075,7 +1075,6 @@ static unsigned long compute_balloon_floor(void) static void post_status(struct hv_dynmem_device *dm) { struct dm_status status; - struct sysinfo val; unsigned long now = jiffies; unsigned long last_post = last_post_time; @@ -1087,7 +1086,6 @@ static void post_status(struct hv_dynmem_device *dm) if (!time_after(now, (last_post_time + HZ))) return; - si_meminfo(&val); memset(&status, 0, sizeof(struct dm_status)); status.hdr.type = DM_STATUS_REPORT; status.hdr.size = sizeof(struct dm_status); @@ -1103,7 +1101,7 @@ static void post_status(struct hv_dynmem_device *dm) * num_pages_onlined) as committed to the host, otherwise it can try * asking us to balloon them out. */ - status.num_avail = val.freeram; + status.num_avail = si_mem_available(); status.num_committed = vm_memory_committed() + dm->num_pages_ballooned + (dm->num_pages_added > dm->num_pages_onlined ? @@ -1209,7 +1207,7 @@ static void balloon_up(struct work_struct *dummy) int ret; bool done = false; int i; - struct sysinfo val; + long avail_pages; unsigned long floor; /* The host balloons pages in 2M granularity. */ @@ -1221,12 +1219,12 @@ static void balloon_up(struct work_struct *dummy) */ alloc_unit = 512; - si_meminfo(&val); + avail_pages = si_mem_available(); floor = compute_balloon_floor(); /* Refuse to balloon below the floor, keep the 2M granularity. */ - if (val.freeram < num_pages || val.freeram - num_pages < floor) { - num_pages = val.freeram > floor ? (val.freeram - floor) : 0; + if (avail_pages < num_pages || avail_pages - num_pages < floor) { + num_pages = avail_pages > floor ? (avail_pages - floor) : 0; num_pages -= num_pages % PAGES_IN_2M; } @@ -1237,7 +1235,6 @@ static void balloon_up(struct work_struct *dummy) bl_resp->hdr.size = sizeof(struct dm_balloon_response); bl_resp->more_pages = 1; - num_pages -= num_ballooned; num_ballooned = alloc_balloon_pages(&dm_device, num_pages, bl_resp, alloc_unit); -- cgit v1.2.3-58-ga151 From b8392153721c83de4e5d47ee455bbfefd0c8259e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 25 Aug 2016 15:18:51 -0600 Subject: coresight: access conn->child_name only if it's initialised If the addition of the coresight devices get deferred, then there's a window before child_name is populated by of_get_coresight_platform_data from the respective component driver's probe and the attempted to access the same from coresight_orphan_match resulting in kernel NULL pointer dereference as below: Unable to handle kernel NULL pointer dereference at virtual address 0x0 Internal error: Oops: 96000004 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1038 Comm: kworker/0:1 Not tainted 4.7.0-rc3 #124 Hardware name: ARM Juno development board (r2) (DT) Workqueue: events amba_deferred_retry_func PC is at strcmp+0x1c/0x160 LR is at coresight_orphan_match+0x7c/0xd0 Call trace: strcmp+0x1c/0x160 bus_for_each_dev+0x60/0xa0 coresight_register+0x264/0x2e0 tmc_probe+0x130/0x310 amba_probe+0xd4/0x1c8 driver_probe_device+0x22c/0x418 __device_attach_driver+0xbc/0x158 bus_for_each_drv+0x58/0x98 __device_attach+0xc4/0x160 device_initial_probe+0x10/0x18 bus_probe_device+0x94/0xa0 device_add+0x344/0x580 amba_device_try_add+0x194/0x238 amba_deferred_retry_func+0x48/0xd0 process_one_work+0x118/0x378 worker_thread+0x48/0x498 kthread+0xd0/0xe8 ret_from_fork+0x10/0x40 This patch adds a check for non-NULL conn->child_name before accessing the same. Cc: Mathieu Poirier Signed-off-by: Sudeep Holla Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index d08d1ab9bba5..ceeaaea41ed6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -725,7 +725,8 @@ static int coresight_orphan_match(struct device *dev, void *data) /* We have found at least one orphan connection */ if (conn->child_dev == NULL) { /* Does it match this newly added device? */ - if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { + if (conn->child_name && + !strcmp(dev_name(&csdev->dev), conn->child_name)) { conn->child_dev = csdev; } else { /* This component still has an orphan */ -- cgit v1.2.3-58-ga151 From f3864d85624a80222c8e01caa112554c998d922b Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 25 Aug 2016 15:18:52 -0600 Subject: coresight-stm: support mmapping channel regions with mmio_addr CoreSight STM device allows direct mapping of the channel regions to userspace for zero-copy writing. To support this ability, the STM framework has provided a hook 'mmio_addr', this patch just implemented this hook for CoreSight STM. This patch also added an item into 'channel_space' to save the physical base address of channel region which mmap operation needs to know. Signed-off-by: Chunyan Zhang Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-stm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 73be58a11e4f..482c8bb27f87 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -105,10 +105,12 @@ module_param_named( /** * struct channel_space - central management entity for extended ports * @base: memory mapped base address where channels start. + * @phys: physical base address of channel region. * @guaraneed: is the channel delivery guaranteed. */ struct channel_space { void __iomem *base; + phys_addr_t phys; unsigned long *guaranteed; }; @@ -356,6 +358,23 @@ static void stm_generic_unlink(struct stm_data *stm_data, stm_disable(drvdata->csdev); } +static phys_addr_t +stm_mmio_addr(struct stm_data *stm_data, unsigned int master, + unsigned int channel, unsigned int nr_chans) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + phys_addr_t addr; + + addr = drvdata->chs.phys + channel * BYTES_PER_CHANNEL; + + if (offset_in_page(addr) || + offset_in_page(nr_chans * BYTES_PER_CHANNEL)) + return 0; + + return addr; +} + static long stm_generic_set_options(struct stm_data *stm_data, unsigned int master, unsigned int channel, @@ -761,7 +780,9 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata) drvdata->stm.sw_end = 1; drvdata->stm.hw_override = true; drvdata->stm.sw_nchannels = drvdata->numsp; + drvdata->stm.sw_mmiosz = BYTES_PER_CHANNEL; drvdata->stm.packet = stm_generic_packet; + drvdata->stm.mmio_addr = stm_mmio_addr; drvdata->stm.link = stm_generic_link; drvdata->stm.unlink = stm_generic_unlink; drvdata->stm.set_options = stm_generic_set_options; @@ -808,6 +829,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); if (ret) return ret; + drvdata->chs.phys = ch_res.start; base = devm_ioremap_resource(dev, &ch_res); if (IS_ERR(base)) -- cgit v1.2.3-58-ga151 From b1149ad917b7808ba0023fd80a6631f10b1a14f2 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 25 Aug 2016 15:18:53 -0600 Subject: coresight: always use stashed trace id value in etm4_trace_id etm4_trace_id is not guaranteed to be executed on the CPU whose ETM is being accessed. This leads to exception similar to below one if the CPU whose ETM is being accessed is in deeper idle states. So it must be executed on the CPU whose ETM is being accessed. Unhandled fault: synchronous external abort (0x96000210) at 0xffff000008db4040 Internal error: : 96000210 [#1] PREEMPT SMP Modules linked in: CPU: 5 PID: 5979 Comm: etm.sh Not tainted 4.7.0-rc3 #159 Hardware name: ARM Juno development board (r2) (DT) task: ffff80096dd34b00 ti: ffff80096dfe4000 task.ti: ffff80096dfe4000 PC is at etm4_trace_id+0x5c/0x90 LR is at etm4_trace_id+0x3c/0x90 Call trace: etm4_trace_id+0x5c/0x90 coresight_id_match+0x78/0xa8 bus_for_each_dev+0x60/0xa0 coresight_enable+0xc0/0x1b8 enable_source_store+0x3c/0x70 dev_attr_store+0x18/0x28 sysfs_kf_write+0x48/0x58 kernfs_fop_write+0x14c/0x1e0 __vfs_write+0x1c/0x100 vfs_write+0xa0/0x1b8 SyS_write+0x44/0xa0 el0_svc_naked+0x24/0x28 However, TRCTRACEIDR is not guaranteed to hold the previous programmed trace id if it enters deeper idle states. Further, the trace id that is computed in etm4_init_trace_id is programmed into TRCTRACEIDR only in etm4_enable_hw which happens much later in the sequence after coresight_id_match is executed from enable_source_store. This patch simplifies etm4_trace_id by returning the stashed trace id value similar to etm4_cpu_id. Cc: Mathieu Poirier Signed-off-by: Sudeep Holla Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1a5e0d14c1dd..17e2e81d2e90 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -79,22 +79,8 @@ static int etm4_cpu_id(struct coresight_device *csdev) static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - unsigned long flags; - int trace_id = -1; - if (!local_read(&drvdata->mode)) - return drvdata->trcid; - - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR); - trace_id &= ETM_TRACEID_MASK; - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - return trace_id; + return drvdata->trcid; } static void etm4_enable_hw(void *info) -- cgit v1.2.3-58-ga151 From 481e46fe7a88557b66330cbb047b25cc13eff4b9 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:54 -0600 Subject: coresight: Remove erroneous dma_free_coherent in tmc_probe commit de5461970b3e9e194 ("coresight: tmc: allocating memory when needed") removed the static allocation of buffer for the trace data in ETR mode in tmc_probe. However it failed to remove the "devm_free_coherent" in tmc_probe when the probe fails due to other reasons. This patch gets rid of the incorrect dma_free_coherent() call. Fixes: commit de5461970b3e9e194 ("coresight: tmc: allocating memory when needed") Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 9e02ac963cd0..3978cbb6b038 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -388,9 +388,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) err_misc_register: coresight_unregister(drvdata->csdev); err_devm_kzalloc: - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) - dma_free_coherent(dev, drvdata->size, - drvdata->vaddr, drvdata->paddr); return ret; } -- cgit v1.2.3-58-ga151 From 3afd0634a2aca32466617b3bc1075c127f75d776 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:55 -0600 Subject: coresight: Consolidate error handling path for tmc_probe This patch cleans up the error handling path for tmc_probe as a side effect of the removal of the spurious dma_free_coherent(). Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc.c | 36 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3978cbb6b038..1b47258e01c9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -309,22 +309,31 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) if (np) { pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto out; + } adev->dev.platform_data = pdata; } + ret = -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) - return -ENOMEM; + goto out; + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + goto out; drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); /* Validity for the resource is already checked by the AMBA core */ base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto out; + } drvdata->base = base; @@ -347,12 +356,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) { - ret = -ENOMEM; - goto err_devm_kzalloc; - } - desc->pdata = pdata; desc->dev = dev; desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; @@ -373,7 +376,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->csdev = coresight_register(desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); - goto err_devm_kzalloc; + goto out; } drvdata->miscdev.name = pdata->name; @@ -381,13 +384,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->miscdev.fops = &tmc_fops; ret = misc_register(&drvdata->miscdev); if (ret) - goto err_misc_register; - - return 0; - -err_misc_register: - coresight_unregister(drvdata->csdev); -err_devm_kzalloc: + coresight_unregister(drvdata->csdev); +out: return ret; } -- cgit v1.2.3-58-ga151 From 068c0a542f6edce90f7d8a9b35a849d990001018 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:56 -0600 Subject: coresight: Fix csdev connections initialisation This is a cleanup patch. coresight_device->conns holds an array to point to the devices connected to the OUT ports of a component. Sinks, e.g ETR, do not have an OUT port (nr_outport = 0), as it streams the trace to memory via AXI. At coresight_register() we do : conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); if (!conns) { ret = -ENOMEM; goto err_kzalloc_conns; } For ETR, since the total size requested for kcalloc is zero, the return value is, ZERO_SIZE_PTR ( != NULL). Hence, csdev->conns = ZERO_SIZE_PTR which cannot be verified later to contain a valid pointer. The code which accesses the csdev->conns is bounded by the csdev->nr_outport check, hence we don't try to dereference the ZERO_SIZE_PTR. This patch cleans up the csdev->conns initialisation to make sure we initialise it properly(i.e, either NULL or valid conns array). Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index ceeaaea41ed6..bb20ee9747e1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -894,7 +894,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) int nr_refcnts = 1; atomic_t *refcnts = NULL; struct coresight_device *csdev; - struct coresight_connection *conns; + struct coresight_connection *conns = NULL; csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); if (!csdev) { @@ -922,16 +922,20 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->nr_inport = desc->pdata->nr_inport; csdev->nr_outport = desc->pdata->nr_outport; - conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); - if (!conns) { - ret = -ENOMEM; - goto err_kzalloc_conns; - } - for (i = 0; i < csdev->nr_outport; i++) { - conns[i].outport = desc->pdata->outports[i]; - conns[i].child_name = desc->pdata->child_names[i]; - conns[i].child_port = desc->pdata->child_ports[i]; + /* Initialise connections if there is at least one outport */ + if (csdev->nr_outport) { + conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); + if (!conns) { + ret = -ENOMEM; + goto err_kzalloc_conns; + } + + for (i = 0; i < csdev->nr_outport; i++) { + conns[i].outport = desc->pdata->outports[i]; + conns[i].child_name = desc->pdata->child_names[i]; + conns[i].child_port = desc->pdata->child_ports[i]; + } } csdev->conns = conns; -- cgit v1.2.3-58-ga151 From 8505feaed9246791e94c30e8bf52fa4c3ef2e7af Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:57 -0600 Subject: coresight: tmc: Limit the trace to available data At present the ETF or ETR gives out the entire device buffer, even if there is less or even no trace data available. This patch limits the trace data given out to the actual trace data collected. Cc: mathieu.poirier@linaro.org Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 2 ++ drivers/hwtracing/coresight/coresight-tmc-etr.c | 12 +++++++++--- drivers/hwtracing/coresight/coresight-tmc.c | 6 +++--- drivers/hwtracing/coresight/coresight-tmc.h | 4 +++- 4 files changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 466af86fd76f..e68289b8c072 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -48,6 +48,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) int i; bufp = drvdata->buf; + drvdata->len = 0; while (1) { for (i = 0; i < drvdata->memwidth; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); @@ -55,6 +56,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) return; memcpy(bufp, &read_data, 4); bufp += 4; + drvdata->len += 4; } } } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 688be9e060fc..03f36cb8b0c8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -64,11 +64,17 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) rwp = readl_relaxed(drvdata->base + TMC_RWP); val = readl_relaxed(drvdata->base + TMC_STS); - /* How much memory do we still have */ - if (val & BIT(0)) + /* + * Adjust the buffer to point to the beginning of the trace data + * and update the available trace data. + */ + if (val & BIT(0)) { drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; - else + drvdata->len = drvdata->size; + } else { drvdata->buf = drvdata->vaddr; + drvdata->len = rwp - drvdata->paddr; + } } static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 1b47258e01c9..b3275bb4d035 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -140,8 +140,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, struct tmc_drvdata, miscdev); char *bufp = drvdata->buf + *ppos; - if (*ppos + len > drvdata->size) - len = drvdata->size - *ppos; + if (*ppos + len > drvdata->len) + len = drvdata->len - *ppos; if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (bufp == (char *)(drvdata->vaddr + drvdata->size)) @@ -160,7 +160,7 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, *ppos += len; dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", - __func__, len, (int)(drvdata->size - *ppos)); + __func__, len, (int)(drvdata->len - *ppos)); return len; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 5c5fe2ad2ca7..44b3ae346118 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -98,7 +98,8 @@ enum tmc_mem_intf_width { * @buf: area of memory where trace data get sent. * @paddr: DMA start location in RAM. * @vaddr: virtual representation of @paddr. - * @size: @buf size. + * @size: trace buffer size. + * @len: size of the available trace. * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. @@ -115,6 +116,7 @@ struct tmc_drvdata { dma_addr_t paddr; void __iomem *vaddr; u32 size; + u32 len; local_t mode; enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; -- cgit v1.2.3-58-ga151 From 78247e25e89b135e8401767e20fa7c814a17479a Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:58 -0600 Subject: coresight: etmv4: Fix ETMv4x peripheral ID table This patch cleans up the peripheral id table for different ETMv4 implementations. As per Cortex-A53 TRM, the ETM has following id values: Peripheral ID0 0x5D 0xFE0 Peripheral ID1 0xB9 0xFE4 Peripheral ID2 0x4B 0xFE8 Peripheral ID3 0x00 0xFEC where, PID2: has the following format: [7:4] Revision [3] JEDEC 0b1 res1. Indicates a JEP106 identity code is used [2:0] DES_1 0b011 ARM Limited. This is bits[6:4] of JEP106 ID code The existing table entry checks only the bits [1:0], which is not sufficient enough. Fix it to match bits [3:0], just like the other entries do. While at it, correct the comment for A57 and the A53 entry. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 17e2e81d2e90..bc5ed207a3b5 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -812,12 +812,12 @@ err_arch_supported: } static struct amba_id etm4_ids[] = { - { /* ETM 4.0 - Qualcomm */ - .id = 0x0003b95d, - .mask = 0x0003ffff, + { /* ETM 4.0 - Cortex-A53 */ + .id = 0x000bb95d, + .mask = 0x000fffff, .data = "ETM 4.0", }, - { /* ETM 4.0 - Juno board */ + { /* ETM 4.0 - Cortex-A57 */ .id = 0x000bb95e, .mask = 0x000fffff, .data = "ETM 4.0", -- cgit v1.2.3-58-ga151 From 1c9cbe118413dbb869d146c45932ba092a8ff485 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:18:59 -0600 Subject: coresight: Cleanup TMC status check Use the defined symbol rather than hardcoding the value to check whether the TMC buffer is full. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 03f36cb8b0c8..6d7de0309e94 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -68,7 +68,7 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) * Adjust the buffer to point to the beginning of the trace data * and update the available trace data. */ - if (val & BIT(0)) { + if (val & TMC_STS_FULL) { drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; drvdata->len = drvdata->size; } else { -- cgit v1.2.3-58-ga151 From 67337e8d8dc44ceb720a1ba5d1f2ff230a53a888 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:19:00 -0600 Subject: coresight: Add better messages for coresight_timeout When we encounter a timeout waiting for a status change via coresight_timeout, the caller always print the offset which was tried. This is pretty much useless as it doesn't specify the bit position we wait for. Also, one needs to lookup the TRM to figure out, what was wrong. This patch changes all such error messages to print something more meaningful. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 6 ++---- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ++---- drivers/hwtracing/coresight/coresight-tmc.c | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 4d20b0be0c0b..3b483e3f00ee 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -184,8 +184,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata) if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) { dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - ETB_FFCR); + "timeout while waiting for completion of Manual Flush\n"); } /* disable trace capture */ @@ -193,8 +192,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata) if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) { dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - ETB_FFCR); + "timeout while waiting for Formatter to Stop\n"); } CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index bc5ed207a3b5..3b16f8a9e44d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -99,8 +99,7 @@ static void etm4_enable_hw(void *info) /* wait for TRCSTATR.IDLE to go up */ if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - TRCSTATR); + "timeout while waiting for Idle Trace Status\n"); writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR); @@ -172,8 +171,7 @@ static void etm4_enable_hw(void *info) /* wait for TRCSTATR.IDLE to go back down to '0' */ if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0)) dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - TRCSTATR); + "timeout while waiting for Idle Trace Status\n"); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index b3275bb4d035..84052c72462c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -38,8 +38,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) if (coresight_timeout(drvdata->base, TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - TMC_STS); + "timeout while waiting for TMC to be Ready\n"); } } @@ -56,8 +55,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata) if (coresight_timeout(drvdata->base, TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { dev_err(drvdata->dev, - "timeout observed when probing at offset %#x\n", - TMC_FFCR); + "timeout while waiting for completion of Manual Flush\n"); } tmc_wait_for_tmcready(drvdata); -- cgit v1.2.3-58-ga151 From 62d39ac934daaaecc716d651809b18da8ac255b0 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:01 -0600 Subject: coresight: delay initialisation when children are missing Depending on when CoreSight device are discovered it is possible that some IP block may be referencing devices that have not been added to the bus yet. The end result is missing nodes in the CoreSight topology even when the devices are present and properly initialised. This patch solves the problem by asking the driver core to try initialising the device at a later time when the children of a CoreSight node are missing. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index b68da1888fd5..18f1c8c4776b 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -166,7 +166,7 @@ struct coresight_platform_data *of_get_coresight_platform_data( rdev = of_coresight_get_endpoint_device(rparent); if (!rdev) - continue; + return ERR_PTR(-EPROBE_DEFER); pdata->child_names[i] = dev_name(rdev); pdata->child_ports[i] = rendpoint.id; -- cgit v1.2.3-58-ga151 From cd9e3474bb793dc93fbe0985f822ad4b2605e847 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:02 -0600 Subject: coresight: add PM runtime calls to coresight_simple_func() It is mandatory to enable a coresight block's power domain before trying to access management registers. Otherwise the transaction simply stalls, leading to a system hang. Signed-off-by: Mathieu Poirier Reviewed-by: Sudeep Holla Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-priv.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ad975c58080d..decfd52b5dc3 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -16,6 +16,7 @@ #include #include #include +#include /* * Coresight management registers (0xf00-0xfcc) @@ -42,8 +43,11 @@ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ type *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ + u32 val; \ + pm_runtime_get_sync(_dev->parent); \ + val = readl_relaxed(drvdata->base + offset); \ + pm_runtime_put_sync(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \ } \ static DEVICE_ATTR_RO(name) -- cgit v1.2.3-58-ga151 From 2a18d7267a566b6be623893b5b5147ae5125d2ca Mon Sep 17 00:00:00 2001 From: Olivier Schonken Date: Thu, 25 Aug 2016 15:19:03 -0600 Subject: coresight-etm3x: Add ARM ETM 3.5 Cortex-A5 peripheral ID Signed-off-by: Olivier Schonken Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 2de4cad9c5ed..b4f380a9da5c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -893,6 +893,11 @@ static struct amba_id etm_ids[] = { .mask = 0x0003ffff, .data = "ETM 3.3", }, + { /* ETM 3.5 - Cortex-A5 */ + .id = 0x0003b955, + .mask = 0x0003ffff, + .data = "ETM 3.5", + }, { /* ETM 3.5 */ .id = 0x0003b956, .mask = 0x0003ffff, -- cgit v1.2.3-58-ga151 From 7f73b0b9faa11446f73dba02811f928a230773c5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 25 Aug 2016 15:19:04 -0600 Subject: hwtracing: coresight: of_coresight: add missing of_node_put after calling of_parse_phandle of_node_put needs to be called when the device node which is got from of_parse_phandle has finished using. Cc: linux-arm-kernel@lists.infradead.org Cc: Mathieu Poirier Signed-off-by: Peter Chen Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 18f1c8c4776b..629e031b7456 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -184,6 +184,7 @@ struct coresight_platform_data *of_get_coresight_platform_data( break; } } + of_node_put(dn); return pdata; } -- cgit v1.2.3-58-ga151 From 9486295ad159fac87eb05bd683445be06a3b69f5 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Aug 2016 15:19:05 -0600 Subject: coresight: Use local coresight_desc instances Each coresight device prepares a description for coresight_register() in struct coresight_desc. Once we register the device, the description is useless and can be freed. The coresight_desc is small enough (48bytes on 64bit)i to be allocated on the stack. Hence use an automatic variable to avoid a needless dynamic allocation and wasting the memory(which will only be free'd when the device is destroyed). Cc: Mathieu Poirier Cc: Pratik Patel Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 20 ++++++-------- drivers/hwtracing/coresight/coresight-etm3x.c | 20 ++++++-------- drivers/hwtracing/coresight/coresight-etm4x.c | 20 ++++++-------- drivers/hwtracing/coresight/coresight-funnel.c | 20 ++++++-------- .../coresight/coresight-replicator-qcom.c | 18 +++++-------- drivers/hwtracing/coresight/coresight-replicator.c | 20 +++++--------- drivers/hwtracing/coresight/coresight-stm.c | 22 ++++++--------- drivers/hwtracing/coresight/coresight-tmc.c | 31 ++++++++++------------ drivers/hwtracing/coresight/coresight-tpiu.c | 18 +++++-------- 9 files changed, 75 insertions(+), 114 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 3b483e3f00ee..8a4927ca9181 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -636,7 +636,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct etb_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; if (np) { @@ -682,17 +682,13 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) return -ENOMEM; } - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - desc->type = CORESIGHT_DEV_TYPE_SINK; - desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; - desc->ops = &etb_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - desc->groups = coresight_etb_groups; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.ops = &etb_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_etb_groups; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index b4f380a9da5c..326afe49251a 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -756,13 +756,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct etm_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; @@ -825,13 +821,13 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) etm_init_trace_id(drvdata); etm_set_default(&drvdata->config); - desc->type = CORESIGHT_DEV_TYPE_SOURCE; - desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; - desc->ops = &etm_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - desc->groups = coresight_etm_groups; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_SOURCE; + desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; + desc.ops = &etm_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_etm_groups; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto err_arch_supported; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 3b16f8a9e44d..d8721fb8da99 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -711,13 +711,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct etmv4_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; @@ -772,13 +768,13 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) etm4_init_trace_id(drvdata); etm4_set_default(&drvdata->config); - desc->type = CORESIGHT_DEV_TYPE_SOURCE; - desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; - desc->ops = &etm4_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - desc->groups = coresight_etmv4_groups; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_SOURCE; + desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; + desc.ops = &etm4_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_etmv4_groups; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto err_arch_supported; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 05df789056cc..860fe6ef5632 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -176,7 +176,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct funnel_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; if (np) { @@ -207,17 +207,13 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) drvdata->base = base; pm_runtime_put(&adev->dev); - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - desc->type = CORESIGHT_DEV_TYPE_LINK; - desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; - desc->ops = &funnel_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - desc->groups = coresight_funnel_groups; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; + desc.ops = &funnel_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_funnel_groups; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 700f710e4bfa..0a3d15f0b009 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -102,7 +102,7 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) struct resource *res = &adev->res; struct coresight_platform_data *pdata = NULL; struct replicator_state *drvdata; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; void __iomem *base; @@ -134,16 +134,12 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) dev_set_drvdata(dev, drvdata); pm_runtime_put(&adev->dev); - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - desc->type = CORESIGHT_DEV_TYPE_LINK; - desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; - desc->ops = &replicator_cs_ops; - desc->pdata = adev->dev.platform_data; - desc->dev = &adev->dev; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; + desc.ops = &replicator_cs_ops; + desc.pdata = adev->dev.platform_data; + desc.dev = &adev->dev; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index c6982e312e15..3756e71cb8f5 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -69,7 +69,7 @@ static int replicator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct coresight_platform_data *pdata = NULL; struct replicator_drvdata *drvdata; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = pdev->dev.of_node; if (np) { @@ -95,18 +95,12 @@ static int replicator_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); platform_set_drvdata(pdev, drvdata); - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) { - ret = -ENOMEM; - goto out_disable_pm; - } - - desc->type = CORESIGHT_DEV_TYPE_LINK; - desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; - desc->ops = &replicator_cs_ops; - desc->pdata = pdev->dev.platform_data; - desc->dev = &pdev->dev; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; + desc.ops = &replicator_cs_ops; + desc.pdata = pdev->dev.platform_data; + desc.dev = &pdev->dev; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto out_disable_pm; diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 482c8bb27f87..819629aed2f7 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -799,7 +799,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) struct resource *res = &adev->res; struct resource ch_res; size_t res_size, bitmap_size; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; if (np) { @@ -865,19 +865,13 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) return -EPROBE_DEFER; } - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) { - ret = -ENOMEM; - goto stm_unregister; - } - - desc->type = CORESIGHT_DEV_TYPE_SOURCE; - desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE; - desc->ops = &stm_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - desc->groups = coresight_stm_groups; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_SOURCE; + desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE; + desc.ops = &stm_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_stm_groups; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto stm_unregister; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 84052c72462c..4cbcaf93c9d9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -302,7 +302,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct tmc_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; if (np) { @@ -319,10 +319,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) if (!drvdata) goto out; - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - goto out; - drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); @@ -354,24 +350,25 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - desc->pdata = pdata; - desc->dev = dev; - desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; - desc->groups = coresight_tmc_groups; + desc.pdata = pdata; + desc.dev = dev; + desc.groups = coresight_tmc_groups; if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { - desc->type = CORESIGHT_DEV_TYPE_SINK; - desc->ops = &tmc_etb_cs_ops; + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.ops = &tmc_etb_cs_ops; } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - desc->type = CORESIGHT_DEV_TYPE_SINK; - desc->ops = &tmc_etr_cs_ops; + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.ops = &tmc_etr_cs_ops; } else { - desc->type = CORESIGHT_DEV_TYPE_LINKSINK; - desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; - desc->ops = &tmc_etf_cs_ops; + desc.type = CORESIGHT_DEV_TYPE_LINKSINK; + desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; + desc.ops = &tmc_etf_cs_ops; } - drvdata->csdev = coresight_register(desc); + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto out; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 4e471e2e9d89..0673baf0f2f5 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -119,7 +119,7 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct tpiu_drvdata *drvdata; struct resource *res = &adev->res; - struct coresight_desc *desc; + struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; if (np) { @@ -154,16 +154,12 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - desc->type = CORESIGHT_DEV_TYPE_SINK; - desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; - desc->ops = &tpiu_cs_ops; - desc->pdata = pdata; - desc->dev = dev; - drvdata->csdev = coresight_register(desc); + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; + desc.ops = &tpiu_cs_ops; + desc.pdata = pdata; + desc.dev = dev; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); -- cgit v1.2.3-58-ga151 From 57a3668bebfd6247dceb862fceaee3b1fa2219b7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 25 Aug 2016 15:19:06 -0600 Subject: coresight: etm4x: remove duplicated include from coresight-etm4x.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d8721fb8da99..b3bde2aec2b9 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-58-ga151 From 1d37ae50671435e842c58b1429dfb0a6d065c42a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 25 Aug 2016 15:19:07 -0600 Subject: coresight: tmc: Delete an unnecessary check before the function call "kfree" The kfree() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index e68289b8c072..5fa49c4252f6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -168,7 +168,7 @@ out: spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free memory outside the spinlock if need be */ - if (!used && buf) + if (!used) kfree(buf); if (!ret) -- cgit v1.2.3-58-ga151 From 46a3d5cdb029471247b7be050332df399eca3ee1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 25 Aug 2016 15:19:08 -0600 Subject: coresight: etm4x: request to retain power to the trace unit when active The Coresight ETMv4 architecture provides a way to request to keep the power to the trace unit. This might help to collect the traces without the need to disable the CPU power management(entering/exiting deeper idle states). Trace PowerDown Control Register provides powerup request bit which when set requests the system to retain power to the trace unit and emulate the powerdown request. Typically, a trace unit drives a signal to the power controller to request that the trace unit core power domain is powered up. However, if the trace unit and the CPU are in the same power domain then the implementation might combine the trace unit power up status with a signal from the CPU. This patch requests to retain power to the trace unit when active and to remove when inactive. Note this change will only request but the behaviour depends on the implementation. However, it matches the exact behaviour expected when the external debugger is connected with respect to CPU power states. Cc: Mathieu Poirier Signed-off-by: Sudeep Holla Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 12 ++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b3bde2aec2b9..c8c7829f7046 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -164,6 +164,13 @@ static void etm4_enable_hw(void *info) writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); + /* + * Request to keep the trace unit powered and also + * emulation of powerdown + */ + writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU, + drvdata->base + TRCPDCR); + /* Enable the trace unit */ writel_relaxed(1, drvdata->base + TRCPRGCTLR); @@ -294,6 +301,11 @@ static void etm4_disable_hw(void *info) CS_UNLOCK(drvdata->base); + /* power can be removed from the trace unit now */ + control = readl_relaxed(drvdata->base + TRCPDCR); + control &= ~TRCPDCR_PU; + writel_relaxed(control, drvdata->base + TRCPDCR); + control = readl_relaxed(drvdata->base + TRCPRGCTLR); /* EN, bit[0] Trace unit enable bit */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 5359c5197c1d..2629954429a1 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -183,6 +183,9 @@ #define TRCSTATR_IDLE_BIT 0 #define ETM_DEFAULT_ADDR_COMP 0 +/* PowerDown Control Register bits */ +#define TRCPDCR_PU BIT(3) + /* secure state access levels */ #define ETM_EXLEVEL_S_APP BIT(8) #define ETM_EXLEVEL_S_OS BIT(9) -- cgit v1.2.3-58-ga151 From 3224dcc5a6ce919ddef2a387dcb4c47666480ed0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 25 Aug 2016 15:19:09 -0600 Subject: coresight: fix handling of ETM trace register access via sysfs The ETM registers are classified into 2 categories: trace and management. The core power domain contains most of the trace unit logic including all(except TRCOSLAR and TRCOSLSR) the trace registers. The debug power domain contains the external debugger interface including all management registers. This patch adds coresight unit specific function coresight_simple_func which can be used for ETM trace registers by providing a ETM specific read function which does smp cross call to ensure the trace core is powered up before the register is accessed. Cc: Mathieu Poirier Signed-off-by: Sudeep Holla Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 2 +- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 2 +- .../hwtracing/coresight/coresight-etm4x-sysfs.c | 62 ++++++++++++++++------ drivers/hwtracing/coresight/coresight-priv.h | 9 +++- drivers/hwtracing/coresight/coresight-stm.c | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- 6 files changed, 57 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 8a4927ca9181..d7325c6534ad 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -559,7 +559,7 @@ static const struct file_operations etb_fops = { }; #define coresight_etb10_simple_func(name, offset) \ - coresight_simple_func(struct etb_drvdata, name, offset) + coresight_simple_func(struct etb_drvdata, NULL, name, offset) coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); coresight_etb10_simple_func(sts, ETB_STATUS_REG); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 02d4b629891f..4856c8098416 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1222,7 +1222,7 @@ static struct attribute *coresight_etm_attrs[] = { }; #define coresight_etm3x_simple_func(name, offset) \ - coresight_simple_func(struct etm_drvdata, name, offset) + coresight_simple_func(struct etm_drvdata, NULL, name, offset) coresight_etm3x_simple_func(etmccr, ETMCCR); coresight_etm3x_simple_func(etmccer, ETMCCER); diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 7c84308c5564..fd7ff613db17 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2039,15 +2039,42 @@ static struct attribute *coresight_etmv4_attrs[] = { NULL, }; +struct etmv4_reg { + void __iomem *addr; + u32 data; +}; + +static void do_smp_cross_read(void *data) +{ + struct etmv4_reg *reg = data; + + reg->data = readl_relaxed(reg->addr); +} + +static u32 etmv4_cross_read(const struct device *dev, u32 offset) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev); + struct etmv4_reg reg; + + reg.addr = drvdata->base + offset; + /* + * smp cross call ensures the CPU will be powered up before + * accessing the ETMv4 trace core registers + */ + smp_call_function_single(drvdata->cpu, do_smp_cross_read, ®, 1); + return reg.data; +} + #define coresight_etm4x_simple_func(name, offset) \ - coresight_simple_func(struct etmv4_drvdata, name, offset) + coresight_simple_func(struct etmv4_drvdata, NULL, name, offset) + +#define coresight_etm4x_cross_read(name, offset) \ + coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \ + name, offset) -coresight_etm4x_simple_func(trcoslsr, TRCOSLSR); coresight_etm4x_simple_func(trcpdcr, TRCPDCR); coresight_etm4x_simple_func(trcpdsr, TRCPDSR); coresight_etm4x_simple_func(trclsr, TRCLSR); -coresight_etm4x_simple_func(trcconfig, TRCCONFIGR); -coresight_etm4x_simple_func(trctraceid, TRCTRACEIDR); coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); coresight_etm4x_simple_func(trcdevid, TRCDEVID); coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); @@ -2055,6 +2082,9 @@ coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); +coresight_etm4x_cross_read(trcoslsr, TRCOSLSR); +coresight_etm4x_cross_read(trcconfig, TRCCONFIGR); +coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR); static struct attribute *coresight_etmv4_mgmt_attrs[] = { &dev_attr_trcoslsr.attr, @@ -2073,19 +2103,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { NULL, }; -coresight_etm4x_simple_func(trcidr0, TRCIDR0); -coresight_etm4x_simple_func(trcidr1, TRCIDR1); -coresight_etm4x_simple_func(trcidr2, TRCIDR2); -coresight_etm4x_simple_func(trcidr3, TRCIDR3); -coresight_etm4x_simple_func(trcidr4, TRCIDR4); -coresight_etm4x_simple_func(trcidr5, TRCIDR5); +coresight_etm4x_cross_read(trcidr0, TRCIDR0); +coresight_etm4x_cross_read(trcidr1, TRCIDR1); +coresight_etm4x_cross_read(trcidr2, TRCIDR2); +coresight_etm4x_cross_read(trcidr3, TRCIDR3); +coresight_etm4x_cross_read(trcidr4, TRCIDR4); +coresight_etm4x_cross_read(trcidr5, TRCIDR5); /* trcidr[6,7] are reserved */ -coresight_etm4x_simple_func(trcidr8, TRCIDR8); -coresight_etm4x_simple_func(trcidr9, TRCIDR9); -coresight_etm4x_simple_func(trcidr10, TRCIDR10); -coresight_etm4x_simple_func(trcidr11, TRCIDR11); -coresight_etm4x_simple_func(trcidr12, TRCIDR12); -coresight_etm4x_simple_func(trcidr13, TRCIDR13); +coresight_etm4x_cross_read(trcidr8, TRCIDR8); +coresight_etm4x_cross_read(trcidr9, TRCIDR9); +coresight_etm4x_cross_read(trcidr10, TRCIDR10); +coresight_etm4x_cross_read(trcidr11, TRCIDR11); +coresight_etm4x_cross_read(trcidr12, TRCIDR12); +coresight_etm4x_cross_read(trcidr13, TRCIDR13); static struct attribute *coresight_etmv4_trcidr_attrs[] = { &dev_attr_trcidr0.attr, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index decfd52b5dc3..39841d1f58e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -38,14 +38,19 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) -#define coresight_simple_func(type, name, offset) \ +typedef u32 (*coresight_read_fn)(const struct device *, u32 offset); +#define coresight_simple_func(type, func, name, offset) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ type *drvdata = dev_get_drvdata(_dev->parent); \ + coresight_read_fn fn = func; \ u32 val; \ pm_runtime_get_sync(_dev->parent); \ - val = readl_relaxed(drvdata->base + offset); \ + if (fn) \ + val = fn(_dev->parent, offset); \ + else \ + val = readl_relaxed(drvdata->base + offset); \ pm_runtime_put_sync(_dev->parent); \ return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \ } \ diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 819629aed2f7..7949f0f6744a 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -635,7 +635,7 @@ static ssize_t traceid_store(struct device *dev, static DEVICE_ATTR_RW(traceid); #define coresight_stm_simple_func(name, offset) \ - coresight_simple_func(struct stm_drvdata, name, offset) + coresight_simple_func(struct stm_drvdata, NULL, name, offset) coresight_stm_simple_func(tcsr, STMTCSR); coresight_stm_simple_func(tsfreqr, STMTSFREQR); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 4cbcaf93c9d9..a4748630f5d6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -218,7 +218,7 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) } #define coresight_tmc_simple_func(name, offset) \ - coresight_simple_func(struct tmc_drvdata, name, offset) + coresight_simple_func(struct tmc_drvdata, NULL, name, offset) coresight_tmc_simple_func(rsz, TMC_RSZ); coresight_tmc_simple_func(sts, TMC_STS); -- cgit v1.2.3-58-ga151 From 68905d73df5d51ba1318a0ad3f3c5a65c4e646a2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:10 -0600 Subject: coresight: etm-perf: pass struct perf_event to source::enable/disable() With this commit [1] address range filter information is now found in the struct hw_perf_event::addr_filters. As such pass the event itself to the coresight_source::enable/disable() functions so that both event attribute and filter can be accessible for configuration. [1] 'commit 375637bc5249 ("perf/core: Introduce address range filtering")' Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 ++-- drivers/hwtracing/coresight/coresight-etm3x.c | 14 ++++++++------ drivers/hwtracing/coresight/coresight-etm4x.c | 19 +++++++++++-------- drivers/hwtracing/coresight/coresight-stm.c | 7 ++++--- drivers/hwtracing/coresight/coresight.c | 2 +- include/linux/coresight.h | 5 +++-- 6 files changed, 29 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 755125f7917f..e4754e8e6906 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -258,7 +258,7 @@ static void etm_event_start(struct perf_event *event, int flags) event->hw.state = 0; /* Finally enable the tracer */ - if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF)) + if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF)) goto fail_end_stop; out: @@ -291,7 +291,7 @@ static void etm_event_stop(struct perf_event *event, int mode) return; /* stop tracer */ - source_ops(csdev)->disable(csdev); + source_ops(csdev)->disable(csdev, event); /* tell the core */ event->hw.state = PERF_HES_STOPPED; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 326afe49251a..3fe368b23d15 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -311,9 +311,10 @@ void etm_config_trace_mode(struct etm_config *config) #define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) static int etm_parse_event_config(struct etm_drvdata *drvdata, - struct perf_event_attr *attr) + struct perf_event *event) { struct etm_config *config = &drvdata->config; + struct perf_event_attr *attr = &event->attr; if (!attr) return -EINVAL; @@ -459,7 +460,7 @@ static int etm_trace_id(struct coresight_device *csdev) } static int etm_enable_perf(struct coresight_device *csdev, - struct perf_event_attr *attr) + struct perf_event *event) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -467,7 +468,7 @@ static int etm_enable_perf(struct coresight_device *csdev, return -EINVAL; /* Configure the tracer based on the session's specifics */ - etm_parse_event_config(drvdata, attr); + etm_parse_event_config(drvdata, event); /* And enable it */ etm_enable_hw(drvdata); @@ -504,7 +505,7 @@ err: } static int etm_enable(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode) + struct perf_event *event, u32 mode) { int ret; u32 val; @@ -521,7 +522,7 @@ static int etm_enable(struct coresight_device *csdev, ret = etm_enable_sysfs(csdev); break; case CS_MODE_PERF: - ret = etm_enable_perf(csdev, attr); + ret = etm_enable_perf(csdev, event); break; default: ret = -EINVAL; @@ -601,7 +602,8 @@ static void etm_disable_sysfs(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } -static void etm_disable(struct coresight_device *csdev) +static void etm_disable(struct coresight_device *csdev, + struct perf_event *event) { u32 mode; struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c8c7829f7046..b9e26d4f6b84 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -185,9 +185,10 @@ static void etm4_enable_hw(void *info) } static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, - struct perf_event_attr *attr) + struct perf_event *event) { struct etmv4_config *config = &drvdata->config; + struct perf_event_attr *attr = &event->attr; if (!attr) return -EINVAL; @@ -221,7 +222,7 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, } static int etm4_enable_perf(struct coresight_device *csdev, - struct perf_event_attr *attr) + struct perf_event *event) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -229,7 +230,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, return -EINVAL; /* Configure the tracer based on the session's specifics */ - etm4_parse_event_config(drvdata, attr); + etm4_parse_event_config(drvdata, event); /* And enable it */ etm4_enable_hw(drvdata); @@ -264,7 +265,7 @@ err: } static int etm4_enable(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode) + struct perf_event *event, u32 mode) { int ret; u32 val; @@ -281,7 +282,7 @@ static int etm4_enable(struct coresight_device *csdev, ret = etm4_enable_sysfs(csdev); break; case CS_MODE_PERF: - ret = etm4_enable_perf(csdev, attr); + ret = etm4_enable_perf(csdev, event); break; default: ret = -EINVAL; @@ -321,7 +322,8 @@ static void etm4_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static int etm4_disable_perf(struct coresight_device *csdev) +static int etm4_disable_perf(struct coresight_device *csdev, + struct perf_event *event) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -357,7 +359,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } -static void etm4_disable(struct coresight_device *csdev) +static void etm4_disable(struct coresight_device *csdev, + struct perf_event *event) { u32 mode; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -376,7 +379,7 @@ static void etm4_disable(struct coresight_device *csdev) etm4_disable_sysfs(csdev); break; case CS_MODE_PERF: - etm4_disable_perf(csdev); + etm4_disable_perf(csdev, event); break; } diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 7949f0f6744a..49e0f1b925a5 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -198,7 +198,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) } static int stm_enable(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode) + struct perf_event *event, u32 mode) { u32 val; struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -260,7 +260,8 @@ static void stm_disable_hw(struct stm_drvdata *drvdata) stm_hwevent_disable_hw(drvdata); } -static void stm_disable(struct coresight_device *csdev) +static void stm_disable(struct coresight_device *csdev, + struct perf_event *event) { struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -355,7 +356,7 @@ static void stm_generic_unlink(struct stm_data *stm_data, if (!drvdata || !drvdata->csdev) return; - stm_disable(drvdata->csdev); + stm_disable(drvdata->csdev, NULL); } static phys_addr_t diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index bb20ee9747e1..fcbedd3905e3 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -257,7 +257,7 @@ static void coresight_disable_source(struct coresight_device *csdev) { if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) { - source_ops(csdev)->disable(csdev); + source_ops(csdev)->disable(csdev, NULL); csdev->enable = false; } } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 385d62e64abb..2a5982c37dfb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -232,8 +232,9 @@ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode); - void (*disable)(struct coresight_device *csdev); + struct perf_event *event, u32 mode); + void (*disable)(struct coresight_device *csdev, + struct perf_event *event); }; struct coresight_ops { -- cgit v1.2.3-58-ga151 From 2b7adc460ff8bff95f79c03451448895e761c4a6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:11 -0600 Subject: coresight: remove duplicated enumeration Both ETMv3 and ETMv4 drivers are declaring an 'enum etm_addr_type', creating reduncancy. This patch removes the enumeration from the driver files and adds it to a common header. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 8 -------- drivers/hwtracing/coresight/coresight-etm3x-sysfs.c | 1 + drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 1 + drivers/hwtracing/coresight/coresight-etm4x.h | 8 -------- drivers/hwtracing/coresight/coresight-priv.h | 8 ++++++++ 5 files changed, 10 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 51597cb2c08a..4a18ee499965 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -259,14 +259,6 @@ struct etm_drvdata { struct etm_config config; }; -enum etm_addr_type { - ETM_ADDR_TYPE_NONE, - ETM_ADDR_TYPE_SINGLE, - ETM_ADDR_TYPE_RANGE, - ETM_ADDR_TYPE_START, - ETM_ADDR_TYPE_STOP, -}; - static inline void etm_writel(struct etm_drvdata *drvdata, u32 val, u32 off) { diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 4856c8098416..e9b071953f80 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -18,6 +18,7 @@ #include #include #include "coresight-etm.h" +#include "coresight-priv.h" static ssize_t nr_addr_cmp_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index fd7ff613db17..b9b1e9c8f4c4 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -18,6 +18,7 @@ #include #include #include "coresight-etm4x.h" +#include "coresight-priv.h" static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 2629954429a1..ba8d3f86de21 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -410,14 +410,6 @@ enum etm_addr_ctxtype { ETM_CTX_CTXID_VMID, }; -enum etm_addr_type { - ETM_ADDR_TYPE_NONE, - ETM_ADDR_TYPE_SINGLE, - ETM_ADDR_TYPE_RANGE, - ETM_ADDR_TYPE_START, - ETM_ADDR_TYPE_STOP, -}; - extern const struct attribute_group *coresight_etmv4_groups[]; void etm4_config_trace_mode(struct etmv4_config *config); #endif diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 39841d1f58e0..196a14be4b3d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -56,6 +56,14 @@ static ssize_t name##_show(struct device *_dev, \ } \ static DEVICE_ATTR_RO(name) +enum etm_addr_type { + ETM_ADDR_TYPE_NONE, + ETM_ADDR_TYPE_SINGLE, + ETM_ADDR_TYPE_RANGE, + ETM_ADDR_TYPE_START, + ETM_ADDR_TYPE_STOP, +}; + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, -- cgit v1.2.3-58-ga151 From ca878b14660c340287fb17d70950cfc09c6d698c Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:12 -0600 Subject: coresight: etm-perf: configuring filters from perf core This patch implements the required API needed to access and retrieve range and start/stop filters from the perf core. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 149 ++++++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm-perf.h | 34 ++++++ 2 files changed, 168 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index e4754e8e6906..98eb207684fa 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -27,6 +27,7 @@ #include #include +#include "coresight-etm-perf.h" #include "coresight-priv.h" static struct pmu etm_pmu; @@ -71,14 +72,48 @@ static const struct attribute_group *etm_pmu_attr_groups[] = { static void etm_event_read(struct perf_event *event) {} -static int etm_event_init(struct perf_event *event) +static int etm_addr_filters_alloc(struct perf_event *event) { - if (event->attr.type != etm_pmu.type) - return -ENOENT; + struct etm_filters *filters; + int node = event->cpu == -1 ? -1 : cpu_to_node(event->cpu); + + filters = kzalloc_node(sizeof(struct etm_filters), GFP_KERNEL, node); + if (!filters) + return -ENOMEM; + + if (event->parent) + memcpy(filters, event->parent->hw.addr_filters, + sizeof(*filters)); + + event->hw.addr_filters = filters; return 0; } +static void etm_event_destroy(struct perf_event *event) +{ + kfree(event->hw.addr_filters); + event->hw.addr_filters = NULL; +} + +static int etm_event_init(struct perf_event *event) +{ + int ret = 0; + + if (event->attr.type != etm_pmu.type) { + ret = -ENOENT; + goto out; + } + + ret = etm_addr_filters_alloc(event); + if (ret) + goto out; + + event->destroy = etm_event_destroy; +out: + return ret; +} + static void free_event_data(struct work_struct *work) { int cpu; @@ -342,6 +377,87 @@ static void etm_event_del(struct perf_event *event, int mode) etm_event_stop(event, PERF_EF_UPDATE); } +static int etm_addr_filters_validate(struct list_head *filters) +{ + bool range = false, address = false; + int index = 0; + struct perf_addr_filter *filter; + + list_for_each_entry(filter, filters, entry) { + /* + * No need to go further if there's no more + * room for filters. + */ + if (++index > ETM_ADDR_CMP_MAX) + return -EOPNOTSUPP; + + /* + * As taken from the struct perf_addr_filter documentation: + * @range: 1: range, 0: address + * + * At this time we don't allow range and start/stop filtering + * to cohabitate, they have to be mutually exclusive. + */ + if ((filter->range == 1) && address) + return -EOPNOTSUPP; + + if ((filter->range == 0) && range) + return -EOPNOTSUPP; + + /* + * For range filtering, the second address in the address + * range comparator needs to be higher than the first. + * Invalid otherwise. + */ + if (filter->range && filter->size == 0) + return -EINVAL; + + /* + * Everything checks out with this filter, record what we've + * received before moving on to the next one. + */ + if (filter->range) + range = true; + else + address = true; + } + + return 0; +} + +static void etm_addr_filters_sync(struct perf_event *event) +{ + struct perf_addr_filters_head *head = perf_event_addr_filters(event); + unsigned long start, stop, *offs = event->addr_filters_offs; + struct etm_filters *filters = event->hw.addr_filters; + struct etm_filter *etm_filter; + struct perf_addr_filter *filter; + int i = 0; + + list_for_each_entry(filter, &head->list, entry) { + start = filter->offset + offs[i]; + stop = start + filter->size; + etm_filter = &filters->etm_filter[i]; + + if (filter->range == 1) { + etm_filter->start_addr = start; + etm_filter->stop_addr = stop; + etm_filter->type = ETM_ADDR_TYPE_RANGE; + } else { + if (filter->filter == 1) { + etm_filter->start_addr = start; + etm_filter->type = ETM_ADDR_TYPE_START; + } else { + etm_filter->stop_addr = stop; + etm_filter->type = ETM_ADDR_TYPE_STOP; + } + } + i++; + } + + filters->nr_filters = i; +} + int etm_perf_symlink(struct coresight_device *csdev, bool link) { char entry[sizeof("cpu9999999")]; @@ -371,18 +487,21 @@ static int __init etm_perf_init(void) { int ret; - etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; - - etm_pmu.attr_groups = etm_pmu_attr_groups; - etm_pmu.task_ctx_nr = perf_sw_context; - etm_pmu.read = etm_event_read; - etm_pmu.event_init = etm_event_init; - etm_pmu.setup_aux = etm_setup_aux; - etm_pmu.free_aux = etm_free_aux; - etm_pmu.start = etm_event_start; - etm_pmu.stop = etm_event_stop; - etm_pmu.add = etm_event_add; - etm_pmu.del = etm_event_del; + etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; + + etm_pmu.attr_groups = etm_pmu_attr_groups; + etm_pmu.task_ctx_nr = perf_sw_context; + etm_pmu.read = etm_event_read; + etm_pmu.event_init = etm_event_init; + etm_pmu.setup_aux = etm_setup_aux; + etm_pmu.free_aux = etm_free_aux; + etm_pmu.start = etm_event_start; + etm_pmu.stop = etm_event_stop; + etm_pmu.add = etm_event_add; + etm_pmu.del = etm_event_del; + etm_pmu.addr_filters_sync = etm_addr_filters_sync; + etm_pmu.addr_filters_validate = etm_addr_filters_validate; + etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX; ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); if (ret == 0) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h index 87f5a134eb6f..3ffc9feb2d64 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -18,8 +18,42 @@ #ifndef _CORESIGHT_ETM_PERF_H #define _CORESIGHT_ETM_PERF_H +#include "coresight-priv.h" + struct coresight_device; +/* + * In both ETMv3 and v4 the maximum number of address comparator implentable + * is 8. The actual number is implementation specific and will be checked + * when filters are applied. + */ +#define ETM_ADDR_CMP_MAX 8 + +/** + * struct etm_filter - single instruction range or start/stop configuration. + * @start_addr: The address to start tracing on. + * @stop_addr: The address to stop tracing on. + * @type: Is this a range or start/stop filter. + */ +struct etm_filter { + unsigned long start_addr; + unsigned long stop_addr; + enum etm_addr_type type; +}; + +/** + * struct etm_filters - set of filters for a session + * @etm_filter: All the filters for this session. + * @nr_filters: Number of filters + * @ssstatus: Status of the start/stop logic. + */ +struct etm_filters { + struct etm_filter etm_filter[ETM_ADDR_CMP_MAX]; + unsigned int nr_filters; + bool ssstatus; +}; + + #ifdef CONFIG_CORESIGHT int etm_perf_symlink(struct coresight_device *csdev, bool link); -- cgit v1.2.3-58-ga151 From 2a5695a585b5aa1fe7b51679f90d994a596a1ec2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:13 -0600 Subject: coresight: etm4x: split default and filter configuration Splitting the steps involved in the configuration of a tracer. The first part is generic and can be reused for both sysFS and Perf methods. The second part pertains to the configuration of filters themselves where the source of the information used to configure the filters will vary depending on the access methods. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 36 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b9e26d4f6b84..f890d1de39e3 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -562,21 +562,8 @@ static void etm4_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm4_set_default(struct etmv4_config *config) +static void etm4_set_default_config(struct etmv4_config *config) { - if (WARN_ON_ONCE(!config)) - return; - - /* - * Make default initialisation trace everything - * - * Select the "always true" resource selector on the - * "Enablign Event" line and configure address range comparator - * '0' to trace all the possible address range. From there - * configure the "include/exclude" engine to include address - * range comparator '0'. - */ - /* disable all events tracing */ config->eventctrl0 = 0x0; config->eventctrl1 = 0x0; @@ -592,7 +579,10 @@ static void etm4_set_default(struct etmv4_config *config) /* TRCVICTLR::EVENT = 0x01, select the always on logic */ config->vinst_ctrl |= BIT(0); +} +static void etm4_set_default_filter(struct etmv4_config *config) +{ /* * TRCVICTLR::SSSTATUS == 1, the start-stop logic is * in the started state @@ -638,6 +628,24 @@ static void etm4_set_default(struct etmv4_config *config) config->vissctlr = 0x0; } +static void etm4_set_default(struct etmv4_config *config) +{ + if (WARN_ON_ONCE(!config)) + return; + + /* + * Make default initialisation trace everything + * + * Select the "always true" resource selector on the + * "Enablign Event" line and configure address range comparator + * '0' to trace all the possible address range. From there + * configure the "include/exclude" engine to include address + * range comparator '0'. + */ + etm4_set_default_config(config); + etm4_set_default_filter(config); +} + void etm4_config_trace_mode(struct etmv4_config *config) { u32 addr_acc, mode; -- cgit v1.2.3-58-ga151 From 5edd944b43223257033a12096ad08298b01a47de Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:14 -0600 Subject: coresight: etm4x: cleaning up default filter configuration The default filter configuration was hard to read and included some redundancy. This patch attempts to stream line configuration and improve readability. No change of functionality is included. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 53 ++++++++++++++------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index f890d1de39e3..04462fffe8e4 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -583,39 +583,34 @@ static void etm4_set_default_config(struct etmv4_config *config) static void etm4_set_default_filter(struct etmv4_config *config) { - /* - * TRCVICTLR::SSSTATUS == 1, the start-stop logic is - * in the started state - */ - config->vinst_ctrl |= BIT(9); + u64 start, stop, access_type = 0; /* * Configure address range comparator '0' to encompass all * possible addresses. */ + start = 0x0; + stop = ~0x0; - /* First half of default address comparator: start at address 0 */ - config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0; - /* trace instruction addresses */ - config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1)); - /* EXLEVEL_NS, bits[12:15], only trace application and kernel space */ - config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP; - /* EXLEVEL_S, bits[11:8], don't trace anything in secure state */ - config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP | - ETM_EXLEVEL_S_OS | - ETM_EXLEVEL_S_HYP); - config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; + /* EXLEVEL_NS, bits[12:15], always stay away from hypervisor mode. */ + access_type = ETM_EXLEVEL_NS_HYP; /* - * Second half of default address comparator: go all - * the way to the top. - */ - config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0; - /* trace instruction addresses */ - config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1)); - /* Address comparator type must be equal for both halves */ - config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = - config->addr_acc[ETM_DEFAULT_ADDR_COMP]; + * EXLEVEL_S, bits[11:8], don't trace anything happening + * in secure state. + */ + access_type |= (ETM_EXLEVEL_S_APP | + ETM_EXLEVEL_S_OS | + ETM_EXLEVEL_S_HYP); + + /* First half of default address comparator */ + config->addr_val[ETM_DEFAULT_ADDR_COMP] = start; + config->addr_acc[ETM_DEFAULT_ADDR_COMP] = access_type; + config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; + + /* Second half of default address comparator */ + config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = stop; + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = access_type; config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE; /* @@ -624,7 +619,13 @@ static void etm4_set_default_filter(struct etmv4_config *config) */ config->viiectlr = BIT(0); - /* no start-stop filtering for ViewInst */ + /* + * TRCVICTLR::SSSTATUS == 1, the start-stop logic is + * in the started state + */ + config->vinst_ctrl |= BIT(9); + + /* No start-stop filtering for ViewInst */ config->vissctlr = 0x0; } -- cgit v1.2.3-58-ga151 From 6cccf66354fabb48de88238bf1343f774113a133 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:15 -0600 Subject: coresight: etm4x: adding range filter configuration function Introducing a new function to do address range configuration generic enough to work for any address range and any comparator. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 56 ++++++++++++++++++--------- 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 04462fffe8e4..aca624010b9f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -581,16 +581,10 @@ static void etm4_set_default_config(struct etmv4_config *config) config->vinst_ctrl |= BIT(0); } -static void etm4_set_default_filter(struct etmv4_config *config) +static void etm4_set_comparator_filter(struct etmv4_config *config, + u64 start, u64 stop, int comparator) { - u64 start, stop, access_type = 0; - - /* - * Configure address range comparator '0' to encompass all - * possible addresses. - */ - start = 0x0; - stop = ~0x0; + u64 access_type = 0; /* EXLEVEL_NS, bits[12:15], always stay away from hypervisor mode. */ access_type = ETM_EXLEVEL_NS_HYP; @@ -604,20 +598,46 @@ static void etm4_set_default_filter(struct etmv4_config *config) ETM_EXLEVEL_S_HYP); /* First half of default address comparator */ - config->addr_val[ETM_DEFAULT_ADDR_COMP] = start; - config->addr_acc[ETM_DEFAULT_ADDR_COMP] = access_type; - config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; + config->addr_val[comparator] = start; + config->addr_acc[comparator] = access_type; + config->addr_type[comparator] = ETM_ADDR_TYPE_RANGE; /* Second half of default address comparator */ - config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = stop; - config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = access_type; - config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE; + config->addr_val[comparator + 1] = stop; + config->addr_acc[comparator + 1] = access_type; + config->addr_type[comparator + 1] = ETM_ADDR_TYPE_RANGE; + + /* + * Configure the ViewInst function to include this address range + * comparator. + * + * @comparator is divided by two since it is the index in the + * etmv4_config::addr_val array but register TRCVIIECTLR deals with + * address range comparator _pairs_. + * + * Therefore: + * index 0 -> compatator pair 0 + * index 2 -> comparator pair 1 + * index 4 -> comparator pair 2 + * ... + * index 14 -> comparator pair 7 + */ + config->viiectlr |= BIT(comparator / 2); +} + +static void etm4_set_default_filter(struct etmv4_config *config) +{ + u64 start, stop; /* - * Configure the ViewInst function to filter on address range - * comparator '0'. + * Configure address range comparator '0' to encompass all + * possible addresses. */ - config->viiectlr = BIT(0); + start = 0x0; + stop = ~0x0; + + etm4_set_comparator_filter(config, start, stop, + ETM_DEFAULT_ADDR_COMP); /* * TRCVICTLR::SSSTATUS == 1, the start-stop logic is -- cgit v1.2.3-58-ga151 From f0d30cc30e545d0b059948f5d6be1b62ee54a355 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:16 -0600 Subject: coresight: etm4x: configuring include/exclude function The include/exclude function of a tracer is applicable to address range and start/stop filters. To avoid duplication and reuse code moving the include/exclude configuration to a function of its own. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 35 +++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index aca624010b9f..1044ed609d81 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -205,13 +205,6 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* Always start from the default config */ etm4_set_default(config); - /* - * By default the tracers are configured to trace the whole address - * range. Narrow the field only if requested by user space. - */ - if (config->mode) - etm4_config_trace_mode(config); - /* Go from generic option to ETMv4 specifics */ if (attr->config & BIT(ETM_OPT_CYCACC)) config->cfg |= ETMv4_MODE_CYCACC; @@ -581,14 +574,28 @@ static void etm4_set_default_config(struct etmv4_config *config) config->vinst_ctrl |= BIT(0); } -static void etm4_set_comparator_filter(struct etmv4_config *config, - u64 start, u64 stop, int comparator) +static u64 etm4_get_access_type(struct etmv4_config *config) { u64 access_type = 0; - /* EXLEVEL_NS, bits[12:15], always stay away from hypervisor mode. */ + /* + * EXLEVEL_NS, bits[15:12] + * The Exception levels are: + * Bit[12] Exception level 0 - Application + * Bit[13] Exception level 1 - OS + * Bit[14] Exception level 2 - Hypervisor + * Bit[15] Never implemented + * + * Always stay away from hypervisor mode. + */ access_type = ETM_EXLEVEL_NS_HYP; + if (config->mode & ETM_MODE_EXCL_KERN) + access_type |= ETM_EXLEVEL_NS_OS; + + if (config->mode & ETM_MODE_EXCL_USER) + access_type |= ETM_EXLEVEL_NS_APP; + /* * EXLEVEL_S, bits[11:8], don't trace anything happening * in secure state. @@ -597,6 +604,14 @@ static void etm4_set_comparator_filter(struct etmv4_config *config, ETM_EXLEVEL_S_OS | ETM_EXLEVEL_S_HYP); + return access_type; +} + +static void etm4_set_comparator_filter(struct etmv4_config *config, + u64 start, u64 stop, int comparator) +{ + u64 access_type = etm4_get_access_type(config); + /* First half of default address comparator */ config->addr_val[comparator] = start; config->addr_acc[comparator] = access_type; -- cgit v1.2.3-58-ga151 From 2703d74c1313271ba78439b0796444add6a9328f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:17 -0600 Subject: coresight: etm4x: adding configurable address range filtering This patch adds the capability to specify address ranges from the perf cmd line using the --filter option. If the IP falls within the range(s) program flow traces are generated. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 128 ++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1044ed609d81..ebaefb45130f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -45,7 +45,9 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETMv4 currently registered */ static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; -static void etm4_set_default(struct etmv4_config *config); +static void etm4_set_default_config(struct etmv4_config *config); +static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, + struct perf_event *event); static enum cpuhp_state hp_online; @@ -187,11 +189,14 @@ static void etm4_enable_hw(void *info) static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, struct perf_event *event) { + int ret = 0; struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr; - if (!attr) - return -EINVAL; + if (!attr) { + ret = -EINVAL; + goto out; + } /* Clear configuration from previous run */ memset(config, 0, sizeof(struct etmv4_config)); @@ -203,7 +208,12 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, config->mode = ETM_MODE_EXCL_USER; /* Always start from the default config */ - etm4_set_default(config); + etm4_set_default_config(config); + + /* Configure filters specified on the perf cmd line, if any. */ + ret = etm4_set_event_filters(drvdata, event); + if (ret) + goto out; /* Go from generic option to ETMv4 specifics */ if (attr->config & BIT(ETM_OPT_CYCACC)) @@ -211,23 +221,30 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, if (attr->config & BIT(ETM_OPT_TS)) config->cfg |= ETMv4_MODE_TIMESTAMP; - return 0; +out: + return ret; } static int etm4_enable_perf(struct coresight_device *csdev, struct perf_event *event) { + int ret = 0; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) - return -EINVAL; + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) { + ret = -EINVAL; + goto out; + } /* Configure the tracer based on the session's specifics */ - etm4_parse_event_config(drvdata, event); + ret = etm4_parse_event_config(drvdata, event); + if (ret) + goto out; /* And enable it */ etm4_enable_hw(drvdata); - return 0; +out: + return ret; } static int etm4_enable_sysfs(struct coresight_device *csdev) @@ -682,6 +699,99 @@ static void etm4_set_default(struct etmv4_config *config) etm4_set_default_filter(config); } +static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type) +{ + int nr_comparator, index = 0; + struct etmv4_config *config = &drvdata->config; + + /* + * nr_addr_cmp holds the number of comparator _pair_, so time 2 + * for the total number of comparators. + */ + nr_comparator = drvdata->nr_addr_cmp * 2; + + /* Go through the tally of comparators looking for a free one. */ + while (index < nr_comparator) { + switch (type) { + case ETM_ADDR_TYPE_RANGE: + if (config->addr_type[index] == ETM_ADDR_TYPE_NONE && + config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE) + return index; + + /* Address range comparators go in pairs */ + index += 2; + break; + default: + return -EINVAL; + } + } + + /* If we are here all the comparators have been used. */ + return -ENOSPC; +} + +static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, + struct perf_event *event) +{ + int i, comparator, ret = 0; + struct etmv4_config *config = &drvdata->config; + struct etm_filters *filters = event->hw.addr_filters; + + if (!filters) + goto default_filter; + + /* Sync events with what Perf got */ + perf_event_addr_filters_sync(event); + + /* + * If there are no filters to deal with simply go ahead with + * the default filter, i.e the entire address range. + */ + if (!filters->nr_filters) + goto default_filter; + + for (i = 0; i < filters->nr_filters; i++) { + struct etm_filter *filter = &filters->etm_filter[i]; + enum etm_addr_type type = filter->type; + + /* See if a comparator is free. */ + comparator = etm4_get_next_comparator(drvdata, type); + if (comparator < 0) { + ret = comparator; + goto out; + } + + switch (type) { + case ETM_ADDR_TYPE_RANGE: + etm4_set_comparator_filter(config, + filter->start_addr, + filter->stop_addr, + comparator); + /* + * TRCVICTLR::SSSTATUS == 1, the start-stop logic is + * in the started state + */ + config->vinst_ctrl |= BIT(9); + + /* No start-stop filtering for ViewInst */ + config->vissctlr = 0x0; + break; + default: + ret = -EINVAL; + goto out; + } + } + + goto out; + + +default_filter: + etm4_set_default_filter(config); + +out: + return ret; +} + void etm4_config_trace_mode(struct etmv4_config *config) { u32 addr_acc, mode; -- cgit v1.2.3-58-ga151 From e97b1c6a8dba967a7c272c3b976ed6d59c7bfad7 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 25 Aug 2016 15:19:18 -0600 Subject: coresight: etm4x: adding configurable start/stop filtering With this patch we add start/stop filtering as specified on the perf cmd line. When the IP matches the start address trace generation gets triggered. The stop condition is achieved when the IP matches the stop address. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index ebaefb45130f..4db8d6a4d0cb 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -335,12 +335,25 @@ static void etm4_disable_hw(void *info) static int etm4_disable_perf(struct coresight_device *csdev, struct perf_event *event) { + u32 control; + struct etm_filters *filters = event->hw.addr_filters; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL; etm4_disable_hw(drvdata); + + /* + * Check if the start/stop logic was active when the unit was stopped. + * That way we can re-enable the start/stop logic when the process is + * scheduled again. Configuration of the start/stop logic happens in + * function etm4_set_event_filters(). + */ + control = readl_relaxed(drvdata->base + TRCVICTLR); + /* TRCVICTLR::SSSTATUS, bit[9] */ + filters->ssstatus = (control & BIT(9)); + return 0; } @@ -657,6 +670,27 @@ static void etm4_set_comparator_filter(struct etmv4_config *config, config->viiectlr |= BIT(comparator / 2); } +static void etm4_set_start_stop_filter(struct etmv4_config *config, + u64 address, int comparator, + enum etm_addr_type type) +{ + int shift; + u64 access_type = etm4_get_access_type(config); + + /* Configure the comparator */ + config->addr_val[comparator] = address; + config->addr_acc[comparator] = access_type; + config->addr_type[comparator] = type; + + /* + * Configure ViewInst Start-Stop control register. + * Addresses configured to start tracing go from bit 0 to n-1, + * while those configured to stop tracing from 16 to 16 + n-1. + */ + shift = (type == ETM_ADDR_TYPE_START ? 0 : 16); + config->vissctlr |= BIT(shift + comparator); +} + static void etm4_set_default_filter(struct etmv4_config *config) { u64 start, stop; @@ -721,6 +755,14 @@ static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type) /* Address range comparators go in pairs */ index += 2; break; + case ETM_ADDR_TYPE_START: + case ETM_ADDR_TYPE_STOP: + if (config->addr_type[index] == ETM_ADDR_TYPE_NONE) + return index; + + /* Start/stop address can have odd indexes */ + index += 1; + break; default: return -EINVAL; } @@ -734,6 +776,7 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, struct perf_event *event) { int i, comparator, ret = 0; + u64 address; struct etmv4_config *config = &drvdata->config; struct etm_filters *filters = event->hw.addr_filters; @@ -776,6 +819,34 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, /* No start-stop filtering for ViewInst */ config->vissctlr = 0x0; break; + case ETM_ADDR_TYPE_START: + case ETM_ADDR_TYPE_STOP: + /* Get the right start or stop address */ + address = (type == ETM_ADDR_TYPE_START ? + filter->start_addr : + filter->stop_addr); + + /* Configure comparator */ + etm4_set_start_stop_filter(config, address, + comparator, type); + + /* + * If filters::ssstatus == 1, trace acquisition was + * started but the process was yanked away before the + * the stop address was hit. As such the start/stop + * logic needs to be re-started so that tracing can + * resume where it left. + * + * The start/stop logic status when a process is + * scheduled out is checked in function + * etm4_disable_perf(). + */ + if (filters->ssstatus) + config->vinst_ctrl |= BIT(9); + + /* No include/exclude filtering for ViewInst */ + config->viiectlr = 0x0; + break; default: ret = -EINVAL; goto out; -- cgit v1.2.3-58-ga151 From 4d31a2588ae37a5d0f61f4d956454e9504846aeb Mon Sep 17 00:00:00 2001 From: Jan Viktorin Date: Tue, 17 May 2016 11:22:17 +0200 Subject: uio: fix dmem_region_start computation The variable i contains a total number of resources (including IORESOURCE_IRQ). However, we want the dmem_region_start to point after the last resource of type IORESOURCE_MEM. The original behaviour leads (very likely) to skipping several UIO mapping regions and makes them useless. Fix this by computing dmem_region_start from the uiomem which points to the last used UIO mapping. Fixes: 0a0c3b5a24bd ("Add new uio device for dynamic memory allocation") Signed-off-by: Jan Viktorin Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 915facbf552e..e1134a4d97f3 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -229,7 +229,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) ++uiomem; } - priv->dmem_region_start = i; + priv->dmem_region_start = uiomem - &uioinfo->mem[0]; priv->num_dmem_regions = pdata->num_dynamic_regions; for (i = 0; i < pdata->num_dynamic_regions; ++i) { -- cgit v1.2.3-58-ga151 From 73edc8f7cceffe537d6888dc20703123769eb3f6 Mon Sep 17 00:00:00 2001 From: Andreas Werner Date: Fri, 26 Aug 2016 09:34:57 +0200 Subject: mcb: Added support for LPC or non PCI based MCB carrier Add support for MCB bases FPGAs connected to the LPC or non PCI Bus. This driver currently supports the SC24 board. The FPGA is connected to the LPC bus and is identified using the BIOS DMI string. Signed-off-by: Andreas Werner Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/Kconfig | 9 +++ drivers/mcb/Makefile | 1 + drivers/mcb/mcb-lpc.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/mcb/mcb-lpc.c (limited to 'drivers') diff --git a/drivers/mcb/Kconfig b/drivers/mcb/Kconfig index e9a6976e1010..76d9c51de6c9 100644 --- a/drivers/mcb/Kconfig +++ b/drivers/mcb/Kconfig @@ -28,4 +28,13 @@ config MCB_PCI If build as a module, the module is called mcb-pci.ko +config MCB_LPC + tristate "LPC (non PCI) based MCB carrier" + default n + help + + This is a MCB carrier on a LPC or non PCI device. + + If build as a module, the module is called mcb-lpc.ko + endif # MCB diff --git a/drivers/mcb/Makefile b/drivers/mcb/Makefile index 1ae141311def..bcc7745774ab 100644 --- a/drivers/mcb/Makefile +++ b/drivers/mcb/Makefile @@ -5,3 +5,4 @@ mcb-y += mcb-core.o mcb-y += mcb-parse.o obj-$(CONFIG_MCB_PCI) += mcb-pci.o +obj-$(CONFIG_MCB_LPC) += mcb-lpc.o diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c new file mode 100644 index 000000000000..d072c088ce73 --- /dev/null +++ b/drivers/mcb/mcb-lpc.c @@ -0,0 +1,158 @@ +/* + * MEN Chameleon Bus. + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Andreas Werner + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include "mcb-internal.h" + +struct priv { + struct mcb_bus *bus; + struct resource *mem; + void __iomem *base; +}; + +static int mcb_lpc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct priv *priv; + int ret = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!priv->mem) { + dev_err(&pdev->dev, "No Memory resource\n"); + return -ENODEV; + } + + res = devm_request_mem_region(&pdev->dev, priv->mem->start, + resource_size(priv->mem), + KBUILD_MODNAME); + if (!res) { + dev_err(&pdev->dev, "Failed to request IO memory\n"); + return -EBUSY; + } + + priv->base = devm_ioremap(&pdev->dev, priv->mem->start, + resource_size(priv->mem)); + if (!priv->base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, priv); + + priv->bus = mcb_alloc_bus(&pdev->dev); + if (IS_ERR(priv->bus)) + return PTR_ERR(priv->bus); + + ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); + if (ret < 0) { + mcb_release_bus(priv->bus); + return ret; + } + + dev_dbg(&pdev->dev, "Found %d cells\n", ret); + + mcb_bus_add_devices(priv->bus); + + return 0; + +} + +static int mcb_lpc_remove(struct platform_device *pdev) +{ + struct priv *priv = platform_get_drvdata(pdev); + + mcb_release_bus(priv->bus); + + return 0; +} + +static struct platform_device *mcb_lpc_pdev; + +static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) +{ + struct resource *res = id->driver_data; + int ret; + + mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); + if (!mcb_lpc_pdev) + return -ENOMEM; + + ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); + if (ret) + goto out_put; + + ret = platform_device_add(mcb_lpc_pdev); + if (ret) + goto out_put; + + return 0; + +out_put: + platform_device_put(mcb_lpc_pdev); + return ret; +} + +static struct resource sc24_fpga_resource = { + .start = 0xe000e000, + .end = 0xe000e000 + CHAM_HEADER_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_driver mcb_lpc_driver = { + .driver = { + .name = "mcb-lpc", + }, + .probe = mcb_lpc_probe, + .remove = mcb_lpc_remove, +}; + +static const struct dmi_system_id mcb_lpc_dmi_table[] = { + { + .ident = "SC24", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEN"), + DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), + }, + .driver_data = (void *)&sc24_fpga_resource, + .callback = mcb_lpc_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); + +static int __init mcb_lpc_init(void) +{ + if (!dmi_check_system(mcb_lpc_dmi_table)) + return -ENODEV; + + return platform_driver_register(&mcb_lpc_driver); +} + +static void __exit mcb_lpc_exit(void) +{ + platform_device_unregister(mcb_lpc_pdev); + platform_driver_unregister(&mcb_lpc_driver); +} + +module_init(mcb_lpc_init); +module_exit(mcb_lpc_exit); + +MODULE_AUTHOR("Andreas Werner "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MCB over LPC support"); -- cgit v1.2.3-58-ga151 From ffc7bb3857e42acab131271e485735cbf673d0f0 Mon Sep 17 00:00:00 2001 From: Andreas Werner Date: Fri, 26 Aug 2016 09:34:58 +0200 Subject: mcb: Added bar descriptor support for non PCI bus MCB carrier Added support for the bar descriptor. This type is used for FPGAs connect to the LPC or to a non PCI bus. The Bar descriptor could have a maximum of 6 BARs. Each of the devices within the FPGA could be mapped to a different BAR. The BAR descriptor is comparable to the PCI header. Signed-off-by: Andreas Werner [ free bar descriptor in the non-error case ] Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-internal.h | 9 ++++ drivers/mcb/mcb-parse.c | 126 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h index 5254e0285725..d6e6933b19f1 100644 --- a/drivers/mcb/mcb-internal.h +++ b/drivers/mcb/mcb-internal.h @@ -112,6 +112,15 @@ struct chameleon_bdd { u32 size; } __packed; +struct chameleon_bar { + u32 addr; + u32 size; +}; + +#define BAR_CNT(x) ((x) & 0x07) +#define CHAMELEON_BAR_MAX 6 +#define BAR_DESC_SIZE(x) ((x) * sizeof(struct chameleon_bar) + sizeof(__le32)) + int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, void __iomem *base); diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index dbecbed0d258..4ca2739b4fad 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p) } static int chameleon_parse_bdd(struct mcb_bus *bus, - phys_addr_t mapbase, + struct chameleon_bar *cb, void __iomem *base) { return 0; } static int chameleon_parse_gdd(struct mcb_bus *bus, - phys_addr_t mapbase, - void __iomem *base) + struct chameleon_bar *cb, + void __iomem *base, int bar_count) { struct chameleon_gdd __iomem *gdd = (struct chameleon_gdd __iomem *) base; struct mcb_device *mdev; + u32 dev_mapbase; u32 offset; u32 size; int ret; @@ -61,13 +62,39 @@ static int chameleon_parse_gdd(struct mcb_bus *bus, mdev->group = GDD_GRP(reg2); mdev->inst = GDD_INS(reg2); + /* + * If the BAR is missing, dev_mapbase is zero, or if the + * device is IO mapped we just print a warning and go on with the + * next device, instead of completely stop the gdd parser + */ + if (mdev->bar > bar_count - 1) { + pr_info("No BAR for 16z%03d\n", mdev->id); + ret = 0; + goto err; + } + + dev_mapbase = cb[mdev->bar].addr; + if (!dev_mapbase) { + pr_info("BAR not assigned for 16z%03d\n", mdev->id); + ret = 0; + goto err; + } + + if (dev_mapbase & 0x01) { + pr_info("IO mapped Device (16z%03d) not yet supported\n", + mdev->id); + ret = 0; + goto err; + } + pr_debug("Found a 16z%03d\n", mdev->id); mdev->irq.start = GDD_IRQ(reg1); mdev->irq.end = GDD_IRQ(reg1); mdev->irq.flags = IORESOURCE_IRQ; - mdev->mem.start = mapbase + offset; + mdev->mem.start = dev_mapbase + offset; + mdev->mem.end = mdev->mem.start + size - 1; mdev->mem.flags = IORESOURCE_MEM; @@ -85,13 +112,76 @@ err: return ret; } +static void chameleon_parse_bar(void __iomem *base, + struct chameleon_bar *cb, int bar_count) +{ + char __iomem *p = base; + int i; + + /* skip reg1 */ + p += sizeof(__le32); + + for (i = 0; i < bar_count; i++) { + cb[i].addr = readl(p); + cb[i].size = readl(p + 4); + + p += sizeof(struct chameleon_bar); + } +} + +static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase, + struct chameleon_bar **cb) +{ + struct chameleon_bar *c; + int bar_count; + __le32 reg; + u32 dtype; + + /* + * For those devices which are not connected + * to the PCI Bus (e.g. LPC) there is a bar + * descriptor located directly after the + * chameleon header. This header is comparable + * to a PCI header. + */ + dtype = get_next_dtype(*base); + if (dtype == CHAMELEON_DTYPE_BAR) { + reg = readl(*base); + + bar_count = BAR_CNT(reg); + if (bar_count <= 0 && bar_count > CHAMELEON_BAR_MAX) + return -ENODEV; + + c = kcalloc(bar_count, sizeof(struct chameleon_bar), + GFP_KERNEL); + if (!c) + return -ENOMEM; + + chameleon_parse_bar(*base, c, bar_count); + *base += BAR_DESC_SIZE(bar_count); + } else { + c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL); + if (!c) + return -ENOMEM; + + bar_count = 1; + c->addr = mapbase; + } + + *cb = c; + + return bar_count; +} + int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, void __iomem *base) { - char __iomem *p = base; struct chameleon_fpga_header *header; - uint32_t dtype; + struct chameleon_bar *cb; + char __iomem *p = base; int num_cells = 0; + uint32_t dtype; + int bar_count; int ret = 0; u32 hsize; @@ -108,8 +198,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, if (header->magic != CHAMELEONV2_MAGIC) { pr_err("Unsupported chameleon version 0x%x\n", header->magic); - kfree(header); - return -ENODEV; + ret = -ENODEV; + goto free_header; } p += hsize; @@ -119,16 +209,20 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", header->filename); + bar_count = chameleon_get_bar(&p, mapbase, &cb); + if (bar_count < 0) + goto free_header; + for_each_chameleon_cell(dtype, p) { switch (dtype) { case CHAMELEON_DTYPE_GENERAL: - ret = chameleon_parse_gdd(bus, mapbase, p); + ret = chameleon_parse_gdd(bus, cb, p, bar_count); if (ret < 0) - goto out; + goto free_bar; p += sizeof(struct chameleon_gdd); break; case CHAMELEON_DTYPE_BRIDGE: - chameleon_parse_bdd(bus, mapbase, p); + chameleon_parse_bdd(bus, cb, p); p += sizeof(struct chameleon_bdd); break; case CHAMELEON_DTYPE_END: @@ -136,8 +230,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, default: pr_err("Invalid chameleon descriptor type 0x%x\n", dtype); - kfree(header); - return -EINVAL; + ret = -EINVAL; + goto free_bar; } num_cells++; } @@ -145,11 +239,15 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, if (num_cells == 0) num_cells = -EINVAL; + kfree(cb); kfree(header); return num_cells; -out: +free_bar: + kfree(cb); +free_header: kfree(header); + return ret; } EXPORT_SYMBOL_GPL(chameleon_parse_cells); -- cgit v1.2.3-58-ga151 From b58189b3c5cf54689a0c154e4d9cff918a29ab0b Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 26 Aug 2016 09:35:00 +0200 Subject: mcb: remove sub-device handling code The MEN Chameleon specification states that a chameleon FPGA can include a bridge descriptor, which then opens up a new bus behind this bridge. MCB included subdevice handling code in the core, but no support for bus descriptors in the parser, due to a lack of hardware access. As this is technically dead code, but it gets executed on a device add, I've decided to remove it. Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-core.c | 17 ----------------- include/linux/mcb.h | 4 ---- 2 files changed, 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index 6f2c8522e14a..5306966efc11 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -369,7 +369,6 @@ struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus) if (!dev) return NULL; - INIT_LIST_HEAD(&dev->bus_list); dev->bus = bus; return dev; @@ -405,20 +404,6 @@ static int __mcb_bus_add_devices(struct device *dev, void *data) return 0; } -static int __mcb_bus_add_child(struct device *dev, void *data) -{ - struct mcb_device *mdev = to_mcb_device(dev); - struct mcb_bus *child; - - BUG_ON(!mdev->is_added); - child = mdev->subordinate; - - if (child) - mcb_bus_add_devices(child); - - return 0; -} - /** * mcb_bus_add_devices() - Add devices in the bus' internal device list * @bus: The @mcb_bus we add the devices @@ -428,8 +413,6 @@ static int __mcb_bus_add_child(struct device *dev, void *data) void mcb_bus_add_devices(const struct mcb_bus *bus) { bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices); - bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_child); - } EXPORT_SYMBOL_GPL(mcb_bus_add_devices); diff --git a/include/linux/mcb.h b/include/linux/mcb.h index a6733d3fe72b..ee5200d660b0 100644 --- a/include/linux/mcb.h +++ b/include/linux/mcb.h @@ -50,10 +50,8 @@ static inline struct mcb_bus *to_mcb_bus(struct device *dev) /** * struct mcb_device - MEN Chameleon Bus device * - * @bus_list: internal list handling for bus code * @dev: device in kernel representation * @bus: mcb bus the device is plugged to - * @subordinate: subordinate MCBus in case of bridge * @is_added: flag to check if device is added to bus * @driver: associated mcb_driver * @id: mcb device id @@ -66,10 +64,8 @@ static inline struct mcb_bus *to_mcb_bus(struct device *dev) * @memory: memory resource */ struct mcb_device { - struct list_head bus_list; struct device dev; struct mcb_bus *bus; - struct mcb_bus *subordinate; bool is_added; struct mcb_driver *driver; u16 id; -- cgit v1.2.3-58-ga151 From 655745b0d1ebeceb0453f1c6adca154a4fac0ad5 Mon Sep 17 00:00:00 2001 From: Muhammad Falak R Wani Date: Fri, 20 May 2016 17:48:56 +0530 Subject: VMCI: use memdup_user(). Use memdup_user to duplicate a memory region from user-space to kernel-space, instead of open coding using kmalloc & copy_from_user. Signed-off-by: Muhammad Falak R Wani Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_host.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index 9ec262a52656..ec090105eb4b 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -381,18 +381,12 @@ static int vmci_host_do_send_datagram(struct vmci_host_dev *vmci_host_dev, return -EINVAL; } - dg = kmalloc(send_info.len, GFP_KERNEL); - if (!dg) { + dg = memdup_user((void __user *)(uintptr_t)send_info.addr, + send_info.len); + if (IS_ERR(dg)) { vmci_ioctl_err( "cannot allocate memory to dispatch datagram\n"); - return -ENOMEM; - } - - if (copy_from_user(dg, (void __user *)(uintptr_t)send_info.addr, - send_info.len)) { - vmci_ioctl_err("error getting datagram\n"); - kfree(dg); - return -EFAULT; + return PTR_ERR(dg); } if (VMCI_DG_SIZE(dg) != send_info.len) { -- cgit v1.2.3-58-ga151 From fa54b326803d91b04705a6adf0ff963593a9fe5c Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Fri, 29 Apr 2016 16:41:02 -0500 Subject: vme: change LM callback argument to void pointer Make the location monitor callback function prototype more useful by changing the argument from an integer to a void pointer. All VME bridge drivers were simply passing the location monitor index (e.g. 0-3) as the argument to these callbacks. It is much more useful to pass back a pointer to data that the callback-registering driver cares about. There appear to be no in-kernel callers of vme_lm_attach (or vme_lme_request for that matter), so this change only affects the VME subsystem and bridge drivers. This has been tested with Tsi148 hardware, but the CA91Cx42 changes have only been compiled. Signed-off-by: Aaron Sierra Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- Documentation/vme_api.txt | 4 ++-- drivers/vme/bridges/vme_ca91cx42.c | 6 ++++-- drivers/vme/bridges/vme_ca91cx42.h | 3 ++- drivers/vme/bridges/vme_tsi148.c | 6 ++++-- drivers/vme/bridges/vme_tsi148.h | 3 ++- drivers/vme/vme.c | 4 ++-- drivers/vme/vme_bridge.h | 3 ++- include/linux/vme.h | 2 +- 8 files changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/Documentation/vme_api.txt b/Documentation/vme_api.txt index ca5b82797f6c..4ca63a6eca24 100644 --- a/Documentation/vme_api.txt +++ b/Documentation/vme_api.txt @@ -385,13 +385,13 @@ location monitor location. Each location monitor can monitor a number of adjacent locations: int vme_lm_attach(struct vme_resource *res, int num, - void (*callback)(int)); + void (*callback)(void *)); int vme_lm_detach(struct vme_resource *res, int num); The callback function is declared as follows. - void callback(int num); + void callback(void *data); Slot Detection diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index 9f2c834e43e0..da29dbe6c9c8 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -69,7 +69,7 @@ static u32 ca91cx42_LM_irqhandler(struct ca91cx42_driver *bridge, u32 stat) for (i = 0; i < 4; i++) { if (stat & CA91CX42_LINT_LM[i]) { /* We only enable interrupts if the callback is set */ - bridge->lm_callback[i](i); + bridge->lm_callback[i](bridge->lm_data[i]); serviced |= CA91CX42_LINT_LM[i]; } } @@ -1410,7 +1410,7 @@ static int ca91cx42_lm_get(struct vme_lm_resource *lm, * Callback will be passed the monitor triggered. */ static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor, - void (*callback)(int)) + void (*callback)(void *), void *data) { u32 lm_ctl, tmp; struct ca91cx42_driver *bridge; @@ -1438,6 +1438,7 @@ static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor, /* Attach callback */ bridge->lm_callback[monitor] = callback; + bridge->lm_data[monitor] = data; /* Enable Location Monitor interrupt */ tmp = ioread32(bridge->base + LINT_EN); @@ -1477,6 +1478,7 @@ static int ca91cx42_lm_detach(struct vme_lm_resource *lm, int monitor) /* Detach callback */ bridge->lm_callback[monitor] = NULL; + bridge->lm_data[monitor] = NULL; /* If all location monitors disabled, disable global Location Monitor */ if ((tmp & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | diff --git a/drivers/vme/bridges/vme_ca91cx42.h b/drivers/vme/bridges/vme_ca91cx42.h index d54119e59d55..f35c9f5348a9 100644 --- a/drivers/vme/bridges/vme_ca91cx42.h +++ b/drivers/vme/bridges/vme_ca91cx42.h @@ -43,7 +43,8 @@ struct ca91cx42_driver { wait_queue_head_t dma_queue; wait_queue_head_t iack_queue; wait_queue_head_t mbox_queue; - void (*lm_callback[4])(int); /* Called in interrupt handler */ + void (*lm_callback[4])(void *); /* Called in interrupt handler */ + void *lm_data[4]; void *crcsr_kernel; dma_addr_t crcsr_bus; struct mutex vme_rmw; /* Only one RMW cycle at a time */ diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 4bc5d451ec6c..2d3ba1a1d42d 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -102,7 +102,7 @@ static u32 tsi148_LM_irqhandler(struct tsi148_driver *bridge, u32 stat) for (i = 0; i < 4; i++) { if (stat & TSI148_LCSR_INTS_LMS[i]) { /* We only enable interrupts if the callback is set */ - bridge->lm_callback[i](i); + bridge->lm_callback[i](bridge->lm_data[i]); serviced |= TSI148_LCSR_INTC_LMC[i]; } } @@ -2047,7 +2047,7 @@ static int tsi148_lm_get(struct vme_lm_resource *lm, * Callback will be passed the monitor triggered. */ static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, - void (*callback)(int)) + void (*callback)(void *), void *data) { u32 lm_ctl, tmp; struct vme_bridge *tsi148_bridge; @@ -2077,6 +2077,7 @@ static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, /* Attach callback */ bridge->lm_callback[monitor] = callback; + bridge->lm_data[monitor] = data; /* Enable Location Monitor interrupt */ tmp = ioread32be(bridge->base + TSI148_LCSR_INTEN); @@ -2124,6 +2125,7 @@ static int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor) /* Detach callback */ bridge->lm_callback[monitor] = NULL; + bridge->lm_data[monitor] = NULL; /* If all location monitors disabled, disable global Location Monitor */ if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S | diff --git a/drivers/vme/bridges/vme_tsi148.h b/drivers/vme/bridges/vme_tsi148.h index f5ed14382a8d..0935d85d32ec 100644 --- a/drivers/vme/bridges/vme_tsi148.h +++ b/drivers/vme/bridges/vme_tsi148.h @@ -38,7 +38,8 @@ struct tsi148_driver { void __iomem *base; /* Base Address of device registers */ wait_queue_head_t dma_queue[2]; wait_queue_head_t iack_queue; - void (*lm_callback[4])(int); /* Called in interrupt handler */ + void (*lm_callback[4])(void *); /* Called in interrupt handler */ + void *lm_data[4]; void *crcsr_kernel; dma_addr_t crcsr_bus; struct vme_master_resource *flush_image; diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 37ac0a58e59a..4693b7bd3364 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -1321,7 +1321,7 @@ int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base, EXPORT_SYMBOL(vme_lm_get); int vme_lm_attach(struct vme_resource *resource, int monitor, - void (*callback)(int)) + void (*callback)(void *), void *data) { struct vme_bridge *bridge = find_bridge(resource); struct vme_lm_resource *lm; @@ -1338,7 +1338,7 @@ int vme_lm_attach(struct vme_resource *resource, int monitor, return -EINVAL; } - return bridge->lm_attach(lm, monitor, callback); + return bridge->lm_attach(lm, monitor, callback, data); } EXPORT_SYMBOL(vme_lm_attach); diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h index cb8246fd97be..2662e916b96a 100644 --- a/drivers/vme/vme_bridge.h +++ b/drivers/vme/vme_bridge.h @@ -160,7 +160,8 @@ struct vme_bridge { int (*lm_set) (struct vme_lm_resource *, unsigned long long, u32, u32); int (*lm_get) (struct vme_lm_resource *, unsigned long long *, u32 *, u32 *); - int (*lm_attach) (struct vme_lm_resource *, int, void (*callback)(int)); + int (*lm_attach)(struct vme_lm_resource *, int, + void (*callback)(void *), void *); int (*lm_detach) (struct vme_lm_resource *, int); /* CR/CSR space functions */ diff --git a/include/linux/vme.h b/include/linux/vme.h index 71e4a6dec5ac..ea6095deba20 100644 --- a/include/linux/vme.h +++ b/include/linux/vme.h @@ -166,7 +166,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *); int vme_lm_count(struct vme_resource *); int vme_lm_set(struct vme_resource *, unsigned long long, u32, u32); int vme_lm_get(struct vme_resource *, unsigned long long *, u32 *, u32 *); -int vme_lm_attach(struct vme_resource *, int, void (*callback)(int)); +int vme_lm_attach(struct vme_resource *, int, void (*callback)(void *), void *); int vme_lm_detach(struct vme_resource *, int); void vme_lm_free(struct vme_resource *); -- cgit v1.2.3-58-ga151 From 553ebb8e40d5a9135be036cc6ebfdf40bdc1f710 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Tue, 14 Jun 2016 16:36:55 +0200 Subject: vme: Fix module autoload These drivers have a PCI device ID table but the PCI module alias information is not created so module autoloading won't work. Signed-off-by: Alessio Igor Bogani Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_ca91cx42.c | 2 ++ drivers/vme/bridges/vme_tsi148.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index da29dbe6c9c8..6b5ee896af63 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -47,6 +47,8 @@ static const struct pci_device_id ca91cx42_ids[] = { { }, }; +MODULE_DEVICE_TABLE(pci, ca91cx42_ids); + static struct pci_driver ca91cx42_driver = { .name = driver_name, .id_table = ca91cx42_ids, diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 2d3ba1a1d42d..fc1b634b969a 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -50,6 +50,8 @@ static const struct pci_device_id tsi148_ids[] = { { }, }; +MODULE_DEVICE_TABLE(pci, tsi148_ids); + static struct pci_driver tsi148_driver = { .name = driver_name, .id_table = tsi148_ids, -- cgit v1.2.3-58-ga151 From 050c3d52cc7810d9d17b8cd231708609af6876ae Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 3 Jul 2016 14:05:56 -0400 Subject: vme: make core vme support explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/vme/Kconfig:menuconfig VME_BUS drivers/vme/Kconfig: bool "VME bridge support" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We replace module.h and moduleparam.h (unused) with init.h and also export.h ; the latter since this file does export some syms. Since this is a struct bus_type and not a platform_driver, we don't have any ".suppress_bind_attrs" to be concerned about when we drop the ".remove" code from this file. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Cc: Manohar Vanga Signed-off-by: Paul Gortmaker Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/vme.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 4693b7bd3364..15b64076bc26 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -13,8 +13,8 @@ * option) any later version. */ -#include -#include +#include +#include #include #include #include @@ -39,7 +39,6 @@ static unsigned int vme_bus_numbers; static LIST_HEAD(vme_bus_list); static DEFINE_MUTEX(vme_buses_lock); -static void __exit vme_exit(void); static int __init vme_init(void); static struct vme_dev *dev_to_vme_dev(struct device *dev) @@ -1622,25 +1621,10 @@ static int vme_bus_probe(struct device *dev) return retval; } -static int vme_bus_remove(struct device *dev) -{ - int retval = -ENODEV; - struct vme_driver *driver; - struct vme_dev *vdev = dev_to_vme_dev(dev); - - driver = dev->platform_data; - - if (driver->remove != NULL) - retval = driver->remove(vdev); - - return retval; -} - struct bus_type vme_bus_type = { .name = "vme", .match = vme_bus_match, .probe = vme_bus_probe, - .remove = vme_bus_remove, }; EXPORT_SYMBOL(vme_bus_type); @@ -1648,11 +1632,4 @@ static int __init vme_init(void) { return bus_register(&vme_bus_type); } - -static void __exit vme_exit(void) -{ - bus_unregister(&vme_bus_type); -} - subsys_initcall(vme_init); -module_exit(vme_exit); -- cgit v1.2.3-58-ga151 From 658bcdae9c6755806e66b33e29d56b33a3ff421a Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 7 Jul 2016 19:51:52 +0100 Subject: vme: Adding Fake VME driver This patch introduces a fake VME bridge driver. This driver currently emulates a subset of the VME bridge functionality. This allows some VME subsystem development and even some VME device driver development to be carried out in the absence of a proper VME bus. Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/Kconfig | 8 + drivers/vme/bridges/Makefile | 1 + drivers/vme/bridges/vme_fake.c | 1299 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1308 insertions(+) create mode 100644 drivers/vme/bridges/vme_fake.c (limited to 'drivers') diff --git a/drivers/vme/bridges/Kconfig b/drivers/vme/bridges/Kconfig index f6d854584906..f6ddc3715401 100644 --- a/drivers/vme/bridges/Kconfig +++ b/drivers/vme/bridges/Kconfig @@ -13,3 +13,11 @@ config VME_TSI148 help If you say Y here you get support for the Tundra TSI148 VME bridge chip. + +config VME_FAKE + tristate "Fake" + help + If you say Y here you get support for the fake VME bridge. This + provides a virtualised VME Bus for devices with no VME bridge. This + is mainly useful for VME development (in the absence of VME + hardware). diff --git a/drivers/vme/bridges/Makefile b/drivers/vme/bridges/Makefile index 59638afcd502..b074542495c5 100644 --- a/drivers/vme/bridges/Makefile +++ b/drivers/vme/bridges/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_VME_CA91CX42) += vme_ca91cx42.o obj-$(CONFIG_VME_TSI148) += vme_tsi148.o +obj-$(CONFIG_VME_FAKE) += vme_fake.o diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c new file mode 100644 index 000000000000..7ef298b289f4 --- /dev/null +++ b/drivers/vme/bridges/vme_fake.c @@ -0,0 +1,1299 @@ +/* + * Fake VME bridge support. + * + * This drive provides a fake VME bridge chip, this enables debugging of the + * VME framework in the absence of a VME system. + * + * This driver has to do a number of things in software that would be driven + * by hardware if it was available, it will also result in extra overhead at + * times when compared with driving actual hardware. + * + * Author: Martyn Welch + * Copyright (c) 2014 Martyn Welch + * + * Based on vme_tsi148.c: + * + * Author: Martyn Welch + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on work by Tom Armistead and Ajit Prem + * Copyright 2004 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../vme_bridge.h" + +/* + * Define the number of each that the fake driver supports. + */ +#define FAKE_MAX_MASTER 8 /* Max Master Windows */ +#define FAKE_MAX_SLAVE 8 /* Max Slave Windows */ + +/* Structures to hold information normally held in device registers */ +struct fake_slave_window { + int enabled; + unsigned long long vme_base; + unsigned long long size; + dma_addr_t buf_base; + u32 aspace; + u32 cycle; +}; + +struct fake_master_window { + int enabled; + unsigned long long vme_base; + unsigned long long size; + u32 aspace; + u32 cycle; + u32 dwidth; +}; + +/* Structure used to hold driver specific information */ +struct fake_driver { + struct vme_bridge *parent; + struct fake_slave_window slaves[FAKE_MAX_SLAVE]; + struct fake_master_window masters[FAKE_MAX_MASTER]; + u32 lm_enabled; + unsigned long long lm_base; + u32 lm_aspace; + u32 lm_cycle; + void (*lm_callback[4])(void *); + void *lm_data[4]; + struct tasklet_struct int_tasklet; + int int_level; + int int_statid; + void *crcsr_kernel; + dma_addr_t crcsr_bus; + /* Only one VME interrupt can be generated at a time, provide locking */ + struct mutex vme_int; +}; + +/* Module parameter */ +static int geoid; + +static const char driver_name[] = "vme_fake"; + +static struct vme_bridge *exit_pointer; + +static struct device *vme_root; + +/* + * Calling VME bus interrupt callback if provided. + */ +static void fake_VIRQ_tasklet(unsigned long data) +{ + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = (struct vme_bridge *) data; + bridge = fake_bridge->driver_priv; + + vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid); +} + +/* + * Configure VME interrupt + */ +static void fake_irq_set(struct vme_bridge *fake_bridge, int level, + int state, int sync) +{ + /* Nothing to do */ +} + +/* + * Generate a VME bus interrupt at the requested level & vector. Wait for + * interrupt to be acked. + */ +static int fake_irq_generate(struct vme_bridge *fake_bridge, int level, + int statid) +{ + struct fake_driver *bridge; + + bridge = fake_bridge->driver_priv; + + mutex_lock(&bridge->vme_int); + + bridge->int_level = level; + + bridge->int_statid = statid; + + /* + * Schedule tasklet to run VME handler to emulate normal VME interrupt + * handler behaviour. + */ + tasklet_schedule(&bridge->int_tasklet); + + mutex_unlock(&bridge->vme_int); + + return 0; +} + +/* + * Initialize a slave window with the requested attributes. + */ +static int fake_slave_set(struct vme_slave_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + dma_addr_t buf_base, u32 aspace, u32 cycle) +{ + unsigned int i, granularity = 0; + unsigned long long vme_bound; + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = image->parent; + bridge = fake_bridge->driver_priv; + + i = image->number; + + switch (aspace) { + case VME_A16: + granularity = 0x10; + break; + case VME_A24: + granularity = 0x1000; + break; + case VME_A32: + granularity = 0x10000; + break; + case VME_A64: + granularity = 0x10000; + break; + case VME_CRCSR: + case VME_USER1: + case VME_USER2: + case VME_USER3: + case VME_USER4: + default: + pr_err("Invalid address space\n"); + return -EINVAL; + } + + /* + * Bound address is a valid address for the window, adjust + * accordingly + */ + vme_bound = vme_base + size - granularity; + + if (vme_base & (granularity - 1)) { + pr_err("Invalid VME base alignment\n"); + return -EINVAL; + } + if (vme_bound & (granularity - 1)) { + pr_err("Invalid VME bound alignment\n"); + return -EINVAL; + } + + mutex_lock(&image->mtx); + + bridge->slaves[i].enabled = enabled; + bridge->slaves[i].vme_base = vme_base; + bridge->slaves[i].size = size; + bridge->slaves[i].buf_base = buf_base; + bridge->slaves[i].aspace = aspace; + bridge->slaves[i].cycle = cycle; + + mutex_unlock(&image->mtx); + + return 0; +} + +/* + * Get slave window configuration. + */ +static int fake_slave_get(struct vme_slave_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + dma_addr_t *buf_base, u32 *aspace, u32 *cycle) +{ + unsigned int i; + struct fake_driver *bridge; + + bridge = image->parent->driver_priv; + + i = image->number; + + mutex_lock(&image->mtx); + + *enabled = bridge->slaves[i].enabled; + *vme_base = bridge->slaves[i].vme_base; + *size = bridge->slaves[i].size; + *buf_base = bridge->slaves[i].buf_base; + *aspace = bridge->slaves[i].aspace; + *cycle = bridge->slaves[i].cycle; + + mutex_unlock(&image->mtx); + + return 0; +} + +/* + * Set the attributes of an outbound window. + */ +static int fake_master_set(struct vme_master_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + u32 aspace, u32 cycle, u32 dwidth) +{ + int retval = 0; + unsigned int i; + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = image->parent; + + bridge = fake_bridge->driver_priv; + + /* Verify input data */ + if (vme_base & 0xFFFF) { + pr_err("Invalid VME Window alignment\n"); + retval = -EINVAL; + goto err_window; + } + + if (size & 0xFFFF) { + spin_unlock(&image->lock); + pr_err("Invalid size alignment\n"); + retval = -EINVAL; + goto err_window; + } + + if ((size == 0) && (enabled != 0)) { + pr_err("Size must be non-zero for enabled windows\n"); + retval = -EINVAL; + goto err_window; + } + + /* Setup data width */ + switch (dwidth) { + case VME_D8: + case VME_D16: + case VME_D32: + break; + default: + spin_unlock(&image->lock); + pr_err("Invalid data width\n"); + retval = -EINVAL; + goto err_dwidth; + } + + /* Setup address space */ + switch (aspace) { + case VME_A16: + case VME_A24: + case VME_A32: + case VME_A64: + case VME_CRCSR: + case VME_USER1: + case VME_USER2: + case VME_USER3: + case VME_USER4: + break; + default: + spin_unlock(&image->lock); + pr_err("Invalid address space\n"); + retval = -EINVAL; + goto err_aspace; + } + + spin_lock(&image->lock); + + i = image->number; + + bridge->masters[i].enabled = enabled; + bridge->masters[i].vme_base = vme_base; + bridge->masters[i].size = size; + bridge->masters[i].aspace = aspace; + bridge->masters[i].cycle = cycle; + bridge->masters[i].dwidth = dwidth; + + spin_unlock(&image->lock); + + return 0; + +err_aspace: +err_dwidth: +err_window: + return retval; + +} + +/* + * Set the attributes of an outbound window. + */ +static int __fake_master_get(struct vme_master_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth) +{ + unsigned int i; + struct fake_driver *bridge; + + bridge = image->parent->driver_priv; + + i = image->number; + + *enabled = bridge->masters[i].enabled; + *vme_base = bridge->masters[i].vme_base; + *size = bridge->masters[i].size; + *aspace = bridge->masters[i].aspace; + *cycle = bridge->masters[i].cycle; + *dwidth = bridge->masters[i].dwidth; + + return 0; +} + + +static int fake_master_get(struct vme_master_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth) +{ + int retval; + + spin_lock(&image->lock); + + retval = __fake_master_get(image, enabled, vme_base, size, aspace, + cycle, dwidth); + + spin_unlock(&image->lock); + + return retval; +} + + +void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, + u32 aspace, u32 cycle) +{ + struct vme_bridge *fake_bridge; + unsigned long long lm_base; + u32 lm_aspace, lm_cycle; + int i; + struct vme_lm_resource *lm; + struct list_head *pos = NULL, *n; + + /* Get vme_bridge */ + fake_bridge = bridge->parent; + + /* Loop through each location monitor resource */ + list_for_each_safe(pos, n, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + + /* If disabled, we're done */ + if (bridge->lm_enabled == 0) + return; + + lm_base = bridge->lm_base; + lm_aspace = bridge->lm_aspace; + lm_cycle = bridge->lm_cycle; + + /* First make sure that the cycle and address space match */ + if ((lm_aspace == aspace) && (lm_cycle == cycle)) { + for (i = 0; i < lm->monitors; i++) { + /* Each location monitor covers 8 bytes */ + if (((lm_base + (8 * i)) <= addr) && + ((lm_base + (8 * i) + 8) > addr)) { + if (bridge->lm_callback[i] != NULL) + bridge->lm_callback[i]( + bridge->lm_data[i]); + } + } + } + } +} + +static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr, + u32 aspace, u32 cycle) +{ + u8 retval = 0xff; + int i; + unsigned long long start, end, offset; + u8 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + if ((addr >= start) && (addr < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset); + retval = *loc; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + + return retval; +} + +static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr, + u32 aspace, u32 cycle) +{ + u16 retval = 0xffff; + int i; + unsigned long long start, end, offset; + u16 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if ((addr >= start) && ((addr + 1) < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset); + retval = *loc; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + + return retval; +} + +static u32 fake_vmeread32(struct fake_driver *bridge, unsigned long long addr, + u32 aspace, u32 cycle) +{ + u32 retval = 0xffffffff; + int i; + unsigned long long start, end, offset; + u32 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if ((addr >= start) && ((addr + 3) < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset); + retval = *loc; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + + return retval; +} + +static ssize_t fake_master_read(struct vme_master_resource *image, void *buf, + size_t count, loff_t offset) +{ + int retval; + u32 aspace, cycle, dwidth; + struct vme_bridge *fake_bridge; + struct fake_driver *priv; + int i; + unsigned long long addr; + unsigned int done = 0; + unsigned int count32; + + fake_bridge = image->parent; + + priv = fake_bridge->driver_priv; + + i = image->number; + + addr = (unsigned long long)priv->masters[i].vme_base + offset; + aspace = priv->masters[i].aspace; + cycle = priv->masters[i].cycle; + dwidth = priv->masters[i].dwidth; + + spin_lock(&image->lock); + + /* The following code handles VME address alignment. We cannot use + * memcpy_xxx here because it may cut data transfers in to 8-bit + * cycles when D16 or D32 cycles are required on the VME bus. + * On the other hand, the bridge itself assures that the maximum data + * cycle configured for the transfer is used and splits it + * automatically for non-aligned addresses, so we don't want the + * overhead of needlessly forcing small transfers for the entire cycle. + */ + if (addr & 0x1) { + *(u8 *)buf = fake_vmeread8(priv, addr, aspace, cycle); + done += 1; + if (done == count) + goto out; + } + if ((dwidth == VME_D16) || (dwidth == VME_D32)) { + if ((addr + done) & 0x2) { + if ((count - done) < 2) { + *(u8 *)(buf + done) = fake_vmeread8(priv, + addr + done, aspace, cycle); + done += 1; + goto out; + } else { + *(u16 *)(buf + done) = fake_vmeread16(priv, + addr + done, aspace, cycle); + done += 2; + } + } + } + + if (dwidth == VME_D32) { + count32 = (count - done) & ~0x3; + while (done < count32) { + *(u32 *)(buf + done) = fake_vmeread32(priv, addr + done, + aspace, cycle); + done += 4; + } + } else if (dwidth == VME_D16) { + count32 = (count - done) & ~0x3; + while (done < count32) { + *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done, + aspace, cycle); + done += 2; + } + } else if (dwidth == VME_D8) { + count32 = (count - done); + while (done < count32) { + *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, + aspace, cycle); + done += 1; + } + + } + + if ((dwidth == VME_D16) || (dwidth == VME_D32)) { + if ((count - done) & 0x2) { + *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done, + aspace, cycle); + done += 2; + } + } + if ((count - done) & 0x1) { + *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, aspace, + cycle); + done += 1; + } + +out: + retval = count; + + spin_unlock(&image->lock); + + return retval; +} + +void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, + unsigned long long addr, u32 aspace, u32 cycle) +{ + int i; + unsigned long long start, end, offset; + u8 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if ((addr >= start) && (addr < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset); + *loc = *buf; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + +} + +void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, + unsigned long long addr, u32 aspace, u32 cycle) +{ + int i; + unsigned long long start, end, offset; + u16 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if ((addr >= start) && ((addr + 1) < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset); + *loc = *buf; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + +} + +void fake_vmewrite32(struct fake_driver *bridge, u32 *buf, + unsigned long long addr, u32 aspace, u32 cycle) +{ + int i; + unsigned long long start, end, offset; + u32 *loc; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + if (aspace != bridge->slaves[i].aspace) + continue; + + if (cycle != bridge->slaves[i].cycle) + continue; + + start = bridge->slaves[i].vme_base; + end = bridge->slaves[i].vme_base + bridge->slaves[i].size; + + if ((addr >= start) && ((addr + 3) < end)) { + offset = addr - bridge->slaves[i].vme_base; + loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset); + *loc = *buf; + + break; + } + } + + fake_lm_check(bridge, addr, aspace, cycle); + +} + +static ssize_t fake_master_write(struct vme_master_resource *image, void *buf, + size_t count, loff_t offset) +{ + int retval = 0; + u32 aspace, cycle, dwidth; + unsigned long long addr; + int i; + unsigned int done = 0; + unsigned int count32; + + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = image->parent; + + bridge = fake_bridge->driver_priv; + + i = image->number; + + addr = bridge->masters[i].vme_base + offset; + aspace = bridge->masters[i].aspace; + cycle = bridge->masters[i].cycle; + dwidth = bridge->masters[i].dwidth; + + spin_lock(&image->lock); + + /* Here we apply for the same strategy we do in master_read + * function in order to assure the correct cycles. + */ + if (addr & 0x1) { + fake_vmewrite8(bridge, (u8 *)buf, addr, aspace, cycle); + done += 1; + if (done == count) + goto out; + } + + if ((dwidth == VME_D16) || (dwidth == VME_D32)) { + if ((addr + done) & 0x2) { + if ((count - done) < 2) { + fake_vmewrite8(bridge, (u8 *)(buf + done), + addr + done, aspace, cycle); + done += 1; + goto out; + } else { + fake_vmewrite16(bridge, (u16 *)(buf + done), + addr + done, aspace, cycle); + done += 2; + } + } + } + + if (dwidth == VME_D32) { + count32 = (count - done) & ~0x3; + while (done < count32) { + fake_vmewrite32(bridge, (u32 *)(buf + done), + addr + done, aspace, cycle); + done += 4; + } + } else if (dwidth == VME_D16) { + count32 = (count - done) & ~0x3; + while (done < count32) { + fake_vmewrite16(bridge, (u16 *)(buf + done), + addr + done, aspace, cycle); + done += 2; + } + } else if (dwidth == VME_D8) { + count32 = (count - done); + while (done < count32) { + fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, + aspace, cycle); + done += 1; + } + + } + + if ((dwidth == VME_D16) || (dwidth == VME_D32)) { + if ((count - done) & 0x2) { + fake_vmewrite16(bridge, (u16 *)(buf + done), + addr + done, aspace, cycle); + done += 2; + } + } + + if ((count - done) & 0x1) { + fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, aspace, + cycle); + done += 1; + } + +out: + retval = count; + + spin_unlock(&image->lock); + + return retval; +} + +/* + * Perform an RMW cycle on the VME bus. + * + * Requires a previously configured master window, returns final value. + */ +static unsigned int fake_master_rmw(struct vme_master_resource *image, + unsigned int mask, unsigned int compare, unsigned int swap, + loff_t offset) +{ + u32 tmp, base; + u32 aspace, cycle; + int i; + struct fake_driver *bridge; + + bridge = image->parent->driver_priv; + + /* Find the PCI address that maps to the desired VME address */ + i = image->number; + + base = bridge->masters[i].vme_base; + aspace = bridge->masters[i].aspace; + cycle = bridge->masters[i].cycle; + + /* Lock image */ + spin_lock(&image->lock); + + /* Read existing value */ + tmp = fake_vmeread32(bridge, base + offset, aspace, cycle); + + /* Perform check */ + if ((tmp && mask) == (compare && mask)) { + tmp = tmp | (mask | swap); + tmp = tmp & (~mask | swap); + + /* Write back */ + fake_vmewrite32(bridge, &tmp, base + offset, aspace, cycle); + } + + /* Unlock image */ + spin_unlock(&image->lock); + + return tmp; +} + +/* + * All 4 location monitors reside at the same base - this is therefore a + * system wide configuration. + * + * This does not enable the LM monitor - that should be done when the first + * callback is attached and disabled when the last callback is removed. + */ +static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, + u32 aspace, u32 cycle) +{ + int i; + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = lm->parent; + + bridge = fake_bridge->driver_priv; + + mutex_lock(&lm->mtx); + + /* If we already have a callback attached, we can't move it! */ + for (i = 0; i < lm->monitors; i++) { + if (bridge->lm_callback[i] != NULL) { + mutex_unlock(&lm->mtx); + pr_err("Location monitor callback attached, can't reset\n"); + return -EBUSY; + } + } + + switch (aspace) { + case VME_A16: + case VME_A24: + case VME_A32: + case VME_A64: + break; + default: + mutex_unlock(&lm->mtx); + pr_err("Invalid address space\n"); + return -EINVAL; + } + + bridge->lm_base = lm_base; + bridge->lm_aspace = aspace; + bridge->lm_cycle = cycle; + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* Get configuration of the callback monitor and return whether it is enabled + * or disabled. + */ +static int fake_lm_get(struct vme_lm_resource *lm, + unsigned long long *lm_base, u32 *aspace, u32 *cycle) +{ + struct fake_driver *bridge; + + bridge = lm->parent->driver_priv; + + mutex_lock(&lm->mtx); + + *lm_base = bridge->lm_base; + *aspace = bridge->lm_aspace; + *cycle = bridge->lm_cycle; + + mutex_unlock(&lm->mtx); + + return bridge->lm_enabled; +} + +/* + * Attach a callback to a specific location monitor. + * + * Callback will be passed the monitor triggered. + */ +static int fake_lm_attach(struct vme_lm_resource *lm, int monitor, + void (*callback)(void *), void *data) +{ + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = lm->parent; + + bridge = fake_bridge->driver_priv; + + mutex_lock(&lm->mtx); + + /* Ensure that the location monitor is configured - need PGM or DATA */ + if (bridge->lm_cycle == 0) { + mutex_unlock(&lm->mtx); + pr_err("Location monitor not properly configured\n"); + return -EINVAL; + } + + /* Check that a callback isn't already attached */ + if (bridge->lm_callback[monitor] != NULL) { + mutex_unlock(&lm->mtx); + pr_err("Existing callback attached\n"); + return -EBUSY; + } + + /* Attach callback */ + bridge->lm_callback[monitor] = callback; + bridge->lm_data[monitor] = data; + + /* Ensure that global Location Monitor Enable set */ + bridge->lm_enabled = 1; + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* + * Detach a callback function forn a specific location monitor. + */ +static int fake_lm_detach(struct vme_lm_resource *lm, int monitor) +{ + u32 tmp; + int i; + struct fake_driver *bridge; + + bridge = lm->parent->driver_priv; + + mutex_lock(&lm->mtx); + + /* Detach callback */ + bridge->lm_callback[monitor] = NULL; + bridge->lm_data[monitor] = NULL; + + /* If all location monitors disabled, disable global Location Monitor */ + tmp = 0; + for (i = 0; i < lm->monitors; i++) { + if (bridge->lm_callback[i] != NULL) + tmp = 1; + } + + if (tmp == 0) + bridge->lm_enabled = 0; + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* + * Determine Geographical Addressing + */ +static int fake_slot_get(struct vme_bridge *fake_bridge) +{ + return geoid; +} + +static void *fake_alloc_consistent(struct device *parent, size_t size, + dma_addr_t *dma) +{ + void *alloc = kmalloc(size, GFP_KERNEL); + + if (alloc != NULL) + *dma = (dma_addr_t)(unsigned long)alloc; + + return alloc; +} + +static void fake_free_consistent(struct device *parent, size_t size, + void *vaddr, dma_addr_t dma) +{ + kfree(vaddr); +/* + dma_free_coherent(parent, size, vaddr, dma); +*/ +} + +/* + * Configure CR/CSR space + * + * Access to the CR/CSR can be configured at power-up. The location of the + * CR/CSR registers in the CR/CSR address space is determined by the boards + * Geographic address. + * + * Each board has a 512kB window, with the highest 4kB being used for the + * boards registers, this means there is a fix length 508kB window which must + * be mapped onto PCI memory. + */ +static int fake_crcsr_init(struct vme_bridge *fake_bridge) +{ + u32 vstat; + struct fake_driver *bridge; + + bridge = fake_bridge->driver_priv; + + /* Allocate mem for CR/CSR image */ + bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL); + bridge->crcsr_bus = (dma_addr_t)bridge->crcsr_kernel; + if (bridge->crcsr_kernel == NULL) + return -ENOMEM; + + vstat = fake_slot_get(fake_bridge); + + pr_info("CR/CSR Offset: %d\n", vstat); + + return 0; +} + +static void fake_crcsr_exit(struct vme_bridge *fake_bridge) +{ + struct fake_driver *bridge; + + bridge = fake_bridge->driver_priv; + + kfree(bridge->crcsr_kernel); +} + + +static int __init fake_init(void) +{ + int retval, i; + struct list_head *pos = NULL, *n; + struct vme_bridge *fake_bridge; + struct fake_driver *fake_device; + struct vme_master_resource *master_image; + struct vme_slave_resource *slave_image; + struct vme_lm_resource *lm; + + /* We need a fake parent device */ + vme_root = __root_device_register("vme", THIS_MODULE); + + /* If we want to support more than one bridge at some point, we need to + * dynamically allocate this so we get one per device. + */ + fake_bridge = kzalloc(sizeof(struct vme_bridge), GFP_KERNEL); + if (fake_bridge == NULL) { + retval = -ENOMEM; + goto err_struct; + } + + fake_device = kzalloc(sizeof(struct fake_driver), GFP_KERNEL); + if (fake_device == NULL) { + retval = -ENOMEM; + goto err_driver; + } + + fake_bridge->driver_priv = fake_device; + + fake_bridge->parent = vme_root; + + fake_device->parent = fake_bridge; + + /* Initialize wait queues & mutual exclusion flags */ + mutex_init(&fake_device->vme_int); + mutex_init(&fake_bridge->irq_mtx); + tasklet_init(&fake_device->int_tasklet, fake_VIRQ_tasklet, + (unsigned long) fake_bridge); + + strcpy(fake_bridge->name, driver_name); + + /* Add master windows to list */ + INIT_LIST_HEAD(&fake_bridge->master_resources); + for (i = 0; i < FAKE_MAX_MASTER; i++) { + master_image = kmalloc(sizeof(struct vme_master_resource), + GFP_KERNEL); + if (master_image == NULL) { + retval = -ENOMEM; + goto err_master; + } + master_image->parent = fake_bridge; + spin_lock_init(&master_image->lock); + master_image->locked = 0; + master_image->number = i; + master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | + VME_A64; + master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | + VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | + VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | + VME_PROG | VME_DATA; + master_image->width_attr = VME_D16 | VME_D32; + memset(&master_image->bus_resource, 0, + sizeof(struct resource)); + master_image->kern_base = NULL; + list_add_tail(&master_image->list, + &fake_bridge->master_resources); + } + + /* Add slave windows to list */ + INIT_LIST_HEAD(&fake_bridge->slave_resources); + for (i = 0; i < FAKE_MAX_SLAVE; i++) { + slave_image = kmalloc(sizeof(struct vme_slave_resource), + GFP_KERNEL); + if (slave_image == NULL) { + retval = -ENOMEM; + goto err_slave; + } + slave_image->parent = fake_bridge; + mutex_init(&slave_image->mtx); + slave_image->locked = 0; + slave_image->number = i; + slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 | + VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | + VME_USER3 | VME_USER4; + slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | + VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | + VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | + VME_PROG | VME_DATA; + list_add_tail(&slave_image->list, + &fake_bridge->slave_resources); + } + + /* Add location monitor to list */ + INIT_LIST_HEAD(&fake_bridge->lm_resources); + lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL); + if (lm == NULL) { + pr_err("Failed to allocate memory for location monitor resource structure\n"); + retval = -ENOMEM; + goto err_lm; + } + lm->parent = fake_bridge; + mutex_init(&lm->mtx); + lm->locked = 0; + lm->number = 1; + lm->monitors = 4; + list_add_tail(&lm->list, &fake_bridge->lm_resources); + + fake_bridge->slave_get = fake_slave_get; + fake_bridge->slave_set = fake_slave_set; + fake_bridge->master_get = fake_master_get; + fake_bridge->master_set = fake_master_set; + fake_bridge->master_read = fake_master_read; + fake_bridge->master_write = fake_master_write; + fake_bridge->master_rmw = fake_master_rmw; + fake_bridge->irq_set = fake_irq_set; + fake_bridge->irq_generate = fake_irq_generate; + fake_bridge->lm_set = fake_lm_set; + fake_bridge->lm_get = fake_lm_get; + fake_bridge->lm_attach = fake_lm_attach; + fake_bridge->lm_detach = fake_lm_detach; + fake_bridge->slot_get = fake_slot_get; + fake_bridge->alloc_consistent = fake_alloc_consistent; + fake_bridge->free_consistent = fake_free_consistent; + + pr_info("Board is%s the VME system controller\n", + (geoid == 1) ? "" : " not"); + + pr_info("VME geographical address is set to %d\n", geoid); + + retval = fake_crcsr_init(fake_bridge); + if (retval) { + pr_err("CR/CSR configuration failed.\n"); + goto err_crcsr; + } + + retval = vme_register_bridge(fake_bridge); + if (retval != 0) { + pr_err("Chip Registration failed.\n"); + goto err_reg; + } + + exit_pointer = fake_bridge; + + return 0; + +err_reg: + fake_crcsr_exit(fake_bridge); +err_crcsr: +err_lm: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } +err_slave: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &fake_bridge->slave_resources) { + slave_image = list_entry(pos, struct vme_slave_resource, list); + list_del(pos); + kfree(slave_image); + } +err_master: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &fake_bridge->master_resources) { + master_image = list_entry(pos, struct vme_master_resource, + list); + list_del(pos); + kfree(master_image); + } + + kfree(fake_device); +err_driver: + kfree(fake_bridge); +err_struct: + return retval; + +} + + +static void __exit fake_exit(void) +{ + struct list_head *pos = NULL; + struct list_head *tmplist; + struct vme_master_resource *master_image; + struct vme_slave_resource *slave_image; + int i; + struct vme_bridge *fake_bridge; + struct fake_driver *bridge; + + fake_bridge = exit_pointer; + + bridge = fake_bridge->driver_priv; + + pr_debug("Driver is being unloaded.\n"); + + /* + * Shutdown all inbound and outbound windows. + */ + for (i = 0; i < FAKE_MAX_MASTER; i++) + bridge->masters[i].enabled = 0; + + for (i = 0; i < FAKE_MAX_SLAVE; i++) + bridge->slaves[i].enabled = 0; + + /* + * Shutdown Location monitor. + */ + bridge->lm_enabled = 0; + + vme_unregister_bridge(fake_bridge); + + fake_crcsr_exit(fake_bridge); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { + slave_image = list_entry(pos, struct vme_slave_resource, list); + list_del(pos); + kfree(slave_image); + } + + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) { + master_image = list_entry(pos, struct vme_master_resource, + list); + list_del(pos); + kfree(master_image); + } + + kfree(fake_bridge->driver_priv); + + kfree(fake_bridge); + + root_device_unregister(vme_root); +} + + +MODULE_PARM_DESC(geoid, "Set geographical addressing"); +module_param(geoid, int, 0); + +MODULE_DESCRIPTION("Fake VME bridge driver"); +MODULE_LICENSE("GPL"); + +module_init(fake_init); +module_exit(fake_exit); -- cgit v1.2.3-58-ga151 From d4c3f97a062969ce52ecc5428e3dbbe93c157dd2 Mon Sep 17 00:00:00 2001 From: Ben Werbowyj Date: Fri, 22 Jul 2016 14:33:33 +1000 Subject: drivers: w1: style corrections for parenthesis and braces Inserted whitespace between command and open parenthesis at two locations. Removed new line between open brace and command/declaration at two locations. Signed-off-by: Ben Werbowyj Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_therm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 581a300fd6cd..efe9d2154a1a 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -81,7 +81,7 @@ static int w1_therm_add_slave(struct w1_slave *sl) static void w1_therm_remove_slave(struct w1_slave *sl) { int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data)); - while(refcnt) { + while (refcnt) { msleep(1000); refcnt = atomic_read(THERM_REFCNT(sl->family_data)); } @@ -151,8 +151,7 @@ static struct w1_family w1_therm_family_DS1825 = { .fops = &w1_therm_fops, }; -struct w1_therm_family_converter -{ +struct w1_therm_family_converter { u8 broken; u16 reserved; struct w1_family *f; @@ -293,7 +292,7 @@ static inline int w1_DS18B20_precision(struct device *device, int val) uint8_t precision_bits; uint8_t mask = 0x60; - if(val > 12 || val < 9) { + if (val > 12 || val < 9) { pr_warn("Unsupported precision\n"); return -1; } @@ -439,8 +438,7 @@ static ssize_t w1_slave_show(struct device *device, if (ret != 0) goto post_unlock; - if(!sl->family_data) - { + if (!sl->family_data) { ret = -ENODEV; goto pre_unlock; } -- cgit v1.2.3-58-ga151 From aaf16f7d98fedb24099361492821fcdc77036eab Mon Sep 17 00:00:00 2001 From: Ben Werbowyj Date: Fri, 22 Jul 2016 14:33:34 +1000 Subject: drivers: w1: removed assignment from within if statement Assignment of variable count removed from within an if statment. This was done at two locations in the file. Signed-off-by: Ben Werbowyj Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_therm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index efe9d2154a1a..83a5036b990b 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -335,7 +335,8 @@ static inline int w1_DS18B20_precision(struct device *device, int val) /* read values to only alter precision bits */ w1_write_8(dev, W1_READ_SCRATCHPAD); - if ((count = w1_read_block(dev, rom, 9)) != 9) + count = w1_read_block(dev, rom, 9); + if (count != 9) dev_warn(device, "w1_read_block() returned %u instead of 9.\n", count); crc = w1_calc_crc8(rom, 8); @@ -493,7 +494,8 @@ static ssize_t w1_slave_show(struct device *device, if (!w1_reset_select_slave(sl)) { w1_write_8(dev, W1_READ_SCRATCHPAD); - if ((count = w1_read_block(dev, rom, 9)) != 9) { + count = w1_read_block(dev, rom, 9); + if (count != 9) { dev_warn(device, "w1_read_block() " "returned %u instead of 9.\n", count); -- cgit v1.2.3-58-ga151 From 368451ea748ce44cd4b879808735976eb2fded77 Mon Sep 17 00:00:00 2001 From: Ben Werbowyj Date: Fri, 22 Jul 2016 14:33:35 +1000 Subject: drivers: w1: style corrections-pointers-blanklines-comparisons Correct pointer notations to include whitespace between variable type and "*" character. Inserted blank line after variable declatations at two locations. Rearranged comparison within an if statment to have the constant on the right-hand side. Signed-off-by: Ben Werbowyj Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_therm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 83a5036b990b..82611f197b0a 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -66,7 +66,7 @@ struct w1_therm_family_data { /* return the address of the refcnt in the family data */ #define THERM_REFCNT(family_data) \ - (&((struct w1_therm_family_data*)family_data)->refcnt) + (&((struct w1_therm_family_data *)family_data)->refcnt) static int w1_therm_add_slave(struct w1_slave *sl) { @@ -81,6 +81,7 @@ static int w1_therm_add_slave(struct w1_slave *sl) static void w1_therm_remove_slave(struct w1_slave *sl) { int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data)); + while (refcnt) { msleep(1000); refcnt = atomic_read(THERM_REFCNT(sl->family_data)); @@ -366,6 +367,7 @@ post_unlock: static inline int w1_DS18B20_convert_temp(u8 rom[9]) { s16 t = le16_to_cpup((__le16 *)rom); + return t*1000/16; } @@ -415,7 +417,7 @@ static ssize_t w1_slave_store(struct device *device, for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) { if (w1_therm_families[i].f->fid == sl->family->fid) { /* zero value indicates to write current configuration to eeprom */ - if (0 == val) + if (val == 0) ret = w1_therm_families[i].eeprom(device); else ret = w1_therm_families[i].precision(device, val); -- cgit v1.2.3-58-ga151 From a46b195ca0d88134a6f60dc28532205075c70cff Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 8 Aug 2016 13:52:41 +0000 Subject: w1: fix timeout_us parameter description Fix 'timeout_us' parameter description. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index bb34362e930a..e213c678bbfe 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -53,8 +53,8 @@ int w1_max_slave_ttl = 10; module_param_named(timeout, w1_timeout, int, 0); MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches"); module_param_named(timeout_us, w1_timeout_us, int, 0); -MODULE_PARM_DESC(timeout, "time in microseconds between automatic slave" - " searches"); +MODULE_PARM_DESC(timeout_us, + "time in microseconds between automatic slave searches"); /* A search stops when w1_max_slave_count devices have been found in that * search. The next search will start over and detect the same set of devices * on a static 1-wire bus. Memory is not allocated based on this number, just -- cgit v1.2.3-58-ga151 From 832c8232dd7be4977ae9fb9c7cbc4decce19a8fc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 24 Aug 2016 16:38:52 +0200 Subject: misc: retire the old BMP085 driver Patches merged to the IIO BMP085 driver makes it fully compliant with all features found in this old misc driver. Retire this old driver in favor of the new one in the proper subsystem. Cc: Jonathan Cameron Cc: Marek Belisko Acked-by: H. Nikolaus Schaller Signed-off-by: Linus Walleij Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-i2c-bmp085 | 31 -- drivers/iio/pressure/Kconfig | 2 - drivers/misc/Kconfig | 28 -- drivers/misc/Makefile | 3 - drivers/misc/bmp085-i2c.c | 83 ----- drivers/misc/bmp085-spi.c | 79 ----- drivers/misc/bmp085.c | 506 ----------------------------- drivers/misc/bmp085.h | 33 -- 8 files changed, 765 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-i2c-bmp085 delete mode 100644 drivers/misc/bmp085-i2c.c delete mode 100644 drivers/misc/bmp085-spi.c delete mode 100644 drivers/misc/bmp085.c delete mode 100644 drivers/misc/bmp085.h (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-i2c-bmp085 b/Documentation/ABI/testing/sysfs-i2c-bmp085 deleted file mode 100644 index 585962ad0465..000000000000 --- a/Documentation/ABI/testing/sysfs-i2c-bmp085 +++ /dev/null @@ -1,31 +0,0 @@ -What: /sys/bus/i2c/devices/-/pressure0_input -Date: June 2010 -Contact: Christoph Mair -Description: Start a pressure measurement and read the result. Values - represent the ambient air pressure in pascal (0.01 millibar). - - Reading: returns the current air pressure. - - -What: /sys/bus/i2c/devices/-/temp0_input -Date: June 2010 -Contact: Christoph Mair -Description: Measure the ambient temperature. The returned value represents - the ambient temperature in units of 0.1 degree celsius. - - Reading: returns the current temperature. - - -What: /sys/bus/i2c/devices/-/oversampling -Date: June 2010 -Contact: Christoph Mair -Description: Tell the bmp085 to use more samples to calculate a pressure - value. When writing to this file the chip will use 2^x samples - to calculate the next pressure value with x being the value - written. Using this feature will decrease RMS noise and - increase the measurement time. - - Reading: returns the current oversampling setting. - - Writing: sets a new oversampling setting. - Accepted values: 0..3. diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index d130cdc78f43..7fa65ab664fb 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -8,8 +8,6 @@ menu "Pressure sensors" config BMP280 tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" depends on (I2C || SPI_MASTER) - depends on !(BMP085_I2C=y || BMP085_I2C=m) - depends on !(BMP085_SPI=y || BMP085_SPI=m) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index a216b4667742..09f6f84a6a7f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -439,34 +439,6 @@ config ARM_CHARLCD line and the Linux version on the second line, but that's still useful. -config BMP085 - tristate - depends on SYSFS - -config BMP085_I2C - tristate "BMP085 digital pressure sensor on I2C" - select BMP085 - select REGMAP_I2C - depends on I2C && SYSFS - help - Say Y here if you want to support Bosch Sensortec's digital pressure - sensor hooked to an I2C bus. - - To compile this driver as a module, choose M here: the - module will be called bmp085-i2c. - -config BMP085_SPI - tristate "BMP085 digital pressure sensor on SPI" - select BMP085 - select REGMAP_SPI - depends on SPI_MASTER && SYSFS - help - Say Y here if you want to support Bosch Sensortec's digital pressure - sensor hooked to an SPI bus. - - To compile this driver as a module, choose M here: the - module will be called bmp085-spi. - config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" select GENERIC_NET_UTILS diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7410c6d9a34d..9d588215a06d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -9,9 +9,6 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o obj-$(CONFIG_INTEL_MID_PTI) += pti.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o -obj-$(CONFIG_BMP085) += bmp085.o -obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o -obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm.o diff --git a/drivers/misc/bmp085-i2c.c b/drivers/misc/bmp085-i2c.c deleted file mode 100644 index f35c218aaa1a..000000000000 --- a/drivers/misc/bmp085-i2c.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2012 Bosch Sensortec GmbH - * Copyright (c) 2012 Unixphere AB - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include "bmp085.h" - -#define BMP085_I2C_ADDRESS 0x77 - -static const unsigned short normal_i2c[] = { BMP085_I2C_ADDRESS, - I2C_CLIENT_END }; - -static int bmp085_i2c_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - if (client->addr != BMP085_I2C_ADDRESS) - return -ENODEV; - - return bmp085_detect(&client->dev); -} - -static int bmp085_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int err; - struct regmap *regmap = devm_regmap_init_i2c(client, - &bmp085_regmap_config); - - if (IS_ERR(regmap)) { - err = PTR_ERR(regmap); - dev_err(&client->dev, "Failed to init regmap: %d\n", err); - return err; - } - - return bmp085_probe(&client->dev, regmap, client->irq); -} - -static int bmp085_i2c_remove(struct i2c_client *client) -{ - return bmp085_remove(&client->dev); -} - -static const struct i2c_device_id bmp085_id[] = { - { BMP085_NAME, 0 }, - { "bmp180", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bmp085_id); - -static struct i2c_driver bmp085_i2c_driver = { - .driver = { - .name = BMP085_NAME, - }, - .id_table = bmp085_id, - .probe = bmp085_i2c_probe, - .remove = bmp085_i2c_remove, - - .detect = bmp085_i2c_detect, - .address_list = normal_i2c -}; - -module_i2c_driver(bmp085_i2c_driver); - -MODULE_AUTHOR("Eric Andersson "); -MODULE_DESCRIPTION("BMP085 I2C bus driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/bmp085-spi.c b/drivers/misc/bmp085-spi.c deleted file mode 100644 index 17ecbf95ff15..000000000000 --- a/drivers/misc/bmp085-spi.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2012 Bosch Sensortec GmbH - * Copyright (c) 2012 Unixphere AB - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include "bmp085.h" - -static int bmp085_spi_probe(struct spi_device *client) -{ - int err; - struct regmap *regmap; - - client->bits_per_word = 8; - err = spi_setup(client); - if (err < 0) { - dev_err(&client->dev, "spi_setup failed!\n"); - return err; - } - - regmap = devm_regmap_init_spi(client, &bmp085_regmap_config); - if (IS_ERR(regmap)) { - err = PTR_ERR(regmap); - dev_err(&client->dev, "Failed to init regmap: %d\n", err); - return err; - } - - return bmp085_probe(&client->dev, regmap, client->irq); -} - -static int bmp085_spi_remove(struct spi_device *client) -{ - return bmp085_remove(&client->dev); -} - -static const struct of_device_id bmp085_of_match[] = { - { .compatible = "bosch,bmp085", }, - { }, -}; -MODULE_DEVICE_TABLE(of, bmp085_of_match); - -static const struct spi_device_id bmp085_id[] = { - { "bmp180", 0 }, - { "bmp181", 0 }, - { } -}; -MODULE_DEVICE_TABLE(spi, bmp085_id); - -static struct spi_driver bmp085_spi_driver = { - .driver = { - .name = BMP085_NAME, - .of_match_table = bmp085_of_match - }, - .id_table = bmp085_id, - .probe = bmp085_spi_probe, - .remove = bmp085_spi_remove -}; - -module_spi_driver(bmp085_spi_driver); - -MODULE_AUTHOR("Eric Andersson "); -MODULE_DESCRIPTION("BMP085 SPI bus driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/bmp085.c b/drivers/misc/bmp085.c deleted file mode 100644 index 9b313f7810f5..000000000000 --- a/drivers/misc/bmp085.c +++ /dev/null @@ -1,506 +0,0 @@ -/* Copyright (c) 2010 Christoph Mair - * Copyright (c) 2012 Bosch Sensortec GmbH - * Copyright (c) 2012 Unixphere AB - * - * This driver supports the bmp085 and bmp18x digital barometric pressure - * and temperature sensors from Bosch Sensortec. The datasheets - * are available from their website: - * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf - * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP180-DS000-07.pdf - * - * A pressure measurement is issued by reading from pressure0_input. - * The return value ranges from 30000 to 110000 pascal with a resulution - * of 1 pascal (0.01 millibar) which enables measurements from 9000m above - * to 500m below sea level. - * - * The temperature can be read from temp0_input. Values range from - * -400 to 850 representing the ambient temperature in degree celsius - * multiplied by 10.The resolution is 0.1 celsius. - * - * Because ambient pressure is temperature dependent, a temperature - * measurement will be executed automatically even if the user is reading - * from pressure0_input. This happens if the last temperature measurement - * has been executed more then one second ago. - * - * To decrease RMS noise from pressure measurements, the bmp085 can - * autonomously calculate the average of up to eight samples. This is - * set up by writing to the oversampling sysfs file. Accepted values - * are 0, 1, 2 and 3. 2^x when x is the value written to this file - * specifies the number of samples used to calculate the ambient pressure. - * RMS noise is specified with six pascal (without averaging) and decreases - * down to 3 pascal when using an oversampling setting of 3. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include "bmp085.h" -#include -#include -#include - -#define BMP085_CHIP_ID 0x55 -#define BMP085_CALIBRATION_DATA_START 0xAA -#define BMP085_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */ -#define BMP085_CHIP_ID_REG 0xD0 -#define BMP085_CTRL_REG 0xF4 -#define BMP085_TEMP_MEASUREMENT 0x2E -#define BMP085_PRESSURE_MEASUREMENT 0x34 -#define BMP085_CONVERSION_REGISTER_MSB 0xF6 -#define BMP085_CONVERSION_REGISTER_LSB 0xF7 -#define BMP085_CONVERSION_REGISTER_XLSB 0xF8 -#define BMP085_TEMP_CONVERSION_TIME 5 - -struct bmp085_calibration_data { - s16 AC1, AC2, AC3; - u16 AC4, AC5, AC6; - s16 B1, B2; - s16 MB, MC, MD; -}; - -struct bmp085_data { - struct device *dev; - struct regmap *regmap; - struct mutex lock; - struct bmp085_calibration_data calibration; - u8 oversampling_setting; - u32 raw_temperature; - u32 raw_pressure; - u32 temp_measurement_period; - unsigned long last_temp_measurement; - u8 chip_id; - s32 b6; /* calculated temperature correction coefficient */ - int irq; - struct completion done; -}; - -static irqreturn_t bmp085_eoc_isr(int irq, void *devid) -{ - struct bmp085_data *data = devid; - - complete(&data->done); - - return IRQ_HANDLED; -} - -static s32 bmp085_read_calibration_data(struct bmp085_data *data) -{ - u16 tmp[BMP085_CALIBRATION_DATA_LENGTH]; - struct bmp085_calibration_data *cali = &(data->calibration); - s32 status = regmap_bulk_read(data->regmap, - BMP085_CALIBRATION_DATA_START, (u8 *)tmp, - (BMP085_CALIBRATION_DATA_LENGTH << 1)); - if (status < 0) - return status; - - cali->AC1 = be16_to_cpu(tmp[0]); - cali->AC2 = be16_to_cpu(tmp[1]); - cali->AC3 = be16_to_cpu(tmp[2]); - cali->AC4 = be16_to_cpu(tmp[3]); - cali->AC5 = be16_to_cpu(tmp[4]); - cali->AC6 = be16_to_cpu(tmp[5]); - cali->B1 = be16_to_cpu(tmp[6]); - cali->B2 = be16_to_cpu(tmp[7]); - cali->MB = be16_to_cpu(tmp[8]); - cali->MC = be16_to_cpu(tmp[9]); - cali->MD = be16_to_cpu(tmp[10]); - return 0; -} - -static s32 bmp085_update_raw_temperature(struct bmp085_data *data) -{ - u16 tmp; - s32 status; - - mutex_lock(&data->lock); - - init_completion(&data->done); - - status = regmap_write(data->regmap, BMP085_CTRL_REG, - BMP085_TEMP_MEASUREMENT); - if (status < 0) { - dev_err(data->dev, - "Error while requesting temperature measurement.\n"); - goto exit; - } - wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies( - BMP085_TEMP_CONVERSION_TIME)); - - status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB, - &tmp, sizeof(tmp)); - if (status < 0) { - dev_err(data->dev, - "Error while reading temperature measurement result\n"); - goto exit; - } - data->raw_temperature = be16_to_cpu(tmp); - data->last_temp_measurement = jiffies; - status = 0; /* everything ok, return 0 */ - -exit: - mutex_unlock(&data->lock); - return status; -} - -static s32 bmp085_update_raw_pressure(struct bmp085_data *data) -{ - u32 tmp = 0; - s32 status; - - mutex_lock(&data->lock); - - init_completion(&data->done); - - status = regmap_write(data->regmap, BMP085_CTRL_REG, - BMP085_PRESSURE_MEASUREMENT + - (data->oversampling_setting << 6)); - if (status < 0) { - dev_err(data->dev, - "Error while requesting pressure measurement.\n"); - goto exit; - } - - /* wait for the end of conversion */ - wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies( - 2+(3 << data->oversampling_setting))); - /* copy data into a u32 (4 bytes), but skip the first byte. */ - status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB, - ((u8 *)&tmp)+1, 3); - if (status < 0) { - dev_err(data->dev, - "Error while reading pressure measurement results\n"); - goto exit; - } - data->raw_pressure = be32_to_cpu((tmp)); - data->raw_pressure >>= (8-data->oversampling_setting); - status = 0; /* everything ok, return 0 */ - -exit: - mutex_unlock(&data->lock); - return status; -} - -/* - * This function starts the temperature measurement and returns the value - * in tenth of a degree celsius. - */ -static s32 bmp085_get_temperature(struct bmp085_data *data, int *temperature) -{ - struct bmp085_calibration_data *cali = &data->calibration; - long x1, x2; - int status; - - status = bmp085_update_raw_temperature(data); - if (status < 0) - goto exit; - - x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15; - x2 = (cali->MC << 11) / (x1 + cali->MD); - data->b6 = x1 + x2 - 4000; - /* if NULL just update b6. Used for pressure only measurements */ - if (temperature != NULL) - *temperature = (x1+x2+8) >> 4; - -exit: - return status; -} - -/* - * This function starts the pressure measurement and returns the value - * in millibar. Since the pressure depends on the ambient temperature, - * a temperature measurement is executed according to the given temperature - * measurement period (default is 1 sec boundary). This period could vary - * and needs to be adjusted according to the sensor environment, i.e. if big - * temperature variations then the temperature needs to be read out often. - */ -static s32 bmp085_get_pressure(struct bmp085_data *data, int *pressure) -{ - struct bmp085_calibration_data *cali = &data->calibration; - s32 x1, x2, x3, b3; - u32 b4, b7; - s32 p; - int status; - - /* alt least every second force an update of the ambient temperature */ - if ((data->last_temp_measurement == 0) || - time_is_before_jiffies(data->last_temp_measurement + 1*HZ)) { - status = bmp085_get_temperature(data, NULL); - if (status < 0) - return status; - } - - status = bmp085_update_raw_pressure(data); - if (status < 0) - return status; - - x1 = (data->b6 * data->b6) >> 12; - x1 *= cali->B2; - x1 >>= 11; - - x2 = cali->AC2 * data->b6; - x2 >>= 11; - - x3 = x1 + x2; - - b3 = (((((s32)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2); - b3 >>= 2; - - x1 = (cali->AC3 * data->b6) >> 13; - x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16; - x3 = (x1 + x2 + 2) >> 2; - b4 = (cali->AC4 * (u32)(x3 + 32768)) >> 15; - - b7 = ((u32)data->raw_pressure - b3) * - (50000 >> data->oversampling_setting); - p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2)); - - x1 = p >> 8; - x1 *= x1; - x1 = (x1 * 3038) >> 16; - x2 = (-7357 * p) >> 16; - p += (x1 + x2 + 3791) >> 4; - - *pressure = p; - - return 0; -} - -/* - * This function sets the chip-internal oversampling. Valid values are 0..3. - * The chip will use 2^oversampling samples for internal averaging. - * This influences the measurement time and the accuracy; larger values - * increase both. The datasheet gives an overview on how measurement time, - * accuracy and noise correlate. - */ -static void bmp085_set_oversampling(struct bmp085_data *data, - unsigned char oversampling) -{ - if (oversampling > 3) - oversampling = 3; - data->oversampling_setting = oversampling; -} - -/* - * Returns the currently selected oversampling. Range: 0..3 - */ -static unsigned char bmp085_get_oversampling(struct bmp085_data *data) -{ - return data->oversampling_setting; -} - -/* sysfs callbacks */ -static ssize_t set_oversampling(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct bmp085_data *data = dev_get_drvdata(dev); - unsigned long oversampling; - int err = kstrtoul(buf, 10, &oversampling); - - if (err == 0) { - mutex_lock(&data->lock); - bmp085_set_oversampling(data, oversampling); - mutex_unlock(&data->lock); - return count; - } - - return err; -} - -static ssize_t show_oversampling(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct bmp085_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", bmp085_get_oversampling(data)); -} -static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO, - show_oversampling, set_oversampling); - - -static ssize_t show_temperature(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int temperature; - int status; - struct bmp085_data *data = dev_get_drvdata(dev); - - status = bmp085_get_temperature(data, &temperature); - if (status < 0) - return status; - else - return sprintf(buf, "%d\n", temperature); -} -static DEVICE_ATTR(temp0_input, S_IRUGO, show_temperature, NULL); - - -static ssize_t show_pressure(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int pressure; - int status; - struct bmp085_data *data = dev_get_drvdata(dev); - - status = bmp085_get_pressure(data, &pressure); - if (status < 0) - return status; - else - return sprintf(buf, "%d\n", pressure); -} -static DEVICE_ATTR(pressure0_input, S_IRUGO, show_pressure, NULL); - - -static struct attribute *bmp085_attributes[] = { - &dev_attr_temp0_input.attr, - &dev_attr_pressure0_input.attr, - &dev_attr_oversampling.attr, - NULL -}; - -static const struct attribute_group bmp085_attr_group = { - .attrs = bmp085_attributes, -}; - -int bmp085_detect(struct device *dev) -{ - struct bmp085_data *data = dev_get_drvdata(dev); - unsigned int id; - int ret; - - ret = regmap_read(data->regmap, BMP085_CHIP_ID_REG, &id); - if (ret < 0) - return ret; - - if (id != data->chip_id) - return -ENODEV; - - return 0; -} -EXPORT_SYMBOL_GPL(bmp085_detect); - -static void bmp085_get_of_properties(struct bmp085_data *data) -{ -#ifdef CONFIG_OF - struct device_node *np = data->dev->of_node; - u32 prop; - - if (!np) - return; - - if (!of_property_read_u32(np, "chip-id", &prop)) - data->chip_id = prop & 0xff; - - if (!of_property_read_u32(np, "temp-measurement-period", &prop)) - data->temp_measurement_period = (prop/100)*HZ; - - if (!of_property_read_u32(np, "default-oversampling", &prop)) - data->oversampling_setting = prop & 0xff; -#endif -} - -static int bmp085_init_client(struct bmp085_data *data) -{ - int status = bmp085_read_calibration_data(data); - - if (status < 0) - return status; - - /* default settings */ - data->chip_id = BMP085_CHIP_ID; - data->last_temp_measurement = 0; - data->temp_measurement_period = 1*HZ; - data->oversampling_setting = 3; - - bmp085_get_of_properties(data); - - mutex_init(&data->lock); - - return 0; -} - -struct regmap_config bmp085_regmap_config = { - .reg_bits = 8, - .val_bits = 8 -}; -EXPORT_SYMBOL_GPL(bmp085_regmap_config); - -int bmp085_probe(struct device *dev, struct regmap *regmap, int irq) -{ - struct bmp085_data *data; - int err = 0; - - data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - dev_set_drvdata(dev, data); - data->dev = dev; - data->regmap = regmap; - data->irq = irq; - - if (data->irq > 0) { - err = devm_request_irq(dev, data->irq, bmp085_eoc_isr, - IRQF_TRIGGER_RISING, "bmp085", - data); - if (err < 0) - goto exit_free; - } - - /* Initialize the BMP085 chip */ - err = bmp085_init_client(data); - if (err < 0) - goto exit_free; - - err = bmp085_detect(dev); - if (err < 0) { - dev_err(dev, "%s: chip_id failed!\n", BMP085_NAME); - goto exit_free; - } - - /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &bmp085_attr_group); - if (err) - goto exit_free; - - dev_info(dev, "Successfully initialized %s!\n", BMP085_NAME); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} -EXPORT_SYMBOL_GPL(bmp085_probe); - -int bmp085_remove(struct device *dev) -{ - struct bmp085_data *data = dev_get_drvdata(dev); - - sysfs_remove_group(&data->dev->kobj, &bmp085_attr_group); - kfree(data); - - return 0; -} -EXPORT_SYMBOL_GPL(bmp085_remove); - -MODULE_AUTHOR("Christoph Mair "); -MODULE_DESCRIPTION("BMP085 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/bmp085.h b/drivers/misc/bmp085.h deleted file mode 100644 index 8b8e3b1f5ca5..000000000000 --- a/drivers/misc/bmp085.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2012 Bosch Sensortec GmbH - * Copyright (c) 2012 Unixphere AB - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _BMP085_H -#define _BMP085_H - -#include - -#define BMP085_NAME "bmp085" - -extern struct regmap_config bmp085_regmap_config; - -int bmp085_probe(struct device *dev, struct regmap *regmap, int irq); -int bmp085_remove(struct device *dev); -int bmp085_detect(struct device *dev); - -#endif -- cgit v1.2.3-58-ga151 From ca75d601b5942e8cf5eed68f308be0526d0dac5d Mon Sep 17 00:00:00 2001 From: PrasannaKumar Muralidharan Date: Thu, 25 Aug 2016 22:30:49 +0530 Subject: miscdevice: Add helper macro for misc device boilerplate Many modules call misc_register and misc_deregister in its module init and exit methods without any additional code. This ends up being boilerplate. This patch adds helper macro module_misc_device(), that replaces module_init()/ module_exit() with template functions. This patch also converts drivers to use new macro. Change since v1: Add device.h include in miscdevice.h as module_driver macro was not available from other include files in some architectures. Signed-off-by: PrasannaKumar Muralidharan Signed-off-by: Greg Kroah-Hartman --- arch/arm/common/bL_switcher_dummy_if.c | 14 +------------- arch/blackfin/mach-bf561/coreb.c | 13 +------------ drivers/hid/uhid.c | 13 +------------ drivers/input/misc/uinput.c | 15 ++------------- drivers/s390/char/sclp_ctl.c | 19 +------------------ drivers/vhost/test.c | 13 +------------ include/linux/miscdevice.h | 8 ++++++++ 7 files changed, 15 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/arch/arm/common/bL_switcher_dummy_if.c b/arch/arm/common/bL_switcher_dummy_if.c index 3f47f1203c6b..6053f64c3752 100644 --- a/arch/arm/common/bL_switcher_dummy_if.c +++ b/arch/arm/common/bL_switcher_dummy_if.c @@ -56,16 +56,4 @@ static struct miscdevice bL_switcher_device = { "b.L_switcher", &bL_switcher_fops }; - -static int __init bL_switcher_dummy_if_init(void) -{ - return misc_register(&bL_switcher_device); -} - -static void __exit bL_switcher_dummy_if_exit(void) -{ - misc_deregister(&bL_switcher_device); -} - -module_init(bL_switcher_dummy_if_init); -module_exit(bL_switcher_dummy_if_exit); +module_misc_device(bL_switcher_device); diff --git a/arch/blackfin/mach-bf561/coreb.c b/arch/blackfin/mach-bf561/coreb.c index 78ecb50bafc8..8a2543c654b3 100644 --- a/arch/blackfin/mach-bf561/coreb.c +++ b/arch/blackfin/mach-bf561/coreb.c @@ -59,18 +59,7 @@ static struct miscdevice coreb_dev = { .name = "coreb", .fops = &coreb_fops, }; - -static int __init bf561_coreb_init(void) -{ - return misc_register(&coreb_dev); -} -module_init(bf561_coreb_init); - -static void __exit bf561_coreb_exit(void) -{ - misc_deregister(&coreb_dev); -} -module_exit(bf561_coreb_exit); +module_misc_device(coreb_dev); MODULE_AUTHOR("Bas Vermeulen "); MODULE_DESCRIPTION("BF561 Core B Support"); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 99ec3ff7563b..7f8ff39ed44b 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -779,19 +779,8 @@ static struct miscdevice uhid_misc = { .minor = UHID_MINOR, .name = UHID_NAME, }; +module_misc_device(uhid_misc); -static int __init uhid_init(void) -{ - return misc_register(&uhid_misc); -} - -static void __exit uhid_exit(void) -{ - misc_deregister(&uhid_misc); -} - -module_init(uhid_init); -module_exit(uhid_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann "); MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 65ebbd111702..92595b98e7ed 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -1013,23 +1013,12 @@ static struct miscdevice uinput_misc = { .minor = UINPUT_MINOR, .name = UINPUT_NAME, }; +module_misc_device(uinput_misc); + MODULE_ALIAS_MISCDEV(UINPUT_MINOR); MODULE_ALIAS("devname:" UINPUT_NAME); -static int __init uinput_init(void) -{ - return misc_register(&uinput_misc); -} - -static void __exit uinput_exit(void) -{ - misc_deregister(&uinput_misc); -} - MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); MODULE_DESCRIPTION("User level driver support for input subsystem"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.3"); - -module_init(uinput_init); -module_exit(uinput_exit); diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c index ea607a4a1bdd..554eaa1e347d 100644 --- a/drivers/s390/char/sclp_ctl.c +++ b/drivers/s390/char/sclp_ctl.c @@ -126,21 +126,4 @@ static struct miscdevice sclp_ctl_device = { .name = "sclp", .fops = &sclp_ctl_fops, }; - -/* - * Register sclp_ctl misc device - */ -static int __init sclp_ctl_init(void) -{ - return misc_register(&sclp_ctl_device); -} -module_init(sclp_ctl_init); - -/* - * Deregister sclp_ctl misc device - */ -static void __exit sclp_ctl_exit(void) -{ - misc_deregister(&sclp_ctl_device); -} -module_exit(sclp_ctl_exit); +module_misc_device(sclp_ctl_device); diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 388eec4e1a90..70c342c987f7 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -322,18 +322,7 @@ static struct miscdevice vhost_test_misc = { "vhost-test", &vhost_test_fops, }; - -static int vhost_test_init(void) -{ - return misc_register(&vhost_test_misc); -} -module_init(vhost_test_init); - -static void vhost_test_exit(void) -{ - misc_deregister(&vhost_test_misc); -} -module_exit(vhost_test_exit); +module_misc_device(vhost_test_misc); MODULE_VERSION("0.0.1"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 543037465973..722698a43d79 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -3,6 +3,7 @@ #include #include #include +#include /* * These allocations are managed by device@lanana.org. If you use an @@ -70,6 +71,13 @@ struct miscdevice { extern int misc_register(struct miscdevice *misc); extern void misc_deregister(struct miscdevice *misc); +/* + * Helper macro for drivers that don't do anything special in module init / exit + * call. This helps in eleminating of boilerplate code. + */ +#define module_misc_device(__misc_device) \ + module_driver(__misc_device, misc_register, misc_deregister) + #define MODULE_ALIAS_MISCDEV(minor) \ MODULE_ALIAS("char-major-" __stringify(MISC_MAJOR) \ "-" __stringify(minor)) -- cgit v1.2.3-58-ga151 From d6a38c0ba778abd6683858bfd7c768f7edab2ebb Mon Sep 17 00:00:00 2001 From: PrasannaKumar Muralidharan Date: Thu, 25 Aug 2016 22:30:50 +0530 Subject: miscdevice: Use module_misc_device() macro This patch removes module_init()/module_exit() from driver code by using module_misc_device() macro. All modules in this patch has a print statement which is removed when module_misc_device() macro is used. If undesirable this patch can be dropped entirely, this is the only purpose of making this as a separate patch. Signed-off-by: PrasannaKumar Muralidharan Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/harddog_kern.c | 25 +------------------------ drivers/bluetooth/hci_vhci.c | 16 +--------------- drivers/char/bfin-otp.c | 40 +--------------------------------------- drivers/lightnvm/core.c | 19 +------------------ 4 files changed, 4 insertions(+), 96 deletions(-) (limited to 'drivers') diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c index 2d0266d0254d..3282787bbcfb 100644 --- a/arch/um/drivers/harddog_kern.c +++ b/arch/um/drivers/harddog_kern.c @@ -175,27 +175,4 @@ static struct miscdevice harddog_miscdev = { .name = "watchdog", .fops = &harddog_fops, }; - -static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n"; - -static int __init harddog_init(void) -{ - int ret; - - ret = misc_register(&harddog_miscdev); - - if (ret) - return ret; - - printk(banner); - - return 0; -} - -static void __exit harddog_exit(void) -{ - misc_deregister(&harddog_miscdev); -} - -module_init(harddog_init); -module_exit(harddog_exit); +module_misc_device(harddog_miscdev); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 3ff229b2e7f3..c4a75a18dcae 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -377,21 +377,7 @@ static struct miscdevice vhci_miscdev = { .fops = &vhci_fops, .minor = VHCI_MINOR, }; - -static int __init vhci_init(void) -{ - BT_INFO("Virtual HCI driver ver %s", VERSION); - - return misc_register(&vhci_miscdev); -} - -static void __exit vhci_exit(void) -{ - misc_deregister(&vhci_miscdev); -} - -module_init(vhci_init); -module_exit(vhci_exit); +module_misc_device(vhci_miscdev); module_param(amp, bool, 0644); MODULE_PARM_DESC(amp, "Create AMP controller device"); diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c index 44660f1c4849..35d46da754d7 100644 --- a/drivers/char/bfin-otp.c +++ b/drivers/char/bfin-otp.c @@ -230,45 +230,7 @@ static struct miscdevice bfin_otp_misc_device = { .name = DRIVER_NAME, .fops = &bfin_otp_fops, }; - -/** - * bfin_otp_init - Initialize module - * - * Registers the device and notifier handler. Actual device - * initialization is handled by bfin_otp_open(). - */ -static int __init bfin_otp_init(void) -{ - int ret; - - stampit(); - - ret = misc_register(&bfin_otp_misc_device); - if (ret) { - pr_init(KERN_ERR PFX "unable to register a misc device\n"); - return ret; - } - - pr_init(KERN_INFO PFX "initialized\n"); - - return 0; -} - -/** - * bfin_otp_exit - Deinitialize module - * - * Unregisters the device and notifier handler. Actual device - * deinitialization is handled by bfin_otp_close(). - */ -static void __exit bfin_otp_exit(void) -{ - stampit(); - - misc_deregister(&bfin_otp_misc_device); -} - -module_init(bfin_otp_init); -module_exit(bfin_otp_exit); +module_misc_device(bfin_otp_misc_device); MODULE_AUTHOR("Mike Frysinger "); MODULE_DESCRIPTION("Blackfin OTP Memory Interface"); diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index 9ebd2cfbd849..c784ddcd4405 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -1171,27 +1171,10 @@ static struct miscdevice _nvm_misc = { .nodename = "lightnvm/control", .fops = &_ctl_fops, }; +module_misc_device(_nvm_misc); MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); -static int __init nvm_mod_init(void) -{ - int ret; - - ret = misc_register(&_nvm_misc); - if (ret) - pr_err("nvm: misc_register failed for control device"); - - return ret; -} - -static void __exit nvm_mod_exit(void) -{ - misc_deregister(&_nvm_misc); -} - MODULE_AUTHOR("Matias Bjorling "); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); -module_init(nvm_mod_init); -module_exit(nvm_mod_exit); -- cgit v1.2.3-58-ga151 From 5ed22cebbacc3d434fe4df5e7178cb2b79042d41 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 29 Jun 2016 15:47:43 +0200 Subject: GenWQE: Change default access rights for device node Since it should always be ok for normal users to operate the accelerator, it makes sense to change it in our driver, rather than adding udev rules for all Linux distributions. Signed-off-by: Frank Haverkamp Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index a70b853fa2c9..6c1f49a85023 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1350,6 +1350,19 @@ static struct pci_driver genwqe_driver = { .err_handler = &genwqe_err_handler, }; +/** + * genwqe_devnode() - Set default access mode for genwqe devices. + * + * Default mode should be rw for everybody. Do not change default + * device name. + */ +static char *genwqe_devnode(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return NULL; +} + /** * genwqe_init_module() - Driver registration and initialization */ @@ -1363,6 +1376,8 @@ static int __init genwqe_init_module(void) return -ENOMEM; } + class_genwqe->devnode = genwqe_devnode; + debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL); if (!debugfs_genwqe) { rc = -ENOMEM; -- cgit v1.2.3-58-ga151 From 8b7d3a9d903ee1d1f5febc8e74223e4e72720e35 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 1 Jun 2016 17:50:08 +0530 Subject: ppdev: use new parport device model Modify ppdev driver to use the new parallel port device model. Initially submitted and committed as: e7223f186033 ("ppdev: use new parport device model") But due to some regression it was reverted by: 1701f680407c ("Revert "ppdev: use new parport device model"") Now that the original source of regression is fixed by: bbca503b2ee0 ("parport: use subsys_initcall") we can again modify ppdev to use device model. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index f8a483c67b07..d23368874710 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -286,7 +286,7 @@ static int register_device(int minor, struct pp_struct *pp) struct parport *port; struct pardevice *pdev = NULL; char *name; - int fl; + struct pardev_cb ppdev_cb; name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor); if (name == NULL) @@ -299,9 +299,11 @@ static int register_device(int minor, struct pp_struct *pp) return -ENXIO; } - fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; - pdev = parport_register_device(port, name, NULL, - NULL, pp_irq, fl, pp); + memset(&ppdev_cb, 0, sizeof(ppdev_cb)); + ppdev_cb.irq_func = pp_irq; + ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; + ppdev_cb.private = pp; + pdev = parport_register_dev_model(port, name, &ppdev_cb, minor); parport_put_port(port); if (!pdev) { @@ -799,10 +801,23 @@ static void pp_detach(struct parport *port) device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number)); } +static int pp_probe(struct pardevice *par_dev) +{ + struct device_driver *drv = par_dev->dev.driver; + int len = strlen(drv->name); + + if (strncmp(par_dev->name, drv->name, len)) + return -ENODEV; + + return 0; +} + static struct parport_driver pp_driver = { .name = CHRDEV, - .attach = pp_attach, + .probe = pp_probe, + .match_port = pp_attach, .detach = pp_detach, + .devmodel = true, }; static int __init ppdev_init(void) -- cgit v1.2.3-58-ga151 From 26ec99b105d5d1465fffed563cfe1a90a7b150fd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Jun 2016 12:04:18 +0200 Subject: char/mwave: remove custom BOOLEAN type The mwave driver has its own macros for the BOOLEAN type and the TRUE/FALSE values. This is redundant because the kernel already has bool/true/false, and it clashes with the ACPI headers that also define these types. The linux/acpi.h header is now included implicitly from mwave through the mc146818rtc.h header, as reported by Stephen Rothwell: In file included from drivers/char/mwave/smapi.c:51:0: drivers/char/mwave/smapi.h:52:0: warning: "TRUE" redefined #define TRUE 1 ^ In file included from include/acpi/acpi.h:58:0, from include/linux/acpi.h:33, from include/linux/mc146818rtc.h:21, from drivers/char/mwave/smapi.c:50: include/acpi/actypes.h:438:0: note: this is the location of the previous definition #define TRUE (1 == 1) ^ This removes the private types from mwave and uses the standard types instead. Signed-off-by: Arnd Bergmann Reviewed-by: Alexandre Belloni Fixes: fd09cc80165c ("rtc: cmos: move mc146818rtc code out of asm-generic/rtc.h") Signed-off-by: Greg Kroah-Hartman --- drivers/char/mwave/3780i.c | 64 ++++++++++++++++++++++---------------------- drivers/char/mwave/3780i.h | 8 +++--- drivers/char/mwave/mwavedd.c | 42 ++++++++++++++--------------- drivers/char/mwave/mwavedd.h | 14 +++++----- drivers/char/mwave/smapi.c | 4 +-- drivers/char/mwave/smapi.h | 6 +---- drivers/char/mwave/tp3780i.c | 52 +++++++++++++++++------------------ 7 files changed, 93 insertions(+), 97 deletions(-) (limited to 'drivers') diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c index 28740046bc83..972c40a19629 100644 --- a/drivers/char/mwave/3780i.c +++ b/drivers/char/mwave/3780i.c @@ -124,7 +124,7 @@ static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex, MKBYTE(rSlaveControl)); rSlaveControl_Save = rSlaveControl; - rSlaveControl.ConfigMode = TRUE; + rSlaveControl.ConfigMode = true; PRINTK_2(TRACE_3780I, "3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n", @@ -155,7 +155,7 @@ unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO, MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl); rSlaveControl_Save = rSlaveControl; - rSlaveControl.ConfigMode = TRUE; + rSlaveControl.ConfigMode = true; OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl)); OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex); ucValue = InByteDsp(DSP_ConfigData); @@ -230,7 +230,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, rUartCfg1.BaseIO = 3; break; } - rUartCfg2.Enable = TRUE; + rUartCfg2.Enable = true; } rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0; @@ -238,7 +238,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse; rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq]; rHBridgeCfg1.AccessMode = 1; - rHBridgeCfg2.Enable = TRUE; + rHBridgeCfg2.Enable = true; rBusmasterCfg2.Reserved = 0; @@ -278,8 +278,8 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, * soft-reset active for 10ms. */ rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = TRUE; - rSlaveControl.ConfigMode = FALSE; + rSlaveControl.SoftReset = true; + rSlaveControl.ConfigMode = false; rSlaveControl.Reserved = 0; PRINTK_4(TRACE_3780I, @@ -302,7 +302,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, for (i = 0; i < 11; i++) udelay(2000); - rSlaveControl.SoftReset = FALSE; + rSlaveControl.SoftReset = false; OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); @@ -326,10 +326,10 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, } - rHBridgeControl.EnableDspInt = FALSE; - rHBridgeControl.MemAutoInc = TRUE; - rHBridgeControl.IoAutoInc = FALSE; - rHBridgeControl.DiagnosticMode = FALSE; + rHBridgeControl.EnableDspInt = false; + rHBridgeControl.MemAutoInc = true; + rHBridgeControl.IoAutoInc = false; + rHBridgeControl.DiagnosticMode = false; PRINTK_3(TRACE_3780I, "3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n", @@ -345,7 +345,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, ChipID = ReadMsaCfg(DSP_ChipID); PRINTK_2(TRACE_3780I, - "3780i::dsp3780I_EnableDSP exiting bRC=TRUE, ChipID %x\n", + "3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n", ChipID); return 0; @@ -361,8 +361,8 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings) PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n"); rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = TRUE; - rSlaveControl.ConfigMode = FALSE; + rSlaveControl.SoftReset = true; + rSlaveControl.ConfigMode = false; rSlaveControl.Reserved = 0; spin_lock_irqsave(&dsp_lock, flags); OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); @@ -398,14 +398,14 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings) PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n", MKWORD(rHBridgeControl)); - rHBridgeControl.EnableDspInt = FALSE; + rHBridgeControl.EnableDspInt = false; OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); spin_unlock_irqrestore(&dsp_lock, flags); /* Reset the core via the boot domain register */ - rBootDomain.ResetCore = TRUE; - rBootDomain.Halt = TRUE; - rBootDomain.NMI = TRUE; + rBootDomain.ResetCore = true; + rBootDomain.Halt = true; + rBootDomain.NMI = true; rBootDomain.Reserved = 0; PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n", @@ -438,26 +438,26 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings) /* Transition the core to a running state */ - rBootDomain.ResetCore = TRUE; - rBootDomain.Halt = FALSE; - rBootDomain.NMI = TRUE; + rBootDomain.ResetCore = true; + rBootDomain.Halt = false; + rBootDomain.NMI = true; rBootDomain.Reserved = 0; WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); udelay(5); - rBootDomain.ResetCore = FALSE; + rBootDomain.ResetCore = false; WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); udelay(5); - rBootDomain.NMI = FALSE; + rBootDomain.NMI = false; WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); udelay(5); /* Enable DSP to PC interrupt */ spin_lock_irqsave(&dsp_lock, flags); MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = TRUE; + rHBridgeControl.EnableDspInt = true; PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n", MKWORD(rHBridgeControl)); @@ -466,7 +466,7 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings) spin_unlock_irqrestore(&dsp_lock, flags); - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=TRUE\n"); + PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=true\n"); return 0; } @@ -508,7 +508,7 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadDStore exit bRC=TRUE\n"); + "3780I::dsp3780I_ReadDStore exit bRC=true\n"); return 0; } @@ -550,7 +550,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadAndClearDStore exit bRC=TRUE\n"); + "3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n"); return 0; } @@ -592,7 +592,7 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, PRINTK_1(TRACE_3780I, - "3780I::dsp3780D_WriteDStore exit bRC=TRUE\n"); + "3780I::dsp3780D_WriteDStore exit bRC=true\n"); return 0; } @@ -640,7 +640,7 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, } PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadIStore exit bRC=TRUE\n"); + "3780I::dsp3780I_ReadIStore exit bRC=true\n"); return 0; } @@ -689,7 +689,7 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, } PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_WriteIStore exit bRC=TRUE\n"); + "3780I::dsp3780I_WriteIStore exit bRC=true\n"); return 0; } @@ -713,7 +713,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, */ spin_lock_irqsave(&dsp_lock, flags); MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = FALSE; + rHBridgeControl.EnableDspInt = false; OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); *pusIPCSource = InWordDsp(DSP_Interrupt); @@ -725,7 +725,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource)); - rHBridgeControl.EnableDspInt = TRUE; + rHBridgeControl.EnableDspInt = true; OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); spin_unlock_irqrestore(&dsp_lock, flags); diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h index fba6ab1160ce..9ccb6b270b07 100644 --- a/drivers/char/mwave/3780i.h +++ b/drivers/char/mwave/3780i.h @@ -101,7 +101,7 @@ typedef struct { } DSP_UART_CFG_1; typedef struct { - unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=FALSE, 1=TRUE */ + unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */ unsigned char Reserved:7; /* 0: Reserved */ } DSP_UART_CFG_2; @@ -114,7 +114,7 @@ typedef struct { } DSP_HBRIDGE_CFG_1; typedef struct { - unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=FALSE, 1=TRUE */ + unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */ unsigned char Reserved:7; /* 0: Reserved */ } DSP_HBRIDGE_CFG_2; @@ -133,12 +133,12 @@ typedef struct { typedef struct { - unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=FALSE, 1=TRUE */ + unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */ unsigned char Reserved:7; /* 0: Reserved */ } DSP_ISA_PROT_CFG; typedef struct { - unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=FALSE, 1=TRUE */ + unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */ unsigned char Reserved:7; /* 0: Reserved */ } DSP_POWER_MGMT_CFG; diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 164544afd680..3a3ff2eb6cba 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -296,8 +296,8 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, pDrvData->IPCs[ipcnum].usIntCount); mutex_lock(&mwave_mutex); - pDrvData->IPCs[ipcnum].bIsHere = FALSE; - pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; + pDrvData->IPCs[ipcnum].bIsHere = false; + pDrvData->IPCs[ipcnum].bIsEnabled = true; mutex_unlock(&mwave_mutex); PRINTK_2(TRACE_MWAVE, @@ -324,7 +324,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, pDrvData->IPCs[ipcnum].usIntCount); mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { + if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { DECLARE_WAITQUEUE(wait, current); PRINTK_2(TRACE_MWAVE, @@ -332,7 +332,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, " ipc %x going to sleep\n", ipcnum); add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); - pDrvData->IPCs[ipcnum].bIsHere = TRUE; + pDrvData->IPCs[ipcnum].bIsHere = true; set_current_state(TASK_INTERRUPTIBLE); /* check whether an event was signalled by */ /* the interrupt handler while we were gone */ @@ -355,7 +355,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, " application\n", ipcnum); } - pDrvData->IPCs[ipcnum].bIsHere = FALSE; + pDrvData->IPCs[ipcnum].bIsHere = false; remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); set_current_state(TASK_RUNNING); PRINTK_2(TRACE_MWAVE, @@ -384,9 +384,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, return -EINVAL; } mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { - pDrvData->IPCs[ipcnum].bIsEnabled = FALSE; - if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) { + if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { + pDrvData->IPCs[ipcnum].bIsEnabled = false; + if (pDrvData->IPCs[ipcnum].bIsHere == true) { wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue); } } @@ -541,7 +541,7 @@ static void mwave_exit(void) if (pDrvData->device_registered) { device_unregister(&mwave_device); - pDrvData->device_registered = FALSE; + pDrvData->device_registered = false; } #endif @@ -576,16 +576,16 @@ static int __init mwave_init(void) memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA)); - pDrvData->bBDInitialized = FALSE; - pDrvData->bResourcesClaimed = FALSE; - pDrvData->bDSPEnabled = FALSE; - pDrvData->bDSPReset = FALSE; - pDrvData->bMwaveDevRegistered = FALSE; + pDrvData->bBDInitialized = false; + pDrvData->bResourcesClaimed = false; + pDrvData->bDSPEnabled = false; + pDrvData->bDSPReset = false; + pDrvData->bMwaveDevRegistered = false; pDrvData->sLine = -1; for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) { - pDrvData->IPCs[i].bIsEnabled = FALSE; - pDrvData->IPCs[i].bIsHere = FALSE; + pDrvData->IPCs[i].bIsEnabled = false; + pDrvData->IPCs[i].bIsHere = false; pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */ init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue); } @@ -601,7 +601,7 @@ static int __init mwave_init(void) " Failed to initialize board data\n"); goto cleanup_error; } - pDrvData->bBDInitialized = TRUE; + pDrvData->bBDInitialized = true; retval = tp3780I_CalcResources(&pDrvData->rBDData); PRINTK_2(TRACE_MWAVE, @@ -626,7 +626,7 @@ static int __init mwave_init(void) " Failed to claim resources\n"); goto cleanup_error; } - pDrvData->bResourcesClaimed = TRUE; + pDrvData->bResourcesClaimed = true; retval = tp3780I_EnableDSP(&pDrvData->rBDData); PRINTK_2(TRACE_MWAVE, @@ -639,7 +639,7 @@ static int __init mwave_init(void) " Failed to enable DSP\n"); goto cleanup_error; } - pDrvData->bDSPEnabled = TRUE; + pDrvData->bDSPEnabled = true; if (misc_register(&mwave_misc_dev) < 0) { PRINTK_ERROR(KERN_ERR_MWAVE @@ -647,7 +647,7 @@ static int __init mwave_init(void) " Failed to register misc device\n"); goto cleanup_error; } - pDrvData->bMwaveDevRegistered = TRUE; + pDrvData->bMwaveDevRegistered = true; pDrvData->sLine = register_serial_portandirq( pDrvData->rBDData.rDspSettings.usUartBaseIO, @@ -668,7 +668,7 @@ static int __init mwave_init(void) if (device_register(&mwave_device)) goto cleanup_error; - pDrvData->device_registered = TRUE; + pDrvData->device_registered = true; for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) { if(device_create_file(&mwave_device, mwave_dev_attrs[i])) { PRINTK_ERROR(KERN_ERR_MWAVE diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h index 7e0d530e2e07..37e0a4926080 100644 --- a/drivers/char/mwave/mwavedd.h +++ b/drivers/char/mwave/mwavedd.h @@ -125,8 +125,8 @@ extern int mwave_uart_io; typedef struct _MWAVE_IPC { unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */ - BOOLEAN bIsEnabled; - BOOLEAN bIsHere; + bool bIsEnabled; + bool bIsHere; /* entry spin lock */ wait_queue_head_t ipc_wait_queue; } MWAVE_IPC; @@ -135,12 +135,12 @@ typedef struct _MWAVE_DEVICE_DATA { THINKPAD_BD_DATA rBDData; /* board driver's data area */ unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */ unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */ - BOOLEAN bBDInitialized; - BOOLEAN bResourcesClaimed; - BOOLEAN bDSPEnabled; - BOOLEAN bDSPReset; + bool bBDInitialized; + bool bResourcesClaimed; + bool bDSPEnabled; + bool bDSPReset; MWAVE_IPC IPCs[16]; - BOOLEAN bMwaveDevRegistered; + bool bMwaveDevRegistered; short sLine; int nr_registered_attrs; int device_registered; diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c index 6187fd14b3fe..8c5411a8f33f 100644 --- a/drivers/char/mwave/smapi.c +++ b/drivers/char/mwave/smapi.c @@ -493,7 +493,7 @@ exit_smapi_request_error: } -int smapi_set_DSP_power_state(BOOLEAN bOn) +int smapi_set_DSP_power_state(bool bOn) { int bRC = -EIO; unsigned short usAX, usBX, usCX, usDX, usDI, usSI; @@ -556,7 +556,7 @@ int smapi_init(void) PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n"); } else { PRINTK_2(TRACE_SMAPI, - "smapi::smapi_init, exit TRUE g_usSmapiPort %x\n", + "smapi::smapi_init, exit true g_usSmapiPort %x\n", g_usSmapiPort); retval = 0; //SmapiQuerySystemID(); diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h index 64b2ec1420e3..ebc206b000b9 100644 --- a/drivers/char/mwave/smapi.h +++ b/drivers/char/mwave/smapi.h @@ -49,10 +49,6 @@ #ifndef _LINUX_SMAPI_H #define _LINUX_SMAPI_H -#define TRUE 1 -#define FALSE 0 -#define BOOLEAN int - typedef struct { int bDSPPresent; int bDSPEnabled; @@ -74,7 +70,7 @@ typedef struct { int smapi_init(void); int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings); int smapi_set_DSP_cfg(void); -int smapi_set_DSP_power_state(BOOLEAN bOn); +int smapi_set_DSP_power_state(bool bOn); #endif diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c index 04e6d6a27994..5e1618a76b2a 100644 --- a/drivers/char/mwave/tp3780i.c +++ b/drivers/char/mwave/tp3780i.c @@ -80,13 +80,13 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData) WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode)); MKWORD(rGpioDriverEnable) = 0; - rGpioDriverEnable.Enable10 = TRUE; - rGpioDriverEnable.Mask10 = TRUE; + rGpioDriverEnable.Enable10 = true; + rGpioDriverEnable.Mask10 = true; WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable)); MKWORD(rGpioOutputData) = 0; rGpioOutputData.Latch10 = 0; - rGpioOutputData.Mask10 = TRUE; + rGpioOutputData.Mask10 = true; WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData)); PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n"); @@ -127,7 +127,7 @@ static irqreturn_t DspInterrupt(int irq, void *dev_id) PRINTK_2(TRACE_TP3780I, "tp3780i::DspInterrupt usIntCount %x\n", pDrvData->IPCs[usPCNum - 1].usIntCount); - if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == TRUE) { + if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) { PRINTK_2(TRACE_TP3780I, "tp3780i::DspInterrupt, waking up usPCNum %x\n", usPCNum - 1); @@ -160,8 +160,8 @@ int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData) PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData); - pBDData->bDSPEnabled = FALSE; - pSettings->bInterruptClaimed = FALSE; + pBDData->bDSPEnabled = false; + pSettings->bInterruptClaimed = false; retval = smapi_init(); if (retval) { @@ -269,7 +269,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData) if (pSettings->bInterruptClaimed) { free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = FALSE; + pSettings->bInterruptClaimed = false; } PRINTK_2(TRACE_TP3780I, @@ -283,7 +283,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData) int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) { DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - BOOLEAN bDSPPoweredUp = FALSE, bInterruptAllocated = FALSE; + bool bDSPPoweredUp = false, bInterruptAllocated = false; PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData); @@ -336,14 +336,14 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) } } - pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = TRUE; - pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = TRUE; + pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true; + pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true; if (pBDData->bShareDspIrq) { - pSettings->bDspIrqActiveLow = FALSE; + pSettings->bDspIrqActiveLow = false; } if (pBDData->bShareUartIrq) { - pSettings->bUartIrqActiveLow = FALSE; + pSettings->bUartIrqActiveLow = false; } pSettings->usNumTransfers = TP_CFG_NumTransfers; @@ -373,16 +373,16 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) PRINTK_3(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n", pSettings->usDspIrq, pBDData->bShareDspIrq); - bInterruptAllocated = TRUE; - pSettings->bInterruptClaimed = TRUE; + bInterruptAllocated = true; + pSettings->bInterruptClaimed = true; } - smapi_set_DSP_power_state(FALSE); - if (smapi_set_DSP_power_state(TRUE)) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(TRUE) failed\n"); + smapi_set_DSP_power_state(false); + if (smapi_set_DSP_power_state(true)) { + PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n"); goto exit_cleanup; } else { - bDSPPoweredUp = TRUE; + bDSPPoweredUp = true; } if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) { @@ -392,7 +392,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) EnableSRAM(pBDData); - pBDData->bDSPEnabled = TRUE; + pBDData->bDSPEnabled = true; PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n"); @@ -401,10 +401,10 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) exit_cleanup: PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n"); if (bDSPPoweredUp) - smapi_set_DSP_power_state(FALSE); + smapi_set_DSP_power_state(false); if (bInterruptAllocated) { free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = FALSE; + pSettings->bInterruptClaimed = false; } return -EIO; } @@ -421,10 +421,10 @@ int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData) dsp3780I_DisableDSP(&pBDData->rDspSettings); if (pSettings->bInterruptClaimed) { free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = FALSE; + pSettings->bInterruptClaimed = false; } - smapi_set_DSP_power_state(FALSE); - pBDData->bDSPEnabled = FALSE; + smapi_set_DSP_power_state(false); + pBDData->bDSPEnabled = false; } PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval); @@ -516,7 +516,7 @@ int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, int retval = 0; DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; unsigned short usDspBaseIO = pSettings->usDspBaseIO; - BOOLEAN bRC = 0; + bool bRC = 0; PRINTK_6(TRACE_TP3780I, "tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", @@ -552,7 +552,7 @@ int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, int retval = 0; DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; unsigned short usDspBaseIO = pSettings->usDspBaseIO; - BOOLEAN bRC = 0; + bool bRC = 0; PRINTK_6(TRACE_TP3780I, "tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", -- cgit v1.2.3-58-ga151 From c1e8ad231141be21471d2cc624773eb5250c07b3 Mon Sep 17 00:00:00 2001 From: Zhouyi Zhou Date: Wed, 10 Aug 2016 16:06:43 +0800 Subject: snsc: check return value of class_create return value of class_create should be considered in module init function. Signed-off-by: Zhouyi Zhou Signed-off-by: Greg Kroah-Hartman --- drivers/char/snsc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index 94006f9c2e43..10e56323f390 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -385,13 +385,18 @@ scdrv_init(void) event_nasid = ia64_sn_get_console_nasid(); + snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME); + if (IS_ERR(snsc_class)) { + printk("%s: failed to allocate class\n", __func__); + return PTR_ERR(snsc_class); + } + if (alloc_chrdev_region(&first_dev, 0, num_cnodes, SYSCTL_BASENAME) < 0) { printk("%s: failed to register SN system controller device\n", __func__); return -ENODEV; } - snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME); for (cnode = 0; cnode < num_cnodes; cnode++) { geoid = cnodeid_get_geoid(cnode); -- cgit v1.2.3-58-ga151 From d61f3088ea79c1f11fc8fe869d6f90a4cc97a452 Mon Sep 17 00:00:00 2001 From: Eli Billauer Date: Fri, 24 Jun 2016 10:48:30 +0300 Subject: char: xillybus: Fix spelling mistake and comment This patch fixes two minor issues: (1) An inaccurate comment (2) A spelling mistake in dev_err message ("upgarde" -> "upgrade") Reported-by: Joe Perches Reported-by: Colin Ian King Signed-off-by: Eli Billauer Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index dcd19f3f182e..b6c9cdead7f3 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -655,10 +655,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint) version = channel->wr_buffers[0]->addr; - /* Check version number. Accept anything below 0x82 for now. */ + /* Check version number. Reject anything above 0x82. */ if (*version > 0x82) { dev_err(endpoint->dev, - "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgarde. Aborting.\n", + "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgrade. Aborting.\n", *version); return -ENODEV; } -- cgit v1.2.3-58-ga151 From 148a1bc84398039e2b96ff78678c4d9a67f81452 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 1 Jun 2016 19:21:42 +0100 Subject: drivers: char: mem: Check {read,write}_kmem() addresses Arriving at read_kmem() with an offset representing a bogus kernel address (e.g. 0 from a simple "cat /dev/kmem") leads to copy_to_user faulting on the kernel-side read. x86_64 happens to get away with this since the optimised implementation uses "rep movs*", thus the user write (which is allowed to fault) and the kernel read are the same instruction, the kernel-side fault falls into the user-side fixup handler and the chain of events which transpires ends up returning an error as one might expect, even if it's an inappropriate -EFAULT. On other architectures, though, the read is not covered by the fixup entry for the write, and we get a big scary "Unable to hande kernel paging request..." dump. The more typical use-case of mmap_kmem() has always (within living memory at least) returned -EIO for addresses which don't satisfy pfn_valid(), so let's make that consistent across {read,write}_kem() too. Reported-by: Kefeng Wang Signed-off-by: Robin Murphy Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index a33163dbb913..5bb1985ec484 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -381,6 +381,9 @@ static ssize_t read_kmem(struct file *file, char __user *buf, char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ int err = 0; + if (!pfn_valid(PFN_DOWN(p))) + return -EIO; + read = 0; if (p < (unsigned long) high_memory) { low_count = count; @@ -509,6 +512,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ int err = 0; + if (!pfn_valid(PFN_DOWN(p))) + return -EIO; + if (p < (unsigned long) high_memory) { unsigned long to_write = min_t(unsigned long, count, (unsigned long)high_memory - p); -- cgit v1.2.3-58-ga151 From aeb83d7054de0180c4f4b8df7d5c75cc60277558 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 28 Aug 2016 01:31:28 +0800 Subject: fix:memory:of_memory:add missing header dependencies We get 2 warnings when biuld kernel with W=1: drivers/memory/of_memory.c:30:30: warning: no previous prototype for 'of_get_min_tck' [-Wmissing-prototypes] drivers/memory/of_memory.c:106:30: warning: no previous prototype for 'of_get_ddr_timings' [-Wmissing-prototypes] In fact, these functions are declared in drivers/memory/of_memory.h so this patch add missing header dependencies. Signed-off-by: Baoyou Xie Signed-off-by: Greg Kroah-Hartman --- drivers/memory/of_memory.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c index 9daf94bb8f27..568f05ed961a 100644 --- a/drivers/memory/of_memory.c +++ b/drivers/memory/of_memory.c @@ -16,6 +16,7 @@ #include #include #include +#include "of_memory.h" /** * of_get_min_tck() - extract min timing values for ddr -- cgit v1.2.3-58-ga151 From 549ce8f134bd95a1d4bf1a62215a62f1e9408a96 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Fri, 12 Aug 2016 12:48:52 +0100 Subject: misc: pch_phub: Read prefetch value from device tree if passed The default prefetch value for the eg20t device is hard coded to 0x000affaa. Add support for an alternative to be read from DT if available Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 4810e039bbec..1dd18f500730 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -28,6 +28,7 @@ #include #include #include +#include #define PHUB_STATUS 0x00 /* Status Register offset */ #define PHUB_CONTROL 0x04 /* Control Register offset */ @@ -711,6 +712,12 @@ static int pch_phub_probe(struct pci_dev *pdev, if (id->driver_data == 1) { /* EG20T PCH */ const char *board_name; + unsigned int prefetch = 0x000affaa; + + if (pdev->dev.of_node) + of_property_read_u32(pdev->dev.of_node, + "intel,eg20t-prefetch", + &prefetch); ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); @@ -736,7 +743,7 @@ static int pch_phub_probe(struct pci_dev *pdev, CLKCFG_UART_MASK); /* set the prefech value */ - iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14); + iowrite32(prefetch, chip->pch_phub_base_address + 0x14); /* set the interrupt delay value */ iowrite32(0x25, chip->pch_phub_base_address + 0x44); chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T; -- cgit v1.2.3-58-ga151 From bed3d7babafda16cf2e095366dec3f64143b370a Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Fri, 12 Aug 2016 12:48:53 +0100 Subject: misc: pch_phub: Add UART_CLK quirk for Boston platform The EG20T has 4 UART blocks. The clock source for the UART block is configured to receive a clock from an external pin by default. An internal 25MHz clock in the EG20T can also be used as a clock source for the clock. The MIPS based Boston platform ties the external clock pin down and relies on the internal clock source for the UART to function. Boston is based on device tree. Add a quirk to allow Boston to be detected via device tree and set the correct clock source for UART. Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 1dd18f500730..e42bdc90fa27 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -58,6 +58,7 @@ /* CM-iTC */ #define CLKCFG_UART_48MHZ (1 << 16) +#define CLKCFG_UART_25MHZ (2 << 16) #define CLKCFG_BAUDDIV (2 << 20) #define CLKCFG_PLL2VCO (8 << 9) #define CLKCFG_UARTCLKSEL (1 << 18) @@ -748,6 +749,16 @@ static int pch_phub_probe(struct pci_dev *pdev, iowrite32(0x25, chip->pch_phub_base_address + 0x44); chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_EG20T; + + /* quirk for MIPS Boston platform */ + if (pdev->dev.of_node) { + if (of_machine_is_compatible("img,boston")) { + pch_phub_read_modify_write_reg(chip, + (unsigned int)CLKCFG_REG_OFFSET, + CLKCFG_UART_25MHZ, + CLKCFG_UART_MASK); + } + } } else if (id->driver_data == 2) { /* ML7213 IOH */ ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); if (ret) -- cgit v1.2.3-58-ga151 From 7789e5a2accdbc5d506405c11ef4e5469440e96b Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Fri, 12 Aug 2016 12:48:54 +0100 Subject: serial: pch_uart: Add support for reading clock-frequency from DT The MIPS based Boston platform provides a 25MHz clock to the UART. Enable the driver for MIPS and add support in the driver to read the frequency from device tree. Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/pch_uart.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 518db24a5b36..c7831407a882 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1380,7 +1380,7 @@ config SERIAL_IFX6X60 config SERIAL_PCH_UART tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART" - depends on PCI && (X86_32 || COMPILE_TEST) + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) select SERIAL_CORE help This driver is for PCH(Platform controller Hub) UART of Intel EG20T diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index ea4ffc2ebb2f..23672f8a343e 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1826,6 +1827,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, priv->trigger_level = 1; priv->fcr = 0; + if (pdev->dev.of_node) + of_property_read_u32(pdev->dev.of_node, "clock-frequency" + , &user_uartclk); + #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE pch_uart_ports[board->line_no] = priv; #endif -- cgit v1.2.3-58-ga151 From 98f531b10d23e3c28e8d34c0e88822a81231b3c2 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 2 Sep 2016 05:58:19 -0700 Subject: Drivers: hv: cleanup vmbus_open() for wrap around mappings In preparation for doing wrap around mappings for ring buffers cleanup vmbus_open() function: - check that ring sizes are PAGE_SIZE aligned (they are for all in-kernel drivers now); - kfree(open_info) on error only after we kzalloc() it (not an issue as it is valid to call kfree(NULL); - rename poorly named labels; - use alloc_pages() instead of __get_free_pages() as we need struct page pointer for future. Signed-off-by: Vitaly Kuznetsov Tested-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 884cebf5f9b9..15e833004fc3 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -80,6 +80,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, int ret, err = 0; struct page *page; + if (send_ringbuffer_size % PAGE_SIZE || + recv_ringbuffer_size % PAGE_SIZE) + return -EINVAL; + spin_lock_irqsave(&newchannel->lock, flags); if (newchannel->state == CHANNEL_OPEN_STATE) { newchannel->state = CHANNEL_OPENING_STATE; @@ -99,17 +103,16 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, recv_ringbuffer_size)); if (!page) - out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, - get_order(send_ringbuffer_size + - recv_ringbuffer_size)); - else - out = (void *)page_address(page); + page = alloc_pages(GFP_KERNEL|__GFP_ZERO, + get_order(send_ringbuffer_size + + recv_ringbuffer_size)); - if (!out) { + if (!page) { err = -ENOMEM; - goto error0; + goto error_set_chnstate; } + out = page_address(page); in = (void *)((unsigned long)out + send_ringbuffer_size); newchannel->ringbuffer_pages = out; @@ -121,14 +124,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (ret != 0) { err = ret; - goto error0; + goto error_free_pages; } ret = hv_ringbuffer_init( &newchannel->inbound, in, recv_ringbuffer_size); if (ret != 0) { err = ret; - goto error0; + goto error_free_pages; } @@ -143,7 +146,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (ret != 0) { err = ret; - goto error0; + goto error_free_pages; } /* Create and init the channel open message */ @@ -152,7 +155,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, GFP_KERNEL); if (!open_info) { err = -ENOMEM; - goto error_gpadl; + goto error_free_gpadl; } init_completion(&open_info->waitevent); @@ -168,7 +171,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (userdatalen > MAX_USER_DEFINED_BYTES) { err = -EINVAL; - goto error_gpadl; + goto error_free_gpadl; } if (userdatalen) @@ -184,7 +187,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (ret != 0) { err = ret; - goto error1; + goto error_clean_msglist; } wait_for_completion(&open_info->waitevent); @@ -195,25 +198,25 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (open_info->response.open_result.status) { err = -EAGAIN; - goto error_gpadl; + goto error_free_gpadl; } newchannel->state = CHANNEL_OPENED_STATE; kfree(open_info); return 0; -error1: +error_clean_msglist: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -error_gpadl: +error_free_gpadl: vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); - -error0: + kfree(open_info); +error_free_pages: free_pages((unsigned long)out, get_order(send_ringbuffer_size + recv_ringbuffer_size)); - kfree(open_info); +error_set_chnstate: newchannel->state = CHANNEL_OPEN_STATE; return err; } -- cgit v1.2.3-58-ga151 From 9988ce685676cebe0b14dc128f00e1ae9cd1a4fa Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 2 Sep 2016 05:58:20 -0700 Subject: Drivers: hv: ring_buffer: wrap around mappings for ring buffers Make it possible to always use a single memcpy() or to provide a direct link to a packet on the ring buffer by creating virtual mapping for two copies of the ring buffer with vmap(). Utilize currently empty hv_ringbuffer_cleanup() to do the unmap. While on it, replace sizeof(struct hv_ring_buffer) check in hv_ringbuffer_init() with BUILD_BUG_ON() as it is a compile time check. Signed-off-by: Vitaly Kuznetsov Tested-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 29 ++++++++++++++--------------- drivers/hv/hyperv_vmbus.h | 4 ++-- drivers/hv/ring_buffer.c | 39 +++++++++++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 15e833004fc3..16f91c8490fe 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -75,7 +75,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, { struct vmbus_channel_open_channel *open_msg; struct vmbus_channel_msginfo *open_info = NULL; - void *in, *out; unsigned long flags; int ret, err = 0; struct page *page; @@ -112,23 +111,21 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto error_set_chnstate; } - out = page_address(page); - in = (void *)((unsigned long)out + send_ringbuffer_size); - - newchannel->ringbuffer_pages = out; + newchannel->ringbuffer_pages = page_address(page); newchannel->ringbuffer_pagecount = (send_ringbuffer_size + recv_ringbuffer_size) >> PAGE_SHIFT; - ret = hv_ringbuffer_init( - &newchannel->outbound, out, send_ringbuffer_size); + ret = hv_ringbuffer_init(&newchannel->outbound, page, + send_ringbuffer_size >> PAGE_SHIFT); if (ret != 0) { err = ret; goto error_free_pages; } - ret = hv_ringbuffer_init( - &newchannel->inbound, in, recv_ringbuffer_size); + ret = hv_ringbuffer_init(&newchannel->inbound, + &page[send_ringbuffer_size >> PAGE_SHIFT], + recv_ringbuffer_size >> PAGE_SHIFT); if (ret != 0) { err = ret; goto error_free_pages; @@ -139,10 +136,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, newchannel->ringbuffer_gpadlhandle = 0; ret = vmbus_establish_gpadl(newchannel, - newchannel->outbound.ring_buffer, - send_ringbuffer_size + - recv_ringbuffer_size, - &newchannel->ringbuffer_gpadlhandle); + page_address(page), + send_ringbuffer_size + + recv_ringbuffer_size, + &newchannel->ringbuffer_gpadlhandle); if (ret != 0) { err = ret; @@ -214,8 +211,10 @@ error_free_gpadl: vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); kfree(open_info); error_free_pages: - free_pages((unsigned long)out, - get_order(send_ringbuffer_size + recv_ringbuffer_size)); + hv_ringbuffer_cleanup(&newchannel->outbound); + hv_ringbuffer_cleanup(&newchannel->inbound); + __free_pages(page, + get_order(send_ringbuffer_size + recv_ringbuffer_size)); error_set_chnstate: newchannel->state = CHANNEL_OPEN_STATE; return err; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index ddcc3485520d..a5b4442433c8 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -522,8 +522,8 @@ extern unsigned int host_info_edx; /* Interface */ -int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, - u32 buflen); +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, + struct page *pages, u32 pagecnt); void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index e3edcaee7ab3..7e21c2c82ad1 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "hyperv_vmbus.h" @@ -243,22 +245,46 @@ void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, /* Initialize the ring buffer. */ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, - void *buffer, u32 buflen) + struct page *pages, u32 page_cnt) { - if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) - return -EINVAL; + int i; + struct page **pages_wraparound; + + BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE)); memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); - ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; + /* + * First page holds struct hv_ring_buffer, do wraparound mapping for + * the rest. + */ + pages_wraparound = kzalloc(sizeof(struct page *) * (page_cnt * 2 - 1), + GFP_KERNEL); + if (!pages_wraparound) + return -ENOMEM; + + pages_wraparound[0] = pages; + for (i = 0; i < 2 * (page_cnt - 1); i++) + pages_wraparound[i + 1] = &pages[i % (page_cnt - 1) + 1]; + + ring_info->ring_buffer = (struct hv_ring_buffer *) + vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP, PAGE_KERNEL); + + kfree(pages_wraparound); + + + if (!ring_info->ring_buffer) + return -ENOMEM; + ring_info->ring_buffer->read_index = ring_info->ring_buffer->write_index = 0; /* Set the feature bit for enabling flow control. */ ring_info->ring_buffer->feature_bits.value = 1; - ring_info->ring_size = buflen; - ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); + ring_info->ring_size = page_cnt << PAGE_SHIFT; + ring_info->ring_datasize = ring_info->ring_size - + sizeof(struct hv_ring_buffer); spin_lock_init(&ring_info->ring_lock); @@ -268,6 +294,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, /* Cleanup the ring buffer. */ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) { + vunmap(ring_info->ring_buffer); } /* Write to the ring buffer. */ -- cgit v1.2.3-58-ga151 From f24f0b495b17df33c03f3b758b1461385e9f0e50 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 2 Sep 2016 05:58:21 -0700 Subject: Drivers: hv: ring_buffer: use wrap around mappings in hv_copy{from, to}_ringbuffer() With wrap around mappings for ring buffers we can always use a single memcpy() to do the job. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Tested-by: Dexuan Cui Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 7e21c2c82ad1..08043da1a61c 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -172,18 +172,7 @@ static u32 hv_copyfrom_ringbuffer( void *ring_buffer = hv_get_ring_buffer(ring_info); u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); - u32 frag_len; - - /* wrap-around detected at the src */ - if (destlen > ring_buffer_size - start_read_offset) { - frag_len = ring_buffer_size - start_read_offset; - - memcpy(dest, ring_buffer + start_read_offset, frag_len); - memcpy(dest + frag_len, ring_buffer, destlen - frag_len); - } else - - memcpy(dest, ring_buffer + start_read_offset, destlen); - + memcpy(dest, ring_buffer + start_read_offset, destlen); start_read_offset += destlen; start_read_offset %= ring_buffer_size; @@ -204,15 +193,8 @@ static u32 hv_copyto_ringbuffer( { void *ring_buffer = hv_get_ring_buffer(ring_info); u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); - u32 frag_len; - - /* wrap-around detected! */ - if (srclen > ring_buffer_size - start_write_offset) { - frag_len = ring_buffer_size - start_write_offset; - memcpy(ring_buffer + start_write_offset, src, frag_len); - memcpy(ring_buffer, src + frag_len, srclen - frag_len); - } else - memcpy(ring_buffer + start_write_offset, src, srclen); + + memcpy(ring_buffer + start_write_offset, src, srclen); start_write_offset += srclen; start_write_offset %= ring_buffer_size; -- cgit v1.2.3-58-ga151 From 509879bdb30b8e12bd0b3cb0bc8429f01478df4b Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 2 Sep 2016 05:58:23 -0700 Subject: Drivers: hv: Introduce a policy for controlling channel affinity Introduce a mechanism to control how channels will be affinitized. We will support two policies: 1. HV_BALANCED: All performance critical channels will be dstributed evenly amongst all the available NUMA nodes. Once the Node is assigned, we will assign the CPU based on a simple round robin scheme. 2. HV_LOCALIZED: Only the primary channels are distributed across all NUMA nodes. Sub-channels will be in the same NUMA node as the primary channel. This is the current behaviour. The default policy will be the HV_BALANCED as it can minimize the remote memory access on NUMA machines with applications that span NUMA nodes. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 68 +++++++++++++++++++++++++++-------------------- include/linux/hyperv.h | 23 ++++++++++++++++ 2 files changed, 62 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index d8b64ba45b1d..bbd812e87511 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -356,8 +356,9 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) * We need to free the bit for init_vp_index() to work in the case * of sub-channel, when we reload drivers like hv_netvsc. */ - cpumask_clear_cpu(channel->target_cpu, - &primary_channel->alloced_cpus_in_node); + if (channel->affinity_policy == HV_LOCALIZED) + cpumask_clear_cpu(channel->target_cpu, + &primary_channel->alloced_cpus_in_node); vmbus_release_relid(relid); @@ -548,17 +549,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) } /* - * We distribute primary channels evenly across all the available - * NUMA nodes and within the assigned NUMA node we will assign the - * first available CPU to the primary channel. - * The sub-channels will be assigned to the CPUs available in the - * NUMA node evenly. + * Based on the channel affinity policy, we will assign the NUMA + * nodes. */ - if (!primary) { + + if ((channel->affinity_policy == HV_BALANCED) || (!primary)) { while (true) { next_node = next_numa_node_id++; - if (next_node == nr_node_ids) + if (next_node == nr_node_ids) { next_node = next_numa_node_id = 0; + continue; + } if (cpumask_empty(cpumask_of_node(next_node))) continue; break; @@ -582,15 +583,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) cur_cpu = -1; - /* - * Normally Hyper-V host doesn't create more subchannels than there - * are VCPUs on the node but it is possible when not all present VCPUs - * on the node are initialized by guest. Clear the alloced_cpus_in_node - * to start over. - */ - if (cpumask_equal(&primary->alloced_cpus_in_node, - cpumask_of_node(primary->numa_node))) - cpumask_clear(&primary->alloced_cpus_in_node); + if (primary->affinity_policy == HV_LOCALIZED) { + /* + * Normally Hyper-V host doesn't create more subchannels + * than there are VCPUs on the node but it is possible when not + * all present VCPUs on the node are initialized by guest. + * Clear the alloced_cpus_in_node to start over. + */ + if (cpumask_equal(&primary->alloced_cpus_in_node, + cpumask_of_node(primary->numa_node))) + cpumask_clear(&primary->alloced_cpus_in_node); + } while (true) { cur_cpu = cpumask_next(cur_cpu, &available_mask); @@ -601,17 +604,24 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) continue; } - /* - * NOTE: in the case of sub-channel, we clear the sub-channel - * related bit(s) in primary->alloced_cpus_in_node in - * hv_process_channel_removal(), so when we reload drivers - * like hv_netvsc in SMP guest, here we're able to re-allocate - * bit from primary->alloced_cpus_in_node. - */ - if (!cpumask_test_cpu(cur_cpu, - &primary->alloced_cpus_in_node)) { - cpumask_set_cpu(cur_cpu, - &primary->alloced_cpus_in_node); + if (primary->affinity_policy == HV_LOCALIZED) { + /* + * NOTE: in the case of sub-channel, we clear the + * sub-channel related bit(s) in + * primary->alloced_cpus_in_node in + * hv_process_channel_removal(), so when we + * reload drivers like hv_netvsc in SMP guest, here + * we're able to re-allocate + * bit from primary->alloced_cpus_in_node. + */ + if (!cpumask_test_cpu(cur_cpu, + &primary->alloced_cpus_in_node)) { + cpumask_set_cpu(cur_cpu, + &primary->alloced_cpus_in_node); + cpumask_set_cpu(cur_cpu, alloced_mask); + break; + } + } else { cpumask_set_cpu(cur_cpu, alloced_mask); break; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e6ef571e6100..c877e7980585 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -674,6 +674,11 @@ enum hv_signal_policy { HV_SIGNAL_POLICY_EXPLICIT, }; +enum hv_numa_policy { + HV_BALANCED = 0, + HV_LOCALIZED, +}; + enum vmbus_device_type { HV_IDE = 0, HV_SCSI, @@ -876,6 +881,18 @@ struct vmbus_channel { */ bool low_latency; + /* + * NUMA distribution policy: + * We support teo policies: + * 1) Balanced: Here all performance critical channels are + * distributed evenly amongst all the NUMA nodes. + * This policy will be the default policy. + * 2) Localized: All channels of a given instance of a + * performance critical service will be assigned CPUs + * within a selected NUMA node. + */ + enum hv_numa_policy affinity_policy; + }; static inline void set_channel_lock_state(struct vmbus_channel *c, bool state) @@ -895,6 +912,12 @@ static inline void set_channel_signal_state(struct vmbus_channel *c, c->signal_policy = policy; } +static inline void set_channel_affinity_state(struct vmbus_channel *c, + enum hv_numa_policy policy) +{ + c->affinity_policy = policy; +} + static inline void set_channel_read_state(struct vmbus_channel *c, bool state) { c->batched_reading = state; -- cgit v1.2.3-58-ga151 From 497af84b81b98b27e9ba7aebb8a373412e328497 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Fri, 2 Sep 2016 05:58:24 -0700 Subject: Drivers: hv: utils: Continue to poll VSS channel after handling requests. Multiple VSS_OP_HOT_BACKUP requests may arrive in quick succession, even though the host only signals once. The driver wass handling the first request while ignoring the others in the ring buffer. We should poll the VSS channel after handling a request to continue processing other requests. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_snapshot.c | 90 +++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index fde45865579a..c4013c859bcd 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -67,11 +67,11 @@ static const char vss_devname[] = "vmbus/hv_vss"; static __u8 *recv_buffer; static struct hvutil_transport *hvt; -static void vss_send_op(struct work_struct *dummy); static void vss_timeout_func(struct work_struct *dummy); +static void vss_handle_request(struct work_struct *dummy); static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func); -static DECLARE_WORK(vss_send_op_work, vss_send_op); +static DECLARE_WORK(vss_handle_request_work, vss_handle_request); static void vss_poll_wrapper(void *channel) { @@ -156,8 +156,7 @@ static int vss_on_msg(void *msg, int len) return 0; } - -static void vss_send_op(struct work_struct *dummy) +static void vss_send_op(void) { int op = vss_transaction.msg->vss_hdr.operation; int rc; @@ -174,6 +173,9 @@ static void vss_send_op(struct work_struct *dummy) vss_msg->vss_hdr.operation = op; vss_transaction.state = HVUTIL_USERSPACE_REQ; + + schedule_delayed_work(&vss_timeout_work, VSS_USERSPACE_TIMEOUT); + rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL); if (rc) { pr_warn("VSS: failed to communicate to the daemon: %d\n", rc); @@ -188,6 +190,40 @@ static void vss_send_op(struct work_struct *dummy) return; } +static void vss_handle_request(struct work_struct *dummy) +{ + switch (vss_transaction.msg->vss_hdr.operation) { + /* + * Initiate a "freeze/thaw" operation in the guest. + * We respond to the host once the operation is complete. + * + * We send the message to the user space daemon and the operation is + * performed in the daemon. + */ + case VSS_OP_THAW: + case VSS_OP_FREEZE: + if (vss_transaction.state < HVUTIL_READY) { + /* Userspace is not registered yet */ + vss_respond_to_host(HV_E_FAIL); + return; + } + vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED; + vss_send_op(); + return; + case VSS_OP_HOT_BACKUP: + vss_transaction.msg->vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY; + break; + case VSS_OP_GET_DM_INFO: + vss_transaction.msg->dm_info.flags = 0; + break; + default: + break; + } + + vss_respond_to_host(0); + hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); +} + /* * Send a response back to the host. */ @@ -272,48 +308,8 @@ void hv_vss_onchannelcallback(void *context) vss_transaction.recv_req_id = requestid; vss_transaction.msg = (struct hv_vss_msg *)vss_msg; - switch (vss_msg->vss_hdr.operation) { - /* - * Initiate a "freeze/thaw" - * operation in the guest. - * We respond to the host once - * the operation is complete. - * - * We send the message to the - * user space daemon and the - * operation is performed in - * the daemon. - */ - case VSS_OP_FREEZE: - case VSS_OP_THAW: - if (vss_transaction.state < HVUTIL_READY) { - /* Userspace is not registered yet */ - vss_respond_to_host(HV_E_FAIL); - return; - } - vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED; - schedule_work(&vss_send_op_work); - schedule_delayed_work(&vss_timeout_work, - VSS_USERSPACE_TIMEOUT); - return; - - case VSS_OP_HOT_BACKUP: - vss_msg->vss_cf.flags = - VSS_HBU_NO_AUTO_RECOVERY; - vss_respond_to_host(0); - return; - - case VSS_OP_GET_DM_INFO: - vss_msg->dm_info.flags = 0; - vss_respond_to_host(0); - return; - - default: - vss_respond_to_host(0); - return; - - } - + schedule_work(&vss_handle_request_work); + return; } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION @@ -364,6 +360,6 @@ void hv_vss_deinit(void) { vss_transaction.state = HVUTIL_DEVICE_DYING; cancel_delayed_work_sync(&vss_timeout_work); - cancel_work_sync(&vss_send_op_work); + cancel_work_sync(&vss_handle_request_work); hvutil_transport_destroy(hvt); } -- cgit v1.2.3-58-ga151 From db886e4d24c2b3d334be2cc1bd1bd05d547eb4c4 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Fri, 2 Sep 2016 05:58:25 -0700 Subject: Drivers: hv: utils: Check VSS daemon is listening before a hot backup Hyper-V host will send a VSS_OP_HOT_BACKUP request to check if guest is ready for a live backup/snapshot. The driver should respond to the check only if the daemon is running and listening to requests. This allows the host to fallback to standard snapshots in case the VSS daemon is not running. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_snapshot.c | 9 ++++++--- tools/hv/hv_vss_daemon.c | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index c4013c859bcd..a6707133c297 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -142,6 +142,11 @@ static int vss_on_msg(void *msg, int len) return vss_handle_handshake(vss_msg); } else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) { vss_transaction.state = HVUTIL_USERSPACE_RECV; + + if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP) + vss_transaction.msg->vss_cf.flags = + VSS_HBU_NO_AUTO_RECOVERY; + if (cancel_delayed_work_sync(&vss_timeout_work)) { vss_respond_to_host(vss_msg->error); /* Transaction is finished, reset the state. */ @@ -202,6 +207,7 @@ static void vss_handle_request(struct work_struct *dummy) */ case VSS_OP_THAW: case VSS_OP_FREEZE: + case VSS_OP_HOT_BACKUP: if (vss_transaction.state < HVUTIL_READY) { /* Userspace is not registered yet */ vss_respond_to_host(HV_E_FAIL); @@ -210,9 +216,6 @@ static void vss_handle_request(struct work_struct *dummy) vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED; vss_send_op(); return; - case VSS_OP_HOT_BACKUP: - vss_transaction.msg->vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY; - break; case VSS_OP_GET_DM_INFO: vss_transaction.msg->dm_info.flags = 0; break; diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 5d51d6ff08e6..e0829809c897 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -250,6 +250,9 @@ int main(int argc, char *argv[]) syslog(LOG_ERR, "/etc/fstab and /proc/mounts"); } break; + case VSS_OP_HOT_BACKUP: + syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n"); + break; default: syslog(LOG_ERR, "Illegal op:%d\n", op); } -- cgit v1.2.3-58-ga151 From 02baff325462cc7e81241b21959c5e62e7ca575e Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Fri, 2 Sep 2016 10:14:27 +0100 Subject: nvmem: rockchip-efuse: add rk3399-efuse support 1) the efuse timing of rk3399 is different from earlier SoCs. 2) rk3399-efuse is organized as 32bits by 32 one-time programmable electrical fuses, the efuse of earlier SoCs is organized as 32bits by 8 one-time programmable electrical fuses with random access interface. This patch adds a new read function for rk3399-efuse. Signed-off-by: Finley Xiao Reviewed-by: Heiko Stuebner Reviewed-by: Douglas Anderson Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-efuse.c | 133 +++++++++++++++++++++++++++++++++++------ 1 file changed, 114 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index 4d3f391f0a0b..423907bdd259 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -22,17 +22,29 @@ #include #include #include +#include #include -#define EFUSE_A_SHIFT 6 -#define EFUSE_A_MASK 0x3ff -#define EFUSE_PGENB BIT(3) -#define EFUSE_LOAD BIT(2) -#define EFUSE_STROBE BIT(1) -#define EFUSE_CSB BIT(0) - -#define REG_EFUSE_CTRL 0x0000 -#define REG_EFUSE_DOUT 0x0004 +#define RK3288_A_SHIFT 6 +#define RK3288_A_MASK 0x3ff +#define RK3288_PGENB BIT(3) +#define RK3288_LOAD BIT(2) +#define RK3288_STROBE BIT(1) +#define RK3288_CSB BIT(0) + +#define RK3399_A_SHIFT 16 +#define RK3399_A_MASK 0x3ff +#define RK3399_NBYTES 4 +#define RK3399_STROBSFTSEL BIT(9) +#define RK3399_RSB BIT(7) +#define RK3399_PD BIT(5) +#define RK3399_PGENB BIT(3) +#define RK3399_LOAD BIT(2) +#define RK3399_STROBE BIT(1) +#define RK3399_CSB BIT(0) + +#define REG_EFUSE_CTRL 0x0000 +#define REG_EFUSE_DOUT 0x0004 struct rockchip_efuse_chip { struct device *dev; @@ -40,8 +52,8 @@ struct rockchip_efuse_chip { struct clk *clk; }; -static int rockchip_efuse_read(void *context, unsigned int offset, - void *val, size_t bytes) +static int rockchip_rk3288_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) { struct rockchip_efuse_chip *efuse = context; u8 *buf = val; @@ -53,27 +65,82 @@ static int rockchip_efuse_read(void *context, unsigned int offset, return ret; } - writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL); + writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); udelay(1); while (bytes--) { writel(readl(efuse->base + REG_EFUSE_CTRL) & - (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), + (~(RK3288_A_MASK << RK3288_A_SHIFT)), efuse->base + REG_EFUSE_CTRL); writel(readl(efuse->base + REG_EFUSE_CTRL) | - ((offset++ & EFUSE_A_MASK) << EFUSE_A_SHIFT), + ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), efuse->base + REG_EFUSE_CTRL); udelay(1); writel(readl(efuse->base + REG_EFUSE_CTRL) | - EFUSE_STROBE, efuse->base + REG_EFUSE_CTRL); + RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); udelay(1); *buf++ = readb(efuse->base + REG_EFUSE_DOUT); writel(readl(efuse->base + REG_EFUSE_CTRL) & - (~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL); + (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); + udelay(1); + } + + /* Switch to standby mode */ + writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); + + clk_disable_unprepare(efuse->clk); + + return 0; +} + +static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct rockchip_efuse_chip *efuse = context; + unsigned int addr_start, addr_end, addr_offset, addr_len; + u32 out_value; + u8 *buf; + int ret, i = 0; + + ret = clk_prepare_enable(efuse->clk); + if (ret < 0) { + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); + return ret; + } + + addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; + addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; + addr_offset = offset % RK3399_NBYTES; + addr_len = addr_end - addr_start; + + buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); + if (!buf) { + clk_disable_unprepare(efuse->clk); + return -ENOMEM; + } + + writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, + efuse->base + REG_EFUSE_CTRL); + udelay(1); + while (addr_len--) { + writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | + ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), + efuse->base + REG_EFUSE_CTRL); udelay(1); + out_value = readl(efuse->base + REG_EFUSE_DOUT); + writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), + efuse->base + REG_EFUSE_CTRL); + udelay(1); + + memcpy(&buf[i], &out_value, RK3399_NBYTES); + i += RK3399_NBYTES; } /* Switch to standby mode */ - writel(EFUSE_PGENB | EFUSE_CSB, efuse->base + REG_EFUSE_CTRL); + writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); + + memcpy(val, buf + addr_offset, bytes); + + kfree(buf); clk_disable_unprepare(efuse->clk); @@ -89,7 +156,27 @@ static struct nvmem_config econfig = { }; static const struct of_device_id rockchip_efuse_match[] = { - { .compatible = "rockchip,rockchip-efuse", }, + /* deprecated but kept around for dts binding compatibility */ + { + .compatible = "rockchip,rockchip-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { + .compatible = "rockchip,rk3066a-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { + .compatible = "rockchip,rk3188-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { + .compatible = "rockchip,rk3288-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { + .compatible = "rockchip,rk3399-efuse", + .data = (void *)&rockchip_rk3399_efuse_read, + }, { /* sentinel */}, }; MODULE_DEVICE_TABLE(of, rockchip_efuse_match); @@ -99,6 +186,14 @@ static int rockchip_efuse_probe(struct platform_device *pdev) struct resource *res; struct nvmem_device *nvmem; struct rockchip_efuse_chip *efuse; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "failed to get match data\n"); + return -EINVAL; + } efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), GFP_KERNEL); @@ -116,7 +211,7 @@ static int rockchip_efuse_probe(struct platform_device *pdev) efuse->dev = &pdev->dev; econfig.size = resource_size(res); - econfig.reg_read = rockchip_efuse_read; + econfig.reg_read = match->data; econfig.priv = efuse; econfig.dev = efuse->dev; nvmem = nvmem_register(&econfig); -- cgit v1.2.3-58-ga151 From e2e808413425d82c8f162933a30304d10a5df231 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 7 Sep 2016 05:39:33 -0700 Subject: Driver: hv: vmbus: Make mmio resource local This fixes a sparse warning because hyperv_mmio resources are only used in this one file and should be static. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index fedf6298ec0b..6cbe07462785 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -105,8 +105,8 @@ static struct notifier_block hyperv_panic_block = { static const char *fb_mmio_name = "fb_range"; static struct resource *fb_mmio; -struct resource *hyperv_mmio; -DEFINE_SEMAPHORE(hyperv_mmio_lock); +static struct resource *hyperv_mmio; +static DEFINE_SEMAPHORE(hyperv_mmio_lock); static int vmbus_exists(void) { -- cgit v1.2.3-58-ga151 From 0f98829a99850836cf7c2cc9fbf1d7ce0f795780 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 7 Sep 2016 05:39:34 -0700 Subject: Drivers: hv: vmbus: suppress some "hv_vmbus: Unknown GUID" warnings Some VMBus devices are not needed by Linux guest[1][2], and, VMBus channels of Hyper-V Sockets don't really mean usual synthetic devices, so let's suppress the warnings for them. [1] https://support.microsoft.com/en-us/kb/2925727 [2] https://msdn.microsoft.com/en-us/library/jj980180(v=winembedded.81).aspx Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 26 ++++++++++++++++++++++++-- include/linux/hyperv.h | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index bbd812e87511..759ba4d62417 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -139,10 +139,32 @@ static const struct vmbus_device vmbus_devs[] = { }, }; -static u16 hv_get_dev_type(const uuid_le *guid) +static const struct { + uuid_le guid; +} vmbus_unsupported_devs[] = { + { HV_AVMA1_GUID }, + { HV_AVMA2_GUID }, + { HV_RDV_GUID }, +}; + +static bool is_unsupported_vmbus_devs(const uuid_le *guid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vmbus_unsupported_devs); i++) + if (!uuid_le_cmp(*guid, vmbus_unsupported_devs[i].guid)) + return true; + return false; +} + +static u16 hv_get_dev_type(const struct vmbus_channel *channel) { + const uuid_le *guid = &channel->offermsg.offer.if_type; u16 i; + if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid)) + return HV_UNKOWN; + for (i = HV_IDE; i < HV_UNKOWN; i++) { if (!uuid_le_cmp(*guid, vmbus_devs[i].guid)) return i; @@ -426,7 +448,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) goto err_free_chan; } - dev_type = hv_get_dev_type(&newchannel->offermsg.offer.if_type); + dev_type = hv_get_dev_type(newchannel); if (dev_type == HV_NIC) set_channel_signal_state(newchannel, HV_SIGNAL_POLICY_EXPLICIT); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 613074ef1ce9..430619a92d3b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1314,6 +1314,27 @@ u64 hv_do_hypercall(u64 control, void *input, void *output); .guid = UUID_LE(0x44c4f61d, 0x4444, 0x4400, 0x9d, 0x52, \ 0x80, 0x2e, 0x27, 0xed, 0xe1, 0x9f) +/* + * Linux doesn't support the 3 devices: the first two are for + * Automatic Virtual Machine Activation, and the third is for + * Remote Desktop Virtualization. + * {f8e65716-3cb3-4a06-9a60-1889c5cccab5} + * {3375baf4-9e15-4b30-b765-67acb10d607b} + * {276aacf4-ac15-426c-98dd-7521ad3f01fe} + */ + +#define HV_AVMA1_GUID \ + .guid = UUID_LE(0xf8e65716, 0x3cb3, 0x4a06, 0x9a, 0x60, \ + 0x18, 0x89, 0xc5, 0xcc, 0xca, 0xb5) + +#define HV_AVMA2_GUID \ + .guid = UUID_LE(0x3375baf4, 0x9e15, 0x4b30, 0xb7, 0x65, \ + 0x67, 0xac, 0xb1, 0x0d, 0x60, 0x7b) + +#define HV_RDV_GUID \ + .guid = UUID_LE(0x276aacf4, 0xac15, 0x426c, 0x98, 0xdd, \ + 0x75, 0x21, 0xad, 0x3f, 0x01, 0xfe) + /* * Common header for Hyper-V ICs */ -- cgit v1.2.3-58-ga151 From abeda47ebb20997aa3c5b4cac7db70f8bea62d7d Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Thu, 8 Sep 2016 05:24:12 -0700 Subject: Drivers: hv: utils: Rename version definitions to reflect protocol version. Different Windows host versions may reuse the same protocol version when negotiating the TimeSync, Shutdown, and Heartbeat protocols. We should only refer to the protocol version to avoid conflating the two concepts. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index d5acaa2d8e61..b27a8ee3de57 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -34,22 +34,22 @@ #define SD_MINOR 0 #define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) -#define SD_WS2008_MAJOR 1 -#define SD_WS2008_VERSION (SD_WS2008_MAJOR << 16 | SD_MINOR) +#define SD_MAJOR_1 1 +#define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR) #define TS_MAJOR 3 #define TS_MINOR 0 #define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) -#define TS_WS2008_MAJOR 1 -#define TS_WS2008_VERSION (TS_WS2008_MAJOR << 16 | TS_MINOR) +#define TS_MAJOR_1 1 +#define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR) #define HB_MAJOR 3 -#define HB_MINOR 0 +#define HB_MINOR 0 #define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) -#define HB_WS2008_MAJOR 1 -#define HB_WS2008_VERSION (HB_WS2008_MAJOR << 16 | HB_MINOR) +#define HB_MAJOR_1 1 +#define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR) static int sd_srv_version; static int ts_srv_version; @@ -350,9 +350,9 @@ static int util_probe(struct hv_device *dev, switch (vmbus_proto_version) { case (VERSION_WS2008): util_fw_version = UTIL_WS2K8_FW_VERSION; - sd_srv_version = SD_WS2008_VERSION; - ts_srv_version = TS_WS2008_VERSION; - hb_srv_version = HB_WS2008_VERSION; + sd_srv_version = SD_VERSION_1; + ts_srv_version = TS_VERSION_1; + hb_srv_version = HB_VERSION_1; break; default: -- cgit v1.2.3-58-ga151 From 2e338f7e034f12066c78999deff8894c337ae23b Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Thu, 8 Sep 2016 05:24:13 -0700 Subject: Drivers: hv: utils: Use TimeSync samples to adjust the clock after boot. Only the first 50 samples after boot were being used to discipline the clock. After the first 50 samples, any samples from the host were ignored and the guest clock would eventually drift from the host clock. This patch allows TimeSync-enabled guests to continuously synchronize the clock with the host clock, even after the first 50 samples. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index b27a8ee3de57..4002b717c777 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -198,29 +198,23 @@ static void hv_set_host_time(struct work_struct *work) * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time * message after the timesync channel is opened. Since the hv_utils module is - * loaded after hv_vmbus, the first message is usually missed. The other - * thing is, systime is automatically set to emulated hardware clock which may - * not be UTC time or in the same time zone. So, to override these effects, we - * use the first 50 time samples for initial system time setting. + * loaded after hv_vmbus, the first message is usually missed. This bit is + * considered a hard request to discipline the clock. + * + * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is + * typically used as a hint to the guest. The guest is under no obligation + * to discipline the clock. */ static inline void adj_guesttime(u64 hosttime, u8 flags) { struct adj_time_work *wrk; - static s32 scnt = 50; wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC); if (wrk == NULL) return; wrk->host_time = hosttime; - if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { - INIT_WORK(&wrk->work, hv_set_host_time); - schedule_work(&wrk->work); - return; - } - - if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { - scnt--; + if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) { INIT_WORK(&wrk->work, hv_set_host_time); schedule_work(&wrk->work); } else -- cgit v1.2.3-58-ga151 From 8e1d260738ca89bc7c87444f95f04a026d12b496 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Thu, 8 Sep 2016 05:24:14 -0700 Subject: Drivers: hv: utils: Support TimeSync version 4.0 protocol samples. This enables support for more accurate TimeSync v4 samples when hosted under Windows Server 2016 and newer hosts. The new time samples include a "vmreferencetime" field that represents the guest's TSC value when the host generated its time sample. This value lets the guest calculate the latency in receiving the time sample. The latency is added to the sample host time prior to updating the clock. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 82 ++++++++++++++++++++++++++++++++++++-------------- include/linux/hyperv.h | 9 ++++++ 2 files changed, 68 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 4002b717c777..6286bdc02818 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -37,13 +37,16 @@ #define SD_MAJOR_1 1 #define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR) -#define TS_MAJOR 3 +#define TS_MAJOR 4 #define TS_MINOR 0 #define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) #define TS_MAJOR_1 1 #define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR) +#define TS_MAJOR_3 3 +#define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR) + #define HB_MAJOR 3 #define HB_MINOR 0 #define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) @@ -160,20 +163,6 @@ static void shutdown_onchannelcallback(void *context) schedule_work(&shutdown_work); } -/* - * Set guest time to host UTC time. - */ -static inline void do_adj_guesttime(u64 hosttime) -{ - s64 host_tns; - struct timespec host_ts; - - host_tns = (hosttime - WLTIMEDELTA) * 100; - host_ts = ns_to_timespec(host_tns); - - do_settimeofday(&host_ts); -} - /* * Set the host time in a process context. */ @@ -181,14 +170,37 @@ static inline void do_adj_guesttime(u64 hosttime) struct adj_time_work { struct work_struct work; u64 host_time; + u64 ref_time; + u8 flags; }; static void hv_set_host_time(struct work_struct *work) { struct adj_time_work *wrk; + s64 host_tns; + u64 newtime; + struct timespec host_ts; wrk = container_of(work, struct adj_time_work, work); - do_adj_guesttime(wrk->host_time); + + newtime = wrk->host_time; + if (ts_srv_version > TS_VERSION_3) { + /* + * Some latency has been introduced since Hyper-V generated + * its time sample. Take that latency into account before + * using TSC reference time sample from Hyper-V. + * + * This sample is given by TimeSync v4 and above hosts. + */ + u64 current_tick; + + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + newtime += (current_tick - wrk->ref_time); + } + host_tns = (newtime - WLTIMEDELTA) * 100; + host_ts = ns_to_timespec(host_tns); + + do_settimeofday(&host_ts); kfree(wrk); } @@ -205,7 +217,7 @@ static void hv_set_host_time(struct work_struct *work) * typically used as a hint to the guest. The guest is under no obligation * to discipline the clock. */ -static inline void adj_guesttime(u64 hosttime, u8 flags) +static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags) { struct adj_time_work *wrk; @@ -214,6 +226,8 @@ static inline void adj_guesttime(u64 hosttime, u8 flags) return; wrk->host_time = hosttime; + wrk->ref_time = reftime; + wrk->flags = flags; if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) { INIT_WORK(&wrk->work, hv_set_host_time); schedule_work(&wrk->work); @@ -231,6 +245,7 @@ static void timesync_onchannelcallback(void *context) u64 requestid; struct icmsg_hdr *icmsghdrp; struct ictimesync_data *timedatap; + struct ictimesync_ref_data *refdata; u8 *time_txf_buf = util_timesynch.recv_buffer; struct icmsg_negotiate *negop = NULL; @@ -246,11 +261,27 @@ static void timesync_onchannelcallback(void *context) time_txf_buf, util_fw_version, ts_srv_version); + pr_info("Using TimeSync version %d.%d\n", + ts_srv_version >> 16, ts_srv_version & 0xFFFF); } else { - timedatap = (struct ictimesync_data *)&time_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - adj_guesttime(timedatap->parenttime, timedatap->flags); + if (ts_srv_version > TS_VERSION_3) { + refdata = (struct ictimesync_ref_data *) + &time_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + adj_guesttime(refdata->parenttime, + refdata->vmreferencetime, + refdata->flags); + } else { + timedatap = (struct ictimesync_data *) + &time_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + adj_guesttime(timedatap->parenttime, + 0, + timedatap->flags); + } } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION @@ -348,12 +379,17 @@ static int util_probe(struct hv_device *dev, ts_srv_version = TS_VERSION_1; hb_srv_version = HB_VERSION_1; break; - - default: + case(VERSION_WIN10): util_fw_version = UTIL_FW_VERSION; sd_srv_version = SD_VERSION; ts_srv_version = TS_VERSION; hb_srv_version = HB_VERSION; + break; + default: + util_fw_version = UTIL_FW_VERSION; + sd_srv_version = SD_VERSION; + ts_srv_version = TS_VERSION_3; + hb_srv_version = HB_VERSION; } ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 430619a92d3b..7d7cbff33bda 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1423,6 +1423,15 @@ struct ictimesync_data { u8 flags; } __packed; +struct ictimesync_ref_data { + u64 parenttime; + u64 vmreferencetime; + u8 flags; + char leapflags; + char stratum; + u8 reserved[3]; +} __packed; + struct hyperv_service_callback { u8 msg_type; char *log_msg; -- cgit v1.2.3-58-ga151 From 54e9b099724a135a3c3cba1d90c0f2b7dd037815 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 8 Sep 2016 09:38:05 -0500 Subject: fpga manager: Add hardware dependency to Zynq driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Zynq FPGA manager driver serves no purpose on other architectures so hide it unless build-testing. Signed-off-by: Jean Delvare Acked-by: Moritz Fischer Acked-by: Alan Tull Acked-by: Michal Simek Cc: "Sören Brinkmann" Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index d61410299ec0..cd84934774cc 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -21,6 +21,7 @@ config FPGA_MGR_SOCFPGA config FPGA_MGR_ZYNQ_FPGA tristate "Xilinx Zynq FPGA" + depends on ARCH_ZYNQ || COMPILE_TEST depends on HAS_DMA help FPGA manager driver support for Xilinx Zynq FPGAs. -- cgit v1.2.3-58-ga151 From 3ba1eb17b633b419737b65429fa7124ce87c5efc Mon Sep 17 00:00:00 2001 From: Vivek yadav Date: Fri, 9 Sep 2016 00:42:30 -0700 Subject: Drivers: hv: hv_util: Avoid dynamic allocation in time synch Under stress, we have seen allocation failure in time synch code. Avoid this dynamic allocation. Signed-off-by: Vivek Yadav Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 6286bdc02818..4aa3cb63fd41 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -64,9 +64,14 @@ static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, }; +static int hv_timesync_init(struct hv_util_service *srv); +static void hv_timesync_deinit(void); + static void timesync_onchannelcallback(void *context); static struct hv_util_service util_timesynch = { .util_cb = timesync_onchannelcallback, + .util_init = hv_timesync_init, + .util_deinit = hv_timesync_deinit, }; static void heartbeat_onchannelcallback(void *context); @@ -201,7 +206,6 @@ static void hv_set_host_time(struct work_struct *work) host_ts = ns_to_timespec(host_tns); do_settimeofday(&host_ts); - kfree(wrk); } /* @@ -217,22 +221,24 @@ static void hv_set_host_time(struct work_struct *work) * typically used as a hint to the guest. The guest is under no obligation * to discipline the clock. */ +static struct adj_time_work wrk; static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags) { - struct adj_time_work *wrk; - wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC); - if (wrk == NULL) + /* + * This check is safe since we are executing in the + * interrupt context and time synch messages arre always + * delivered on the same CPU. + */ + if (work_pending(&wrk.work)) return; - wrk->host_time = hosttime; - wrk->ref_time = reftime; - wrk->flags = flags; + wrk.host_time = hosttime; + wrk.ref_time = reftime; + wrk.flags = flags; if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) { - INIT_WORK(&wrk->work, hv_set_host_time); - schedule_work(&wrk->work); - } else - kfree(wrk); + schedule_work(&wrk.work); + } } /* @@ -457,6 +463,17 @@ static struct hv_driver util_drv = { .remove = util_remove, }; +static int hv_timesync_init(struct hv_util_service *srv) +{ + INIT_WORK(&wrk.work, hv_set_host_time); + return 0; +} + +static void hv_timesync_deinit(void) +{ + cancel_work_sync(&wrk.work); +} + static int __init init_hyperv_utils(void) { pr_info("Registering HyperV Utility Driver\n"); -- cgit v1.2.3-58-ga151 From 8e67cdbc2f7b57bc1448c505ce5c20e60238728b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 8 Sep 2016 16:50:38 -0600 Subject: coresight: perf: deal with error condition properly Function coresight_build_path() should return -ENOMEM when kzalloc fails to allocated the requested memory. That way callers can deal with the error condition in a similar way. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 ++-- drivers/hwtracing/coresight/coresight.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 98eb207684fa..2cd7c718198a 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -135,7 +135,7 @@ static void free_event_data(struct work_struct *work) } for_each_cpu(cpu, mask) { - if (event_data->path[cpu]) + if (!(IS_ERR_OR_NULL(event_data->path[cpu]))) coresight_release_path(event_data->path[cpu]); } @@ -220,7 +220,7 @@ static void *etm_setup_aux(int event_cpu, void **pages, * referenced later when the path is actually needed. */ event_data->path[cpu] = coresight_build_path(csdev); - if (!event_data->path[cpu]) + if (IS_ERR(event_data->path[cpu])) goto err; } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index fcbedd3905e3..7bf00a0beb6f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -429,7 +429,7 @@ struct list_head *coresight_build_path(struct coresight_device *csdev) path = kzalloc(sizeof(struct list_head), GFP_KERNEL); if (!path) - return NULL; + return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(path); -- cgit v1.2.3-58-ga151 From 0ef7528d15c2981d315c5dd45917559e47d5149d Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Thu, 8 Sep 2016 16:50:39 -0600 Subject: coresight: tmc: mark symbols static where possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We get a few warnings when building kernel with W=1: drivers/hwtracing/coresight/coresight-tmc-etr.c:23:6: warning: no previous prototype for 'tmc_etr_enable_hw' [-Wmissing-prototypes] drivers/hwtracing/coresight/coresight-tmc-etf.c:25:6: warning: no previous prototype for 'tmc_etb_enable_hw' [-Wmissing-prototypes] drivers/hwtracing/coresight/coresight-tmc.c:250:9: warning: no previous prototype for ‘trigger_cntr_show’ [-Wmissing-prototypes] ... In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. so this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 2 +- drivers/hwtracing/coresight/coresight-tmc-etr.c | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 5fa49c4252f6..d6941ea24d8d 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -22,7 +22,7 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 6d7de0309e94..886ea83c68e0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -20,7 +20,7 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) +static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { u32 axictl; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index a4748630f5d6..d8517d2a968c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -247,8 +247,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { NULL, }; -ssize_t trigger_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t trigger_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val = drvdata->trigger_cntr; -- cgit v1.2.3-58-ga151 From ac182e8abc6f93c1c4cc12f042af64c9d7be0d1e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 12 Sep 2016 16:21:43 +0300 Subject: mei: me: add kaby point device ids Add device ids for Intel Kabypoint PCH (Kabylake) Cc: #4.6+ Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 3 +++ drivers/misc/mei/pci-me.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 0dcb854b4bfc..7ad15d678878 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -125,6 +125,9 @@ #define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ #define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ +#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */ +#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */ + /* * MEI HW Section */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 031e7c92fcc7..e85bb371c87d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -91,6 +91,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)}, + /* required last entry */ {0, } }; -- cgit v1.2.3-58-ga151 From 0b3191d4ea6403f429beab5097428093e170fe44 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Sep 2016 09:49:04 -0700 Subject: ttyprintk: Neaten and simplify printing The code is a bit obscure. Simplify it by using a single routine to flush the tpk_buffer and emit the buffer prefixed with "[U] ". Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 69 ++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index b098d2d0b7c4..67549ce88cc9 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -31,60 +31,53 @@ static struct ttyprintk_port tpk_port; * printk messages (also suitable for logging service): * - any cr is replaced by nl * - adds a ttyprintk source tag in front of each line - * - too long message is fragmeted, with '\'nl between fragments - * - TPK_STR_SIZE isn't really the write_room limiting factor, bcause + * - too long message is fragmented, with '\'nl between fragments + * - TPK_STR_SIZE isn't really the write_room limiting factor, because * it is emptied on the fly during preformatting. */ #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */ #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */ -static const char *tpk_tag = "[U] "; /* U for User */ static int tpk_curr; +static char tpk_buffer[TPK_STR_SIZE + 4]; + +static void tpk_flush(void) +{ + if (tpk_curr > 0) { + tpk_buffer[tpk_curr] = '\0'; + pr_info("[U] %s\n", tpk_buffer); + tpk_curr = 0; + } +} + static int tpk_printk(const unsigned char *buf, int count) { - static char tmp[TPK_STR_SIZE + 4]; int i = tpk_curr; if (buf == NULL) { - /* flush tmp[] */ - if (tpk_curr > 0) { - /* non nl or cr terminated message - add nl */ - tmp[tpk_curr + 0] = '\n'; - tmp[tpk_curr + 1] = '\0'; - printk(KERN_INFO "%s%s", tpk_tag, tmp); - tpk_curr = 0; - } + tpk_flush(); return i; } for (i = 0; i < count; i++) { - tmp[tpk_curr] = buf[i]; - if (tpk_curr < TPK_STR_SIZE) { - switch (buf[i]) { - case '\r': - /* replace cr with nl */ - tmp[tpk_curr + 0] = '\n'; - tmp[tpk_curr + 1] = '\0'; - printk(KERN_INFO "%s%s", tpk_tag, tmp); - tpk_curr = 0; - if ((i + 1) < count && buf[i + 1] == '\n') - i++; - break; - case '\n': - tmp[tpk_curr + 1] = '\0'; - printk(KERN_INFO "%s%s", tpk_tag, tmp); - tpk_curr = 0; - break; - default: - tpk_curr++; - } - } else { + if (tpk_curr >= TPK_STR_SIZE) { /* end of tmp buffer reached: cut the message in two */ - tmp[tpk_curr + 1] = '\\'; - tmp[tpk_curr + 2] = '\n'; - tmp[tpk_curr + 3] = '\0'; - printk(KERN_INFO "%s%s", tpk_tag, tmp); - tpk_curr = 0; + tpk_buffer[tpk_curr++] = '\\'; + tpk_flush(); + } + + switch (buf[i]) { + case '\r': + tpk_flush(); + if ((i + 1) < count && buf[i + 1] == '\n') + i++; + break; + case '\n': + tpk_flush(); + break; + default: + tpk_buffer[tpk_curr++] = buf[i]; + break; } } -- cgit v1.2.3-58-ga151 From 4b7b948b1fcde59d9a0085237be0060e825216fc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Sep 2016 14:59:41 +0200 Subject: vme: fake: fix build for 64-bit dma_addr_t casting between dma_addr_t and a pointer is generally tricky, as they might not be the same size and almost never point into the same address space. With 32-bit ARM systems and LPAE, we get this warning for the vme_fake driver that stores a pointer in a dma_addr_t variable: drivers/vme/bridges/vme_fake.c: In function 'fake_slave_set': drivers/vme/bridges/vme_fake.c:204:29: error: assignment makes pointer from integer without a cast [-Werror=int-conversion] To make this clearer while fixing the warning, I'm adding a set of helper functions for the type conversion. Signed-off-by: Arnd Bergmann Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_fake.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c index 7ef298b289f4..ebf35d305321 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/vme/bridges/vme_fake.c @@ -48,7 +48,7 @@ struct fake_slave_window { int enabled; unsigned long long vme_base; unsigned long long size; - dma_addr_t buf_base; + void *buf_base; u32 aspace; u32 cycle; }; @@ -114,6 +114,16 @@ static void fake_irq_set(struct vme_bridge *fake_bridge, int level, /* Nothing to do */ } +static void *fake_pci_to_ptr(dma_addr_t addr) +{ + return (void *)(uintptr_t)addr; +} + +static dma_addr_t fake_ptr_to_pci(void *addr) +{ + return (dma_addr_t)(uintptr_t)addr; +} + /* * Generate a VME bus interrupt at the requested level & vector. Wait for * interrupt to be acked. @@ -202,7 +212,7 @@ static int fake_slave_set(struct vme_slave_resource *image, int enabled, bridge->slaves[i].enabled = enabled; bridge->slaves[i].vme_base = vme_base; bridge->slaves[i].size = size; - bridge->slaves[i].buf_base = buf_base; + bridge->slaves[i].buf_base = fake_pci_to_ptr(buf_base); bridge->slaves[i].aspace = aspace; bridge->slaves[i].cycle = cycle; @@ -230,7 +240,7 @@ static int fake_slave_get(struct vme_slave_resource *image, int *enabled, *enabled = bridge->slaves[i].enabled; *vme_base = bridge->slaves[i].vme_base; *size = bridge->slaves[i].size; - *buf_base = bridge->slaves[i].buf_base; + *buf_base = fake_ptr_to_pci(bridge->slaves[i].buf_base); *aspace = bridge->slaves[i].aspace; *cycle = bridge->slaves[i].cycle; @@ -431,7 +441,7 @@ static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr, if ((addr >= start) && (addr < end)) { offset = addr - bridge->slaves[i].vme_base; - loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset); + loc = (u8 *)(bridge->slaves[i].buf_base + offset); retval = *loc; break; @@ -463,7 +473,7 @@ static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr, if ((addr >= start) && ((addr + 1) < end)) { offset = addr - bridge->slaves[i].vme_base; - loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset); + loc = (u16 *)(bridge->slaves[i].buf_base + offset); retval = *loc; break; @@ -495,7 +505,7 @@ static u32 fake_vmeread32(struct fake_driver *bridge, unsigned long long addr, if ((addr >= start) && ((addr + 3) < end)) { offset = addr - bridge->slaves[i].vme_base; - loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset); + loc = (u32 *)(bridge->slaves[i].buf_base + offset); retval = *loc; break; @@ -997,7 +1007,7 @@ static void *fake_alloc_consistent(struct device *parent, size_t size, void *alloc = kmalloc(size, GFP_KERNEL); if (alloc != NULL) - *dma = (dma_addr_t)(unsigned long)alloc; + *dma = fake_ptr_to_pci(alloc); return alloc; } @@ -1031,7 +1041,7 @@ static int fake_crcsr_init(struct vme_bridge *fake_bridge) /* Allocate mem for CR/CSR image */ bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL); - bridge->crcsr_bus = (dma_addr_t)bridge->crcsr_kernel; + bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel); if (bridge->crcsr_kernel == NULL) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 6eb1c9496b81680f2cd2e0eda06c531317e2e28d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 19 Sep 2016 01:16:44 +0900 Subject: clk: probe common clock drivers earlier Several SoCs implement platform drivers for clocks rather than CLK_OF_DECLARE(). Clocks should come earlier because they are prerequisites for many of other drivers. It will help to mitigate EPROBE_DEFER issues. Also, drop the comment since it does not carry much value. Signed-off-by: Masahiro Yamada Acked-by: Michael Turquette Signed-off-by: Greg Kroah-Hartman --- drivers/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index 53abb4a5f736..f0afdfb3c7df 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_SFI) += sfi/ # was used and do nothing if so obj-$(CONFIG_PNP) += pnp/ obj-y += amba/ + +obj-y += clk/ # Many drivers will want to use DMA so this has to be made available # really early. obj-$(CONFIG_DMADEVICES) += dma/ @@ -142,8 +144,6 @@ obj-$(CONFIG_VHOST) += vhost/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ -#common clk code -obj-y += clk/ obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ -- cgit v1.2.3-58-ga151 From 1892fc2ee4c08d7ac17adb93f1341d9c3d7bdaf6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 25 Sep 2016 13:25:31 +0300 Subject: mei: stop the stall timer worker if not needed The stall timer worker checks periodically if there is a stalled i/o transaction. The issue with the current implementation is that the timer is ticking also when there is no pending i/o transaction. This patch provides a simple change that prevents rescheduling of the delayed work when there is no pending i/o. Cc: Andy Lutomirski Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 1 + drivers/misc/mei/client.c | 2 ++ drivers/misc/mei/hbm.c | 3 +++ drivers/misc/mei/init.c | 2 +- drivers/misc/mei/interrupt.c | 25 +++++++++++++++++++++---- drivers/misc/mei/mei_dev.h | 1 + drivers/misc/mei/pci-me.c | 2 -- 7 files changed, 29 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 082462ea90c9..7ae89b4a21d5 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -277,6 +277,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) case MEI_FOP_WRITE: if (!cb->status) { dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; + mei_schedule_stall_timer(dev); mei_io_cb_free(cb); return; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 45a7652820cf..6fe02350578d 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -826,6 +826,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) list_move_tail(&cb->list, &dev->ctrl_rd_list.list); cl->timer_count = MEI_CONNECT_TIMEOUT; + mei_schedule_stall_timer(dev); return 0; } @@ -1011,6 +1012,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) list_move_tail(&cb->list, &dev->ctrl_rd_list.list); cl->timer_count = MEI_CONNECT_TIMEOUT; + mei_schedule_stall_timer(dev); return 0; } diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 4b9495f0394c..dd7f15a65eed 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -277,6 +277,7 @@ int mei_hbm_start_req(struct mei_device *dev) dev->hbm_state = MEI_HBM_STARTING; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; + mei_schedule_stall_timer(dev); return 0; } @@ -312,6 +313,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) } dev->hbm_state = MEI_HBM_ENUM_CLIENTS; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; + mei_schedule_stall_timer(dev); return 0; } @@ -562,6 +564,7 @@ static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx) } dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; + mei_schedule_stall_timer(dev); return 0; } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index f7c8dfdb6a12..9a9c2484d107 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -94,7 +94,7 @@ void mei_cancel_work(struct mei_device *dev) cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->bus_rescan_work); - cancel_delayed_work(&dev->timer_work); + cancel_delayed_work_sync(&dev->timer_work); } EXPORT_SYMBOL_GPL(mei_cancel_work); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index bf745e03f21e..5a4893ce9c24 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -459,6 +459,19 @@ static void mei_connect_timeout(struct mei_cl *cl) mei_reset(dev); } +#define MEI_STALL_TIMER_FREQ (2 * HZ) +/** + * mei_schedule_stall_timer - re-arm stall_timer work + * + * Schedule stall timer + * + * @dev: the device structure + */ +void mei_schedule_stall_timer(struct mei_device *dev) +{ + schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ); +} + /** * mei_timer - timer function. * @@ -468,10 +481,9 @@ static void mei_connect_timeout(struct mei_cl *cl) void mei_timer(struct work_struct *work) { struct mei_cl *cl; - struct mei_device *dev = container_of(work, struct mei_device, timer_work.work); - + bool reschedule_timer = false; mutex_lock(&dev->device_lock); @@ -486,6 +498,7 @@ void mei_timer(struct work_struct *work) mei_reset(dev); goto out; } + reschedule_timer = true; } } @@ -500,6 +513,7 @@ void mei_timer(struct work_struct *work) mei_connect_timeout(cl); goto out; } + reschedule_timer = true; } } @@ -512,11 +526,14 @@ void mei_timer(struct work_struct *work) mei_reset(dev); mei_amthif_run_next_cmd(dev); + goto out; } + reschedule_timer = true; } out: - if (dev->dev_state != MEI_DEV_DISABLED) - schedule_delayed_work(&dev->timer_work, 2 * HZ); + if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer) + mei_schedule_stall_timer(dev); + mutex_unlock(&dev->device_lock); } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 397ae2b45c9e..1169fd9e7d02 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -548,6 +548,7 @@ void mei_cancel_work(struct mei_device *dev); */ void mei_timer(struct work_struct *work); +void mei_schedule_stall_timer(struct mei_device *dev); int mei_irq_read_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list, s32 *slots); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index e85bb371c87d..f3ffd883b232 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -220,8 +220,6 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->timer_work, HZ); - /* * For not wake-able HW runtime pm framework * can't be used on pci device level. -- cgit v1.2.3-58-ga151 From 15bb81d334e99081e105b76e6d0b46657b4f4f2e Mon Sep 17 00:00:00 2001 From: Michael Moese Date: Wed, 14 Sep 2016 12:05:23 +0200 Subject: mcb: Enable PCI bus mastering by default In order to successfully perform DMA operations on PCI devices, it is necessary to enble PCI bus mastering, so enable it by default. Signed-off-by: Michael Moese Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index b15a0349cd97..af4d2f26f1c6 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -46,6 +46,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(&pdev->dev, "Failed to enable PCI device\n"); return -ENODEV; } + pci_set_master(pdev); priv->mapbase = pci_resource_start(pdev, 0); if (!priv->mapbase) { -- cgit v1.2.3-58-ga151 From 2d8784df1a1c11ace4e244780facec1e945c5b4f Mon Sep 17 00:00:00 2001 From: Michael Moese Date: Wed, 14 Sep 2016 12:05:24 +0200 Subject: mcb: Add a dma_device to mcb_device When performing DMA operations on a MCB device, the device needed for using the DMA API is "mcb_device->bus_carrier". This is rather lengthy, so a shortcut is introduced to struct mcb_device in order to ensure the MCB device driver uses the correct device for DMA operations. Signed-off-by: Michael Moese Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-core.c | 1 + include/linux/mcb.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index 5306966efc11..921a5d2a802b 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -233,6 +233,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev) dev->dev.bus = &mcb_bus_type; dev->dev.parent = bus->dev.parent; dev->dev.release = mcb_release_dev; + dev->dma_dev = bus->carrier; device_id = dev->id; dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d", diff --git a/include/linux/mcb.h b/include/linux/mcb.h index ee5200d660b0..4097ac9ea13a 100644 --- a/include/linux/mcb.h +++ b/include/linux/mcb.h @@ -76,6 +76,7 @@ struct mcb_device { int rev; struct resource irq; struct resource mem; + struct device *dma_dev; }; static inline struct mcb_device *to_mcb_device(struct device *dev) -- cgit v1.2.3-58-ga151 From b294809dbfa7e50229d00253d43f9a56e5d6a0ba Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 16 Sep 2016 09:01:17 -0700 Subject: Drivers: hv: make VMBus bus ids persistent Some tools use bus ids to identify devices and they count on the fact that these ids are persistent across reboot. This may be not true for VMBus as we use auto incremented counter from alloc_channel() as such id. Switch to using if_instance from channel offer, this id is supposed to be persistent. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 6cbe07462785..a259e18d22d5 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -961,8 +961,8 @@ int vmbus_device_register(struct hv_device *child_device_obj) { int ret = 0; - dev_set_name(&child_device_obj->device, "vmbus_%d", - child_device_obj->channel->id); + dev_set_name(&child_device_obj->device, "vmbus-%pUl", + child_device_obj->channel->offermsg.offer.if_instance.b); child_device_obj->device.bus = &hv_bus; child_device_obj->device.parent = &hv_acpi_dev->dev; -- cgit v1.2.3-58-ga151 From e7fca5d860aeeb1e606448f5191cea8d925cc7a3 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 16 Sep 2016 09:01:18 -0700 Subject: Drivers: hv: get rid of id in struct vmbus_channel The auto incremented counter is not being used anymore, get rid of it. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 -- include/linux/hyperv.h | 3 --- 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 759ba4d62417..96a85cd39580 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -274,14 +274,12 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); */ static struct vmbus_channel *alloc_channel(void) { - static atomic_t chan_num = ATOMIC_INIT(0); struct vmbus_channel *channel; channel = kzalloc(sizeof(*channel), GFP_ATOMIC); if (!channel) return NULL; - channel->id = atomic_inc_return(&chan_num); channel->acquire_ring_lock = true; spin_lock_init(&channel->inbound_lock); spin_lock_init(&channel->lock); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7d7cbff33bda..cd184bdca58f 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -706,9 +706,6 @@ struct vmbus_device { }; struct vmbus_channel { - /* Unique channel id */ - int id; - struct list_head listentry; struct hv_device *device_obj; -- cgit v1.2.3-58-ga151 From e98cc182a06af3b8e91d6d719708db07045d78c4 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 26 Sep 2016 22:24:46 +0200 Subject: spmi: pmic-arb: Return an error code if sanity check fails If the test 'if (channel > 5)' is true, then we will return 'err' which is known to be 0 at this point. Return -EINVAL instead. Signed-off-by: Christophe JAILLET Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index aca282d45421..5ec3a595dc7d 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -954,6 +954,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) if (channel > 5) { dev_err(&pdev->dev, "invalid channel (%u) specified.\n", channel); + err = -EINVAL; goto err_put_ctrl; } -- cgit v1.2.3-58-ga151 From 58ccaae30322b13c587596adb39408fca2a5ef7f Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Fri, 23 Sep 2016 21:38:28 +0800 Subject: vme: fake: mark symbols static where possible We get 4 warnings when building kernel with W=1: drivers/vme/bridges/vme_fake.c:384:6: warning: no previous prototype for 'fake_lm_check' [-Wmissing-prototypes] drivers/vme/bridges/vme_fake.c:619:6: warning: no previous prototype for 'fake_vmewrite8' [-Wmissing-prototypes] drivers/vme/bridges/vme_fake.c:649:6: warning: no previous prototype for 'fake_vmewrite16' [-Wmissing-prototypes] drivers/vme/bridges/vme_fake.c:679:6: warning: no previous prototype for 'fake_vmewrite32' [-Wmissing-prototypes] In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. so this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_fake.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c index ebf35d305321..29ac74f7889d 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/vme/bridges/vme_fake.c @@ -381,8 +381,8 @@ static int fake_master_get(struct vme_master_resource *image, int *enabled, } -void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, - u32 aspace, u32 cycle) +static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, + u32 aspace, u32 cycle) { struct vme_bridge *fake_bridge; unsigned long long lm_base; @@ -616,8 +616,8 @@ out: return retval; } -void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, + unsigned long long addr, u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; @@ -646,8 +646,8 @@ void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, } -void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, + unsigned long long addr, u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; @@ -676,8 +676,8 @@ void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, } -void fake_vmewrite32(struct fake_driver *bridge, u32 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static void fake_vmewrite32(struct fake_driver *bridge, u32 *buf, + unsigned long long addr, u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; -- cgit v1.2.3-58-ga151 From db08948b97180a3c9f49ce91caf1fb643a8110be Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 17 Sep 2016 01:57:34 +0000 Subject: vme: fake: remove unexpected unlock in fake_master_set() image->lock is unlocked in some error handling path without take the lock, so remove those unexpected unlock. Fixes: 658bcdae9c67 ("vme: Adding Fake VME driver") Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_fake.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c index 29ac74f7889d..30b3acc93833 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/vme/bridges/vme_fake.c @@ -273,7 +273,6 @@ static int fake_master_set(struct vme_master_resource *image, int enabled, } if (size & 0xFFFF) { - spin_unlock(&image->lock); pr_err("Invalid size alignment\n"); retval = -EINVAL; goto err_window; @@ -292,7 +291,6 @@ static int fake_master_set(struct vme_master_resource *image, int enabled, case VME_D32: break; default: - spin_unlock(&image->lock); pr_err("Invalid data width\n"); retval = -EINVAL; goto err_dwidth; @@ -311,7 +309,6 @@ static int fake_master_set(struct vme_master_resource *image, int enabled, case VME_USER4: break; default: - spin_unlock(&image->lock); pr_err("Invalid address space\n"); retval = -EINVAL; goto err_aspace; -- cgit v1.2.3-58-ga151 From 13decfb4383b13b0f75e88709f4fad9138bd243b Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 12 Sep 2016 20:09:40 +0200 Subject: misc/genwqe: ensure zero initialization Genwqe uses dma_alloc_coherent and depends on zero initialized memory. On one occasion it ueses an explicit memset on others it uses un-initialized memory. This bug was covered because some archs actually return zero initialized memory when using dma_alloc_coherent but this is by no means guaranteed. Simply switch to dma_zalloc_coherent. Signed-off-by: Sebastian Ott Signed-off-by: Frank Haverkamp Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_ddcb.c | 2 -- drivers/misc/genwqe/card_utils.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index 353ee0cc733d..ddfeefe39540 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -1048,8 +1048,6 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) "[%s] **err: could not allocate DDCB **\n", __func__); return -ENOMEM; } - memset(queue->ddcb_vaddr, 0, queue_size); - queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) * queue->ddcb_max, GFP_KERNEL); if (!queue->ddcb_req) { diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 222367cc8c81..8a679ecc8fd1 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -220,8 +220,8 @@ void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size, if (get_order(size) > MAX_ORDER) return NULL; - return dma_alloc_coherent(&cd->pci_dev->dev, size, dma_handle, - GFP_KERNEL); + return dma_zalloc_coherent(&cd->pci_dev->dev, size, dma_handle, + GFP_KERNEL); } void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size, -- cgit v1.2.3-58-ga151 From 3936e4c8db2095af5e6c704a3e4b2b500f568ae5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 11 Sep 2016 14:58:26 +0300 Subject: at25: fix debug and error messaging The patch does the following: - fixes specifiers and removes explicit casting of the parameters - joins literals to one line - increases readability of the parameters Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 2c6c7c8e3ead..5afe4cd16569 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -121,9 +121,8 @@ static int at25_ee_read(void *priv, unsigned int offset, * this chip is clocked very slowly */ status = spi_sync(at25->spi, &m); - dev_dbg(&at25->spi->dev, - "read %Zd bytes at %d --> %d\n", - count, offset, (int) status); + dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n", + count, offset, status); mutex_unlock(&at25->lock); return status; @@ -167,8 +166,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) *cp = AT25_WREN; status = spi_write(at25->spi, cp, 1); if (status < 0) { - dev_dbg(&at25->spi->dev, "WREN --> %d\n", - (int) status); + dev_dbg(&at25->spi->dev, "WREN --> %d\n", status); break; } @@ -196,9 +194,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) memcpy(cp, buf, segment); status = spi_write(at25->spi, bounce, segment + at25->addrlen + 1); - dev_dbg(&at25->spi->dev, - "write %u bytes at %u --> %d\n", - segment, offset, (int) status); + dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n", + segment, offset, status); if (status < 0) break; @@ -225,8 +222,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) if ((sr < 0) || (sr & AT25_SR_nRDY)) { dev_err(&at25->spi->dev, - "write %d bytes offset %d, " - "timeout after %u msecs\n", + "write %u bytes offset %u, timeout after %u msecs\n", segment, offset, jiffies_to_msecs(jiffies - (timeout - EE_TIMEOUT))); @@ -368,9 +364,7 @@ static int at25_probe(struct spi_device *spi) return PTR_ERR(at25->nvmem); dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n", - (chip.byte_len < 1024) - ? chip.byte_len - : (chip.byte_len / 1024), + (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), (chip.byte_len < 1024) ? "Byte" : "KByte", at25->chip.name, (chip.flags & EE_READONLY) ? " (readonly)" : "", -- cgit v1.2.3-58-ga151 From c9fef1cc3dd3677633e6fd6ea5bd7ef3b741fab3 Mon Sep 17 00:00:00 2001 From: "Rusk, Mark" Date: Mon, 19 Sep 2016 19:50:01 +0000 Subject: drivers/misc/hpilo: Changes to support new security states in iLO5 FW Changes to support new security states of the iLO5 firmware. - use BAR5 for CCB's for iLO5 - simplification of error handling Signed-off-by: Mark Rusk Signed-off-by: David Altobelli Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index d6a901cd4222..fea8ff40440f 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -688,7 +688,8 @@ static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) { - int error = -ENOMEM; + int bar; + unsigned long off; /* map the memory mapped i/o registers */ hw->mmio_vaddr = pci_iomap(pdev, 1, 0); @@ -698,7 +699,15 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) } /* map the adapter shared memory region */ - hw->ram_vaddr = pci_iomap(pdev, 2, max_ccb * ILOHW_CCB_SZ); + if (pdev->subsystem_device == 0x00E4) { + bar = 5; + /* Last 8k is reserved for CCBs */ + off = pci_resource_len(pdev, bar) - 0x2000; + } else { + bar = 2; + off = 0; + } + hw->ram_vaddr = pci_iomap_range(pdev, bar, off, max_ccb * ILOHW_CCB_SZ); if (hw->ram_vaddr == NULL) { dev_err(&pdev->dev, "Error mapping shared mem\n"); goto mmio_free; @@ -717,7 +726,7 @@ ram_free: mmio_free: pci_iounmap(pdev, hw->mmio_vaddr); out: - return error; + return -ENOMEM; } static void ilo_remove(struct pci_dev *pdev) @@ -899,7 +908,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.4.1"); +MODULE_VERSION("1.5.0"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli "); -- cgit v1.2.3-58-ga151