| .. | .. |
|---|
| 8 | 8 | |
|---|
| 9 | 9 | #include <linux/delay.h> |
|---|
| 10 | 10 | #include <linux/device.h> |
|---|
| 11 | +#include <linux/debugfs.h> |
|---|
| 11 | 12 | #include <linux/interrupt.h> |
|---|
| 13 | +#include <linux/io.h> |
|---|
| 12 | 14 | #include <linux/module.h> |
|---|
| 13 | 15 | #include <linux/mod_devicetable.h> |
|---|
| 16 | +#include <linux/pm_runtime.h> |
|---|
| 14 | 17 | #include <linux/soundwire/sdw_registers.h> |
|---|
| 15 | 18 | #include <linux/soundwire/sdw.h> |
|---|
| 16 | 19 | #include <sound/pcm_params.h> |
|---|
| 17 | 20 | #include <sound/soc.h> |
|---|
| 21 | +#include <linux/workqueue.h> |
|---|
| 18 | 22 | #include "bus.h" |
|---|
| 19 | 23 | #include "cadence_master.h" |
|---|
| 24 | + |
|---|
| 25 | +static int interrupt_mask; |
|---|
| 26 | +module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444); |
|---|
| 27 | +MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); |
|---|
| 20 | 28 | |
|---|
| 21 | 29 | #define CDNS_MCP_CONFIG 0x0 |
|---|
| 22 | 30 | |
|---|
| .. | .. |
|---|
| 42 | 50 | #define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1) |
|---|
| 43 | 51 | #define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0) |
|---|
| 44 | 52 | |
|---|
| 45 | | - |
|---|
| 46 | 53 | #define CDNS_MCP_CMDCTRL 0x8 |
|---|
| 54 | + |
|---|
| 55 | +#define CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2) |
|---|
| 56 | + |
|---|
| 47 | 57 | #define CDNS_MCP_SSPSTAT 0xC |
|---|
| 48 | 58 | #define CDNS_MCP_FRAME_SHAPE 0x10 |
|---|
| 49 | 59 | #define CDNS_MCP_FRAME_SHAPE_INIT 0x14 |
|---|
| 60 | +#define CDNS_MCP_FRAME_SHAPE_COL_MASK GENMASK(2, 0) |
|---|
| 61 | +#define CDNS_MCP_FRAME_SHAPE_ROW_MASK GENMASK(7, 3) |
|---|
| 50 | 62 | |
|---|
| 51 | 63 | #define CDNS_MCP_CONFIG_UPDATE 0x18 |
|---|
| 52 | 64 | #define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0) |
|---|
| .. | .. |
|---|
| 56 | 68 | #define CDNS_MCP_SSP_CTRL1 0x28 |
|---|
| 57 | 69 | #define CDNS_MCP_CLK_CTRL0 0x30 |
|---|
| 58 | 70 | #define CDNS_MCP_CLK_CTRL1 0x38 |
|---|
| 71 | +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0) |
|---|
| 59 | 72 | |
|---|
| 60 | 73 | #define CDNS_MCP_STAT 0x40 |
|---|
| 61 | 74 | |
|---|
| .. | .. |
|---|
| 66 | 79 | #define CDNS_MCP_INTMASK 0x48 |
|---|
| 67 | 80 | |
|---|
| 68 | 81 | #define CDNS_MCP_INT_IRQ BIT(31) |
|---|
| 82 | +#define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17) |
|---|
| 69 | 83 | #define CDNS_MCP_INT_WAKEUP BIT(16) |
|---|
| 70 | 84 | #define CDNS_MCP_INT_SLAVE_RSVD BIT(15) |
|---|
| 71 | 85 | #define CDNS_MCP_INT_SLAVE_ALERT BIT(14) |
|---|
| .. | .. |
|---|
| 75 | 89 | #define CDNS_MCP_INT_DPINT BIT(11) |
|---|
| 76 | 90 | #define CDNS_MCP_INT_CTRL_CLASH BIT(10) |
|---|
| 77 | 91 | #define CDNS_MCP_INT_DATA_CLASH BIT(9) |
|---|
| 92 | +#define CDNS_MCP_INT_PARITY BIT(8) |
|---|
| 78 | 93 | #define CDNS_MCP_INT_CMD_ERR BIT(7) |
|---|
| 94 | +#define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4) |
|---|
| 95 | +#define CDNS_MCP_INT_RX_NE BIT(3) |
|---|
| 79 | 96 | #define CDNS_MCP_INT_RX_WL BIT(2) |
|---|
| 80 | 97 | #define CDNS_MCP_INT_TXE BIT(1) |
|---|
| 98 | +#define CDNS_MCP_INT_TXF BIT(0) |
|---|
| 99 | +#define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2) |
|---|
| 81 | 100 | |
|---|
| 82 | 101 | #define CDNS_MCP_INTSET 0x4C |
|---|
| 83 | 102 | |
|---|
| .. | .. |
|---|
| 114 | 133 | #define CDNS_MCP_CMD_SSP_TAG BIT(31) |
|---|
| 115 | 134 | #define CDNS_MCP_CMD_COMMAND GENMASK(30, 28) |
|---|
| 116 | 135 | #define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24) |
|---|
| 117 | | -#define CDNS_MCP_CMD_REG_ADDR_H GENMASK(23, 16) |
|---|
| 118 | | -#define CDNS_MCP_CMD_REG_ADDR_L GENMASK(15, 8) |
|---|
| 136 | +#define CDNS_MCP_CMD_REG_ADDR GENMASK(23, 8) |
|---|
| 119 | 137 | #define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0) |
|---|
| 120 | 138 | |
|---|
| 121 | 139 | #define CDNS_MCP_CMD_READ 2 |
|---|
| .. | .. |
|---|
| 157 | 175 | #define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8) |
|---|
| 158 | 176 | |
|---|
| 159 | 177 | #define CDNS_PORTCTRL 0x130 |
|---|
| 178 | +#define CDNS_PORTCTRL_TEST_FAILED BIT(1) |
|---|
| 160 | 179 | #define CDNS_PORTCTRL_DIRN BIT(7) |
|---|
| 161 | 180 | #define CDNS_PORTCTRL_BANK_INVERT BIT(8) |
|---|
| 162 | 181 | |
|---|
| .. | .. |
|---|
| 169 | 188 | #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0) |
|---|
| 170 | 189 | |
|---|
| 171 | 190 | /* Driver defaults */ |
|---|
| 172 | | - |
|---|
| 173 | | -#define CDNS_DEFAULT_CLK_DIVIDER 0 |
|---|
| 174 | | -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 |
|---|
| 175 | | -#define CDNS_DEFAULT_SSP_INTERVAL 0x18 |
|---|
| 176 | 191 | #define CDNS_TX_TIMEOUT 2000 |
|---|
| 177 | | - |
|---|
| 178 | | -#define CDNS_PCM_PDI_OFFSET 0x2 |
|---|
| 179 | | -#define CDNS_PDM_PDI_OFFSET 0x6 |
|---|
| 180 | 192 | |
|---|
| 181 | 193 | #define CDNS_SCP_RX_FIFOLEVEL 0x2 |
|---|
| 182 | 194 | |
|---|
| .. | .. |
|---|
| 203 | 215 | cdns_writel(cdns, offset, tmp); |
|---|
| 204 | 216 | } |
|---|
| 205 | 217 | |
|---|
| 206 | | -static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) |
|---|
| 218 | +static int cdns_set_wait(struct sdw_cdns *cdns, int offset, u32 mask, u32 value) |
|---|
| 207 | 219 | { |
|---|
| 208 | 220 | int timeout = 10; |
|---|
| 209 | 221 | u32 reg_read; |
|---|
| 210 | 222 | |
|---|
| 211 | | - writel(value, cdns->registers + offset); |
|---|
| 212 | | - |
|---|
| 213 | | - /* Wait for bit to be self cleared */ |
|---|
| 223 | + /* Wait for bit to be set */ |
|---|
| 214 | 224 | do { |
|---|
| 215 | 225 | reg_read = readl(cdns->registers + offset); |
|---|
| 216 | | - if ((reg_read & value) == 0) |
|---|
| 226 | + if ((reg_read & mask) == value) |
|---|
| 217 | 227 | return 0; |
|---|
| 218 | 228 | |
|---|
| 219 | 229 | timeout--; |
|---|
| 220 | | - udelay(50); |
|---|
| 230 | + usleep_range(50, 100); |
|---|
| 221 | 231 | } while (timeout != 0); |
|---|
| 222 | 232 | |
|---|
| 223 | | - return -EAGAIN; |
|---|
| 233 | + return -ETIMEDOUT; |
|---|
| 224 | 234 | } |
|---|
| 235 | + |
|---|
| 236 | +static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) |
|---|
| 237 | +{ |
|---|
| 238 | + writel(value, cdns->registers + offset); |
|---|
| 239 | + |
|---|
| 240 | + /* Wait for bit to be self cleared */ |
|---|
| 241 | + return cdns_set_wait(cdns, offset, value, 0); |
|---|
| 242 | +} |
|---|
| 243 | + |
|---|
| 244 | +/* |
|---|
| 245 | + * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL |
|---|
| 246 | + * need to be confirmed with a write to MCP_CONFIG_UPDATE |
|---|
| 247 | + */ |
|---|
| 248 | +static int cdns_config_update(struct sdw_cdns *cdns) |
|---|
| 249 | +{ |
|---|
| 250 | + int ret; |
|---|
| 251 | + |
|---|
| 252 | + if (sdw_cdns_is_clock_stop(cdns)) { |
|---|
| 253 | + dev_err(cdns->dev, "Cannot program MCP_CONFIG_UPDATE in ClockStopMode\n"); |
|---|
| 254 | + return -EINVAL; |
|---|
| 255 | + } |
|---|
| 256 | + |
|---|
| 257 | + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, |
|---|
| 258 | + CDNS_MCP_CONFIG_UPDATE_BIT); |
|---|
| 259 | + if (ret < 0) |
|---|
| 260 | + dev_err(cdns->dev, "Config update timedout\n"); |
|---|
| 261 | + |
|---|
| 262 | + return ret; |
|---|
| 263 | +} |
|---|
| 264 | + |
|---|
| 265 | +/* |
|---|
| 266 | + * debugfs |
|---|
| 267 | + */ |
|---|
| 268 | +#ifdef CONFIG_DEBUG_FS |
|---|
| 269 | + |
|---|
| 270 | +#define RD_BUF (2 * PAGE_SIZE) |
|---|
| 271 | + |
|---|
| 272 | +static ssize_t cdns_sprintf(struct sdw_cdns *cdns, |
|---|
| 273 | + char *buf, size_t pos, unsigned int reg) |
|---|
| 274 | +{ |
|---|
| 275 | + return scnprintf(buf + pos, RD_BUF - pos, |
|---|
| 276 | + "%4x\t%8x\n", reg, cdns_readl(cdns, reg)); |
|---|
| 277 | +} |
|---|
| 278 | + |
|---|
| 279 | +static int cdns_reg_show(struct seq_file *s, void *data) |
|---|
| 280 | +{ |
|---|
| 281 | + struct sdw_cdns *cdns = s->private; |
|---|
| 282 | + char *buf; |
|---|
| 283 | + ssize_t ret; |
|---|
| 284 | + int num_ports; |
|---|
| 285 | + int i, j; |
|---|
| 286 | + |
|---|
| 287 | + buf = kzalloc(RD_BUF, GFP_KERNEL); |
|---|
| 288 | + if (!buf) |
|---|
| 289 | + return -ENOMEM; |
|---|
| 290 | + |
|---|
| 291 | + ret = scnprintf(buf, RD_BUF, "Register Value\n"); |
|---|
| 292 | + ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n"); |
|---|
| 293 | + /* 8 MCP registers */ |
|---|
| 294 | + for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32)) |
|---|
| 295 | + ret += cdns_sprintf(cdns, buf, ret, i); |
|---|
| 296 | + |
|---|
| 297 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 298 | + "\nStatus & Intr Registers\n"); |
|---|
| 299 | + /* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */ |
|---|
| 300 | + for (i = CDNS_MCP_STAT; i <= CDNS_MCP_FIFOSTAT; i += sizeof(u32)) |
|---|
| 301 | + ret += cdns_sprintf(cdns, buf, ret, i); |
|---|
| 302 | + |
|---|
| 303 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 304 | + "\nSSP & Clk ctrl Registers\n"); |
|---|
| 305 | + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0); |
|---|
| 306 | + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1); |
|---|
| 307 | + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0); |
|---|
| 308 | + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1); |
|---|
| 309 | + |
|---|
| 310 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 311 | + "\nDPn B0 Registers\n"); |
|---|
| 312 | + |
|---|
| 313 | + num_ports = cdns->num_ports; |
|---|
| 314 | + |
|---|
| 315 | + for (i = 0; i < num_ports; i++) { |
|---|
| 316 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 317 | + "\nDP-%d\n", i); |
|---|
| 318 | + for (j = CDNS_DPN_B0_CONFIG(i); |
|---|
| 319 | + j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32)) |
|---|
| 320 | + ret += cdns_sprintf(cdns, buf, ret, j); |
|---|
| 321 | + } |
|---|
| 322 | + |
|---|
| 323 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 324 | + "\nDPn B1 Registers\n"); |
|---|
| 325 | + for (i = 0; i < num_ports; i++) { |
|---|
| 326 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 327 | + "\nDP-%d\n", i); |
|---|
| 328 | + |
|---|
| 329 | + for (j = CDNS_DPN_B1_CONFIG(i); |
|---|
| 330 | + j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32)) |
|---|
| 331 | + ret += cdns_sprintf(cdns, buf, ret, j); |
|---|
| 332 | + } |
|---|
| 333 | + |
|---|
| 334 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 335 | + "\nDPn Control Registers\n"); |
|---|
| 336 | + for (i = 0; i < num_ports; i++) |
|---|
| 337 | + ret += cdns_sprintf(cdns, buf, ret, |
|---|
| 338 | + CDNS_PORTCTRL + i * CDNS_PORT_OFFSET); |
|---|
| 339 | + |
|---|
| 340 | + ret += scnprintf(buf + ret, RD_BUF - ret, |
|---|
| 341 | + "\nPDIn Config Registers\n"); |
|---|
| 342 | + |
|---|
| 343 | + /* number of PDI and ports is interchangeable */ |
|---|
| 344 | + for (i = 0; i < num_ports; i++) |
|---|
| 345 | + ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i)); |
|---|
| 346 | + |
|---|
| 347 | + seq_printf(s, "%s", buf); |
|---|
| 348 | + kfree(buf); |
|---|
| 349 | + |
|---|
| 350 | + return 0; |
|---|
| 351 | +} |
|---|
| 352 | +DEFINE_SHOW_ATTRIBUTE(cdns_reg); |
|---|
| 353 | + |
|---|
| 354 | +static int cdns_hw_reset(void *data, u64 value) |
|---|
| 355 | +{ |
|---|
| 356 | + struct sdw_cdns *cdns = data; |
|---|
| 357 | + int ret; |
|---|
| 358 | + |
|---|
| 359 | + if (value != 1) |
|---|
| 360 | + return -EINVAL; |
|---|
| 361 | + |
|---|
| 362 | + /* Userspace changed the hardware state behind the kernel's back */ |
|---|
| 363 | + add_taint(TAINT_USER, LOCKDEP_STILL_OK); |
|---|
| 364 | + |
|---|
| 365 | + ret = sdw_cdns_exit_reset(cdns); |
|---|
| 366 | + |
|---|
| 367 | + dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret); |
|---|
| 368 | + |
|---|
| 369 | + return ret; |
|---|
| 370 | +} |
|---|
| 371 | + |
|---|
| 372 | +DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); |
|---|
| 373 | + |
|---|
| 374 | +static int cdns_parity_error_injection(void *data, u64 value) |
|---|
| 375 | +{ |
|---|
| 376 | + struct sdw_cdns *cdns = data; |
|---|
| 377 | + struct sdw_bus *bus; |
|---|
| 378 | + int ret; |
|---|
| 379 | + |
|---|
| 380 | + if (value != 1) |
|---|
| 381 | + return -EINVAL; |
|---|
| 382 | + |
|---|
| 383 | + bus = &cdns->bus; |
|---|
| 384 | + |
|---|
| 385 | + /* |
|---|
| 386 | + * Resume Master device. If this results in a bus reset, the |
|---|
| 387 | + * Slave devices will re-attach and be re-enumerated. |
|---|
| 388 | + */ |
|---|
| 389 | + ret = pm_runtime_get_sync(bus->dev); |
|---|
| 390 | + if (ret < 0 && ret != -EACCES) { |
|---|
| 391 | + dev_err_ratelimited(cdns->dev, |
|---|
| 392 | + "pm_runtime_get_sync failed in %s, ret %d\n", |
|---|
| 393 | + __func__, ret); |
|---|
| 394 | + pm_runtime_put_noidle(bus->dev); |
|---|
| 395 | + return ret; |
|---|
| 396 | + } |
|---|
| 397 | + |
|---|
| 398 | + /* |
|---|
| 399 | + * wait long enough for Slave(s) to be in steady state. This |
|---|
| 400 | + * does not need to be super precise. |
|---|
| 401 | + */ |
|---|
| 402 | + msleep(200); |
|---|
| 403 | + |
|---|
| 404 | + /* |
|---|
| 405 | + * Take the bus lock here to make sure that any bus transactions |
|---|
| 406 | + * will be queued while we inject a parity error on a dummy read |
|---|
| 407 | + */ |
|---|
| 408 | + mutex_lock(&bus->bus_lock); |
|---|
| 409 | + |
|---|
| 410 | + /* program hardware to inject parity error */ |
|---|
| 411 | + cdns_updatel(cdns, CDNS_MCP_CMDCTRL, |
|---|
| 412 | + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, |
|---|
| 413 | + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR); |
|---|
| 414 | + |
|---|
| 415 | + /* commit changes */ |
|---|
| 416 | + cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, |
|---|
| 417 | + CDNS_MCP_CONFIG_UPDATE_BIT, |
|---|
| 418 | + CDNS_MCP_CONFIG_UPDATE_BIT); |
|---|
| 419 | + |
|---|
| 420 | + /* do a broadcast dummy read to avoid bus clashes */ |
|---|
| 421 | + ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0); |
|---|
| 422 | + dev_info(cdns->dev, "parity error injection, read: %d\n", ret); |
|---|
| 423 | + |
|---|
| 424 | + /* program hardware to disable parity error */ |
|---|
| 425 | + cdns_updatel(cdns, CDNS_MCP_CMDCTRL, |
|---|
| 426 | + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, |
|---|
| 427 | + 0); |
|---|
| 428 | + |
|---|
| 429 | + /* commit changes */ |
|---|
| 430 | + cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, |
|---|
| 431 | + CDNS_MCP_CONFIG_UPDATE_BIT, |
|---|
| 432 | + CDNS_MCP_CONFIG_UPDATE_BIT); |
|---|
| 433 | + |
|---|
| 434 | + /* Continue bus operation with parity error injection disabled */ |
|---|
| 435 | + mutex_unlock(&bus->bus_lock); |
|---|
| 436 | + |
|---|
| 437 | + /* Userspace changed the hardware state behind the kernel's back */ |
|---|
| 438 | + add_taint(TAINT_USER, LOCKDEP_STILL_OK); |
|---|
| 439 | + |
|---|
| 440 | + /* |
|---|
| 441 | + * allow Master device to enter pm_runtime suspend. This may |
|---|
| 442 | + * also result in Slave devices suspending. |
|---|
| 443 | + */ |
|---|
| 444 | + pm_runtime_mark_last_busy(bus->dev); |
|---|
| 445 | + pm_runtime_put_autosuspend(bus->dev); |
|---|
| 446 | + |
|---|
| 447 | + return 0; |
|---|
| 448 | +} |
|---|
| 449 | + |
|---|
| 450 | +DEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL, |
|---|
| 451 | + cdns_parity_error_injection, "%llu\n"); |
|---|
| 452 | + |
|---|
| 453 | +/** |
|---|
| 454 | + * sdw_cdns_debugfs_init() - Cadence debugfs init |
|---|
| 455 | + * @cdns: Cadence instance |
|---|
| 456 | + * @root: debugfs root |
|---|
| 457 | + */ |
|---|
| 458 | +void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) |
|---|
| 459 | +{ |
|---|
| 460 | + debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); |
|---|
| 461 | + |
|---|
| 462 | + debugfs_create_file("cdns-hw-reset", 0200, root, cdns, |
|---|
| 463 | + &cdns_hw_reset_fops); |
|---|
| 464 | + |
|---|
| 465 | + debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns, |
|---|
| 466 | + &cdns_parity_error_fops); |
|---|
| 467 | +} |
|---|
| 468 | +EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); |
|---|
| 469 | + |
|---|
| 470 | +#endif /* CONFIG_DEBUG_FS */ |
|---|
| 225 | 471 | |
|---|
| 226 | 472 | /* |
|---|
| 227 | 473 | * IO Calls |
|---|
| 228 | 474 | */ |
|---|
| 229 | | -static enum sdw_command_response cdns_fill_msg_resp( |
|---|
| 230 | | - struct sdw_cdns *cdns, |
|---|
| 231 | | - struct sdw_msg *msg, int count, int offset) |
|---|
| 475 | +static enum sdw_command_response |
|---|
| 476 | +cdns_fill_msg_resp(struct sdw_cdns *cdns, |
|---|
| 477 | + struct sdw_msg *msg, int count, int offset) |
|---|
| 232 | 478 | { |
|---|
| 233 | 479 | int nack = 0, no_ack = 0; |
|---|
| 234 | 480 | int i; |
|---|
| .. | .. |
|---|
| 237 | 483 | for (i = 0; i < count; i++) { |
|---|
| 238 | 484 | if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { |
|---|
| 239 | 485 | no_ack = 1; |
|---|
| 240 | | - dev_dbg(cdns->dev, "Msg Ack not received\n"); |
|---|
| 241 | | - if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { |
|---|
| 242 | | - nack = 1; |
|---|
| 243 | | - dev_err(cdns->dev, "Msg NACK received\n"); |
|---|
| 244 | | - } |
|---|
| 486 | + dev_dbg_ratelimited(cdns->dev, "Msg Ack not received\n"); |
|---|
| 487 | + } |
|---|
| 488 | + if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { |
|---|
| 489 | + nack = 1; |
|---|
| 490 | + dev_err_ratelimited(cdns->dev, "Msg NACK received\n"); |
|---|
| 245 | 491 | } |
|---|
| 246 | 492 | } |
|---|
| 247 | 493 | |
|---|
| 248 | 494 | if (nack) { |
|---|
| 249 | | - dev_err(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); |
|---|
| 495 | + dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); |
|---|
| 250 | 496 | return SDW_CMD_FAIL; |
|---|
| 251 | | - } else if (no_ack) { |
|---|
| 252 | | - dev_dbg(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); |
|---|
| 497 | + } |
|---|
| 498 | + |
|---|
| 499 | + if (no_ack) { |
|---|
| 500 | + dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); |
|---|
| 253 | 501 | return SDW_CMD_IGNORED; |
|---|
| 254 | 502 | } |
|---|
| 255 | 503 | |
|---|
| 256 | | - /* fill response */ |
|---|
| 257 | | - for (i = 0; i < count; i++) |
|---|
| 258 | | - msg->buf[i + offset] = cdns->response_buf[i] >> |
|---|
| 259 | | - SDW_REG_SHIFT(CDNS_MCP_RESP_RDATA); |
|---|
| 504 | + if (msg->flags == SDW_MSG_FLAG_READ) { |
|---|
| 505 | + /* fill response */ |
|---|
| 506 | + for (i = 0; i < count; i++) |
|---|
| 507 | + msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA, |
|---|
| 508 | + cdns->response_buf[i]); |
|---|
| 509 | + } |
|---|
| 260 | 510 | |
|---|
| 261 | 511 | return SDW_CMD_OK; |
|---|
| 262 | 512 | } |
|---|
| 263 | 513 | |
|---|
| 264 | 514 | static enum sdw_command_response |
|---|
| 265 | 515 | _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, |
|---|
| 266 | | - int offset, int count, bool defer) |
|---|
| 516 | + int offset, int count, bool defer) |
|---|
| 267 | 517 | { |
|---|
| 268 | 518 | unsigned long time; |
|---|
| 269 | 519 | u32 base, i, data; |
|---|
| .. | .. |
|---|
| 279 | 529 | addr = msg->addr; |
|---|
| 280 | 530 | |
|---|
| 281 | 531 | for (i = 0; i < count; i++) { |
|---|
| 282 | | - data = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); |
|---|
| 283 | | - data |= cmd << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); |
|---|
| 284 | | - data |= addr++ << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); |
|---|
| 532 | + data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num); |
|---|
| 533 | + data |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, cmd); |
|---|
| 534 | + data |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, addr); |
|---|
| 535 | + addr++; |
|---|
| 285 | 536 | |
|---|
| 286 | 537 | if (msg->flags == SDW_MSG_FLAG_WRITE) |
|---|
| 287 | 538 | data |= msg->buf[i + offset]; |
|---|
| 288 | 539 | |
|---|
| 289 | | - data |= msg->ssp_sync << SDW_REG_SHIFT(CDNS_MCP_CMD_SSP_TAG); |
|---|
| 540 | + data |= FIELD_PREP(CDNS_MCP_CMD_SSP_TAG, msg->ssp_sync); |
|---|
| 290 | 541 | cdns_writel(cdns, base, data); |
|---|
| 291 | 542 | base += CDNS_MCP_CMD_WORD_LEN; |
|---|
| 292 | 543 | } |
|---|
| .. | .. |
|---|
| 296 | 547 | |
|---|
| 297 | 548 | /* wait for timeout or response */ |
|---|
| 298 | 549 | time = wait_for_completion_timeout(&cdns->tx_complete, |
|---|
| 299 | | - msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
|---|
| 550 | + msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
|---|
| 300 | 551 | if (!time) { |
|---|
| 301 | | - dev_err(cdns->dev, "IO transfer timed out\n"); |
|---|
| 552 | + dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n", |
|---|
| 553 | + cmd, msg->dev_num, msg->addr, msg->len); |
|---|
| 302 | 554 | msg->len = 0; |
|---|
| 303 | 555 | return SDW_CMD_TIMEOUT; |
|---|
| 304 | 556 | } |
|---|
| .. | .. |
|---|
| 306 | 558 | return cdns_fill_msg_resp(cdns, msg, count, offset); |
|---|
| 307 | 559 | } |
|---|
| 308 | 560 | |
|---|
| 309 | | -static enum sdw_command_response cdns_program_scp_addr( |
|---|
| 310 | | - struct sdw_cdns *cdns, struct sdw_msg *msg) |
|---|
| 561 | +static enum sdw_command_response |
|---|
| 562 | +cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg) |
|---|
| 311 | 563 | { |
|---|
| 312 | 564 | int nack = 0, no_ack = 0; |
|---|
| 313 | 565 | unsigned long time; |
|---|
| .. | .. |
|---|
| 320 | 572 | cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL; |
|---|
| 321 | 573 | } |
|---|
| 322 | 574 | |
|---|
| 323 | | - data[0] = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); |
|---|
| 324 | | - data[0] |= 0x3 << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); |
|---|
| 575 | + data[0] = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num); |
|---|
| 576 | + data[0] |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, 0x3); |
|---|
| 325 | 577 | data[1] = data[0]; |
|---|
| 326 | 578 | |
|---|
| 327 | | - data[0] |= SDW_SCP_ADDRPAGE1 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); |
|---|
| 328 | | - data[1] |= SDW_SCP_ADDRPAGE2 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); |
|---|
| 579 | + data[0] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE1); |
|---|
| 580 | + data[1] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE2); |
|---|
| 329 | 581 | |
|---|
| 330 | 582 | data[0] |= msg->addr_page1; |
|---|
| 331 | 583 | data[1] |= msg->addr_page2; |
|---|
| .. | .. |
|---|
| 336 | 588 | cdns_writel(cdns, base, data[1]); |
|---|
| 337 | 589 | |
|---|
| 338 | 590 | time = wait_for_completion_timeout(&cdns->tx_complete, |
|---|
| 339 | | - msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
|---|
| 591 | + msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
|---|
| 340 | 592 | if (!time) { |
|---|
| 341 | 593 | dev_err(cdns->dev, "SCP Msg trf timed out\n"); |
|---|
| 342 | 594 | msg->len = 0; |
|---|
| .. | .. |
|---|
| 347 | 599 | for (i = 0; i < 2; i++) { |
|---|
| 348 | 600 | if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { |
|---|
| 349 | 601 | no_ack = 1; |
|---|
| 350 | | - dev_err(cdns->dev, "Program SCP Ack not received"); |
|---|
| 602 | + dev_err(cdns->dev, "Program SCP Ack not received\n"); |
|---|
| 351 | 603 | if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { |
|---|
| 352 | 604 | nack = 1; |
|---|
| 353 | | - dev_err(cdns->dev, "Program SCP NACK received"); |
|---|
| 605 | + dev_err(cdns->dev, "Program SCP NACK received\n"); |
|---|
| 354 | 606 | } |
|---|
| 355 | 607 | } |
|---|
| 356 | 608 | } |
|---|
| 357 | 609 | |
|---|
| 358 | 610 | /* For NACK, NO ack, don't return err if we are in Broadcast mode */ |
|---|
| 359 | 611 | if (nack) { |
|---|
| 360 | | - dev_err(cdns->dev, |
|---|
| 361 | | - "SCP_addrpage NACKed for Slave %d", msg->dev_num); |
|---|
| 612 | + dev_err_ratelimited(cdns->dev, |
|---|
| 613 | + "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); |
|---|
| 362 | 614 | return SDW_CMD_FAIL; |
|---|
| 363 | | - } else if (no_ack) { |
|---|
| 364 | | - dev_dbg(cdns->dev, |
|---|
| 365 | | - "SCP_addrpage ignored for Slave %d", msg->dev_num); |
|---|
| 615 | + } |
|---|
| 616 | + |
|---|
| 617 | + if (no_ack) { |
|---|
| 618 | + dev_dbg_ratelimited(cdns->dev, |
|---|
| 619 | + "SCP_addrpage ignored for Slave %d\n", msg->dev_num); |
|---|
| 366 | 620 | return SDW_CMD_IGNORED; |
|---|
| 367 | 621 | } |
|---|
| 368 | 622 | |
|---|
| .. | .. |
|---|
| 410 | 664 | |
|---|
| 411 | 665 | for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) { |
|---|
| 412 | 666 | ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, |
|---|
| 413 | | - CDNS_MCP_CMD_LEN, false); |
|---|
| 667 | + CDNS_MCP_CMD_LEN, false); |
|---|
| 414 | 668 | if (ret < 0) |
|---|
| 415 | 669 | goto exit; |
|---|
| 416 | 670 | } |
|---|
| .. | .. |
|---|
| 419 | 673 | goto exit; |
|---|
| 420 | 674 | |
|---|
| 421 | 675 | ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, |
|---|
| 422 | | - msg->len % CDNS_MCP_CMD_LEN, false); |
|---|
| 676 | + msg->len % CDNS_MCP_CMD_LEN, false); |
|---|
| 423 | 677 | |
|---|
| 424 | 678 | exit: |
|---|
| 425 | 679 | return ret; |
|---|
| .. | .. |
|---|
| 428 | 682 | |
|---|
| 429 | 683 | enum sdw_command_response |
|---|
| 430 | 684 | cdns_xfer_msg_defer(struct sdw_bus *bus, |
|---|
| 431 | | - struct sdw_msg *msg, struct sdw_defer *defer) |
|---|
| 685 | + struct sdw_msg *msg, struct sdw_defer *defer) |
|---|
| 432 | 686 | { |
|---|
| 433 | 687 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
|---|
| 434 | 688 | int cmd = 0, ret; |
|---|
| .. | .. |
|---|
| 483 | 737 | } |
|---|
| 484 | 738 | |
|---|
| 485 | 739 | static int cdns_update_slave_status(struct sdw_cdns *cdns, |
|---|
| 486 | | - u32 slave0, u32 slave1) |
|---|
| 740 | + u32 slave0, u32 slave1) |
|---|
| 487 | 741 | { |
|---|
| 488 | 742 | enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; |
|---|
| 489 | 743 | bool is_slave = false; |
|---|
| 490 | | - u64 slave, mask; |
|---|
| 744 | + u64 slave; |
|---|
| 745 | + u32 mask; |
|---|
| 491 | 746 | int i, set_status; |
|---|
| 492 | 747 | |
|---|
| 493 | 748 | /* combine the two status */ |
|---|
| .. | .. |
|---|
| 525 | 780 | |
|---|
| 526 | 781 | /* first check if Slave reported multiple status */ |
|---|
| 527 | 782 | if (set_status > 1) { |
|---|
| 528 | | - dev_warn(cdns->dev, |
|---|
| 529 | | - "Slave reported multiple Status: %d\n", |
|---|
| 530 | | - status[i]); |
|---|
| 531 | | - /* |
|---|
| 532 | | - * TODO: we need to reread the status here by |
|---|
| 533 | | - * issuing a PING cmd |
|---|
| 534 | | - */ |
|---|
| 783 | + u32 val; |
|---|
| 784 | + |
|---|
| 785 | + dev_warn_ratelimited(cdns->dev, |
|---|
| 786 | + "Slave %d reported multiple Status: %d\n", |
|---|
| 787 | + i, mask); |
|---|
| 788 | + |
|---|
| 789 | + /* check latest status extracted from PING commands */ |
|---|
| 790 | + val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); |
|---|
| 791 | + val >>= (i * 2); |
|---|
| 792 | + |
|---|
| 793 | + switch (val & 0x3) { |
|---|
| 794 | + case 0: |
|---|
| 795 | + status[i] = SDW_SLAVE_UNATTACHED; |
|---|
| 796 | + break; |
|---|
| 797 | + case 1: |
|---|
| 798 | + status[i] = SDW_SLAVE_ATTACHED; |
|---|
| 799 | + break; |
|---|
| 800 | + case 2: |
|---|
| 801 | + status[i] = SDW_SLAVE_ALERT; |
|---|
| 802 | + break; |
|---|
| 803 | + case 3: |
|---|
| 804 | + default: |
|---|
| 805 | + status[i] = SDW_SLAVE_RESERVED; |
|---|
| 806 | + break; |
|---|
| 807 | + } |
|---|
| 808 | + |
|---|
| 809 | + dev_warn_ratelimited(cdns->dev, |
|---|
| 810 | + "Slave %d status updated to %d\n", |
|---|
| 811 | + i, status[i]); |
|---|
| 812 | + |
|---|
| 535 | 813 | } |
|---|
| 536 | 814 | } |
|---|
| 537 | 815 | |
|---|
| .. | .. |
|---|
| 558 | 836 | |
|---|
| 559 | 837 | int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); |
|---|
| 560 | 838 | |
|---|
| 839 | + /* check for reserved values read as zero */ |
|---|
| 840 | + if (int_status & CDNS_MCP_INT_RESERVED) |
|---|
| 841 | + return IRQ_NONE; |
|---|
| 842 | + |
|---|
| 561 | 843 | if (!(int_status & CDNS_MCP_INT_IRQ)) |
|---|
| 562 | 844 | return IRQ_NONE; |
|---|
| 563 | 845 | |
|---|
| .. | .. |
|---|
| 566 | 848 | |
|---|
| 567 | 849 | if (cdns->defer) { |
|---|
| 568 | 850 | cdns_fill_msg_resp(cdns, cdns->defer->msg, |
|---|
| 569 | | - cdns->defer->length, 0); |
|---|
| 851 | + cdns->defer->length, 0); |
|---|
| 570 | 852 | complete(&cdns->defer->complete); |
|---|
| 571 | 853 | cdns->defer = NULL; |
|---|
| 572 | | - } else |
|---|
| 854 | + } else { |
|---|
| 573 | 855 | complete(&cdns->tx_complete); |
|---|
| 856 | + } |
|---|
| 857 | + } |
|---|
| 858 | + |
|---|
| 859 | + if (int_status & CDNS_MCP_INT_PARITY) { |
|---|
| 860 | + /* Parity error detected by Master */ |
|---|
| 861 | + dev_err_ratelimited(cdns->dev, "Parity error\n"); |
|---|
| 574 | 862 | } |
|---|
| 575 | 863 | |
|---|
| 576 | 864 | if (int_status & CDNS_MCP_INT_CTRL_CLASH) { |
|---|
| 577 | | - |
|---|
| 578 | 865 | /* Slave is driving bit slot during control word */ |
|---|
| 579 | 866 | dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); |
|---|
| 580 | | - int_status |= CDNS_MCP_INT_CTRL_CLASH; |
|---|
| 581 | 867 | } |
|---|
| 582 | 868 | |
|---|
| 583 | 869 | if (int_status & CDNS_MCP_INT_DATA_CLASH) { |
|---|
| .. | .. |
|---|
| 586 | 872 | * ownership of data bits or Slave gone bonkers |
|---|
| 587 | 873 | */ |
|---|
| 588 | 874 | dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); |
|---|
| 589 | | - int_status |= CDNS_MCP_INT_DATA_CLASH; |
|---|
| 875 | + } |
|---|
| 876 | + |
|---|
| 877 | + if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL && |
|---|
| 878 | + int_status & CDNS_MCP_INT_DPINT) { |
|---|
| 879 | + u32 port_intstat; |
|---|
| 880 | + |
|---|
| 881 | + /* just log which ports report an error */ |
|---|
| 882 | + port_intstat = cdns_readl(cdns, CDNS_MCP_PORT_INTSTAT); |
|---|
| 883 | + dev_err_ratelimited(cdns->dev, "DP interrupt: PortIntStat %8x\n", |
|---|
| 884 | + port_intstat); |
|---|
| 885 | + |
|---|
| 886 | + /* clear status w/ write1 */ |
|---|
| 887 | + cdns_writel(cdns, CDNS_MCP_PORT_INTSTAT, port_intstat); |
|---|
| 590 | 888 | } |
|---|
| 591 | 889 | |
|---|
| 592 | 890 | if (int_status & CDNS_MCP_INT_SLAVE_MASK) { |
|---|
| 593 | 891 | /* Mask the Slave interrupt and wake thread */ |
|---|
| 594 | 892 | cdns_updatel(cdns, CDNS_MCP_INTMASK, |
|---|
| 595 | | - CDNS_MCP_INT_SLAVE_MASK, 0); |
|---|
| 893 | + CDNS_MCP_INT_SLAVE_MASK, 0); |
|---|
| 596 | 894 | |
|---|
| 597 | 895 | int_status &= ~CDNS_MCP_INT_SLAVE_MASK; |
|---|
| 598 | | - ret = IRQ_WAKE_THREAD; |
|---|
| 896 | + |
|---|
| 897 | + /* |
|---|
| 898 | + * Deal with possible race condition between interrupt |
|---|
| 899 | + * handling and disabling interrupts on suspend. |
|---|
| 900 | + * |
|---|
| 901 | + * If the master is in the process of disabling |
|---|
| 902 | + * interrupts, don't schedule a workqueue |
|---|
| 903 | + */ |
|---|
| 904 | + if (cdns->interrupt_enabled) |
|---|
| 905 | + schedule_work(&cdns->work); |
|---|
| 599 | 906 | } |
|---|
| 600 | 907 | |
|---|
| 601 | 908 | cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); |
|---|
| .. | .. |
|---|
| 604 | 911 | EXPORT_SYMBOL(sdw_cdns_irq); |
|---|
| 605 | 912 | |
|---|
| 606 | 913 | /** |
|---|
| 607 | | - * sdw_cdns_thread() - Cadence irq thread handler |
|---|
| 608 | | - * @irq: irq number |
|---|
| 609 | | - * @dev_id: irq context |
|---|
| 914 | + * To update slave status in a work since we will need to handle |
|---|
| 915 | + * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave |
|---|
| 916 | + * process. |
|---|
| 917 | + * @work: cdns worker thread |
|---|
| 610 | 918 | */ |
|---|
| 611 | | -irqreturn_t sdw_cdns_thread(int irq, void *dev_id) |
|---|
| 919 | +static void cdns_update_slave_status_work(struct work_struct *work) |
|---|
| 612 | 920 | { |
|---|
| 613 | | - struct sdw_cdns *cdns = dev_id; |
|---|
| 921 | + struct sdw_cdns *cdns = |
|---|
| 922 | + container_of(work, struct sdw_cdns, work); |
|---|
| 614 | 923 | u32 slave0, slave1; |
|---|
| 615 | 924 | |
|---|
| 616 | | - dev_dbg(cdns->dev, "Slave status change\n"); |
|---|
| 925 | + dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); |
|---|
| 617 | 926 | |
|---|
| 618 | 927 | slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); |
|---|
| 619 | 928 | slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); |
|---|
| .. | .. |
|---|
| 625 | 934 | /* clear and unmask Slave interrupt now */ |
|---|
| 626 | 935 | cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK); |
|---|
| 627 | 936 | cdns_updatel(cdns, CDNS_MCP_INTMASK, |
|---|
| 628 | | - CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); |
|---|
| 937 | + CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); |
|---|
| 629 | 938 | |
|---|
| 630 | | - return IRQ_HANDLED; |
|---|
| 631 | 939 | } |
|---|
| 632 | | -EXPORT_SYMBOL(sdw_cdns_thread); |
|---|
| 633 | 940 | |
|---|
| 634 | 941 | /* |
|---|
| 635 | 942 | * init routines |
|---|
| 636 | 943 | */ |
|---|
| 637 | | -static int _cdns_enable_interrupt(struct sdw_cdns *cdns) |
|---|
| 944 | + |
|---|
| 945 | +/** |
|---|
| 946 | + * sdw_cdns_exit_reset() - Program reset parameters and start bus operations |
|---|
| 947 | + * @cdns: Cadence instance |
|---|
| 948 | + */ |
|---|
| 949 | +int sdw_cdns_exit_reset(struct sdw_cdns *cdns) |
|---|
| 950 | +{ |
|---|
| 951 | + /* program maximum length reset to be safe */ |
|---|
| 952 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 953 | + CDNS_MCP_CONTROL_RST_DELAY, |
|---|
| 954 | + CDNS_MCP_CONTROL_RST_DELAY); |
|---|
| 955 | + |
|---|
| 956 | + /* use hardware generated reset */ |
|---|
| 957 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 958 | + CDNS_MCP_CONTROL_HW_RST, |
|---|
| 959 | + CDNS_MCP_CONTROL_HW_RST); |
|---|
| 960 | + |
|---|
| 961 | + /* commit changes */ |
|---|
| 962 | + cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, |
|---|
| 963 | + CDNS_MCP_CONFIG_UPDATE_BIT, |
|---|
| 964 | + CDNS_MCP_CONFIG_UPDATE_BIT); |
|---|
| 965 | + |
|---|
| 966 | + /* don't wait here */ |
|---|
| 967 | + return 0; |
|---|
| 968 | + |
|---|
| 969 | +} |
|---|
| 970 | +EXPORT_SYMBOL(sdw_cdns_exit_reset); |
|---|
| 971 | + |
|---|
| 972 | +/** |
|---|
| 973 | + * sdw_cdns_enable_slave_interrupt() - Enable SDW slave interrupts |
|---|
| 974 | + * @cdns: Cadence instance |
|---|
| 975 | + * @state: boolean for true/false |
|---|
| 976 | + */ |
|---|
| 977 | +static void cdns_enable_slave_interrupts(struct sdw_cdns *cdns, bool state) |
|---|
| 638 | 978 | { |
|---|
| 639 | 979 | u32 mask; |
|---|
| 640 | 980 | |
|---|
| 641 | | - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, |
|---|
| 642 | | - CDNS_MCP_SLAVE_INTMASK0_MASK); |
|---|
| 643 | | - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, |
|---|
| 644 | | - CDNS_MCP_SLAVE_INTMASK1_MASK); |
|---|
| 981 | + mask = cdns_readl(cdns, CDNS_MCP_INTMASK); |
|---|
| 982 | + if (state) |
|---|
| 983 | + mask |= CDNS_MCP_INT_SLAVE_MASK; |
|---|
| 984 | + else |
|---|
| 985 | + mask &= ~CDNS_MCP_INT_SLAVE_MASK; |
|---|
| 645 | 986 | |
|---|
| 646 | | - mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | |
|---|
| 647 | | - CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH | |
|---|
| 648 | | - CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | |
|---|
| 649 | | - CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT; |
|---|
| 987 | + cdns_writel(cdns, CDNS_MCP_INTMASK, mask); |
|---|
| 988 | +} |
|---|
| 650 | 989 | |
|---|
| 990 | +/** |
|---|
| 991 | + * sdw_cdns_enable_interrupt() - Enable SDW interrupts |
|---|
| 992 | + * @cdns: Cadence instance |
|---|
| 993 | + * @state: True if we are trying to enable interrupt. |
|---|
| 994 | + */ |
|---|
| 995 | +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) |
|---|
| 996 | +{ |
|---|
| 997 | + u32 slave_intmask0 = 0; |
|---|
| 998 | + u32 slave_intmask1 = 0; |
|---|
| 999 | + u32 mask = 0; |
|---|
| 1000 | + |
|---|
| 1001 | + if (!state) |
|---|
| 1002 | + goto update_masks; |
|---|
| 1003 | + |
|---|
| 1004 | + slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK; |
|---|
| 1005 | + slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK; |
|---|
| 1006 | + |
|---|
| 1007 | + /* enable detection of all slave state changes */ |
|---|
| 1008 | + mask = CDNS_MCP_INT_SLAVE_MASK; |
|---|
| 1009 | + |
|---|
| 1010 | + /* enable detection of bus issues */ |
|---|
| 1011 | + mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | |
|---|
| 1012 | + CDNS_MCP_INT_PARITY; |
|---|
| 1013 | + |
|---|
| 1014 | + /* port interrupt limited to test modes for now */ |
|---|
| 1015 | + if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL) |
|---|
| 1016 | + mask |= CDNS_MCP_INT_DPINT; |
|---|
| 1017 | + |
|---|
| 1018 | + /* enable detection of RX fifo level */ |
|---|
| 1019 | + mask |= CDNS_MCP_INT_RX_WL; |
|---|
| 1020 | + |
|---|
| 1021 | + /* |
|---|
| 1022 | + * CDNS_MCP_INT_IRQ needs to be set otherwise all previous |
|---|
| 1023 | + * settings are irrelevant |
|---|
| 1024 | + */ |
|---|
| 1025 | + mask |= CDNS_MCP_INT_IRQ; |
|---|
| 1026 | + |
|---|
| 1027 | + if (interrupt_mask) /* parameter override */ |
|---|
| 1028 | + mask = interrupt_mask; |
|---|
| 1029 | + |
|---|
| 1030 | +update_masks: |
|---|
| 1031 | + /* clear slave interrupt status before enabling interrupt */ |
|---|
| 1032 | + if (state) { |
|---|
| 1033 | + u32 slave_state; |
|---|
| 1034 | + |
|---|
| 1035 | + slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); |
|---|
| 1036 | + cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state); |
|---|
| 1037 | + slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); |
|---|
| 1038 | + cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state); |
|---|
| 1039 | + } |
|---|
| 1040 | + cdns->interrupt_enabled = state; |
|---|
| 1041 | + |
|---|
| 1042 | + /* |
|---|
| 1043 | + * Complete any on-going status updates before updating masks, |
|---|
| 1044 | + * and cancel queued status updates. |
|---|
| 1045 | + * |
|---|
| 1046 | + * There could be a race with a new interrupt thrown before |
|---|
| 1047 | + * the 3 mask updates below are complete, so in the interrupt |
|---|
| 1048 | + * we use the 'interrupt_enabled' status to prevent new work |
|---|
| 1049 | + * from being queued. |
|---|
| 1050 | + */ |
|---|
| 1051 | + if (!state) |
|---|
| 1052 | + cancel_work_sync(&cdns->work); |
|---|
| 1053 | + |
|---|
| 1054 | + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); |
|---|
| 1055 | + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); |
|---|
| 651 | 1056 | cdns_writel(cdns, CDNS_MCP_INTMASK, mask); |
|---|
| 652 | 1057 | |
|---|
| 653 | 1058 | return 0; |
|---|
| 654 | 1059 | } |
|---|
| 655 | | - |
|---|
| 656 | | -/** |
|---|
| 657 | | - * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config |
|---|
| 658 | | - * @cdns: Cadence instance |
|---|
| 659 | | - */ |
|---|
| 660 | | -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) |
|---|
| 661 | | -{ |
|---|
| 662 | | - int ret; |
|---|
| 663 | | - |
|---|
| 664 | | - _cdns_enable_interrupt(cdns); |
|---|
| 665 | | - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, |
|---|
| 666 | | - CDNS_MCP_CONFIG_UPDATE_BIT); |
|---|
| 667 | | - if (ret < 0) |
|---|
| 668 | | - dev_err(cdns->dev, "Config update timedout"); |
|---|
| 669 | | - |
|---|
| 670 | | - return ret; |
|---|
| 671 | | -} |
|---|
| 672 | 1060 | EXPORT_SYMBOL(sdw_cdns_enable_interrupt); |
|---|
| 673 | 1061 | |
|---|
| 674 | 1062 | static int cdns_allocate_pdi(struct sdw_cdns *cdns, |
|---|
| 675 | | - struct sdw_cdns_pdi **stream, |
|---|
| 676 | | - u32 num, u32 pdi_offset) |
|---|
| 1063 | + struct sdw_cdns_pdi **stream, |
|---|
| 1064 | + u32 num, u32 pdi_offset) |
|---|
| 677 | 1065 | { |
|---|
| 678 | 1066 | struct sdw_cdns_pdi *pdi; |
|---|
| 679 | 1067 | int i; |
|---|
| .. | .. |
|---|
| 687 | 1075 | |
|---|
| 688 | 1076 | for (i = 0; i < num; i++) { |
|---|
| 689 | 1077 | pdi[i].num = i + pdi_offset; |
|---|
| 690 | | - pdi[i].assigned = false; |
|---|
| 691 | 1078 | } |
|---|
| 692 | 1079 | |
|---|
| 693 | 1080 | *stream = pdi; |
|---|
| .. | .. |
|---|
| 701 | 1088 | * @config: Stream configurations |
|---|
| 702 | 1089 | */ |
|---|
| 703 | 1090 | int sdw_cdns_pdi_init(struct sdw_cdns *cdns, |
|---|
| 704 | | - struct sdw_cdns_stream_config config) |
|---|
| 1091 | + struct sdw_cdns_stream_config config) |
|---|
| 705 | 1092 | { |
|---|
| 706 | 1093 | struct sdw_cdns_streams *stream; |
|---|
| 707 | | - int offset, i, ret; |
|---|
| 1094 | + int offset; |
|---|
| 1095 | + int ret; |
|---|
| 708 | 1096 | |
|---|
| 709 | 1097 | cdns->pcm.num_bd = config.pcm_bd; |
|---|
| 710 | 1098 | cdns->pcm.num_in = config.pcm_in; |
|---|
| .. | .. |
|---|
| 716 | 1104 | /* Allocate PDIs for PCMs */ |
|---|
| 717 | 1105 | stream = &cdns->pcm; |
|---|
| 718 | 1106 | |
|---|
| 719 | | - /* First two PDIs are reserved for bulk transfers */ |
|---|
| 720 | | - stream->num_bd -= CDNS_PCM_PDI_OFFSET; |
|---|
| 721 | | - offset = CDNS_PCM_PDI_OFFSET; |
|---|
| 1107 | + /* we allocate PDI0 and PDI1 which are used for Bulk */ |
|---|
| 1108 | + offset = 0; |
|---|
| 722 | 1109 | |
|---|
| 723 | 1110 | ret = cdns_allocate_pdi(cdns, &stream->bd, |
|---|
| 724 | 1111 | stream->num_bd, offset); |
|---|
| .. | .. |
|---|
| 745 | 1132 | |
|---|
| 746 | 1133 | /* Allocate PDIs for PDMs */ |
|---|
| 747 | 1134 | stream = &cdns->pdm; |
|---|
| 748 | | - offset = CDNS_PDM_PDI_OFFSET; |
|---|
| 749 | 1135 | ret = cdns_allocate_pdi(cdns, &stream->bd, |
|---|
| 750 | 1136 | stream->num_bd, offset); |
|---|
| 751 | 1137 | if (ret) |
|---|
| .. | .. |
|---|
| 762 | 1148 | |
|---|
| 763 | 1149 | ret = cdns_allocate_pdi(cdns, &stream->out, |
|---|
| 764 | 1150 | stream->num_out, offset); |
|---|
| 1151 | + |
|---|
| 765 | 1152 | if (ret) |
|---|
| 766 | 1153 | return ret; |
|---|
| 767 | 1154 | |
|---|
| .. | .. |
|---|
| 769 | 1156 | stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; |
|---|
| 770 | 1157 | cdns->num_ports += stream->num_pdi; |
|---|
| 771 | 1158 | |
|---|
| 772 | | - cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, |
|---|
| 773 | | - sizeof(*cdns->ports), GFP_KERNEL); |
|---|
| 774 | | - if (!cdns->ports) { |
|---|
| 775 | | - ret = -ENOMEM; |
|---|
| 776 | | - return ret; |
|---|
| 777 | | - } |
|---|
| 778 | | - |
|---|
| 779 | | - for (i = 0; i < cdns->num_ports; i++) { |
|---|
| 780 | | - cdns->ports[i].assigned = false; |
|---|
| 781 | | - cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ |
|---|
| 782 | | - } |
|---|
| 783 | | - |
|---|
| 784 | 1159 | return 0; |
|---|
| 785 | 1160 | } |
|---|
| 786 | 1161 | EXPORT_SYMBOL(sdw_cdns_pdi_init); |
|---|
| 1162 | + |
|---|
| 1163 | +static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) |
|---|
| 1164 | +{ |
|---|
| 1165 | + u32 val; |
|---|
| 1166 | + int c; |
|---|
| 1167 | + int r; |
|---|
| 1168 | + |
|---|
| 1169 | + r = sdw_find_row_index(n_rows); |
|---|
| 1170 | + c = sdw_find_col_index(n_cols); |
|---|
| 1171 | + |
|---|
| 1172 | + val = FIELD_PREP(CDNS_MCP_FRAME_SHAPE_ROW_MASK, r); |
|---|
| 1173 | + val |= FIELD_PREP(CDNS_MCP_FRAME_SHAPE_COL_MASK, c); |
|---|
| 1174 | + |
|---|
| 1175 | + return val; |
|---|
| 1176 | +} |
|---|
| 1177 | + |
|---|
| 1178 | +static void cdns_init_clock_ctrl(struct sdw_cdns *cdns) |
|---|
| 1179 | +{ |
|---|
| 1180 | + struct sdw_bus *bus = &cdns->bus; |
|---|
| 1181 | + struct sdw_master_prop *prop = &bus->prop; |
|---|
| 1182 | + u32 val; |
|---|
| 1183 | + u32 ssp_interval; |
|---|
| 1184 | + int divider; |
|---|
| 1185 | + |
|---|
| 1186 | + /* Set clock divider */ |
|---|
| 1187 | + divider = (prop->mclk_freq / prop->max_clk_freq) - 1; |
|---|
| 1188 | + |
|---|
| 1189 | + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, |
|---|
| 1190 | + CDNS_MCP_CLK_MCLKD_MASK, divider); |
|---|
| 1191 | + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, |
|---|
| 1192 | + CDNS_MCP_CLK_MCLKD_MASK, divider); |
|---|
| 1193 | + |
|---|
| 1194 | + /* |
|---|
| 1195 | + * Frame shape changes after initialization have to be done |
|---|
| 1196 | + * with the bank switch mechanism |
|---|
| 1197 | + */ |
|---|
| 1198 | + val = cdns_set_initial_frame_shape(prop->default_row, |
|---|
| 1199 | + prop->default_col); |
|---|
| 1200 | + cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val); |
|---|
| 1201 | + |
|---|
| 1202 | + /* Set SSP interval to default value */ |
|---|
| 1203 | + ssp_interval = prop->default_frame_rate / SDW_CADENCE_GSYNC_HZ; |
|---|
| 1204 | + cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, ssp_interval); |
|---|
| 1205 | + cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval); |
|---|
| 1206 | +} |
|---|
| 787 | 1207 | |
|---|
| 788 | 1208 | /** |
|---|
| 789 | 1209 | * sdw_cdns_init() - Cadence initialization |
|---|
| .. | .. |
|---|
| 792 | 1212 | int sdw_cdns_init(struct sdw_cdns *cdns) |
|---|
| 793 | 1213 | { |
|---|
| 794 | 1214 | u32 val; |
|---|
| 795 | | - int ret; |
|---|
| 796 | 1215 | |
|---|
| 797 | | - /* Exit clock stop */ |
|---|
| 798 | | - ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, |
|---|
| 799 | | - CDNS_MCP_CONTROL_CLK_STOP_CLR); |
|---|
| 800 | | - if (ret < 0) { |
|---|
| 801 | | - dev_err(cdns->dev, "Couldn't exit from clock stop\n"); |
|---|
| 802 | | - return ret; |
|---|
| 803 | | - } |
|---|
| 1216 | + cdns_init_clock_ctrl(cdns); |
|---|
| 804 | 1217 | |
|---|
| 805 | | - /* Set clock divider */ |
|---|
| 806 | | - val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0); |
|---|
| 807 | | - val |= CDNS_DEFAULT_CLK_DIVIDER; |
|---|
| 808 | | - cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); |
|---|
| 1218 | + /* reset msg_count to default value of FIFOLEVEL */ |
|---|
| 1219 | + cdns->msg_count = cdns_readl(cdns, CDNS_MCP_FIFOLEVEL); |
|---|
| 809 | 1220 | |
|---|
| 810 | | - /* Set the default frame shape */ |
|---|
| 811 | | - cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE); |
|---|
| 812 | | - |
|---|
| 813 | | - /* Set SSP interval to default value */ |
|---|
| 814 | | - cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); |
|---|
| 815 | | - cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); |
|---|
| 1221 | + /* flush command FIFOs */ |
|---|
| 1222 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, |
|---|
| 1223 | + CDNS_MCP_CONTROL_CMD_RST); |
|---|
| 816 | 1224 | |
|---|
| 817 | 1225 | /* Set cmd accept mode */ |
|---|
| 818 | 1226 | cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, |
|---|
| 819 | | - CDNS_MCP_CONTROL_CMD_ACCEPT); |
|---|
| 1227 | + CDNS_MCP_CONTROL_CMD_ACCEPT); |
|---|
| 820 | 1228 | |
|---|
| 821 | 1229 | /* Configure mcp config */ |
|---|
| 822 | 1230 | val = cdns_readl(cdns, CDNS_MCP_CONFIG); |
|---|
| 823 | 1231 | |
|---|
| 824 | | - /* Set Max cmd retry to 15 */ |
|---|
| 825 | | - val |= CDNS_MCP_CONFIG_MCMD_RETRY; |
|---|
| 826 | | - |
|---|
| 827 | | - /* Set frame delay between PREQ and ping frame to 15 frames */ |
|---|
| 828 | | - val |= 0xF << SDW_REG_SHIFT(CDNS_MCP_CONFIG_MPREQ_DELAY); |
|---|
| 829 | | - |
|---|
| 830 | | - /* Disable auto bus release */ |
|---|
| 831 | | - val &= ~CDNS_MCP_CONFIG_BUS_REL; |
|---|
| 832 | | - |
|---|
| 833 | | - /* Disable sniffer mode */ |
|---|
| 834 | | - val &= ~CDNS_MCP_CONFIG_SNIFFER; |
|---|
| 1232 | + /* enable bus operations with clock and data */ |
|---|
| 1233 | + val &= ~CDNS_MCP_CONFIG_OP; |
|---|
| 1234 | + val |= CDNS_MCP_CONFIG_OP_NORMAL; |
|---|
| 835 | 1235 | |
|---|
| 836 | 1236 | /* Set cmd mode for Tx and Rx cmds */ |
|---|
| 837 | 1237 | val &= ~CDNS_MCP_CONFIG_CMD; |
|---|
| 838 | 1238 | |
|---|
| 839 | | - /* Set operation to normal */ |
|---|
| 840 | | - val &= ~CDNS_MCP_CONFIG_OP; |
|---|
| 841 | | - val |= CDNS_MCP_CONFIG_OP_NORMAL; |
|---|
| 1239 | + /* Disable sniffer mode */ |
|---|
| 1240 | + val &= ~CDNS_MCP_CONFIG_SNIFFER; |
|---|
| 1241 | + |
|---|
| 1242 | + /* Disable auto bus release */ |
|---|
| 1243 | + val &= ~CDNS_MCP_CONFIG_BUS_REL; |
|---|
| 1244 | + |
|---|
| 1245 | + if (cdns->bus.multi_link) |
|---|
| 1246 | + /* Set Multi-master mode to take gsync into account */ |
|---|
| 1247 | + val |= CDNS_MCP_CONFIG_MMASTER; |
|---|
| 1248 | + |
|---|
| 1249 | + /* leave frame delay to hardware default of 0x1F */ |
|---|
| 1250 | + |
|---|
| 1251 | + /* leave command retry to hardware default of 0 */ |
|---|
| 842 | 1252 | |
|---|
| 843 | 1253 | cdns_writel(cdns, CDNS_MCP_CONFIG, val); |
|---|
| 844 | 1254 | |
|---|
| 1255 | + /* changes will be committed later */ |
|---|
| 845 | 1256 | return 0; |
|---|
| 846 | 1257 | } |
|---|
| 847 | 1258 | EXPORT_SYMBOL(sdw_cdns_init); |
|---|
| 848 | 1259 | |
|---|
| 849 | 1260 | int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) |
|---|
| 850 | 1261 | { |
|---|
| 1262 | + struct sdw_master_prop *prop = &bus->prop; |
|---|
| 851 | 1263 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
|---|
| 852 | | - int mcp_clkctrl_off, mcp_clkctrl; |
|---|
| 1264 | + int mcp_clkctrl_off; |
|---|
| 853 | 1265 | int divider; |
|---|
| 854 | 1266 | |
|---|
| 855 | 1267 | if (!params->curr_dr_freq) { |
|---|
| 856 | | - dev_err(cdns->dev, "NULL curr_dr_freq"); |
|---|
| 1268 | + dev_err(cdns->dev, "NULL curr_dr_freq\n"); |
|---|
| 857 | 1269 | return -EINVAL; |
|---|
| 858 | 1270 | } |
|---|
| 859 | 1271 | |
|---|
| 860 | | - divider = (params->max_dr_freq / params->curr_dr_freq) - 1; |
|---|
| 1272 | + divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR / |
|---|
| 1273 | + params->curr_dr_freq; |
|---|
| 1274 | + divider--; /* divider is 1/(N+1) */ |
|---|
| 861 | 1275 | |
|---|
| 862 | 1276 | if (params->next_bank) |
|---|
| 863 | 1277 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; |
|---|
| 864 | 1278 | else |
|---|
| 865 | 1279 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; |
|---|
| 866 | 1280 | |
|---|
| 867 | | - mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); |
|---|
| 868 | | - mcp_clkctrl |= divider; |
|---|
| 869 | | - cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); |
|---|
| 1281 | + cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider); |
|---|
| 870 | 1282 | |
|---|
| 871 | 1283 | return 0; |
|---|
| 872 | 1284 | } |
|---|
| 873 | 1285 | EXPORT_SYMBOL(cdns_bus_conf); |
|---|
| 874 | 1286 | |
|---|
| 875 | 1287 | static int cdns_port_params(struct sdw_bus *bus, |
|---|
| 876 | | - struct sdw_port_params *p_params, unsigned int bank) |
|---|
| 1288 | + struct sdw_port_params *p_params, unsigned int bank) |
|---|
| 877 | 1289 | { |
|---|
| 878 | 1290 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
|---|
| 879 | 1291 | int dpn_config = 0, dpn_config_off; |
|---|
| .. | .. |
|---|
| 885 | 1297 | |
|---|
| 886 | 1298 | dpn_config = cdns_readl(cdns, dpn_config_off); |
|---|
| 887 | 1299 | |
|---|
| 888 | | - dpn_config |= ((p_params->bps - 1) << |
|---|
| 889 | | - SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); |
|---|
| 890 | | - dpn_config |= (p_params->flow_mode << |
|---|
| 891 | | - SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); |
|---|
| 892 | | - dpn_config |= (p_params->data_mode << |
|---|
| 893 | | - SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); |
|---|
| 1300 | + u32p_replace_bits(&dpn_config, (p_params->bps - 1), CDNS_DPN_CONFIG_WL); |
|---|
| 1301 | + u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW); |
|---|
| 1302 | + u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT); |
|---|
| 894 | 1303 | |
|---|
| 895 | 1304 | cdns_writel(cdns, dpn_config_off, dpn_config); |
|---|
| 896 | 1305 | |
|---|
| .. | .. |
|---|
| 898 | 1307 | } |
|---|
| 899 | 1308 | |
|---|
| 900 | 1309 | static int cdns_transport_params(struct sdw_bus *bus, |
|---|
| 901 | | - struct sdw_transport_params *t_params, |
|---|
| 902 | | - enum sdw_reg_bank bank) |
|---|
| 1310 | + struct sdw_transport_params *t_params, |
|---|
| 1311 | + enum sdw_reg_bank bank) |
|---|
| 903 | 1312 | { |
|---|
| 904 | 1313 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
|---|
| 905 | 1314 | int dpn_offsetctrl = 0, dpn_offsetctrl_off; |
|---|
| .. | .. |
|---|
| 926 | 1335 | } |
|---|
| 927 | 1336 | |
|---|
| 928 | 1337 | dpn_config = cdns_readl(cdns, dpn_config_off); |
|---|
| 929 | | - |
|---|
| 930 | | - dpn_config |= (t_params->blk_grp_ctrl << |
|---|
| 931 | | - SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); |
|---|
| 932 | | - dpn_config |= (t_params->blk_pkg_mode << |
|---|
| 933 | | - SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); |
|---|
| 1338 | + u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC); |
|---|
| 1339 | + u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM); |
|---|
| 934 | 1340 | cdns_writel(cdns, dpn_config_off, dpn_config); |
|---|
| 935 | 1341 | |
|---|
| 936 | | - dpn_offsetctrl |= (t_params->offset1 << |
|---|
| 937 | | - SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); |
|---|
| 938 | | - dpn_offsetctrl |= (t_params->offset2 << |
|---|
| 939 | | - SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); |
|---|
| 1342 | + u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1); |
|---|
| 1343 | + u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2); |
|---|
| 940 | 1344 | cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); |
|---|
| 941 | 1345 | |
|---|
| 942 | | - dpn_hctrl |= (t_params->hstart << |
|---|
| 943 | | - SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); |
|---|
| 944 | | - dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); |
|---|
| 945 | | - dpn_hctrl |= (t_params->lane_ctrl << |
|---|
| 946 | | - SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); |
|---|
| 1346 | + u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART); |
|---|
| 1347 | + u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP); |
|---|
| 1348 | + u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL); |
|---|
| 947 | 1349 | |
|---|
| 948 | 1350 | cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); |
|---|
| 949 | 1351 | cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); |
|---|
| .. | .. |
|---|
| 952 | 1354 | } |
|---|
| 953 | 1355 | |
|---|
| 954 | 1356 | static int cdns_port_enable(struct sdw_bus *bus, |
|---|
| 955 | | - struct sdw_enable_ch *enable_ch, unsigned int bank) |
|---|
| 1357 | + struct sdw_enable_ch *enable_ch, unsigned int bank) |
|---|
| 956 | 1358 | { |
|---|
| 957 | 1359 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
|---|
| 958 | 1360 | int dpn_chnen_off, ch_mask; |
|---|
| .. | .. |
|---|
| 975 | 1377 | }; |
|---|
| 976 | 1378 | |
|---|
| 977 | 1379 | /** |
|---|
| 1380 | + * sdw_cdns_is_clock_stop: Check clock status |
|---|
| 1381 | + * |
|---|
| 1382 | + * @cdns: Cadence instance |
|---|
| 1383 | + */ |
|---|
| 1384 | +bool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns) |
|---|
| 1385 | +{ |
|---|
| 1386 | + return !!(cdns_readl(cdns, CDNS_MCP_STAT) & CDNS_MCP_STAT_CLK_STOP); |
|---|
| 1387 | +} |
|---|
| 1388 | +EXPORT_SYMBOL(sdw_cdns_is_clock_stop); |
|---|
| 1389 | + |
|---|
| 1390 | +/** |
|---|
| 1391 | + * sdw_cdns_clock_stop: Cadence clock stop configuration routine |
|---|
| 1392 | + * |
|---|
| 1393 | + * @cdns: Cadence instance |
|---|
| 1394 | + * @block_wake: prevent wakes if required by the platform |
|---|
| 1395 | + */ |
|---|
| 1396 | +int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) |
|---|
| 1397 | +{ |
|---|
| 1398 | + bool slave_present = false; |
|---|
| 1399 | + struct sdw_slave *slave; |
|---|
| 1400 | + int ret; |
|---|
| 1401 | + |
|---|
| 1402 | + /* Check suspend status */ |
|---|
| 1403 | + if (sdw_cdns_is_clock_stop(cdns)) { |
|---|
| 1404 | + dev_dbg(cdns->dev, "Clock is already stopped\n"); |
|---|
| 1405 | + return 0; |
|---|
| 1406 | + } |
|---|
| 1407 | + |
|---|
| 1408 | + /* |
|---|
| 1409 | + * Before entering clock stop we mask the Slave |
|---|
| 1410 | + * interrupts. This helps avoid having to deal with e.g. a |
|---|
| 1411 | + * Slave becoming UNATTACHED while the clock is being stopped |
|---|
| 1412 | + */ |
|---|
| 1413 | + cdns_enable_slave_interrupts(cdns, false); |
|---|
| 1414 | + |
|---|
| 1415 | + /* |
|---|
| 1416 | + * For specific platforms, it is required to be able to put |
|---|
| 1417 | + * master into a state in which it ignores wake-up trials |
|---|
| 1418 | + * in clock stop state |
|---|
| 1419 | + */ |
|---|
| 1420 | + if (block_wake) |
|---|
| 1421 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 1422 | + CDNS_MCP_CONTROL_BLOCK_WAKEUP, |
|---|
| 1423 | + CDNS_MCP_CONTROL_BLOCK_WAKEUP); |
|---|
| 1424 | + |
|---|
| 1425 | + list_for_each_entry(slave, &cdns->bus.slaves, node) { |
|---|
| 1426 | + if (slave->status == SDW_SLAVE_ATTACHED || |
|---|
| 1427 | + slave->status == SDW_SLAVE_ALERT) { |
|---|
| 1428 | + slave_present = true; |
|---|
| 1429 | + break; |
|---|
| 1430 | + } |
|---|
| 1431 | + } |
|---|
| 1432 | + |
|---|
| 1433 | + /* |
|---|
| 1434 | + * This CMD_ACCEPT should be used when there are no devices |
|---|
| 1435 | + * attached on the link when entering clock stop mode. If this is |
|---|
| 1436 | + * not set and there is a broadcast write then the command ignored |
|---|
| 1437 | + * will be treated as a failure |
|---|
| 1438 | + */ |
|---|
| 1439 | + if (!slave_present) |
|---|
| 1440 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 1441 | + CDNS_MCP_CONTROL_CMD_ACCEPT, |
|---|
| 1442 | + CDNS_MCP_CONTROL_CMD_ACCEPT); |
|---|
| 1443 | + else |
|---|
| 1444 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 1445 | + CDNS_MCP_CONTROL_CMD_ACCEPT, 0); |
|---|
| 1446 | + |
|---|
| 1447 | + /* commit changes */ |
|---|
| 1448 | + ret = cdns_config_update(cdns); |
|---|
| 1449 | + if (ret < 0) { |
|---|
| 1450 | + dev_err(cdns->dev, "%s: config_update failed\n", __func__); |
|---|
| 1451 | + return ret; |
|---|
| 1452 | + } |
|---|
| 1453 | + |
|---|
| 1454 | + /* Prepare slaves for clock stop */ |
|---|
| 1455 | + if (slave_present) { |
|---|
| 1456 | + ret = sdw_bus_prep_clk_stop(&cdns->bus); |
|---|
| 1457 | + if (ret < 0 && ret != -ENODATA) { |
|---|
| 1458 | + dev_err(cdns->dev, "prepare clock stop failed %d\n", ret); |
|---|
| 1459 | + return ret; |
|---|
| 1460 | + } |
|---|
| 1461 | + } |
|---|
| 1462 | + |
|---|
| 1463 | + /* |
|---|
| 1464 | + * Enter clock stop mode and only report errors if there are |
|---|
| 1465 | + * Slave devices present (ALERT or ATTACHED) |
|---|
| 1466 | + */ |
|---|
| 1467 | + ret = sdw_bus_clk_stop(&cdns->bus); |
|---|
| 1468 | + if (ret < 0 && slave_present && ret != -ENODATA) { |
|---|
| 1469 | + dev_err(cdns->dev, "bus clock stop failed %d", ret); |
|---|
| 1470 | + return ret; |
|---|
| 1471 | + } |
|---|
| 1472 | + |
|---|
| 1473 | + ret = cdns_set_wait(cdns, CDNS_MCP_STAT, |
|---|
| 1474 | + CDNS_MCP_STAT_CLK_STOP, |
|---|
| 1475 | + CDNS_MCP_STAT_CLK_STOP); |
|---|
| 1476 | + if (ret < 0) |
|---|
| 1477 | + dev_err(cdns->dev, "Clock stop failed %d\n", ret); |
|---|
| 1478 | + |
|---|
| 1479 | + return ret; |
|---|
| 1480 | +} |
|---|
| 1481 | +EXPORT_SYMBOL(sdw_cdns_clock_stop); |
|---|
| 1482 | + |
|---|
| 1483 | +/** |
|---|
| 1484 | + * sdw_cdns_clock_restart: Cadence PM clock restart configuration routine |
|---|
| 1485 | + * |
|---|
| 1486 | + * @cdns: Cadence instance |
|---|
| 1487 | + * @bus_reset: context may be lost while in low power modes and the bus |
|---|
| 1488 | + * may require a Severe Reset and re-enumeration after a wake. |
|---|
| 1489 | + */ |
|---|
| 1490 | +int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) |
|---|
| 1491 | +{ |
|---|
| 1492 | + int ret; |
|---|
| 1493 | + |
|---|
| 1494 | + /* unmask Slave interrupts that were masked when stopping the clock */ |
|---|
| 1495 | + cdns_enable_slave_interrupts(cdns, true); |
|---|
| 1496 | + |
|---|
| 1497 | + ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, |
|---|
| 1498 | + CDNS_MCP_CONTROL_CLK_STOP_CLR); |
|---|
| 1499 | + if (ret < 0) { |
|---|
| 1500 | + dev_err(cdns->dev, "Couldn't exit from clock stop\n"); |
|---|
| 1501 | + return ret; |
|---|
| 1502 | + } |
|---|
| 1503 | + |
|---|
| 1504 | + ret = cdns_set_wait(cdns, CDNS_MCP_STAT, CDNS_MCP_STAT_CLK_STOP, 0); |
|---|
| 1505 | + if (ret < 0) { |
|---|
| 1506 | + dev_err(cdns->dev, "clock stop exit failed %d\n", ret); |
|---|
| 1507 | + return ret; |
|---|
| 1508 | + } |
|---|
| 1509 | + |
|---|
| 1510 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, |
|---|
| 1511 | + CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0); |
|---|
| 1512 | + |
|---|
| 1513 | + /* |
|---|
| 1514 | + * clear CMD_ACCEPT so that the command ignored |
|---|
| 1515 | + * will be treated as a failure during a broadcast write |
|---|
| 1516 | + */ |
|---|
| 1517 | + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0); |
|---|
| 1518 | + |
|---|
| 1519 | + if (!bus_reset) { |
|---|
| 1520 | + |
|---|
| 1521 | + /* enable bus operations with clock and data */ |
|---|
| 1522 | + cdns_updatel(cdns, CDNS_MCP_CONFIG, |
|---|
| 1523 | + CDNS_MCP_CONFIG_OP, |
|---|
| 1524 | + CDNS_MCP_CONFIG_OP_NORMAL); |
|---|
| 1525 | + |
|---|
| 1526 | + ret = cdns_config_update(cdns); |
|---|
| 1527 | + if (ret < 0) { |
|---|
| 1528 | + dev_err(cdns->dev, "%s: config_update failed\n", __func__); |
|---|
| 1529 | + return ret; |
|---|
| 1530 | + } |
|---|
| 1531 | + |
|---|
| 1532 | + ret = sdw_bus_exit_clk_stop(&cdns->bus); |
|---|
| 1533 | + if (ret < 0) |
|---|
| 1534 | + dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); |
|---|
| 1535 | + } |
|---|
| 1536 | + |
|---|
| 1537 | + return ret; |
|---|
| 1538 | +} |
|---|
| 1539 | +EXPORT_SYMBOL(sdw_cdns_clock_restart); |
|---|
| 1540 | + |
|---|
| 1541 | +/** |
|---|
| 978 | 1542 | * sdw_cdns_probe() - Cadence probe routine |
|---|
| 979 | 1543 | * @cdns: Cadence instance |
|---|
| 980 | 1544 | */ |
|---|
| .. | .. |
|---|
| 983 | 1547 | init_completion(&cdns->tx_complete); |
|---|
| 984 | 1548 | cdns->bus.port_ops = &cdns_port_ops; |
|---|
| 985 | 1549 | |
|---|
| 1550 | + INIT_WORK(&cdns->work, cdns_update_slave_status_work); |
|---|
| 986 | 1551 | return 0; |
|---|
| 987 | 1552 | } |
|---|
| 988 | 1553 | EXPORT_SYMBOL(sdw_cdns_probe); |
|---|
| 989 | 1554 | |
|---|
| 990 | 1555 | int cdns_set_sdw_stream(struct snd_soc_dai *dai, |
|---|
| 991 | | - void *stream, bool pcm, int direction) |
|---|
| 1556 | + void *stream, bool pcm, int direction) |
|---|
| 992 | 1557 | { |
|---|
| 993 | 1558 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
|---|
| 994 | 1559 | struct sdw_cdns_dma_data *dma; |
|---|
| 995 | 1560 | |
|---|
| 996 | | - dma = kzalloc(sizeof(*dma), GFP_KERNEL); |
|---|
| 997 | | - if (!dma) |
|---|
| 998 | | - return -ENOMEM; |
|---|
| 1561 | + if (stream) { |
|---|
| 1562 | + /* first paranoia check */ |
|---|
| 1563 | + if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 1564 | + dma = dai->playback_dma_data; |
|---|
| 1565 | + else |
|---|
| 1566 | + dma = dai->capture_dma_data; |
|---|
| 999 | 1567 | |
|---|
| 1000 | | - if (pcm) |
|---|
| 1001 | | - dma->stream_type = SDW_STREAM_PCM; |
|---|
| 1002 | | - else |
|---|
| 1003 | | - dma->stream_type = SDW_STREAM_PDM; |
|---|
| 1568 | + if (dma) { |
|---|
| 1569 | + dev_err(dai->dev, |
|---|
| 1570 | + "dma_data already allocated for dai %s\n", |
|---|
| 1571 | + dai->name); |
|---|
| 1572 | + return -EINVAL; |
|---|
| 1573 | + } |
|---|
| 1004 | 1574 | |
|---|
| 1005 | | - dma->bus = &cdns->bus; |
|---|
| 1006 | | - dma->link_id = cdns->instance; |
|---|
| 1575 | + /* allocate and set dma info */ |
|---|
| 1576 | + dma = kzalloc(sizeof(*dma), GFP_KERNEL); |
|---|
| 1577 | + if (!dma) |
|---|
| 1578 | + return -ENOMEM; |
|---|
| 1007 | 1579 | |
|---|
| 1008 | | - dma->stream = stream; |
|---|
| 1580 | + if (pcm) |
|---|
| 1581 | + dma->stream_type = SDW_STREAM_PCM; |
|---|
| 1582 | + else |
|---|
| 1583 | + dma->stream_type = SDW_STREAM_PDM; |
|---|
| 1009 | 1584 | |
|---|
| 1010 | | - if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 1011 | | - dai->playback_dma_data = dma; |
|---|
| 1012 | | - else |
|---|
| 1013 | | - dai->capture_dma_data = dma; |
|---|
| 1585 | + dma->bus = &cdns->bus; |
|---|
| 1586 | + dma->link_id = cdns->instance; |
|---|
| 1014 | 1587 | |
|---|
| 1588 | + dma->stream = stream; |
|---|
| 1589 | + |
|---|
| 1590 | + if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 1591 | + dai->playback_dma_data = dma; |
|---|
| 1592 | + else |
|---|
| 1593 | + dai->capture_dma_data = dma; |
|---|
| 1594 | + } else { |
|---|
| 1595 | + /* for NULL stream we release allocated dma_data */ |
|---|
| 1596 | + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { |
|---|
| 1597 | + kfree(dai->playback_dma_data); |
|---|
| 1598 | + dai->playback_dma_data = NULL; |
|---|
| 1599 | + } else { |
|---|
| 1600 | + kfree(dai->capture_dma_data); |
|---|
| 1601 | + dai->capture_dma_data = NULL; |
|---|
| 1602 | + } |
|---|
| 1603 | + } |
|---|
| 1015 | 1604 | return 0; |
|---|
| 1016 | 1605 | } |
|---|
| 1017 | 1606 | EXPORT_SYMBOL(cdns_set_sdw_stream); |
|---|
| .. | .. |
|---|
| 1020 | 1609 | * cdns_find_pdi() - Find a free PDI |
|---|
| 1021 | 1610 | * |
|---|
| 1022 | 1611 | * @cdns: Cadence instance |
|---|
| 1612 | + * @offset: Starting offset |
|---|
| 1023 | 1613 | * @num: Number of PDIs |
|---|
| 1024 | 1614 | * @pdi: PDI instances |
|---|
| 1615 | + * @dai_id: DAI id |
|---|
| 1025 | 1616 | * |
|---|
| 1026 | | - * Find and return a free PDI for a given PDI array |
|---|
| 1617 | + * Find a PDI for a given PDI array. The PDI num and dai_id are |
|---|
| 1618 | + * expected to match, return NULL otherwise. |
|---|
| 1027 | 1619 | */ |
|---|
| 1028 | 1620 | static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, |
|---|
| 1029 | | - unsigned int num, struct sdw_cdns_pdi *pdi) |
|---|
| 1621 | + unsigned int offset, |
|---|
| 1622 | + unsigned int num, |
|---|
| 1623 | + struct sdw_cdns_pdi *pdi, |
|---|
| 1624 | + int dai_id) |
|---|
| 1030 | 1625 | { |
|---|
| 1031 | 1626 | int i; |
|---|
| 1032 | 1627 | |
|---|
| 1033 | | - for (i = 0; i < num; i++) { |
|---|
| 1034 | | - if (pdi[i].assigned == true) |
|---|
| 1035 | | - continue; |
|---|
| 1036 | | - pdi[i].assigned = true; |
|---|
| 1037 | | - return &pdi[i]; |
|---|
| 1038 | | - } |
|---|
| 1628 | + for (i = offset; i < offset + num; i++) |
|---|
| 1629 | + if (pdi[i].num == dai_id) |
|---|
| 1630 | + return &pdi[i]; |
|---|
| 1039 | 1631 | |
|---|
| 1040 | 1632 | return NULL; |
|---|
| 1041 | 1633 | } |
|---|
| .. | .. |
|---|
| 1044 | 1636 | * sdw_cdns_config_stream: Configure a stream |
|---|
| 1045 | 1637 | * |
|---|
| 1046 | 1638 | * @cdns: Cadence instance |
|---|
| 1047 | | - * @port: Cadence data port |
|---|
| 1048 | 1639 | * @ch: Channel count |
|---|
| 1049 | 1640 | * @dir: Data direction |
|---|
| 1050 | 1641 | * @pdi: PDI to be used |
|---|
| 1051 | 1642 | */ |
|---|
| 1052 | 1643 | void sdw_cdns_config_stream(struct sdw_cdns *cdns, |
|---|
| 1053 | | - struct sdw_cdns_port *port, |
|---|
| 1054 | | - u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) |
|---|
| 1644 | + u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) |
|---|
| 1055 | 1645 | { |
|---|
| 1056 | 1646 | u32 offset, val = 0; |
|---|
| 1057 | 1647 | |
|---|
| 1058 | | - if (dir == SDW_DATA_DIR_RX) |
|---|
| 1648 | + if (dir == SDW_DATA_DIR_RX) { |
|---|
| 1059 | 1649 | val = CDNS_PORTCTRL_DIRN; |
|---|
| 1060 | 1650 | |
|---|
| 1061 | | - offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET; |
|---|
| 1062 | | - cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); |
|---|
| 1651 | + if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL) |
|---|
| 1652 | + val |= CDNS_PORTCTRL_TEST_FAILED; |
|---|
| 1653 | + } |
|---|
| 1654 | + offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET; |
|---|
| 1655 | + cdns_updatel(cdns, offset, |
|---|
| 1656 | + CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED, |
|---|
| 1657 | + val); |
|---|
| 1063 | 1658 | |
|---|
| 1064 | | - val = port->num; |
|---|
| 1065 | | - val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL); |
|---|
| 1659 | + val = pdi->num; |
|---|
| 1660 | + val |= CDNS_PDI_CONFIG_SOFT_RESET; |
|---|
| 1661 | + val |= FIELD_PREP(CDNS_PDI_CONFIG_CHANNEL, (1 << ch) - 1); |
|---|
| 1066 | 1662 | cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); |
|---|
| 1067 | 1663 | } |
|---|
| 1068 | 1664 | EXPORT_SYMBOL(sdw_cdns_config_stream); |
|---|
| 1069 | 1665 | |
|---|
| 1070 | 1666 | /** |
|---|
| 1071 | | - * cdns_get_num_pdi() - Get number of PDIs required |
|---|
| 1072 | | - * |
|---|
| 1073 | | - * @cdns: Cadence instance |
|---|
| 1074 | | - * @pdi: PDI to be used |
|---|
| 1075 | | - * @num: Number of PDIs |
|---|
| 1076 | | - * @ch_count: Channel count |
|---|
| 1077 | | - */ |
|---|
| 1078 | | -static int cdns_get_num_pdi(struct sdw_cdns *cdns, |
|---|
| 1079 | | - struct sdw_cdns_pdi *pdi, |
|---|
| 1080 | | - unsigned int num, u32 ch_count) |
|---|
| 1081 | | -{ |
|---|
| 1082 | | - int i, pdis = 0; |
|---|
| 1083 | | - |
|---|
| 1084 | | - for (i = 0; i < num; i++) { |
|---|
| 1085 | | - if (pdi[i].assigned == true) |
|---|
| 1086 | | - continue; |
|---|
| 1087 | | - |
|---|
| 1088 | | - if (pdi[i].ch_count < ch_count) |
|---|
| 1089 | | - ch_count -= pdi[i].ch_count; |
|---|
| 1090 | | - else |
|---|
| 1091 | | - ch_count = 0; |
|---|
| 1092 | | - |
|---|
| 1093 | | - pdis++; |
|---|
| 1094 | | - |
|---|
| 1095 | | - if (!ch_count) |
|---|
| 1096 | | - break; |
|---|
| 1097 | | - } |
|---|
| 1098 | | - |
|---|
| 1099 | | - if (ch_count) |
|---|
| 1100 | | - return 0; |
|---|
| 1101 | | - |
|---|
| 1102 | | - return pdis; |
|---|
| 1103 | | -} |
|---|
| 1104 | | - |
|---|
| 1105 | | -/** |
|---|
| 1106 | | - * sdw_cdns_get_stream() - Get stream information |
|---|
| 1667 | + * sdw_cdns_alloc_pdi() - Allocate a PDI |
|---|
| 1107 | 1668 | * |
|---|
| 1108 | 1669 | * @cdns: Cadence instance |
|---|
| 1109 | 1670 | * @stream: Stream to be allocated |
|---|
| 1110 | 1671 | * @ch: Channel count |
|---|
| 1111 | 1672 | * @dir: Data direction |
|---|
| 1673 | + * @dai_id: DAI id |
|---|
| 1112 | 1674 | */ |
|---|
| 1113 | | -int sdw_cdns_get_stream(struct sdw_cdns *cdns, |
|---|
| 1114 | | - struct sdw_cdns_streams *stream, |
|---|
| 1115 | | - u32 ch, u32 dir) |
|---|
| 1116 | | -{ |
|---|
| 1117 | | - int pdis = 0; |
|---|
| 1118 | | - |
|---|
| 1119 | | - if (dir == SDW_DATA_DIR_RX) |
|---|
| 1120 | | - pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch); |
|---|
| 1121 | | - else |
|---|
| 1122 | | - pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch); |
|---|
| 1123 | | - |
|---|
| 1124 | | - /* check if we found PDI, else find in bi-directional */ |
|---|
| 1125 | | - if (!pdis) |
|---|
| 1126 | | - pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch); |
|---|
| 1127 | | - |
|---|
| 1128 | | - return pdis; |
|---|
| 1129 | | -} |
|---|
| 1130 | | -EXPORT_SYMBOL(sdw_cdns_get_stream); |
|---|
| 1131 | | - |
|---|
| 1132 | | -/** |
|---|
| 1133 | | - * sdw_cdns_alloc_stream() - Allocate a stream |
|---|
| 1134 | | - * |
|---|
| 1135 | | - * @cdns: Cadence instance |
|---|
| 1136 | | - * @stream: Stream to be allocated |
|---|
| 1137 | | - * @port: Cadence data port |
|---|
| 1138 | | - * @ch: Channel count |
|---|
| 1139 | | - * @dir: Data direction |
|---|
| 1140 | | - */ |
|---|
| 1141 | | -int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, |
|---|
| 1142 | | - struct sdw_cdns_streams *stream, |
|---|
| 1143 | | - struct sdw_cdns_port *port, u32 ch, u32 dir) |
|---|
| 1675 | +struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, |
|---|
| 1676 | + struct sdw_cdns_streams *stream, |
|---|
| 1677 | + u32 ch, u32 dir, int dai_id) |
|---|
| 1144 | 1678 | { |
|---|
| 1145 | 1679 | struct sdw_cdns_pdi *pdi = NULL; |
|---|
| 1146 | 1680 | |
|---|
| 1147 | 1681 | if (dir == SDW_DATA_DIR_RX) |
|---|
| 1148 | | - pdi = cdns_find_pdi(cdns, stream->num_in, stream->in); |
|---|
| 1682 | + pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, |
|---|
| 1683 | + dai_id); |
|---|
| 1149 | 1684 | else |
|---|
| 1150 | | - pdi = cdns_find_pdi(cdns, stream->num_out, stream->out); |
|---|
| 1685 | + pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, |
|---|
| 1686 | + dai_id); |
|---|
| 1151 | 1687 | |
|---|
| 1152 | 1688 | /* check if we found a PDI, else find in bi-directional */ |
|---|
| 1153 | 1689 | if (!pdi) |
|---|
| 1154 | | - pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd); |
|---|
| 1690 | + pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, |
|---|
| 1691 | + dai_id); |
|---|
| 1155 | 1692 | |
|---|
| 1156 | | - if (!pdi) |
|---|
| 1157 | | - return -EIO; |
|---|
| 1693 | + if (pdi) { |
|---|
| 1694 | + pdi->l_ch_num = 0; |
|---|
| 1695 | + pdi->h_ch_num = ch - 1; |
|---|
| 1696 | + pdi->dir = dir; |
|---|
| 1697 | + pdi->ch_count = ch; |
|---|
| 1698 | + } |
|---|
| 1158 | 1699 | |
|---|
| 1159 | | - port->pdi = pdi; |
|---|
| 1160 | | - pdi->l_ch_num = 0; |
|---|
| 1161 | | - pdi->h_ch_num = ch - 1; |
|---|
| 1162 | | - pdi->dir = dir; |
|---|
| 1163 | | - pdi->ch_count = ch; |
|---|
| 1164 | | - |
|---|
| 1165 | | - return 0; |
|---|
| 1700 | + return pdi; |
|---|
| 1166 | 1701 | } |
|---|
| 1167 | | -EXPORT_SYMBOL(sdw_cdns_alloc_stream); |
|---|
| 1168 | | - |
|---|
| 1169 | | -void sdw_cdns_shutdown(struct snd_pcm_substream *substream, |
|---|
| 1170 | | - struct snd_soc_dai *dai) |
|---|
| 1171 | | -{ |
|---|
| 1172 | | - struct sdw_cdns_dma_data *dma; |
|---|
| 1173 | | - |
|---|
| 1174 | | - dma = snd_soc_dai_get_dma_data(dai, substream); |
|---|
| 1175 | | - if (!dma) |
|---|
| 1176 | | - return; |
|---|
| 1177 | | - |
|---|
| 1178 | | - snd_soc_dai_set_dma_data(dai, substream, NULL); |
|---|
| 1179 | | - kfree(dma); |
|---|
| 1180 | | -} |
|---|
| 1181 | | -EXPORT_SYMBOL(sdw_cdns_shutdown); |
|---|
| 1702 | +EXPORT_SYMBOL(sdw_cdns_alloc_pdi); |
|---|
| 1182 | 1703 | |
|---|
| 1183 | 1704 | MODULE_LICENSE("Dual BSD/GPL"); |
|---|
| 1184 | 1705 | MODULE_DESCRIPTION("Cadence Soundwire Library"); |
|---|