From ae8a35aef41f2b9cf69fd489767291675f3fe291 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sun, 7 Apr 2013 11:28:42 +0800 Subject: [PATCH 001/102] 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(-) diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 1dfde4d543db..5bc10fa193de 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) From 21b066f19a81bd2af496745796914cfeca7a8ea7 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 2 May 2013 14:39:34 +0200 Subject: [PATCH 002/102] 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(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 437192e43006..4a938860fec0 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) From 87f0a427547d8552f87bfb19baa3d66d528aa761 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 14:32:11 +0900 Subject: [PATCH 003/102] 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(-) diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index 48651ef0028c..1256a4bf1c04 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); } From e7d87ca1089165c05376df50f1be0f68d72ab08b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 14:34:15 +0900 Subject: [PATCH 004/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 96787ec15cad..9ba93f0fe1df 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)); From db086fa926e57e1bd70e8c41235d230b3caa5e99 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 12 May 2013 15:34:45 +0300 Subject: [PATCH 005/102] 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(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e310ca6ed1a3..c2534ca5c6f1 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 cfdb144526aa..7dc2af7b6fba 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 2ad736989410..93da90a0986f 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); From 6220d6a05089220d913701416ab9423af23cd1bb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 12 May 2013 15:34:46 +0300 Subject: [PATCH 006/102] 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(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b3e50984d2c8..749452f8e2f6 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 93da90a0986f..7d9509a094e9 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 4de5140e7379..197ba6481bf5 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); From 80fe6361540fd7e0a38326b02b4f2a5e68c99d0a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 7 May 2013 21:12:31 +0300 Subject: [PATCH 007/102] 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(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7c44c8dbae42..053139f61086 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; } From a0ea59d56dfab021ecc65365275e532c6b937adb Mon Sep 17 00:00:00 2001 From: Libin Date: Mon, 13 May 2013 10:17:39 +0800 Subject: [PATCH 008/102] 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(-) diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index e1f60f968fdd..f1d7fa45c275 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); From 5e6e78e27a27179bb80545a2a55b8c3ae667aed8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 20 May 2013 10:16:39 -0300 Subject: [PATCH 009/102] 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(-) diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index 46d97014342e..f54ece268c98 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) { From 9db5c9088f741a7e83310a6a459793c25079c636 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 16 May 2013 13:15:39 +0200 Subject: [PATCH 010/102] 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(-) diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index c09c28f92055..1abd5ad59925 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); From aa8c06f7c9471f5722faa14ec8b887989de1226d Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:41 +0200 Subject: [PATCH 011/102] 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(-) diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c index 1548982db58b..f3611c2d83b6 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); From 8bcaec4ee2451937278aab5a655739ad73f25de6 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:44 +0200 Subject: [PATCH 012/102] 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(-) diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 81a10a563120..541983217085 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"); From b59f9a0533110f236a15bbbf81a2b7a7bffa8ed4 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:43 +0200 Subject: [PATCH 013/102] 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(-) diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c index 6a4ba5e83e37..28a766b9e198 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"); From ced9017a4fc7ecf35a9c0c0bd8e46d14876b9fd1 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 20 May 2013 22:47:42 +0200 Subject: [PATCH 014/102] 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(-) diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c index 7dd6fc60539d..22cdf385ab33 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"); From 3edad321b1bd2e6c8b5f38146c115c8982438f06 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Apr 2013 16:21:26 -0300 Subject: [PATCH 015/102] 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 --- .../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 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 000000000000..653c90c34a71 --- /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 067f31174a0e..29a11db365bc 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 9cce5d70ed52..969d923dad93 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 000000000000..978e8e3abc5c --- /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"); From cd4373984a5903276f52777a6003425e023eaa7e Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 21 May 2013 22:47:44 +0200 Subject: [PATCH 016/102] 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(-) diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c index 14aa10c1f6de..077ae12269ce 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); From 7131799b145aa67984cc57e52d6379862c78afa3 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 May 2013 10:03:13 +0300 Subject: [PATCH 017/102] 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(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 053139f61086..b9ad5106f5e1 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 197ba6481bf5..80a90319fc29 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 6251a4ee7067..b8921432e89d 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 */ From 139aacf757fc89792c43a79ba99bc2361c98ecd3 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 29 May 2013 20:09:30 +0300 Subject: [PATCH 018/102] 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(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index b9ad5106f5e1..5e11b5b9b65d 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) { + if (cl->read_cb) { cb = cl->read_cb; - goto copy_buffer; - } else if (cl->read_cb && cl->read_cb->buf_idx > 0 && - cl->read_cb->buf_idx <= *offset) { - 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); From c6148f8f8ceb18fd6ae3b637c56e8327b73873eb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 29 May 2013 20:09:31 +0300 Subject: [PATCH 019/102] 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(-) diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index a727464e9c3f..a2f5520f4060 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); From 9093ca8891e238adc323629d69aaae379fd38bb0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:35:23 +0900 Subject: [PATCH 020/102] 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(-) diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index 736c7714f565..fa017cf64bd1 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 7508cafff103..a2128af706b2 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 7deb25dc86a7..84a8dabc18fb 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 83269f1d16e3..83907c720594 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 From 1dd24daef9c5a4b51f4981a9c65a36a32b2ea98a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 23 May 2013 14:52:33 +0200 Subject: [PATCH 021/102] 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(-) diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index d31ee23c9f13..96677fc7ea4d 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] */ From 84524cf43d693746896782628466205ccc193e0d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 23 May 2013 14:52:34 +0200 Subject: [PATCH 022/102] 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(-) diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index 96677fc7ea4d..38b145eaf24d 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 *************/ From f0ac23639c62add367309101d18ae2aa1d4a377c Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Tue, 28 May 2013 13:00:20 -0700 Subject: [PATCH 023/102] 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(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2baeec56edfe..5d4fd69d04ca 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; } From 01fe7b43e7c83fe8fbf0b30b47f4ccd2f2058682 Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Tue, 28 May 2013 13:01:21 -0700 Subject: [PATCH 024/102] 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(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index ad8fd8e64937..840b3594a5ae 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; } From 6c7dd64abdd0d07bab3c1cc4327dc328cb70005a Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:21:01 -0700 Subject: [PATCH 025/102] 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 | 69 ++++++++++----------------------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 9ba93f0fe1df..36370b429bc7 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; - } + pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pwm->mmio_base)) + return PTR_ERR(pwm->mmio_base); - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - err = -EBUSY; - goto fail_no_mem_resource; - } + ret = ep93xx_pwm_acquire_gpio(pdev); + if (ret) + return ret; - 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; + 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; From 1f7190fd1ef23c3c0423cf4471572b6ec2b57c8b Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:21:50 -0700 Subject: [PATCH 026/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 36370b429bc7..af01fb9fcd7e 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) From a39ca2740c58ddef91efc5ea6889ecee1c314c42 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:22:30 -0700 Subject: [PATCH 027/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index af01fb9fcd7e..06f0c63e8b44 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; } /* From 53e2e380891facf2b0fdb43482b8febb6e17f7f6 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:23:00 -0700 Subject: [PATCH 028/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 06f0c63e8b44..ab5a48135508 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); From aa919769c625e2b2c4a70c6bd62a1a83f9f64aa5 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:23:38 -0700 Subject: [PATCH 029/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index ab5a48135508..7a133042e92c 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); From ac91b96f3070a2c3b562464604847887cc44b342 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:24:16 -0700 Subject: [PATCH 030/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 7a133042e92c..cf42347182bc 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; } From 02846b97d7464b92441df2e9d2f59fd5591bbd81 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:24:51 -0700 Subject: [PATCH 031/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index cf42347182bc..320d911edcbc 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); From 47a36ee95a5fe31358e97a055988fe7a00163038 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:25:23 -0700 Subject: [PATCH 032/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 320d911edcbc..5193faf594bf 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; From d98d7903d14781ab6a8f85daa393eddf13b7f918 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:26:11 -0700 Subject: [PATCH 033/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 5193faf594bf..cdc2ffc08ab9 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 From 7501ba361f4b22004deec31a8227ca0240630193 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:26:41 -0700 Subject: [PATCH 034/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index cdc2ffc08ab9..6f483753b435 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); From 5b2cd8f9b4a098d5aab91539ab5d589c6b676b1e Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:27:08 -0700 Subject: [PATCH 035/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 6f483753b435..3dd20069971c 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; From ddfd6894db8d1c1a5b5e532283aee1f3afd880b9 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:27:38 -0700 Subject: [PATCH 036/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 3dd20069971c..dd570d1d2245 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, From 6e1cf66ee3105b41ec93a3848e848ae682add3ff Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:28:14 -0700 Subject: [PATCH 037/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index dd570d1d2245..b5ad0c6a5af1 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 "); From a4b05d1262c1f79ff07e9baceccbf3398846b8c0 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 24 May 2013 16:28:44 -0700 Subject: [PATCH 038/102] 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(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index b5ad0c6a5af1..cdb67a9c1959 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; From 8d7bda51888d14c07cbebacc5a10be776477bb63 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sun, 26 May 2013 20:06:50 +0200 Subject: [PATCH 039/102] 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(-) diff --git a/Documentation/w1/w1.generic b/Documentation/w1/w1.generic index 212f4ac31c01..a31c5a242973 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 e45eca1044bd..91cc2cdf02c0 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 829786252c6b..85937773a96a 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 40a10b5ed120..7f86aec74088 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 984b30331a45..cef8605e43ec 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 85f2cdb27fa2..10cc1b6176e6 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 e86a69dc411e..93719d25d849 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 98ed9c49cf50..0cd7a27b5d6b 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 5140d7be67ab..1aba8e41ad46 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 98117db595bb..cd30a6d95ea5 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 84655625c870..ed4c87506def 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 c1a702f8c803..8978360bd387 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 7ce277d2bb67..0459df843c58 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) { From e68d2971d26577b932a16333ce165af98a96e068 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 23 May 2013 12:02:32 -0700 Subject: [PATCH 040/102] 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(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 0b122f8c7005..6de6c98ce6eb 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 21ef68934a20..0df75908200e 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 253a74ba245c..ec3b8cdf1e04 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 c2559847d7ee..405e05a28e65 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 */ From 98b80d8938b8e381027f1c3f30855087238fb4fb Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 23 May 2013 12:02:33 -0700 Subject: [PATCH 041/102] Drivers: hv: Add the GUID fot synthetic fibre channel device In preparation for supporting synthetic Fiber Channel device, add the GUID for this service. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 405e05a28e65..fae8bac907ef 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1388,6 +1388,16 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \ } +/* + * Synthetic FC GUID + * {2f9bcc4a-0069-4af3-b76b-6fd0be528cda} + */ +#define HV_SYNTHFC_GUID \ + .guid = { \ + 0x4A, 0xCC, 0x9B, 0x2F, 0x69, 0x00, 0xF3, 0x4A, \ + 0xB7, 0x6B, 0x6F, 0xD0, 0xBE, 0x52, 0x8C, 0xDA \ + } + /* * Common header for Hyper-V ICs */ From 359016032239e0a585c01bffe04b72ab037e5de4 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 22 May 2013 14:54:30 +0200 Subject: [PATCH 042/102] tools: hv: Check return value of setsockopt call Check return value of setsockopt call and if it fails print error to the system log and exit with non-zero value. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 5a1f6489d185..ae9aa1cd97fe 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1457,7 +1457,13 @@ int main(void) exit(EXIT_FAILURE); } nl_group = CN_KVP_IDX; - setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); + + if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { + syslog(LOG_ERR, "setsockopt failed; error: %d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + /* * Register ourselves with the kernel. */ From 4d81e3074423d1bd69a29d5795ff71205814d9e1 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 22 May 2013 14:54:31 +0200 Subject: [PATCH 043/102] tools: hv: Check return value of poll call Check return value of poll call and if it fails print error to the system log. If errno is EINVAL then exit with non-zero value otherwise continue the while loop and call poll again. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index ae9aa1cd97fe..81cbb6ecce89 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1490,7 +1490,16 @@ int main(void) socklen_t addr_l = sizeof(addr); pfd.events = POLLIN; pfd.revents = 0; - poll(&pfd, 1, -1); + + if (poll(&pfd, 1, -1) < 0) { + syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno)); + if (errno == EINVAL) { + close(fd); + exit(EXIT_FAILURE); + } + else + continue; + } len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0, addr_p, &addr_l); From f14e600a3bb62905fe9e4e0350048af4ead54ca9 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 22 May 2013 14:54:32 +0200 Subject: [PATCH 044/102] tools: hv: Check retrun value of strchr call Check return value of strchr call and dereference it only if it is not NULL. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 81cbb6ecce89..5b8f28f2aafa 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -765,7 +765,9 @@ static void kvp_process_ipconfig_file(char *cmd, break; x = strchr(p, '\n'); - *x = '\0'; + if (x) + *x = '\0'; + strcat(config_buf, p); strcat(config_buf, ";"); } From fca597558e47f936a0e03bab15af726aa3a0fa0e Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 22 May 2013 14:54:33 +0200 Subject: [PATCH 045/102] tools: hv: Fix file descriptor leaks Close "fd" file descriptor when is goes out of scope so it does not leak. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 5b8f28f2aafa..d55ce4040b74 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -257,12 +257,15 @@ static int kvp_file_init(void) filep = fopen(fname, "re"); - if (!filep) + if (!filep) { + close(fd); return 1; + } record = malloc(alloc_unit * num_blocks); if (record == NULL) { fclose(filep); + close(fd); return 1; } for (;;) { @@ -286,6 +289,7 @@ static int kvp_file_init(void) num_blocks); if (record == NULL) { fclose(filep); + close(fd); return 1; } continue; From c265a0d5152619bddcf1986f17995d2df6781554 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 31 May 2013 20:01:30 +0800 Subject: [PATCH 046/102] 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(-) diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 84a8dabc18fb..4ba0ea352968 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; } From 143e9c76c487957de89808cf2a3c828518ad131b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 May 2013 18:54:58 +0300 Subject: [PATCH 047/102] 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(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 931e635aa491..8b4d4fb9187b 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; From 4cd5773a2ae6facdde3f563087a4cc50f00d9530 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Jun 2013 19:46:26 +0300 Subject: [PATCH 048/102] 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 diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c002d8660e30..80889d5f95f5 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 59ac143dec25..4f777ed9b089 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 12b4d55a02af..d5569734f672 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 e9ef6d6b51d5..3afb969441d1 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 fe01d418b09a..d246a3bbd6ef 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 c55a037a354e..22f0f4e8a9e1 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 000000000000..2e3c52c8d050 --- /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 2ddc9046868e..6dfe1c636a80 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 cec074be8c43..35a9f0804b6f 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 3c7f5b51b979..aa88e23fc87a 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); From f7b41276b6b07f47c5f5212fa244385b0e3aaa30 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 4 Jun 2013 13:15:16 +0900 Subject: [PATCH 049/102] 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 | 12 ++--- drivers/misc/spear13xx_pcie_gadget.c | 57 ++++++++++++++++-------- drivers/misc/ti_dac7512.c | 6 ++- 14 files changed, 153 insertions(+), 82 deletions(-) diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 8f99e8e3f0ac..0daadcf1ed7a 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 5b5fd8416b3e..0c6e037153d2 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 98f9bb26492a..868a30a1b417 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 f4975f7d0d5b..99a04686e45f 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 818f3a0e62bf..057580e026c0 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 fa017cf64bd1..c6bd7e84de24 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 a2128af706b2..7b56563f8b74 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 423cd40f1c0f..170bd3daf336 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 c5145b3fcce8..e3183f26216b 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 0aa08c746463..b7f84dacf822 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 4cd4a3d2a76a..036effe9a795 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 797d7962cc88..4f7635922394 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]; + int ret; - 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; + 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 4ba0ea352968..2e13614d41e8 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 1d86407189eb..9b237221bc4e 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; From 29ddae2a40883640941932f73eb3d15535c3f4a2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 4 Jun 2013 22:44:27 +0800 Subject: [PATCH 050/102] 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(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 8b4d4fb9187b..a5925f7f17f6 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 */ From 71811f3271cd986e223be44830e5961056561ac3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 5 Jun 2013 17:09:39 +0000 Subject: [PATCH 051/102] 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(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1ccbe9482faa..2ca6d7844ad9 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; } From fd5c25614012ccad75ef505982af4d6c08d0df83 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 6 Jun 2013 12:29:16 +0100 Subject: [PATCH 052/102] 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(+) diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 5e6c7d74e19f..f6856b427496 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); From cb3771b049b3b554455094a3b06d8ceb07086e06 Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Thu, 6 Jun 2013 15:55:28 +0200 Subject: [PATCH 053/102] 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(-) diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 6e2ab007fe9c..21f7a72301e4 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); From e39506b466edcda2a7e9d0174d7987ae654137b7 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:14 +0200 Subject: [PATCH 054/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 01463c781847..1b2c6317c772 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; From 40ca0209bae06eeede0f861e988b4bb38ae9cf98 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:15 +0200 Subject: [PATCH 055/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 1b2c6317c772..4eec14bcb7b8 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), From 54fe15918bb7ad8aac994bf69f6d2f8c23fbdc34 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:16 +0200 Subject: [PATCH 056/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 4eec14bcb7b8..43bc342e6c03 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; } From a843168dc9f222c4c46751d96c2b701b6539f261 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:17 +0200 Subject: [PATCH 057/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 43bc342e6c03..bce8a64cd7c0 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 From ed9084ecfccae55ea45c7f068d1f513b979a0132 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 6 Jun 2013 10:24:18 +0200 Subject: [PATCH 058/102] 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 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 000000000000..c1d22b3ae134 --- /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 b90f85bf5f81..80faa56c684d 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 bce8a64cd7c0..149b95c957da 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, From 43f50752256d8374602559832eaddd0cbf5aad64 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Thu, 6 Jun 2013 10:24:19 +0200 Subject: [PATCH 059/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 149b95c957da..8ddc57c5bc47 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"); From d652f7022b359afd5d34fc9fffd71df118521ead Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 6 Jun 2013 10:24:20 +0200 Subject: [PATCH 060/102] 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(-) diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 8ddc57c5bc47..b8f5acf02261 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; } } From 214da6728acac285cac0e8e9b6c4c13838b8e1b0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 7 Jun 2013 10:16:54 +0800 Subject: [PATCH 061/102] 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(+) diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index b29d97e170ae..a4c16ee5c718 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; } From 12e50c30545b74ca8b6ef7f442b826fed155193f Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Mon, 17 Jun 2013 10:39:44 +0200 Subject: [PATCH 062/102] tools: hv: Improve error logging in KVP daemon. Use errno and strerror() when logging errors to provide more information. Signed-off-by: Tomas Hozza Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index d55ce4040b74..ca9fa4d32e07 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -127,7 +127,8 @@ static void kvp_acquire_lock(int pool) fl.l_pid = getpid(); if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { - syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); + syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool, + errno, strerror(errno)); exit(EXIT_FAILURE); } } @@ -138,8 +139,8 @@ static void kvp_release_lock(int pool) fl.l_pid = getpid(); if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { - perror("fcntl"); - syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); + syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool, + errno, strerror(errno)); exit(EXIT_FAILURE); } } @@ -157,8 +158,9 @@ static void kvp_update_file(int pool) filep = fopen(kvp_file_info[pool].fname, "we"); if (!filep) { + syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, + errno, strerror(errno)); kvp_release_lock(pool); - syslog(LOG_ERR, "Failed to open file, pool: %d", pool); exit(EXIT_FAILURE); } @@ -188,8 +190,9 @@ static void kvp_update_mem_state(int pool) filep = fopen(kvp_file_info[pool].fname, "re"); if (!filep) { + syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, + errno, strerror(errno)); kvp_release_lock(pool); - syslog(LOG_ERR, "Failed to open file, pool: %d", pool); exit(EXIT_FAILURE); } for (;;) { @@ -240,7 +243,8 @@ static int kvp_file_init(void) if (access(KVP_CONFIG_LOC, F_OK)) { if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { - syslog(LOG_ERR, " Failed to create %s", KVP_CONFIG_LOC); + syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, + errno, strerror(errno)); exit(EXIT_FAILURE); } } @@ -1280,7 +1284,8 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) file = fopen(if_file, "w"); if (file == NULL) { - syslog(LOG_ERR, "Failed to open config file"); + syslog(LOG_ERR, "Failed to open config file; error: %d %s", + errno, strerror(errno)); return HV_E_FAIL; } @@ -1447,7 +1452,8 @@ int main(void) fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (fd < 0) { - syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); + syslog(LOG_ERR, "netlink socket creation failed; error: %d %s", errno, + strerror(errno)); exit(EXIT_FAILURE); } addr.nl_family = AF_NETLINK; @@ -1458,7 +1464,7 @@ int main(void) error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (error < 0) { - syslog(LOG_ERR, "bind failed; error:%d", error); + syslog(LOG_ERR, "bind failed; error: %d %s", errno, strerror(errno)); close(fd); exit(EXIT_FAILURE); } @@ -1484,7 +1490,7 @@ int main(void) len = netlink_send(fd, message); if (len < 0) { - syslog(LOG_ERR, "netlink_send failed; error:%d", len); + syslog(LOG_ERR, "netlink_send failed; error: %d %s", errno, strerror(errno)); close(fd); exit(EXIT_FAILURE); } @@ -1716,7 +1722,8 @@ kvp_done: len = netlink_send(fd, incoming_cn_msg); if (len < 0) { - syslog(LOG_ERR, "net_link send failed; error:%d", len); + syslog(LOG_ERR, "net_link send failed; error: %d %s", errno, + strerror(errno)); exit(EXIT_FAILURE); } } From ea9dd9d655adf16b083b79ad84e5b012543f659f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 22 May 2013 19:31:59 +0900 Subject: [PATCH 063/102] 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(-) diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 60adc04b0561..a5a03ae069ee 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; } } From 9c8a013af49cb82a1a47bede56e8ce5efd93b734 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 4 Jun 2013 01:13:38 +0900 Subject: [PATCH 064/102] 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(+) diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index a5a03ae069ee..8c69803558fe 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 From b1f254e35d85535b17af2786d06fe88f15f304f7 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Tue, 28 May 2013 10:50:11 +0900 Subject: [PATCH 065/102] 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 diff --git a/Documentation/devicetree/bindings/extcon/extcon-twl.txt b/Documentation/devicetree/bindings/extcon/extcon-twl.txt new file mode 100644 index 000000000000..58f531ab4df3 --- /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 3297301a42d4..63f454e20576 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 f98a3c4d46e0..540e2c3a4431 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 000000000000..b752a0ad7b63 --- /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 8f21daf62fb5..9b81b2bdc46b 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) From 9c9f32eddee56888c7acd0d69134a5dcae09e1a8 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 12 Jun 2013 09:13:25 +0200 Subject: [PATCH 066/102] 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 diff --git a/MAINTAINERS b/MAINTAINERS index 5be702cc8449..14746ad5ee15 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 9953a42809ec..ae050b54c368 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 130abc1dfd65..336b0ad0acd0 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 000000000000..e28790267c69 --- /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 000000000000..a2784d8b5306 --- /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 000000000000..fc3547f32d5e --- /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"); From e34fae7841b12d77f2621bd28fd41929879ef951 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 12 Jun 2013 09:13:36 +0200 Subject: [PATCH 067/102] FMC: add needed headers This set of headers comes from commit ab23167f (current master of the project on ohwr.org). They define the basic data structures for FMC and its SDB support. 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 --- include/linux/fmc-sdb.h | 36 ++++++ include/linux/fmc.h | 237 +++++++++++++++++++++++++++++++++++++++ include/linux/ipmi-fru.h | 135 ++++++++++++++++++++++ include/linux/sdb.h | 159 ++++++++++++++++++++++++++ 4 files changed, 567 insertions(+) create mode 100644 include/linux/fmc-sdb.h create mode 100644 include/linux/fmc.h create mode 100644 include/linux/ipmi-fru.h create mode 100644 include/linux/sdb.h diff --git a/include/linux/fmc-sdb.h b/include/linux/fmc-sdb.h new file mode 100644 index 000000000000..1974317a9b3d --- /dev/null +++ b/include/linux/fmc-sdb.h @@ -0,0 +1,36 @@ +/* + * This file is separate from sdb.h, because I want that one to remain + * unchanged (as far as possible) from the official sdb distribution + * + * This file and associated functionality are a playground for me to + * understand stuff which will later be implemented in more generic places. + */ +#include + +/* This is the union of all currently defined types */ +union sdb_record { + struct sdb_interconnect ic; + struct sdb_device dev; + struct sdb_bridge bridge; + struct sdb_integration integr; + struct sdb_empty empty; +}; + +struct fmc_device; + +/* Every sdb table is turned into this structure */ +struct sdb_array { + int len; + int level; + unsigned long baseaddr; + struct fmc_device *fmc; /* the device that hosts it */ + struct sdb_array *parent; /* NULL at root */ + union sdb_record *record; /* copies of the struct */ + struct sdb_array **subtree; /* only valid for bridge items */ +}; + +extern int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address); +extern void fmc_show_sdb_tree(const struct fmc_device *fmc); +extern signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor, + uint32_t device, unsigned long *sz); +extern int fmc_free_sdb_tree(struct fmc_device *fmc); diff --git a/include/linux/fmc.h b/include/linux/fmc.h new file mode 100644 index 000000000000..a3c498503983 --- /dev/null +++ b/include/linux/fmc.h @@ -0,0 +1,237 @@ +/* + * 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. + */ +#ifndef __LINUX_FMC_H__ +#define __LINUX_FMC_H__ +#include +#include +#include +#include +#include +#include + +struct fmc_device; +struct fmc_driver; + +/* + * This bus abstraction is developed separately from drivers, so we need + * to check the version of the data structures we receive. + */ + +#define FMC_MAJOR 3 +#define FMC_MINOR 0 +#define FMC_VERSION ((FMC_MAJOR << 16) | FMC_MINOR) +#define __FMC_MAJOR(x) ((x) >> 16) +#define __FMC_MINOR(x) ((x) & 0xffff) + +/* + * The device identification, as defined by the IPMI FRU (Field Replaceable + * Unit) includes four different strings to describe the device. Here we + * only match the "Board Manufacturer" and the "Board Product Name", + * ignoring the "Board Serial Number" and "Board Part Number". All 4 are + * expected to be strings, so they are treated as zero-terminated C strings. + * Unspecified string (NULL) means "any", so if both are unspecified this + * is a catch-all driver. So null entries are allowed and we use array + * and length. This is unlike pci and usb that use null-terminated arrays + */ +struct fmc_fru_id { + char *manufacturer; + char *product_name; +}; + +/* + * If the FPGA is already programmed (think Etherbone or the second + * SVEC slot), we can match on SDB devices in the memory image. This + * match uses an array of devices that must all be present, and the + * match is based on vendor and device only. Further checks are expected + * to happen in the probe function. Zero means "any" and catch-all is allowed. + */ +struct fmc_sdb_one_id { + uint64_t vendor; + uint32_t device; +}; +struct fmc_sdb_id { + struct fmc_sdb_one_id *cores; + int cores_nr; +}; + +struct fmc_device_id { + struct fmc_fru_id *fru_id; + int fru_id_nr; + struct fmc_sdb_id *sdb_id; + int sdb_id_nr; +}; + +/* This sizes the module_param_array used by generic module parameters */ +#define FMC_MAX_CARDS 32 + +/* The driver is a pretty simple thing */ +struct fmc_driver { + unsigned long version; + struct device_driver driver; + int (*probe)(struct fmc_device *); + int (*remove)(struct fmc_device *); + const struct fmc_device_id id_table; + /* What follows is for generic module parameters */ + int busid_n; + int busid_val[FMC_MAX_CARDS]; + int gw_n; + char *gw_val[FMC_MAX_CARDS]; +}; +#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver) + +/* These are the generic parameters, that drivers may instantiate */ +#define FMC_PARAM_BUSID(_d) \ + module_param_array_named(busid, _d.busid_val, int, &_d.busid_n, 0444) +#define FMC_PARAM_GATEWARE(_d) \ + module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444) + +/* + * Drivers may need to configure gpio pins in the carrier. To read input + * (a very uncommon operation, and definitely not in the hot paths), just + * configure one gpio only and get 0 or 1 as retval of the config method + */ +struct fmc_gpio { + char *carrier_name; /* name or NULL for virtual pins */ + int gpio; + int _gpio; /* internal use by the carrier */ + int mode; /* GPIOF_DIR_OUT etc, from */ + int irqmode; /* IRQF_TRIGGER_LOW and so on */ +}; + +/* The numbering of gpio pins allows access to raw pins or virtual roles */ +#define FMC_GPIO_RAW(x) (x) /* 4096 of them */ +#define __FMC_GPIO_IS_RAW(x) ((x) < 0x1000) +#define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */ +#define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */ +#define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */ +#define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */ +#define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */ +/* We may add SCL and SDA, or other roles if the need arises */ + +/* GPIOF_DIR_IN etc are missing before 3.0. copy from */ +#ifndef GPIOF_DIR_IN +# define GPIOF_DIR_OUT (0 << 0) +# define GPIOF_DIR_IN (1 << 0) +# define GPIOF_INIT_LOW (0 << 1) +# define GPIOF_INIT_HIGH (1 << 1) +#endif + +/* + * The operations are offered by each carrier and should make driver + * design completely independent of the carrier. Named GPIO pins may be + * 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); + 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, + char *name, int flags); + void (*irq_ack)(struct fmc_device *fmc); + int (*irq_free)(struct fmc_device *fmc); + int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio, + int ngpio); + int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l); + int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l); +}; + +/* Prefer this helper rather than calling of fmc->reprogram directly */ +extern int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw, + int sdb_entry); + +/* + * The device reports all information needed to access hw. + * + * If we have eeprom_len and not contents, the core reads it. + * Then, parsing of identifiers is done by the core which fills fmc_fru_id.. + * Similarly a device that must be matched based on SDB cores must + * fill the entry point and the core will scan the bus (FIXME: sdb match) + */ +struct fmc_device { + unsigned long version; + unsigned long flags; + struct module *owner; /* char device must pin it */ + struct fmc_fru_id id; /* for EEPROM-based match */ + struct fmc_operations *op; /* carrier-provided */ + int irq; /* according to host bus. 0 == none */ + int eeprom_len; /* Usually 8kB, may be less */ + int eeprom_addr; /* 0x50, 0x52 etc */ + uint8_t *eeprom; /* Full contents or leading part */ + char *carrier_name; /* "SPEC" or similar, for special use */ + void *carrier_data; /* "struct spec *" or equivalent */ + __iomem void *fpga_base; /* May be NULL (Etherbone) */ + __iomem void *slot_base; /* Set by the driver */ + struct fmc_device **devarray; /* Allocated by the bus */ + int slot_id; /* Index in the slot array */ + int nr_slots; /* Number of slots in this carrier */ + unsigned long memlen; /* Used for the char device */ + struct device dev; /* For Linux use */ + struct device *hwdev; /* The underlying hardware device */ + unsigned long sdbfs_entry; + struct sdb_array *sdb; + uint32_t device_id; /* Filled by the device */ + char *mezzanine_name; /* Defaults to ``fmc'' */ + void *mezzanine_data; +}; +#define to_fmc_device(x) container_of((x), struct fmc_device, dev) + +#define FMC_DEVICE_HAS_GOLDEN 1 +#define FMC_DEVICE_HAS_CUSTOM 2 +#define FMC_DEVICE_NO_MEZZANINE 4 +#define FMC_DEVICE_MATCH_SDB 8 /* fmc-core must scan sdb in fpga */ + +/* + * If fpga_base can be used, the carrier offers no readl/writel methods, and + * this expands to a single, fast, I/O access. + */ +static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset) +{ + if (unlikely(fmc->op->readl)) + return fmc->op->readl(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); + else + writel(val, fmc->fpga_base + off); +} + +/* pci-like naming */ +static inline void *fmc_get_drvdata(const struct fmc_device *fmc) +{ + return dev_get_drvdata(&fmc->dev); +} + +static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data) +{ + dev_set_drvdata(&fmc->dev, data); +} + +/* The 4 access points */ +extern int fmc_driver_register(struct fmc_driver *drv); +extern void fmc_driver_unregister(struct fmc_driver *drv); +extern int fmc_device_register(struct fmc_device *tdev); +extern void fmc_device_unregister(struct fmc_device *tdev); + +/* Two more for device sets, all driven by the same FPGA */ +extern int fmc_device_register_n(struct fmc_device **devs, int n); +extern void fmc_device_unregister_n(struct fmc_device **devs, int n); + +/* Internal cross-calls between files; not exported to other modules */ +extern int fmc_match(struct device *dev, struct device_driver *drv); +extern int fmc_fill_id_info(struct fmc_device *fmc); +extern void fmc_free_id_info(struct fmc_device *fmc); +extern void fmc_dump_eeprom(const struct fmc_device *fmc); +extern void fmc_dump_sdb(const struct fmc_device *fmc); + +#endif /* __LINUX_FMC_H__ */ diff --git a/include/linux/ipmi-fru.h b/include/linux/ipmi-fru.h new file mode 100644 index 000000000000..4d3a76380e32 --- /dev/null +++ b/include/linux/ipmi-fru.h @@ -0,0 +1,135 @@ +/* + * 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. + */ +#ifndef __LINUX_IPMI_FRU_H__ +#define __LINUX_IPMI_FRU_H__ +#ifdef __KERNEL__ +# include +# include +#else +# include +# include +#endif + +/* + * These structures match the unaligned crap we have in FRU1011.pdf + * (http://download.intel.com/design/servers/ipmi/FRU1011.pdf) + */ + +/* chapter 8, page 5 */ +struct fru_common_header { + uint8_t format; /* 0x01 */ + uint8_t internal_use_off; /* multiple of 8 bytes */ + uint8_t chassis_info_off; /* multiple of 8 bytes */ + uint8_t board_area_off; /* multiple of 8 bytes */ + uint8_t product_area_off; /* multiple of 8 bytes */ + uint8_t multirecord_off; /* multiple of 8 bytes */ + uint8_t pad; /* must be 0 */ + uint8_t checksum; /* sum modulo 256 must be 0 */ +}; + +/* chapter 9, page 5 -- internal_use: not used by us */ + +/* chapter 10, page 6 -- chassis info: not used by us */ + +/* chapter 13, page 9 -- used by board_info_area below */ +struct fru_type_length { + uint8_t type_length; + uint8_t data[0]; +}; + +/* chapter 11, page 7 */ +struct fru_board_info_area { + uint8_t format; /* 0x01 */ + uint8_t area_len; /* multiple of 8 bytes */ + uint8_t language; /* I hope it's 0 */ + uint8_t mfg_date[3]; /* LSB, minutes since 1996-01-01 */ + struct fru_type_length tl[0]; /* type-length stuff follows */ + + /* + * the TL there are in order: + * Board Manufacturer + * Board Product Name + * Board Serial Number + * Board Part Number + * FRU File ID (may be null) + * more manufacturer-specific stuff + * 0xc1 as a terminator + * 0x00 pad to a multiple of 8 bytes - 1 + * checksum (sum of all stuff module 256 must be zero) + */ +}; + +enum fru_type { + FRU_TYPE_BINARY = 0x00, + FRU_TYPE_BCDPLUS = 0x40, + FRU_TYPE_ASCII6 = 0x80, + FRU_TYPE_ASCII = 0xc0, /* not ascii: depends on language */ +}; + +/* + * some helpers + */ +static inline struct fru_board_info_area *fru_get_board_area( + const struct fru_common_header *header) +{ + /* we know for sure that the header is 8 bytes in size */ + return (struct fru_board_info_area *)(header + header->board_area_off); +} + +static inline int fru_type(struct fru_type_length *tl) +{ + return tl->type_length & 0xc0; +} + +static inline int fru_length(struct fru_type_length *tl) +{ + return (tl->type_length & 0x3f) + 1; /* len of whole record */ +} + +/* assume ascii-latin1 encoding */ +static inline int fru_strlen(struct fru_type_length *tl) +{ + return fru_length(tl) - 1; +} + +static inline char *fru_strcpy(char *dest, struct fru_type_length *tl) +{ + int len = fru_strlen(tl); + memcpy(dest, tl->data, len); + dest[len] = '\0'; + return dest; +} + +static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl) +{ + return tl + fru_length(tl); +} + +static inline int fru_is_eof(struct fru_type_length *tl) +{ + return tl->type_length == 0xc1; +} + +/* + * External functions defined in fru-parse.c. + */ +extern int fru_header_cksum_ok(struct fru_common_header *header); +extern int fru_bia_cksum_ok(struct fru_board_info_area *bia); + +/* All these 4 return allocated strings by calling fru_alloc() */ +extern char *fru_get_board_manufacturer(struct fru_common_header *header); +extern char *fru_get_product_name(struct fru_common_header *header); +extern char *fru_get_serial_number(struct fru_common_header *header); +extern char *fru_get_part_number(struct fru_common_header *header); + +/* This must be defined by the caller of the above functions */ +extern void *fru_alloc(size_t size); + +#endif /* __LINUX_IMPI_FRU_H__ */ diff --git a/include/linux/sdb.h b/include/linux/sdb.h new file mode 100644 index 000000000000..fbb76a46c8a5 --- /dev/null +++ b/include/linux/sdb.h @@ -0,0 +1,159 @@ +/* + * This is the official version 1.1 of sdb.h + */ +#ifndef __SDB_H__ +#define __SDB_H__ +#ifdef __KERNEL__ +#include +#else +#include +#endif + +/* + * All structures are 64 bytes long and are expected + * to live in an array, one for each interconnect. + * Most fields of the structures are shared among the + * various types, and most-specific fields are at the + * beginning (for alignment reasons, and to keep the + * magic number at the head of the interconnect record + */ + +/* Product, 40 bytes at offset 24, 8-byte aligned + * + * device_id is vendor-assigned; version is device-specific, + * date is hex (e.g 0x20120501), name is UTF-8, blank-filled + * and not terminated with a 0 byte. + */ +struct sdb_product { + uint64_t vendor_id; /* 0x18..0x1f */ + uint32_t device_id; /* 0x20..0x23 */ + uint32_t version; /* 0x24..0x27 */ + uint32_t date; /* 0x28..0x2b */ + uint8_t name[19]; /* 0x2c..0x3e */ + uint8_t record_type; /* 0x3f */ +}; + +/* + * Component, 56 bytes at offset 8, 8-byte aligned + * + * The address range is first to last, inclusive + * (for example 0x100000 - 0x10ffff) + */ +struct sdb_component { + uint64_t addr_first; /* 0x08..0x0f */ + uint64_t addr_last; /* 0x10..0x17 */ + struct sdb_product product; /* 0x18..0x3f */ +}; + +/* Type of the SDB record */ +enum sdb_record_type { + sdb_type_interconnect = 0x00, + sdb_type_device = 0x01, + sdb_type_bridge = 0x02, + sdb_type_integration = 0x80, + sdb_type_repo_url = 0x81, + sdb_type_synthesis = 0x82, + sdb_type_empty = 0xFF, +}; + +/* Type 0: interconnect (first of the array) + * + * sdb_records is the length of the table including this first + * record, version is 1. The bus type is enumerated later. + */ +#define SDB_MAGIC 0x5344422d /* "SDB-" */ +struct sdb_interconnect { + uint32_t sdb_magic; /* 0x00-0x03 */ + uint16_t sdb_records; /* 0x04-0x05 */ + uint8_t sdb_version; /* 0x06 */ + uint8_t sdb_bus_type; /* 0x07 */ + struct sdb_component sdb_component; /* 0x08-0x3f */ +}; + +/* Type 1: device + * + * class is 0 for "custom device", other values are + * to be standardized; ABI version is for the driver, + * bus-specific bits are defined by each bus (see below) + */ +struct sdb_device { + uint16_t abi_class; /* 0x00-0x01 */ + uint8_t abi_ver_major; /* 0x02 */ + uint8_t abi_ver_minor; /* 0x03 */ + uint32_t bus_specific; /* 0x04-0x07 */ + struct sdb_component sdb_component; /* 0x08-0x3f */ +}; + +/* Type 2: bridge + * + * child is the address of the nested SDB table + */ +struct sdb_bridge { + uint64_t sdb_child; /* 0x00-0x07 */ + struct sdb_component sdb_component; /* 0x08-0x3f */ +}; + +/* Type 0x80: integration + * + * all types with bit 7 set are meta-information, so + * software can ignore the types it doesn't know. Here we + * just provide product information for an aggregate device + */ +struct sdb_integration { + uint8_t reserved[24]; /* 0x00-0x17 */ + struct sdb_product product; /* 0x08-0x3f */ +}; + +/* Type 0x81: Top module repository url + * + * again, an informative field that software can ignore + */ +struct sdb_repo_url { + uint8_t repo_url[63]; /* 0x00-0x3e */ + uint8_t record_type; /* 0x3f */ +}; + +/* Type 0x82: Synthesis tool information + * + * this informative record + */ +struct sdb_synthesis { + uint8_t syn_name[16]; /* 0x00-0x0f */ + uint8_t commit_id[16]; /* 0x10-0x1f */ + uint8_t tool_name[8]; /* 0x20-0x27 */ + uint32_t tool_version; /* 0x28-0x2b */ + uint32_t date; /* 0x2c-0x2f */ + uint8_t user_name[15]; /* 0x30-0x3e */ + uint8_t record_type; /* 0x3f */ +}; + +/* Type 0xff: empty + * + * this allows keeping empty slots during development, + * so they can be filled later with minimal efforts and + * no misleading description is ever shipped -- hopefully. + * It can also be used to pad a table to a desired length. + */ +struct sdb_empty { + uint8_t reserved[63]; /* 0x00-0x3e */ + uint8_t record_type; /* 0x3f */ +}; + +/* The type of bus, for bus-specific flags */ +enum sdb_bus_type { + sdb_wishbone = 0x00, + sdb_data = 0x01, +}; + +#define SDB_WB_WIDTH_MASK 0x0f +#define SDB_WB_ACCESS8 0x01 +#define SDB_WB_ACCESS16 0x02 +#define SDB_WB_ACCESS32 0x04 +#define SDB_WB_ACCESS64 0x08 +#define SDB_WB_LITTLE_ENDIAN 0x80 + +#define SDB_DATA_READ 0x04 +#define SDB_DATA_WRITE 0x02 +#define SDB_DATA_EXEC 0x01 + +#endif /* __SDB_H__ */ From ea6add0bf1a209a3eab725e4429b1ac4e4cdef73 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 16 Jun 2013 22:42:05 +0400 Subject: [PATCH 068/102] 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(-) diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 80faa56c684d..6f6193168281 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" From f3cbfa5d6e1bbd80b16ea337a2b00a89264c2b45 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 10 Jun 2013 18:43:53 +0400 Subject: [PATCH 069/102] 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(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 4a938860fec0..d87cc91bc016 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) From e31c51e4a1d11ed0b61557ca9da9cf18c8855531 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Tue, 11 Jun 2013 11:20:17 +0100 Subject: [PATCH 070/102] 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(-) diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 9c1aa4dc39c9..94ce64d7a5f6 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 */ From 2981795cc0c02d12d0e231bfe22480564b937cd5 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Tue, 11 Jun 2013 17:03:14 +0100 Subject: [PATCH 071/102] 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(-) diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 94ce64d7a5f6..c04600e26a38 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 From c36c5736be5cc638db7659093bf8c2c7f48c5000 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 17 Jun 2013 17:16:17 +0100 Subject: [PATCH 072/102] 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(-) diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index c04600e26a38..0bb512cfad77 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; From a151427ed086952cc28f1d5f1cda84c33e48e358 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 13 Jun 2013 19:37:35 -0700 Subject: [PATCH 073/102] 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(-) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index d784650d14f0..448ce5e29c56 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 2efa176beab0..9f2e3be2c5b8 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 35487e8ded59..0d91fe52f3f5 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 91470fdbab2a..c0cbbd429bdc 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, From 2c9b48ac3cb2cd2c84c43f235c65b7fc238f6f1f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 16 Jun 2013 09:16:31 +0300 Subject: [PATCH 074/102] 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(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 6916045166eb..565027b1bc73 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 e80dc24ef3e2..4ae2e56e404f 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 f580d30bb784..79e9e1c30562 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; } From d777f98bfa44e5a2748603276e9e9fb6ab999079 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Tue, 11 Jun 2013 13:11:18 +0300 Subject: [PATCH 075/102] 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(-) diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c index 2ca5f2814f4a..0548eeacd573 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; } From 36dd68319c7b56a76ed450b0e470067b5d74b9b2 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Tue, 11 Jun 2013 13:11:19 +0300 Subject: [PATCH 076/102] 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(-) diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c index f4ae074badc3..58d2979b4035 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; } From 35848f68b07df3f917cb13fc3c134718669f569b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 18 Jun 2013 13:04:23 +0800 Subject: [PATCH 077/102] 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(-) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index d6fbb5772b8d..791f45dfc85d 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 bf421e0efa1e..4004e54ef05d 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) { /* From 7b175c46720f8e6b92801bb634c93d1016f80c62 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 18 Jun 2013 12:58:12 -0700 Subject: [PATCH 078/102] MAINTAINERS: add stable_kernel_rules.txt to stable maintainer information This hopefully will help point developers to the proper way that patches should be submitted for inclusion in the stable kernel releases. Reported-by: David Howells Acked-by: David Howells Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 14746ad5ee15..ad1c6ee7caab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7676,6 +7676,7 @@ STABLE BRANCH M: Greg Kroah-Hartman L: stable@vger.kernel.org S: Supported +F: Documentation/stable_kernel_rules.txt STAGING SUBSYSTEM M: Greg Kroah-Hartman From 77864f2e0a824a92bd93b4c9ad22c31d28ff55a6 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:13 +0200 Subject: [PATCH 079/102] 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 diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index a2784d8b5306..df9893972a62 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 fc3547f32d5e..24d52497524d 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 000000000000..c91afd6388f6 --- /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 000000000000..104a5efc2207 --- /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 000000000000..74fb326f4af1 --- /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 000000000000..cb46263c5da2 --- /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); +} From 022c674728f45ad22ce2bb5eb628ac9d3dbc3aea Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:24 +0200 Subject: [PATCH 080/102] FMC: add documentation for the core This is selected sections of the current manual for fmc-bus, as developed outside of the kernel before submission. Like the other patches in this set, it corresponds to commit ab23167f of the repository at ohwr.org Signed-off-by: Alessandro Rubini Acked-by: Juan David Gonzalez Cobas Acked-by: Emilio G. Cota Acked-by: Samuel Iglesias Gonsalvez Acked-by: Rob Landley Signed-off-by: Greg Kroah-Hartman --- Documentation/00-INDEX | 2 + Documentation/fmc/00-INDEX | 26 +++ Documentation/fmc/API.txt | 47 +++++ Documentation/fmc/FMC-and-SDB.txt | 88 +++++++++ Documentation/fmc/carrier.txt | 311 ++++++++++++++++++++++++++++++ Documentation/fmc/identifiers.txt | 168 ++++++++++++++++ Documentation/fmc/mezzanine.txt | 123 ++++++++++++ Documentation/fmc/parameters.txt | 56 ++++++ 8 files changed, 821 insertions(+) create mode 100644 Documentation/fmc/00-INDEX create mode 100644 Documentation/fmc/API.txt create mode 100644 Documentation/fmc/FMC-and-SDB.txt create mode 100644 Documentation/fmc/carrier.txt create mode 100644 Documentation/fmc/identifiers.txt create mode 100644 Documentation/fmc/mezzanine.txt create mode 100644 Documentation/fmc/parameters.txt diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 45b3df936d2f..0c4cc688e89a 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -187,6 +187,8 @@ firmware_class/ - request_firmware() hotplug interface info. flexible-arrays.txt - how to make use of flexible sized arrays in linux +fmc/ + - information about the FMC bus abstraction frv/ - Fujitsu FR-V Linux documentation. futex-requeue-pi.txt diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX new file mode 100644 index 000000000000..71304b7fc491 --- /dev/null +++ b/Documentation/fmc/00-INDEX @@ -0,0 +1,26 @@ + +Documentation in this directory comes from sections of the manual we +wrote for the externally-developed fmc-bus package. The complete +manual as of today (2013-02) is available in PDF format at +http://www.ohwr.org/projects/fmc-bus/files + +00-INDEX + - this file. + +FMC-and-SDB.txt + - What are FMC and SDB, basic concepts for this framework + +API.txt + - The functions that are exported by the bus driver + +parameters.txt + - The module parameters + +carrier.txt + - writing a carrier (a device) + +mezzanine.txt + - writing code for your mezzanine (a driver) + +identifiers.txt + - how identification and matching works diff --git a/Documentation/fmc/API.txt b/Documentation/fmc/API.txt new file mode 100644 index 000000000000..06b06b92c794 --- /dev/null +++ b/Documentation/fmc/API.txt @@ -0,0 +1,47 @@ +Functions Exported by fmc.ko +**************************** + +The FMC core exports the usual 4 functions that are needed for a bus to +work, and a few more: + + int fmc_driver_register(struct fmc_driver *drv); + void fmc_driver_unregister(struct fmc_driver *drv); + int fmc_device_register(struct fmc_device *fmc); + void fmc_device_unregister(struct fmc_device *fmc); + + int fmc_device_register_n(struct fmc_device **fmc, int n); + void fmc_device_unregister_n(struct fmc_device **fmc, int n); + + uint32_t fmc_readl(struct fmc_device *fmc, int offset); + void fmc_writel(struct fmc_device *fmc, uint32_t val, int off); + void *fmc_get_drvdata(struct fmc_device *fmc); + void fmc_set_drvdata(struct fmc_device *fmc, void *data); + + int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw, + int sdb_entry); + +The data structure that describe a device is detailed in *note FMC +Device::, the one that describes a driver is detailed in *note FMC +Driver::. Please note that structures of type fmc_device must be +allocated by the caller, but must not be released after unregistering. +The fmc-bus itself takes care of releasing the structure when their use +count reaches zero - actually, the device model does that in lieu of us. + +The functions to register and unregister n devices are meant to be used +by carriers that host more than one mezzanine. The devices must all be +registered at the same time because if the FPGA is reprogrammed, all +devices in the array are affected. Usually, the driver matching the +first device will reprogram the FPGA, so other devices must know they +are already driven by a reprogrammed FPGA. + +If a carrier hosts slots that are driven by different FPGA devices, it +should register as a group only mezzanines that are driven by the same +FPGA, for the reason outlined above. + +Finally, the fmc_reprogram function calls the reprogram method (see +*note The API Offered by Carriers:: and also scans the memory area for +an SDB tree. You can pass -1 as sdb_entry to disable such scan. +Otherwise, the function fails if no tree is found at the specified +entry point. The function is meant to factorize common code, and by +the time you read this it is already used by the spec-sw and fine-delay +modules. diff --git a/Documentation/fmc/FMC-and-SDB.txt b/Documentation/fmc/FMC-and-SDB.txt new file mode 100644 index 000000000000..fa14e0b24521 --- /dev/null +++ b/Documentation/fmc/FMC-and-SDB.txt @@ -0,0 +1,88 @@ + +FMC (FPGA Mezzanine Card) is the standard we use for our I/O devices, +in the context of White Rabbit and related hardware. + +In our I/O environments we need to write drivers for each mezzanine +card, and such drivers must work regardless of the carrier being used. +To achieve this, we abstract the FMC interface. + +We have a carrier for PCI-E called SPEC and one for VME called SVEC, +but more are planned. Also, we support stand-alone devices (usually +plugged on a SPEC card), controlled through Etherbone, developed by GSI. + +Code and documentation for the FMC bus was born as part of the spec-sw +project, but now it lives in its own project. Other projects, i.e. +software support for the various carriers, should include this as a +submodule. + +The most up to date version of code and documentation is always +available from the repository you can clone from: + + git://ohwr.org/fmc-projects/fmc-bus.git (read-only) + git@ohwr.org:fmc-projects/fmc-bus.git (read-write for developers) + +Selected versions of the documentation, as well as complete tar +archives for selected revisions are placed to the Files section of the +project: `http://www.ohwr.org/projects/fmc-bus/files' + + +What is FMC +*********** + +FMC, as said, stands for "FPGA Mezzanine Card". It is a standard +developed by the VME consortium called VITA (VMEbus International Trade +Association and ratified by ANSI, the American National Standard +Institute. The official documentation is called "ANSI-VITA 57.1". + +The FMC card is an almost square PCB, around 70x75 millimeters, that is +called mezzanine in this document. It usually lives plugged into +another PCB for power supply and control; such bigger circuit board is +called carrier from now on, and a single carrier may host more than one +mezzanine. + +In the typical application the mezzanine is mostly analog while the +carrier is mostly digital, and hosts an FPGA that must be configured to +match the specific mezzanine and the desired application. Thus, you may +need to load different FPGA images to drive different instances of the +same mezzanine. + +FMC, as such, is not a bus in the usual meaning of the term, because +most carriers have only one connector, and carriers with several +connectors have completely separate electrical connections to them. +This package, however, implements a bus as a software abstraction. + + +What is SDB +*********** + +SDB (Self Describing Bus) is a set of data structures that we use for +enumerating the internal structure of an FPGA image. We also use it as +a filesystem inside the FMC EEPROM. + +SDB is not mandatory for use of this FMC kernel bus, but if you have SDB +this package can make good use of it. SDB itself is developed in the +fpga-config-space OHWR project. The link to the repository is +`git://ohwr.org/hdl-core-lib/fpga-config-space.git' and what is used in +this project lives in the sdbfs subdirectory in there. + +SDB support for FMC is described in *note FMC Identification:: and +*note SDB Support:: + + +SDB Support +*********** + +The fmc.ko bus driver exports a few functions to help drivers taking +advantage of the SDB information that may be present in your own FPGA +memory image. + +The module exports the following functions, in the special header +. The linux/ prefix in the name is there because we +plan to submit it upstream in the future, and don't want to force +changes on our drivers if that happens. + + int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address); + void fmc_show_sdb_tree(struct fmc_device *fmc); + signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor, + uint32_t device, unsigned long *sz); + int fmc_free_sdb_tree(struct fmc_device *fmc); diff --git a/Documentation/fmc/carrier.txt b/Documentation/fmc/carrier.txt new file mode 100644 index 000000000000..173f6d65c88d --- /dev/null +++ b/Documentation/fmc/carrier.txt @@ -0,0 +1,311 @@ +FMC Device +********** + +Within the Linux bus framework, the FMC device is created and +registered by the carrier driver. For example, the PCI driver for the +SPEC card fills a data structure for each SPEC that it drives, and +registers an associated FMC device for each card. The SVEC driver can +do exactly the same for the VME carrier (actually, it should do it +twice, because the SVEC carries two FMC mezzanines). Similarly, an +Etherbone driver will be able to register its own FMC devices, offering +communication primitives through frame exchange. + +The contents of the EEPROM within the FMC are used for identification +purposes, i.e. for matching the device with its own driver. For this +reason the device structure includes a complete copy of the EEPROM +(actually, the carrier driver may choose whether or not to return it - +for example we most likely won't have the whole EEPROM available for +Etherbone devices. + +The following listing shows the current structure defining a device. +Please note that all the machinery is in place but some details may +still change in the future. For this reason, there is a version field +at the beginning of the structure. As usual, the minor number will +change for compatible changes (like a new flag) and the major number +will increase when an incompatible change happens (for example, a +change in layout of some fmc data structures). Device writers should +just set it to the value FMC_VERSION, and be ready to get back -EINVAL +at registration time. + + struct fmc_device { + unsigned long version; + unsigned long flags; + struct module *owner; /* char device must pin it */ + struct fmc_fru_id id; /* for EEPROM-based match */ + struct fmc_operations *op; /* carrier-provided */ + int irq; /* according to host bus. 0 == none */ + int eeprom_len; /* Usually 8kB, may be less */ + int eeprom_addr; /* 0x50, 0x52 etc */ + uint8_t *eeprom; /* Full contents or leading part */ + char *carrier_name; /* "SPEC" or similar, for special use */ + void *carrier_data; /* "struct spec *" or equivalent */ + __iomem void *fpga_base; /* May be NULL (Etherbone) */ + __iomem void *slot_base; /* Set by the driver */ + struct fmc_device **devarray; /* Allocated by the bus */ + int slot_id; /* Index in the slot array */ + int nr_slots; /* Number of slots in this carrier */ + unsigned long memlen; /* Used for the char device */ + struct device dev; /* For Linux use */ + struct device *hwdev; /* The underlying hardware device */ + unsigned long sdbfs_entry; + struct sdb_array *sdb; + uint32_t device_id; /* Filled by the device */ + char *mezzanine_name; /* Defaults to ``fmc'' */ + void *mezzanine_data; + }; + +The meaning of most fields is summarized in the code comment above. + +The following fields must be filled by the carrier driver before +registration: + + * version: must be set to FMC_VERSION. + + * owner: set to MODULE_OWNER. + + * op: the operations to act on the device. + + * irq: number for the mezzanine; may be zero. + + * eeprom_len: length of the following array. + + * eeprom_addr: 0x50 for first mezzanine and so on. + + * eeprom: the full content of the I2C EEPROM. + + * carrier_name. + + * carrier_data: a unique pointer for the carrier. + + * fpga_base: the I/O memory address (may be NULL). + + * slot_id: the index of this slot (starting from zero). + + * memlen: if fpga_base is valid, the length of I/O memory. + + * hwdev: to be used in some dev_err() calls. + + * device_id: a slot-specific unique integer number. + + +Please note that the carrier should read its own EEPROM memory before +registering the device, as well as fill all other fields listed above. + +The following fields should not be assigned, because they are filled +later by either the bus or the device driver: + + * flags. + + * fru_id: filled by the bus, parsing the eeprom. + + * slot_base: filled and used by the driver, if useful to it. + + * devarray: an array og all mezzanines driven by a singe FPGA. + + * nr_slots: set by the core at registration time. + + * dev: used by Linux. + + * sdb: FPGA contents, scanned according to driver's directions. + + * sdbfs_entry: SDB entry point in EEPROM: autodetected. + + * mezzanine_data: available for the driver. + + * mezzanine_name: filled by fmc-bus during identification. + + +Note: mezzanine_data may be redundant, because Linux offers the drvdata +approach, so the field may be removed in later versions of this bus +implementation. + +As I write this, she SPEC carrier is already completely functional in +the fmc-bus environment, and is a good reference to look at. + + +The API Offered by Carriers +=========================== + +The carrier provides a number of methods by means of the +`fmc_operations' structure, which currently is defined like this +(again, it is a moving target, please refer to the header rather than +this document): + + struct fmc_operations { + uint32_t (*readl)(struct fmc_device *fmc, int offset); + void (*writel)(struct fmc_device *fmc, uint32_t value, int offset); + int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw); + int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv); + int (*irq_request)(struct fmc_device *fmc, irq_handler_t h, + char *name, int flags); + void (*irq_ack)(struct fmc_device *fmc); + int (*irq_free)(struct fmc_device *fmc); + int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio, + int ngpio); + int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l); + int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l); + }; + +The individual methods perform the following tasks: + +`readl' +`writel' + These functions access FPGA registers by whatever means the + carrier offers. They are not expected to fail, and most of the time + they will just make a memory access to the host bus. If the + carrier provides a fpga_base pointer, the driver may use direct + access through that pointer. For this reason the header offers the + inline functions fmc_readl and fmc_writel that access fpga_base if + the respective method is NULL. A driver that wants to be portable + and efficient should use fmc_readl and fmc_writel. For Etherbone, + or other non-local carriers, error-management is still to be + defined. + +`validate' + Module parameters are used to manage different applications for + two or more boards of the same kind. Validation is based on the + busid module parameter, if provided, and returns the matching + index in the associated array. See *note Module Parameters:: in in + doubt. If no match is found, `-ENOENT' is returned; if the user + didn't pass `busid=', all devices will pass validation. The value + returned by the validate method can be used as index into other + parameters (for example, some drivers use the `lm32=' parameter in + this way). Such "generic parameters" are documented in *note + Module Parameters::, below. The validate method is used by + `fmc-trivial.ko', described in *note fmc-trivial::. + +`reprogram' + The carrier enumerates FMC devices by loading a standard (or + golden) FPGA binary that allows EEPROM access. Each driver, then, + will need to reprogram the FPGA by calling this function. If the + name argument is NULL, the carrier should reprogram the golden + binary. If the gateware name has been overridden through module + parameters (in a carrier-specific way) the file loaded will match + the parameters. Per-device gateware names can be specified using + the `gateware=' parameter, see *note Module Parameters::. Note: + Clients should call rhe new helper, fmc_reprogram, which both + calls this method and parse the SDB tree of the FPGA. + +`irq_request' +`irq_ack' +`irq_free' + Interrupt management is carrier-specific, so it is abstracted as + operations. The interrupt number is listed in the device + structure, and for the mezzanine driver the number is only + informative. The handler will receive the fmc pointer as dev_id; + the flags argument is passed to the Linux request_irq function, + but fmc-specific flags may be added in the future. You'll most + likely want to pass the `IRQF_SHARED' flag. + +`gpio_config' + The method allows to configure a GPIO pin in the carrier, and read + its current value if it is configured as input. See *note The GPIO + Abstraction:: for details. + +`read_ee' +`write_ee' + Read or write the EEPROM. The functions are expected to be only + called before reprogramming and the carrier should refuse them + with `ENODEV' after reprogramming. The offset is expected to be + within 8kB (the current size), but addresses up to 1MB are + reserved to fit bigger I2C devices in the future. Carriers may + offer access to other internal flash memories using these same + methods: for example the SPEC driver may define that its carrier + I2C memory is seen at offset 1M and the internal SPI flash is seen + at offset 16M. This multiplexing of several flash memories in the + same address space is is carrier-specific and should only be used + by a driver that has verified the `carrier_name' field. + + + +The GPIO Abstraction +==================== + +Support for GPIO pins in the fmc-bus environment is not very +straightforward and deserves special discussion. + +While the general idea of a carrier-independent driver seems to fly, +configuration of specific signals within the carrier needs at least +some knowledge of the carrier itself. For this reason, the specific +driver can request to configure carrier-specific GPIO pins, numbered +from 0 to at most 4095. Configuration is performed by passing a +pointer to an array of struct fmc_gpio items, as well as the length of +the array. This is the data structure: + + struct fmc_gpio { + char *carrier_name; + int gpio; + int _gpio; /* internal use by the carrier */ + int mode; /* GPIOF_DIR_OUT etc, from */ + int irqmode; /* IRQF_TRIGGER_LOW and so on */ + }; + +By specifying a carrier_name for each pin, the driver may access +different pins in different carriers. The gpio_config method is +expected to return the number of pins successfully configured, ignoring +requests for other carriers. However, if no pin is configured (because +no structure at all refers to the current carrier_name), the operation +returns an error so the caller will know that it is running under a +yet-unsupported carrier. + +So, for example, a driver that has been developed and tested on both +the SPEC and the SVEC may request configuration of two different GPIO +pins, and expect one such configuration to succeed - if none succeeds +it most likely means that the current carrier is a still-unknown one. + +If, however, your GPIO pin has a specific known role, you can pass a +special number in the gpio field, using one of the following macros: + + #define FMC_GPIO_RAW(x) (x) /* 4096 of them */ + #define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */ + #define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */ + #define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */ + #define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */ + #define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */ + +Use of virtual GPIO numbers (anything but FMC_GPIO_RAW) is allowed +provided the carrier_name field in the data structure is left +unspecified (NULL). Each carrier is responsible for providing a mapping +between virtual and physical GPIO numbers. The carrier may then use the +_gpio field to cache the result of this mapping. + +All carriers must map their I/O lines to the sets above starting from +zero. The SPEC, for example, maps interrupt pins 0 and 1, and test +points 0 through 3 (even if the test points on the PCB are called +5,6,7,8). + +If, for example, a driver requires a free LED and a test point (for a +scope probe to be plugged at some point during development) it may ask +for FMC_GPIO_LED(0) and FMC_GPIO_TP(0). Each carrier will provide +suitable GPIO pins. Clearly, the person running the drivers will know +the order used by the specific carrier driver in assigning leds and +testpoints, so to make a carrier-dependent use of the diagnostic tools. + +In theory, some form of autodetection should be possible: a driver like +the wr-nic (which uses IRQ(1) on the SPEC card) should configure +IRQ(0), make a test with software-generated interrupts and configure +IRQ(1) if the test fails. This probing step should be used because even +if the wr-nic gateware is known to use IRQ1 on the SPEC, the driver +should be carrier-independent and thus use IRQ(0) as a first bet - +actually, the knowledge that IRQ0 may fail is carrier-dependent +information, but using it doesn't make the driver unsuitable for other +carriers. + +The return value of gpio_config is defined as follows: + + * If no pin in the array can be used by the carrier, `-ENODEV'. + + * If at least one virtual GPIO number cannot be mapped, `-ENOENT'. + + * On success, 0 or positive. The value returned is the number of + high input bits (if no input is configured, the value for success + is 0). + +While I admit the procedure is not completely straightforward, it +allows configuration, input and output with a single carrier operation. +Given the typical use case of FMC devices, GPIO operations are not +expected to ever by in hot paths, and GPIO access so fare has only been +used to configure the interrupt pin, mode and polarity. Especially +reading inputs is not expected to be common. If your device has GPIO +capabilities in the hot path, you should consider using the kernel's +GPIO mechanisms. diff --git a/Documentation/fmc/identifiers.txt b/Documentation/fmc/identifiers.txt new file mode 100644 index 000000000000..3bb577ff0d52 --- /dev/null +++ b/Documentation/fmc/identifiers.txt @@ -0,0 +1,168 @@ +FMC Identification +****************** + +The FMC standard requires every compliant mezzanine to carry +identification information in an I2C EEPROM. The information must be +laid out according to the "IPMI Platform Management FRU Information", +where IPMI is a lie I'd better not expand, and FRU means "Field +Replaceable Unit". + +The FRU information is an intricate unreadable binary blob that must +live at offset 0 of the EEPROM, and typically extends for a few hundred +bytes. The standard allows the application to use all the remaining +storage area of the EEPROM as it wants. + +This chapter explains how to create your own EEPROM image and how to +write it in your mezzanine, as well as how devices and drivers are +paired at run time. EEPROM programming uses tools that are part of this +package and SDB (part of the fpga-config-space package). + +The first sections are only interesting for manufacturers who need to +write the EEPROM. If you are just a software developer writing an FMC +device or driver, you may jump straight to *note SDB Support::. + + +Building the FRU Structure +========================== + +If you want to know the internals of the FRU structure and despair, you +can retrieve the document from +`http://download.intel.com/design/servers/ipmi/FRU1011.pdf' . The +standard is awful and difficult without reason, so we only support the +minimum mandatory subset - we create a simple structure and parse it +back at run time, but we are not able to either generate or parse more +arcane features like non-english languages and 6-bit text. If you need +more items of the FRU standard for your boards, please submit patches. + +This package includes the Python script that Matthieu Cattin wrote to +generate the FRU binary blob, based on an helper libipmi by Manohar +Vanga and Matthieu himself. I changed the test script to receive +parameters from the command line or from the environment (the command +line takes precedence) + +To make a long story short, in order to build a standard-compliant +binary file to be burned in your EEPROM, you need the following items: + + Environment Opt Official Name Default +--------------------------------------------------------------------- + FRU_VENDOR -v "Board Manufacturer" fmc-example + FRU_NAME -n "Board Product Name" mezzanine + FRU_SERIAL -s `Board Serial Number" 0001 + FRU_PART -p "Board Part Number" sample-part + FRU_OUTPUT -o not applicable /dev/stdout + +The "Official Name" above is what you find in the FRU official +documentation, chapter 11, page 7 ("Board Info Area Format"). The +output option is used to save the generated binary to a specific file +name instead of stdout. + +You can pass the items to the FRU generator either in the environment +or on the command line. This package has currently no support for +specifying power consumption or such stuff, but I plan to add it as +soon as I find some time for that. + +FIXME: consumption etc for FRU are here or in PTS? + +The following example creates a binary image for a specific board: + + ./tools/fru-generator -v CERN -n FmcAdc100m14b4cha \ + -s HCCFFIA___-CR000003 -p EDA-02063-V5-0 > eeprom.bin + +The following example shows a script that builds several binary EEPROM +images for a series of boards, changing the serial number for each of +them. The script uses a mix of environment variables and command line +options, and uses the same string patterns shown above. + + #!/bin/sh + + export FRU_VENDOR="CERN" + export FRU_NAME="FmcAdc100m14b4cha" + export FRU_PART="EDA-02063-V5-0" + + serial="HCCFFIA___-CR" + + for number in $(seq 1 50); do + # build number-string "ns" + ns="$(printf %06d $number)" + ./fru-generator -s "${serial}${ns}" > eeprom-${ns}.bin + done + + +Using SDB-FS in the EEPROM +========================== + +If you want to use SDB as a filesystem in the EEPROM device within the +mezzanine, you should create one such filesystem using gensdbfs, from +the fpga-config-space package on OHWR. + +By using an SBD filesystem you can cluster several files in a single +EEPROM, so both the host system and a soft-core running in the FPGA (if +any) can access extra production-time information. + +We chose to use SDB as a storage filesystem because the format is very +simple, and both the host system and the soft-core will likely already +include support code for such format. The SDB library offered by the +fpga-config-space is less than 1kB under LM32, so it proves quite up to +the task. + +The SDB entry point (which acts as a directory listing) cannot live at +offset zero in the flash device, because the FRU information must live +there. To avoid wasting precious storage space while still allowing +for more-than-minimal FRU structures, the fmc.ko will look for the SDB +record at address 256, 512 and 1024. + +In order to generate the complete EEPROM image you'll need a +configuration file for gensdbfs: you tell the program where to place +the sdb entry point, and you must force the FRU data file to be placed +at the beginning of the storage device. If needed, you can also place +other files at a special offset (we sometimes do it for backward +compatibility with drivers we wrote before implementing SDB for flash +memory). + +The directory tools/sdbfs of this package includes a well-commented +example that you may want to use as a starting point (the comments are +in the file called -SDB-CONFIG-). Reading documentation for gensdbfs +is a suggested first step anyways. + +This package (generic FMC bus support) only accesses two files in the +EEPROM: the FRU information, at offset zero, with a suggested filename +of IPMI-FRU and the short name for the mezzanine, in a file called +name. The IPMI-FRU name is not mandatory, but a strongly suggested +choice; the name filename is mandatory, because this is the preferred +short name used by the FMC core. For example, a name of "fdelay" may +supplement a Product Name like "FmcDelay1ns4cha" - exactly as +demonstrated in `tools/sdbfs'. + +Note: SDB access to flash memory is not yet supported, so the short +name currently in use is just the "Product Name" FRU string. + +The example in tools/sdbfs includes an extra file, that is needed by +the fine-delay driver, and must live at a known address of 0x1800. By +running gensdbfs on that directory you can output your binary EEPROM +image (here below spusa$ is the shell prompt): + + spusa$ ../fru-generator -v CERN -n FmcDelay1ns4cha -s proto-0 \ + -p EDA-02267-V3 > IPMI-FRU + spusa$ ls -l + total 16 + -rw-rw-r-- 1 rubini staff 975 Nov 19 18:08 --SDB-CONFIG-- + -rw-rw-r-- 1 rubini staff 216 Nov 19 18:13 IPMI-FRU + -rw-rw-r-- 1 rubini staff 11 Nov 19 18:04 fd-calib + -rw-rw-r-- 1 rubini staff 7 Nov 19 18:04 name + spusa$ sudo gensdbfs . /lib/firmware/fdelay-eeprom.bin + spusa$ sdb-read -l -e 0x100 /lib/firmware/fdelay-eeprom.bin + /home/rubini/wip/sdbfs/userspace/sdb-read: listing format is to be defined + 46696c6544617461:2e202020 00000100-000018ff . + 46696c6544617461:6e616d65 00000200-00000206 name + 46696c6544617461:66642d63 00001800-000018ff fd-calib + 46696c6544617461:49504d49 00000000-000000d7 IPMI-FRU + spusa$ ../fru-dump /lib/firmware/fdelay-eeprom.bin + /lib/firmware/fdelay-eeprom.bin: manufacturer: CERN + /lib/firmware/fdelay-eeprom.bin: product-name: FmcDelay1ns4cha + /lib/firmware/fdelay-eeprom.bin: serial-number: proto-0 + /lib/firmware/fdelay-eeprom.bin: part-number: EDA-02267-V3 + +As expected, the output file is both a proper sdbfs object and an IPMI +FRU information blob. The fd-calib file lives at offset 0x1800 and is +over-allocated to 256 bytes, according to the configuration file for +gensdbfs. diff --git a/Documentation/fmc/mezzanine.txt b/Documentation/fmc/mezzanine.txt new file mode 100644 index 000000000000..87910dbfc91e --- /dev/null +++ b/Documentation/fmc/mezzanine.txt @@ -0,0 +1,123 @@ +FMC Driver +********** + +An FMC driver is concerned with the specific mezzanine and associated +gateware. As such, it is expected to be independent of the carrier +being used: it will perform I/O accesses only by means of +carrier-provided functions. + +The matching between device and driver is based on the content of the +EEPROM (as mandated by the FMC standard) or by the actual cores +configured in the FPGA; the latter technique is used when the FPGA is +already programmed when the device is registered to the bus core. + +In some special cases it is possible for a driver to directly access +FPGA registers, by means of the `fpga_base' field of the device +structure. This may be needed for high-bandwidth peripherals like fast +ADC cards. If the device module registered a remote device (for example +by means of Etherbone), the `fpga_base' pointer will be NULL. +Therefore, drivers must be ready to deal with NULL base pointers, and +fail gracefully. Most driver, however, are not expected to access the +pointer directly but run fmc_readl and fmc_writel instead, which will +work in any case. + +In even more special cases, the driver may access carrier-specific +functionality: the `carrier_name' string allows the driver to check +which is the current carrier and make use of the `carrier_data' +pointer. We chose to use carrier names rather than numeric identifiers +for greater flexibility, but also to avoid a central registry within +the `fmc.h' file - we hope other users will exploit our framework with +their own carriers. An example use of carrier names is in GPIO setup +(see *note The GPIO Abstraction::), although the name match is not +expected to be performed by the driver. If you depend on specific +carriers, please check the carrier name and fail gracefully if your +driver finds it is running in a yet-unknown-to-it environment. + + +ID Table +======== + +Like most other Linux drivers, and FMC driver must list all the devices +which it is able to drive. This is usually done by means of a device +table, but in FMC we can match hardware based either on the contents of +their EEPROM or on the actual FPGA cores that can be enumerated. +Therefore, we have two tables of identifiers. + +Matching of FRU information depends on two names, the manufacturer (or +vendor) and the device (see *note FMC Identification::); for +flexibility during production (i.e. before writing to the EEPROM) the +bus supports a catch-all driver that specifies NULL strings. For this +reason, the table is specified as pointer-and-length, not a a +null-terminated array - the entry with NULL names can be a valid entry. + +Matching on FPGA cores depends on two numeric fields: the 64-bit vendor +number and the 32-bit device number. Support for matching based on +class is not yet implemented. Each device is expected to be uniquely +identified by an array of cores (it matches if all of the cores are +instantiated), and for consistency the list is passed as +pointer-and-length. Several similar devices can be driven by the same +driver, and thus the driver specifies and array of such arrays. + +The complete set of involved data structures is thus the following: + + struct fmc_fru_id { char *manufacturer; char *product_name; }; + struct fmc_sdb_one_id { uint64_t vendor; uint32_t device; }; + struct fmc_sdb_id { struct fmc_sdb_one_id *cores; int cores_nr; }; + + struct fmc_device_id { + struct fmc_fru_id *fru_id; int fru_id_nr; + struct fmc_sdb_id *sdb_id; int sdb_id_nr; + }; + +A better reference, with full explanation, is the header. + + +Module Parameters +================= + +Most of the FMC drivers need the same set of kernel parameters. This +package includes support to implement common parameters by means of +fields in the `fmc_driver' structure and simple macro definitions. + +The parameters are carrier-specific, in that they rely on the busid +concept, that varies among carriers. For the SPEC, the identifier is a +PCI bus and devfn number, 16 bits wide in total; drivers for other +carriers will most likely offer something similar but not identical, +and some code duplication is unavoidable. + +This is the list of parameters that are common to several modules to +see how they are actually used, please look at spec-trivial.c. + +`busid=' + This is an array of integers, listing carrier-specific + identification numbers. For PIC, for example, `0x0400' represents + bus 4, slot 0. If any such ID is specified, the driver will only + accept to drive cards that appear in the list (even if the FMC ID + matches). This is accomplished by the validate carrier method. + +`gateware=' + The argument is an array of strings. If no busid= is specified, + the first string of gateware= is used for all cards; otherwise the + identifiers and gateware names are paired one by one, in the order + specified. + +`show_sdb=' + For modules supporting it, this parameter asks to show the SDB + internal structure by means of kernel messages. It is disabled by + default because those lines tend to hide more important messages, + if you look at the system console while loading the drivers. + Note: the parameter is being obsoleted, because fmc.ko itself now + supports dump_sdb= that applies to every client driver. + + +For example, if you are using the trivial driver to load two different +gateware files to two different cards, you can use the following +parameters to load different binaries to the cards, after looking up +the PCI identifiers. This has been tested with a SPEC carrier. + + insmod fmc-trivial.ko \ + busid=0x0200,0x0400 \ + gateware=fmc/fine-delay.bin,fmc/simple-dio.bin + +Please note that not all sub-modules support all of those parameters. +You can use modinfo to check what is supported by each module. diff --git a/Documentation/fmc/parameters.txt b/Documentation/fmc/parameters.txt new file mode 100644 index 000000000000..59edf088e3a4 --- /dev/null +++ b/Documentation/fmc/parameters.txt @@ -0,0 +1,56 @@ +Module Parameters in fmc.ko +*************************** + +The core driver receives two module parameters, meant to help debugging +client modules. Both parameters can be modified by writing to +/sys/module/fmc/parameters/, because they are used when client drivers +are devices are registered, not when fmc.ko is loaded. + +`dump_eeprom=' + If not zero, the parameter asks the bus controller to dump the + EEPROM of any device that is registered, using printk. + +`dump_sdb=' + If not zero, the parameter prints the SDB tree of every FPGA it is + loaded by fmc_reprogram(). If greater than one, it asks to dump + the binary content of SDB records. This currently only dumps the + top-level SDB array, though. + + +EEPROM dumping avoids repeating lines, since most of the contents is +usually empty and all bits are one or zero. This is an example of the +output: + + [ 6625.850480] spec 0000:02:00.0: FPGA programming successful + [ 6626.139949] spec 0000:02:00.0: Manufacturer: CERN + [ 6626.144666] spec 0000:02:00.0: Product name: FmcDelay1ns4cha + [ 6626.150370] FMC: mezzanine 0: 0000:02:00.0 on SPEC + [ 6626.155179] FMC: dumping eeprom 0x2000 (8192) bytes + [ 6626.160087] 0000: 01 00 00 01 00 0b 00 f3 01 0a 00 a5 85 87 c4 43 + [ 6626.167069] 0010: 45 52 4e cf 46 6d 63 44 65 6c 61 79 31 6e 73 34 + [ 6626.174019] 0020: 63 68 61 c7 70 72 6f 74 6f 2d 30 cc 45 44 41 2d + [ 6626.180975] 0030: 30 32 32 36 37 2d 56 33 da 32 30 31 32 2d 31 31 + [...] + [ 6626.371366] 0200: 66 64 65 6c 61 79 0a 00 00 00 00 00 00 00 00 00 + [ 6626.378359] 0210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + [ 6626.385361] [...] + [ 6626.387308] 1800: 70 6c 61 63 65 68 6f 6c 64 65 72 ff ff ff ff ff + [ 6626.394259] 1810: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff + [ 6626.401250] [...] + +The dump of SDB looks like the following; the example shows the simple +golden gateware for the SPEC card, removing the leading timestamps to +fit the page: + + spec 0000:02:00.0: SDB: 00000651:e6a542c9 WB4-Crossbar-GSI + spec 0000:02:00.0: SDB: 0000ce42:ff07fc47 WR-Periph-Syscon (00000000-000000ff) + FMC: mezzanine 0: 0000:02:00.0 on SPEC + FMC: poor dump of sdb first level: + 0000: 53 44 42 2d 00 02 01 00 00 00 00 00 00 00 00 00 + 0010: 00 00 00 00 00 00 01 ff 00 00 00 00 00 00 06 51 + 0020: e6 a5 42 c9 00 00 00 02 20 12 05 11 57 42 34 2d + 0030: 43 72 6f 73 73 62 61 72 2d 47 53 49 20 20 20 00 + 0040: 00 00 01 01 00 00 00 07 00 00 00 00 00 00 00 00 + 0050: 00 00 00 00 00 00 00 ff 00 00 00 00 00 00 ce 42 + 0060: ff 07 fc 47 00 00 00 01 20 12 03 05 57 52 2d 50 + 0070: 65 72 69 70 68 2d 53 79 73 63 6f 6e 20 20 20 01 From 6c62a895e572145f8aa24f2040d1bb8eff473313 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:35 +0200 Subject: [PATCH 081/102] 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 diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 71304b7fc491..34df5cfc0c41 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 000000000000..e85b74a4ae30 --- /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 e28790267c69..505e96b02a93 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 df9893972a62..9832b79976b1 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 000000000000..bec94ac0764c --- /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"); From 056d83f3c30c398f14eb879f1d1707e3a7808f4a Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:46 +0200 Subject: [PATCH 082/102] 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 diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 34df5cfc0c41..c22d6873fd08 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 000000000000..d1910bc67159 --- /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 505e96b02a93..7eacef990be8 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 9832b79976b1..52624e6c0b1f 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 000000000000..6c590f54c79d --- /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"); From 6007b1bd0f752a5c022f7944c65fb96c39d6db3d Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:47:56 +0200 Subject: [PATCH 083/102] 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 diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index c22d6873fd08..177c3e4a9511 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 000000000000..44a3bc678bf0 --- /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 7eacef990be8..2bb1953c9681 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 52624e6c0b1f..13701fa6db79 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 000000000000..2cc680dd604d --- /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"); From 4debfe409b6e550032bfef9733e9f6f7c5613617 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Tue, 18 Jun 2013 23:48:07 +0200 Subject: [PATCH 084/102] 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 diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX index 177c3e4a9511..431c69570f43 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 000000000000..d9ccb278e597 --- /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 2bb1953c9681..c01cf45bc3d8 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 13701fa6db79..b9452919739f 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 000000000000..b0710393ede6 --- /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"); From 05c3e0bb5629b897b0459e4bfb1b93d729033b99 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 18 Jun 2013 22:35:58 +0200 Subject: [PATCH 085/102] 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(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index c122bca669b6..960809f010d7 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 From e3a3c3a205554e564751cd9c0276b2af813d7a92 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 18 Jun 2013 22:34:53 +0200 Subject: [PATCH 086/102] 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 | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 960809f010d7..97415492779e 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) { - dev_err(&pdev->dev, "failed to get IRQ\n"); - goto bad0; - } 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 bad1; + } } + 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; } From e42d50baf43120a78985f13f6e9c8f92fae091c2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Jun 2013 19:01:01 +0300 Subject: [PATCH 087/102] 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(-) diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c index 74fb326f4af1..79adc39221ea 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; From c2955da0e101c3432ae21945e5d77786adf1f094 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Jun 2013 12:49:30 +0200 Subject: [PATCH 088/102] 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(-) diff --git a/drivers/fmc/fmc-fakedev.c b/drivers/fmc/fmc-fakedev.c index bec94ac0764c..941d0930969a 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 a3c498503983..a5f0aa5c2a8d 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); } From 4640a3f2bff64b808bdedadcddf882aa4606f374 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 20 Jun 2013 11:10:18 +0300 Subject: [PATCH 089/102] 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(-) diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c index b0710393ede6..cc031db2d2a3 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; } From 0686ab7a7d05cda3373e311a49b2478bd4f03c5d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 19 Jun 2013 10:42:35 +0800 Subject: [PATCH 090/102] 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(-) diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 0bb512cfad77..94c892f27be4 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; } From e91e84fa4cfeb67a9a096f1adaa1a1a692474724 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 20 Jun 2013 12:58:57 +0800 Subject: [PATCH 091/102] 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(+) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 791f45dfc85d..26c93cf9f6be 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. From 2608fb653103419ac163206ff6d51b7b6528e2d9 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jun 2013 11:28:10 +0800 Subject: [PATCH 092/102] 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(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae4923756d98..88f4096fa078 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 12f2f9e989f7..d84918fe19ab 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 4004e54ef05d..a2464bf07c49 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: From 585d98e00ba7a5e2abe65f7a1eff631cb612289b Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 21 Jun 2013 15:01:05 +0200 Subject: [PATCH 093/102] 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(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 190d4423653f..fd504d358596 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); From 077797117dfa209717e1f3f1416c1101c0e047f0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Jun 2013 10:02:47 +0530 Subject: [PATCH 094/102] 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(-) diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 252434c9ea9d..125d0e5a6887 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), }, }; From 34cb27528398738bea94852b99ef8fb05944ec41 Mon Sep 17 00:00:00 2001 From: Vitalii Demianets Date: Thu, 20 Jun 2013 16:36:00 +0300 Subject: [PATCH 095/102] 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(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 97415492779e..bcd72f3a62c3 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); From 21767546e955c3c1705387ca4548db812382fe08 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 09:36:59 +0300 Subject: [PATCH 096/102] 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(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c2534ca5c6f1..788f6478b4ab 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 7dc2af7b6fba..26b157d8bad5 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 7d9509a094e9..4b59cb742dee 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 From 4dfaa9f7020b1ff4bf87899f4797d2efd76e80fd Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 09:37:00 +0300 Subject: [PATCH 097/102] 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(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 788f6478b4ab..11a465a25896 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; } From 206ecfc21121aa272f3f9fa23e1ed252a12e8a5c Mon Sep 17 00:00:00 2001 From: Frode Isaksen Date: Sun, 23 Jun 2013 09:37:01 +0300 Subject: [PATCH 098/102] 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(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 11a465a25896..21d3f5aa8353 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) { From c20c68d535409f2ff000415d5e0578529c016521 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 10:42:49 +0300 Subject: [PATCH 099/102] 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(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 822170f00348..e4f8dec4dc3c 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 79e9e1c30562..15253886f37e 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 80a90319fc29..1aa499782abe 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) From 9049f7932109f3fe189ffa8028f2e23f06a9bd0b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 23 Jun 2013 22:49:04 +0300 Subject: [PATCH 100/102] 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(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 15253886f37e..6fc573cef178 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 1aa499782abe..7b918b2fb894 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) From cee4fbd6cd7608f6dad7539f39e9281125702a16 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 25 Jun 2013 09:59:52 +0530 Subject: [PATCH 101/102] 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(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index bcd72f3a62c3..4eb8eaf71be8 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), }, }; From 380672698b8e64f0b5e418412b1ed370bd366428 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 10:12:48 -0700 Subject: [PATCH 102/102] 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(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index fd504d358596..190d4423653f 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);