| .. | .. |
|---|
| 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); |
|---|