diff options
| author | Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> | 2013-11-15 19:21:54 -0500 |
|---|---|---|
| committer | Matthew Garrett <matthew.garrett@nebula.com> | 2013-11-20 20:16:21 -0500 |
| commit | ed12f295bfd5c378970106891f12999589aec4e5 (patch) | |
| tree | f98475306d6404a1dcc35370b88ed810e30c4494 | |
| parent | c7094d1d994c23950d8a55d33dcb7ed6d9b13f8f (diff) | |
ipc: Added support for IPC interrupt mode
This patch adds support for ipc command interrupt mode.
Also added platform data option to select 'irq_mode'
irq_mode = 1: configure the driver to receive IOC interrupt
for each successful ipc_command.
irq_mode = 0: makes driver use polling method to
track the command completion status.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: David Cohen <david.a.cohen@linux.intel.com>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
| -rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index e26830f6c8dd..60ea476a9130 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
| @@ -60,6 +60,7 @@ | |||
| 60 | 60 | ||
| 61 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ | 61 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ |
| 62 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ | 62 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ |
| 63 | #define IPC_IOC 0x100 /* IPC command register IOC bit */ | ||
| 63 | 64 | ||
| 64 | enum { | 65 | enum { |
| 65 | SCU_IPC_LINCROFT, | 66 | SCU_IPC_LINCROFT, |
| @@ -74,6 +75,7 @@ struct intel_scu_ipc_pdata_t { | |||
| 74 | u32 i2c_base; | 75 | u32 i2c_base; |
| 75 | u32 ipc_len; | 76 | u32 ipc_len; |
| 76 | u32 i2c_len; | 77 | u32 i2c_len; |
| 78 | u8 irq_mode; | ||
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = { | 81 | static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = { |
| @@ -82,24 +84,28 @@ static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = { | |||
| 82 | .i2c_base = 0xff12b000, | 84 | .i2c_base = 0xff12b000, |
| 83 | .ipc_len = 0x100, | 85 | .ipc_len = 0x100, |
| 84 | .i2c_len = 0x10, | 86 | .i2c_len = 0x10, |
| 87 | .irq_mode = 0, | ||
| 85 | }, | 88 | }, |
| 86 | [SCU_IPC_PENWELL] = { | 89 | [SCU_IPC_PENWELL] = { |
| 87 | .ipc_base = 0xff11c000, | 90 | .ipc_base = 0xff11c000, |
| 88 | .i2c_base = 0xff12b000, | 91 | .i2c_base = 0xff12b000, |
| 89 | .ipc_len = 0x100, | 92 | .ipc_len = 0x100, |
| 90 | .i2c_len = 0x10, | 93 | .i2c_len = 0x10, |
| 94 | .irq_mode = 1, | ||
| 91 | }, | 95 | }, |
| 92 | [SCU_IPC_CLOVERVIEW] = { | 96 | [SCU_IPC_CLOVERVIEW] = { |
| 93 | .ipc_base = 0xff11c000, | 97 | .ipc_base = 0xff11c000, |
| 94 | .i2c_base = 0xff12b000, | 98 | .i2c_base = 0xff12b000, |
| 95 | .ipc_len = 0x100, | 99 | .ipc_len = 0x100, |
| 96 | .i2c_len = 0x10, | 100 | .i2c_len = 0x10, |
| 101 | .irq_mode = 1, | ||
| 97 | }, | 102 | }, |
| 98 | [SCU_IPC_TANGIER] = { | 103 | [SCU_IPC_TANGIER] = { |
| 99 | .ipc_base = 0xff009000, | 104 | .ipc_base = 0xff009000, |
| 100 | .i2c_base = 0xff00d000, | 105 | .i2c_base = 0xff00d000, |
| 101 | .ipc_len = 0x100, | 106 | .ipc_len = 0x100, |
| 102 | .i2c_len = 0x10, | 107 | .i2c_len = 0x10, |
| 108 | .irq_mode = 0, | ||
| 103 | }, | 109 | }, |
| 104 | }; | 110 | }; |
| 105 | 111 | ||
| @@ -110,6 +116,8 @@ struct intel_scu_ipc_dev { | |||
| 110 | struct pci_dev *pdev; | 116 | struct pci_dev *pdev; |
| 111 | void __iomem *ipc_base; | 117 | void __iomem *ipc_base; |
| 112 | void __iomem *i2c_base; | 118 | void __iomem *i2c_base; |
| 119 | struct completion cmd_complete; | ||
| 120 | u8 irq_mode; | ||
| 113 | }; | 121 | }; |
| 114 | 122 | ||
| 115 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ | 123 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ |
| @@ -136,6 +144,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ | |||
| 136 | */ | 144 | */ |
| 137 | static inline void ipc_command(u32 cmd) /* Send ipc command */ | 145 | static inline void ipc_command(u32 cmd) /* Send ipc command */ |
| 138 | { | 146 | { |
| 147 | if (ipcdev.irq_mode) { | ||
| 148 | reinit_completion(&ipcdev.cmd_complete); | ||
| 149 | writel(cmd | IPC_IOC, ipcdev.ipc_base); | ||
| 150 | } | ||
| 139 | writel(cmd, ipcdev.ipc_base); | 151 | writel(cmd, ipcdev.ipc_base); |
| 140 | } | 152 | } |
| 141 | 153 | ||
| @@ -194,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */ | |||
| 194 | return 0; | 206 | return 0; |
| 195 | } | 207 | } |
| 196 | 208 | ||
| 209 | /* Wait till ipc ioc interrupt is received or timeout in 3 HZ */ | ||
| 210 | static inline int ipc_wait_for_interrupt(void) | ||
| 211 | { | ||
| 212 | int status; | ||
| 213 | |||
| 214 | if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) { | ||
| 215 | struct device *dev = &ipcdev.pdev->dev; | ||
| 216 | dev_err(dev, "IPC timed out\n"); | ||
| 217 | return -ETIMEDOUT; | ||
| 218 | } | ||
| 219 | |||
| 220 | status = ipc_read_status(); | ||
| 221 | |||
| 222 | if ((status >> 1) & 1) | ||
| 223 | return -EIO; | ||
| 224 | |||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | int intel_scu_ipc_check_status(void) | ||
| 229 | { | ||
| 230 | return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop(); | ||
| 231 | } | ||
| 232 | |||
| 197 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ | 233 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ |
| 198 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | 234 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) |
| 199 | { | 235 | { |
| @@ -234,7 +270,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | |||
| 234 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); | 270 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); |
| 235 | } | 271 | } |
| 236 | 272 | ||
| 237 | err = busy_loop(); | 273 | err = intel_scu_ipc_check_status(); |
| 238 | if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ | 274 | if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ |
| 239 | /* Workaround: values are read as 0 without memcpy_fromio */ | 275 | /* Workaround: values are read as 0 without memcpy_fromio */ |
| 240 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); | 276 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); |
| @@ -429,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub) | |||
| 429 | return -ENODEV; | 465 | return -ENODEV; |
| 430 | } | 466 | } |
| 431 | ipc_command(sub << 12 | cmd); | 467 | ipc_command(sub << 12 | cmd); |
| 432 | err = busy_loop(); | 468 | err = intel_scu_ipc_check_status(); |
| 433 | mutex_unlock(&ipclock); | 469 | mutex_unlock(&ipclock); |
| 434 | return err; | 470 | return err; |
| 435 | } | 471 | } |
| @@ -463,7 +499,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | |||
| 463 | ipc_data_writel(*in++, 4 * i); | 499 | ipc_data_writel(*in++, 4 * i); |
| 464 | 500 | ||
| 465 | ipc_command((inlen << 16) | (sub << 12) | cmd); | 501 | ipc_command((inlen << 16) | (sub << 12) | cmd); |
| 466 | err = busy_loop(); | 502 | err = intel_scu_ipc_check_status(); |
| 467 | 503 | ||
| 468 | if (!err) { | 504 | if (!err) { |
| 469 | for (i = 0; i < outlen; i++) | 505 | for (i = 0; i < outlen; i++) |
| @@ -531,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); | |||
| 531 | */ | 567 | */ |
| 532 | static irqreturn_t ioc(int irq, void *dev_id) | 568 | static irqreturn_t ioc(int irq, void *dev_id) |
| 533 | { | 569 | { |
| 570 | if (ipcdev.irq_mode) | ||
| 571 | complete(&ipcdev.cmd_complete); | ||
| 572 | |||
| 534 | return IRQ_HANDLED; | 573 | return IRQ_HANDLED; |
| 535 | } | 574 | } |
| 536 | 575 | ||
| @@ -555,6 +594,7 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 555 | pdata = &intel_scu_ipc_pdata[pid]; | 594 | pdata = &intel_scu_ipc_pdata[pid]; |
| 556 | 595 | ||
| 557 | ipcdev.pdev = pci_dev_get(dev); | 596 | ipcdev.pdev = pci_dev_get(dev); |
| 597 | ipcdev.irq_mode = pdata->irq_mode; | ||
| 558 | 598 | ||
| 559 | err = pci_enable_device(dev); | 599 | err = pci_enable_device(dev); |
| 560 | if (err) | 600 | if (err) |
| @@ -568,6 +608,8 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 568 | if (!pci_resource) | 608 | if (!pci_resource) |
| 569 | return -ENOMEM; | 609 | return -ENOMEM; |
| 570 | 610 | ||
| 611 | init_completion(&ipcdev.cmd_complete); | ||
| 612 | |||
| 571 | if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) | 613 | if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) |
| 572 | return -EBUSY; | 614 | return -EBUSY; |
| 573 | 615 | ||
