.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2017 Broadcom |
---|
5 | | - * |
---|
6 | | - * Released under the GPLv2 only. |
---|
7 | | - * SPDX-License-Identifier: GPL-2.0 |
---|
8 | 6 | */ |
---|
9 | 7 | |
---|
10 | 8 | /* |
---|
.. | .. |
---|
25 | 23 | * - BE kernel + LE firmware image |
---|
26 | 24 | * - BE kernel + BE firmware image |
---|
27 | 25 | * |
---|
28 | | - * The DPCU always runs in big endian mode. The firwmare image, however, can |
---|
| 26 | + * The DPCU always runs in big endian mode. The firmware image, however, can |
---|
29 | 27 | * be in either format. Also, communication between host CPU and DCPU is |
---|
30 | 28 | * always in little endian. |
---|
31 | 29 | */ |
---|
.. | .. |
---|
35 | 33 | #include <linux/io.h> |
---|
36 | 34 | #include <linux/module.h> |
---|
37 | 35 | #include <linux/of_address.h> |
---|
| 36 | +#include <linux/of_device.h> |
---|
38 | 37 | #include <linux/platform_device.h> |
---|
39 | 38 | |
---|
40 | 39 | #define DRVNAME "brcmstb-dpfe" |
---|
41 | | -#define FIRMWARE_NAME "dpfe.bin" |
---|
42 | 40 | |
---|
43 | 41 | /* DCPU register offsets */ |
---|
44 | 42 | #define REG_DCPU_RESET 0x0 |
---|
.. | .. |
---|
61 | 59 | #define DRAM_INFO_MR4 0x4 |
---|
62 | 60 | #define DRAM_INFO_ERROR 0x8 |
---|
63 | 61 | #define DRAM_INFO_MR4_MASK 0xff |
---|
| 62 | +#define DRAM_INFO_MR4_SHIFT 24 /* We need to look at byte 3 */ |
---|
64 | 63 | |
---|
65 | 64 | /* DRAM MR4 Offsets & Masks */ |
---|
66 | 65 | #define DRAM_MR4_REFRESH 0x0 /* Refresh rate */ |
---|
.. | .. |
---|
75 | 74 | #define DRAM_MR4_TH_OFFS_MASK 0x3 |
---|
76 | 75 | #define DRAM_MR4_TUF_MASK 0x1 |
---|
77 | 76 | |
---|
78 | | -/* DRAM Vendor Offsets & Masks */ |
---|
| 77 | +/* DRAM Vendor Offsets & Masks (API v2) */ |
---|
79 | 78 | #define DRAM_VENDOR_MR5 0x0 |
---|
80 | 79 | #define DRAM_VENDOR_MR6 0x4 |
---|
81 | 80 | #define DRAM_VENDOR_MR7 0x8 |
---|
82 | 81 | #define DRAM_VENDOR_MR8 0xc |
---|
83 | 82 | #define DRAM_VENDOR_ERROR 0x10 |
---|
84 | 83 | #define DRAM_VENDOR_MASK 0xff |
---|
| 84 | +#define DRAM_VENDOR_SHIFT 24 /* We need to look at byte 3 */ |
---|
| 85 | + |
---|
| 86 | +/* DRAM Information Offsets & Masks (API v3) */ |
---|
| 87 | +#define DRAM_DDR_INFO_MR4 0x0 |
---|
| 88 | +#define DRAM_DDR_INFO_MR5 0x4 |
---|
| 89 | +#define DRAM_DDR_INFO_MR6 0x8 |
---|
| 90 | +#define DRAM_DDR_INFO_MR7 0xc |
---|
| 91 | +#define DRAM_DDR_INFO_MR8 0x10 |
---|
| 92 | +#define DRAM_DDR_INFO_ERROR 0x14 |
---|
| 93 | +#define DRAM_DDR_INFO_MASK 0xff |
---|
85 | 94 | |
---|
86 | 95 | /* Reset register bits & masks */ |
---|
87 | 96 | #define DCPU_RESET_SHIFT 0x0 |
---|
.. | .. |
---|
111 | 120 | #define DPFE_MSG_TYPE_COMMAND 1 |
---|
112 | 121 | #define DPFE_MSG_TYPE_RESPONSE 2 |
---|
113 | 122 | |
---|
114 | | -#define DELAY_LOOP_MAX 200000 |
---|
| 123 | +#define DELAY_LOOP_MAX 1000 |
---|
115 | 124 | |
---|
116 | 125 | enum dpfe_msg_fields { |
---|
117 | 126 | MSG_HEADER, |
---|
118 | 127 | MSG_COMMAND, |
---|
119 | 128 | MSG_ARG_COUNT, |
---|
120 | 129 | MSG_ARG0, |
---|
121 | | - MSG_CHKSUM, |
---|
122 | | - MSG_FIELD_MAX /* Last entry */ |
---|
| 130 | + MSG_FIELD_MAX = 16 /* Max number of arguments */ |
---|
123 | 131 | }; |
---|
124 | 132 | |
---|
125 | 133 | enum dpfe_commands { |
---|
.. | .. |
---|
127 | 135 | DPFE_CMD_GET_REFRESH, |
---|
128 | 136 | DPFE_CMD_GET_VENDOR, |
---|
129 | 137 | DPFE_CMD_MAX /* Last entry */ |
---|
130 | | -}; |
---|
131 | | - |
---|
132 | | -struct dpfe_msg { |
---|
133 | | - u32 header; |
---|
134 | | - u32 command; |
---|
135 | | - u32 arg_count; |
---|
136 | | - u32 arg0; |
---|
137 | | - u32 chksum; /* This is the sum of all other entries. */ |
---|
138 | 138 | }; |
---|
139 | 139 | |
---|
140 | 140 | /* |
---|
.. | .. |
---|
170 | 170 | bool is_big_endian; |
---|
171 | 171 | }; |
---|
172 | 172 | |
---|
| 173 | +/* API version and corresponding commands */ |
---|
| 174 | +struct dpfe_api { |
---|
| 175 | + int version; |
---|
| 176 | + const char *fw_name; |
---|
| 177 | + const struct attribute_group **sysfs_attrs; |
---|
| 178 | + u32 command[DPFE_CMD_MAX][MSG_FIELD_MAX]; |
---|
| 179 | +}; |
---|
| 180 | + |
---|
173 | 181 | /* Things we need for as long as we are active. */ |
---|
174 | | -struct private_data { |
---|
| 182 | +struct brcmstb_dpfe_priv { |
---|
175 | 183 | void __iomem *regs; |
---|
176 | 184 | void __iomem *dmem; |
---|
177 | 185 | void __iomem *imem; |
---|
178 | 186 | struct device *dev; |
---|
| 187 | + const struct dpfe_api *dpfe_api; |
---|
179 | 188 | struct mutex lock; |
---|
180 | 189 | }; |
---|
181 | 190 | |
---|
182 | | -static const char *error_text[] = { |
---|
183 | | - "Success", "Header code incorrect", "Unknown command or argument", |
---|
184 | | - "Incorrect checksum", "Malformed command", "Timed out", |
---|
| 191 | +/* |
---|
| 192 | + * Forward declaration of our sysfs attribute functions, so we can declare the |
---|
| 193 | + * attribute data structures early. |
---|
| 194 | + */ |
---|
| 195 | +static ssize_t show_info(struct device *, struct device_attribute *, char *); |
---|
| 196 | +static ssize_t show_refresh(struct device *, struct device_attribute *, char *); |
---|
| 197 | +static ssize_t store_refresh(struct device *, struct device_attribute *, |
---|
| 198 | + const char *, size_t); |
---|
| 199 | +static ssize_t show_vendor(struct device *, struct device_attribute *, char *); |
---|
| 200 | +static ssize_t show_dram(struct device *, struct device_attribute *, char *); |
---|
| 201 | + |
---|
| 202 | +/* |
---|
| 203 | + * Declare our attributes early, so they can be referenced in the API data |
---|
| 204 | + * structure. We need to do this, because the attributes depend on the API |
---|
| 205 | + * version. |
---|
| 206 | + */ |
---|
| 207 | +static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL); |
---|
| 208 | +static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh); |
---|
| 209 | +static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL); |
---|
| 210 | +static DEVICE_ATTR(dpfe_dram, 0444, show_dram, NULL); |
---|
| 211 | + |
---|
| 212 | +/* API v2 sysfs attributes */ |
---|
| 213 | +static struct attribute *dpfe_v2_attrs[] = { |
---|
| 214 | + &dev_attr_dpfe_info.attr, |
---|
| 215 | + &dev_attr_dpfe_refresh.attr, |
---|
| 216 | + &dev_attr_dpfe_vendor.attr, |
---|
| 217 | + NULL |
---|
| 218 | +}; |
---|
| 219 | +ATTRIBUTE_GROUPS(dpfe_v2); |
---|
| 220 | + |
---|
| 221 | +/* API v3 sysfs attributes */ |
---|
| 222 | +static struct attribute *dpfe_v3_attrs[] = { |
---|
| 223 | + &dev_attr_dpfe_info.attr, |
---|
| 224 | + &dev_attr_dpfe_dram.attr, |
---|
| 225 | + NULL |
---|
| 226 | +}; |
---|
| 227 | +ATTRIBUTE_GROUPS(dpfe_v3); |
---|
| 228 | + |
---|
| 229 | +/* |
---|
| 230 | + * Old API v2 firmware commands, as defined in the rev 0.61 specification, we |
---|
| 231 | + * use a version set to 1 to denote that it is not compatible with the new API |
---|
| 232 | + * v2 and onwards. |
---|
| 233 | + */ |
---|
| 234 | +static const struct dpfe_api dpfe_api_old_v2 = { |
---|
| 235 | + .version = 1, |
---|
| 236 | + .fw_name = "dpfe.bin", |
---|
| 237 | + .sysfs_attrs = dpfe_v2_groups, |
---|
| 238 | + .command = { |
---|
| 239 | + [DPFE_CMD_GET_INFO] = { |
---|
| 240 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 241 | + [MSG_COMMAND] = 1, |
---|
| 242 | + [MSG_ARG_COUNT] = 1, |
---|
| 243 | + [MSG_ARG0] = 1, |
---|
| 244 | + }, |
---|
| 245 | + [DPFE_CMD_GET_REFRESH] = { |
---|
| 246 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 247 | + [MSG_COMMAND] = 2, |
---|
| 248 | + [MSG_ARG_COUNT] = 1, |
---|
| 249 | + [MSG_ARG0] = 1, |
---|
| 250 | + }, |
---|
| 251 | + [DPFE_CMD_GET_VENDOR] = { |
---|
| 252 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 253 | + [MSG_COMMAND] = 2, |
---|
| 254 | + [MSG_ARG_COUNT] = 1, |
---|
| 255 | + [MSG_ARG0] = 2, |
---|
| 256 | + }, |
---|
| 257 | + } |
---|
185 | 258 | }; |
---|
186 | 259 | |
---|
187 | | -/* List of supported firmware commands */ |
---|
188 | | -static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = { |
---|
189 | | - [DPFE_CMD_GET_INFO] = { |
---|
190 | | - [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
191 | | - [MSG_COMMAND] = 1, |
---|
192 | | - [MSG_ARG_COUNT] = 1, |
---|
193 | | - [MSG_ARG0] = 1, |
---|
194 | | - [MSG_CHKSUM] = 4, |
---|
195 | | - }, |
---|
196 | | - [DPFE_CMD_GET_REFRESH] = { |
---|
197 | | - [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
198 | | - [MSG_COMMAND] = 2, |
---|
199 | | - [MSG_ARG_COUNT] = 1, |
---|
200 | | - [MSG_ARG0] = 1, |
---|
201 | | - [MSG_CHKSUM] = 5, |
---|
202 | | - }, |
---|
203 | | - [DPFE_CMD_GET_VENDOR] = { |
---|
204 | | - [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
205 | | - [MSG_COMMAND] = 2, |
---|
206 | | - [MSG_ARG_COUNT] = 1, |
---|
207 | | - [MSG_ARG0] = 2, |
---|
208 | | - [MSG_CHKSUM] = 6, |
---|
| 260 | +/* |
---|
| 261 | + * API v2 firmware commands, as defined in the rev 0.8 specification, named new |
---|
| 262 | + * v2 here |
---|
| 263 | + */ |
---|
| 264 | +static const struct dpfe_api dpfe_api_new_v2 = { |
---|
| 265 | + .version = 2, |
---|
| 266 | + .fw_name = NULL, /* We expect the firmware to have been downloaded! */ |
---|
| 267 | + .sysfs_attrs = dpfe_v2_groups, |
---|
| 268 | + .command = { |
---|
| 269 | + [DPFE_CMD_GET_INFO] = { |
---|
| 270 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 271 | + [MSG_COMMAND] = 0x101, |
---|
| 272 | + }, |
---|
| 273 | + [DPFE_CMD_GET_REFRESH] = { |
---|
| 274 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 275 | + [MSG_COMMAND] = 0x201, |
---|
| 276 | + }, |
---|
| 277 | + [DPFE_CMD_GET_VENDOR] = { |
---|
| 278 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 279 | + [MSG_COMMAND] = 0x202, |
---|
| 280 | + }, |
---|
| 281 | + } |
---|
| 282 | +}; |
---|
| 283 | + |
---|
| 284 | +/* API v3 firmware commands */ |
---|
| 285 | +static const struct dpfe_api dpfe_api_v3 = { |
---|
| 286 | + .version = 3, |
---|
| 287 | + .fw_name = NULL, /* We expect the firmware to have been downloaded! */ |
---|
| 288 | + .sysfs_attrs = dpfe_v3_groups, |
---|
| 289 | + .command = { |
---|
| 290 | + [DPFE_CMD_GET_INFO] = { |
---|
| 291 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 292 | + [MSG_COMMAND] = 0x0101, |
---|
| 293 | + [MSG_ARG_COUNT] = 1, |
---|
| 294 | + [MSG_ARG0] = 1, |
---|
| 295 | + }, |
---|
| 296 | + [DPFE_CMD_GET_REFRESH] = { |
---|
| 297 | + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
---|
| 298 | + [MSG_COMMAND] = 0x0202, |
---|
| 299 | + [MSG_ARG_COUNT] = 0, |
---|
| 300 | + }, |
---|
| 301 | + /* There's no GET_VENDOR command in API v3. */ |
---|
209 | 302 | }, |
---|
210 | 303 | }; |
---|
211 | 304 | |
---|
212 | | -static bool is_dcpu_enabled(void __iomem *regs) |
---|
| 305 | +static const char *get_error_text(unsigned int i) |
---|
| 306 | +{ |
---|
| 307 | + static const char * const error_text[] = { |
---|
| 308 | + "Success", "Header code incorrect", |
---|
| 309 | + "Unknown command or argument", "Incorrect checksum", |
---|
| 310 | + "Malformed command", "Timed out", "Unknown error", |
---|
| 311 | + }; |
---|
| 312 | + |
---|
| 313 | + if (unlikely(i >= ARRAY_SIZE(error_text))) |
---|
| 314 | + i = ARRAY_SIZE(error_text) - 1; |
---|
| 315 | + |
---|
| 316 | + return error_text[i]; |
---|
| 317 | +} |
---|
| 318 | + |
---|
| 319 | +static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv) |
---|
213 | 320 | { |
---|
214 | 321 | u32 val; |
---|
215 | 322 | |
---|
216 | | - val = readl_relaxed(regs + REG_DCPU_RESET); |
---|
| 323 | + mutex_lock(&priv->lock); |
---|
| 324 | + val = readl_relaxed(priv->regs + REG_DCPU_RESET); |
---|
| 325 | + mutex_unlock(&priv->lock); |
---|
217 | 326 | |
---|
218 | 327 | return !(val & DCPU_RESET_MASK); |
---|
219 | 328 | } |
---|
220 | 329 | |
---|
221 | | -static void __disable_dcpu(void __iomem *regs) |
---|
| 330 | +static void __disable_dcpu(struct brcmstb_dpfe_priv *priv) |
---|
222 | 331 | { |
---|
223 | 332 | u32 val; |
---|
224 | 333 | |
---|
225 | | - if (!is_dcpu_enabled(regs)) |
---|
| 334 | + if (!is_dcpu_enabled(priv)) |
---|
226 | 335 | return; |
---|
227 | 336 | |
---|
| 337 | + mutex_lock(&priv->lock); |
---|
| 338 | + |
---|
228 | 339 | /* Put DCPU in reset if it's running. */ |
---|
229 | | - val = readl_relaxed(regs + REG_DCPU_RESET); |
---|
| 340 | + val = readl_relaxed(priv->regs + REG_DCPU_RESET); |
---|
230 | 341 | val |= (1 << DCPU_RESET_SHIFT); |
---|
231 | | - writel_relaxed(val, regs + REG_DCPU_RESET); |
---|
| 342 | + writel_relaxed(val, priv->regs + REG_DCPU_RESET); |
---|
| 343 | + |
---|
| 344 | + mutex_unlock(&priv->lock); |
---|
232 | 345 | } |
---|
233 | 346 | |
---|
234 | | -static void __enable_dcpu(void __iomem *regs) |
---|
| 347 | +static void __enable_dcpu(struct brcmstb_dpfe_priv *priv) |
---|
235 | 348 | { |
---|
| 349 | + void __iomem *regs = priv->regs; |
---|
236 | 350 | u32 val; |
---|
| 351 | + |
---|
| 352 | + mutex_lock(&priv->lock); |
---|
237 | 353 | |
---|
238 | 354 | /* Clear mailbox registers. */ |
---|
239 | 355 | writel_relaxed(0, regs + REG_TO_DCPU_MBOX); |
---|
.. | .. |
---|
248 | 364 | val = readl_relaxed(regs + REG_DCPU_RESET); |
---|
249 | 365 | val &= ~(1 << DCPU_RESET_SHIFT); |
---|
250 | 366 | writel_relaxed(val, regs + REG_DCPU_RESET); |
---|
| 367 | + |
---|
| 368 | + mutex_unlock(&priv->lock); |
---|
251 | 369 | } |
---|
252 | 370 | |
---|
253 | | -static unsigned int get_msg_chksum(const u32 msg[]) |
---|
| 371 | +static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) |
---|
254 | 372 | { |
---|
255 | 373 | unsigned int sum = 0; |
---|
256 | 374 | unsigned int i; |
---|
257 | 375 | |
---|
258 | 376 | /* Don't include the last field in the checksum. */ |
---|
259 | | - for (i = 0; i < MSG_FIELD_MAX - 1; i++) |
---|
| 377 | + for (i = 0; i < max; i++) |
---|
260 | 378 | sum += msg[i]; |
---|
261 | 379 | |
---|
262 | 380 | return sum; |
---|
263 | 381 | } |
---|
264 | 382 | |
---|
265 | | -static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, |
---|
| 383 | +static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response, |
---|
266 | 384 | char *buf, ssize_t *size) |
---|
267 | 385 | { |
---|
268 | 386 | unsigned int msg_type; |
---|
269 | 387 | unsigned int offset; |
---|
270 | 388 | void __iomem *ptr = NULL; |
---|
| 389 | + |
---|
| 390 | + /* There is no need to use this function for API v3 or later. */ |
---|
| 391 | + if (unlikely(priv->dpfe_api->version >= 3)) |
---|
| 392 | + return NULL; |
---|
271 | 393 | |
---|
272 | 394 | msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK; |
---|
273 | 395 | offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK; |
---|
.. | .. |
---|
296 | 418 | return ptr; |
---|
297 | 419 | } |
---|
298 | 420 | |
---|
299 | | -static int __send_command(struct private_data *priv, unsigned int cmd, |
---|
| 421 | +static void __finalize_command(struct brcmstb_dpfe_priv *priv) |
---|
| 422 | +{ |
---|
| 423 | + unsigned int release_mbox; |
---|
| 424 | + |
---|
| 425 | + /* |
---|
| 426 | + * It depends on the API version which MBOX register we have to write to |
---|
| 427 | + * to signal we are done. |
---|
| 428 | + */ |
---|
| 429 | + release_mbox = (priv->dpfe_api->version < 2) |
---|
| 430 | + ? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX; |
---|
| 431 | + writel_relaxed(0, priv->regs + release_mbox); |
---|
| 432 | +} |
---|
| 433 | + |
---|
| 434 | +static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd, |
---|
300 | 435 | u32 result[]) |
---|
301 | 436 | { |
---|
302 | | - const u32 *msg = dpfe_commands[cmd]; |
---|
303 | 437 | void __iomem *regs = priv->regs; |
---|
304 | | - unsigned int i, chksum; |
---|
| 438 | + unsigned int i, chksum, chksum_idx; |
---|
| 439 | + const u32 *msg; |
---|
305 | 440 | int ret = 0; |
---|
306 | 441 | u32 resp; |
---|
307 | 442 | |
---|
308 | 443 | if (cmd >= DPFE_CMD_MAX) |
---|
309 | 444 | return -1; |
---|
310 | 445 | |
---|
| 446 | + msg = priv->dpfe_api->command[cmd]; |
---|
| 447 | + |
---|
311 | 448 | mutex_lock(&priv->lock); |
---|
312 | 449 | |
---|
| 450 | + /* Wait for DCPU to become ready */ |
---|
| 451 | + for (i = 0; i < DELAY_LOOP_MAX; i++) { |
---|
| 452 | + resp = readl_relaxed(regs + REG_TO_HOST_MBOX); |
---|
| 453 | + if (resp == 0) |
---|
| 454 | + break; |
---|
| 455 | + msleep(1); |
---|
| 456 | + } |
---|
| 457 | + if (resp != 0) { |
---|
| 458 | + mutex_unlock(&priv->lock); |
---|
| 459 | + return -ffs(DCPU_RET_ERR_TIMEDOUT); |
---|
| 460 | + } |
---|
| 461 | + |
---|
| 462 | + /* Compute checksum over the message */ |
---|
| 463 | + chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1; |
---|
| 464 | + chksum = get_msg_chksum(msg, chksum_idx); |
---|
| 465 | + |
---|
313 | 466 | /* Write command and arguments to message area */ |
---|
314 | | - for (i = 0; i < MSG_FIELD_MAX; i++) |
---|
315 | | - writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); |
---|
| 467 | + for (i = 0; i < MSG_FIELD_MAX; i++) { |
---|
| 468 | + if (i == chksum_idx) |
---|
| 469 | + writel_relaxed(chksum, regs + DCPU_MSG_RAM(i)); |
---|
| 470 | + else |
---|
| 471 | + writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); |
---|
| 472 | + } |
---|
316 | 473 | |
---|
317 | 474 | /* Tell DCPU there is a command waiting */ |
---|
318 | 475 | writel_relaxed(1, regs + REG_TO_DCPU_MBOX); |
---|
.. | .. |
---|
323 | 480 | resp = readl_relaxed(regs + REG_TO_HOST_MBOX); |
---|
324 | 481 | if (resp > 0) |
---|
325 | 482 | break; |
---|
326 | | - udelay(5); |
---|
| 483 | + msleep(1); |
---|
327 | 484 | } |
---|
328 | 485 | |
---|
329 | 486 | if (i == DELAY_LOOP_MAX) { |
---|
.. | .. |
---|
333 | 490 | /* Read response data */ |
---|
334 | 491 | for (i = 0; i < MSG_FIELD_MAX; i++) |
---|
335 | 492 | result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i)); |
---|
| 493 | + chksum_idx = result[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1; |
---|
336 | 494 | } |
---|
337 | 495 | |
---|
338 | 496 | /* Tell DCPU we are done */ |
---|
339 | | - writel_relaxed(0, regs + REG_TO_HOST_MBOX); |
---|
| 497 | + __finalize_command(priv); |
---|
340 | 498 | |
---|
341 | 499 | mutex_unlock(&priv->lock); |
---|
342 | 500 | |
---|
.. | .. |
---|
344 | 502 | return ret; |
---|
345 | 503 | |
---|
346 | 504 | /* Verify response */ |
---|
347 | | - chksum = get_msg_chksum(result); |
---|
348 | | - if (chksum != result[MSG_CHKSUM]) |
---|
| 505 | + chksum = get_msg_chksum(result, chksum_idx); |
---|
| 506 | + if (chksum != result[chksum_idx]) |
---|
349 | 507 | resp = DCPU_RET_ERR_CHKSUM; |
---|
350 | 508 | |
---|
351 | 509 | if (resp != DCPU_RET_SUCCESS) { |
---|
.. | .. |
---|
405 | 563 | |
---|
406 | 564 | /* Verify checksum by reading back the firmware from co-processor RAM. */ |
---|
407 | 565 | static int __verify_fw_checksum(struct init_data *init, |
---|
408 | | - struct private_data *priv, |
---|
| 566 | + struct brcmstb_dpfe_priv *priv, |
---|
409 | 567 | const struct dpfe_firmware_header *header, |
---|
410 | 568 | u32 checksum) |
---|
411 | 569 | { |
---|
.. | .. |
---|
459 | 617 | return 0; |
---|
460 | 618 | } |
---|
461 | 619 | |
---|
462 | | -static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, |
---|
463 | | - struct init_data *init) |
---|
| 620 | +static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv) |
---|
464 | 621 | { |
---|
465 | 622 | const struct dpfe_firmware_header *header; |
---|
466 | 623 | unsigned int dmem_size, imem_size; |
---|
467 | | - struct device *dev = &pdev->dev; |
---|
| 624 | + struct device *dev = priv->dev; |
---|
468 | 625 | bool is_big_endian = false; |
---|
469 | | - struct private_data *priv; |
---|
470 | 626 | const struct firmware *fw; |
---|
471 | 627 | const u32 *dmem, *imem; |
---|
| 628 | + struct init_data init; |
---|
472 | 629 | const void *fw_blob; |
---|
473 | 630 | int ret; |
---|
474 | | - |
---|
475 | | - priv = platform_get_drvdata(pdev); |
---|
476 | 631 | |
---|
477 | 632 | /* |
---|
478 | 633 | * Skip downloading the firmware if the DCPU is already running and |
---|
479 | 634 | * responding to commands. |
---|
480 | 635 | */ |
---|
481 | | - if (is_dcpu_enabled(priv->regs)) { |
---|
| 636 | + if (is_dcpu_enabled(priv)) { |
---|
482 | 637 | u32 response[MSG_FIELD_MAX]; |
---|
483 | 638 | |
---|
484 | 639 | ret = __send_command(priv, DPFE_CMD_GET_INFO, response); |
---|
.. | .. |
---|
486 | 641 | return 0; |
---|
487 | 642 | } |
---|
488 | 643 | |
---|
489 | | - ret = request_firmware(&fw, FIRMWARE_NAME, dev); |
---|
490 | | - /* request_firmware() prints its own error messages. */ |
---|
| 644 | + /* |
---|
| 645 | + * If the firmware filename is NULL it means the boot firmware has to |
---|
| 646 | + * download the DCPU firmware for us. If that didn't work, we have to |
---|
| 647 | + * bail, since downloading it ourselves wouldn't work either. |
---|
| 648 | + */ |
---|
| 649 | + if (!priv->dpfe_api->fw_name) |
---|
| 650 | + return -ENODEV; |
---|
| 651 | + |
---|
| 652 | + ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev); |
---|
| 653 | + /* |
---|
| 654 | + * Defer the firmware download if the firmware file couldn't be found. |
---|
| 655 | + * The root file system may not be available yet. |
---|
| 656 | + */ |
---|
491 | 657 | if (ret) |
---|
492 | | - return ret; |
---|
| 658 | + return (ret == -ENOENT) ? -EPROBE_DEFER : ret; |
---|
493 | 659 | |
---|
494 | | - ret = __verify_firmware(init, fw); |
---|
495 | | - if (ret) |
---|
496 | | - return -EFAULT; |
---|
| 660 | + ret = __verify_firmware(&init, fw); |
---|
| 661 | + if (ret) { |
---|
| 662 | + ret = -EFAULT; |
---|
| 663 | + goto release_fw; |
---|
| 664 | + } |
---|
497 | 665 | |
---|
498 | | - __disable_dcpu(priv->regs); |
---|
| 666 | + __disable_dcpu(priv); |
---|
499 | 667 | |
---|
500 | | - is_big_endian = init->is_big_endian; |
---|
501 | | - dmem_size = init->dmem_len; |
---|
502 | | - imem_size = init->imem_len; |
---|
| 668 | + is_big_endian = init.is_big_endian; |
---|
| 669 | + dmem_size = init.dmem_len; |
---|
| 670 | + imem_size = init.imem_len; |
---|
503 | 671 | |
---|
504 | 672 | /* At the beginning of the firmware blob is a header. */ |
---|
505 | 673 | header = (struct dpfe_firmware_header *)fw->data; |
---|
.. | .. |
---|
512 | 680 | |
---|
513 | 681 | ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian); |
---|
514 | 682 | if (ret) |
---|
515 | | - return ret; |
---|
| 683 | + goto release_fw; |
---|
516 | 684 | ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian); |
---|
517 | 685 | if (ret) |
---|
518 | | - return ret; |
---|
| 686 | + goto release_fw; |
---|
519 | 687 | |
---|
520 | | - ret = __verify_fw_checksum(init, priv, header, init->chksum); |
---|
| 688 | + ret = __verify_fw_checksum(&init, priv, header, init.chksum); |
---|
521 | 689 | if (ret) |
---|
522 | | - return ret; |
---|
| 690 | + goto release_fw; |
---|
523 | 691 | |
---|
524 | | - __enable_dcpu(priv->regs); |
---|
| 692 | + __enable_dcpu(priv); |
---|
525 | 693 | |
---|
526 | | - return 0; |
---|
| 694 | +release_fw: |
---|
| 695 | + release_firmware(fw); |
---|
| 696 | + return ret; |
---|
527 | 697 | } |
---|
528 | 698 | |
---|
529 | 699 | static ssize_t generic_show(unsigned int command, u32 response[], |
---|
530 | | - struct device *dev, char *buf) |
---|
| 700 | + struct brcmstb_dpfe_priv *priv, char *buf) |
---|
531 | 701 | { |
---|
532 | | - struct private_data *priv; |
---|
533 | 702 | int ret; |
---|
534 | 703 | |
---|
535 | | - priv = dev_get_drvdata(dev); |
---|
536 | 704 | if (!priv) |
---|
537 | 705 | return sprintf(buf, "ERROR: driver private data not set\n"); |
---|
538 | 706 | |
---|
539 | 707 | ret = __send_command(priv, command, response); |
---|
540 | 708 | if (ret < 0) |
---|
541 | | - return sprintf(buf, "ERROR: %s\n", error_text[-ret]); |
---|
| 709 | + return sprintf(buf, "ERROR: %s\n", get_error_text(-ret)); |
---|
542 | 710 | |
---|
543 | 711 | return 0; |
---|
544 | 712 | } |
---|
.. | .. |
---|
547 | 715 | char *buf) |
---|
548 | 716 | { |
---|
549 | 717 | u32 response[MSG_FIELD_MAX]; |
---|
| 718 | + struct brcmstb_dpfe_priv *priv; |
---|
550 | 719 | unsigned int info; |
---|
551 | 720 | ssize_t ret; |
---|
552 | 721 | |
---|
553 | | - ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf); |
---|
| 722 | + priv = dev_get_drvdata(dev); |
---|
| 723 | + ret = generic_show(DPFE_CMD_GET_INFO, response, priv, buf); |
---|
554 | 724 | if (ret) |
---|
555 | 725 | return ret; |
---|
556 | 726 | |
---|
.. | .. |
---|
568 | 738 | { |
---|
569 | 739 | u32 response[MSG_FIELD_MAX]; |
---|
570 | 740 | void __iomem *info; |
---|
571 | | - struct private_data *priv; |
---|
| 741 | + struct brcmstb_dpfe_priv *priv; |
---|
572 | 742 | u8 refresh, sr_abort, ppre, thermal_offs, tuf; |
---|
573 | 743 | u32 mr4; |
---|
574 | 744 | ssize_t ret; |
---|
575 | 745 | |
---|
576 | | - ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf); |
---|
| 746 | + priv = dev_get_drvdata(dev); |
---|
| 747 | + ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf); |
---|
577 | 748 | if (ret) |
---|
578 | 749 | return ret; |
---|
579 | | - |
---|
580 | | - priv = dev_get_drvdata(dev); |
---|
581 | 750 | |
---|
582 | 751 | info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret); |
---|
583 | 752 | if (!info) |
---|
584 | 753 | return ret; |
---|
585 | 754 | |
---|
586 | | - mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK; |
---|
| 755 | + mr4 = (readl_relaxed(info + DRAM_INFO_MR4) >> DRAM_INFO_MR4_SHIFT) & |
---|
| 756 | + DRAM_INFO_MR4_MASK; |
---|
587 | 757 | |
---|
588 | 758 | refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK; |
---|
589 | 759 | sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK; |
---|
.. | .. |
---|
601 | 771 | const char *buf, size_t count) |
---|
602 | 772 | { |
---|
603 | 773 | u32 response[MSG_FIELD_MAX]; |
---|
604 | | - struct private_data *priv; |
---|
| 774 | + struct brcmstb_dpfe_priv *priv; |
---|
605 | 775 | void __iomem *info; |
---|
606 | 776 | unsigned long val; |
---|
607 | 777 | int ret; |
---|
.. | .. |
---|
610 | 780 | return -EINVAL; |
---|
611 | 781 | |
---|
612 | 782 | priv = dev_get_drvdata(dev); |
---|
613 | | - |
---|
614 | 783 | ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response); |
---|
615 | 784 | if (ret) |
---|
616 | 785 | return ret; |
---|
.. | .. |
---|
625 | 794 | } |
---|
626 | 795 | |
---|
627 | 796 | static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, |
---|
628 | | - char *buf) |
---|
| 797 | + char *buf) |
---|
629 | 798 | { |
---|
630 | 799 | u32 response[MSG_FIELD_MAX]; |
---|
631 | | - struct private_data *priv; |
---|
| 800 | + struct brcmstb_dpfe_priv *priv; |
---|
632 | 801 | void __iomem *info; |
---|
633 | 802 | ssize_t ret; |
---|
634 | | - |
---|
635 | | - ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf); |
---|
636 | | - if (ret) |
---|
637 | | - return ret; |
---|
| 803 | + u32 mr5, mr6, mr7, mr8, err; |
---|
638 | 804 | |
---|
639 | 805 | priv = dev_get_drvdata(dev); |
---|
| 806 | + ret = generic_show(DPFE_CMD_GET_VENDOR, response, priv, buf); |
---|
| 807 | + if (ret) |
---|
| 808 | + return ret; |
---|
640 | 809 | |
---|
641 | 810 | info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret); |
---|
642 | 811 | if (!info) |
---|
643 | 812 | return ret; |
---|
644 | 813 | |
---|
645 | | - return sprintf(buf, "%#x %#x %#x %#x %#x\n", |
---|
646 | | - readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK, |
---|
647 | | - readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK, |
---|
648 | | - readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK, |
---|
649 | | - readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK, |
---|
650 | | - readl_relaxed(info + DRAM_VENDOR_ERROR) & |
---|
651 | | - DRAM_VENDOR_MASK); |
---|
| 814 | + mr5 = (readl_relaxed(info + DRAM_VENDOR_MR5) >> DRAM_VENDOR_SHIFT) & |
---|
| 815 | + DRAM_VENDOR_MASK; |
---|
| 816 | + mr6 = (readl_relaxed(info + DRAM_VENDOR_MR6) >> DRAM_VENDOR_SHIFT) & |
---|
| 817 | + DRAM_VENDOR_MASK; |
---|
| 818 | + mr7 = (readl_relaxed(info + DRAM_VENDOR_MR7) >> DRAM_VENDOR_SHIFT) & |
---|
| 819 | + DRAM_VENDOR_MASK; |
---|
| 820 | + mr8 = (readl_relaxed(info + DRAM_VENDOR_MR8) >> DRAM_VENDOR_SHIFT) & |
---|
| 821 | + DRAM_VENDOR_MASK; |
---|
| 822 | + err = readl_relaxed(info + DRAM_VENDOR_ERROR) & DRAM_VENDOR_MASK; |
---|
| 823 | + |
---|
| 824 | + return sprintf(buf, "%#x %#x %#x %#x %#x\n", mr5, mr6, mr7, mr8, err); |
---|
| 825 | +} |
---|
| 826 | + |
---|
| 827 | +static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, |
---|
| 828 | + char *buf) |
---|
| 829 | +{ |
---|
| 830 | + u32 response[MSG_FIELD_MAX]; |
---|
| 831 | + struct brcmstb_dpfe_priv *priv; |
---|
| 832 | + ssize_t ret; |
---|
| 833 | + u32 mr4, mr5, mr6, mr7, mr8, err; |
---|
| 834 | + |
---|
| 835 | + priv = dev_get_drvdata(dev); |
---|
| 836 | + ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf); |
---|
| 837 | + if (ret) |
---|
| 838 | + return ret; |
---|
| 839 | + |
---|
| 840 | + mr4 = response[MSG_ARG0 + 0] & DRAM_INFO_MR4_MASK; |
---|
| 841 | + mr5 = response[MSG_ARG0 + 1] & DRAM_DDR_INFO_MASK; |
---|
| 842 | + mr6 = response[MSG_ARG0 + 2] & DRAM_DDR_INFO_MASK; |
---|
| 843 | + mr7 = response[MSG_ARG0 + 3] & DRAM_DDR_INFO_MASK; |
---|
| 844 | + mr8 = response[MSG_ARG0 + 4] & DRAM_DDR_INFO_MASK; |
---|
| 845 | + err = response[MSG_ARG0 + 5] & DRAM_DDR_INFO_MASK; |
---|
| 846 | + |
---|
| 847 | + return sprintf(buf, "%#x %#x %#x %#x %#x %#x\n", mr4, mr5, mr6, mr7, |
---|
| 848 | + mr8, err); |
---|
652 | 849 | } |
---|
653 | 850 | |
---|
654 | 851 | static int brcmstb_dpfe_resume(struct platform_device *pdev) |
---|
655 | 852 | { |
---|
656 | | - struct init_data init; |
---|
| 853 | + struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev); |
---|
657 | 854 | |
---|
658 | | - return brcmstb_dpfe_download_firmware(pdev, &init); |
---|
| 855 | + return brcmstb_dpfe_download_firmware(priv); |
---|
659 | 856 | } |
---|
660 | | - |
---|
661 | | -static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL); |
---|
662 | | -static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh); |
---|
663 | | -static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL); |
---|
664 | | -static struct attribute *dpfe_attrs[] = { |
---|
665 | | - &dev_attr_dpfe_info.attr, |
---|
666 | | - &dev_attr_dpfe_refresh.attr, |
---|
667 | | - &dev_attr_dpfe_vendor.attr, |
---|
668 | | - NULL |
---|
669 | | -}; |
---|
670 | | -ATTRIBUTE_GROUPS(dpfe); |
---|
671 | 857 | |
---|
672 | 858 | static int brcmstb_dpfe_probe(struct platform_device *pdev) |
---|
673 | 859 | { |
---|
674 | 860 | struct device *dev = &pdev->dev; |
---|
675 | | - struct private_data *priv; |
---|
676 | | - struct init_data init; |
---|
| 861 | + struct brcmstb_dpfe_priv *priv; |
---|
677 | 862 | struct resource *res; |
---|
678 | 863 | int ret; |
---|
679 | 864 | |
---|
680 | 865 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
---|
681 | 866 | if (!priv) |
---|
682 | 867 | return -ENOMEM; |
---|
| 868 | + |
---|
| 869 | + priv->dev = dev; |
---|
683 | 870 | |
---|
684 | 871 | mutex_init(&priv->lock); |
---|
685 | 872 | platform_set_drvdata(pdev, priv); |
---|
.. | .. |
---|
705 | 892 | return -ENOENT; |
---|
706 | 893 | } |
---|
707 | 894 | |
---|
708 | | - ret = brcmstb_dpfe_download_firmware(pdev, &init); |
---|
709 | | - if (ret) |
---|
710 | | - return ret; |
---|
| 895 | + priv->dpfe_api = of_device_get_match_data(dev); |
---|
| 896 | + if (unlikely(!priv->dpfe_api)) { |
---|
| 897 | + /* |
---|
| 898 | + * It should be impossible to end up here, but to be safe we |
---|
| 899 | + * check anyway. |
---|
| 900 | + */ |
---|
| 901 | + dev_err(dev, "Couldn't determine API\n"); |
---|
| 902 | + return -ENOENT; |
---|
| 903 | + } |
---|
711 | 904 | |
---|
712 | | - ret = sysfs_create_groups(&pdev->dev.kobj, dpfe_groups); |
---|
| 905 | + ret = brcmstb_dpfe_download_firmware(priv); |
---|
| 906 | + if (ret) |
---|
| 907 | + return dev_err_probe(dev, ret, "Couldn't download firmware\n"); |
---|
| 908 | + |
---|
| 909 | + ret = sysfs_create_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); |
---|
713 | 910 | if (!ret) |
---|
714 | | - dev_info(dev, "registered.\n"); |
---|
| 911 | + dev_info(dev, "registered with API v%d.\n", |
---|
| 912 | + priv->dpfe_api->version); |
---|
715 | 913 | |
---|
716 | 914 | return ret; |
---|
717 | 915 | } |
---|
718 | 916 | |
---|
719 | 917 | static int brcmstb_dpfe_remove(struct platform_device *pdev) |
---|
720 | 918 | { |
---|
721 | | - sysfs_remove_groups(&pdev->dev.kobj, dpfe_groups); |
---|
| 919 | + struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev); |
---|
| 920 | + |
---|
| 921 | + sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); |
---|
722 | 922 | |
---|
723 | 923 | return 0; |
---|
724 | 924 | } |
---|
725 | 925 | |
---|
726 | 926 | static const struct of_device_id brcmstb_dpfe_of_match[] = { |
---|
727 | | - { .compatible = "brcm,dpfe-cpu", }, |
---|
| 927 | + /* Use legacy API v2 for a select number of chips */ |
---|
| 928 | + { .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
---|
| 929 | + { .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
---|
| 930 | + { .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
---|
| 931 | + { .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 }, |
---|
| 932 | + /* API v3 is the default going forward */ |
---|
| 933 | + { .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 }, |
---|
728 | 934 | {} |
---|
729 | 935 | }; |
---|
730 | 936 | MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match); |
---|