From ae8a35aef41f2b9cf69fd489767291675f3fe291 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sun, 7 Apr 2013 11:28:42 +0800 Subject: Drivers: Misc: tsl2250: fix warnings, unsigned long will never < 0 val is unsigned long which never < 0 Signed-off-by: Chen Gang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tsl2550.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 1dfde4d543d..5bc10fa193d 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -204,7 +204,7 @@ static ssize_t tsl2550_store_power_state(struct device *dev, unsigned long val = simple_strtoul(buf, NULL, 10); int ret; - if (val < 0 || val > 1) + if (val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -236,7 +236,7 @@ static ssize_t tsl2550_store_operating_mode(struct device *dev, unsigned long val = simple_strtoul(buf, NULL, 10); int ret; - if (val < 0 || val > 1) + if (val > 1) return -EINVAL; if (data->power_state == 0) -- cgit v1.2.3-70-g09d2 From 21b066f19a81bd2af496745796914cfeca7a8ea7 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 2 May 2013 14:39:34 +0200 Subject: drivers: misc: use devm_ioremap_resource() Replace a call to deprecated devm_request_and_ioremap by devm_ioremap_resource. Found with coccicheck and this semantic patch: scripts/coccinelle/api/devm_request_and_ioremap.cocci. Signed-off-by: Laurent Navet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 437192e4300..4a938860fec 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -50,9 +50,10 @@ static int sram_probe(struct platform_device *pdev) size = resource_size(res); - virt_base = devm_request_and_ioremap(&pdev->dev, res); - if (!virt_base) - return -EADDRNOTAVAIL; + virt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(virt_base)) + return PTR_ERR(virt_base); + sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); if (!sram) -- cgit v1.2.3-70-g09d2 From 87f0a427547d8552f87bfb19baa3d66d528aa761 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 14:32:11 +0900 Subject: misc: arm-charlcd: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/misc/arm-charlcd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index 48651ef0028..1256a4bf1c0 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -291,7 +291,7 @@ static int __init charlcd_probe(struct platform_device *pdev) lcd->virtbase = ioremap(lcd->phybase, lcd->physize); if (!lcd->virtbase) { ret = -ENOMEM; - goto out_no_remap; + goto out_no_memregion; } lcd->irq = platform_get_irq(pdev, 0); @@ -320,8 +320,6 @@ static int __init charlcd_probe(struct platform_device *pdev) out_no_irq: iounmap(lcd->virtbase); -out_no_remap: - platform_set_drvdata(pdev, NULL); out_no_memregion: release_mem_region(lcd->phybase, SZ_4K); out_no_resource: @@ -337,7 +335,6 @@ static int __exit charlcd_remove(struct platform_device *pdev) free_irq(lcd->irq, lcd); iounmap(lcd->virtbase); release_mem_region(lcd->phybase, lcd->physize); - platform_set_drvdata(pdev, NULL); kfree(lcd); } -- cgit v1.2.3-70-g09d2 From e7d87ca1089165c05376df50f1be0f68d72ab08b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 14:34:15 +0900 Subject: misc: ep93xx_pwm: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Reviewed-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 96787ec15ca..9ba93f0fe1d 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -347,7 +347,6 @@ static int __exit ep93xx_pwm_remove(struct platform_device *pdev) ep93xx_pwm_disable(pwm); clk_disable(pwm->clk); clk_put(pwm->clk); - platform_set_drvdata(pdev, NULL); sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); iounmap(pwm->mmio_base); release_mem_region(res->start, resource_size(res)); -- cgit v1.2.3-70-g09d2 From db086fa926e57e1bd70e8c41235d230b3caa5e99 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 12 May 2013 15:34:45 +0300 Subject: mei: move mei_cl_complete to client.c 1. rename mei_cl_complete_handler to mei_cl_complete 2. move the function client.c where it belongs Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 26 ++++++++++++++++++++++++++ drivers/misc/mei/client.h | 1 + drivers/misc/mei/interrupt.c | 28 +--------------------------- 3 files changed, 28 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e310ca6ed1a..c2534ca5c6f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -785,6 +785,32 @@ err: } +/** + * mei_cl_complete - processes completed operation for a client + * + * @cl: private data of the file object. + * @cb: callback block. + */ +void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) +{ + if (cb->fop_type == MEI_FOP_WRITE) { + mei_io_cb_free(cb); + cb = NULL; + cl->writing_state = MEI_WRITE_COMPLETE; + if (waitqueue_active(&cl->tx_wait)) + wake_up_interruptible(&cl->tx_wait); + + } else if (cb->fop_type == MEI_FOP_READ && + MEI_READING == cl->reading_state) { + cl->reading_state = MEI_READ_COMPLETE; + if (waitqueue_active(&cl->rx_wait)) + wake_up_interruptible(&cl->rx_wait); + else + mei_cl_bus_rx_event(cl); + + } +} + /** * mei_cl_all_disconnect - disconnect forcefully all connected clients diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index cfdb144526a..7dc2af7b6fb 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -89,6 +89,7 @@ int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); int mei_cl_read_start(struct mei_cl *cl, size_t length); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); +void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); void mei_host_client_init(struct work_struct *work); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 2ad73698941..93da90a0986 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -30,32 +30,6 @@ #include "client.h" -/** - * mei_cl_complete_handler - processes completed operation for a client - * - * @cl: private data of the file object. - * @cb: callback block. - */ -static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb) -{ - if (cb->fop_type == MEI_FOP_WRITE) { - mei_io_cb_free(cb); - cb = NULL; - cl->writing_state = MEI_WRITE_COMPLETE; - if (waitqueue_active(&cl->tx_wait)) - wake_up_interruptible(&cl->tx_wait); - - } else if (cb->fop_type == MEI_FOP_READ && - MEI_READING == cl->reading_state) { - cl->reading_state = MEI_READ_COMPLETE; - if (waitqueue_active(&cl->rx_wait)) - wake_up_interruptible(&cl->rx_wait); - else - mei_cl_bus_rx_event(cl); - - } -} - /** * mei_irq_compl_handler - dispatch complete handelers * for the completed callbacks @@ -78,7 +52,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) if (cl == &dev->iamthif_cl) mei_amthif_complete(dev, cb); else - mei_cl_complete_handler(cl, cb); + mei_cl_complete(cl, cb); } } EXPORT_SYMBOL_GPL(mei_irq_compl_handler); -- cgit v1.2.3-70-g09d2 From 6220d6a05089220d913701416ab9423af23cd1bb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 12 May 2013 15:34:46 +0300 Subject: mei: revamp interrupt thread handlers 1. Use common prefix mei_cl_irq_ and common parameter list for client handlers. 2. Update function kdocs Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 6 +- drivers/misc/mei/interrupt.c | 130 +++++++++++++++++++++---------------------- drivers/misc/mei/mei_dev.h | 13 +---- 3 files changed, 68 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b3e50984d2c..749452f8e2f 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -443,11 +443,11 @@ unsigned int mei_amthif_poll(struct mei_device *dev, * * returns 0, OK; otherwise, error. */ -int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) +int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) { + struct mei_device *dev = cl->dev; struct mei_msg_hdr mei_hdr; - struct mei_cl *cl = cb->cl; size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; u32 msg_slots = mei_data2slots(len); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 93da90a0986..7d9509a094e 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -163,21 +163,21 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, } /** - * _mei_irq_thread_close - processes close related operation. + * mei_cl_irq_close - processes close related operation from + * interrupt thread context - send disconnect request * - * @dev: the device structure. + * @cl: client + * @cb: callback block. * @slots: free slots. - * @cb_pos: callback block. - * @cl: private data of the file object. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb_pos, - struct mei_cl *cl, - struct mei_cl_cb *cmpl_list) +static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) { + struct mei_device *dev = cl->dev; + u32 msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); @@ -188,15 +188,15 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, if (mei_hbm_cl_disconnect_req(dev, cl)) { cl->status = 0; - cb_pos->buf_idx = 0; - list_move_tail(&cb_pos->list, &cmpl_list->list); + cb->buf_idx = 0; + list_move_tail(&cb->list, &cmpl_list->list); return -EIO; } cl->state = MEI_FILE_DISCONNECTING; cl->status = 0; - cb_pos->buf_idx = 0; - list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); + cb->buf_idx = 0; + list_move_tail(&cb->list, &dev->ctrl_rd_list.list); cl->timer_count = MEI_CONNECT_TIMEOUT; return 0; @@ -204,26 +204,26 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, /** - * _mei_irq_thread_read - processes read related operation. + * mei_cl_irq_close - processes client read related operation from the + * interrupt thread context - request for flow control credits * - * @dev: the device structure. + * @cl: client + * @cb: callback block. * @slots: free slots. - * @cb_pos: callback block. - * @cl: private data of the file object. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb_pos, - struct mei_cl *cl, - struct mei_cl_cb *cmpl_list) +static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) { + struct mei_device *dev = cl->dev; + u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); if (*slots < msg_slots) { /* return the cancel routine */ - list_del(&cb_pos->list); + list_del(&cb->list); return -EMSGSIZE; } @@ -231,38 +231,38 @@ static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, if (mei_hbm_cl_flow_control_req(dev, cl)) { cl->status = -ENODEV; - cb_pos->buf_idx = 0; - list_move_tail(&cb_pos->list, &cmpl_list->list); + cb->buf_idx = 0; + list_move_tail(&cb->list, &cmpl_list->list); return -ENODEV; } - list_move_tail(&cb_pos->list, &dev->read_list.list); + list_move_tail(&cb->list, &dev->read_list.list); return 0; } /** - * _mei_irq_thread_ioctl - processes ioctl related operation. + * mei_cl_irq_ioctl - processes client ioctl related operation from the + * interrupt thread context - send connection request * - * @dev: the device structure. + * @cl: client + * @cb: callback block. * @slots: free slots. - * @cb_pos: callback block. - * @cl: private data of the file object. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb_pos, - struct mei_cl *cl, - struct mei_cl_cb *cmpl_list) +static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) { + struct mei_device *dev = cl->dev; + u32 msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); if (*slots < msg_slots) { /* return the cancel routine */ - list_del(&cb_pos->list); + list_del(&cb->list); return -EMSGSIZE; } @@ -272,31 +272,31 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, if (mei_hbm_cl_connect_req(dev, cl)) { cl->status = -ENODEV; - cb_pos->buf_idx = 0; - list_del(&cb_pos->list); + cb->buf_idx = 0; + list_del(&cb->list); return -ENODEV; - } else { - list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); - cl->timer_count = MEI_CONNECT_TIMEOUT; } + + list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + cl->timer_count = MEI_CONNECT_TIMEOUT; return 0; } /** - * mei_irq_thread_write_complete - write messages to device. + * mei_cl_irq_write_complete - write messages to device. * - * @dev: the device structure. - * @slots: free slots. + * @cl: client * @cb: callback block. + * @slots: free slots. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) +static int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) { + struct mei_device *dev = cl->dev; struct mei_msg_hdr mei_hdr; - struct mei_cl *cl = cb->cl; size_t len = cb->request_buffer.size - cb->buf_idx; u32 msg_slots = mei_data2slots(len); @@ -455,7 +455,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) { struct mei_cl *cl; - struct mei_cl_cb *pos = NULL, *next = NULL; + struct mei_cl_cb *cb, *next; struct mei_cl_cb *list; s32 slots; int ret; @@ -472,19 +472,19 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); list = &dev->write_waiting_list; - list_for_each_entry_safe(pos, next, &list->list, list) { - cl = pos->cl; + list_for_each_entry_safe(cb, next, &list->list, list) { + cl = cb->cl; if (cl == NULL) continue; cl->status = 0; - list_del(&pos->list); + list_del(&cb->list); if (MEI_WRITING == cl->writing_state && - pos->fop_type == MEI_FOP_WRITE && + cb->fop_type == MEI_FOP_WRITE && cl != &dev->iamthif_cl) { dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; - list_add_tail(&pos->list, &cmpl_list->list); + list_add_tail(&cb->list, &cmpl_list->list); } if (cl == &dev->iamthif_cl) { dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); @@ -526,25 +526,23 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) /* complete control write list CB */ dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); - list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) { - cl = pos->cl; + list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { + cl = cb->cl; if (!cl) { - list_del(&pos->list); + list_del(&cb->list); return -ENODEV; } - switch (pos->fop_type) { + switch (cb->fop_type) { case MEI_FOP_CLOSE: /* send disconnect message */ - ret = _mei_irq_thread_close(dev, &slots, pos, - cl, cmpl_list); + ret = mei_cl_irq_close(cl, cb, &slots, cmpl_list); if (ret) return ret; break; case MEI_FOP_READ: /* send flow control message */ - ret = _mei_irq_thread_read(dev, &slots, pos, - cl, cmpl_list); + ret = mei_cl_irq_read(cl, cb, &slots, cmpl_list); if (ret) return ret; @@ -553,8 +551,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) /* connect message */ if (mei_cl_is_other_connecting(cl)) continue; - ret = _mei_irq_thread_ioctl(dev, &slots, pos, - cl, cmpl_list); + ret = mei_cl_irq_ioctl(cl, cb, &slots, cmpl_list); if (ret) return ret; @@ -567,8 +564,8 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); - list_for_each_entry_safe(pos, next, &dev->write_list.list, list) { - cl = pos->cl; + list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { + cl = cb->cl; if (cl == NULL) continue; if (mei_cl_flow_ctrl_creds(cl) <= 0) { @@ -579,14 +576,13 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } if (cl == &dev->iamthif_cl) - ret = mei_amthif_irq_write_complete(dev, &slots, - pos, cmpl_list); + ret = mei_amthif_irq_write_complete(cl, cb, + &slots, cmpl_list); else - ret = mei_irq_thread_write_complete(dev, &slots, pos, - cmpl_list); + ret = mei_cl_irq_write_complete(cl, cb, + &slots, cmpl_list); if (ret) return ret; - } return 0; } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4de5140e737..197ba6481bf 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -502,8 +502,8 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, void mei_amthif_run_next_cmd(struct mei_device *dev); -int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); +int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list); void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_device *dev, @@ -522,15 +522,6 @@ void mei_nfc_host_exit(void); */ extern const uuid_le mei_nfc_guid; -int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, - struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); - -void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); -int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, - struct mei_device *dev, struct mei_msg_hdr *mei_hdr); -int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); - - int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev); int mei_wd_host_init(struct mei_device *dev); -- cgit v1.2.3-70-g09d2 From 80fe6361540fd7e0a38326b02b4f2a5e68c99d0a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 7 May 2013 21:12:31 +0300 Subject: mei: fix out of array access to me clients array The patch 9f81abdac362: "mei: implement mei_cl_connect function" from Jan 8, 2013, leads to the following static checker warning: "drivers/misc/mei/main.c:522 mei_ioctl_connect_client() warn: check 'dev->me_clients[]' for negative offsets (-2)" Reported-by: Dan Carpenter Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7c44c8dbae4..053139f6108 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -489,11 +489,16 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (i >= 0 && !dev->me_clients[i].props.fixed_address) { - cl->me_client_id = dev->me_clients[i].client_id; - cl->state = MEI_FILE_CONNECTING; + if (i < 0 || dev->me_clients[i].props.fixed_address) { + dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", + &data->in_client_uuid); + rets = -ENODEV; + goto end; } + cl->me_client_id = dev->me_clients[i].client_id; + cl->state = MEI_FILE_CONNECTING; + dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", cl->me_client_id); dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", @@ -527,11 +532,6 @@ static int mei_ioctl_connect_client(struct file *file, goto end; } - if (cl->state != MEI_FILE_CONNECTING) { - rets = -ENODEV; - goto end; - } - /* prepare the output buffer */ client = &data->out_client_properties; @@ -543,7 +543,6 @@ static int mei_ioctl_connect_client(struct file *file, rets = mei_cl_connect(cl, file); end: - dev_dbg(&dev->pdev->dev, "free connect cb memory."); return rets; } -- cgit v1.2.3-70-g09d2 From a0ea59d56dfab021ecc65365275e532c6b937adb Mon Sep 17 00:00:00 2001 From: Libin Date: Mon, 13 May 2013 10:17:39 +0800 Subject: char: Use vma_pages() to replace (vm_end - vm_start) >> PAGE_SHIFT (*->vm_end - *->vm_start) >> PAGE_SHIFT operation is implemented as a inline funcion vma_pages() in linux/mm.h, so using it. Signed-off-by: Libin Signed-off-by: Greg Kroah-Hartman --- drivers/char/mspec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index e1f60f968fd..f1d7fa45c27 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -267,7 +267,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, if ((vma->vm_flags & VM_WRITE) == 0) return -EPERM; - pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + pages = vma_pages(vma); vdata_size = sizeof(struct vma_data) + pages * sizeof(long); if (vdata_size <= PAGE_SIZE) vdata = kzalloc(vdata_size, GFP_KERNEL); -- cgit v1.2.3-70-g09d2 From 5e6e78e27a27179bb80545a2a55b8c3ae667aed8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 20 May 2013 10:16:39 -0300 Subject: w1-gpio: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core) we can rely on device core for handling pinctrl, so remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/w1-gpio.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index 46d97014342..f54ece268c9 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -78,13 +77,8 @@ static int w1_gpio_probe(struct platform_device *pdev) { struct w1_bus_master *master; struct w1_gpio_platform_data *pdata; - struct pinctrl *pinctrl; int err; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, "unable to select pin group\n"); - if (of_have_populated_dt()) { err = w1_gpio_probe_dt(pdev); if (err < 0) { -- cgit v1.2.3-70-g09d2 From 9db5c9088f741a7e83310a6a459793c25079c636 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 16 May 2013 13:15:39 +0200 Subject: drivers/misc: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/atmel-ssc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index c09c28f9205..1abd5ad5992 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -154,11 +154,6 @@ static int ssc_probe(struct platform_device *pdev) ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "no mmio resource defined\n"); - return -ENXIO; - } - ssc->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(ssc->regs)) return PTR_ERR(ssc->regs); -- cgit v1.2.3-70-g09d2 From aa8c06f7c9471f5722faa14ec8b887989de1226d Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:41 +0200 Subject: uio/uio_aec: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_aec.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c index 1548982db58..f3611c2d83b 100644 --- a/drivers/uio/uio_aec.c +++ b/drivers/uio/uio_aec.c @@ -160,17 +160,5 @@ static struct pci_driver pci_driver = { .remove = remove, }; -static int __init aectc_init(void) -{ - return pci_register_driver(&pci_driver); -} - -static void __exit aectc_exit(void) -{ - pci_unregister_driver(&pci_driver); -} - +module_pci_driver(pci_driver); MODULE_LICENSE("GPL"); - -module_init(aectc_init); -module_exit(aectc_exit); -- cgit v1.2.3-70-g09d2 From 8bcaec4ee2451937278aab5a655739ad73f25de6 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:44 +0200 Subject: uio/uio_sercos3: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_sercos3.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 81a10a56312..54198321708 100644 --- a/drivers/uio/uio_sercos3.c +++ b/drivers/uio/uio_sercos3.c @@ -226,19 +226,7 @@ static struct pci_driver sercos3_pci_driver = { .remove = sercos3_pci_remove, }; -static int __init sercos3_init_module(void) -{ - return pci_register_driver(&sercos3_pci_driver); -} - -static void __exit sercos3_exit_module(void) -{ - pci_unregister_driver(&sercos3_pci_driver); -} - -module_init(sercos3_init_module); -module_exit(sercos3_exit_module); - +module_pci_driver(sercos3_pci_driver); MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card"); MODULE_AUTHOR("John Ogness "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From b59f9a0533110f236a15bbbf81a2b7a7bffa8ed4 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:43 +0200 Subject: uio/uio_netx: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_netx.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c index 6a4ba5e83e3..28a766b9e19 100644 --- a/drivers/uio/uio_netx.c +++ b/drivers/uio/uio_netx.c @@ -174,19 +174,7 @@ static struct pci_driver netx_pci_driver = { .remove = netx_pci_remove, }; -static int __init netx_init_module(void) -{ - return pci_register_driver(&netx_pci_driver); -} - -static void __exit netx_exit_module(void) -{ - pci_unregister_driver(&netx_pci_driver); -} - -module_init(netx_init_module); -module_exit(netx_exit_module); - +module_pci_driver(netx_pci_driver); MODULE_DEVICE_TABLE(pci, netx_pci_ids); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Hans J. Koch, Manuel Traut"); -- cgit v1.2.3-70-g09d2 From ced9017a4fc7ecf35a9c0c0bd8e46d14876b9fd1 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:42 +0200 Subject: uio/uio_cif: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_cif.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c index 7dd6fc60539..22cdf385ab3 100644 --- a/drivers/uio/uio_cif.c +++ b/drivers/uio/uio_cif.c @@ -135,19 +135,7 @@ static struct pci_driver hilscher_pci_driver = { .remove = hilscher_pci_remove, }; -static int __init hilscher_init_module(void) -{ - return pci_register_driver(&hilscher_pci_driver); -} - -static void __exit hilscher_exit_module(void) -{ - pci_unregister_driver(&hilscher_pci_driver); -} - -module_init(hilscher_init_module); -module_exit(hilscher_exit_module); - +module_pci_driver(hilscher_pci_driver); MODULE_DEVICE_TABLE(pci, hilscher_pci_ids); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger"); -- cgit v1.2.3-70-g09d2 From 3edad321b1bd2e6c8b5f38146c115c8982438f06 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Apr 2013 16:21:26 -0300 Subject: drivers: memory: Introduce Marvell EBU Device Bus driver Marvell EBU SoCs such as Armada 370/XP, Orion5x (88f5xxx) and Discovery (mv78xx0) supports a Device Bus controller to access several kinds of memories and I/O devices (NOR, NAND, SRAM, FPGA). This commit adds a driver to handle this controller. So far only Armada 370, Armada XP and Discovery SoCs are supported. The driver must be registered through a device tree node; as explained in the binding document. For each child node in the device tree, this driver will: * set timing parameters * register a child device * setup an address decoding window, using the mbus driver Keep in mind the address decoding window setup is only a temporary hack. This code will be removed from this devbus driver as soon as a proper device tree binding for the mbus driver is added. Signed-off-by: Ezequiel Garcia Acked-by: Arnd Bergmann Acked-by: Jason Cooper Signed-off-by: Greg Kroah-Hartman --- .../bindings/memory-controllers/mvebu-devbus.txt | 156 ++++++++++ drivers/memory/Kconfig | 10 + drivers/memory/Makefile | 1 + drivers/memory/mvebu-devbus.c | 340 +++++++++++++++++++++ 4 files changed, 507 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt create mode 100644 drivers/memory/mvebu-devbus.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt new file mode 100644 index 00000000000..653c90c34a7 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt @@ -0,0 +1,156 @@ +Device tree bindings for MVEBU Device Bus controllers + +The Device Bus controller available in some Marvell's SoC allows to control +different types of standard memory and I/O devices such as NOR, NAND, and FPGA. +The actual devices are instantiated from the child nodes of a Device Bus node. + +Required properties: + + - compatible: Currently only Armada 370/XP SoC are supported, + with this compatible string: + + marvell,mvebu-devbus + + - reg: A resource specifier for the register space. + This is the base address of a chip select within + the controller's register space. + (see the example below) + + - #address-cells: Must be set to 1 + - #size-cells: Must be set to 1 + - ranges: Must be set up to reflect the memory layout with four + integer values for each chip-select line in use: + 0 + +Mandatory timing properties for child nodes: + +Read parameters: + + - devbus,turn-off-ps: Defines the time during which the controller does not + drive the AD bus after the completion of a device read. + This prevents contentions on the Device Bus after a read + cycle from a slow device. + + - devbus,bus-width: Defines the bus width (e.g. <16>) + + - devbus,badr-skew-ps: Defines the time delay from from A[2:0] toggle, + to read data sample. This parameter is useful for + synchronous pipelined devices, where the address + precedes the read data by one or two cycles. + + - devbus,acc-first-ps: Defines the time delay from the negation of + ALE[0] to the cycle that the first read data is sampled + by the controller. + + - devbus,acc-next-ps: Defines the time delay between the cycle that + samples data N and the cycle that samples data N+1 + (in burst accesses). + + - devbus,rd-setup-ps: Defines the time delay between DEV_CSn assertion to + DEV_OEn assertion. If set to 0 (default), + DEV_OEn and DEV_CSn are asserted at the same cycle. + This parameter has no affect on parameter + (no affect on first data sample). Set + to a value smaller than . + + - devbus,rd-hold-ps: Defines the time between the last data sample to the + de-assertion of DEV_CSn. If set to 0 (default), + DEV_OEn and DEV_CSn are de-asserted at the same cycle + (the cycle of the last data sample). + This parameter has no affect on DEV_OEn de-assertion. + DEV_OEn is always de-asserted the next cycle after + last data sampled. Also this parameter has no + affect on parameter. + Set to a value smaller than . + +Write parameters: + + - devbus,ale-wr-ps: Defines the time delay from the ALE[0] negation cycle + to the DEV_WEn assertion. + + - devbus,wr-low-ps: Defines the time during which DEV_WEn is active. + A[2:0] and Data are kept valid as long as DEV_WEn + is active. This parameter defines the setup time of + address and data to DEV_WEn rise. + + - devbus,wr-high-ps: Defines the time during which DEV_WEn is kept + inactive (high) between data beats of a burst write. + DEV_A[2:0] and Data are kept valid (do not toggle) for + - ps. + This parameter defines the hold time of address and + data after DEV_WEn rise. + + - devbus,sync-enable: Synchronous device enable. + 1: True + 0: False + +An example for an Armada XP GP board, with a 16 MiB NOR device as child +is showed below. Note that the Device Bus driver is in charge of allocating +the mbus address decoding window for each of its child devices. +The window is created using the chip select specified in the child +device node together with the base address and size specified in the ranges +property. For instance, in the example below the allocated decoding window +will start at base address 0xf0000000, with a size 0x1000000 (16 MiB) +for chip select 0 (a.k.a DEV_BOOTCS). + +This address window handling is done in this mvebu-devbus only as a temporary +solution. It will be removed when the support for mbus device tree binding is +added. + +The reg property implicitly specifies the chip select as this: + + 0x10400: DEV_BOOTCS + 0x10408: DEV_CS0 + 0x10410: DEV_CS1 + 0x10418: DEV_CS2 + 0x10420: DEV_CS3 + +Example: + + devbus-bootcs@d0010400 { + status = "okay"; + ranges = <0 0xf0000000 0x1000000>; /* @addr 0xf0000000, size 0x1000000 */ + #address-cells = <1>; + #size-cells = <1>; + + /* Device Bus parameters are required */ + + /* Read parameters */ + devbus,bus-width = <8>; + devbus,turn-off-ps = <60000>; + devbus,badr-skew-ps = <0>; + devbus,acc-first-ps = <124000>; + devbus,acc-next-ps = <248000>; + devbus,rd-setup-ps = <0>; + devbus,rd-hold-ps = <0>; + + /* Write parameters */ + devbus,sync-enable = <0>; + devbus,wr-high-ps = <60000>; + devbus,wr-low-ps = <60000>; + devbus,ale-wr-ps = <60000>; + + flash@0 { + compatible = "cfi-flash"; + + /* 16 MiB */ + reg = <0 0x1000000>; + bank-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + + /* + * We split the 16 MiB in two partitions, + * just as an example. + */ + partition@0 { + label = "First"; + reg = <0 0x800000>; + }; + + partition@800000 { + label = "Second"; + reg = <0x800000 0x800000>; + }; + }; + }; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 067f31174a0..29a11db365b 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -20,6 +20,16 @@ config TI_EMIF parameters and other settings during frequency, voltage and temperature changes +config MVEBU_DEVBUS + bool "Marvell EBU Device Bus Controller" + default y + depends on PLAT_ORION && OF + help + This driver is for the Device Bus controller available in some + Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and + Armada 370 and Armada XP. This controller allows to handle flash + devices such as NOR, NAND, SRAM, and FPGA. + config TEGRA20_MC bool "Tegra20 Memory Controller(MC) driver" default y diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 9cce5d70ed5..969d923dad9 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -6,5 +6,6 @@ ifeq ($(CONFIG_DDR),y) obj-$(CONFIG_OF) += of_memory.o endif obj-$(CONFIG_TI_EMIF) += emif.o +obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c new file mode 100644 index 00000000000..978e8e3abc5 --- /dev/null +++ b/drivers/memory/mvebu-devbus.c @@ -0,0 +1,340 @@ +/* + * Marvell EBU SoC Device Bus Controller + * (memory controller for NOR/NAND/SRAM/FPGA devices) + * + * Copyright (C) 2013 Marvell + * + * 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. + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define DEV_WIDTH_BIT 30 +#define BADR_SKEW_BIT 28 +#define RD_HOLD_BIT 23 +#define ACC_NEXT_BIT 17 +#define RD_SETUP_BIT 12 +#define ACC_FIRST_BIT 6 + +#define SYNC_ENABLE_BIT 24 +#define WR_HIGH_BIT 16 +#define WR_LOW_BIT 8 + +#define READ_PARAM_OFFSET 0x0 +#define WRITE_PARAM_OFFSET 0x4 + +static const char * const devbus_wins[] = { + "devbus-boot", + "devbus-cs0", + "devbus-cs1", + "devbus-cs2", + "devbus-cs3", +}; + +struct devbus_read_params { + u32 bus_width; + u32 badr_skew; + u32 turn_off; + u32 acc_first; + u32 acc_next; + u32 rd_setup; + u32 rd_hold; +}; + +struct devbus_write_params { + u32 sync_enable; + u32 wr_high; + u32 wr_low; + u32 ale_wr; +}; + +struct devbus { + struct device *dev; + void __iomem *base; + unsigned long tick_ps; +}; + +static int get_timing_param_ps(struct devbus *devbus, + struct device_node *node, + const char *name, + u32 *ticks) +{ + u32 time_ps; + int err; + + err = of_property_read_u32(node, name, &time_ps); + if (err < 0) { + dev_err(devbus->dev, "%s has no '%s' property\n", + name, node->full_name); + return err; + } + + *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps; + + dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n", + name, time_ps, *ticks); + return 0; +} + +static int devbus_set_timing_params(struct devbus *devbus, + struct device_node *node) +{ + struct devbus_read_params r; + struct devbus_write_params w; + u32 value; + int err; + + dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", + devbus->tick_ps); + + /* Get read timings */ + err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width); + if (err < 0) { + dev_err(devbus->dev, + "%s has no 'devbus,bus-width' property\n", + node->full_name); + return err; + } + /* Convert bit width to byte width */ + r.bus_width /= 8; + + err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", + &r.badr_skew); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps", + &r.turn_off); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps", + &r.acc_first); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps", + &r.acc_next); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", + &r.rd_setup); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", + &r.rd_hold); + if (err < 0) + return err; + + /* Get write timings */ + err = of_property_read_u32(node, "devbus,sync-enable", + &w.sync_enable); + if (err < 0) { + dev_err(devbus->dev, + "%s has no 'devbus,sync-enable' property\n", + node->full_name); + return err; + } + + err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", + &w.ale_wr); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps", + &w.wr_low); + if (err < 0) + return err; + + err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps", + &w.wr_high); + if (err < 0) + return err; + + /* Set read timings */ + value = r.bus_width << DEV_WIDTH_BIT | + r.badr_skew << BADR_SKEW_BIT | + r.rd_hold << RD_HOLD_BIT | + r.acc_next << ACC_NEXT_BIT | + r.rd_setup << RD_SETUP_BIT | + r.acc_first << ACC_FIRST_BIT | + r.turn_off; + + dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n", + devbus->base + READ_PARAM_OFFSET, + value); + + writel(value, devbus->base + READ_PARAM_OFFSET); + + /* Set write timings */ + value = w.sync_enable << SYNC_ENABLE_BIT | + w.wr_low << WR_LOW_BIT | + w.wr_high << WR_HIGH_BIT | + w.ale_wr; + + dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n", + devbus->base + WRITE_PARAM_OFFSET, + value); + + writel(value, devbus->base + WRITE_PARAM_OFFSET); + + return 0; +} + +static int mvebu_devbus_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct device_node *parent; + struct devbus *devbus; + struct resource *res; + struct clk *clk; + unsigned long rate; + const __be32 *ranges; + int err, cs; + int addr_cells, p_addr_cells, size_cells; + int ranges_len, tuple_len; + u32 base, size; + + devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL); + if (!devbus) + return -ENOMEM; + + devbus->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + devbus->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(devbus->base)) + return PTR_ERR(devbus->base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clk_prepare_enable(clk); + + /* + * Obtain clock period in picoseconds, + * we need this in order to convert timing + * parameters from cycles to picoseconds. + */ + rate = clk_get_rate(clk) / 1000; + devbus->tick_ps = 1000000000 / rate; + + /* Read the device tree node and set the new timing parameters */ + err = devbus_set_timing_params(devbus, node); + if (err < 0) + return err; + + /* + * Allocate an address window for this device. + * If the device probing fails, then we won't be able to + * remove the allocated address decoding window. + * + * FIXME: This is only a temporary hack! We need to do this here + * because we still don't have device tree bindings for mbus. + * Once that support is added, we will declare these address windows + * statically in the device tree, and remove the window configuration + * from here. + */ + + /* + * Get the CS to choose the window string. + * This is a bit hacky, but it will be removed once the + * address windows are declared in the device tree. + */ + cs = (((unsigned long)devbus->base) % 0x400) / 8; + + /* + * Parse 'ranges' property to obtain a (base,size) window tuple. + * This will be removed once the address windows + * are declared in the device tree. + */ + parent = of_get_parent(node); + if (!parent) + return -EINVAL; + + p_addr_cells = of_n_addr_cells(parent); + of_node_put(parent); + + addr_cells = of_n_addr_cells(node); + size_cells = of_n_size_cells(node); + tuple_len = (p_addr_cells + addr_cells + size_cells) * sizeof(__be32); + + ranges = of_get_property(node, "ranges", &ranges_len); + if (ranges == NULL || ranges_len != tuple_len) + return -EINVAL; + + base = of_translate_address(node, ranges + addr_cells); + if (base == OF_BAD_ADDR) + return -EINVAL; + size = of_read_number(ranges + addr_cells + p_addr_cells, size_cells); + + /* + * Create an mbus address windows. + * FIXME: Remove this, together with the above code, once the + * address windows are declared in the device tree. + */ + err = mvebu_mbus_add_window(devbus_wins[cs], base, size); + if (err < 0) + return err; + + /* + * We need to create a child device explicitly from here to + * guarantee that the child will be probed after the timing + * parameters for the bus are written. + */ + err = of_platform_populate(node, NULL, NULL, dev); + if (err < 0) { + mvebu_mbus_del_window(base, size); + return err; + } + + return 0; +} + +static const struct of_device_id mvebu_devbus_of_match[] = { + { .compatible = "marvell,mvebu-devbus" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match); + +static struct platform_driver mvebu_devbus_driver = { + .probe = mvebu_devbus_probe, + .driver = { + .name = "mvebu-devbus", + .owner = THIS_MODULE, + .of_match_table = mvebu_devbus_of_match, + }, +}; + +static int __init mvebu_devbus_init(void) +{ + return platform_driver_register(&mvebu_devbus_driver); +} +module_init(mvebu_devbus_init); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ezequiel Garcia "); +MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller"); -- cgit v1.2.3-70-g09d2 From cd4373984a5903276f52777a6003425e023eaa7e Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 21 May 2013 22:47:44 +0200 Subject: uio/uio_pci_generic: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. It removes a pr_info showing some details about the driver, but these infos can also be retrieved by using modinfo. The name of the pci_driver struct had to be changed in order to prevent a build failure. Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pci_generic.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c index 14aa10c1f6d..077ae12269c 100644 --- a/drivers/uio/uio_pci_generic.c +++ b/drivers/uio/uio_pci_generic.c @@ -113,27 +113,14 @@ static void remove(struct pci_dev *pdev) kfree(gdev); } -static struct pci_driver driver = { +static struct pci_driver uio_pci_driver = { .name = "uio_pci_generic", .id_table = NULL, /* only dynamic id's */ .probe = probe, .remove = remove, }; -static int __init init(void) -{ - pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - return pci_register_driver(&driver); -} - -static void __exit cleanup(void) -{ - pci_unregister_driver(&driver); -} - -module_init(init); -module_exit(cleanup); - +module_pci_driver(uio_pci_driver); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v1.2.3-70-g09d2 From 7131799b145aa67984cc57e52d6379862c78afa3 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 May 2013 10:03:13 +0300 Subject: mei: deprecate the mei_wd_state_independence_msg wd independence is deprecated, remove it. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 25 ------------------------- drivers/misc/mei/mei_dev.h | 6 ------ drivers/misc/mei/wd.c | 6 ------ 3 files changed, 37 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 053139f6108..b9ad5106f5e 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -194,7 +194,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb = NULL; struct mei_device *dev; - int i; int rets; int err; @@ -210,20 +209,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) { - /* Do not allow to read watchdog client */ - i = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (i >= 0) { - struct mei_me_client *me_client = &dev->me_clients[i]; - if (cl->me_client_id == me_client->client_id) { - rets = -EBADF; - goto out; - } - } - } else { - cl->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT; - } - if (cl == &dev->iamthif_cl) { rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; @@ -420,16 +405,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets) goto out; - cl->sm_state = 0; - if (length == 4 && - ((memcmp(mei_wd_state_independence_msg[0], - write_cb->request_buffer.data, 4) == 0) || - (memcmp(mei_wd_state_independence_msg[1], - write_cb->request_buffer.data, 4) == 0) || - (memcmp(mei_wd_state_independence_msg[2], - write_cb->request_buffer.data, 4) == 0))) - cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; - if (cl == &dev->iamthif_cl) { rets = mei_amthif_write(dev, write_cb); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 197ba6481bf..80a90319fc2 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -55,11 +55,6 @@ extern const uuid_le mei_amthif_guid; */ extern const uuid_le mei_wd_guid; -/* - * Watchdog independence state message - */ -extern const u8 mei_wd_state_independence_msg[3][4]; - /* * Number of Maximum MEI Clients */ @@ -201,7 +196,6 @@ struct mei_cl { u8 timer_count; enum mei_file_transaction_states reading_state; enum mei_file_transaction_states writing_state; - int sm_state; struct mei_cl_cb *read_cb; /* MEI CL bus data */ diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 6251a4ee706..b8921432e89 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -31,12 +31,6 @@ static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; -const u8 mei_wd_state_independence_msg[3][4] = { - {0x05, 0x02, 0x51, 0x10}, - {0x05, 0x02, 0x52, 0x10}, - {0x07, 0x02, 0x01, 0x10} -}; - /* * AMT Watchdog Device */ -- cgit v1.2.3-70-g09d2 From 139aacf757fc89792c43a79ba99bc2361c98ecd3 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 29 May 2013 20:09:30 +0300 Subject: mei: fix read after read scenario mei read always has to be preceded by write but 'write write read read' scenario should work as well. In this case the offset is not zero but new read should be initiated Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index b9ad5106f5e..5e11b5b9b65 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -214,19 +214,21 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (cl->read_cb && cl->read_cb->buf_idx > *offset) { - cb = cl->read_cb; - goto copy_buffer; - } else if (cl->read_cb && cl->read_cb->buf_idx > 0 && - cl->read_cb->buf_idx <= *offset) { + if (cl->read_cb) { cb = cl->read_cb; - rets = 0; - goto free; - } else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) { - /*Offset needs to be cleaned for contiguous reads*/ + /* read what left */ + if (cb->buf_idx > *offset) + goto copy_buffer; + /* offset is beyond buf_idx we have no more data return 0 */ + if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { + rets = 0; + goto free; + } + /* Offset needs to be cleaned for contiguous reads*/ + if (cb->buf_idx == 0 && *offset > 0) + *offset = 0; + } else if (*offset > 0) { *offset = 0; - rets = 0; - goto out; } err = mei_cl_read_start(cl, length); -- cgit v1.2.3-70-g09d2 From c6148f8f8ceb18fd6ae3b637c56e8327b73873eb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 29 May 2013 20:09:31 +0300 Subject: mei: me: remove artificial singleton requirement There is only one device on the platform drop mei_pdev and the mutex Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/pci-me.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index a727464e9c3..a2f5520f406 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -43,9 +43,6 @@ #include "hw-me.h" #include "client.h" -/* AMT device is a singleton on the platform */ -static struct pci_dev *mei_pdev; - /* mei_pci_tbl - PCI Device ID Table */ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, @@ -88,8 +85,6 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); -static DEFINE_MUTEX(mei_mutex); - /** * mei_quirk_probe - probe for devices that doesn't valid ME interface * @@ -126,17 +121,12 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct mei_me_hw *hw; int err; - mutex_lock(&mei_mutex); if (!mei_me_quirk_probe(pdev, ent)) { err = -ENODEV; goto end; } - if (mei_pdev) { - err = -EEXIST; - goto end; - } /* enable pci dev */ err = pci_enable_device(pdev); if (err) { @@ -195,13 +185,10 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto release_irq; - mei_pdev = pdev; pci_set_drvdata(pdev, dev); schedule_delayed_work(&dev->timer_work, HZ); - mutex_unlock(&mei_mutex); - pr_debug("initialization successful.\n"); return 0; @@ -220,7 +207,6 @@ release_regions: disable_device: pci_disable_device(pdev); end: - mutex_unlock(&mei_mutex); dev_err(&pdev->dev, "initialization failed.\n"); return err; } @@ -238,9 +224,6 @@ static void mei_me_remove(struct pci_dev *pdev) struct mei_device *dev; struct mei_me_hw *hw; - if (mei_pdev != pdev) - return; - dev = pci_get_drvdata(pdev); if (!dev) return; @@ -251,8 +234,6 @@ static void mei_me_remove(struct pci_dev *pdev) dev_err(&pdev->dev, "stop\n"); mei_stop(dev); - mei_pdev = NULL; - /* disable interrupts */ mei_disable_interrupts(dev); -- cgit v1.2.3-70-g09d2 From 9093ca8891e238adc323629d69aaae379fd38bb0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:35:23 +0900 Subject: misc: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/misc/carma/carma-fpga-program.c | 4 ++-- drivers/misc/carma/carma-fpga.c | 4 ++-- drivers/misc/spear13xx_pcie_gadget.c | 4 ++-- drivers/misc/ti-st/st_kim.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index 736c7714f56..fa017cf64bd 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -919,7 +919,7 @@ static bool dma_filter(struct dma_chan *chan, void *data) static int fpga_of_remove(struct platform_device *op) { - struct fpga_dev *priv = dev_get_drvdata(&op->dev); + struct fpga_dev *priv = platform_get_drvdata(op); struct device *this_device = priv->miscdev.this_device; sysfs_remove_group(&this_device->kobj, &fpga_attr_group); @@ -969,7 +969,7 @@ static int fpga_of_probe(struct platform_device *op) kref_init(&priv->ref); - dev_set_drvdata(&op->dev, priv); + platform_set_drvdata(op, priv); priv->dev = &op->dev; mutex_init(&priv->lock); init_completion(&priv->completion); diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 7508cafff10..a2128af706b 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -1296,7 +1296,7 @@ static int data_of_probe(struct platform_device *op) goto out_return; } - dev_set_drvdata(&op->dev, priv); + platform_set_drvdata(op, priv); priv->dev = &op->dev; kref_init(&priv->ref); mutex_init(&priv->mutex); @@ -1400,7 +1400,7 @@ out_return: static int data_of_remove(struct platform_device *op) { - struct fpga_device *priv = dev_get_drvdata(&op->dev); + struct fpga_device *priv = platform_get_drvdata(op); struct device *this_device = priv->miscdev.this_device; /* remove all sysfs files, now the device cannot be re-enabled */ diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 7deb25dc86a..84a8dabc18f 100644 --- a/drivers/misc/spear13xx_pcie_gadget.c +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -776,7 +776,7 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) goto err_iounmap_app; } - dev_set_drvdata(&pdev->dev, target); + platform_set_drvdata(pdev, target); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -863,7 +863,7 @@ static int spear_pcie_gadget_remove(struct platform_device *pdev) res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); irq = platform_get_irq(pdev, 0); - target = dev_get_drvdata(&pdev->dev); + target = platform_get_drvdata(pdev); config = &target->config; free_irq(irq, NULL); diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 83269f1d16e..83907c72059 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -680,7 +680,7 @@ void st_kim_ref(struct st_data_s **core_data, int id) *core_data = NULL; return; } - kim_gdata = dev_get_drvdata(&pdev->dev); + kim_gdata = platform_get_drvdata(pdev); *core_data = kim_gdata->core_data; } @@ -735,7 +735,7 @@ static int kim_probe(struct platform_device *pdev) pr_err("no mem to allocate"); return -ENOMEM; } - dev_set_drvdata(&pdev->dev, kim_gdata); + platform_set_drvdata(pdev, kim_gdata); err = st_core_init(&kim_gdata->core_data); if (err != 0) { @@ -810,7 +810,7 @@ static int kim_remove(struct platform_device *pdev) struct ti_st_plat_data *pdata = pdev->dev.platform_data; struct kim_data_s *kim_gdata; - kim_gdata = dev_get_drvdata(&pdev->dev); + kim_gdata = platform_get_drvdata(pdev); /* Free the Bluetooth/FM/GPIO * nShutdown gpio from the system -- cgit v1.2.3-70-g09d2 From 1dd24daef9c5a4b51f4981a9c65a36a32b2ea98a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 23 May 2013 14:52:33 +0200 Subject: char: xilinx_hwicap: Checkpatch.pl cleanup Remove checkpatch warning: WARNING: Use #include instead of Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/char/xilinx_hwicap/xilinx_hwicap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index d31ee23c9f1..96677fc7ea4 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.h +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -37,7 +37,7 @@ #include #include -#include +#include struct hwicap_drvdata { u32 write_buffer_in_use; /* Always in [0,3] */ -- cgit v1.2.3-70-g09d2 From 84524cf43d693746896782628466205ccc193e0d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 23 May 2013 14:52:34 +0200 Subject: char: xilinx_hwicap: Fix typo in comment and extend it s/regsiter/register/ Use origin comment from the first driver version which also explain the usage of XHI_MAX_RETRIES better. Signed-off-by: Michal Simek Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/char/xilinx_hwicap/xilinx_hwicap.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index 96677fc7ea4..38b145eaf24 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.h +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -85,7 +85,13 @@ struct hwicap_driver_config { void (*reset)(struct hwicap_drvdata *drvdata); }; -/* Number of times to poll the done regsiter */ +/* Number of times to poll the done register. This has to be large + * enough to allow an entire configuration to complete. If an entire + * page (4kb) is configured at once, that could take up to 4k cycles + * with a byte-wide icap interface. In most cases, this driver is + * used with a much smaller fifo, but this should be sufficient in the + * worst case. + */ #define XHI_MAX_RETRIES 5000 /************ Constant Definitions *************/ -- cgit v1.2.3-70-g09d2 From f0ac23639c62add367309101d18ae2aa1d4a377c Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Tue, 28 May 2013 13:00:20 -0700 Subject: drivers/misc: at24: convert to use devm_kzalloc Use devm_kzalloc to make cleanup paths simpler Signed-off-by: Nikolay Balandin Reviewed-by: Andy Shevchenko Reviewed-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at24.c | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2baeec56edf..5d4fd69d04c 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -492,10 +492,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) if (client->dev.platform_data) { chip = *(struct at24_platform_data *)client->dev.platform_data; } else { - if (!id->driver_data) { - err = -ENODEV; - goto err_out; - } + if (!id->driver_data) + return -ENODEV; + magic = id->driver_data; chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); magic >>= AT24_SIZE_BYTELEN; @@ -519,8 +518,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) "byte_len looks suspicious (no power of 2)!\n"); if (!chip.page_size) { dev_err(&client->dev, "page_size must not be 0!\n"); - err = -EINVAL; - goto err_out; + return -EINVAL; } if (!is_power_of_2(chip.page_size)) dev_warn(&client->dev, @@ -528,10 +526,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Use I2C operations unless we're stuck with SMBus extensions. */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - if (chip.flags & AT24_FLAG_ADDR16) { - err = -EPFNOSUPPORT; - goto err_out; - } + if (chip.flags & AT24_FLAG_ADDR16) + return -EPFNOSUPPORT; + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; @@ -542,8 +539,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) I2C_FUNC_SMBUS_READ_BYTE_DATA)) { use_smbus = I2C_SMBUS_BYTE_DATA; } else { - err = -EPFNOSUPPORT; - goto err_out; + return -EPFNOSUPPORT; } } @@ -553,12 +549,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); - at24 = kzalloc(sizeof(struct at24_data) + + at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); - if (!at24) { - err = -ENOMEM; - goto err_out; - } + if (!at24) + return -ENOMEM; mutex_init(&at24->lock); at24->use_smbus = use_smbus; @@ -596,11 +590,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->write_max = write_max; /* buffer (data + address at the beginning) */ - at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL); - if (!at24->writebuf) { - err = -ENOMEM; - goto err_struct; - } + at24->writebuf = devm_kzalloc(&client->dev, + write_max + 2, GFP_KERNEL); + if (!at24->writebuf) + return -ENOMEM; } else { dev_warn(&client->dev, "cannot write due to controller restrictions."); @@ -648,11 +641,6 @@ err_clients: if (at24->client[i]) i2c_unregister_device(at24->client[i]); - kfree(at24->writebuf); -err_struct: - kfree(at24); -err_out: - dev_dbg(&client->dev, "probe error %d\n", err); return err; } @@ -667,8 +655,6 @@ static int at24_remove(struct i2c_client *client) for (i = 1; i < at24->num_addresses; i++) i2c_unregister_device(at24->client[i]); - kfree(at24->writebuf); - kfree(at24); return 0; } -- cgit v1.2.3-70-g09d2 From 01fe7b43e7c83fe8fbf0b30b47f4ccd2f2058682 Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Tue, 28 May 2013 13:01:21 -0700 Subject: drivers/misc: at25: convert to use devm_kzalloc Use devm_kzalloc to make cleanup paths simpler Signed-off-by: Nikolay Balandin Reviewed-by: Andy Shevchenko Reviewed-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index ad8fd8e6493..840b3594a5a 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -371,11 +371,10 @@ static int at25_probe(struct spi_device *spi) if (np) { err = at25_np_to_chip(&spi->dev, np, &chip); if (err) - goto fail; + return err; } else { dev_err(&spi->dev, "Error: no chip description\n"); - err = -ENODEV; - goto fail; + return -ENODEV; } } else chip = *(struct spi_eeprom *)spi->dev.platform_data; @@ -389,8 +388,7 @@ static int at25_probe(struct spi_device *spi) addrlen = 3; else { dev_dbg(&spi->dev, "unsupported address type\n"); - err = -EINVAL; - goto fail; + return -EINVAL; } /* Ping the chip ... the status register is pretty portable, @@ -400,14 +398,12 @@ static int at25_probe(struct spi_device *spi) sr = spi_w8r8(spi, AT25_RDSR); if (sr < 0 || sr & AT25_SR_nRDY) { dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); - err = -ENXIO; - goto fail; + return -ENXIO; } - if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) { - err = -ENOMEM; - goto fail; - } + at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL); + if (!at25) + return -ENOMEM; mutex_init(&at25->lock); at25->chip = chip; @@ -439,7 +435,7 @@ static int at25_probe(struct spi_device *spi) err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin); if (err) - goto fail; + return err; if (chip.setup) chip.setup(&at25->mem, chip.context); @@ -453,10 +449,6 @@ static int at25_probe(struct spi_device *spi) (chip.flags & EE_READONLY) ? " (readonly)" : "", at25->chip.page_size); return 0; -fail: - dev_dbg(&spi->dev, "probe err %d\n", err); - kfree(at25); - return err; } static int at25_remove(struct spi_device *spi) @@ -465,7 +457,6 @@ static int at25_remove(struct spi_device *spi) at25 = spi_get_drvdata(spi); sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin); - kfree(at25); return 0; } -- cgit v1.2.3-70-g09d2 From 6c7dd64abdd0d07bab3c1cc4327dc328cb70005a Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:21:01 -0700 Subject: misc/ep93xx_pwm: use managed device resources Use managed device resources to clean up the probe/remove. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 73 +++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 9ba93f0fe1d..36370b429bc 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -273,50 +273,33 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) { struct ep93xx_pwm *pwm; struct resource *res; - int err; + int ret; - err = ep93xx_pwm_acquire_gpio(pdev); - if (err) - return err; + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; - pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL); - if (!pwm) { - err = -ENOMEM; - goto fail_no_mem; - } + pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - err = -ENXIO; - goto fail_no_mem_resource; - } - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - err = -EBUSY; - goto fail_no_mem_resource; - } - - pwm->mmio_base = ioremap(res->start, resource_size(res)); - if (pwm->mmio_base == NULL) { - err = -ENXIO; - goto fail_no_ioremap; - } - - err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); - if (err) - goto fail_no_sysfs; - - pwm->clk = clk_get(&pdev->dev, "pwm_clk"); - if (IS_ERR(pwm->clk)) { - err = PTR_ERR(pwm->clk); - goto fail_no_clk; + pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pwm->mmio_base)) + return PTR_ERR(pwm->mmio_base); + + ret = ep93xx_pwm_acquire_gpio(pdev); + if (ret) + return ret; + + ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); + if (ret) { + ep93xx_pwm_release_gpio(pdev); + return ret; } pwm->duty_percent = 50; - platform_set_drvdata(pdev, pwm); - /* disable pwm at startup. Avoids zero value. */ ep93xx_pwm_disable(pwm); ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT); @@ -324,33 +307,17 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) clk_enable(pwm->clk); + platform_set_drvdata(pdev, pwm); return 0; - -fail_no_clk: - sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); -fail_no_sysfs: - iounmap(pwm->mmio_base); -fail_no_ioremap: - release_mem_region(res->start, resource_size(res)); -fail_no_mem_resource: - kfree(pwm); -fail_no_mem: - ep93xx_pwm_release_gpio(pdev); - return err; } static int __exit ep93xx_pwm_remove(struct platform_device *pdev) { struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ep93xx_pwm_disable(pwm); clk_disable(pwm->clk); - clk_put(pwm->clk); sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); - iounmap(pwm->mmio_base); - release_mem_region(res->start, resource_size(res)); - kfree(pwm); ep93xx_pwm_release_gpio(pdev); return 0; -- cgit v1.2.3-70-g09d2 From 1f7190fd1ef23c3c0423cf4471572b6ec2b57c8b Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:21:50 -0700 Subject: misc/ep93xx_pwm: use {read,write}* instead of __raw_* versions for io The mmio_base is an ioremap'ed memory resource. The normal memory io functions should be used not the __raw_* versions. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 36370b429bc..af01fb9fcd7 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -42,13 +42,13 @@ struct ep93xx_pwm { static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm, unsigned int val, unsigned int off) { - __raw_writel(val, pwm->mmio_base + off); + writel(val, pwm->mmio_base + off); } static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm, unsigned int off) { - return __raw_readl(pwm->mmio_base + off); + return readl(pwm->mmio_base + off); } static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) -- cgit v1.2.3-70-g09d2 From a39ca2740c58ddef91efc5ea6889ecee1c314c42 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:22:30 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_{write,read}l() inline functions These are simple wrappers around writel() and readl(). Remove them. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index af01fb9fcd7..06f0c63e8b4 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -39,61 +39,49 @@ struct ep93xx_pwm { u32 duty_percent; }; -static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm, - unsigned int val, unsigned int off) -{ - writel(val, pwm->mmio_base + off); -} - -static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm, - unsigned int off) -{ - return readl(pwm->mmio_base + off); -} - static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) { - ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT); + writel(value, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) { - return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT); + return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value) { - ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE); + writel(value, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); } static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) { - ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE); + writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); } static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) { - ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); } static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) { - return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1; + return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; } static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm) { - ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT); + writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); } static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) { - ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); } static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) { - return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1; + return readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; } /* -- cgit v1.2.3-70-g09d2 From 53e2e380891facf2b0fdb43482b8febb6e17f7f6 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:23:00 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_write_tc() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 06f0c63e8b4..ab5a4813550 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -39,11 +39,6 @@ struct ep93xx_pwm { u32 duty_percent; }; -static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) -{ - writel(value, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); -} - static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) { return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); @@ -157,11 +152,11 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, /* If pwm is running, order is important */ if (val > term) { - ep93xx_pwm_write_tc(pwm, val); + writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); ep93xx_pwm_write_dc(pwm, duty); } else { ep93xx_pwm_write_dc(pwm, duty); - ep93xx_pwm_write_tc(pwm, val); + writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } if (!ep93xx_pwm_is_enabled(pwm)) @@ -290,7 +285,7 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) /* disable pwm at startup. Avoids zero value. */ ep93xx_pwm_disable(pwm); - ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT); + writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2); clk_enable(pwm->clk); -- cgit v1.2.3-70-g09d2 From aa919769c625e2b2c4a70c6bd62a1a83f9f64aa5 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:23:38 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_write_dc() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index ab5a4813550..7a133042e92 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -44,11 +44,6 @@ static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } -static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value) -{ - writel(value, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); -} - static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) { writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); @@ -153,9 +148,9 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, /* If pwm is running, order is important */ if (val > term) { writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - ep93xx_pwm_write_dc(pwm, duty); + writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); } else { - ep93xx_pwm_write_dc(pwm, duty); + writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } @@ -191,7 +186,9 @@ static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, if (val > 0 && val < 100) { u32 term = ep93xx_pwm_read_tc(pwm); - ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1); + u32 duty = ((term + 1) * val / 100) - 1; + + writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); pwm->duty_percent = val; return count; } @@ -286,7 +283,7 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) /* disable pwm at startup. Avoids zero value. */ ep93xx_pwm_disable(pwm); writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2); + writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); clk_enable(pwm->clk); -- cgit v1.2.3-70-g09d2 From ac91b96f3070a2c3b562464604847887cc44b342 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:24:16 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_enable() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 7a133042e92..cf42347182b 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -44,11 +44,6 @@ static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } -static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) -{ - writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); -} - static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) { writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); @@ -155,7 +150,7 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, } if (!ep93xx_pwm_is_enabled(pwm)) - ep93xx_pwm_enable(pwm); + writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); } else { return -EINVAL; } -- cgit v1.2.3-70-g09d2 From 02846b97d7464b92441df2e9d2f59fd5591bbd81 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:24:51 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_disable() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index cf42347182b..320d911edcb 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -44,11 +44,6 @@ static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } -static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) -{ - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); -} - static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) { return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; @@ -127,7 +122,7 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, return -EINVAL; if (val == 0) { - ep93xx_pwm_disable(pwm); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); } else if (val <= (clk_get_rate(pwm->clk) / 2)) { u32 term, duty; @@ -276,7 +271,7 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) pwm->duty_percent = 50; /* disable pwm at startup. Avoids zero value. */ - ep93xx_pwm_disable(pwm); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); @@ -290,7 +285,7 @@ static int __exit ep93xx_pwm_remove(struct platform_device *pdev) { struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - ep93xx_pwm_disable(pwm); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); clk_disable(pwm->clk); sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); ep93xx_pwm_release_gpio(pdev); -- cgit v1.2.3-70-g09d2 From 47a36ee95a5fe31358e97a055988fe7a00163038 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:25:23 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_invert() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 320d911edcb..5193faf594b 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -49,11 +49,6 @@ static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; } -static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm) -{ - writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); -} - static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) { writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); @@ -210,7 +205,7 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, if (val == 0) ep93xx_pwm_normal(pwm); else if (val == 1) - ep93xx_pwm_invert(pwm); + writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); else return -EINVAL; -- cgit v1.2.3-70-g09d2 From d98d7903d14781ab6a8f85daa393eddf13b7f918 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:26:11 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_normal() inline function This is a simple wrapper around writel(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 5193faf594b..cdc2ffc08ab 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -49,11 +49,6 @@ static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; } -static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) -{ - writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); -} - static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) { return readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; @@ -203,7 +198,7 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, return -EINVAL; if (val == 0) - ep93xx_pwm_normal(pwm); + writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); else if (val == 1) writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); else -- cgit v1.2.3-70-g09d2 From 7501ba361f4b22004deec31a8227ca0240630193 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:26:41 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_read_tc() inline function This is a simple wrapper around readl(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index cdc2ffc08ab..6f483753b43 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -39,11 +39,6 @@ struct ep93xx_pwm { u32 duty_percent; }; -static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) -{ - return readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); -} - static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) { return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; @@ -91,7 +86,7 @@ static ssize_t ep93xx_pwm_get_freq(struct device *dev, if (ep93xx_pwm_is_enabled(pwm)) { unsigned long rate = clk_get_rate(pwm->clk); - u16 term = ep93xx_pwm_read_tc(pwm); + u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); return sprintf(buf, "%ld\n", rate / (term + 1)); } else { @@ -122,7 +117,7 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, if (val < 1) val = 1; - term = ep93xx_pwm_read_tc(pwm); + term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); duty = ((val + 1) * pwm->duty_percent / 100) - 1; /* If pwm is running, order is important */ @@ -165,7 +160,7 @@ static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, return -EINVAL; if (val > 0 && val < 100) { - u32 term = ep93xx_pwm_read_tc(pwm); + u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); u32 duty = ((term + 1) * val / 100) - 1; writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); -- cgit v1.2.3-70-g09d2 From 5b2cd8f9b4a098d5aab91539ab5d589c6b676b1e Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:27:08 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_is_enabled() inline function This is a simple wrapper around readl(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 6f483753b43..3dd20069971 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -39,11 +39,6 @@ struct ep93xx_pwm { u32 duty_percent; }; -static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) -{ - return readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1; -} - static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) { return readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; @@ -84,7 +79,7 @@ static ssize_t ep93xx_pwm_get_freq(struct device *dev, struct platform_device *pdev = to_platform_device(dev); struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - if (ep93xx_pwm_is_enabled(pwm)) { + if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) { unsigned long rate = clk_get_rate(pwm->clk); u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); @@ -129,7 +124,7 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); } - if (!ep93xx_pwm_is_enabled(pwm)) + if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); } else { return -EINVAL; -- cgit v1.2.3-70-g09d2 From ddfd6894db8d1c1a5b5e532283aee1f3afd880b9 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:27:38 -0700 Subject: misc/ep93xx_pwm: remove ep93xx_pwm_is_inverted() inline function This is a simple wrapper around readl(), remove it. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 3dd20069971..dd570d1d224 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -39,11 +39,6 @@ struct ep93xx_pwm { u32 duty_percent; }; -static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) -{ - return readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; -} - /* * /sys/devices/platform/ep93xx-pwm.N * /min_freq read-only minimum pwm output frequency @@ -171,8 +166,9 @@ static ssize_t ep93xx_pwm_get_invert(struct device *dev, { struct platform_device *pdev = to_platform_device(dev); struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; - return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm)); + return sprintf(buf, "%d\n", inverted); } static ssize_t ep93xx_pwm_set_invert(struct device *dev, -- cgit v1.2.3-70-g09d2 From 6e1cf66ee3105b41ec93a3848e848ae682add3ff Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:28:14 -0700 Subject: misc/ep93xx_pwm: use module_platform_driver() Add the (*probe) function to the platform_driver and use the module_platform_driver() macro to initialize the module. Remove the unnecessary __init and __exit tags. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index dd570d1d224..b5ad0c6a5af 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -215,7 +215,7 @@ static const struct attribute_group ep93xx_pwm_sysfs_files = { .attrs = ep93xx_pwm_attrs, }; -static int __init ep93xx_pwm_probe(struct platform_device *pdev) +static int ep93xx_pwm_probe(struct platform_device *pdev) { struct ep93xx_pwm *pwm; struct resource *res; @@ -257,7 +257,7 @@ static int __init ep93xx_pwm_probe(struct platform_device *pdev) return 0; } -static int __exit ep93xx_pwm_remove(struct platform_device *pdev) +static int ep93xx_pwm_remove(struct platform_device *pdev) { struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); @@ -274,10 +274,10 @@ static struct platform_driver ep93xx_pwm_driver = { .name = "ep93xx-pwm", .owner = THIS_MODULE, }, - .remove = __exit_p(ep93xx_pwm_remove), + .probe = ep93xx_pwm_probe, + .remove = ep93xx_pwm_remove, }; - -module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe); +module_platform_driver(ep93xx_pwm_driver); MODULE_AUTHOR("Matthieu Crapet , " "H Hartley Sweeten "); -- cgit v1.2.3-70-g09d2 From a4b05d1262c1f79ff07e9baceccbf3398846b8c0 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:28:44 -0700 Subject: misc/ep93xx_pwm: use kstrtol instead of strict_strtol strict_strtol is deprecated in favor of kstrtol. Signed-off-by: H Hartley Sweeten Acked-by: Arnd Bergmann Reviewed-by: Ryan Mallon Cc: Matthieu Crapet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index b5ad0c6a5af..cdb67a9c195 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -92,7 +92,7 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev, long val; int err; - err = strict_strtol(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return -EINVAL; @@ -145,7 +145,7 @@ static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, long val; int err; - err = strict_strtol(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return -EINVAL; @@ -179,7 +179,7 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, long val; int err; - err = strict_strtol(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return -EINVAL; -- cgit v1.2.3-70-g09d2 From 8d7bda51888d14c07cbebacc5a10be776477bb63 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sun, 26 May 2013 20:06:50 +0200 Subject: w1: add family based automatic module loading This patch allows the 1-wire bus to autoload the corresponding module for each slave being attached. This works similar to bluetooth protocols. Signed-off-by: Alexander Stein Signed-off-by: Greg Kroah-Hartman --- Documentation/w1/w1.generic | 4 ++-- drivers/w1/slaves/w1_ds2408.c | 1 + drivers/w1/slaves/w1_ds2413.c | 1 + drivers/w1/slaves/w1_ds2423.c | 1 + drivers/w1/slaves/w1_ds2431.c | 1 + drivers/w1/slaves/w1_ds2433.c | 1 + drivers/w1/slaves/w1_ds2760.c | 1 + drivers/w1/slaves/w1_ds2780.c | 1 + drivers/w1/slaves/w1_ds2781.c | 1 + drivers/w1/slaves/w1_ds28e04.c | 1 + drivers/w1/slaves/w1_smem.c | 2 ++ drivers/w1/slaves/w1_therm.c | 5 +++++ drivers/w1/w1.c | 2 ++ 13 files changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/w1/w1.generic b/Documentation/w1/w1.generic index 212f4ac31c0..a31c5a24297 100644 --- a/Documentation/w1/w1.generic +++ b/Documentation/w1/w1.generic @@ -25,8 +25,8 @@ When a w1 master driver registers with the w1 subsystem, the following occurs: - sysfs entries for that w1 master are created - the w1 bus is periodically searched for new slave devices -When a device is found on the bus, w1 core checks if driver for its family is -loaded. If so, the family driver is attached to the slave. +When a device is found on the bus, w1 core tries to load the driver for its family +and check if it is loaded. If so, the family driver is attached to the slave. If there is no driver for the family, default one is assigned, which allows to perform almost any kind of operations. Each logical operation is a transaction in nature, which can contain several (two or one) low-level operations. diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index e45eca1044b..91cc2cdf02c 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -22,6 +22,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jean-Francois Dagenais "); MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408)); #define W1_F29_RETRIES 3 diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index 829786252c6..85937773a96 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -23,6 +23,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mariusz Bialonczyk "); MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413)); #define W1_F3A_RETRIES 3 #define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c index 40a10b5ed12..7f86aec7408 100644 --- a/drivers/w1/slaves/w1_ds2423.c +++ b/drivers/w1/slaves/w1_ds2423.c @@ -164,3 +164,4 @@ module_exit(w1_f1d_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mika Laitio "); MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram"); +MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423)); diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 984b30331a4..cef8605e43e 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -310,3 +310,4 @@ module_exit(w1_f2d_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bernhard Weirich "); MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431)); diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 85f2cdb27fa..10cc1b6176e 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Gardner "); MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); #define W1_EEPROM_SIZE 512 #define W1_PAGE_COUNT 16 diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index e86a69dc411..93719d25d84 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -203,3 +203,4 @@ module_exit(w1_ds2760_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Szabolcs Gyurko "); MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760)); diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index 98ed9c49cf5..0cd7a27b5d6 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -188,3 +188,4 @@ module_exit(w1_ds2780_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Clifton Barnes "); MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780)); diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index 5140d7be67a..1aba8e41ad4 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -186,3 +186,4 @@ module_exit(w1_ds2781_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Renata Sayakhova "); MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781)); diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index 98117db595b..cd30a6d95ea 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -27,6 +27,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Markus Franke , "); MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04)); /* Allow the strong pullup to be disabled, but default to enabled. * If it was disabled a parasite powered device might not get the required diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c index 84655625c87..ed4c87506de 100644 --- a/drivers/w1/slaves/w1_smem.c +++ b/drivers/w1/slaves/w1_smem.c @@ -34,6 +34,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family."); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01)); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81)); static struct w1_family w1_smem_family_01 = { .fid = W1_FAMILY_SMEM_01, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index c1a702f8c80..8978360bd38 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -36,6 +36,11 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00)); /* Allow the strong pullup to be disabled, but default to enabled. * If it was disabled a parasite powered device might not get the require diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 7ce277d2bb6..0459df843c5 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -680,6 +680,8 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) atomic_set(&sl->refcnt, 0); init_completion(&sl->released); + request_module("w1-family-0x%0x", rn->family); + spin_lock(&w1_flock); f = w1_family_registered(rn->family); if (!f) { -- cgit v1.2.3-70-g09d2 From e68d2971d26577b932a16333ce165af98a96e068 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 23 May 2013 12:02:32 -0700 Subject: Drivers: hv: vmbus: Implement multi-channel support Starting with Win8, the host supports multiple sub-channels for a given device. As in the past, the initial channel offer specifies the device and is associated with both the type and the instance GUIDs. For performance critical devices, the host may support multiple sub-channels. The sub-channels share the same type and instance GUID as the primary channel. The number of sub-channels offerrred to the guest depends on the number of virtual CPUs assigned to the guest. The guest can request the creation of these sub-channels and once created and opened, the guest can distribute the traffic across all the channels (the primary and the sub-channels). A request sent on a sub-channel will have the response delivered on the same sub-channel. At channel (sub-channel) creation we bind the channel interrupt to a CPU and with this sub-channel support we will be able to spread the interrupt load of a given device across all available CPUs. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 50 +++++++++++++++++-- drivers/hv/channel_mgmt.c | 119 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/hv/connection.c | 14 ++++++ include/linux/hyperv.h | 62 +++++++++++++++++++++++- 4 files changed, 236 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 0b122f8c700..6de6c98ce6e 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -116,6 +116,15 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, unsigned long flags; int ret, t, err = 0; + spin_lock_irqsave(&newchannel->sc_lock, flags); + if (newchannel->state == CHANNEL_OPEN_STATE) { + newchannel->state = CHANNEL_OPENING_STATE; + } else { + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + newchannel->onchannel_callback = onchannelcallback; newchannel->channel_callback_context = context; @@ -216,6 +225,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (err == 0) + newchannel->state = CHANNEL_OPENED_STATE; + kfree(open_info); return err; @@ -500,15 +512,14 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) } EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) +static void vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; int ret; unsigned long flags; + channel->state = CHANNEL_OPEN_STATE; + channel->sc_creation_callback = NULL; /* Stop callback and cancel the timer asap */ spin_lock_irqsave(&channel->inbound_lock, flags); channel->onchannel_callback = NULL; @@ -538,6 +549,37 @@ void vmbus_close(struct vmbus_channel *channel) } + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + struct list_head *cur, *tmp; + struct vmbus_channel *cur_channel; + + if (channel->primary_channel != NULL) { + /* + * We will only close sub-channels when + * the primary is closed. + */ + return; + } + /* + * Close all the sub-channels first and then close the + * primary channel. + */ + list_for_each_safe(cur, tmp, &channel->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + if (cur_channel->state != CHANNEL_OPENED_STATE) + continue; + vmbus_close_internal(cur_channel); + } + /* + * Now close the primary. + */ + vmbus_close_internal(channel); +} EXPORT_SYMBOL_GPL(vmbus_close); /** diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 21ef68934a2..0df75908200 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -115,6 +115,9 @@ static struct vmbus_channel *alloc_channel(void) return NULL; spin_lock_init(&channel->inbound_lock); + spin_lock_init(&channel->sc_lock); + + INIT_LIST_HEAD(&channel->sc_list); channel->controlwq = create_workqueue("hv_vmbus_ctl"); if (!channel->controlwq) { @@ -166,6 +169,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work) struct vmbus_channel, work); unsigned long flags; + struct vmbus_channel *primary_channel; struct vmbus_channel_relid_released msg; vmbus_device_unregister(channel->device_obj); @@ -174,9 +178,16 @@ static void vmbus_process_rescind_offer(struct work_struct *work) msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&channel->listentry); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + if (channel->primary_channel == NULL) { + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&channel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + } else { + primary_channel = channel->primary_channel; + spin_lock_irqsave(&primary_channel->sc_lock, flags); + list_del(&channel->listentry); + spin_unlock_irqrestore(&primary_channel->sc_lock, flags); + } free_channel(channel); } @@ -228,6 +239,24 @@ static void vmbus_process_offer(struct work_struct *work) spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); if (!fnew) { + /* + * Check to see if this is a sub-channel. + */ + if (newchannel->offermsg.offer.sub_channel_index != 0) { + /* + * Process the sub-channel. + */ + newchannel->primary_channel = channel; + spin_lock_irqsave(&channel->sc_lock, flags); + list_add_tail(&newchannel->sc_list, &channel->sc_list); + spin_unlock_irqrestore(&channel->sc_lock, flags); + newchannel->state = CHANNEL_OPEN_STATE; + if (channel->sc_creation_callback != NULL) + channel->sc_creation_callback(newchannel); + + return; + } + free_channel(newchannel); return; } @@ -685,4 +714,86 @@ cleanup: return ret; } -/* eof */ +/* + * Retrieve the (sub) channel on which to send an outgoing request. + * When a primary channel has multiple sub-channels, we choose a + * channel whose VCPU binding is closest to the VCPU on which + * this call is being made. + */ +struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) +{ + struct list_head *cur, *tmp; + int cur_cpu = hv_context.vp_index[smp_processor_id()]; + struct vmbus_channel *cur_channel; + struct vmbus_channel *outgoing_channel = primary; + int cpu_distance, new_cpu_distance; + + if (list_empty(&primary->sc_list)) + return outgoing_channel; + + list_for_each_safe(cur, tmp, &primary->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + if (cur_channel->state != CHANNEL_OPENED_STATE) + continue; + + if (cur_channel->target_vp == cur_cpu) + return cur_channel; + + cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ? + (outgoing_channel->target_vp - cur_cpu) : + (cur_cpu - outgoing_channel->target_vp)); + + new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ? + (cur_channel->target_vp - cur_cpu) : + (cur_cpu - cur_channel->target_vp)); + + if (cpu_distance < new_cpu_distance) + continue; + + outgoing_channel = cur_channel; + } + + return outgoing_channel; +} +EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel); + +static void invoke_sc_cb(struct vmbus_channel *primary_channel) +{ + struct list_head *cur, *tmp; + struct vmbus_channel *cur_channel; + + if (primary_channel->sc_creation_callback == NULL) + return; + + list_for_each_safe(cur, tmp, &primary_channel->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + + primary_channel->sc_creation_callback(cur_channel); + } +} + +void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel, + void (*sc_cr_cb)(struct vmbus_channel *new_sc)) +{ + primary_channel->sc_creation_callback = sc_cr_cb; +} +EXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback); + +bool vmbus_are_subchannels_present(struct vmbus_channel *primary) +{ + bool ret; + + ret = !list_empty(&primary->sc_list); + + if (ret) { + /* + * Invoke the callback on sub-channel creation. + * This will present a uniform interface to the + * clients. + */ + invoke_sc_cb(primary); + } + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_are_subchannels_present); diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 253a74ba245..ec3b8cdf1e0 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -246,12 +246,26 @@ struct vmbus_channel *relid2channel(u32 relid) struct vmbus_channel *channel; struct vmbus_channel *found_channel = NULL; unsigned long flags; + struct list_head *cur, *tmp; + struct vmbus_channel *cur_sc; spin_lock_irqsave(&vmbus_connection.channel_lock, flags); list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (channel->offermsg.child_relid == relid) { found_channel = channel; break; + } else if (!list_empty(&channel->sc_list)) { + /* + * Deal with sub-channels. + */ + list_for_each_safe(cur, tmp, &channel->sc_list) { + cur_sc = list_entry(cur, struct vmbus_channel, + sc_list); + if (cur_sc->offermsg.child_relid == relid) { + found_channel = cur_sc; + break; + } + } } } spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index c2559847d7e..405e05a28e6 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -909,6 +909,7 @@ enum vmbus_channel_state { CHANNEL_OFFER_STATE, CHANNEL_OPENING_STATE, CHANNEL_OPEN_STATE, + CHANNEL_OPENED_STATE, }; struct vmbus_channel_debug_info { @@ -1046,6 +1047,38 @@ struct vmbus_channel { * preserve the earlier behavior. */ u32 target_vp; + /* + * Support for sub-channels. For high performance devices, + * it will be useful to have multiple sub-channels to support + * a scalable communication infrastructure with the host. + * The support for sub-channels is implemented as an extention + * to the current infrastructure. + * The initial offer is considered the primary channel and this + * offer message will indicate if the host supports sub-channels. + * The guest is free to ask for sub-channels to be offerred and can + * open these sub-channels as a normal "primary" channel. However, + * all sub-channels will have the same type and instance guids as the + * primary channel. Requests sent on a given channel will result in a + * response on the same channel. + */ + + /* + * Sub-channel creation callback. This callback will be called in + * process context when a sub-channel offer is received from the host. + * The guest can open the sub-channel in the context of this callback. + */ + void (*sc_creation_callback)(struct vmbus_channel *new_sc); + + spinlock_t sc_lock; + /* + * All Sub-channels of a primary channel are linked here. + */ + struct list_head sc_list; + /* + * The primary channel this sub-channel belongs to. + * This will be NULL for the primary channel. + */ + struct vmbus_channel *primary_channel; }; static inline void set_channel_read_state(struct vmbus_channel *c, bool state) @@ -1057,6 +1090,34 @@ void vmbus_onmessage(void *context); int vmbus_request_offers(void); +/* + * APIs for managing sub-channels. + */ + +void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel, + void (*sc_cr_cb)(struct vmbus_channel *new_sc)); + +/* + * Retrieve the (sub) channel on which to send an outgoing request. + * When a primary channel has multiple sub-channels, we choose a + * channel whose VCPU binding is closest to the VCPU on which + * this call is being made. + */ +struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary); + +/* + * Check if sub-channels have already been offerred. This API will be useful + * when the driver is unloaded after establishing sub-channels. In this case, + * when the driver is re-loaded, the driver would have to check if the + * subchannels have already been established before attempting to request + * the creation of sub-channels. + * This function returns TRUE to indicate that subchannels have already been + * created. + * This function should be invoked after setting the callback function for + * sub-channel creation. + */ +bool vmbus_are_subchannels_present(struct vmbus_channel *primary); + /* The format must be the same as struct vmdata_gpa_direct */ struct vmbus_channel_packet_page_buffer { u16 type; @@ -1327,7 +1388,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \ } - /* * Common header for Hyper-V ICs */ -- cgit v1.2.3-70-g09d2 From c265a0d5152619bddcf1986f17995d2df6781554 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 31 May 2013 20:01:30 +0800 Subject: pcie-gadget-spear: fix error return code in spear_pcie_gadget_probe() Fix to return a negative error code in the clk_get_sys() and clk_enable() error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/spear13xx_pcie_gadget.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 84a8dabc18f..4ba0ea35296 100644 --- a/drivers/misc/spear13xx_pcie_gadget.c +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -814,9 +814,11 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie1", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie1\n", __func__); + status = PTR_ERR(clk); goto err_irq; } - if (clk_enable(clk)) { + status = clk_enable(clk); + if (status) { pr_err("%s:couldn't enable clk for pcie1\n", __func__); goto err_irq; } @@ -828,9 +830,11 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie2", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie2\n", __func__); + status = PTR_ERR(clk); goto err_irq; } - if (clk_enable(clk)) { + status = clk_enable(clk); + if (status) { pr_err("%s:couldn't enable clk for pcie2\n", __func__); goto err_irq; } -- cgit v1.2.3-70-g09d2 From 143e9c76c487957de89808cf2a3c828518ad131b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 May 2013 18:54:58 +0300 Subject: pch_phub: re-use native MAC address parser We have mac_pton() helper to parse MAC addresses. Signed-off-by: Andy Shevchenko Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 931e635aa49..8b4d4fb9187 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -633,17 +633,13 @@ static ssize_t show_pch_mac(struct device *dev, struct device_attribute *attr, static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - u8 mac[6]; + u8 mac[ETH_ALEN]; ssize_t rom_size; struct pch_phub_reg *chip = dev_get_drvdata(dev); - if (count != 18) + if (!mac_pton(buf, mac)) return -EINVAL; - sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", - (u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2], (u32 *)&mac[3], - (u32 *)&mac[4], (u32 *)&mac[5]); - chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size); if (!chip->pch_phub_extrom_base_address) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 4cd5773a2ae6facdde3f563087a4cc50f00d9530 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Jun 2013 19:46:26 +0300 Subject: net: core: move mac_pton() to lib/net_utils.c Since we have at least one user of this function outside of CONFIG_NET scope, we have to provide this function independently. The proposed solution is to move it under lib/net_utils.c with corresponding configuration variable and select wherever it is needed. Signed-off-by: Andy Shevchenko Reported-by: Arnd Bergmann Acked-by: David S. Miller Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 + drivers/net/netconsole.c | 1 + include/linux/if_ether.h | 1 - include/linux/kernel.h | 2 ++ lib/Kconfig | 3 +++ lib/Makefile | 2 ++ lib/net_utils.c | 26 ++++++++++++++++++++++++++ net/Kconfig | 1 + net/core/netpoll.c | 1 + net/core/utils.c | 22 ---------------------- 10 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 lib/net_utils.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c002d8660e3..80889d5f95f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -480,6 +480,7 @@ config BMP085_SPI config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" + select GENERIC_NET_UTILS depends on PCI help This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 59ac143dec2..4f777ed9b08 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 12b4d55a02a..d5569734f67 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -30,7 +30,6 @@ static inline struct ethhdr *eth_hdr(const struct sk_buff *skb) int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); -int mac_pton(const char *s, u8 *mac); extern ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len); #endif /* _LINUX_IF_ETHER_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e9ef6d6b51d..3afb969441d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -450,6 +450,8 @@ static inline char * __deprecated pack_hex_byte(char *buf, u8 byte) extern int hex_to_bin(char ch); extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); +int mac_pton(const char *s, u8 *mac); + /* * General tracing related utility functions - trace_printk(), * tracing_on/tracing_off and tracing_start()/tracing_stop diff --git a/lib/Kconfig b/lib/Kconfig index fe01d418b09..d246a3bbd6e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -22,6 +22,9 @@ config GENERIC_STRNCPY_FROM_USER config GENERIC_STRNLEN_USER bool +config GENERIC_NET_UTILS + bool + config GENERIC_FIND_FIRST_BIT bool diff --git a/lib/Makefile b/lib/Makefile index c55a037a354..22f0f4e8a9e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -137,6 +137,8 @@ obj-$(CONFIG_DDR) += jedec_ddr_data.o obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o +obj-$(CONFIG_GENERIC_NET_UTILS) += net_utils.o + obj-$(CONFIG_STMP_DEVICE) += stmp_device.o libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o diff --git a/lib/net_utils.c b/lib/net_utils.c new file mode 100644 index 00000000000..2e3c52c8d05 --- /dev/null +++ b/lib/net_utils.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +int mac_pton(const char *s, u8 *mac) +{ + int i; + + /* XX:XX:XX:XX:XX:XX */ + if (strlen(s) < 3 * ETH_ALEN - 1) + return 0; + + /* Don't dirty result unless string is valid MAC. */ + for (i = 0; i < ETH_ALEN; i++) { + if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1])) + return 0; + if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') + return 0; + } + for (i = 0; i < ETH_ALEN; i++) { + mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); + } + return 1; +} +EXPORT_SYMBOL(mac_pton); diff --git a/net/Kconfig b/net/Kconfig index 2ddc9046868..6dfe1c636a8 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -5,6 +5,7 @@ menuconfig NET bool "Networking support" select NLATTR + select GENERIC_NET_UTILS ---help--- Unless you really know what you are doing, you should say Y here. The reason is that some programs need kernel networking support even diff --git a/net/core/netpoll.c b/net/core/netpoll.c index cec074be8c4..35a9f0804b6 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/net/core/utils.c b/net/core/utils.c index 3c7f5b51b97..aa88e23fc87 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -338,25 +338,3 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, csum_unfold(*sum))); } EXPORT_SYMBOL(inet_proto_csum_replace16); - -int mac_pton(const char *s, u8 *mac) -{ - int i; - - /* XX:XX:XX:XX:XX:XX */ - if (strlen(s) < 3 * ETH_ALEN - 1) - return 0; - - /* Don't dirty result unless string is valid MAC. */ - for (i = 0; i < ETH_ALEN; i++) { - if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1])) - return 0; - if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') - return 0; - } - for (i = 0; i < ETH_ALEN; i++) { - mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); - } - return 1; -} -EXPORT_SYMBOL(mac_pton); -- cgit v1.2.3-70-g09d2 From f7b41276b6b07f47c5f5212fa244385b0e3aaa30 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 4 Jun 2013 13:15:16 +0900 Subject: misc: replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot.c | 2 +- drivers/misc/apds9802als.c | 5 +-- drivers/misc/apds990x.c | 37 +++++++++++++-------- drivers/misc/bh1770glc.c | 59 +++++++++++++++++++++------------ drivers/misc/bh1780gli.c | 2 +- drivers/misc/carma/carma-fpga-program.c | 10 +++--- drivers/misc/carma/carma-fpga.c | 4 +-- drivers/misc/hmc6352.c | 5 +-- drivers/misc/isl29003.c | 24 +++++++++++--- drivers/misc/isl29020.c | 6 ++-- drivers/misc/lis3lv02d/lis3lv02d.c | 6 ++-- drivers/misc/sgi-gru/gruprocfs.c | 14 +++----- drivers/misc/spear13xx_pcie_gadget.c | 57 +++++++++++++++++++++---------- drivers/misc/ti_dac7512.c | 6 ++-- 14 files changed, 154 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 8f99e8e3f0a..0daadcf1ed7 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -470,7 +470,7 @@ static ssize_t sysfs_set_reg(struct device *dev, !test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask)) return -EPERM; - err = strict_strtoul(buf, 10, &value); + err = kstrtoul(buf, 10, &value); if (err) return err; diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index 5b5fd8416b3..0c6e037153d 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -126,8 +126,9 @@ static ssize_t als_sensing_range_store(struct device *dev, int ret_val; unsigned long val; - if (strict_strtoul(buf, 10, &val)) - return -EINVAL; + ret_val = kstrtoul(buf, 10, &val); + if (ret_val) + return ret_val; if (val < 4096) val = 1; diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 98f9bb26492..868a30a1b41 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -696,9 +696,11 @@ static ssize_t apds990x_lux_calib_store(struct device *dev, { struct apds990x_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; chip->lux_calib = value; @@ -759,8 +761,9 @@ static ssize_t apds990x_rate_store(struct device *dev, unsigned long value; int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); ret = apds990x_set_arate(chip, value); @@ -813,9 +816,11 @@ static ssize_t apds990x_prox_enable_store(struct device *dev, { struct apds990x_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); @@ -892,11 +897,12 @@ static ssize_t apds990x_lux_thresh_below_show(struct device *dev, static ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target, const char *buf) { - int ret = 0; unsigned long thresh; + int ret; - if (strict_strtoul(buf, 0, &thresh)) - return -EINVAL; + ret = kstrtoul(buf, 0, &thresh); + if (ret) + return ret; if (thresh > APDS_RANGE) return -EINVAL; @@ -957,9 +963,11 @@ static ssize_t apds990x_prox_threshold_store(struct device *dev, { struct apds990x_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; if ((value > APDS_RANGE) || (value == 0) || (value < APDS_PROX_HYSTERESIS)) @@ -990,9 +998,12 @@ static ssize_t apds990x_power_state_store(struct device *dev, { struct apds990x_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; + + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; if (value) { pm_runtime_get_sync(dev); mutex_lock(&chip->mutex); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index f4975f7d0d5..99a04686e45 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -651,8 +651,9 @@ static ssize_t bh1770_power_state_store(struct device *dev, unsigned long value; ssize_t ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); if (value) { @@ -726,9 +727,11 @@ static ssize_t bh1770_prox_enable_store(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); /* Assume no proximity. Sensor will tell real state soon */ @@ -824,9 +827,11 @@ static ssize_t bh1770_set_prox_rate_above(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); chip->prox_rate_threshold = bh1770_prox_rate_validate(value); @@ -840,9 +845,11 @@ static ssize_t bh1770_set_prox_rate_below(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); chip->prox_rate = bh1770_prox_rate_validate(value); @@ -865,8 +872,10 @@ static ssize_t bh1770_set_prox_thres(struct device *dev, unsigned long value; int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; + if (value > BH1770_PROX_RANGE) return -EINVAL; @@ -893,9 +902,11 @@ static ssize_t bh1770_prox_persistence_store(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; if (value > BH1770_PROX_MAX_PERSISTENCE) return -EINVAL; @@ -918,9 +929,11 @@ static ssize_t bh1770_prox_abs_thres_store(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; if (value > BH1770_PROX_RANGE) return -EINVAL; @@ -963,9 +976,11 @@ static ssize_t bh1770_lux_calib_store(struct device *dev, unsigned long value; u32 old_calib; u32 new_corr; + int ret; - if (strict_strtoul(buf, 0, &value)) - return -EINVAL; + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; mutex_lock(&chip->mutex); old_calib = chip->lux_calib; @@ -1012,8 +1027,9 @@ static ssize_t bh1770_set_lux_rate(struct device *dev, unsigned long rate_hz; int ret, i; - if (strict_strtoul(buf, 0, &rate_hz)) - return -EINVAL; + ret = kstrtoul(buf, 0, &rate_hz); + if (ret) + return ret; for (i = 0; i < ARRAY_SIZE(lux_rates_hz) - 1; i++) if (rate_hz >= lux_rates_hz[i]) @@ -1047,11 +1063,12 @@ static ssize_t bh1770_get_lux_thresh_below(struct device *dev, static ssize_t bh1770_set_lux_thresh(struct bh1770_chip *chip, u16 *target, const char *buf) { - int ret = 0; unsigned long thresh; + int ret; - if (strict_strtoul(buf, 0, &thresh)) - return -EINVAL; + ret = kstrtoul(buf, 0, &thresh); + if (ret) + return ret; if (thresh > BH1770_LUX_RANGE) return -EINVAL; diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index 818f3a0e62b..057580e026c 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c @@ -107,7 +107,7 @@ static ssize_t bh1780_store_power_state(struct device *dev, unsigned long val; int error; - error = strict_strtoul(buf, 0, &val); + error = kstrtoul(buf, 0, &val); if (error) return error; diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index fa017cf64bd..c6bd7e84de2 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -830,8 +830,9 @@ static ssize_t penable_store(struct device *dev, struct device_attribute *attr, unsigned long val; int ret; - if (strict_strtoul(buf, 0, &val)) - return -EINVAL; + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; if (val) { ret = fpga_enable_power_supplies(priv); @@ -859,8 +860,9 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, unsigned long val; int ret; - if (strict_strtoul(buf, 0, &val)) - return -EINVAL; + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; /* We can't have an image writer and be programming simultaneously */ if (mutex_lock_interruptible(&priv->lock)) diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index a2128af706b..7b56563f8b7 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -1002,10 +1002,10 @@ static ssize_t data_en_set(struct device *dev, struct device_attribute *attr, unsigned long enable; int ret; - ret = strict_strtoul(buf, 0, &enable); + ret = kstrtoul(buf, 0, &enable); if (ret) { dev_err(priv->dev, "unable to parse enable input\n"); - return -EINVAL; + return ret; } /* protect against concurrent enable/disable */ diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index 423cd40f1c0..170bd3daf33 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -46,8 +46,9 @@ static int compass_store(struct device *dev, const char *buf, size_t count, int ret; unsigned long val; - if (strict_strtoul(buf, 10, &val)) - return -EINVAL; + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; if (val >= strlen(map)) return -EINVAL; mutex_lock(&compass_mutex); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index c5145b3fcce..e3183f26216 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -208,7 +208,11 @@ static ssize_t isl29003_store_range(struct device *dev, unsigned long val; int ret; - if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val > 3) return -EINVAL; ret = isl29003_set_range(client, val); @@ -239,7 +243,11 @@ static ssize_t isl29003_store_resolution(struct device *dev, unsigned long val; int ret; - if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val > 3) return -EINVAL; ret = isl29003_set_resolution(client, val); @@ -267,7 +275,11 @@ static ssize_t isl29003_store_mode(struct device *dev, unsigned long val; int ret; - if ((strict_strtoul(buf, 10, &val) < 0) || (val > 2)) + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val > 2) return -EINVAL; ret = isl29003_set_mode(client, val); @@ -298,7 +310,11 @@ static ssize_t isl29003_store_power_state(struct device *dev, unsigned long val; int ret; - if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val > 1) return -EINVAL; ret = isl29003_set_power_state(client, val); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 0aa08c74646..b7f84dacf82 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -90,8 +90,10 @@ static ssize_t als_sensing_range_store(struct device *dev, int ret_val; unsigned long val; - if (strict_strtoul(buf, 10, &val)) - return -EINVAL; + ret_val = kstrtoul(buf, 10, &val); + if (ret_val) + return ret_val; + if (val < 1 || val > 64000) return -EINVAL; diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 4cd4a3d2a76..036effe9a79 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -831,9 +831,11 @@ static ssize_t lis3lv02d_rate_set(struct device *dev, { struct lis3lv02d *lis3 = dev_get_drvdata(dev); unsigned long rate; + int ret; - if (strict_strtoul(buf, 0, &rate)) - return -EINVAL; + ret = kstrtoul(buf, 0, &rate); + if (ret) + return ret; lis3lv02d_sysfs_poweron(lis3); if (lis3lv02d_set_odr(lis3, rate)) diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index 797d7962cc8..4f763592239 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c @@ -160,15 +160,11 @@ static int options_show(struct seq_file *s, void *p) static ssize_t options_write(struct file *file, const char __user *userbuf, size_t count, loff_t *data) { - char buf[20]; - - if (count >= sizeof(buf)) - return -EINVAL; - if (copy_from_user(buf, userbuf, count)) - return -EFAULT; - buf[count] = '\0'; - if (strict_strtoul(buf, 0, &gru_options)) - return -EINVAL; + int ret; + + ret = kstrtoul_from_user(userbuf, count, 0, &gru_options); + if (ret) + return ret; return count; } diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 4ba0ea35296..2e13614d41e 100644 --- a/drivers/misc/spear13xx_pcie_gadget.c +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -316,8 +316,12 @@ static ssize_t pcie_gadget_store_no_of_msi( struct spear_pcie_gadget_config *config, const char *buf, size_t count) { - if (strict_strtoul(buf, 0, &config->requested_msi)) - return -EINVAL; + int ret; + + ret = kstrtoul(buf, 0, &config->requested_msi); + if (ret) + return ret; + if (config->requested_msi > 32) config->requested_msi = 32; @@ -330,9 +334,11 @@ static ssize_t pcie_gadget_store_inta( { struct pcie_app_reg __iomem *app_reg = config->va_app_base; ulong en; + int ret; - if (strict_strtoul(buf, 0, &en)) - return -EINVAL; + ret = kstrtoul(buf, 0, &en); + if (ret) + return ret; if (en) writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID), @@ -351,9 +357,11 @@ static ssize_t pcie_gadget_store_send_msi( struct pcie_app_reg __iomem *app_reg = config->va_app_base; ulong vector; u32 ven_msi; + int ret; - if (strict_strtoul(buf, 0, &vector)) - return -EINVAL; + ret = kstrtoul(buf, 0, &vector); + if (ret) + return ret; if (!config->configured_msi) return -EINVAL; @@ -395,9 +403,11 @@ static ssize_t pcie_gadget_store_vendor_id( const char *buf, size_t count) { ulong id; + int ret; - if (strict_strtoul(buf, 0, &id)) - return -EINVAL; + ret = kstrtoul(buf, 0, &id); + if (ret) + return ret; spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id); @@ -420,9 +430,11 @@ static ssize_t pcie_gadget_store_device_id( const char *buf, size_t count) { ulong id; + int ret; - if (strict_strtoul(buf, 0, &id)) - return -EINVAL; + ret = kstrtoul(buf, 0, &id); + if (ret) + return ret; spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id); @@ -443,9 +455,12 @@ static ssize_t pcie_gadget_store_bar0_size( ulong size; u32 pos, pos1; u32 no_of_bit = 0; + int ret; + + ret = kstrtoul(buf, 0, &size); + if (ret) + return ret; - if (strict_strtoul(buf, 0, &size)) - return -EINVAL; /* min bar size is 256 */ if (size <= 0x100) size = 0x100; @@ -490,9 +505,11 @@ static ssize_t pcie_gadget_store_bar0_address( { struct pcie_app_reg __iomem *app_reg = config->va_app_base; ulong address; + int ret; - if (strict_strtoul(buf, 0, &address)) - return -EINVAL; + ret = kstrtoul(buf, 0, &address); + if (ret) + return ret; address &= ~(config->bar0_size - 1); if (config->va_bar0_address) @@ -518,9 +535,11 @@ static ssize_t pcie_gadget_store_bar0_rw_offset( const char *buf, size_t count) { ulong offset; + int ret; - if (strict_strtoul(buf, 0, &offset)) - return -EINVAL; + ret = kstrtoul(buf, 0, &offset); + if (ret) + return ret; if (offset % 4) return -EINVAL; @@ -549,9 +568,11 @@ static ssize_t pcie_gadget_store_bar0_data( const char *buf, size_t count) { ulong data; + int ret; - if (strict_strtoul(buf, 0, &data)) - return -EINVAL; + ret = kstrtoul(buf, 0, &data); + if (ret) + return ret; if (!config->va_bar0_address) return -ENOMEM; diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 1d86407189e..9b237221bc4 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -33,9 +33,11 @@ static ssize_t dac7512_store_val(struct device *dev, struct spi_device *spi = to_spi_device(dev); unsigned char tmp[2]; unsigned long val; + int ret; - if (strict_strtoul(buf, 10, &val) < 0) - return -EINVAL; + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; tmp[0] = val >> 8; tmp[1] = val & 0xff; -- cgit v1.2.3-70-g09d2 From 29ddae2a40883640941932f73eb3d15535c3f4a2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 4 Jun 2013 22:44:27 +0800 Subject: pch_phub: fix error return code in pch_phub_probe() Error code had been assigned to retval in some error handling case but never use it, 0 is always returned in those case, this patch fix them to return a negative error code. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 8b4d4fb9187..a5925f7f17f 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -665,8 +665,6 @@ static struct bin_attribute pch_bin_attr = { static int pch_phub_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int retval; - int ret; struct pch_phub_reg *chip; @@ -709,13 +707,13 @@ static int pch_phub_probe(struct pci_dev *pdev, if (id->driver_data == 1) { /* EG20T PCH */ const char *board_name; - retval = sysfs_create_file(&pdev->dev.kobj, - &dev_attr_pch_mac.attr); - if (retval) + ret = sysfs_create_file(&pdev->dev.kobj, + &dev_attr_pch_mac.attr); + if (ret) goto err_sysfs_create; - retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); - if (retval) + ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); + if (ret) goto exit_bin_attr; pch_phub_read_modify_write_reg(chip, @@ -739,8 +737,8 @@ static int pch_phub_probe(struct pci_dev *pdev, chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_EG20T; } else if (id->driver_data == 2) { /* ML7213 IOH */ - retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); - if (retval) + ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); + if (ret) goto err_sysfs_create; /* set the prefech value * Device2(USB OHCI #1/ USB EHCI #1/ USB Device):a @@ -762,12 +760,12 @@ static int pch_phub_probe(struct pci_dev *pdev, PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; } else if (id->driver_data == 4) { /* ML7223 IOH Bus-n*/ - retval = sysfs_create_file(&pdev->dev.kobj, - &dev_attr_pch_mac.attr); - if (retval) + ret = sysfs_create_file(&pdev->dev.kobj, + &dev_attr_pch_mac.attr); + if (ret) goto err_sysfs_create; - retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); - if (retval) + ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); + if (ret) goto exit_bin_attr; /* set the prefech value * Device2(USB OHCI #0,1,2,3/ USB EHCI #0):a @@ -779,13 +777,13 @@ static int pch_phub_probe(struct pci_dev *pdev, PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; } else if (id->driver_data == 5) { /* ML7831 */ - retval = sysfs_create_file(&pdev->dev.kobj, - &dev_attr_pch_mac.attr); - if (retval) + ret = sysfs_create_file(&pdev->dev.kobj, + &dev_attr_pch_mac.attr); + if (ret) goto err_sysfs_create; - retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); - if (retval) + ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); + if (ret) goto exit_bin_attr; /* set the prefech value */ -- cgit v1.2.3-70-g09d2 From 71811f3271cd986e223be44830e5961056561ac3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 5 Jun 2013 17:09:39 +0000 Subject: drivers: char: mem: use IS_ERR_VALUE() in memory_lseek() Use IS_ERR_VALUE() instead of comparing the new offset to a hard-coded value of -MAX_ERRNO (which is also off-by-one due to the use of ~ instead of -). Signed-off-by: Rasmus Villemoes Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1ccbe9482fa..2ca6d7844ad 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -745,7 +745,7 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) offset += file->f_pos; case SEEK_SET: /* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */ - if ((unsigned long long)offset >= ~0xFFFULL) { + if (IS_ERR_VALUE((unsigned long long)offset)) { ret = -EOVERFLOW; break; } -- cgit v1.2.3-70-g09d2 From fd5c25614012ccad75ef505982af4d6c08d0df83 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 6 Jun 2013 12:29:16 +0100 Subject: vme: Free DMA resource allocation structure There is a memory leak in the function vme_dma_free(). The resource structure allocated in vme_dma_request() needs to be free'd in vme_dma_free(). Reported-by: De Roo, Steven Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/vme.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 5e6c7d74e19..f6856b42749 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -959,6 +959,8 @@ int vme_dma_free(struct vme_resource *resource) mutex_unlock(&ctrlr->mtx); + kfree(resource); + return 0; } EXPORT_SYMBOL(vme_dma_free); -- cgit v1.2.3-70-g09d2 From cb3771b049b3b554455094a3b06d8ceb07086e06 Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Thu, 6 Jun 2013 15:55:28 +0200 Subject: uio: uio_pruss: Fix potential NULL pointer dereference In function pruss_probe we free gdev and try to use it on the next line. I have moved the dereference to above the kfree of gdev. Signed-off-by: Emil Goode Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pruss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 6e2ab007fe9..21f7a72301e 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -136,9 +136,9 @@ static int pruss_probe(struct platform_device *dev) gdev->pruss_clk = clk_get(&dev->dev, "pruss"); if (IS_ERR(gdev->pruss_clk)) { dev_err(&dev->dev, "Failed to get clock\n"); + ret = PTR_ERR(gdev->pruss_clk); kfree(gdev->info); kfree(gdev); - ret = PTR_ERR(gdev->pruss_clk); return ret; } else { clk_enable(gdev->pruss_clk); -- cgit v1.2.3-70-g09d2 From e39506b466edcda2a7e9d0174d7987ae654137b7 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:14 +0200 Subject: pcmcia: at91_cf: fix gpio_get_value in at91_cf_get_status Commit 80af9e6d (pcmcia at91_cf: fix raw gpio number usage) forgot to change the parameter in gpio_get_value after adding gpio validation. Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: stable # 3.4+ Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 01463c78184..1b2c6317c77 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -100,9 +100,9 @@ static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp) int vcc = gpio_is_valid(cf->board->vcc_pin); *sp = SS_DETECT | SS_3VCARD; - if (!rdy || gpio_get_value(rdy)) + if (!rdy || gpio_get_value(cf->board->irq_pin)) *sp |= SS_READY; - if (!vcc || gpio_get_value(vcc)) + if (!vcc || gpio_get_value(cf->board->vcc_pin)) *sp |= SS_POWERON; } else *sp = 0; -- cgit v1.2.3-70-g09d2 From 40ca0209bae06eeede0f861e988b4bb38ae9cf98 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:15 +0200 Subject: pcmcia: at91_cf: convert to dev_ print functions Convert all pr_* functions to equivalent dev_* functions and drop the driver_name variable. Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 1b2c6317c77..4eec14bcb7b 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -41,8 +41,6 @@ /*--------------------------------------------------------------------------*/ -static const char driver_name[] = "at91_cf"; - struct at91_cf_socket { struct pcmcia_socket socket; @@ -76,7 +74,7 @@ static irqreturn_t at91_cf_irq(int irq, void *_cf) /* kick pccard as needed */ if (present != cf->present) { cf->present = present; - pr_debug("%s: card %s\n", driver_name, + dev_dbg(&cf->pdev->dev, "card %s\n", present ? "present" : "gone"); pcmcia_parse_events(&cf->socket, SS_DETECT); } @@ -134,8 +132,8 @@ at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) /* toggle reset if needed */ gpio_set_value(cf->board->rst_pin, s->flags & SS_RESET); - pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", - driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); + dev_dbg(&cf->pdev->dev, "Vcc %d, io_irq %d, flags %04x csc %04x\n", + s->Vcc, s->io_irq, s->flags, s->csc_mask); return 0; } @@ -171,10 +169,10 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) */ if (!(io->flags & (MAP_16BIT | MAP_AUTOSZ))) { csr |= AT91_SMC_DBW_8; - pr_debug("%s: 8bit i/o bus\n", driver_name); + dev_dbg(&cf->pdev->dev, "8bit i/o bus\n"); } else { csr |= AT91_SMC_DBW_16; - pr_debug("%s: 16bit i/o bus\n", driver_name); + dev_dbg(&cf->pdev->dev, "16bit i/o bus\n"); } at91_ramc_write(0, AT91_SMC_CSR(cf->board->chipselect), csr); @@ -242,7 +240,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) status = gpio_request(board->det_pin, "cf_det"); if (status < 0) goto fail0; - status = request_irq(gpio_to_irq(board->det_pin), at91_cf_irq, 0, driver_name, cf); + status = request_irq(gpio_to_irq(board->det_pin), at91_cf_irq, 0, "at91_cf detect", cf); if (status < 0) goto fail00; device_init_wakeup(&pdev->dev, 1); @@ -268,7 +266,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) if (status < 0) goto fail0c; status = request_irq(gpio_to_irq(board->irq_pin), at91_cf_irq, - IRQF_SHARED, driver_name, cf); + IRQF_SHARED, "at91_cf", cf); if (status < 0) goto fail0d; cf->socket.pci_irq = gpio_to_irq(board->irq_pin); @@ -284,12 +282,12 @@ static int __init at91_cf_probe(struct platform_device *pdev) } /* reserve chip-select regions */ - if (!request_mem_region(io->start, resource_size(io), driver_name)) { + if (!request_mem_region(io->start, resource_size(io), "at91_cf")) { status = -ENXIO; goto fail1; } - pr_info("%s: irqs det #%d, io #%d\n", driver_name, + dev_info(&pdev->dev, "irqs det #%d, io #%d\n", gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin)); cf->socket.owner = THIS_MODULE; @@ -391,7 +389,7 @@ static int at91_cf_resume(struct platform_device *pdev) static struct platform_driver at91_cf_driver = { .driver = { - .name = (char *) driver_name, + .name = "at91_cf", .owner = THIS_MODULE, }, .remove = __exit_p(at91_cf_remove), -- cgit v1.2.3-70-g09d2 From 54fe15918bb7ad8aac994bf69f6d2f8c23fbdc34 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:16 +0200 Subject: pcmcia: at91_cf: use devm_ functions for allocations Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 77 +++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 4eec14bcb7b..43bc342e6c0 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -227,7 +227,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) if (!io) return -ENODEV; - cf = kzalloc(sizeof *cf, GFP_KERNEL); + cf = devm_kzalloc(&pdev->dev, sizeof(*cf), GFP_KERNEL); if (!cf) return -ENOMEM; @@ -237,22 +237,25 @@ static int __init at91_cf_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cf); /* must be a GPIO; ergo must trigger on both edges */ - status = gpio_request(board->det_pin, "cf_det"); + status = devm_gpio_request(&pdev->dev, board->det_pin, "cf_det"); if (status < 0) - goto fail0; - status = request_irq(gpio_to_irq(board->det_pin), at91_cf_irq, 0, "at91_cf detect", cf); + return status; + + status = devm_request_irq(&pdev->dev, gpio_to_irq(board->det_pin), + at91_cf_irq, 0, "at91_cf detect", cf); if (status < 0) - goto fail00; + return status; + device_init_wakeup(&pdev->dev, 1); - status = gpio_request(board->rst_pin, "cf_rst"); + status = devm_gpio_request(&pdev->dev, board->rst_pin, "cf_rst"); if (status < 0) goto fail0a; if (gpio_is_valid(board->vcc_pin)) { - status = gpio_request(board->vcc_pin, "cf_vcc"); + status = devm_gpio_request(&pdev->dev, board->vcc_pin, "cf_vcc"); if (status < 0) - goto fail0b; + goto fail0a; } /* @@ -262,29 +265,30 @@ static int __init at91_cf_probe(struct platform_device *pdev) * (Note: DK board doesn't wire the IRQ pin...) */ if (gpio_is_valid(board->irq_pin)) { - status = gpio_request(board->irq_pin, "cf_irq"); + status = devm_gpio_request(&pdev->dev, board->irq_pin, "cf_irq"); if (status < 0) - goto fail0c; - status = request_irq(gpio_to_irq(board->irq_pin), at91_cf_irq, - IRQF_SHARED, "at91_cf", cf); + goto fail0a; + + status = devm_request_irq(&pdev->dev, gpio_to_irq(board->irq_pin), + at91_cf_irq, IRQF_SHARED, "at91_cf", cf); if (status < 0) - goto fail0d; + goto fail0a; cf->socket.pci_irq = gpio_to_irq(board->irq_pin); } else cf->socket.pci_irq = nr_irqs + 1; /* pcmcia layer only remaps "real" memory not iospace */ - cf->socket.io_offset = (unsigned long) - ioremap(cf->phys_baseaddr + CF_IO_PHYS, SZ_2K); + cf->socket.io_offset = (unsigned long) devm_ioremap(&pdev->dev, + cf->phys_baseaddr + CF_IO_PHYS, SZ_2K); if (!cf->socket.io_offset) { status = -ENXIO; - goto fail1; + goto fail0a; } /* reserve chip-select regions */ - if (!request_mem_region(io->start, resource_size(io), "at91_cf")) { + if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) { status = -ENXIO; - goto fail1; + goto fail0a; } dev_info(&pdev->dev, "irqs det #%d, io #%d\n", @@ -301,55 +305,22 @@ static int __init at91_cf_probe(struct platform_device *pdev) status = pcmcia_register_socket(&cf->socket); if (status < 0) - goto fail2; + goto fail0a; return 0; -fail2: - release_mem_region(io->start, resource_size(io)); -fail1: - if (cf->socket.io_offset) - iounmap((void __iomem *) cf->socket.io_offset); - if (gpio_is_valid(board->irq_pin)) { - free_irq(gpio_to_irq(board->irq_pin), cf); -fail0d: - gpio_free(board->irq_pin); - } -fail0c: - if (gpio_is_valid(board->vcc_pin)) - gpio_free(board->vcc_pin); -fail0b: - gpio_free(board->rst_pin); fail0a: device_init_wakeup(&pdev->dev, 0); - free_irq(gpio_to_irq(board->det_pin), cf); -fail00: - gpio_free(board->det_pin); -fail0: - kfree(cf); return status; } static int __exit at91_cf_remove(struct platform_device *pdev) { struct at91_cf_socket *cf = platform_get_drvdata(pdev); - struct at91_cf_data *board = cf->board; - struct resource *io = cf->socket.io[0].res; pcmcia_unregister_socket(&cf->socket); - release_mem_region(io->start, resource_size(io)); - iounmap((void __iomem *) cf->socket.io_offset); - if (gpio_is_valid(board->irq_pin)) { - free_irq(gpio_to_irq(board->irq_pin), cf); - gpio_free(board->irq_pin); - } - if (gpio_is_valid(board->vcc_pin)) - gpio_free(board->vcc_pin); - gpio_free(board->rst_pin); device_init_wakeup(&pdev->dev, 0); - free_irq(gpio_to_irq(board->det_pin), cf); - gpio_free(board->det_pin); - kfree(cf); + return 0; } -- cgit v1.2.3-70-g09d2 From a843168dc9f222c4c46751d96c2b701b6539f261 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:17 +0200 Subject: pcmcia: at91_cf: clean up header includes Use includes from linux/ instead of asm/ and remove a unnecessary mach/ include. Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 43bc342e6c0..bce8a64cd7c 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -18,13 +18,11 @@ #include #include #include +#include +#include #include -#include -#include -#include - #include #include -- cgit v1.2.3-70-g09d2 From ed9084ecfccae55ea45c7f068d1f513b979a0132 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:18 +0200 Subject: pcmcia: at91_cf: add support for DT Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/ata/atmel-at91_cf.txt | 19 +++++++++ drivers/pcmcia/Kconfig | 2 +- drivers/pcmcia/at91_cf.c | 45 +++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/ata/atmel-at91_cf.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/ata/atmel-at91_cf.txt b/Documentation/devicetree/bindings/ata/atmel-at91_cf.txt new file mode 100644 index 00000000000..c1d22b3ae13 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/atmel-at91_cf.txt @@ -0,0 +1,19 @@ +Atmel AT91RM9200 CompactFlash + +Required properties: +- compatible : "atmel,at91rm9200-cf". +- reg : should specify localbus address and size used. +- gpios : specifies the gpio pins to control the CF device. Detect + and reset gpio's are mandatory while irq and vcc gpio's are + optional and may be set to 0 if not present. + +Example: +compact-flash@50000000 { + compatible = "atmel,at91rm9200-cf"; + reg = <0x50000000 0x30000000>; + gpios = <&pioC 13 0 /* irq */ + &pioC 15 0 /* detect */ + 0 /* vcc */ + &pioC 5 0 /* reset */ + >; +}; diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index b90f85bf5f8..80faa56c684 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -288,7 +288,7 @@ config BFIN_CFPCMCIA config AT91_CF tristate "AT91 CompactFlash Controller" - depends on PCMCIA && ARCH_AT91RM9200 + depends on PCMCIA && ARCH_AT91 help Say Y here to support the CompactFlash controller on AT91 chips. Or choose M to compile the driver as a module named "at91_cf". diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index bce8a64cd7c..149b95c957d 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include @@ -211,6 +214,37 @@ static struct pccard_operations at91_cf_ops = { /*--------------------------------------------------------------------------*/ +#if defined(CONFIG_OF) +static const struct of_device_id at91_cf_dt_ids[] = { + { .compatible = "atmel,at91rm9200-cf" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_cf_dt_ids); + +static int at91_cf_dt_init(struct platform_device *pdev) +{ + struct at91_cf_data *board; + + board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); + if (!board) + return -ENOMEM; + + board->irq_pin = of_get_gpio(pdev->dev.of_node, 0); + board->det_pin = of_get_gpio(pdev->dev.of_node, 1); + board->vcc_pin = of_get_gpio(pdev->dev.of_node, 2); + board->rst_pin = of_get_gpio(pdev->dev.of_node, 3); + + pdev->dev.platform_data = board; + + return 0; +} +#else +static int at91_cf_dt_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + static int __init at91_cf_probe(struct platform_device *pdev) { struct at91_cf_socket *cf; @@ -218,7 +252,15 @@ static int __init at91_cf_probe(struct platform_device *pdev) struct resource *io; int status; - if (!board || !gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin)) + if (!board) { + status = at91_cf_dt_init(pdev); + if (status) + return status; + + board = pdev->dev.platform_data; + } + + if (!gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin)) return -ENODEV; io = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -360,6 +402,7 @@ static struct platform_driver at91_cf_driver = { .driver = { .name = "at91_cf", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_cf_dt_ids), }, .remove = __exit_p(at91_cf_remove), .suspend = at91_cf_suspend, -- cgit v1.2.3-70-g09d2 From 43f50752256d8374602559832eaddd0cbf5aad64 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Thu, 6 Jun 2013 10:24:19 +0200 Subject: pcmcia: at91_cf: use module_platform_driver_probe() Use module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Fabio Porcedda Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 149b95c957d..8ddc57c5bc4 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -411,17 +411,7 @@ static struct platform_driver at91_cf_driver = { /*--------------------------------------------------------------------------*/ -static int __init at91_cf_init(void) -{ - return platform_driver_probe(&at91_cf_driver, at91_cf_probe); -} -module_init(at91_cf_init); - -static void __exit at91_cf_exit(void) -{ - platform_driver_unregister(&at91_cf_driver); -} -module_exit(at91_cf_exit); +module_platform_driver_probe(at91_cf_driver, at91_cf_probe); MODULE_DESCRIPTION("AT91 Compact Flash Driver"); MODULE_AUTHOR("David Brownell"); -- cgit v1.2.3-70-g09d2 From d652f7022b359afd5d34fc9fffd71df118521ead Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 6 Jun 2013 10:24:20 +0200 Subject: pcmcia/trivial: at91_cf: fix checkpatch error fix this checkpatch error: - ERROR: switch and case should be at the same indent Signed-off-by: Laurent Navet Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/at91_cf.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 8ddc57c5bc4..b8f5acf0226 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -119,14 +119,14 @@ at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) /* switch Vcc if needed and possible */ if (gpio_is_valid(cf->board->vcc_pin)) { switch (s->Vcc) { - case 0: - gpio_set_value(cf->board->vcc_pin, 0); - break; - case 33: - gpio_set_value(cf->board->vcc_pin, 1); - break; - default: - return -EINVAL; + case 0: + gpio_set_value(cf->board->vcc_pin, 0); + break; + case 33: + gpio_set_value(cf->board->vcc_pin, 1); + break; + default: + return -EINVAL; } } -- cgit v1.2.3-70-g09d2 From 214da6728acac285cac0e8e9b6c4c13838b8e1b0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 7 Jun 2013 10:16:54 +0800 Subject: pcmcia: pd6729: fix error return code in pd6729_pci_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/pd6729.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index b29d97e170a..a4c16ee5c71 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -644,6 +644,7 @@ static int pd6729_pci_probe(struct pci_dev *dev, if (!pci_resource_start(dev, 0)) { dev_warn(&dev->dev, "refusing to load the driver as the " "io_base is NULL.\n"); + ret = -ENOMEM; goto err_out_disable; } @@ -673,6 +674,7 @@ static int pd6729_pci_probe(struct pci_dev *dev, mask = pd6729_isa_scan(); if (irq_mode == 0 && mask == 0) { dev_warn(&dev->dev, "no ISA interrupt is available.\n"); + ret = -ENODEV; goto err_out_free_res; } -- cgit v1.2.3-70-g09d2 From ea9dd9d655adf16b083b79ad84e5b012543f659f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 22 May 2013 19:31:59 +0900 Subject: extcon: Change permission 'state' sysfs entry (rw -> r) This patch change permission from read/write to only read. The specific process in the user-space couldn't change the state of cable when cable is attached or detached. - /sys/class/extcon/[devine name]/state Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-class.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 60adc04b056..a5a03ae069e 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -185,26 +185,6 @@ static ssize_t cable_state_show(struct device *dev, cable->cable_index)); } -static ssize_t cable_state_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct extcon_cable *cable = container_of(attr, struct extcon_cable, - attr_state); - int ret, state; - - ret = sscanf(buf, "%d", &state); - if (ret == 0) - ret = -EINVAL; - else - ret = extcon_set_cable_state_(cable->edev, cable->cable_index, - state); - - if (ret < 0) - return ret; - return count; -} - /** * extcon_update_state() - Update the cable attach states of the extcon device * only for the masked bits. @@ -665,9 +645,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) sysfs_attr_init(&cable->attr_state.attr); cable->attr_state.attr.name = "state"; - cable->attr_state.attr.mode = 0644; + cable->attr_state.attr.mode = 0444; cable->attr_state.show = cable_state_show; - cable->attr_state.store = cable_state_store; } } -- cgit v1.2.3-70-g09d2 From 9c8a013af49cb82a1a47bede56e8ce5efd93b734 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 4 Jun 2013 01:13:38 +0900 Subject: extcon: add EXPORT_SYMBOL_GPL for exported functions Added EXPORT_SYMBOL_GPL() for extcon_register_interest and extcon_register_notifier in order to avoid undefined reference error when building the consumer modules of extcon as _modules_. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lokesh Vutla Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-class.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index a5a03ae069e..8c69803558f 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -481,6 +481,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, return -ENODEV; } } +EXPORT_SYMBOL_GPL(extcon_register_interest); /** * extcon_unregister_interest() - Unregister the notifier registered by @@ -495,6 +496,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb); } +EXPORT_SYMBOL_GPL(extcon_unregister_interest); /** * extcon_register_notifier() - Register a notifiee to get notified by -- cgit v1.2.3-70-g09d2 From b1f254e35d85535b17af2786d06fe88f15f304f7 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Tue, 28 May 2013 10:50:11 +0900 Subject: extcon: Palmas Extcon Driver This is the driver for the USB comparator built into the palmas chip. It handles the various USB OTG events that can be generated by cable insertion/removal. Signed-off-by: Graeme Gregory Signed-off-by: Moiz Sonasath Signed-off-by: Ruchika Kharwar Signed-off-by: Kishon Vijay Abraham I Signed-off-by: George Cherian [kishon@ti.com: adapted palmas usb driver to use the extcon framework] Signed-off-by: Sebastien Guiriec Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/extcon/extcon-twl.txt | 15 ++ drivers/extcon/Kconfig | 7 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-palmas.c | 246 +++++++++++++++++++++ include/linux/mfd/palmas.h | 26 ++- 5 files changed, 283 insertions(+), 12 deletions(-) create mode 100644 Documentation/devicetree/bindings/extcon/extcon-twl.txt create mode 100644 drivers/extcon/extcon-palmas.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/extcon/extcon-twl.txt b/Documentation/devicetree/bindings/extcon/extcon-twl.txt new file mode 100644 index 00000000000..58f531ab4df --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/extcon-twl.txt @@ -0,0 +1,15 @@ +EXTCON FOR TWL CHIPS + +PALMAS USB COMPARATOR +Required Properties: + - compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb" + - vbus-supply : phandle to the regulator device tree node. + +Optional Properties: + - ti,wakeup : To enable the wakeup comparator in probe + +palmas-usb { + compatible = "ti,twl6035-usb", "ti,palmas-usb"; + vbus-supply = <&smps10_reg>; + ti,wakeup; +}; diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 3297301a42d..63f454e2057 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -53,4 +53,11 @@ config EXTCON_ARIZONA with Wolfson Arizona devices. These are audio CODECs with advanced audio accessory detection support. +config EXTCON_PALMAS + tristate "Palmas USB EXTCON support" + depends on MFD_PALMAS + help + Say Y here to enable support for USB peripheral and USB host + detection by palmas usb. + endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index f98a3c4d46e..540e2c3a443 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o +obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c new file mode 100644 index 00000000000..b752a0ad7b6 --- /dev/null +++ b/drivers/extcon/extcon-palmas.c @@ -0,0 +1,246 @@ +/* + * Palmas USB transceiver driver + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * 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. + * + * Author: Graeme Gregory + * Author: Kishon Vijay Abraham I + * + * Based on twl6030_usb.c + * + * Author: Hema HK + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +static const char *palmas_extcon_cable[] = { + [0] = "USB", + [1] = "USB-HOST", + NULL, +}; + +static const int mutually_exclusive[] = {0x3, 0x0}; + +static void palmas_usb_wakeup(struct palmas *palmas, int enable) +{ + if (enable) + palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, + PALMAS_USB_WAKEUP_ID_WK_UP_COMP); + else + palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0); +} + +static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) +{ + struct palmas_usb *palmas_usb = _palmas_usb; + unsigned int vbus_line_state; + + palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_LINE_STATE, &vbus_line_state); + + if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { + if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { + palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; + extcon_set_cable_state(&palmas_usb->edev, "USB", true); + } else { + dev_dbg(palmas_usb->dev, + "Spurious connect event detected\n"); + } + } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { + if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { + palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; + extcon_set_cable_state(&palmas_usb->edev, "USB", false); + } else { + dev_dbg(palmas_usb->dev, + "Spurious disconnect event detected\n"); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) +{ + unsigned int set; + struct palmas_usb *palmas_usb = _palmas_usb; + + palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_LATCH_SET, &set); + + if (set & PALMAS_USB_ID_INT_SRC_ID_GND) { + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_SET, + PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_CLR, + PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_LATCH_CLR, + PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); + palmas_usb->linkstat = PALMAS_USB_STATE_ID; + extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); + } else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_SET, + PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_CLR, + PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_LATCH_CLR, + PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); + palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; + extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + } + + return IRQ_HANDLED; +} + +static void palmas_enable_irq(struct palmas_usb *palmas_usb) +{ + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_VBUS_CTRL_SET, + PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); + + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); + + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_SET, + PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); + + palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); + + /* cold plug for host mode needs this delay */ + msleep(30); + palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); +} + +static int palmas_usb_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_usb_platform_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; + struct palmas_usb *palmas_usb; + int status; + + if (node && !pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + if (!pdata) + return -ENOMEM; + + pdata->wakeup = of_property_read_bool(node, "ti,wakeup"); + } else if (!pdata) { + return -EINVAL; + } + + palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); + if (!palmas_usb) + return -ENOMEM; + + palmas->usb = palmas_usb; + palmas_usb->palmas = palmas; + + palmas_usb->dev = &pdev->dev; + + palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_ID_OTG_IRQ); + palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_ID_IRQ); + palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_VBUS_OTG_IRQ); + palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_VBUS_IRQ); + + palmas_usb_wakeup(palmas, pdata->wakeup); + + platform_set_drvdata(pdev, palmas_usb); + + palmas_usb->edev.name = "palmas-usb"; + palmas_usb->edev.supported_cable = palmas_extcon_cable; + palmas_usb->edev.mutually_exclusive = mutually_exclusive; + + status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev); + if (status) { + dev_err(&pdev->dev, "failed to register extcon device\n"); + return status; + } + + status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq, + NULL, palmas_id_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "palmas_usb_id", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + palmas_usb->id_irq, status); + goto fail_extcon; + } + + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "palmas_usb_vbus", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + palmas_usb->vbus_irq, status); + goto fail_extcon; + } + + palmas_enable_irq(palmas_usb); + + return 0; + +fail_extcon: + extcon_dev_unregister(&palmas_usb->edev); + + return status; +} + +static int palmas_usb_remove(struct platform_device *pdev) +{ + struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); + + extcon_dev_unregister(&palmas_usb->edev); + + return 0; +} + +static struct of_device_id of_palmas_match_tbl[] = { + { .compatible = "ti,palmas-usb", }, + { .compatible = "ti,twl6035-usb", }, + { /* end */ } +}; + +static struct platform_driver palmas_usb_driver = { + .probe = palmas_usb_probe, + .remove = palmas_usb_remove, + .driver = { + .name = "palmas-usb", + .of_match_table = of_palmas_match_tbl, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(palmas_usb_driver); + +MODULE_ALIAS("platform:palmas-usb"); +MODULE_AUTHOR("Graeme Gregory "); +MODULE_DESCRIPTION("Palmas USB transceiver driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl); diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 8f21daf62fb..9b81b2bdc46 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #define PALMAS_NUM_CLIENTS 3 @@ -37,6 +39,12 @@ struct palmas_gpadc; struct palmas_resource; struct palmas_usb; +enum palmas_usb_state { + PALMAS_USB_STATE_DISCONNECT, + PALMAS_USB_STATE_VBUS, + PALMAS_USB_STATE_ID, +}; + struct palmas { struct device *dev; @@ -180,9 +188,6 @@ struct palmas_pmic_platform_data { }; struct palmas_usb_platform_data { - /* Set this if platform wishes its own vbus control */ - int no_control_vbus; - /* Do we enable the wakeup comparator on probe */ int wakeup; }; @@ -350,22 +355,19 @@ struct palmas_usb { struct palmas *palmas; struct device *dev; - /* for vbus reporting with irqs disabled */ - spinlock_t lock; - - struct regulator *vbus_reg; + struct extcon_dev edev; /* used to set vbus, in atomic path */ struct work_struct set_vbus_work; - int irq1; - int irq2; - int irq3; - int irq4; + int id_otg_irq; + int id_irq; + int vbus_otg_irq; + int vbus_irq; int vbus_enable; - u8 linkstat; + enum palmas_usb_state linkstat; }; #define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator) -- cgit v1.2.3-70-g09d2 From 9c9f32eddee56888c7acd0d69134a5dcae09e1a8 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 12 Jun 2013 09:13:25 +0200 Subject: FMC: create drivers/fmc and toplevel Kconfig question This commit creates the drivers/fmc directory and puts the necessary hooks for kbuild and kconfig. The code is currently a placeholder that only registers an empty bus. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 9 +++++++++ drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/fmc/Kconfig | 17 +++++++++++++++++ drivers/fmc/Makefile | 4 ++++ drivers/fmc/fmc-core.c | 24 ++++++++++++++++++++++++ 6 files changed, 57 insertions(+) create mode 100644 drivers/fmc/Kconfig create mode 100644 drivers/fmc/Makefile create mode 100644 drivers/fmc/fmc-core.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 5be702cc844..14746ad5ee1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3309,6 +3309,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git S: Odd fixes F: drivers/block/floppy.c +FMC SUBSYSTEM +M: Alessandro Rubini +W: http://www.ohwr.org/projects/fmc-bus +S: Supported +F: drivers/fmc/ +F: include/linux/fmc*.h +F: include/linux/ipmi-fru.h +K: fmc_d.*register + FPU EMULATOR M: Bill Metzenthen W: http://floatingpoint.sourceforge.net/emulator/index.html diff --git a/drivers/Kconfig b/drivers/Kconfig index 9953a42809e..ae050b54c36 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -166,4 +166,6 @@ source "drivers/ipack/Kconfig" source "drivers/reset/Kconfig" +source "drivers/fmc/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 130abc1dfd6..336b0ad0acd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -152,3 +152,4 @@ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ +obj-$(CONFIG_FMC) += fmc/ diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig new file mode 100644 index 00000000000..e28790267c6 --- /dev/null +++ b/drivers/fmc/Kconfig @@ -0,0 +1,17 @@ +# +# FMC (ANSI-VITA 57.1) bus support +# + +menuconfig FMC + tristate "FMC support" + help + + FMC (FPGA Mezzanine Carrier) is a mechanical and electrical + standard for mezzanine cards that plug into a carrier board. + This kernel subsystem supports the matching between carrier + and mezzanine based on identifiers stored in the internal I2C + EEPROM, as well as having carrier-independent drivers. + + The framework was born outside of the kernel and at this time + the off-tree code base is more complete. Code and documentation + is at git://ohwr.org/fmc-projects/fmc-bus.git . diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile new file mode 100644 index 00000000000..a2784d8b530 --- /dev/null +++ b/drivers/fmc/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_FMC) += fmc.o + +fmc-y = fmc-core.o diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c new file mode 100644 index 00000000000..fc3547f32d5 --- /dev/null +++ b/drivers/fmc/fmc-core.c @@ -0,0 +1,24 @@ +/* Temporary placeholder so the empty code can build */ +#include +#include +#include +#include + +static struct bus_type fmc_bus_type = { + .name = "fmc", +}; + +static int fmc_init(void) +{ + return bus_register(&fmc_bus_type); +} + +static void fmc_exit(void) +{ + bus_unregister(&fmc_bus_type); +} + +module_init(fmc_init); +module_exit(fmc_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From ea6add0bf1a209a3eab725e4429b1ac4e4cdef73 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 16 Jun 2013 22:42:05 +0400 Subject: pcmcia: Remove ARCH_CLPS711X dependency No dependencies of PCMCIA and ARCH_CLPS711X. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 80faa56c684..6f619316828 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -243,7 +243,7 @@ config PCMCIA_DEBUG config PCMCIA_PROBE bool - default y if ISA && !ARCH_SA1100 && !ARCH_CLPS711X && !PARISC + default y if ISA && !ARCH_SA1100 && !PARISC config M32R_PCC bool "M32R PCMCIA I/F" -- cgit v1.2.3-70-g09d2 From f3cbfa5d6e1bbd80b16ea337a2b00a89264c2b45 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 10 Jun 2013 18:43:53 +0400 Subject: Misc: sram: Remove unneeded check Patch removes unneeded check for resource since devm_ioremap_resource do all for us. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 4a938860fec..d87cc91bc01 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -45,15 +45,11 @@ static int sram_probe(struct platform_device *pdev) int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - size = resource_size(res); - virt_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(virt_base)) return PTR_ERR(virt_base); + size = resource_size(res); sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); if (!sram) -- cgit v1.2.3-70-g09d2 From e31c51e4a1d11ed0b61557ca9da9cf18c8855531 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Tue, 11 Jun 2013 11:20:17 +0100 Subject: vme: tsi148: Only store VME bus errors if they will be checked The TSI148 driver provides an optional mechanism for ensuring that errors resulting from posted transfers are caught whilst still relevant. To do this errors are stored in a link list. If bus errors are not checked, this list would grow until available memory had been exhausted. Only store the errors in a link list if error detection is switched on. Reported-by: De Roo, Steven Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_tsi148.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 9c1aa4dc39c..94ce64d7a5f 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -169,7 +169,7 @@ static u32 tsi148_VERR_irqhandler(struct vme_bridge *tsi148_bridge) unsigned int error_addr_high, error_addr_low; unsigned long long error_addr; u32 error_attrib; - struct vme_bus_error *error; + struct vme_bus_error *error = NULL; struct tsi148_driver *bridge; bridge = tsi148_bridge->driver_priv; @@ -186,16 +186,22 @@ static u32 tsi148_VERR_irqhandler(struct vme_bridge *tsi148_bridge) "Occurred\n"); } - error = kmalloc(sizeof(struct vme_bus_error), GFP_ATOMIC); - if (error) { - error->address = error_addr; - error->attributes = error_attrib; - list_add_tail(&error->list, &tsi148_bridge->vme_errors); - } else { - dev_err(tsi148_bridge->parent, "Unable to alloc memory for " - "VMEbus Error reporting\n"); - dev_err(tsi148_bridge->parent, "VME Bus Error at address: " - "0x%llx, attributes: %08x\n", error_addr, error_attrib); + if (err_chk) { + error = kmalloc(sizeof(struct vme_bus_error), GFP_ATOMIC); + if (error) { + error->address = error_addr; + error->attributes = error_attrib; + list_add_tail(&error->list, &tsi148_bridge->vme_errors); + } else { + dev_err(tsi148_bridge->parent, + "Unable to alloc memory for VMEbus Error reporting\n"); + } + } + + if (!error) { + dev_err(tsi148_bridge->parent, + "VME Bus Error at address: 0x%llx, attributes: %08x\n", + error_addr, error_attrib); } /* Clear Status */ -- cgit v1.2.3-70-g09d2 From 2981795cc0c02d12d0e231bfe22480564b937cd5 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Tue, 11 Jun 2013 17:03:14 +0100 Subject: vme: tsi148: CR/CSR logic arround the wrong way The logic in the init routine for the TSI148 is inverted. It isn't switching on the CR/CSR space when it should be and is reporting it's on when its not. Correct the logic to do the right thing. Reported-by: De Roo, Steven Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_tsi148.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 94ce64d7a5f..c04600e26a3 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -2300,12 +2300,13 @@ static int tsi148_crcsr_init(struct vme_bridge *tsi148_bridge, dev_info(tsi148_bridge->parent, "CR/CSR Offset: %d\n", cbar); crat = ioread32be(bridge->base + TSI148_LCSR_CRAT); - if (crat & TSI148_LCSR_CRAT_EN) { + if (crat & TSI148_LCSR_CRAT_EN) + dev_info(tsi148_bridge->parent, "CR/CSR already enabled\n"); + else { dev_info(tsi148_bridge->parent, "Enabling CR/CSR space\n"); iowrite32be(crat | TSI148_LCSR_CRAT_EN, bridge->base + TSI148_LCSR_CRAT); - } else - dev_info(tsi148_bridge->parent, "CR/CSR already enabled\n"); + } /* If we want flushed, error-checked writes, set up a window * over the CR/CSR registers. We read from here to safely flush -- cgit v1.2.3-70-g09d2 From c36c5736be5cc638db7659093bf8c2c7f48c5000 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 17 Jun 2013 17:16:17 +0100 Subject: vme: tsi148: Window reserved for accessing CR/CSR does not need attributes set The tsi148 driver can be configured to reserve a window for internal use (as part of the error checking routine). The intialisation of this window currently configures a set of attributes that are never used as these are only ever used by the VME core and this window is not advertised to the core. Remove configuration of these attributes. Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_tsi148.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index c04600e26a3..0bb512cfad7 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -2448,13 +2448,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init(&tsi148_device->flush_image->lock); tsi148_device->flush_image->locked = 1; tsi148_device->flush_image->number = master_num; - tsi148_device->flush_image->address_attr = VME_A16 | VME_A24 | - VME_A32 | VME_A64; - tsi148_device->flush_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; - tsi148_device->flush_image->width_attr = VME_D16 | VME_D32; memset(&tsi148_device->flush_image->bus_resource, 0, sizeof(struct resource)); tsi148_device->flush_image->kern_base = NULL; -- cgit v1.2.3-70-g09d2 From a151427ed086952cc28f1d5f1cda84c33e48e358 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 13 Jun 2013 19:37:35 -0700 Subject: char: Convert use of typedef ctl_table to struct ctl_table This typedef is unnecessary and should just be removed. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/char/hpet.c | 6 +++--- drivers/char/ipmi/ipmi_poweroff.c | 6 +++--- drivers/char/random.c | 8 ++++---- drivers/char/rtc.c | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index d784650d14f..448ce5e29c5 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -725,7 +725,7 @@ static int hpet_is_known(struct hpet_data *hdp) return 0; } -static ctl_table hpet_table[] = { +static struct ctl_table hpet_table[] = { { .procname = "max-user-freq", .data = &hpet_max_freq, @@ -736,7 +736,7 @@ static ctl_table hpet_table[] = { {} }; -static ctl_table hpet_root[] = { +static struct ctl_table hpet_root[] = { { .procname = "hpet", .maxlen = 0, @@ -746,7 +746,7 @@ static ctl_table hpet_root[] = { {} }; -static ctl_table dev_root[] = { +static struct ctl_table dev_root[] = { { .procname = "dev", .maxlen = 0, diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 2efa176beab..9f2e3be2c5b 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -659,7 +659,7 @@ static struct ipmi_smi_watcher smi_watcher = { #ifdef CONFIG_PROC_FS #include -static ctl_table ipmi_table[] = { +static struct ctl_table ipmi_table[] = { { .procname = "poweroff_powercycle", .data = &poweroff_powercycle, .maxlen = sizeof(poweroff_powercycle), @@ -668,14 +668,14 @@ static ctl_table ipmi_table[] = { { } }; -static ctl_table ipmi_dir_table[] = { +static struct ctl_table ipmi_dir_table[] = { { .procname = "ipmi", .mode = 0555, .child = ipmi_table }, { } }; -static ctl_table ipmi_root_table[] = { +static struct ctl_table ipmi_root_table[] = { { .procname = "dev", .mode = 0555, .child = ipmi_dir_table }, diff --git a/drivers/char/random.c b/drivers/char/random.c index 35487e8ded5..0d91fe52f3f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1381,10 +1381,10 @@ static char sysctl_bootid[16]; * as an ASCII string in the standard UUID format. If accesses via the * sysctl system call, it is returned as 16 bytes of binary data. */ -static int proc_do_uuid(ctl_table *table, int write, +static int proc_do_uuid(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - ctl_table fake_table; + struct ctl_table fake_table; unsigned char buf[64], tmp_uuid[16], *uuid; uuid = table->data; @@ -1409,8 +1409,8 @@ static int proc_do_uuid(ctl_table *table, int write, } static int sysctl_poolsize = INPUT_POOL_WORDS * 32; -extern ctl_table random_table[]; -ctl_table random_table[] = { +extern struct ctl_table random_table[]; +struct ctl_table random_table[] = { { .procname = "poolsize", .data = &sysctl_poolsize, diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 91470fdbab2..c0cbbd429bd 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -280,7 +280,7 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id) /* * sysctl-tuning infrastructure. */ -static ctl_table rtc_table[] = { +static struct ctl_table rtc_table[] = { { .procname = "max-user-freq", .data = &rtc_max_user_freq, @@ -291,7 +291,7 @@ static ctl_table rtc_table[] = { { } }; -static ctl_table rtc_root[] = { +static struct ctl_table rtc_root[] = { { .procname = "rtc", .mode = 0555, @@ -300,7 +300,7 @@ static ctl_table rtc_root[] = { { } }; -static ctl_table dev_root[] = { +static struct ctl_table dev_root[] = { { .procname = "dev", .mode = 0555, -- cgit v1.2.3-70-g09d2 From 2c9b48ac3cb2cd2c84c43f235c65b7fc238f6f1f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 16 Jun 2013 09:16:31 +0300 Subject: mei: support HBM versioning Driver can work properly if device support driver HBM version or driver can downgrade its supported HBM version level Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 37 ++++++++++++++++++++++++++++++++----- drivers/misc/mei/hbm.h | 2 +- drivers/misc/mei/init.c | 3 +-- 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 6916045166e..565027b1bc7 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -535,6 +535,20 @@ static void mei_hbm_fw_disconnect_req(struct mei_device *dev, } +/** + * mei_hbm_version_is_supported - checks whether the driver can + * support the hbm version of the device + * + * @dev: the device structure + * returns true if driver can support hbm version of the device + */ +bool mei_hbm_version_is_supported(struct mei_device *dev) +{ + return (dev->version.major_version < HBM_MAJOR_VERSION) || + (dev->version.major_version == HBM_MAJOR_VERSION && + dev->version.minor_version <= HBM_MINOR_VERSION); +} + /** * mei_hbm_dispatch - bottom half read routine after ISR to * handle the read bus message cmd processing. @@ -562,9 +576,24 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) switch (mei_msg->hbm_cmd) { case HOST_START_RES_CMD: version_res = (struct hbm_host_version_response *)mei_msg; - if (!version_res->host_version_supported) { - dev->version = version_res->me_max_version; - dev_dbg(&dev->pdev->dev, "version mismatch.\n"); + + dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n", + HBM_MAJOR_VERSION, HBM_MINOR_VERSION, + version_res->me_max_version.major_version, + version_res->me_max_version.minor_version); + + if (version_res->host_version_supported) { + dev->version.major_version = HBM_MAJOR_VERSION; + dev->version.minor_version = HBM_MINOR_VERSION; + } else { + dev->version.major_version = + version_res->me_max_version.major_version; + dev->version.minor_version = + version_res->me_max_version.minor_version; + } + + if (!mei_hbm_version_is_supported(dev)) { + dev_warn(&dev->pdev->dev, "hbm version mismatch: stopping the driver.\n"); dev->hbm_state = MEI_HBM_STOP; mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr, @@ -575,8 +604,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) return; } - dev->version.major_version = HBM_MAJOR_VERSION; - dev->version.minor_version = HBM_MINOR_VERSION; if (dev->dev_state == MEI_DEV_INIT_CLIENTS && dev->hbm_state == MEI_HBM_START) { dev->init_clients_timer = 0; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index e80dc24ef3e..4ae2e56e404 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -54,7 +54,7 @@ int mei_hbm_start_wait(struct mei_device *dev); int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); - +bool mei_hbm_version_is_supported(struct mei_device *dev); #endif /* _MEI_HBM_H_ */ diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index f580d30bb78..79e9e1c3056 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -106,8 +106,7 @@ int mei_start(struct mei_device *dev) goto err; } - if (dev->version.major_version != HBM_MAJOR_VERSION || - dev->version.minor_version != HBM_MINOR_VERSION) { + if (!mei_hbm_version_is_supported(dev)) { dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); goto err; } -- cgit v1.2.3-70-g09d2 From d777f98bfa44e5a2748603276e9e9fb6ab999079 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Tue, 11 Jun 2013 13:11:18 +0300 Subject: memory: tegra20-mc: Fix hang in IRQ handler. In Tegra20 any memory controller interrupt would cause an infinite loop in the IRQ handler. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Reviewed-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/memory/tegra20-mc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c index 2ca5f2814f4..0548eeacd57 100644 --- a/drivers/memory/tegra20-mc.c +++ b/drivers/memory/tegra20-mc.c @@ -193,8 +193,11 @@ static irqreturn_t tegra20_mc_isr(int irq, void *data) mask &= stat; if (!mask) return IRQ_NONE; - while ((bit = ffs(mask)) != 0) + while ((bit = ffs(mask)) != 0) { tegra20_mc_decode(mc, bit - 1); + mask &= ~BIT(bit - 1); + } + mc_writel(mc, stat, MC_INTSTATUS); return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From 36dd68319c7b56a76ed450b0e470067b5d74b9b2 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Tue, 11 Jun 2013 13:11:19 +0300 Subject: memory: tegra30-mc: Fix IRQ handler. In Tegra30 any memory controller interrupt would cause an infinite loop in the IRQ handler. Additionally, a garbage pointer was used to read the MC status registers, which causes wrong values to be printed if a MC error occurred. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Reviewed-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/memory/tegra30-mc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c index f4ae074badc..58d2979b403 100644 --- a/drivers/memory/tegra30-mc.c +++ b/drivers/memory/tegra30-mc.c @@ -218,7 +218,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n) return; } - err = readl(mc + MC_ERR_STATUS); + err = mc_readl(mc, MC_ERR_STATUS); type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT; perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >> @@ -235,7 +235,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n) if (cid < ARRAY_SIZE(tegra30_mc_client)) client = tegra30_mc_client[cid]; - addr = readl(mc + MC_ERR_ADR); + addr = mc_readl(mc, MC_ERR_ADR); dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n", mc_int_err[idx], err, addr, client, @@ -313,8 +313,11 @@ static irqreturn_t tegra30_mc_isr(int irq, void *data) mask &= stat; if (!mask) return IRQ_NONE; - while ((bit = ffs(mask)) != 0) + while ((bit = ffs(mask)) != 0) { tegra30_mc_decode(mc, bit - 1); + mask &= ~BIT(bit - 1); + } + mc_writel(mc, stat, MC_INTSTATUS); return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From 35848f68b07df3f917cb13fc3c134718669f569b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 18 Jun 2013 13:04:23 +0800 Subject: drivers: hv: switch to use mb() instead of smp_mb() Even if guest were compiled without SMP support, it could not assume that host wasn't. So switch to use mb() instead of smp_mb() to force memory barriers for UP guest. Signed-off-by: Jason Wang Cc: Haiyang Zhang Cc: stable Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 10 +++++----- drivers/hv/vmbus_drv.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index d6fbb5772b8..791f45dfc85 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -32,7 +32,7 @@ void hv_begin_read(struct hv_ring_buffer_info *rbi) { rbi->ring_buffer->interrupt_mask = 1; - smp_mb(); + mb(); } u32 hv_end_read(struct hv_ring_buffer_info *rbi) @@ -41,7 +41,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi) u32 write; rbi->ring_buffer->interrupt_mask = 0; - smp_mb(); + mb(); /* * Now check to see if the ring buffer is still empty. @@ -71,7 +71,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi) static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) { - smp_mb(); + mb(); if (rbi->ring_buffer->interrupt_mask) return false; @@ -442,7 +442,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, sizeof(u64)); /* Issue a full memory barrier before updating the write index */ - smp_mb(); + mb(); /* Now, update the write location */ hv_set_next_write_location(outring_info, next_write_location); @@ -549,7 +549,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, /* Make sure all reads are done before we update the read index since */ /* the writer may start writing to the read area once the read index */ /*is updated */ - smp_mb(); + mb(); /* Update the read index */ hv_set_next_read_location(inring_info, next_read_location); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index bf421e0efa1..4004e54ef05 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -434,7 +434,7 @@ static void vmbus_on_msg_dpc(unsigned long data) * will not deliver any more messages since there is * no empty slot */ - smp_mb(); + mb(); if (msg->header.message_flags.msg_pending) { /* -- cgit v1.2.3-70-g09d2 From 77864f2e0a824a92bd93b4c9ad22c31d28ff55a6 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:13 +0200 Subject: FMC: add core bus driver This module offers registration services for both carriers (i.e. devices) and mezzanines (i.e. drivers). The matching for devices and drivers is performed according to the IPMI standard for FRU devices (Field Replaceable Units). The code includes support for parsing an SDB tree if present in the FPGA, and dumping it for diagnostics. SDB is not mandatory. Files in this commit correspond to commit ab23167f in the master branch of the project hosted on ohwr.org. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- drivers/fmc/Makefile | 4 + drivers/fmc/fmc-core.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++- drivers/fmc/fmc-dump.c | 100 ++++++++++++++++++ drivers/fmc/fmc-match.c | 114 ++++++++++++++++++++ drivers/fmc/fmc-sdb.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/fmc/fru-parse.c | 82 +++++++++++++++ 6 files changed, 838 insertions(+), 1 deletion(-) create mode 100644 drivers/fmc/fmc-dump.c create mode 100644 drivers/fmc/fmc-match.c create mode 100644 drivers/fmc/fmc-sdb.c create mode 100644 drivers/fmc/fru-parse.c (limited to 'drivers') diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index a2784d8b530..df9893972a6 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -2,3 +2,7 @@ obj-$(CONFIG_FMC) += fmc.o fmc-y = fmc-core.o +fmc-y += fmc-match.o +fmc-y += fmc-sdb.o +fmc-y += fru-parse.o +fmc-y += fmc-dump.o diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c index fc3547f32d5..24d52497524 100644 --- a/drivers/fmc/fmc-core.c +++ b/drivers/fmc/fmc-core.c @@ -1,13 +1,285 @@ -/* Temporary placeholder so the empty code can build */ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ #include #include +#include #include #include +#include + +static int fmc_check_version(unsigned long version, const char *name) +{ + if (__FMC_MAJOR(version) != FMC_MAJOR) { + pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n", + __func__, name, __FMC_MAJOR(version), FMC_MAJOR); + return -EINVAL; + } + + if (__FMC_MINOR(version) != FMC_MINOR) + pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n", + __func__, name, __FMC_MINOR(version), FMC_MINOR); + return 0; +} + +static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* struct fmc_device *fdev = to_fmc_device(dev); */ + + /* FIXME: The MODALIAS */ + add_uevent_var(env, "MODALIAS=%s", "fmc"); + return 0; +} + +static int fmc_probe(struct device *dev) +{ + struct fmc_driver *fdrv = to_fmc_driver(dev->driver); + struct fmc_device *fdev = to_fmc_device(dev); + + return fdrv->probe(fdev); +} + +static int fmc_remove(struct device *dev) +{ + struct fmc_driver *fdrv = to_fmc_driver(dev->driver); + struct fmc_device *fdev = to_fmc_device(dev); + + return fdrv->remove(fdev); +} + +static void fmc_shutdown(struct device *dev) +{ + /* not implemented but mandatory */ +} static struct bus_type fmc_bus_type = { .name = "fmc", + .match = fmc_match, + .uevent = fmc_uevent, + .probe = fmc_probe, + .remove = fmc_remove, + .shutdown = fmc_shutdown, }; +static void fmc_release(struct device *dev) +{ + struct fmc_device *fmc = container_of(dev, struct fmc_device, dev); + + kfree(fmc); +} + +/* + * The eeprom is exported in sysfs, through a binary attribute + */ + +static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct fmc_device *fmc; + int eelen; + + dev = container_of(kobj, struct device, kobj); + fmc = container_of(dev, struct fmc_device, dev); + eelen = fmc->eeprom_len; + if (off > eelen) + return -ESPIPE; + if (off == eelen) + return 0; /* EOF */ + if (off + count > eelen) + count = eelen - off; + memcpy(buf, fmc->eeprom + off, count); + return count; +} + +static struct bin_attribute fmc_eeprom_attr = { + .attr = { .name = "eeprom", .mode = S_IRUGO, }, + .size = 8192, /* more or less standard */ + .read = fmc_read_eeprom, +}; + +/* + * Functions for client modules follow + */ + +int fmc_driver_register(struct fmc_driver *drv) +{ + if (fmc_check_version(drv->version, drv->driver.name)) + return -EINVAL; + drv->driver.bus = &fmc_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(fmc_driver_register); + +void fmc_driver_unregister(struct fmc_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(fmc_driver_unregister); + +/* + * When a device set is registered, all eeproms must be read + * and all FRUs must be parsed + */ +int fmc_device_register_n(struct fmc_device **devs, int n) +{ + struct fmc_device *fmc, **devarray; + uint32_t device_id; + int i, ret = 0; + + if (n < 1) + return 0; + + /* Check the version of the first data structure (function prints) */ + if (fmc_check_version(devs[0]->version, devs[0]->carrier_name)) + return -EINVAL; + + devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL); + if (!devarray) + return -ENOMEM; + + /* Make all other checks before continuing, for all devices */ + for (i = 0; i < n; i++) { + fmc = devarray[i]; + if (!fmc->hwdev) { + pr_err("%s: device nr. %i has no hwdev pointer\n", + __func__, i); + ret = -EINVAL; + break; + } + if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) { + dev_info(fmc->hwdev, "absent mezzanine in slot %d\n", + fmc->slot_id); + continue; + } + if (!fmc->eeprom) { + dev_err(fmc->hwdev, "no eeprom provided for slot %i\n", + fmc->slot_id); + ret = -EINVAL; + } + if (!fmc->eeprom_addr) { + dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n", + fmc->slot_id); + ret = -EINVAL; + } + if (!fmc->carrier_name || !fmc->carrier_data || + !fmc->device_id) { + dev_err(fmc->hwdev, + "deivce nr %i: carrier name, " + "data or dev_id not set\n", i); + ret = -EINVAL; + } + if (ret) + break; + + } + if (ret) { + kfree(devarray); + return ret; + } + + /* Validation is ok. Now init and register the devices */ + for (i = 0; i < n; i++) { + fmc = devarray[i]; + + if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) + continue; /* dev_info already done above */ + + fmc->nr_slots = n; /* each slot must know how many are there */ + fmc->devarray = devarray; + + device_initialize(&fmc->dev); + fmc->dev.release = fmc_release; + fmc->dev.parent = fmc->hwdev; + + /* Fill the identification stuff (may fail) */ + fmc_fill_id_info(fmc); + + fmc->dev.bus = &fmc_bus_type; + + /* Name from mezzanine info or carrier info. Or 0,1,2.. */ + device_id = fmc->device_id; + if (!fmc->mezzanine_name) + dev_set_name(&fmc->dev, "fmc-%04x", device_id); + else + dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, + device_id); + ret = device_add(&fmc->dev); + if (ret < 0) { + dev_err(fmc->hwdev, "Slot %i: Failed in registering " + "\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name); + goto out; + } + ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr); + if (ret < 0) { + dev_err(&fmc->dev, "Failed in registering eeprom\n"); + goto out1; + } + /* This device went well, give information to the user */ + fmc_dump_eeprom(fmc); + fmc_dump_sdb(fmc); + } + return 0; + +out1: + device_del(&fmc->dev); +out: + fmc_free_id_info(fmc); + put_device(&fmc->dev); + + kfree(devarray); + for (i--; i >= 0; i--) { + sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); + device_del(&devs[i]->dev); + fmc_free_id_info(devs[i]); + put_device(&devs[i]->dev); + } + return ret; + +} +EXPORT_SYMBOL(fmc_device_register_n); + +int fmc_device_register(struct fmc_device *fmc) +{ + return fmc_device_register_n(&fmc, 1); +} +EXPORT_SYMBOL(fmc_device_register); + +void fmc_device_unregister_n(struct fmc_device **devs, int n) +{ + int i; + + if (n < 1) + return; + + /* Free devarray first, not used by the later loop */ + kfree(devs[0]->devarray); + + for (i = 0; i < n; i++) { + if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE) + continue; + sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); + device_del(&devs[i]->dev); + fmc_free_id_info(devs[i]); + put_device(&devs[i]->dev); + } +} +EXPORT_SYMBOL(fmc_device_unregister_n); + +void fmc_device_unregister(struct fmc_device *fmc) +{ + fmc_device_unregister_n(&fmc, 1); +} +EXPORT_SYMBOL(fmc_device_unregister); + +/* Init and exit are trivial */ static int fmc_init(void) { return bus_register(&fmc_bus_type); diff --git a/drivers/fmc/fmc-dump.c b/drivers/fmc/fmc-dump.c new file mode 100644 index 00000000000..c91afd6388f --- /dev/null +++ b/drivers/fmc/fmc-dump.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include +#include +#include +#include +#include + +static int fmc_must_dump_eeprom; +module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644); +static int fmc_must_dump_sdb; +module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644); + +#define LINELEN 16 + +/* Dumping 8k takes oh so much: avoid duplicate lines */ +static const uint8_t *dump_line(int addr, const uint8_t *line, + const uint8_t *prev) +{ + int i; + + if (!prev || memcmp(line, prev, LINELEN)) { + pr_info("%04x: ", addr); + for (i = 0; i < LINELEN; ) { + printk(KERN_CONT "%02x", line[i]); + i++; + printk(i & 3 ? " " : i & (LINELEN - 1) ? " " : "\n"); + } + return line; + } + /* repeated line */ + if (line == prev + LINELEN) + pr_info("[...]\n"); + return prev; +} + +void fmc_dump_eeprom(const struct fmc_device *fmc) +{ + const uint8_t *line, *prev; + int i; + + if (!fmc_must_dump_eeprom) + return; + + pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), + fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); + pr_info("FMC: dumping eeprom 0x%x (%i) bytes\n", fmc->eeprom_len, + fmc->eeprom_len); + + line = fmc->eeprom; + prev = NULL; + for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN) + prev = dump_line(i, line, prev); +} + +void fmc_dump_sdb(const struct fmc_device *fmc) +{ + const uint8_t *line, *prev; + int i, len; + + if (!fmc->sdb) + return; + if (!fmc_must_dump_sdb) + return; + + /* If the argument is not-zero, do simple dump (== show) */ + if (fmc_must_dump_sdb > 0) + fmc_show_sdb_tree(fmc); + + if (fmc_must_dump_sdb == 1) + return; + + /* If bigger than 1, dump it seriously, to help debugging */ + + /* + * Here we should really use libsdbfs (which is designed to + * work in kernel space as well) , but it doesn't support + * directories yet, and it requires better intergration (it + * should be used instead of fmc-specific code). + * + * So, lazily, just dump the top-level array + */ + pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), + fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); + pr_info("FMC: poor dump of sdb first level:\n"); + + len = fmc->sdb->len * sizeof(union sdb_record); + line = (void *)fmc->sdb->record; + prev = NULL; + for (i = 0; i < len; i += LINELEN, line += LINELEN) + prev = dump_line(i, line, prev); + return; +} diff --git a/drivers/fmc/fmc-match.c b/drivers/fmc/fmc-match.c new file mode 100644 index 00000000000..104a5efc220 --- /dev/null +++ b/drivers/fmc/fmc-match.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include +#include +#include +#include + +/* The fru parser is both user and kernel capable: it needs alloc */ +void *fru_alloc(size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + +/* The actual match function */ +int fmc_match(struct device *dev, struct device_driver *drv) +{ + struct fmc_driver *fdrv = to_fmc_driver(drv); + struct fmc_device *fdev = to_fmc_device(dev); + struct fmc_fru_id *fid; + int i, matched = 0; + + /* This currently only matches the EEPROM (FRU id) */ + fid = fdrv->id_table.fru_id; + if (!fid) { + dev_warn(&fdev->dev, "Driver has no ID: matches all\n"); + matched = 1; + } else { + if (!fdev->id.manufacturer || !fdev->id.product_name) + return 0; /* the device has no FRU information */ + for (i = 0; i < fdrv->id_table.fru_id_nr; i++, fid++) { + if (fid->manufacturer && + strcmp(fid->manufacturer, fdev->id.manufacturer)) + continue; + if (fid->product_name && + strcmp(fid->product_name, fdev->id.product_name)) + continue; + matched = 1; + break; + } + } + + /* FIXME: match SDB contents */ + return matched; +} + +/* This function creates ID info for a newly registered device */ +int fmc_fill_id_info(struct fmc_device *fmc) +{ + struct fru_common_header *h; + struct fru_board_info_area *bia; + int ret, allocated = 0; + + /* If we know the eeprom length, try to read it off the device */ + if (fmc->eeprom_len && !fmc->eeprom) { + fmc->eeprom = kzalloc(fmc->eeprom_len, GFP_KERNEL); + if (!fmc->eeprom) + return -ENOMEM; + allocated = 1; + ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); + if (ret < 0) + goto out; + } + + /* If no eeprom, continue with other matches */ + if (!fmc->eeprom) + return 0; + + dev_info(fmc->hwdev, "mezzanine %i\n", fmc->slot_id); /* header */ + + /* So we have the eeprom: parse the FRU part (if any) */ + h = (void *)fmc->eeprom; + if (h->format != 1) { + pr_info(" EEPROM has no FRU information\n"); + goto out; + } + if (!fru_header_cksum_ok(h)) { + pr_info(" FRU: wrong header checksum\n"); + goto out; + } + bia = fru_get_board_area(h); + if (!fru_bia_cksum_ok(bia)) { + pr_info(" FRU: wrong board area checksum\n"); + goto out; + } + fmc->id.manufacturer = fru_get_board_manufacturer(h); + fmc->id.product_name = fru_get_product_name(h); + pr_info(" Manufacturer: %s\n", fmc->id.manufacturer); + pr_info(" Product name: %s\n", fmc->id.product_name); + + /* Create the short name (FIXME: look in sdb as well) */ + fmc->mezzanine_name = kstrdup(fmc->id.product_name, GFP_KERNEL); + +out: + if (allocated) { + kfree(fmc->eeprom); + fmc->eeprom = NULL; + } + return 0; /* no error: let other identification work */ +} + +/* Some ID data is allocated using fru_alloc() above, so release it */ +void fmc_free_id_info(struct fmc_device *fmc) +{ + kfree(fmc->mezzanine_name); + kfree(fmc->id.manufacturer); + kfree(fmc->id.product_name); +} diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c new file mode 100644 index 00000000000..74fb326f4af --- /dev/null +++ b/drivers/fmc/fmc-sdb.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include +#include +#include +#include +#include +#include +#include + +static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address, + int convert) +{ + uint32_t res = fmc_readl(fmc, address); + if (convert) + return __be32_to_cpu(res); + return res; +} + +static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc, + unsigned long sdb_addr, + unsigned long reg_base, int level) +{ + uint32_t onew; + int i, j, n, convert = 0; + struct sdb_array *arr, *sub; + + onew = fmc_readl(fmc, sdb_addr); + if (onew == SDB_MAGIC) { + /* Uh! If we are little-endian, we must convert */ + if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC)) + convert = 1; + } else if (onew == __be32_to_cpu(SDB_MAGIC)) { + /* ok, don't convert */ + } else { + return ERR_PTR(-ENOENT); + } + /* So, the magic was there: get the count from offset 4*/ + onew = __sdb_rd(fmc, sdb_addr + 4, convert); + n = __be16_to_cpu(*(uint16_t *)&onew); + arr = kzalloc(sizeof(*arr), GFP_KERNEL); + if (arr) { + arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL); + arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL); + } + if (!arr || !arr->record || !arr->subtree) { + kfree(arr->record); + kfree(arr->subtree); + kfree(arr); + return ERR_PTR(-ENOMEM); + } + arr->len = n; + arr->level = level; + arr->fmc = fmc; + for (i = 0; i < n; i++) { + union sdb_record *r; + + for (j = 0; j < sizeof(arr->record[0]); j += 4) { + *(uint32_t *)((void *)(arr->record + i) + j) = + __sdb_rd(fmc, sdb_addr + (i * 64) + j, convert); + } + r = &arr->record[i]; + arr->subtree[i] = ERR_PTR(-ENODEV); + if (r->empty.record_type == sdb_type_bridge) { + struct sdb_component *c = &r->bridge.sdb_component; + uint64_t subaddr = __be64_to_cpu(r->bridge.sdb_child); + uint64_t newbase = __be64_to_cpu(c->addr_first); + + subaddr += reg_base; + newbase += reg_base; + sub = __fmc_scan_sdb_tree(fmc, subaddr, newbase, + level + 1); + arr->subtree[i] = sub; /* may be error */ + if (IS_ERR(sub)) + continue; + sub->parent = arr; + sub->baseaddr = newbase; + } + } + return arr; +} + +int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address) +{ + struct sdb_array *ret; + if (fmc->sdb) + return -EBUSY; + ret = __fmc_scan_sdb_tree(fmc, address, 0 /* regs */, 0); + if (IS_ERR(ret)) + return PTR_ERR(ret); + fmc->sdb = ret; + return 0; +} +EXPORT_SYMBOL(fmc_scan_sdb_tree); + +static void __fmc_sdb_free(struct sdb_array *arr) +{ + int i, n; + + if (!arr) + return; + n = arr->len; + for (i = 0; i < n; i++) { + if (IS_ERR(arr->subtree[i])) + continue; + __fmc_sdb_free(arr->subtree[i]); + } + kfree(arr->record); + kfree(arr->subtree); + kfree(arr); +} + +int fmc_free_sdb_tree(struct fmc_device *fmc) +{ + __fmc_sdb_free(fmc->sdb); + fmc->sdb = NULL; + return 0; +} +EXPORT_SYMBOL(fmc_free_sdb_tree); + +/* This helper calls reprogram and inizialized sdb as well */ +int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, + int sdb_entry) +{ + int ret; + + ret = fmc->op->reprogram(fmc, d, gw); + if (ret < 0) + return ret; + if (sdb_entry < 0) + return ret; + + /* We are required to find SDB at a given offset */ + ret = fmc_scan_sdb_tree(fmc, sdb_entry); + if (ret < 0) { + dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n", + sdb_entry); + return -ENODEV; + } + fmc_dump_sdb(fmc); + return 0; +} +EXPORT_SYMBOL(fmc_reprogram); + +static void __fmc_show_sdb_tree(const struct fmc_device *fmc, + const struct sdb_array *arr) +{ + int i, j, n = arr->len, level = arr->level; + const struct sdb_array *ap; + + for (i = 0; i < n; i++) { + unsigned long base; + union sdb_record *r; + struct sdb_product *p; + struct sdb_component *c; + r = &arr->record[i]; + c = &r->dev.sdb_component; + p = &c->product; + base = 0; + for (ap = arr; ap; ap = ap->parent) + base += ap->baseaddr; + dev_info(&fmc->dev, "SDB: "); + + for (j = 0; j < level; j++) + printk(KERN_CONT " "); + switch (r->empty.record_type) { + case sdb_type_interconnect: + printk(KERN_CONT "%08llx:%08x %.19s\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name); + break; + case sdb_type_device: + printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name, + __be64_to_cpu(c->addr_first) + base, + __be64_to_cpu(c->addr_last) + base); + break; + case sdb_type_bridge: + printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name, + __be64_to_cpu(c->addr_first) + base); + if (IS_ERR(arr->subtree[i])) { + printk(KERN_CONT "(bridge error %li)\n", + PTR_ERR(arr->subtree[i])); + break; + } + __fmc_show_sdb_tree(fmc, arr->subtree[i]); + break; + case sdb_type_integration: + printk(KERN_CONT "integration\n"); + break; + case sdb_type_repo_url: + printk(KERN_CONT "repo-url\n"); + break; + case sdb_type_synthesis: + printk(KERN_CONT "synthesis-info\n"); + break; + case sdb_type_empty: + printk(KERN_CONT "empty\n"); + break; + default: + printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n", + r->empty.record_type); + break; + } + } +} + +void fmc_show_sdb_tree(const struct fmc_device *fmc) +{ + if (!fmc->sdb) + return; + __fmc_show_sdb_tree(fmc, fmc->sdb); +} +EXPORT_SYMBOL(fmc_show_sdb_tree); + +signed long fmc_find_sdb_device(struct sdb_array *tree, + uint64_t vid, uint32_t did, unsigned long *sz) +{ + signed long res = -ENODEV; + union sdb_record *r; + struct sdb_product *p; + struct sdb_component *c; + int i, n = tree->len; + uint64_t last, first; + + /* FIXME: what if the first interconnect is not at zero? */ + for (i = 0; i < n; i++) { + r = &tree->record[i]; + c = &r->dev.sdb_component; + p = &c->product; + + if (!IS_ERR(tree->subtree[i])) + res = fmc_find_sdb_device(tree->subtree[i], + vid, did, sz); + if (res >= 0) + return res + tree->baseaddr; + if (r->empty.record_type != sdb_type_device) + continue; + if (__be64_to_cpu(p->vendor_id) != vid) + continue; + if (__be32_to_cpu(p->device_id) != did) + continue; + /* found */ + last = __be64_to_cpu(c->addr_last); + first = __be64_to_cpu(c->addr_first); + if (sz) + *sz = (typeof(*sz))(last + 1 - first); + return first + tree->baseaddr; + } + return res; +} +EXPORT_SYMBOL(fmc_find_sdb_device); diff --git a/drivers/fmc/fru-parse.c b/drivers/fmc/fru-parse.c new file mode 100644 index 00000000000..cb46263c5da --- /dev/null +++ b/drivers/fmc/fru-parse.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include + +/* Some internal helpers */ +static struct fru_type_length * +__fru_get_board_tl(struct fru_common_header *header, int nr) +{ + struct fru_board_info_area *bia; + struct fru_type_length *tl; + + bia = fru_get_board_area(header); + tl = bia->tl; + while (nr > 0 && !fru_is_eof(tl)) { + tl = fru_next_tl(tl); + nr--; + } + if (fru_is_eof(tl)) + return NULL; + return tl; +} + +static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr) +{ + struct fru_type_length *tl; + char *res; + int len; + + tl = __fru_get_board_tl(header, nr); + if (!tl) + return NULL; + len = fru_strlen(tl); + res = fru_alloc(fru_strlen(tl) + 1); + if (!res) + return NULL; + return fru_strcpy(res, tl); +} + +/* Public checksum verifiers */ +int fru_header_cksum_ok(struct fru_common_header *header) +{ + uint8_t *ptr = (void *)header; + int i, sum; + + for (i = sum = 0; i < sizeof(*header); i++) + sum += ptr[i]; + return (sum & 0xff) == 0; +} +int fru_bia_cksum_ok(struct fru_board_info_area *bia) +{ + uint8_t *ptr = (void *)bia; + int i, sum; + + for (i = sum = 0; i < 8 * bia->area_len; i++) + sum += ptr[i]; + return (sum & 0xff) == 0; +} + +/* Get various stuff, trivial */ +char *fru_get_board_manufacturer(struct fru_common_header *header) +{ + return __fru_alloc_get_tl(header, 0); +} +char *fru_get_product_name(struct fru_common_header *header) +{ + return __fru_alloc_get_tl(header, 1); +} +char *fru_get_serial_number(struct fru_common_header *header) +{ + return __fru_alloc_get_tl(header, 2); +} +char *fru_get_part_number(struct fru_common_header *header) +{ + return __fru_alloc_get_tl(header, 3); +} -- cgit v1.2.3-70-g09d2 From 6c62a895e572145f8aa24f2040d1bb8eff473313 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:35 +0200 Subject: FMC: add a software carrier driver This fake carrier is designed to help FMC users understand how a carrier driver works, and to experiment the behaviour with EEPROM reprogramming (with a mezzanine driver commited later). This carrier can register up to 4 (fake) mezzanines. We have real carriers (both on PCI-E and VME), but they are bigger things and are not part of this submission. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- Documentation/fmc/00-INDEX | 3 + Documentation/fmc/fmc-fakedev.txt | 36 ++++ drivers/fmc/Kconfig | 11 ++ drivers/fmc/Makefile | 2 + drivers/fmc/fmc-fakedev.c | 355 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 407 insertions(+) create mode 100644 Documentation/fmc/fmc-fakedev.txt create mode 100644 drivers/fmc/fmc-fakedev.c (limited to 'drivers') diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 71304b7fc49..34df5cfc0c4 100644 --- a/Documentation/fmc/00-INDEX +++ b/Documentation/fmc/00-INDEX @@ -24,3 +24,6 @@ mezzanine.txt identifiers.txt - how identification and matching works + +fmc-fakedev.txt + - about drivers/fmc/fmc-fakedev.ko diff --git a/Documentation/fmc/fmc-fakedev.txt b/Documentation/fmc/fmc-fakedev.txt new file mode 100644 index 00000000000..e85b74a4ae3 --- /dev/null +++ b/Documentation/fmc/fmc-fakedev.txt @@ -0,0 +1,36 @@ +fmc-fakedev +=========== + +This package includes a software-only device, called fmc-fakedev, which +is able to register up to 4 mezzanines (by default it registers one). +Unlike the SPEC driver, which creates an FMC device for each PCI cards +it manages, this module creates a single instance of its set of +mezzanines. + +It is meant as the simplest possible example of how a driver should be +written, and it includes a fake EEPROM image (built using the tools +described in *note FMC Identification::),, which by default is +replicated for each fake mezzanine. + +You can also use this device to verify the match algorithms, by asking +it to test your own EEPROM image. You can provide the image by means of +the eeprom= module parameter: the new EEPROM image is loaded, as usual, +by means of the firmware loader. This example shows the defaults and a +custom EEPROM image: + + spusa.root# insmod fmc-fakedev.ko + [ 99.971247] fake-fmc-carrier: mezzanine 0 + [ 99.975393] Manufacturer: fake-vendor + [ 99.979624] Product name: fake-design-for-testing + spusa.root# rmmod fmc-fakedev + spusa.root# insmod fmc-fakedev.ko eeprom=fdelay-eeprom.bin + [ 121.447464] fake-fmc-carrier: Mezzanine 0: eeprom "fdelay-eeprom.bin" + [ 121.462725] fake-fmc-carrier: mezzanine 0 + [ 121.466858] Manufacturer: CERN + [ 121.470477] Product name: FmcDelay1ns4cha + spusa.root# rmmod fmc-fakedev + +After loading the device, you can use the write_ee method do modify its +own internal fake EEPROM: whenever the image is overwritten starting at +offset 0, the module will unregister and register again the FMC device. +This is shown in fmc-write-eeprom.txt diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig index e28790267c6..505e96b02a9 100644 --- a/drivers/fmc/Kconfig +++ b/drivers/fmc/Kconfig @@ -15,3 +15,14 @@ menuconfig FMC The framework was born outside of the kernel and at this time the off-tree code base is more complete. Code and documentation is at git://ohwr.org/fmc-projects/fmc-bus.git . + +if FMC + +config FMC_FAKEDEV + tristate "FMC fake device (software testing)" + help + This is a fake carrier, bringing a default EEPROM content + that can be rewritten at run time and usef for matching + mezzanines. + +endif # FMC diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index df9893972a6..9832b79976b 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -6,3 +6,5 @@ fmc-y += fmc-match.o fmc-y += fmc-sdb.o fmc-y += fru-parse.o fmc-y += fmc-dump.o + +obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o diff --git a/drivers/fmc/fmc-fakedev.c b/drivers/fmc/fmc-fakedev.c new file mode 100644 index 00000000000..bec94ac0764 --- /dev/null +++ b/drivers/fmc/fmc-fakedev.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * The software is provided "as is"; the copyright holders disclaim + * all warranties and liabilities, to the extent permitted by + * applicable law. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FF_EEPROM_SIZE 8192 /* The standard eeprom size */ +#define FF_MAX_MEZZANINES 4 /* Fakes a multi-mezzanine carrier */ + +/* The user can pass up to 4 names of eeprom images to load */ +static char *ff_eeprom[FF_MAX_MEZZANINES]; +static int ff_nr_eeprom; +module_param_array_named(eeprom, ff_eeprom, charp, &ff_nr_eeprom, 0444); + +/* The user can ask for a multi-mezzanine carrier, with the default eeprom */ +static int ff_nr_dev = 1; +module_param_named(ndev, ff_nr_dev, int, 0444); + + +/* Lazily, don't support the "standard" module parameters */ + +/* + * Eeprom built from these commands: + + ../fru-generator -v fake-vendor -n fake-design-for-testing \ + -s 01234 -p none > IPMI-FRU + + gensdbfs . ../fake-eeprom.bin +*/ +static char ff_eeimg[FF_MAX_MEZZANINES][FF_EEPROM_SIZE] = { + { + 0x01, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x01, 0x0b, 0x00, 0xb2, + 0x86, 0x87, 0xcb, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0xd7, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x64, 0x65, 0x73, 0x69, + 0x67, 0x6e, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0xc5, 0x30, 0x31, 0x32, 0x33, 0x34, 0xc4, 0x6e, 0x6f, 0x6e, + 0x65, 0xda, 0x32, 0x30, 0x31, 0x32, 0x2d, 0x31, 0x31, 0x2d, 0x31, 0x39, + 0x20, 0x32, 0x32, 0x3a, 0x34, 0x32, 0x3a, 0x33, 0x30, 0x2e, 0x30, 0x37, + 0x34, 0x30, 0x35, 0x35, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, + 0x02, 0x02, 0x0d, 0xf7, 0xf8, 0x02, 0xb0, 0x04, 0x74, 0x04, 0xec, 0x04, + 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x02, 0x02, 0x0d, 0x5c, 0x93, 0x01, + 0x4a, 0x01, 0x39, 0x01, 0x5a, 0x01, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x0b, + 0x02, 0x02, 0x0d, 0x63, 0x8c, 0x00, 0xfa, 0x00, 0xed, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x0f, 0x01, 0x02, 0x0d, 0xfb, 0xf5, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x0d, 0xfc, 0xf4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0d, 0xfd, 0xf3, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x82, 0x0b, 0xea, 0x8f, 0xa2, 0x12, 0x00, 0x00, 0x1e, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x53, 0x44, 0x42, 0x2d, 0x00, 0x03, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x2e, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, + 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x49, 0x50, 0x4d, 0x49, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x50, 0x4d, 0x49, + 0x2d, 0x46, 0x52, 0x55, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x01, 0x66, 0x61, 0x6b, 0x65, 0x0a, + }, +}; + +struct ff_dev { + struct fmc_device *fmc[FF_MAX_MEZZANINES]; + struct device dev; +}; + +static struct ff_dev *ff_current_dev; /* We have 1 carrier, 1 slot */ + +static int ff_reprogram(struct fmc_device *fmc, struct fmc_driver *drv, + char *gw) +{ + const struct firmware *fw; + int ret; + + if (!gw) { + /* program golden: success */ + fmc->flags &= ~FMC_DEVICE_HAS_CUSTOM; + fmc->flags |= FMC_DEVICE_HAS_GOLDEN; + return 0; + } + + dev_info(&fmc->dev, "reprogramming with %s\n", gw); + ret = request_firmware(&fw, gw, &fmc->dev); + if (ret < 0) { + dev_warn(&fmc->dev, "request firmware \"%s\": error %i\n", + gw, ret); + goto out; + } + fmc->flags &= ~FMC_DEVICE_HAS_GOLDEN; + fmc->flags |= FMC_DEVICE_HAS_CUSTOM; + +out: + release_firmware(fw); + return ret; +} + +static int ff_irq_request(struct fmc_device *fmc, irq_handler_t handler, + char *name, int flags) +{ + return -EOPNOTSUPP; +} + +/* FIXME: should also have some fake FMC GPIO mapping */ + + +/* + * This work function is called when we changed the eeprom. It removes the + * current fmc device and registers a new one, with different identifiers. + */ +static struct ff_dev *ff_dev_create(void); /* defined later */ + +static void ff_work_fn(struct work_struct *work) +{ + struct ff_dev *ff = ff_current_dev; + int ret; + + fmc_device_unregister_n(ff->fmc, ff_nr_dev); + device_unregister(&ff->dev); + ff_current_dev = NULL; + + ff = ff_dev_create(); + if (IS_ERR(ff)) { + pr_warning("%s: can't re-create FMC devices\n", __func__); + return; + } + ret = fmc_device_register_n(ff->fmc, ff_nr_dev); + if (ret < 0) { + dev_warn(&ff->dev, "can't re-register FMC devices\n"); + device_unregister(&ff->dev); + return; + } + + ff_current_dev = ff; +} + +static DECLARE_DELAYED_WORK(ff_work, ff_work_fn); + + +/* low-level i2c */ +static int ff_eeprom_read(struct fmc_device *fmc, uint32_t offset, + void *buf, size_t size) +{ + if (offset > FF_EEPROM_SIZE) + return -EINVAL; + if (offset + size > FF_EEPROM_SIZE) + size = FF_EEPROM_SIZE - offset; + memcpy(buf, fmc->eeprom + offset, size); + return size; +} + +static int ff_eeprom_write(struct fmc_device *fmc, uint32_t offset, + const void *buf, size_t size) +{ + if (offset > FF_EEPROM_SIZE) + return -EINVAL; + if (offset + size > FF_EEPROM_SIZE) + size = FF_EEPROM_SIZE - offset; + dev_info(&fmc->dev, "write_eeprom: offset %i, size %zi\n", + (int)offset, size); + memcpy(fmc->eeprom + offset, buf, size); + schedule_delayed_work(&ff_work, HZ * 2); /* remove, replug, in 2s */ + return size; +} + +/* i2c operations for fmc */ +static int ff_read_ee(struct fmc_device *fmc, int pos, void *data, int len) +{ + if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN)) + return -EOPNOTSUPP; + return ff_eeprom_read(fmc, pos, data, len); +} + +static int ff_write_ee(struct fmc_device *fmc, int pos, + const void *data, int len) +{ + if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN)) + return -EOPNOTSUPP; + return ff_eeprom_write(fmc, pos, data, len); +} + +/* readl and writel do not do anything. Don't waste RAM with "base" */ +static uint32_t ff_readl(struct fmc_device *fmc, int offset) +{ + return 0; +} + +static void ff_writel(struct fmc_device *fmc, uint32_t value, int offset) +{ + return; +} + +/* validate is useful so fmc-write-eeprom will not reprogram every 2 seconds */ +static int ff_validate(struct fmc_device *fmc, struct fmc_driver *drv) +{ + int i; + + if (!drv->busid_n) + return 0; /* everyhing is valid */ + for (i = 0; i < drv->busid_n; i++) + if (drv->busid_val[i] == fmc->device_id) + return i; + return -ENOENT; +} + + + +static struct fmc_operations ff_fmc_operations = { + .readl = ff_readl, + .writel = ff_writel, + .reprogram = ff_reprogram, + .irq_request = ff_irq_request, + .read_ee = ff_read_ee, + .write_ee = ff_write_ee, + .validate = ff_validate, +}; + +/* This device is kmalloced: release it */ +static void ff_dev_release(struct device *dev) +{ + struct ff_dev *ff = container_of(dev, struct ff_dev, dev); + kfree(ff); +} + +static struct fmc_device ff_template_fmc = { + .version = FMC_VERSION, + .owner = THIS_MODULE, + .carrier_name = "fake-fmc-carrier", + .device_id = 0xf001, /* fool */ + .eeprom_len = sizeof(ff_eeimg[0]), + .memlen = 0x1000, /* 4k, to show something */ + .op = &ff_fmc_operations, + .hwdev = NULL, /* filled at creation time */ + .flags = FMC_DEVICE_HAS_GOLDEN, +}; + +static struct ff_dev *ff_dev_create(void) +{ + struct ff_dev *ff; + struct fmc_device *fmc; + int i, ret; + + ff = kzalloc(sizeof(*ff), GFP_KERNEL); + if (!ff) + return ERR_PTR(-ENOMEM); + dev_set_name(&ff->dev, "fake-fmc-carrier"); + ff->dev.release = ff_dev_release; + + ret = device_register(&ff->dev); + if (ret < 0) { + put_device(&ff->dev); + return ERR_PTR(ret); + } + + /* Create fmc structures that refer to this new "hw" device */ + for (i = 0; i < ff_nr_dev; i++) { + fmc = kmemdup(&ff_template_fmc, sizeof(ff_template_fmc), + GFP_KERNEL); + fmc->hwdev = &ff->dev; + fmc->carrier_data = ff; + fmc->nr_slots = ff_nr_dev; + /* the following fields are different for each slot */ + fmc->eeprom = ff_eeimg[i]; + fmc->eeprom_addr = 0x50 + 2 * i; + fmc->slot_id = i; + ff->fmc[i] = fmc; + /* increment the identifier, each must be different */ + ff_template_fmc.device_id++; + } + return ff; +} + +/* init and exit */ +static int ff_init(void) +{ + struct ff_dev *ff; + const struct firmware *fw; + int i, len, ret = 0; + + /* Replicate the default eeprom for the max number of mezzanines */ + for (i = 1; i < FF_MAX_MEZZANINES; i++) + memcpy(ff_eeimg[i], ff_eeimg[0], sizeof(ff_eeimg[0])); + + if (ff_nr_eeprom > ff_nr_dev) + ff_nr_dev = ff_nr_eeprom; + + ff = ff_dev_create(); + if (IS_ERR(ff)) + return PTR_ERR(ff); + + /* If the user passed "eeprom=" as a parameter, fetch them */ + for (i = 0; i < ff_nr_eeprom; i++) { + if (!strlen(ff_eeprom[i])) + continue; + ret = request_firmware(&fw, ff_eeprom[i], &ff->dev); + if (ret < 0) { + dev_err(&ff->dev, "Mezzanine %i: can't load \"%s\" " + "(error %i)\n", i, ff_eeprom[i], -ret); + } else { + len = min_t(size_t, fw->size, (size_t)FF_EEPROM_SIZE); + memcpy(ff_eeimg[i], fw->data, len); + release_firmware(fw); + dev_info(&ff->dev, "Mezzanine %i: eeprom \"%s\"\n", i, + ff_eeprom[i]); + } + } + + ret = fmc_device_register_n(ff->fmc, ff_nr_dev); + if (ret) { + device_unregister(&ff->dev); + return ret; + } + ff_current_dev = ff; + return ret; +} + +static void ff_exit(void) +{ + if (ff_current_dev) { + fmc_device_unregister_n(ff_current_dev->fmc, ff_nr_dev); + device_unregister(&ff_current_dev->dev); + } + cancel_delayed_work_sync(&ff_work); +} + +module_init(ff_init); +module_exit(ff_exit); + +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3-70-g09d2 From 056d83f3c30c398f14eb879f1d1707e3a7808f4a Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:46 +0200 Subject: FMC: add a software mezzanine driver This simple do-nothing mezzanine driver shows how to write a mezzanine driver, that can also handle interrupts reported by the carrier. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- Documentation/fmc/00-INDEX | 3 ++ Documentation/fmc/fmc-trivial.txt | 17 ++++++ drivers/fmc/Kconfig | 7 +++ drivers/fmc/Makefile | 1 + drivers/fmc/fmc-trivial.c | 107 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 Documentation/fmc/fmc-trivial.txt create mode 100644 drivers/fmc/fmc-trivial.c (limited to 'drivers') diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 34df5cfc0c4..c22d6873fd0 100644 --- a/Documentation/fmc/00-INDEX +++ b/Documentation/fmc/00-INDEX @@ -27,3 +27,6 @@ identifiers.txt fmc-fakedev.txt - about drivers/fmc/fmc-fakedev.ko + +fmc-trivial.txt + - about drivers/fmc/fmc-trivial.ko diff --git a/Documentation/fmc/fmc-trivial.txt b/Documentation/fmc/fmc-trivial.txt new file mode 100644 index 00000000000..d1910bc6715 --- /dev/null +++ b/Documentation/fmc/fmc-trivial.txt @@ -0,0 +1,17 @@ +fmc-trivial +=========== + +The simple module fmc-trivial is just a simple client that registers an +interrupt handler. I used it to verify the basic mechanism of the FMC +bus and how interrupts worked. + +The module implements the generic FMC parameters, so it can program a +different gateware file in each card. The whole list of parameters it +accepts are: + +`busid=' +`gateware=' + Generic parameters. See mezzanine.txt + + +This driver is worth reading, in my opinion. diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig index 505e96b02a9..7eacef990be 100644 --- a/drivers/fmc/Kconfig +++ b/drivers/fmc/Kconfig @@ -25,4 +25,11 @@ config FMC_FAKEDEV that can be rewritten at run time and usef for matching mezzanines. +config FMC_TRIVIAL + tristate "FMC trivial mezzanine driver (software testing)" + help + This is a fake mezzanine driver, to show how FMC works and test it. + The driver also handles interrupts (we used it with a real carrier + before the mezzanines were produced) + endif # FMC diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index 9832b79976b..52624e6c0b1 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -8,3 +8,4 @@ fmc-y += fru-parse.o fmc-y += fmc-dump.o obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o +obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o diff --git a/drivers/fmc/fmc-trivial.c b/drivers/fmc/fmc-trivial.c new file mode 100644 index 00000000000..6c590f54c79 --- /dev/null +++ b/drivers/fmc/fmc-trivial.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * The software is provided "as is"; the copyright holders disclaim + * all warranties and liabilities, to the extent permitted by + * applicable law. + */ + +/* A trivial fmc driver that can load a gateware file and reports interrupts */ +#include +#include +#include +#include +#include + +static struct fmc_driver t_drv; /* initialized later */ + +static irqreturn_t t_handler(int irq, void *dev_id) +{ + struct fmc_device *fmc = dev_id; + + fmc->op->irq_ack(fmc); + dev_info(&fmc->dev, "received irq %i\n", irq); + return IRQ_HANDLED; +} + +static struct fmc_gpio t_gpio[] = { + { + .gpio = FMC_GPIO_IRQ(0), + .mode = GPIOF_DIR_IN, + .irqmode = IRQF_TRIGGER_RISING, + }, { + .gpio = FMC_GPIO_IRQ(1), + .mode = GPIOF_DIR_IN, + .irqmode = IRQF_TRIGGER_RISING, + } +}; + +static int t_probe(struct fmc_device *fmc) +{ + int ret; + int index = 0; + + if (fmc->op->validate) + index = fmc->op->validate(fmc, &t_drv); + if (index < 0) + return -EINVAL; /* not our device: invalid */ + + ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); + if (ret < 0) + return ret; + /* ignore error code of call below, we really don't care */ + fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); + + /* Reprogram, if asked to. ESRCH == no filename specified */ + ret = -ESRCH; + if (fmc->op->reprogram) + ret = fmc->op->reprogram(fmc, &t_drv, ""); + if (ret == -ESRCH) + ret = 0; + if (ret < 0) + fmc->op->irq_free(fmc); + + /* FIXME: reprogram LM32 too */ + return ret; +} + +static int t_remove(struct fmc_device *fmc) +{ + fmc->op->irq_free(fmc); + return 0; +} + +static struct fmc_driver t_drv = { + .version = FMC_VERSION, + .driver.name = KBUILD_MODNAME, + .probe = t_probe, + .remove = t_remove, + /* no table, as the current match just matches everything */ +}; + + /* We accept the generic parameters */ +FMC_PARAM_BUSID(t_drv); +FMC_PARAM_GATEWARE(t_drv); + +static int t_init(void) +{ + int ret; + + ret = fmc_driver_register(&t_drv); + return ret; +} + +static void t_exit(void) +{ + fmc_driver_unregister(&t_drv); +} + +module_init(t_init); +module_exit(t_exit); + +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3-70-g09d2 From 6007b1bd0f752a5c022f7944c65fb96c39d6db3d Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:56 +0200 Subject: FMC: add a driver to write mezzanine EEPROM This driver allows to reprogram the EEPROM in a mezzanine, to store its own identifiers during manufacturing or to save other useful data. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- Documentation/fmc/00-INDEX | 3 + Documentation/fmc/fmc-write-eeprom.txt | 125 +++++++++++++++++++++++ drivers/fmc/Kconfig | 8 ++ drivers/fmc/Makefile | 1 + drivers/fmc/fmc-write-eeprom.c | 176 +++++++++++++++++++++++++++++++++ 5 files changed, 313 insertions(+) create mode 100644 Documentation/fmc/fmc-write-eeprom.txt create mode 100644 drivers/fmc/fmc-write-eeprom.c (limited to 'drivers') diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index c22d6873fd0..177c3e4a951 100644 --- a/Documentation/fmc/00-INDEX +++ b/Documentation/fmc/00-INDEX @@ -30,3 +30,6 @@ fmc-fakedev.txt fmc-trivial.txt - about drivers/fmc/fmc-trivial.ko + +fmc-write-eeprom.txt + - about drivers/fmc/fmc-write-eeprom.ko diff --git a/Documentation/fmc/fmc-write-eeprom.txt b/Documentation/fmc/fmc-write-eeprom.txt new file mode 100644 index 00000000000..44a3bc678bf --- /dev/null +++ b/Documentation/fmc/fmc-write-eeprom.txt @@ -0,0 +1,125 @@ +fmc-write-eeprom +================ + +This module is designed to load a binary file from /lib/firmware and to +write it to the internal EEPROM of the mezzanine card. This driver uses +the `busid' generic parameter. + +Overwriting the EEPROM is not something you should do daily, and it is +expected to only happen during manufacturing. For this reason, the +module makes it unlikely for the random user to change a working EEPROM. + +The module takes the following measures: + + * It accepts a `file=' argument (within /lib/firmware) and if no + such argument is received, it doesn't write anything to EEPROM + (i.e. there is no default file name). + + * If the file name ends with `.bin' it is written verbatim starting + at offset 0. + + * If the file name ends with `.tlv' it is interpreted as + type-length-value (i.e., it allows writev(2)-like operation). + + * If the file name doesn't match any of the patterns above, it is + ignored and no write is performed. + + * Only cards listed with `busid=' are written to. If no busid is + specified, no programming is done (and the probe function of the + driver will fail). + + +Each TLV tuple is formatted in this way: the header is 5 bytes, +followed by data. The first byte is `w' for write, the next two bytes +represent the address, in little-endian byte order, and the next two +represent the data length, in little-endian order. The length does not +include the header (it is the actual number of bytes to be written). + +This is a real example: that writes 5 bytes at position 0x110: + + spusa.root# od -t x1 -Ax /lib/firmware/try.tlv + 000000 77 10 01 05 00 30 31 32 33 34 + 00000a + spusa.root# insmod /tmp/fmc-write-eeprom.ko busid=0x0200 file=try.tlv + [19983.391498] spec 0000:03:00.0: write 5 bytes at 0x0110 + [19983.414615] spec 0000:03:00.0: write_eeprom: success + +Please note that you'll most likely want to use SDBFS to build your +EEPROM image, at least if your mezzanines are being used in the White +Rabbit environment. For this reason the TLV format is not expected to +be used much and is not expected to be developed further. + +If you want to try reflashing fake EEPROM devices, you can use the +fmc-fakedev.ko module (see *note fmc-fakedev::). Whenever you change +the image starting at offset 0, it will deregister and register again +after two seconds. Please note, however, that if fmc-write-eeprom is +still loaded, the system will associate it to the new device, which +will be reprogrammed and thus will be unloaded after two seconds. The +following example removes the module after it reflashed fakedev the +first time. + + spusa.root# insmod fmc-fakedev.ko + [ 72.984733] fake-fmc: Manufacturer: fake-vendor + [ 72.989434] fake-fmc: Product name: fake-design-for-testing + spusa.root# insmod fmc-write-eeprom.ko busid=0 file=fdelay-eeprom.bin; \ + rmmod fmc-write-eeprom + [ 130.874098] fake-fmc: Matching a generic driver (no ID) + [ 130.887845] fake-fmc: programming 6155 bytes + [ 130.894567] fake-fmc: write_eeprom: success + [ 132.895794] fake-fmc: Manufacturer: CERN + [ 132.899872] fake-fmc: Product name: FmcDelay1ns4cha + + +Writing to the EEPROM +===================== + +Once you have created a binary file for your EEPROM, you can write it +to the storage medium using the fmc-write-eeprom (See *note +fmc-write-eeprom::, while relying on a carrier driver. The procedure +here shown here uses the SPEC driver +(`http://www.ohwr.org/projects/spec-sw'). + +The example assumes no driver is already loaded (actually, I unloaded +them by hand as everything loads automatically at boot time after you +installed the modules), and shows kernel messages together with +commands. Here the prompt is spusa.root# and two SPEC cards are plugged +in the system. + + spusa.root# insmod fmc.ko + spusa.root# insmod spec.ko + [13972.382818] spec 0000:02:00.0: probe for device 0002:0000 + [13972.392773] spec 0000:02:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes + [13972.591388] spec 0000:02:00.0: FPGA programming successful + [13972.883011] spec 0000:02:00.0: EEPROM has no FRU information + [13972.888719] spec 0000:02:00.0: No device_id filled, using index + [13972.894676] spec 0000:02:00.0: No mezzanine_name found + [13972.899863] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init + [13972.906578] spec 0000:04:00.0: probe for device 0004:0000 + [13972.916509] spec 0000:04:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes + [13973.115096] spec 0000:04:00.0: FPGA programming successful + [13973.401798] spec 0000:04:00.0: EEPROM has no FRU information + [13973.407474] spec 0000:04:00.0: No device_id filled, using index + [13973.413417] spec 0000:04:00.0: No mezzanine_name found + [13973.418600] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init + spusa.root# ls /sys/bus/fmc/devices + fmc-0000 fmc-0001 + spusa.root# insmod fmc-write-eeprom.ko busid=0x0200 file=fdelay-eeprom.bin + [14103.966259] spec 0000:02:00.0: Matching an generic driver (no ID) + [14103.975519] spec 0000:02:00.0: programming 6155 bytes + [14126.373762] spec 0000:02:00.0: write_eeprom: success + [14126.378770] spec 0000:04:00.0: Matching an generic driver (no ID) + [14126.384903] spec 0000:04:00.0: fmc_write_eeprom: no filename given: not programming + [14126.392600] fmc_write_eeprom: probe of fmc-0001 failed with error -2 + +Reading back the EEPROM +======================= + +In order to read back the binary content of the EEPROM of your +mezzanine device, the bus creates a read-only sysfs file called eeprom +for each mezzanine it knows about: + + spusa.root# cd /sys/bus/fmc/devices; ls -l */eeprom + -r--r--r-- 1 root root 8192 Apr 9 16:53 FmcDelay1ns4cha-f001/eeprom + -r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f002/eeprom + -r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f003/eeprom + -r--r--r-- 1 root root 8192 Apr 9 17:19 fmc-f004/eeprom diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig index 7eacef990be..2bb1953c968 100644 --- a/drivers/fmc/Kconfig +++ b/drivers/fmc/Kconfig @@ -32,4 +32,12 @@ config FMC_TRIVIAL The driver also handles interrupts (we used it with a real carrier before the mezzanines were produced) +config FMC_WRITE_EEPROM + tristate "FMC mezzanine driver to write I2C EEPROM" + help + This driver matches every mezzanine device and can write the + internal EEPROM of the PCB, using the firmware loader to get + its binary and the function carrier->reprogram to actually do it. + It is useful when the mezzanines are produced. + endif # FMC diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index 52624e6c0b1..13701fa6db7 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -9,3 +9,4 @@ fmc-y += fmc-dump.o obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o +obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o diff --git a/drivers/fmc/fmc-write-eeprom.c b/drivers/fmc/fmc-write-eeprom.c new file mode 100644 index 00000000000..2cc680dd604 --- /dev/null +++ b/drivers/fmc/fmc-write-eeprom.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include +#include +#include +#include +#include +#include + +/* + * This module uses the firmware loader to program the whole or part + * of the FMC eeprom. The meat is in the _run functions. However, no + * default file name is provided, to avoid accidental mishaps. Also, + * you must pass the busid argument + */ +static struct fmc_driver fwe_drv; + +FMC_PARAM_BUSID(fwe_drv); + +/* The "file=" is like the generic "gateware=" used elsewhere */ +static char *fwe_file[FMC_MAX_CARDS]; +static int fwe_file_n; +module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444); + +static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw, + int write) +{ + const uint8_t *p = fw->data; + int len = fw->size; + uint16_t thislen, thisaddr; + int err; + + /* format is: 'w' addr16 len16 data... */ + while (len > 5) { + thisaddr = get_unaligned_le16(p+1); + thislen = get_unaligned_le16(p+3); + if (p[0] != 'w' || thislen + 5 > len) { + dev_err(&fmc->dev, "invalid tlv at offset %ti\n", + p - fw->data); + return -EINVAL; + } + err = 0; + if (write) { + dev_info(&fmc->dev, "write %i bytes at 0x%04x\n", + thislen, thisaddr); + err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen); + } + if (err < 0) { + dev_err(&fmc->dev, "write failure @0x%04x\n", + thisaddr); + return err; + } + p += 5 + thislen; + len -= 5 + thislen; + } + if (write) + dev_info(&fmc->dev, "write_eeprom: success\n"); + return 0; +} + +static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw) +{ + int ret; + + dev_info(&fmc->dev, "programming %zi bytes\n", fw->size); + ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size); + if (ret < 0) { + dev_info(&fmc->dev, "write_eeprom: error %i\n", ret); + return ret; + } + dev_info(&fmc->dev, "write_eeprom: success\n"); + return 0; +} + +static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s) +{ + char *last4 = s + strlen(s) - 4; + int err; + + if (!strcmp(last4, ".bin")) + return fwe_run_bin(fmc, fw); + if (!strcmp(last4, ".tlv")) { + err = fwe_run_tlv(fmc, fw, 0); + if (!err) + err = fwe_run_tlv(fmc, fw, 1); + return err; + } + dev_err(&fmc->dev, "invalid file name \"%s\"\n", s); + return -EINVAL; +} + +/* + * Programming is done at probe time. Morever, only those listed with + * busid= are programmed. + * card is probed for, only one is programmed. Unfortunately, it's + * difficult to know in advance when probing the first card if others + * are there. + */ +int fwe_probe(struct fmc_device *fmc) +{ + int err, index = 0; + const struct firmware *fw; + struct device *dev = &fmc->dev; + char *s; + + if (!fwe_drv.busid_n) { + dev_err(dev, "%s: no busid passed, refusing all cards\n", + KBUILD_MODNAME); + return -ENODEV; + } + if (fmc->op->validate) + index = fmc->op->validate(fmc, &fwe_drv); + if (index < 0) { + pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME, + dev_name(dev)); + return -ENODEV; + } + if (index >= fwe_file_n) { + pr_err("%s: no filename for device index %i\n", + KBUILD_MODNAME, index); + return -ENODEV; + } + s = fwe_file[index]; + if (!s) { + pr_err("%s: no filename for \"%s\" not programming\n", + KBUILD_MODNAME, dev_name(dev)); + return -ENOENT; + } + err = request_firmware(&fw, s, dev); + if (err < 0) { + dev_err(&fmc->dev, "request firmware \"%s\": error %i\n", + s, err); + return err; + } + fwe_run(fmc, fw, s); + release_firmware(fw); + return 0; +} + +int fwe_remove(struct fmc_device *fmc) +{ + return 0; +} + +static struct fmc_driver fwe_drv = { + .version = FMC_VERSION, + .driver.name = KBUILD_MODNAME, + .probe = fwe_probe, + .remove = fwe_remove, + /* no table, as the current match just matches everything */ +}; + +static int fwe_init(void) +{ + int ret; + + ret = fmc_driver_register(&fwe_drv); + return ret; +} + +static void fwe_exit(void) +{ + fmc_driver_unregister(&fwe_drv); +} + +module_init(fwe_init); +module_exit(fwe_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 4debfe409b6e550032bfef9733e9f6f7c5613617 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:48:07 +0200 Subject: FMC: add a char-device mezzanine driver This driver exports the memory area associated with the mezzanine card as a misc device, so users can access registers. Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- Documentation/fmc/00-INDEX | 5 +- Documentation/fmc/fmc-chardev.txt | 64 +++++++++++++ drivers/fmc/Kconfig | 8 ++ drivers/fmc/Makefile | 1 + drivers/fmc/fmc-chardev.c | 197 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 Documentation/fmc/fmc-chardev.txt create mode 100644 drivers/fmc/fmc-chardev.c (limited to 'drivers') diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 177c3e4a951..431c69570f4 100644 --- a/Documentation/fmc/00-INDEX +++ b/Documentation/fmc/00-INDEX @@ -32,4 +32,7 @@ fmc-trivial.txt - about drivers/fmc/fmc-trivial.ko fmc-write-eeprom.txt - - about drivers/fmc/fmc-write-eeprom.ko + - about drivers/fmc/fmc-write-eeprom.ko + +fmc-chardev.txt + - about drivers/fmc/fmc-chardev.ko diff --git a/Documentation/fmc/fmc-chardev.txt b/Documentation/fmc/fmc-chardev.txt new file mode 100644 index 00000000000..d9ccb278e59 --- /dev/null +++ b/Documentation/fmc/fmc-chardev.txt @@ -0,0 +1,64 @@ +fmc-chardev +=========== + +This is a simple generic driver, that allows user access by means of a +character device (actually, one for each mezzanine it takes hold of). + +The char device is created as a misc device. Its name in /dev (as +created by udev) is the same name as the underlying FMC device. Thus, +the name can be a silly fmc-0000 look-alike if the device has no +identifiers nor bus_id, a more specific fmc-0400 if the device has a +bus-specific address but no associated name, or something like +fdelay-0400 if the FMC core can rely on both a mezzanine name and a bus +address. + +Currently the driver only supports read and write: you can lseek to the +desired address and read or write a register. + +The driver assumes all registers are 32-bit in size, and only accepts a +single read or write per system call. However, as a result of Unix read +and write semantics, users can simply fread or fwrite bigger areas in +order to dump or store bigger memory areas. + +There is currently no support for mmap, user-space interrupt management +and DMA buffers. They may be added in later versions, if the need +arises. + +The example below shows raw access to a SPEC card programmed with its +golden FPGA file, that features an SDB structure at offset 256 - i.e. +64 words. The mezzanine's EEPROM in this case is not programmed, so the +default name is fmc-, and there are two cards in the system: + + spusa.root# insmod fmc-chardev.ko + [ 1073.339332] spec 0000:02:00.0: Driver has no ID: matches all + [ 1073.345051] spec 0000:02:00.0: Created misc device "fmc-0200" + [ 1073.350821] spec 0000:04:00.0: Driver has no ID: matches all + [ 1073.356525] spec 0000:04:00.0: Created misc device "fmc-0400" + spusa.root# ls -l /dev/fmc* + crw------- 1 root root 10, 58 Nov 20 19:23 /dev/fmc-0200 + crw------- 1 root root 10, 57 Nov 20 19:23 /dev/fmc-0400 + spusa.root# dd bs=4 skip=64 count=1 if=/dev/fmc-0200 2> /dev/null | od -t x1z + 0000000 2d 42 44 53 >-BDS< + 0000004 + +The simple program tools/fmc-mem in this package can access an FMC char +device and read or write a word or a whole area. Actually, the program +is not specific to FMC at all, it just uses lseek, read and write. + +Its first argument is the device name, the second the offset, the third +(if any) the value to write and the optional last argument that must +begin with "+" is the number of bytes to read or write. In case of +repeated reading data is written to stdout; repeated writes read from +stdin and the value argument is ignored. + +The following examples show reading the SDB magic number and the first +SDB record from a SPEC device programmed with its golden image: + + spusa.root# ./fmc-mem /dev/fmc-0200 100 + 5344422d + spusa.root# ./fmc-mem /dev/fmc-0200 100 +40 | od -Ax -t x1z + 000000 2d 42 44 53 00 01 02 00 00 00 00 00 00 00 00 00 >-BDS............< + 000010 00 00 00 00 ff 01 00 00 00 00 00 00 51 06 00 00 >............Q...< + 000020 c9 42 a5 e6 02 00 00 00 11 05 12 20 2d 34 42 57 >.B......... -4BW< + 000030 73 6f 72 43 72 61 62 73 49 53 47 2d 00 20 20 20 >sorCrabsISG-. < + 000040 diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig index 2bb1953c968..c01cf45bc3d 100644 --- a/drivers/fmc/Kconfig +++ b/drivers/fmc/Kconfig @@ -40,4 +40,12 @@ config FMC_WRITE_EEPROM its binary and the function carrier->reprogram to actually do it. It is useful when the mezzanines are produced. +config FMC_CHARDEV + tristate "FMC mezzanine driver that registers a char device" + help + This driver matches every mezzanine device and allows user + space to read and write registers using a char device. It + can be used to write user-space drivers, or just get + aquainted with a mezzanine before writing its specific driver. + endif # FMC diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index 13701fa6db7..b9452919739 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -10,3 +10,4 @@ fmc-y += fmc-dump.o obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o +obj-$(CONFIG_FMC_CHARDEV) += fmc-chardev.o diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c new file mode 100644 index 00000000000..b0710393ede --- /dev/null +++ b/drivers/fmc/fmc-chardev.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2012 CERN (www.cern.ch) + * Author: Alessandro Rubini + * + * Released according to the GNU GPL, version 2 or any later version. + * + * This work is part of the White Rabbit project, a research effort led + * by CERN, the European Institute for Nuclear Research. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(fc_devices); +static DEFINE_SPINLOCK(fc_lock); + +struct fc_instance { + struct list_head list; + struct fmc_device *fmc; + struct miscdevice misc; +}; + +/* at open time, we must identify our device */ +static int fc_open(struct inode *ino, struct file *f) +{ + struct fmc_device *fmc; + struct fc_instance *fc; + int minor = iminor(ino); + + list_for_each_entry(fc, &fc_devices, list) + if (fc->misc.minor == minor) + break; + if (fc->misc.minor != minor) + return -ENODEV; + fmc = fc->fmc; + if (try_module_get(fmc->owner) == 0) + return -ENODEV; + + f->private_data = fmc; + return 0; +} + +static int fc_release(struct inode *ino, struct file *f) +{ + struct fmc_device *fmc = f->private_data; + module_put(fmc->owner); + return 0; +} + +/* read and write are simple after the default llseek has been used */ +static ssize_t fc_read(struct file *f, char __user *buf, size_t count, + loff_t *offp) +{ + struct fmc_device *fmc = f->private_data; + unsigned long addr; + uint32_t val; + + if (count < sizeof(val)) + return -EINVAL; + count = sizeof(val); + + addr = *offp; + if (addr > fmc->memlen) + return -ESPIPE; /* Illegal seek */ + val = fmc_readl(fmc, addr); + if (copy_to_user(buf, &val, count)) + return -EFAULT; + *offp += count; + return count; +} + +static ssize_t fc_write(struct file *f, const char __user *buf, size_t count, + loff_t *offp) +{ + struct fmc_device *fmc = f->private_data; + unsigned long addr; + uint32_t val; + + if (count < sizeof(val)) + return -EINVAL; + count = sizeof(val); + + addr = *offp; + if (addr > fmc->memlen) + return -ESPIPE; /* Illegal seek */ + if (copy_from_user(&val, buf, count)) + return -EFAULT; + fmc_writel(fmc, val, addr); + *offp += count; + return count; +} + +static const struct file_operations fc_fops = { + .owner = THIS_MODULE, + .open = fc_open, + .release = fc_release, + .llseek = generic_file_llseek, + .read = fc_read, + .write = fc_write, +}; + + +/* Device part .. */ +static int fc_probe(struct fmc_device *fmc); +static int fc_remove(struct fmc_device *fmc); + +static struct fmc_driver fc_drv = { + .version = FMC_VERSION, + .driver.name = KBUILD_MODNAME, + .probe = fc_probe, + .remove = fc_remove, + /* no table: we want to match everything */ +}; + +/* We accept the generic busid parameter */ +FMC_PARAM_BUSID(fc_drv); + +/* probe and remove must allocate and release a misc device */ +static int fc_probe(struct fmc_device *fmc) +{ + int ret; + int index = 0; + + struct fc_instance *fc; + + if (fmc->op->validate) + index = fmc->op->validate(fmc, &fc_drv); + if (index < 0) + return -EINVAL; /* not our device: invalid */ + + /* Create a char device: we want to create it anew */ + fc = kzalloc(sizeof(*fc), GFP_KERNEL); + fc->fmc = fmc; + fc->misc.minor = MISC_DYNAMIC_MINOR; + fc->misc.fops = &fc_fops; + fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL); + + spin_lock(&fc_lock); + ret = misc_register(&fc->misc); + if (ret < 0) { + kfree(fc->misc.name); + kfree(fc); + } else { + list_add(&fc->list, &fc_devices); + } + spin_unlock(&fc_lock); + dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n", + fc->misc.name); + return ret; +} + +static int fc_remove(struct fmc_device *fmc) +{ + struct fc_instance *fc; + + list_for_each_entry(fc, &fc_devices, list) + if (fc->fmc == fmc) + break; + if (fc->fmc != fmc) { + dev_err(&fmc->dev, "remove called but not found\n"); + return -ENODEV; + } + + spin_lock(&fc_lock); + list_del(&fc->list); + misc_deregister(&fc->misc); + kfree(fc->misc.name); + kfree(fc); + spin_unlock(&fc_lock); + + return 0; +} + + +static int fc_init(void) +{ + int ret; + + ret = fmc_driver_register(&fc_drv); + return ret; +} + +static void fc_exit(void) +{ + fmc_driver_unregister(&fc_drv); +} + +module_init(fc_init); +module_exit(fc_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 05c3e0bb5629b897b0459e4bfb1b93d729033b99 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 18 Jun 2013 22:35:58 +0200 Subject: UIO: allow binding uio_pdrv_genirq.c to devices using command line option This adds ability to bind uio driver to given open firmware device using command line option. Thus, userspace driver can be developed and used without modifying the kernel. Signed-off-by: Pavel Machek Tested-by: Detlev Zundel Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index c122bca669b..960809f010d 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -263,10 +263,14 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { }; #ifdef CONFIG_OF -static const struct of_device_id uio_of_genirq_match[] = { - { /* empty for now */ }, +static struct of_device_id uio_of_genirq_match[] = { + { /* This is filled with module_parm */ }, + { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, uio_of_genirq_match); + +module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0); +MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio"); #else # define uio_of_genirq_match NULL #endif -- cgit v1.2.3-70-g09d2 From e3a3c3a205554e564751cd9c0276b2af813d7a92 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 18 Jun 2013 22:34:53 +0200 Subject: UIO: fix uio_pdrv_genirq with device tree but no interrupt If device is initialized from device tree, but has no interrupt assigned, uio will still try to request and interrupt old way, fails, and fails registration. This is wrong; don't try initializing irq using platform data if device tree is available. Simplified code based on suggestion by Grant Likely. Fixed memory leak in "irq can not be registered" error path. Signed-off-by: Pavel Machek Reported-by: Detlev Zundel Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 960809f010d..97415492779 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -103,24 +103,16 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) int i; if (pdev->dev.of_node) { - int irq; - /* alloc uioinfo for one device */ uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL); if (!uioinfo) { ret = -ENOMEM; dev_err(&pdev->dev, "unable to kmalloc\n"); - goto bad2; + return ret; } uioinfo->name = pdev->dev.of_node->name; uioinfo->version = "devicetree"; - /* Multiple IRQs are not supported */ - irq = platform_get_irq(pdev, 0); - if (irq == -ENXIO) - uioinfo->irq = UIO_IRQ_NONE; - else - uioinfo->irq = irq; } if (!uioinfo || !uioinfo->name || !uioinfo->version) { @@ -148,12 +140,15 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) if (!uioinfo->irq) { ret = platform_get_irq(pdev, 0); - if (ret < 0) { + uioinfo->irq = ret; + if (ret == -ENXIO && pdev->dev.of_node) + uioinfo->irq = UIO_IRQ_NONE; + else if (ret < 0) { dev_err(&pdev->dev, "failed to get IRQ\n"); - goto bad0; + goto bad1; } - uioinfo->irq = ret; } + uiomem = &uioinfo->mem[0]; for (i = 0; i < pdev->num_resources; ++i) { @@ -206,19 +201,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) ret = uio_register_device(&pdev->dev, priv->uioinfo); if (ret) { dev_err(&pdev->dev, "unable to register uio device\n"); - goto bad1; + goto bad2; } platform_set_drvdata(pdev, priv); return 0; + bad2: + pm_runtime_disable(&pdev->dev); bad1: kfree(priv); - pm_runtime_disable(&pdev->dev); bad0: /* kfree uioinfo for OF */ if (pdev->dev.of_node) kfree(uioinfo); - bad2: return ret; } -- cgit v1.2.3-70-g09d2 From e42d50baf43120a78985f13f6e9c8f92fae091c2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Jun 2013 19:01:01 +0300 Subject: FMC: NULL dereference on allocation failure If we don't allocate "arr" then the cleanup path will dereference it and oops. Signed-off-by: Dan Carpenter Acked-by: Alessandro Rubini Signed-off-by: Greg Kroah-Hartman --- drivers/fmc/fmc-sdb.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c index 74fb326f4af..79adc39221e 100644 --- a/drivers/fmc/fmc-sdb.c +++ b/drivers/fmc/fmc-sdb.c @@ -46,16 +46,17 @@ static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc, onew = __sdb_rd(fmc, sdb_addr + 4, convert); n = __be16_to_cpu(*(uint16_t *)&onew); arr = kzalloc(sizeof(*arr), GFP_KERNEL); - if (arr) { - arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL); - arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL); - } - if (!arr || !arr->record || !arr->subtree) { + if (!arr) + return ERR_PTR(-ENOMEM); + arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL); + arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL); + if (!arr->record || !arr->subtree) { kfree(arr->record); kfree(arr->subtree); kfree(arr); return ERR_PTR(-ENOMEM); } + arr->len = n; arr->level = level; arr->fmc = fmc; -- cgit v1.2.3-70-g09d2 From c2955da0e101c3432ae21945e5d77786adf1f094 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Jun 2013 12:49:30 +0200 Subject: fmc: avoid readl/writel namespace conflict The use of the 'readl' and 'writel' identifiers here causes build errors on architectures where those are macros. This renames the fields to read32/write32 to avoid the problem. Reported-by: kbuild test robot Signed-off-by: Arnd Bergmann Acked-by: Alessandro Rubini Signed-off-by: Greg Kroah-Hartman --- drivers/fmc/fmc-fakedev.c | 4 ++-- include/linux/fmc.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/fmc/fmc-fakedev.c b/drivers/fmc/fmc-fakedev.c index bec94ac0764..941d0930969 100644 --- a/drivers/fmc/fmc-fakedev.c +++ b/drivers/fmc/fmc-fakedev.c @@ -232,8 +232,8 @@ static int ff_validate(struct fmc_device *fmc, struct fmc_driver *drv) static struct fmc_operations ff_fmc_operations = { - .readl = ff_readl, - .writel = ff_writel, + .read32 = ff_readl, + .write32 = ff_writel, .reprogram = ff_reprogram, .irq_request = ff_irq_request, .read_ee = ff_read_ee, diff --git a/include/linux/fmc.h b/include/linux/fmc.h index a3c49850398..a5f0aa5c2a8 100644 --- a/include/linux/fmc.h +++ b/include/linux/fmc.h @@ -129,8 +129,8 @@ struct fmc_gpio { * the exception. */ struct fmc_operations { - uint32_t (*readl)(struct fmc_device *fmc, int offset); - void (*writel)(struct fmc_device *fmc, uint32_t value, int offset); + uint32_t (*read32)(struct fmc_device *fmc, int offset); + void (*write32)(struct fmc_device *fmc, uint32_t value, int offset); int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv); int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw); int (*irq_request)(struct fmc_device *fmc, irq_handler_t h, @@ -194,14 +194,14 @@ struct fmc_device { */ static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset) { - if (unlikely(fmc->op->readl)) - return fmc->op->readl(fmc, offset); + if (unlikely(fmc->op->read32)) + return fmc->op->read32(fmc, offset); return readl(fmc->fpga_base + offset); } static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off) { - if (unlikely(fmc->op->writel)) - fmc->op->writel(fmc, val, off); + if (unlikely(fmc->op->write32)) + fmc->op->write32(fmc, val, off); else writel(val, fmc->fpga_base + off); } -- cgit v1.2.3-70-g09d2 From 4640a3f2bff64b808bdedadcddf882aa4606f374 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 20 Jun 2013 11:10:18 +0300 Subject: FMC: fix error handling in probe() function The call to kzalloc() wasn't checked. The dev_info() message dereferenced freed memory on error. Signed-off-by: Dan Carpenter Acked-by: Alessandro Rubini Signed-off-by: Greg Kroah-Hartman --- drivers/fmc/fmc-chardev.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c index b0710393ede..cc031db2d2a 100644 --- a/drivers/fmc/fmc-chardev.c +++ b/drivers/fmc/fmc-chardev.c @@ -136,6 +136,8 @@ static int fc_probe(struct fmc_device *fmc) /* Create a char device: we want to create it anew */ fc = kzalloc(sizeof(*fc), GFP_KERNEL); + if (!fc) + return -ENOMEM; fc->fmc = fmc; fc->misc.minor = MISC_DYNAMIC_MINOR; fc->misc.fops = &fc_fops; @@ -143,15 +145,18 @@ static int fc_probe(struct fmc_device *fmc) spin_lock(&fc_lock); ret = misc_register(&fc->misc); - if (ret < 0) { - kfree(fc->misc.name); - kfree(fc); - } else { - list_add(&fc->list, &fc_devices); - } + if (ret < 0) + goto err_unlock; + list_add(&fc->list, &fc_devices); spin_unlock(&fc_lock); dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n", fc->misc.name); + return 0; + +err_unlock: + spin_unlock(&fc_lock); + kfree(fc->misc.name); + kfree(fc); return ret; } -- cgit v1.2.3-70-g09d2 From 0686ab7a7d05cda3373e311a49b2478bd4f03c5d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 19 Jun 2013 10:42:35 +0800 Subject: vme: vme_tsi148.c: fix error return code in tsi148_probe() Fix to return a negative error code in the tsi148_crcsr_init() error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_tsi148.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 0bb512cfad7..94c892f27be 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -2582,7 +2582,8 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", err_chk ? "enabled" : "disabled"); - if (tsi148_crcsr_init(tsi148_bridge, pdev)) { + retval = tsi148_crcsr_init(tsi148_bridge, pdev); + if (retval) { dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); goto err_crcsr; } -- cgit v1.2.3-70-g09d2 From e91e84fa4cfeb67a9a096f1adaa1a1a692474724 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 20 Jun 2013 12:58:57 +0800 Subject: drivers: hv: check interrupt mask before read_index This patches add a read barriers to force the driver to check the interrupt mask before read_index. Otherwise we may lost a kick to host. Cc: K. Y. Srinivasan Cc: Haiyang Zhang Signed-off-by: Jason Wang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 791f45dfc85..26c93cf9f6b 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -75,6 +75,8 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) if (rbi->ring_buffer->interrupt_mask) return false; + /* check interrupt_mask before read_index */ + rmb(); /* * This is the only case we need to signal when the * ring transitions from being empty to non-empty. -- cgit v1.2.3-70-g09d2 From 2608fb653103419ac163206ff6d51b7b6528e2d9 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jun 2013 11:28:10 +0800 Subject: drivers: hv: allocate synic structures before hv_synic_init() We currently allocate synic structures in hv_sync_init(), but there's no way for the driver to know about the allocation failure and it may continue to use the uninitialized pointers. Solve this by introducing helpers for allocating and freeing and doing the allocation before the on_each_cpu() call in vmbus_bus_init(). Cc: Haiyang Zhang Signed-off-by: Jason Wang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 85 +++++++++++++++++++++++++++++------------------ drivers/hv/hyperv_vmbus.h | 4 +++ drivers/hv/vmbus_drv.c | 8 +++-- 3 files changed, 63 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae4923756d9..88f4096fa07 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -265,6 +265,59 @@ u16 hv_signal_event(void *con_id) return status; } + +int hv_synic_alloc(void) +{ + size_t size = sizeof(struct tasklet_struct); + int cpu; + + for_each_online_cpu(cpu) { + hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC); + if (hv_context.event_dpc[cpu] == NULL) { + pr_err("Unable to allocate event dpc\n"); + goto err; + } + tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu); + + hv_context.synic_message_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_message_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC message page\n"); + goto err; + } + + hv_context.synic_event_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_event_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC event page\n"); + goto err; + } + } + + return 0; +err: + return -ENOMEM; +} + +void hv_synic_free_cpu(int cpu) +{ + kfree(hv_context.event_dpc[cpu]); + if (hv_context.synic_message_page[cpu]) + free_page((unsigned long)hv_context.synic_event_page[cpu]); + if (hv_context.synic_message_page[cpu]) + free_page((unsigned long)hv_context.synic_message_page[cpu]); +} + +void hv_synic_free(void) +{ + int cpu; + + for_each_online_cpu(cpu) + hv_synic_free_cpu(cpu); +} + /* * hv_synic_init - Initialize the Synthethic Interrupt Controller. * @@ -289,30 +342,6 @@ void hv_synic_init(void *arg) /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); - hv_context.event_dpc[cpu] = kmalloc(sizeof(struct tasklet_struct), - GFP_ATOMIC); - if (hv_context.event_dpc[cpu] == NULL) { - pr_err("Unable to allocate event dpc\n"); - goto cleanup; - } - tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu); - - hv_context.synic_message_page[cpu] = - (void *)get_zeroed_page(GFP_ATOMIC); - - if (hv_context.synic_message_page[cpu] == NULL) { - pr_err("Unable to allocate SYNIC message page\n"); - goto cleanup; - } - - hv_context.synic_event_page[cpu] = - (void *)get_zeroed_page(GFP_ATOMIC); - - if (hv_context.synic_event_page[cpu] == NULL) { - pr_err("Unable to allocate SYNIC event page\n"); - goto cleanup; - } - /* Setup the Synic's message page */ rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); simp.simp_enabled = 1; @@ -355,14 +384,6 @@ void hv_synic_init(void *arg) rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); hv_context.vp_index[cpu] = (u32)vp_index; return; - -cleanup: - if (hv_context.synic_event_page[cpu]) - free_page((unsigned long)hv_context.synic_event_page[cpu]); - - if (hv_context.synic_message_page[cpu]) - free_page((unsigned long)hv_context.synic_message_page[cpu]); - return; } /* diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 12f2f9e989f..d84918fe19a 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -527,6 +527,10 @@ extern int hv_post_message(union hv_connection_id connection_id, extern u16 hv_signal_event(void *con_id); +extern int hv_synic_alloc(void); + +extern void hv_synic_free(void); + extern void hv_synic_init(void *irqarg); extern void hv_synic_cleanup(void *arg); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 4004e54ef05..a2464bf07c4 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -563,6 +563,9 @@ static int vmbus_bus_init(int irq) */ hv_register_vmbus_handler(irq, vmbus_isr); + ret = hv_synic_alloc(); + if (ret) + goto err_alloc; /* * Initialize the per-cpu interrupt state and * connect to the host. @@ -570,13 +573,14 @@ static int vmbus_bus_init(int irq) on_each_cpu(hv_synic_init, NULL, 1); ret = vmbus_connect(); if (ret) - goto err_irq; + goto err_alloc; vmbus_request_offers(); return 0; -err_irq: +err_alloc: + hv_synic_free(); free_irq(irq, hv_acpi_dev); err_unregister: -- cgit v1.2.3-70-g09d2 From 585d98e00ba7a5e2abe65f7a1eff631cb612289b Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 21 Jun 2013 15:01:05 +0200 Subject: char: misc: assign file->private_data in all cases In fa1f68db6ca ("drivers: misc: pass miscdevice pointer via file private data"), the misc driver infrastructure was changed to assigned file->private_data as a pointer to the 'struct miscdevice' that corresponds to the device being opened. However, this assignment was only done when the misc driver was declaring a driver-specific ->open() operation in its file_operations. This doesn't make sense, as the driver may not necessarily have a custom ->open() operation, and might still be interested in having file->private_data properly set for use in its ->read() and write() operations. Therefore, we move the assignment of file->private_data outside of the condition that tests whether a driver-specific ->open() operation was defined. Signed-off-by: Thomas Petazzoni Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 190d4423653..fd504d35859 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -143,8 +143,8 @@ static int misc_open(struct inode * inode, struct file * file) err = 0; old_fops = file->f_op; file->f_op = new_fops; + file->private_data = c; if (file->f_op->open) { - file->private_data = c; err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); -- cgit v1.2.3-70-g09d2 From 077797117dfa209717e1f3f1416c1101c0e047f0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Jun 2013 10:02:47 +0530 Subject: drivers: uio_dmem_genirq: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Acked-by: Damian Hobson-Garcia Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 252434c9ea9..125d0e5a688 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -336,8 +336,6 @@ static const struct of_device_id uio_of_genirq_match[] = { { /* empty for now */ }, }; MODULE_DEVICE_TABLE(of, uio_of_genirq_match); -#else -# define uio_of_genirq_match NULL #endif static struct platform_driver uio_dmem_genirq = { @@ -347,7 +345,7 @@ static struct platform_driver uio_dmem_genirq = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &uio_dmem_genirq_dev_pm_ops, - .of_match_table = uio_of_genirq_match, + .of_match_table = of_match_ptr(uio_of_genirq_match), }, }; -- cgit v1.2.3-70-g09d2 From 34cb27528398738bea94852b99ef8fb05944ec41 Mon Sep 17 00:00:00 2001 From: Vitalii Demianets Date: Thu, 20 Jun 2013 16:36:00 +0300 Subject: UIO: Fix concurrency issue In a SMP case there was a race condition issue between uio_pdrv_genirq_irqcontrol() running on one CPU and irq handler on another CPU. Fix it by spin_locking shared resources access inside irq handler. Also: - Change disable_irq to disable_irq_nosync to avoid deadlock, because disable_irq waits for the completion of the irq handler; - Change atomic bit-manipulation routines to their non-atomic counterparts as we already are guarding the code by spinlock. Signed-off-by: Vitalii Demianets Reviewed-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 97415492779..bcd72f3a62c 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -37,6 +37,11 @@ struct uio_pdrv_genirq_platdata { struct platform_device *pdev; }; +/* Bits in uio_pdrv_genirq_platdata.flags */ +enum { + UIO_IRQ_DISABLED = 0, +}; + static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode) { struct uio_pdrv_genirq_platdata *priv = info->priv; @@ -63,8 +68,10 @@ static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info) * remember the state so we can allow user space to enable it later. */ - if (!test_and_set_bit(0, &priv->flags)) + spin_lock(&priv->lock); + if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags)) disable_irq_nosync(irq); + spin_unlock(&priv->lock); return IRQ_HANDLED; } @@ -78,16 +85,17 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on) * in the interrupt controller, but keep track of the * state to prevent per-irq depth damage. * - * Serialize this operation to support multiple tasks. + * Serialize this operation to support multiple tasks and concurrency + * with irq handler on SMP systems. */ spin_lock_irqsave(&priv->lock, flags); if (irq_on) { - if (test_and_clear_bit(0, &priv->flags)) + if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags)) enable_irq(dev_info->irq); } else { - if (!test_and_set_bit(0, &priv->flags)) - disable_irq(dev_info->irq); + if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags)) + disable_irq_nosync(dev_info->irq); } spin_unlock_irqrestore(&priv->lock, flags); -- cgit v1.2.3-70-g09d2 From 21767546e955c3c1705387ca4548db812382fe08 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 09:36:59 +0300 Subject: mei: move mei_cl_irq_write_complete to client.c mei_cl_irq_write_complete operates on a client so move it to client.c Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/mei/client.h | 3 +++ drivers/misc/mei/interrupt.c | 59 ------------------------------------------- 3 files changed, 63 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c2534ca5c6f..788f6478b4a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -681,6 +681,66 @@ err: return rets; } +/** + * mei_cl_irq_write_complete - write a message to device + * from the interrupt thread context + * + * @cl: client + * @cb: callback block. + * @slots: free slots. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise error. + */ +int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list) +{ + struct mei_device *dev = cl->dev; + struct mei_msg_hdr mei_hdr; + size_t len = cb->request_buffer.size - cb->buf_idx; + u32 msg_slots = mei_data2slots(len); + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + + if (*slots >= msg_slots) { + mei_hdr.length = len; + mei_hdr.msg_complete = 1; + /* Split the message only if we can write the whole host buffer */ + } else if (*slots == dev->hbuf_depth) { + msg_slots = *slots; + len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); + mei_hdr.length = len; + mei_hdr.msg_complete = 0; + } else { + /* wait for next time the host buffer is empty */ + return 0; + } + + dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", + cb->request_buffer.size, cb->buf_idx); + dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); + + *slots -= msg_slots; + if (mei_write_message(dev, &mei_hdr, + cb->request_buffer.data + cb->buf_idx)) { + cl->status = -ENODEV; + list_move_tail(&cb->list, &cmpl_list->list); + return -ENODEV; + } + + cl->status = 0; + cb->buf_idx += mei_hdr.length; + if (mei_hdr.msg_complete) { + if (mei_cl_flow_ctrl_reduce(cl)) + return -ENODEV; + list_move_tail(&cb->list, &dev->write_waiting_list.list); + } + + return 0; +} + /** * mei_cl_write - submit a write cb to mei device assumes device_lock is locked diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 7dc2af7b6fb..26b157d8bad 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -89,6 +89,9 @@ int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); int mei_cl_read_start(struct mei_cl *cl, size_t length); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); +int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, + s32 *slots, struct mei_cl_cb *cmpl_list); + void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); void mei_host_client_init(struct work_struct *work); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 7d9509a094e..4b59cb742de 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -282,65 +282,6 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } -/** - * mei_cl_irq_write_complete - write messages to device. - * - * @cl: client - * @cb: callback block. - * @slots: free slots. - * @cmpl_list: complete list. - * - * returns 0, OK; otherwise, error. - */ -static int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) -{ - struct mei_device *dev = cl->dev; - struct mei_msg_hdr mei_hdr; - size_t len = cb->request_buffer.size - cb->buf_idx; - u32 msg_slots = mei_data2slots(len); - - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - - if (*slots >= msg_slots) { - mei_hdr.length = len; - mei_hdr.msg_complete = 1; - /* Split the message only if we can write the whole host buffer */ - } else if (*slots == dev->hbuf_depth) { - msg_slots = *slots; - len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); - mei_hdr.length = len; - mei_hdr.msg_complete = 0; - } else { - /* wait for next time the host buffer is empty */ - return 0; - } - - dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", - cb->request_buffer.size, cb->buf_idx); - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); - - *slots -= msg_slots; - if (mei_write_message(dev, &mei_hdr, - cb->request_buffer.data + cb->buf_idx)) { - cl->status = -ENODEV; - list_move_tail(&cb->list, &cmpl_list->list); - return -ENODEV; - } - - - cl->status = 0; - cb->buf_idx += mei_hdr.length; - if (mei_hdr.msg_complete) { - if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; - list_move_tail(&cb->list, &dev->write_waiting_list.list); - } - - return 0; -} /** * mei_irq_read_handler - bottom half read routine after ISR to -- cgit v1.2.3-70-g09d2 From 4dfaa9f7020b1ff4bf87899f4797d2efd76e80fd Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 09:37:00 +0300 Subject: mei: do not override a client writing state when buffering when we buffer write request we should not switch to MEI_WRITING since this will override MEI_WRITE_COMPLETE state of preceding write Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 788f6478b4a..11a465a2589 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -731,7 +731,9 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, } cl->status = 0; + cl->writing_state = MEI_WRITING; cb->buf_idx += mei_hdr.length; + if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) return -ENODEV; @@ -783,7 +785,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) cb->buf_idx = 0; /* unseting complete will enqueue the cb for write */ mei_hdr.msg_complete = 0; - cl->writing_state = MEI_WRITING; rets = buf->size; goto out; } -- cgit v1.2.3-70-g09d2 From 206ecfc21121aa272f3f9fa23e1ed252a12e8a5c Mon Sep 17 00:00:00 2001 From: Frode Isaksen Date: Sun, 23 Jun 2013 09:37:01 +0300 Subject: mei: mei_cl_connect: don't multiply the timeout twice MEI_CL_CONNECT_TIMEOUT is the timeout in seconds to wait for a response in mei_cl_connect. The value was converted to jiffies using mei_secs_to_jiffies helper function and assigned to a local variable which is by mistake again multiplied by HZ Signed-off-by: Frode Isaksen Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 11a465a2589..21d3f5aa835 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -485,7 +485,6 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) { struct mei_device *dev; struct mei_cl_cb *cb; - long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT); int rets; if (WARN_ON(!cl || !cl->dev)) @@ -518,7 +517,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) rets = wait_event_timeout(dev->wait_recvd_msg, (cl->state == MEI_FILE_CONNECTED || cl->state == MEI_FILE_DISCONNECTED), - timeout * HZ); + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); if (cl->state != MEI_FILE_CONNECTED) { -- cgit v1.2.3-70-g09d2 From c20c68d535409f2ff000415d5e0578529c016521 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 10:42:49 +0300 Subject: mei: check if the hardware reset succeeded The hw may have multiple steps for resetting so we need to check if it has really succeeded. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 3 ++- drivers/misc/mei/init.c | 8 +++++++- drivers/misc/mei/mei_dev.h | 6 +++--- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 822170f0034..e4f8dec4dc3 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -171,7 +171,7 @@ static void mei_me_hw_reset_release(struct mei_device *dev) * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. */ -static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable) +static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) { struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); @@ -191,6 +191,7 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable) mei_me_hw_reset_release(dev); dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw)); + return 0; } /** diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 79e9e1c3056..15253886f37 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -132,13 +132,19 @@ EXPORT_SYMBOL_GPL(mei_start); void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; + int ret; unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); - mei_hw_reset(dev, interrupts_enabled); + ret = mei_hw_reset(dev, interrupts_enabled); + if (ret) { + dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); + interrupts_enabled = false; + dev->dev_state = MEI_DEV_DISABLED; + } dev->hbm_state = MEI_HBM_IDLE; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 80a90319fc2..1aa499782ab 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -233,7 +233,7 @@ struct mei_hw_ops { bool (*host_is_ready) (struct mei_device *dev); bool (*hw_is_ready) (struct mei_device *dev); - void (*hw_reset) (struct mei_device *dev, bool enable); + int (*hw_reset) (struct mei_device *dev, bool enable); int (*hw_start) (struct mei_device *dev); void (*hw_config) (struct mei_device *dev); @@ -539,9 +539,9 @@ static inline void mei_hw_config(struct mei_device *dev) { dev->ops->hw_config(dev); } -static inline void mei_hw_reset(struct mei_device *dev, bool enable) +static inline int mei_hw_reset(struct mei_device *dev, bool enable) { - dev->ops->hw_reset(dev, enable); + return dev->ops->hw_reset(dev, enable); } static inline void mei_hw_start(struct mei_device *dev) -- cgit v1.2.3-70-g09d2 From 9049f7932109f3fe189ffa8028f2e23f06a9bd0b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 22:49:04 +0300 Subject: mei: check whether hw start has succeeded hw start may fail therefore the reset flow has to check for the return value Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/init.c | 7 ++++++- drivers/misc/mei/mei_dev.h | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 15253886f37..6fc573cef17 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -181,7 +181,12 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) return; } - mei_hw_start(dev); + ret = mei_hw_start(dev); + if (ret) { + dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n"); + dev->dev_state = MEI_DEV_DISABLED; + return; + } dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); /* link is established * start sending messages. */ diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 1aa499782ab..7b918b2fb89 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -544,9 +544,9 @@ static inline int mei_hw_reset(struct mei_device *dev, bool enable) return dev->ops->hw_reset(dev, enable); } -static inline void mei_hw_start(struct mei_device *dev) +static inline int mei_hw_start(struct mei_device *dev) { - dev->ops->hw_start(dev); + return dev->ops->hw_start(dev); } static inline void mei_clear_interrupts(struct mei_device *dev) -- cgit v1.2.3-70-g09d2 From cee4fbd6cd7608f6dad7539f39e9281125702a16 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 25 Jun 2013 09:59:52 +0530 Subject: drivers: uio_pdrv_genirq: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index bcd72f3a62c..4eb8eaf71be 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -271,11 +271,8 @@ static struct of_device_id uio_of_genirq_match[] = { { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, uio_of_genirq_match); - module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0); MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio"); -#else -# define uio_of_genirq_match NULL #endif static struct platform_driver uio_pdrv_genirq = { @@ -285,7 +282,7 @@ static struct platform_driver uio_pdrv_genirq = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &uio_pdrv_genirq_dev_pm_ops, - .of_match_table = uio_of_genirq_match, + .of_match_table = of_match_ptr(uio_of_genirq_match), }, }; -- cgit v1.2.3-70-g09d2 From 380672698b8e64f0b5e418412b1ed370bd366428 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 10:12:48 -0700 Subject: Revert "char: misc: assign file->private_data in all cases" This reverts commit 585d98e00ba7a5e2abe65f7a1eff631cb612289b, as it breaks the FUSE misc driver. Reported-by: Sedat Dilek Cc: Thomas Petazzoni Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/misc.c b/drivers/char/misc.c index fd504d35859..190d4423653 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -143,8 +143,8 @@ static int misc_open(struct inode * inode, struct file * file) err = 0; old_fops = file->f_op; file->f_op = new_fops; - file->private_data = c; if (file->f_op->open) { + file->private_data = c; err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); -- cgit v1.2.3-70-g09d2