]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/uwb/i1480/dfu/dfu.c
Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[mirror_ubuntu-jammy-kernel.git] / drivers / uwb / i1480 / dfu / dfu.c
CommitLineData
04672fe6 1// SPDX-License-Identifier: GPL-2.0-only
1ba47da5
IPG
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 *
1ba47da5
IPG
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/uwb.h>
21#include <linux/random.h>
475c0a6b 22#include <linux/export.h>
1ba47da5 23
bce83697 24/*
b5784f10
AL
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 */
1ba47da5 38int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
b5784f10
AL
39 const char *cmd, u8 context, u8 expected_type,
40 unsigned expected_event)
1ba47da5
IPG
41{
42 int result = 0;
43 struct device *dev = i1480->dev;
44 if (rceb->bEventContext != context) {
b5784f10
AL
45 if (cmd)
46 dev_err(dev, "%s: unexpected context id 0x%02x "
47 "(expected 0x%02x)\n", cmd,
48 rceb->bEventContext, context);
1ba47da5
IPG
49 result = -EINVAL;
50 }
51 if (rceb->bEventType != expected_type) {
b5784f10
AL
52 if (cmd)
53 dev_err(dev, "%s: unexpected event type 0x%02x "
54 "(expected 0x%02x)\n", cmd,
55 rceb->bEventType, expected_type);
1ba47da5
IPG
56 result = -EINVAL;
57 }
58 if (le16_to_cpu(rceb->wEvent) != expected_event) {
b5784f10
AL
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);
1ba47da5
IPG
63 result = -EINVAL;
64 }
65 return result;
66}
67EXPORT_SYMBOL_GPL(i1480_rceb_check);
68
69
bce83697 70/*
1ba47da5
IPG
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 */
78ssize_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
1ba47da5
IPG
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 }
b5784f10
AL
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 }
1ba47da5
IPG
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);
135error:
1ba47da5
IPG
136 return result;
137}
138EXPORT_SYMBOL_GPL(i1480_cmd);
139
140
1ba47da5
IPG
141static
142int 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]);
153error:
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 */
163int 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 }
8c7e8cb8
AL
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 */
1ba47da5
IPG
190 dev_info(i1480->dev, "firmware uploaded successfully\n");
191error_rc_release:
192 if (i1480->rc_release)
193 i1480->rc_release(i1480);
194 result = 0;
195error:
196 return result;
197}
198EXPORT_SYMBOL_GPL(i1480_fw_upload);