Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / staging / uwb / i1480 / dfu / dfu.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Wireless UWB Link 1480
4  * Main driver
5  *
6  * Copyright (C) 2005-2006 Intel Corporation
7  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
8  *
9  * Common code for firmware upload used by the USB and PCI version;
10  * i1480_fw_upload() takes a device descriptor and uses the function
11  * pointers it provides to upload firmware and prepare the PHY.
12  *
13  * As well, provides common functions used by the rest of the code.
14  */
15 #include "i1480-dfu.h"
16 #include <linux/errno.h>
17 #include <linux/delay.h>
18 #include <linux/pci.h>
19 #include <linux/device.h>
20 #include <linux/random.h>
21 #include <linux/export.h>
22 #include "../../uwb.h"
23
24 /*
25  * i1480_rceb_check - Check RCEB for expected field values
26  * @i1480: pointer to device for which RCEB is being checked
27  * @rceb: RCEB being checked
28  * @cmd: which command the RCEB is related to
29  * @context: expected context
30  * @expected_type: expected event type
31  * @expected_event: expected event
32  *
33  * If @cmd is NULL, do not print error messages, but still return an error
34  * code.
35  *
36  * Return 0 if @rceb matches the expected values, -EINVAL otherwise.
37  */
38 int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
39                      const char *cmd, u8 context, u8 expected_type,
40                      unsigned expected_event)
41 {
42         int result = 0;
43         struct device *dev = i1480->dev;
44         if (rceb->bEventContext != context) {
45                 if (cmd)
46                         dev_err(dev, "%s: unexpected context id 0x%02x "
47                                 "(expected 0x%02x)\n", cmd,
48                                 rceb->bEventContext, context);
49                 result = -EINVAL;
50         }
51         if (rceb->bEventType != expected_type) {
52                 if (cmd)
53                         dev_err(dev, "%s: unexpected event type 0x%02x "
54                                 "(expected 0x%02x)\n", cmd,
55                                 rceb->bEventType, expected_type);
56                 result = -EINVAL;
57         }
58         if (le16_to_cpu(rceb->wEvent) != expected_event) {
59                 if (cmd)
60                         dev_err(dev, "%s: unexpected event 0x%04x "
61                                 "(expected 0x%04x)\n", cmd,
62                                 le16_to_cpu(rceb->wEvent), expected_event);
63                 result = -EINVAL;
64         }
65         return result;
66 }
67 EXPORT_SYMBOL_GPL(i1480_rceb_check);
68
69
70 /*
71  * Execute a Radio Control Command
72  *
73  * Command data has to be in i1480->cmd_buf.
74  *
75  * @returns size of the reply data filled in i1480->evt_buf or < 0 errno
76  *          code on error.
77  */
78 ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
79                   size_t reply_size)
80 {
81         ssize_t result;
82         struct uwb_rceb *reply = i1480->evt_buf;
83         struct uwb_rccb *cmd = i1480->cmd_buf;
84         u16 expected_event = reply->wEvent;
85         u8 expected_type = reply->bEventType;
86         u8 context;
87
88         init_completion(&i1480->evt_complete);
89         i1480->evt_result = -EINPROGRESS;
90         do {
91                 get_random_bytes(&context, 1);
92         } while (context == 0x00 || context == 0xff);
93         cmd->bCommandContext = context;
94         result = i1480->cmd(i1480, cmd_name, cmd_size);
95         if (result < 0)
96                 goto error;
97         /* wait for the callback to report a event was received */
98         result = wait_for_completion_interruptible_timeout(
99                 &i1480->evt_complete, HZ);
100         if (result == 0) {
101                 result = -ETIMEDOUT;
102                 goto error;
103         }
104         if (result < 0)
105                 goto error;
106         result = i1480->evt_result;
107         if (result < 0) {
108                 dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
109                         cmd_name, result);
110                 goto error;
111         }
112         /*
113          * Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
114          * spurious notification after firmware is downloaded. So check whether
115          * the receibed RCEB is such notification before assuming that the
116          * command has failed.
117          */
118         if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
119                              0, 0xfd, 0x0022) == 0) {
120                 /* Now wait for the actual RCEB for this command. */
121                 result = i1480->wait_init_done(i1480);
122                 if (result < 0)
123                         goto error;
124                 result = i1480->evt_result;
125         }
126         if (result != reply_size) {
127                 dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
128                         cmd_name, result, reply_size);
129                 result = -EINVAL;
130                 goto error;
131         }
132         /* Verify we got the right event in response */
133         result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
134                                   expected_type, expected_event);
135 error:
136         return result;
137 }
138 EXPORT_SYMBOL_GPL(i1480_cmd);
139
140
141 static
142 int i1480_print_state(struct i1480 *i1480)
143 {
144         int result;
145         u32 *buf = (u32 *) i1480->cmd_buf;
146
147         result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
148         if (result < 0) {
149                 dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
150                 goto error;
151         }
152         dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
153 error:
154         return result;
155 }
156
157
158 /*
159  * PCI probe, firmware uploader
160  *
161  * _mac_fw_upload() will call rc_setup(), which needs an rc_release().
162  */
163 int i1480_fw_upload(struct i1480 *i1480)
164 {
165         int result;
166
167         result = i1480_pre_fw_upload(i1480);    /* PHY pre fw */
168         if (result < 0 && result != -ENOENT) {
169                 i1480_print_state(i1480);
170                 goto error;
171         }
172         result = i1480_mac_fw_upload(i1480);    /* MAC fw */
173         if (result < 0) {
174                 if (result == -ENOENT)
175                         dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
176                                 i1480->mac_fw_name);
177                 else
178                         i1480_print_state(i1480);
179                 goto error;
180         }
181         result = i1480_phy_fw_upload(i1480);    /* PHY fw */
182         if (result < 0 && result != -ENOENT) {
183                 i1480_print_state(i1480);
184                 goto error_rc_release;
185         }
186         /*
187          * FIXME: find some reliable way to check whether firmware is running
188          * properly. Maybe use some standard request that has no side effects?
189          */
190         dev_info(i1480->dev, "firmware uploaded successfully\n");
191 error_rc_release:
192         if (i1480->rc_release)
193                 i1480->rc_release(i1480);
194         result = 0;
195 error:
196         return result;
197 }
198 EXPORT_SYMBOL_GPL(i1480_fw_upload);