diff options
Diffstat (limited to 'drivers/uwb/i1480/dfu/phy.c')
-rw-r--r-- | drivers/uwb/i1480/dfu/phy.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/drivers/uwb/i1480/dfu/phy.c b/drivers/uwb/i1480/dfu/phy.c new file mode 100644 index 000000000000..3b1a87de8e63 --- /dev/null +++ b/drivers/uwb/i1480/dfu/phy.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Intel Wireless UWB Link 1480 | ||
3 | * PHY parameters upload | ||
4 | * | ||
5 | * Copyright (C) 2005-2006 Intel Corporation | ||
6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | * | ||
22 | * | ||
23 | * Code for uploading the PHY parameters to the PHY through the UWB | ||
24 | * Radio Control interface. | ||
25 | * | ||
26 | * We just send the data through the MPI interface using HWA-like | ||
27 | * commands and then reset the PHY to make sure it is ok. | ||
28 | */ | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/device.h> | ||
31 | #include <linux/firmware.h> | ||
32 | #include <linux/usb/wusb.h> | ||
33 | #include "i1480-dfu.h" | ||
34 | |||
35 | |||
36 | /** | ||
37 | * Write a value array to an address of the MPI interface | ||
38 | * | ||
39 | * @i1480: Device descriptor | ||
40 | * @data: Data array to write | ||
41 | * @size: Size of the data array | ||
42 | * @returns: 0 if ok, < 0 errno code on error. | ||
43 | * | ||
44 | * The data array is organized into pairs: | ||
45 | * | ||
46 | * ADDRESS VALUE | ||
47 | * | ||
48 | * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has | ||
49 | * to be a multiple of three. | ||
50 | */ | ||
51 | static | ||
52 | int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size) | ||
53 | { | ||
54 | int result; | ||
55 | struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf; | ||
56 | struct i1480_evt_confirm *reply = i1480->evt_buf; | ||
57 | |||
58 | BUG_ON(size > 480); | ||
59 | result = -ENOMEM; | ||
60 | cmd->rccb.bCommandType = i1480_CET_VS1; | ||
61 | cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE); | ||
62 | cmd->size = cpu_to_le16(size); | ||
63 | memcpy(cmd->data, data, size); | ||
64 | reply->rceb.bEventType = i1480_CET_VS1; | ||
65 | reply->rceb.wEvent = i1480_CMD_MPI_WRITE; | ||
66 | result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply)); | ||
67 | if (result < 0) | ||
68 | goto out; | ||
69 | if (reply->bResultCode != UWB_RC_RES_SUCCESS) { | ||
70 | dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n", | ||
71 | reply->bResultCode); | ||
72 | result = -EIO; | ||
73 | } | ||
74 | out: | ||
75 | return result; | ||
76 | } | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Read a value array to from an address of the MPI interface | ||
81 | * | ||
82 | * @i1480: Device descriptor | ||
83 | * @data: where to place the read array | ||
84 | * @srcaddr: Where to read from | ||
85 | * @size: Size of the data read array | ||
86 | * @returns: 0 if ok, < 0 errno code on error. | ||
87 | * | ||
88 | * The command data array is organized into pairs ADDR0 ADDR1..., and | ||
89 | * the returned data in ADDR0 VALUE0 ADDR1 VALUE1... | ||
90 | * | ||
91 | * We generate the command array to be a sequential read and then | ||
92 | * rearrange the result. | ||
93 | * | ||
94 | * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply. | ||
95 | * | ||
96 | * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount | ||
97 | * of values we can read is (512 - sizeof(*reply)) / 3 | ||
98 | */ | ||
99 | static | ||
100 | int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size) | ||
101 | { | ||
102 | int result; | ||
103 | struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf; | ||
104 | struct i1480_evt_mpi_read *reply = i1480->evt_buf; | ||
105 | unsigned cnt; | ||
106 | |||
107 | memset(i1480->cmd_buf, 0x69, 512); | ||
108 | memset(i1480->evt_buf, 0x69, 512); | ||
109 | |||
110 | BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3); | ||
111 | result = -ENOMEM; | ||
112 | cmd->rccb.bCommandType = i1480_CET_VS1; | ||
113 | cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ); | ||
114 | cmd->size = cpu_to_le16(3*size); | ||
115 | for (cnt = 0; cnt < size; cnt++) { | ||
116 | cmd->data[cnt].page = (srcaddr + cnt) >> 8; | ||
117 | cmd->data[cnt].offset = (srcaddr + cnt) & 0xff; | ||
118 | } | ||
119 | reply->rceb.bEventType = i1480_CET_VS1; | ||
120 | reply->rceb.wEvent = i1480_CMD_MPI_READ; | ||
121 | result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size, | ||
122 | sizeof(*reply) + 3*size); | ||
123 | if (result < 0) | ||
124 | goto out; | ||
125 | if (reply->bResultCode != UWB_RC_RES_SUCCESS) { | ||
126 | dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n", | ||
127 | reply->bResultCode); | ||
128 | result = -EIO; | ||
129 | } | ||
130 | for (cnt = 0; cnt < size; cnt++) { | ||
131 | if (reply->data[cnt].page != (srcaddr + cnt) >> 8) | ||
132 | dev_err(i1480->dev, "MPI-READ: page inconsistency at " | ||
133 | "index %u: expected 0x%02x, got 0x%02x\n", cnt, | ||
134 | (srcaddr + cnt) >> 8, reply->data[cnt].page); | ||
135 | if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff)) | ||
136 | dev_err(i1480->dev, "MPI-READ: offset inconsistency at " | ||
137 | "index %u: expected 0x%02x, got 0x%02x\n", cnt, | ||
138 | (srcaddr + cnt) & 0x00ff, | ||
139 | reply->data[cnt].offset); | ||
140 | data[cnt] = reply->data[cnt].value; | ||
141 | } | ||
142 | result = 0; | ||
143 | out: | ||
144 | return result; | ||
145 | } | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Upload a PHY firmware, wait for it to start | ||
150 | * | ||
151 | * @i1480: Device instance | ||
152 | * @fw_name: Name of the file that contains the firmware | ||
153 | * | ||
154 | * We assume the MAC fw is up and running. This means we can use the | ||
155 | * MPI interface to write the PHY firmware. Once done, we issue an | ||
156 | * MBOA Reset, which will force the MAC to reset and reinitialize the | ||
157 | * PHY. If that works, we are ready to go. | ||
158 | * | ||
159 | * Max packet size for the MPI write is 512, so the max buffer is 480 | ||
160 | * (which gives us 160 byte triads of MSB, LSB and VAL for the data). | ||
161 | */ | ||
162 | int i1480_phy_fw_upload(struct i1480 *i1480) | ||
163 | { | ||
164 | int result; | ||
165 | const struct firmware *fw; | ||
166 | const char *data_itr, *data_top; | ||
167 | const size_t MAX_BLK_SIZE = 480; /* 160 triads */ | ||
168 | size_t data_size; | ||
169 | u8 phy_stat; | ||
170 | |||
171 | result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev); | ||
172 | if (result < 0) | ||
173 | goto out; | ||
174 | /* Loop writing data in chunks as big as possible until done. */ | ||
175 | for (data_itr = fw->data, data_top = data_itr + fw->size; | ||
176 | data_itr < data_top; data_itr += MAX_BLK_SIZE) { | ||
177 | data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr)); | ||
178 | result = i1480_mpi_write(i1480, data_itr, data_size); | ||
179 | if (result < 0) | ||
180 | goto error_mpi_write; | ||
181 | } | ||
182 | /* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */ | ||
183 | result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1); | ||
184 | if (result < 0) { | ||
185 | dev_err(i1480->dev, "PHY: can't get status: %d\n", result); | ||
186 | goto error_mpi_status; | ||
187 | } | ||
188 | if (phy_stat != 0) { | ||
189 | result = -ENODEV; | ||
190 | dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat); | ||
191 | goto error_phy_status; | ||
192 | } | ||
193 | dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name); | ||
194 | error_phy_status: | ||
195 | error_mpi_status: | ||
196 | error_mpi_write: | ||
197 | release_firmware(fw); | ||
198 | if (result < 0) | ||
199 | dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), " | ||
200 | "power cycle device\n", i1480->phy_fw_name, result); | ||
201 | out: | ||
202 | return result; | ||
203 | } | ||