{"id":817198,"url":"http://patchwork.ozlabs.org/api/patches/817198/?format=json","web_url":"http://patchwork.ozlabs.org/project/openbmc/patch/1506033838-2078-3-git-send-email-eajames@linux.vnet.ibm.com/","project":{"id":56,"url":"http://patchwork.ozlabs.org/api/projects/56/?format=json","name":"OpenBMC development","link_name":"openbmc","list_id":"openbmc.lists.ozlabs.org","list_email":"openbmc@lists.ozlabs.org","web_url":"http://github.com/openbmc/","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<1506033838-2078-3-git-send-email-eajames@linux.vnet.ibm.com>","list_archive_url":null,"date":"2017-09-21T22:43:57","name":"[linux,dev-4.10,2/3] drivers/fsi/occ: refactor to upstream list state","commit_ref":null,"pull_url":null,"state":"superseded","archived":true,"hash":"6245dc9b11adce61d83469f04b22a0666c84d227","submitter":{"id":70876,"url":"http://patchwork.ozlabs.org/api/people/70876/?format=json","name":"Eddie James","email":"eajames@linux.vnet.ibm.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/openbmc/patch/1506033838-2078-3-git-send-email-eajames@linux.vnet.ibm.com/mbox/","series":[{"id":4493,"url":"http://patchwork.ozlabs.org/api/series/4493/?format=json","web_url":"http://patchwork.ozlabs.org/project/openbmc/list/?series=4493","date":"2017-09-21T22:43:57","name":"drivers/fsi: Fixup client remove functions","version":1,"mbox":"http://patchwork.ozlabs.org/series/4493/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/817198/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/817198/checks/","tags":{},"related":[],"headers":{"Return-Path":"<openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>","X-Original-To":["incoming@patchwork.ozlabs.org","openbmc@lists.ozlabs.org"],"Delivered-To":["patchwork-incoming@bilbo.ozlabs.org","openbmc@lists.ozlabs.org"],"Received":["from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68])\n\t(using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xysBw5GJ1z9s06\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri, 22 Sep 2017 08:45:28 +1000 (AEST)","from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 3xysBv2fV8zDsM4\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri, 22 Sep 2017 08:45:27 +1000 (AEST)","from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com\n\t[148.163.156.1])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 3xys9d0XsTzDqT0\n\tfor <openbmc@lists.ozlabs.org>; Fri, 22 Sep 2017 08:44:20 +1000 (AEST)","from pps.filterd (m0098410.ppops.net [127.0.0.1])\n\tby mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id\n\tv8LMiFim002911\n\tfor <openbmc@lists.ozlabs.org>; Thu, 21 Sep 2017 18:44:17 -0400","from e17.ny.us.ibm.com (e17.ny.us.ibm.com [129.33.205.207])\n\tby mx0a-001b2d01.pphosted.com with ESMTP id 2d4m4f6mhd-1\n\t(version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT)\n\tfor <openbmc@lists.ozlabs.org>; Thu, 21 Sep 2017 18:44:17 -0400","from localhost\n\tby e17.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use\n\tOnly! Violators will be prosecuted\n\tfor <openbmc@lists.ozlabs.org> from <eajames@linux.vnet.ibm.com>;\n\tThu, 21 Sep 2017 18:44:06 -0400","from b01cxnp23034.gho.pok.ibm.com (9.57.198.29)\n\tby e17.ny.us.ibm.com (146.89.104.204) with IBM ESMTP SMTP Gateway:\n\tAuthorized Use Only! Violators will be prosecuted; \n\tThu, 21 Sep 2017 18:44:04 -0400","from b01ledav001.gho.pok.ibm.com (b01ledav001.gho.pok.ibm.com\n\t[9.57.199.106])\n\tby b01cxnp23034.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP\n\tid v8LMi32c40829008; Thu, 21 Sep 2017 22:44:03 GMT","from b01ledav001.gho.pok.ibm.com (unknown [127.0.0.1])\n\tby IMSVA (Postfix) with ESMTP id 310442803E;\n\tThu, 21 Sep 2017 18:43:57 -0400 (EDT)","from oc3016140333.ibm.com (unknown [9.41.174.252])\n\tby b01ledav001.gho.pok.ibm.com (Postfix) with ESMTP id AC1B728048;\n\tThu, 21 Sep 2017 18:43:56 -0400 (EDT)"],"Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=linux.vnet.ibm.com\n\t(client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com;\n\tenvelope-from=eajames@linux.vnet.ibm.com; receiver=<UNKNOWN>)","From":"Eddie James <eajames@linux.vnet.ibm.com>","To":"openbmc@lists.ozlabs.org","Subject":"[PATCH linux dev-4.10 2/3] drivers/fsi/occ: refactor to upstream\n\tlist state","Date":"Thu, 21 Sep 2017 17:43:57 -0500","X-Mailer":"git-send-email 1.8.3.1","In-Reply-To":"<1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com>","References":"<1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com>","X-TM-AS-GCONF":"00","x-cbid":"17092122-0040-0000-0000-000003A6090A","X-IBM-SpamModules-Scores":"","X-IBM-SpamModules-Versions":"BY=3.00007775; HX=3.00000241; KW=3.00000007;\n\tPH=3.00000004; SC=3.00000231; SDB=6.00920429; UDB=6.00462502;\n\tIPR=6.00700651; \n\tBA=6.00005601; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009;\n\tZB=6.00000000; \n\tZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00017240;\n\tXFM=3.00000015; UTC=2017-09-21 22:44:05","X-IBM-AV-DETECTION":"SAVI=unused REMOTE=unused XFE=unused","x-cbparentid":"17092122-0041-0000-0000-0000079B0EB9","Message-Id":"<1506033838-2078-3-git-send-email-eajames@linux.vnet.ibm.com>","X-Proofpoint-Virus-Version":"vendor=fsecure engine=2.50.10432:, ,\n\tdefinitions=2017-09-21_06:, , signatures=0","X-Proofpoint-Spam-Details":"rule=outbound_notspam policy=outbound score=0\n\tspamscore=0 suspectscore=3\n\tmalwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam\n\tadjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000\n\tdefinitions=main-1709210305","X-BeenThere":"openbmc@lists.ozlabs.org","X-Mailman-Version":"2.1.24","Precedence":"list","List-Id":"Development list for OpenBMC <openbmc.lists.ozlabs.org>","List-Unsubscribe":"<https://lists.ozlabs.org/options/openbmc>,\n\t<mailto:openbmc-request@lists.ozlabs.org?subject=unsubscribe>","List-Archive":"<http://lists.ozlabs.org/pipermail/openbmc/>","List-Post":"<mailto:openbmc@lists.ozlabs.org>","List-Help":"<mailto:openbmc-request@lists.ozlabs.org?subject=help>","List-Subscribe":"<https://lists.ozlabs.org/listinfo/openbmc>,\n\t<mailto:openbmc-request@lists.ozlabs.org?subject=subscribe>","Cc":"andrew@aj.id.au, \"Edward A. James\" <eajames@us.ibm.com>","Errors-To":"openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org","Sender":"\"openbmc\"\n\t<openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>"},"content":"From: \"Edward A. James\" <eajames@us.ibm.com>\n\nIncludes various fixes:\n - Fix probe failure scenario\n - General cleanup\n - Reorder remove() operations for safety\n - Add cancel boolean to prevent further xfrs when we're removing\n\nSigned-off-by: Edward A. James <eajames@us.ibm.com>\n---\n drivers/fsi/occ.c   | 227 ++++++++++++++++++++++++++++++++--------------------\n include/linux/occ.h |  20 ++++-\n 2 files changed, 155 insertions(+), 92 deletions(-)","diff":"diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c\nindex 621fbf0..4dd5048 100644\n--- a/drivers/fsi/occ.c\n+++ b/drivers/fsi/occ.c\n@@ -10,16 +10,22 @@\n #include <asm/unaligned.h>\n #include <linux/device.h>\n #include <linux/err.h>\n+#include <linux/errno.h>\n+#include <linux/fs.h>\n #include <linux/fsi-sbefifo.h>\n-#include <linux/init.h>\n+#include <linux/idr.h>\n #include <linux/kernel.h>\n+#include <linux/list.h>\n #include <linux/miscdevice.h>\n #include <linux/module.h>\n #include <linux/mutex.h>\n+#include <linux/occ.h>\n #include <linux/of.h>\n #include <linux/of_platform.h>\n #include <linux/platform_device.h>\n+#include <linux/sched.h>\n #include <linux/slab.h>\n+#include <linux/spinlock.h>\n #include <linux/uaccess.h>\n #include <linux/wait.h>\n #include <linux/workqueue.h>\n@@ -28,49 +34,45 @@\n #define OCC_CMD_DATA_BYTES\t4090\n #define OCC_RESP_DATA_BYTES\t4089\n \n+#define OCC_TIMEOUT_MS\t\t1000\n+#define OCC_CMD_IN_PRG_WAIT_MS\t50\n+\n struct occ {\n \tstruct device *sbefifo;\n \tchar name[32];\n \tint idx;\n \tstruct miscdevice mdev;\n \tstruct list_head xfrs;\n-\tspinlock_t list_lock;\n-\tstruct mutex occ_lock;\n+\tspinlock_t list_lock;\t\t/* lock access to the xfrs list */\n+\tstruct mutex occ_lock;\t\t/* lock access to the hardware */\n \tstruct work_struct work;\n+\tbool cancel;\n };\n \n #define to_occ(x)\tcontainer_of((x), struct occ, mdev)\n \n-struct occ_command {\n-\tu8 seq_no;\n-\tu8 cmd_type;\n-\tu16 data_length;\n-\tu8 data[OCC_CMD_DATA_BYTES];\n-\tu16 checksum;\n-} __packed;\n-\n struct occ_response {\n \tu8 seq_no;\n \tu8 cmd_type;\n \tu8 return_status;\n-\tu16 data_length;\n+\t__be16 data_length;\n \tu8 data[OCC_RESP_DATA_BYTES];\n-\tu16 checksum;\n+\t__be16 checksum;\n } __packed;\n \n /*\n  * transfer flags are NOT mutually exclusive\n- * \n+ *\n  * Initial flags are none; transfer is created and queued from write(). All\n- * \tflags are cleared when the transfer is completed by closing the file or\n- * \treading all of the available response data.\n+ *  flags are cleared when the transfer is completed by closing the file or\n+ *  reading all of the available response data.\n  * XFR_IN_PROGRESS is set when a transfer is started from occ_worker_putsram,\n- * \tand cleared if the transfer fails or occ_worker_getsram completes.\n+ *  and cleared if the transfer fails or occ_worker_getsram completes.\n  * XFR_COMPLETE is set when a transfer fails or finishes occ_worker_getsram.\n  * XFR_CANCELED is set when the transfer's client is released.\n  * XFR_WAITING is set from read() if the transfer isn't complete and\n- * \tNONBLOCKING wasn't specified. Cleared in read() when transfer completes\n- * \tor fails.\n+ *  O_NONBLOCK wasn't specified. Cleared in read() when transfer completes or\n+ *  fails.\n  */\n enum {\n \tXFR_IN_PROGRESS,\n@@ -92,9 +94,9 @@ struct occ_xfr {\n  * client flags\n  *\n  * CLIENT_NONBLOCKING is set during open() if the file was opened with the\n- * \tO_NONBLOCKING flag.\n+ *  O_NONBLOCK flag.\n  * CLIENT_XFR_PENDING is set during write() and cleared when all data has been\n- * \tread.\n+ *  read.\n  */\n enum {\n \tCLIENT_NONBLOCKING,\n@@ -104,7 +106,7 @@ enum {\n struct occ_client {\n \tstruct occ *occ;\n \tstruct occ_xfr xfr;\n-\tspinlock_t lock;\n+\tspinlock_t lock;\t\t/* lock access to the client state */\n \twait_queue_head_t wait;\n \tsize_t read_offset;\n \tunsigned long flags;\n@@ -116,12 +118,15 @@ struct occ_client {\n \n static DEFINE_IDA(occ_ida);\n \n-static void occ_enqueue_xfr(struct occ_xfr *xfr)\n+static int occ_enqueue_xfr(struct occ_xfr *xfr)\n {\n \tint empty;\n \tstruct occ_client *client = to_client(xfr);\n \tstruct occ *occ = client->occ;\n \n+\tif (occ->cancel)\n+\t\treturn -ECANCELED;\n+\n \tspin_lock_irq(&occ->list_lock);\n \n \tempty = list_empty(&occ->xfrs);\n@@ -131,14 +136,15 @@ static void occ_enqueue_xfr(struct occ_xfr *xfr)\n \n \tif (empty)\n \t\tqueue_work(occ_wq, &occ->work);\n+\n+\treturn 0;\n }\n \n static struct occ_client *occ_open_common(struct occ *occ, unsigned long flags)\n {\n-\tstruct occ_client *client;\n+\tstruct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);\n \n-\tclient = kzalloc(sizeof(*client), GFP_KERNEL);\n-\tif (!client)\n+\tif (!client || occ->cancel)\n \t\treturn NULL;\n \n \tclient->occ = occ;\n@@ -172,6 +178,7 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,\n \tint rc;\n \tsize_t bytes;\n \tstruct occ_xfr *xfr = &client->xfr;\n+\tstruct occ *occ = client->occ;\n \n \tif (len > OCC_SRAM_BYTES)\n \t\treturn -EINVAL;\n@@ -183,8 +190,9 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,\n \t\tif (client->read_offset) {\n \t\t\trc = 0;\n \t\t\tclient->read_offset = 0;\n-\t\t} else\n+\t\t} else {\n \t\t\trc = -ENOMSG;\n+\t\t}\n \n \t\tgoto done;\n \t}\n@@ -200,8 +208,11 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,\n \t\tspin_unlock_irq(&client->lock);\n \n \t\trc = wait_event_interruptible(client->wait,\n-\t\t\ttest_bit(XFR_COMPLETE, &xfr->flags) ||\n-\t\t\ttest_bit(XFR_CANCELED, &xfr->flags));\n+\t\t\t\t\t      test_bit(XFR_COMPLETE,\n+\t\t\t\t\t\t       &xfr->flags) ||\n+\t\t\t\t\t      test_bit(XFR_CANCELED,\n+\t\t\t\t\t\t       &xfr->flags) ||\n+\t\t\t\t\t      occ->cancel);\n \n \t\tspin_lock_irq(&client->lock);\n \n@@ -225,12 +236,14 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,\n \n \tbytes = min(len, xfr->resp_data_length - client->read_offset);\n \tif (ubuf) {\n-\t\tif (copy_to_user(ubuf, &xfr->buf[client->read_offset], bytes)) {\n+\t\tif (copy_to_user(ubuf, &xfr->buf[client->read_offset],\n+\t\t\t\t bytes)) {\n \t\t\trc = -EFAULT;\n \t\t\tgoto done;\n \t\t}\n-\t} else\n+\t} else {\n \t\tmemcpy(kbuf, &xfr->buf[client->read_offset], bytes);\n+\t}\n \n \tclient->read_offset += bytes;\n \n@@ -250,12 +263,6 @@ static ssize_t occ_read(struct file *file, char __user *buf, size_t len,\n {\n \tstruct occ_client *client = file->private_data;\n \n-\t/* check this ahead of time so we don't go changing the xfr state\n-\t * needlessly\n-\t */\n-\tif (!access_ok(VERIFY_WRITE, buf, len))\n-\t\treturn -EFAULT;\n-\n \treturn occ_read_common(client, buf, NULL, len);\n }\n \n@@ -272,28 +279,31 @@ static ssize_t occ_write_common(struct occ_client *client,\n \t\treturn -EINVAL;\n \n \tspin_lock_irq(&client->lock);\n-\tif (test_and_set_bit(CLIENT_XFR_PENDING, &client->flags)) {\n+\n+\tif (test_bit(CLIENT_XFR_PENDING, &client->flags)) {\n \t\trc = -EBUSY;\n \t\tgoto done;\n \t}\n \n-\t/* clear out the transfer */\n-\tmemset(xfr, 0, sizeof(*xfr));\n-\n-\txfr->buf[0] = 1;\n+\tmemset(xfr, 0, sizeof(*xfr));\t/* clear out the transfer */\n+\txfr->buf[0] = 1;\t\t/* occ sequence number */\n \n+\t/* Assume user data follows the occ command format.\n+\t * byte 0: command type\n+\t * bytes 1-2: data length (msb first)\n+\t * bytes 3-n: data\n+\t */\n \tif (ubuf) {\n \t\tif (copy_from_user(&xfr->buf[1], ubuf, len)) {\n-\t\t\tkfree(xfr);\n \t\t\trc = -EFAULT;\n \t\t\tgoto done;\n \t\t}\n-\t} else\n+\t} else {\n \t\tmemcpy(&xfr->buf[1], kbuf, len);\n+\t}\n \n \tdata_length = (xfr->buf[2] << 8) + xfr->buf[3];\n \tif (data_length > OCC_CMD_DATA_BYTES) {\n-\t\tkfree(xfr);\n \t\trc = -EINVAL;\n \t\tgoto done;\n \t}\n@@ -306,9 +316,11 @@ static ssize_t occ_write_common(struct occ_client *client,\n \n \txfr->cmd_data_length = data_length + 6;\n \tclient->read_offset = 0;\n+\trc = occ_enqueue_xfr(xfr);\n+\tif (rc)\n+\t\tgoto done;\n \n-\tocc_enqueue_xfr(xfr);\n-\n+\tset_bit(CLIENT_XFR_PENDING, &client->flags);\n \trc = len;\n \n done:\n@@ -321,12 +333,6 @@ static ssize_t occ_write(struct file *file, const char __user *buf,\n {\n \tstruct occ_client *client = file->private_data;\n \n-\t/* check this ahead of time so we don't go changing the xfr state\n-\t * needlessly\n-\t */\n-\tif (!access_ok(VERIFY_READ, buf, len))\n-\t\treturn -EFAULT;\n-\n \treturn occ_write_common(client, buf, NULL, len);\n }\n \n@@ -366,7 +372,7 @@ static int occ_release_common(struct occ_client *client)\n \t\treturn 0;\n \t}\n \n-\t/* operation is in progress; let worker clean up*/\n+\t/* operation is in progress; let worker clean up */\n \tspin_unlock_irq(&occ->list_lock);\n \tspin_unlock_irq(&client->lock);\n \treturn 0;\n@@ -403,7 +409,7 @@ static int occ_write_sbefifo(struct sbefifo_client *client, const char *buf,\n \t\ttotal += rc;\n \t} while (total < len);\n \n-\treturn (total == len) ? 0 : -EMSGSIZE;\n+\treturn (total == len) ? 0 : -ENODATA;\n }\n \n static int occ_read_sbefifo(struct sbefifo_client *client, char *buf,\n@@ -422,7 +428,7 @@ static int occ_read_sbefifo(struct sbefifo_client *client, char *buf,\n \t\ttotal += rc;\n \t} while (total < len);\n \n-\treturn (total == len) ? 0 : -EMSGSIZE;\n+\treturn (total == len) ? 0 : -ENODATA;\n }\n \n static int occ_getsram(struct device *sbefifo, u32 address, u8 *data,\n@@ -430,10 +436,13 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data,\n {\n \tint rc;\n \tu8 *resp;\n-\tu32 buf[5];\n-\tu32 data_len = ((len + 7) / 8) * 8;\n+\t__be32 buf[5];\n+\tu32 data_len = ((len + 7) / 8) * 8;\t/* must be multiples of 8 B */\n \tstruct sbefifo_client *client;\n \n+\t/* Magic sequence to do SBE getsram command. SBE will fetch data from\n+\t * specified SRAM address.\n+\t */\n \tbuf[0] = cpu_to_be32(0x5);\n \tbuf[1] = cpu_to_be32(0xa403);\n \tbuf[2] = cpu_to_be32(1);\n@@ -447,7 +456,7 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data,\n \trc = occ_write_sbefifo(client, (const char *)buf, sizeof(buf));\n \tif (rc)\n \t\tgoto done;\n-\t\n+\n \tresp = kzalloc(data_len, GFP_KERNEL);\n \tif (!resp) {\n \t\trc = -ENOMEM;\n@@ -467,7 +476,7 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data,\n \t    (be32_to_cpu(buf[1]) == 0xC0DEA403))\n \t\tmemcpy(data, resp, len);\n \telse\n-\t\trc = -EFAULT;\n+\t\trc = -EBADMSG;\n \n free:\n \tkfree(resp);\n@@ -481,8 +490,8 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data,\n \t\t       ssize_t len)\n {\n \tint rc;\n-\tu32 *buf;\n-\tu32 data_len = ((len + 7) / 8) * 8;\n+\t__be32 *buf;\n+\tu32 data_len = ((len + 7) / 8) * 8;\t/* must be multiples of 8 B */\n \tsize_t cmd_len = data_len + 20;\n \tstruct sbefifo_client *client;\n \n@@ -490,6 +499,9 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data,\n \tif (!buf)\n \t\treturn -ENOMEM;\n \n+\t/* Magic sequence to do SBE putsram command. SBE will transfer\n+\t * data to specified SRAM address.\n+\t */\n \tbuf[0] = cpu_to_be32(0x5 + (data_len / 4));\n \tbuf[1] = cpu_to_be32(0xa404);\n \tbuf[2] = cpu_to_be32(1);\n@@ -515,7 +527,7 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data,\n \t/* check for good response */\n \tif ((be32_to_cpu(buf[0]) != data_len) ||\n \t    (be32_to_cpu(buf[1]) != 0xC0DEA404))\n-\t\trc = -EFAULT;\n+\t\trc = -EBADMSG;\n \n done:\n \tsbefifo_drv_release(client);\n@@ -527,14 +539,17 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data,\n static int occ_trigger_attn(struct device *sbefifo)\n {\n \tint rc;\n-\tu32 buf[6];\n+\t__be32 buf[6];\n \tstruct sbefifo_client *client;\n \n+\t/* Magic sequence to do SBE putscom command. SBE will write 8 bytes to\n+\t * specified SCOM address.\n+\t */\n \tbuf[0] = cpu_to_be32(0x6);\n \tbuf[1] = cpu_to_be32(0xa202);\n \tbuf[2] = 0;\n \tbuf[3] = cpu_to_be32(0x6D035);\n-\tbuf[4] = cpu_to_be32(0x20010000);\n+\tbuf[4] = cpu_to_be32(0x20010000);\t/* trigger occ attention */\n \tbuf[5] = 0;\n \n \tclient = sbefifo_drv_open(sbefifo, 0);\n@@ -552,7 +567,7 @@ static int occ_trigger_attn(struct device *sbefifo)\n \t/* check for good response */\n \tif ((be32_to_cpu(buf[0]) != 0xC0DEA202) ||\n \t    (be32_to_cpu(buf[1]) & 0x0FFFFFFF))\n-\t\trc = -EFAULT;\n+\t\trc = -EBADMSG;\n \n done:\n \tsbefifo_drv_release(client);\n@@ -564,12 +579,19 @@ static void occ_worker(struct work_struct *work)\n {\n \tint rc = 0, empty, waiting, canceled;\n \tu16 resp_data_length;\n+\tunsigned long start;\n+\tconst unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);\n+\tconst long int wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);\n \tstruct occ_xfr *xfr;\n+\tstruct occ_response *resp;\n \tstruct occ_client *client;\n \tstruct occ *occ = container_of(work, struct occ, work);\n \tstruct device *sbefifo = occ->sbefifo;\n \n again:\n+\tif (occ->cancel)\n+\t\treturn;\n+\n \tspin_lock_irq(&occ->list_lock);\n \n \txfr = list_first_entry_or_null(&occ->xfrs, struct occ_xfr, link);\n@@ -578,11 +600,14 @@ static void occ_worker(struct work_struct *work)\n \t\treturn;\n \t}\n \n+\tresp = (struct occ_response *)xfr->buf;\n \tset_bit(XFR_IN_PROGRESS, &xfr->flags);\n \n \tspin_unlock_irq(&occ->list_lock);\n \tmutex_lock(&occ->occ_lock);\n \n+\t/* write occ command */\n+\tstart = jiffies;\n \trc = occ_putsram(sbefifo, 0xFFFBE000, xfr->buf,\n \t\t\t xfr->cmd_data_length);\n \tif (rc)\n@@ -592,13 +617,26 @@ static void occ_worker(struct work_struct *work)\n \tif (rc)\n \t\tgoto done;\n \n-\trc = occ_getsram(sbefifo, 0xFFFBF000, xfr->buf, 8);\n-\tif (rc)\n-\t\tgoto done;\n+\t/* read occ response */\n+\tdo {\n+\t\trc = occ_getsram(sbefifo, 0xFFFBF000, xfr->buf, 8);\n+\t\tif (rc)\n+\t\t\tgoto done;\n+\n+\t\tif (resp->return_status == OCC_RESP_CMD_IN_PRG) {\n+\t\t\trc = -EALREADY;\n+\n+\t\t\tif (time_after(jiffies, start + timeout))\n+\t\t\t\tbreak;\n \n-\tresp_data_length = (xfr->buf[3] << 8) + xfr->buf[4];\n+\t\t\tset_current_state(TASK_INTERRUPTIBLE);\n+\t\t\tschedule_timeout(wait_time);\n+\t\t}\n+\t} while (rc);\n+\n+\tresp_data_length = get_unaligned_be16(&resp->data_length);\n \tif (resp_data_length > OCC_RESP_DATA_BYTES) {\n-\t\trc = -EDOM;\n+\t\trc = -EMSGSIZE;\n \t\tgoto done;\n \t}\n \n@@ -704,9 +742,6 @@ static int occ_probe(struct platform_device *pdev)\n \tmutex_init(&occ->occ_lock);\n \tINIT_WORK(&occ->work, occ_worker);\n \n-\t/* ensure NULL before we probe children, so they don't hang FSI */\n-\tplatform_set_drvdata(pdev, NULL);\n-\n \tif (dev->of_node) {\n \t\trc = of_property_read_u32(dev->of_node, \"reg\", &reg);\n \t\tif (!rc) {\n@@ -716,23 +751,13 @@ static int occ_probe(struct platform_device *pdev)\n \t\t\tif (occ->idx < 0)\n \t\t\t\tocc->idx = ida_simple_get(&occ_ida, 1, INT_MAX,\n \t\t\t\t\t\t\t  GFP_KERNEL);\n-\t\t} else\n+\t\t} else {\n \t\t\tocc->idx = ida_simple_get(&occ_ida, 1, INT_MAX,\n \t\t\t\t\t\t  GFP_KERNEL);\n-\n-\t\t/* create platform devs for dts child nodes (hwmon, etc) */\n-\t\tfor_each_child_of_node(dev->of_node, np) {\n-\t\t\tsnprintf(child_name, sizeof(child_name), \"occ%d-dev%d\",\n-\t\t\t\t occ->idx, child_idx++);\n-\t\t\tchild = of_platform_device_create(np, child_name, dev);\n-\t\t\tif (!child)\n-\t\t\t\tdev_warn(dev,\n-\t\t\t\t\t \"failed to create child node dev\\n\");\n \t\t}\n-\t} else\n+\t} else {\n \t\tocc->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL);\n-\n-\tplatform_set_drvdata(pdev, occ);\n+\t}\n \n \tsnprintf(occ->name, sizeof(occ->name), \"occ%d\", occ->idx);\n \tocc->mdev.fops = &occ_fops;\n@@ -742,20 +767,42 @@ static int occ_probe(struct platform_device *pdev)\n \n \trc = misc_register(&occ->mdev);\n \tif (rc) {\n-\t\tdev_err(dev, \"failed to register miscdevice\\n\");\n+\t\tdev_err(dev, \"failed to register miscdevice: %d\\n\", rc);\n+\t\tida_simple_remove(&occ_ida, occ->idx);\n \t\treturn rc;\n \t}\n \n+\t/* create platform devs for dts child nodes (hwmon, etc) */\n+\tfor_each_available_child_of_node(dev->of_node, np) {\n+\t\tsnprintf(child_name, sizeof(child_name), \"occ%d-dev%d\",\n+\t\t\t occ->idx, child_idx++);\n+\t\tchild = of_platform_device_create(np, child_name, dev);\n+\t\tif (!child)\n+\t\t\tdev_warn(dev, \"failed to create child node dev\\n\");\n+\t}\n+\n+\tplatform_set_drvdata(pdev, occ);\n+\n \treturn 0;\n }\n \n static int occ_remove(struct platform_device *pdev)\n {\n \tstruct occ *occ = platform_get_drvdata(pdev);\n+\tstruct occ_xfr *xfr;\n+\tstruct occ_client *client;\n+\n+\tocc->cancel = true;\n+\tlist_for_each_entry(xfr, &occ->xfrs, link) {\n+\t\tclient = to_client(xfr);\n+\t\twake_up_interruptible(&client->wait);\n+\t}\n \n-\tflush_work(&occ->work);\n \tmisc_deregister(&occ->mdev);\n \tdevice_for_each_child(&pdev->dev, NULL, occ_unregister_child);\n+\n+\tflush_work(&occ->work);\n+\n \tida_simple_remove(&occ_ida, occ->idx);\n \n \treturn 0;\n@@ -789,6 +836,8 @@ static void occ_exit(void)\n \tdestroy_workqueue(occ_wq);\n \n \tplatform_driver_unregister(&occ_driver);\n+\n+\tida_destroy(&occ_ida);\n }\n \n module_init(occ_init);\ndiff --git a/include/linux/occ.h b/include/linux/occ.h\nindex d78332c..0a4a54a 100644\n--- a/include/linux/occ.h\n+++ b/include/linux/occ.h\n@@ -11,12 +11,26 @@\n  * GNU General Public License for more details.\n  */\n \n-#ifndef __OCC_H__\n-#define __OCC_H__\n+#ifndef LINUX_FSI_OCC_H\n+#define LINUX_FSI_OCC_H\n \n struct device;\n struct occ_client;\n \n+#define OCC_RESP_CMD_IN_PRG\t\t0xFF\n+#define OCC_RESP_SUCCESS\t\t0\n+#define OCC_RESP_CMD_INVAL\t\t0x11\n+#define OCC_RESP_CMD_LEN_INVAL\t\t0x12\n+#define OCC_RESP_DATA_INVAL\t\t0x13\n+#define OCC_RESP_CHKSUM_ERR\t\t0x14\n+#define OCC_RESP_INT_ERR\t\t0x15\n+#define OCC_RESP_BAD_STATE\t\t0x16\n+#define OCC_RESP_CRIT_EXCEPT\t\t0xE0\n+#define OCC_RESP_CRIT_INIT\t\t0xE1\n+#define OCC_RESP_CRIT_WATCHDOG\t\t0xE2\n+#define OCC_RESP_CRIT_OCB\t\t0xE3\n+#define OCC_RESP_CRIT_HW\t\t0xE4\n+\n extern struct occ_client *occ_drv_open(struct device *dev,\n \t\t\t\t       unsigned long flags);\n extern int occ_drv_read(struct occ_client *client, char *buf, size_t len);\n@@ -24,4 +38,4 @@ extern int occ_drv_write(struct occ_client *client, const char *buf,\n \t\t\t size_t len);\n extern void occ_drv_release(struct occ_client *client);\n \n-#endif /* __OCC_H__ */\n+#endif /* LINUX_FSI_OCC_H */\n","prefixes":["linux","dev-4.10","2/3"]}