Skip to content

Commit

Permalink
ipc: Added support for IPC interrupt mode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kuppuswamy Sathyanarayanan authored and Matthew Garrett committed Nov 21, 2013
1 parent c7094d1 commit ed12f29
Showing 1 changed file with 45 additions and 3 deletions.
48 changes: 45 additions & 3 deletions drivers/platform/x86/intel_scu_ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@

#define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */
#define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */
#define IPC_IOC 0x100 /* IPC command register IOC bit */

enum {
SCU_IPC_LINCROFT,
Expand All @@ -74,6 +75,7 @@ struct intel_scu_ipc_pdata_t {
u32 i2c_base;
u32 ipc_len;
u32 i2c_len;
u8 irq_mode;
};

static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = {
Expand All @@ -82,24 +84,28 @@ static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = {
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 0,
},
[SCU_IPC_PENWELL] = {
.ipc_base = 0xff11c000,
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 1,
},
[SCU_IPC_CLOVERVIEW] = {
.ipc_base = 0xff11c000,
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 1,
},
[SCU_IPC_TANGIER] = {
.ipc_base = 0xff009000,
.i2c_base = 0xff00d000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 0,
},
};

Expand All @@ -110,6 +116,8 @@ struct intel_scu_ipc_dev {
struct pci_dev *pdev;
void __iomem *ipc_base;
void __iomem *i2c_base;
struct completion cmd_complete;
u8 irq_mode;
};

static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
Expand All @@ -136,6 +144,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
*/
static inline void ipc_command(u32 cmd) /* Send ipc command */
{
if (ipcdev.irq_mode) {
reinit_completion(&ipcdev.cmd_complete);
writel(cmd | IPC_IOC, ipcdev.ipc_base);
}
writel(cmd, ipcdev.ipc_base);
}

Expand Down Expand Up @@ -194,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
return 0;
}

/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
static inline int ipc_wait_for_interrupt(void)
{
int status;

if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
struct device *dev = &ipcdev.pdev->dev;
dev_err(dev, "IPC timed out\n");
return -ETIMEDOUT;
}

status = ipc_read_status();

if ((status >> 1) & 1)
return -EIO;

return 0;
}

int intel_scu_ipc_check_status(void)
{
return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
}

/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
{
Expand Down Expand Up @@ -234,7 +270,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
}

err = busy_loop();
err = intel_scu_ipc_check_status();
if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
Expand Down Expand Up @@ -429,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
return -ENODEV;
}
ipc_command(sub << 12 | cmd);
err = busy_loop();
err = intel_scu_ipc_check_status();
mutex_unlock(&ipclock);
return err;
}
Expand Down Expand Up @@ -463,7 +499,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
ipc_data_writel(*in++, 4 * i);

ipc_command((inlen << 16) | (sub << 12) | cmd);
err = busy_loop();
err = intel_scu_ipc_check_status();

if (!err) {
for (i = 0; i < outlen; i++)
Expand Down Expand Up @@ -531,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
*/
static irqreturn_t ioc(int irq, void *dev_id)
{
if (ipcdev.irq_mode)
complete(&ipcdev.cmd_complete);

return IRQ_HANDLED;
}

Expand All @@ -555,6 +594,7 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
pdata = &intel_scu_ipc_pdata[pid];

ipcdev.pdev = pci_dev_get(dev);
ipcdev.irq_mode = pdata->irq_mode;

err = pci_enable_device(dev);
if (err)
Expand All @@ -568,6 +608,8 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (!pci_resource)
return -ENOMEM;

init_completion(&ipcdev.cmd_complete);

if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
return -EBUSY;

Expand Down

0 comments on commit ed12f29

Please sign in to comment.