diff options
Diffstat (limited to 'drivers/platform/x86/intel_scu_ipc.c')
| -rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 350 | 
1 files changed, 140 insertions, 210 deletions
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 41a9e34899a..76ca094ed01 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -9,7 +9,7 @@   * as published by the Free Software Foundation; version 2   * of the License.   * - * SCU runing in ARC processor communicates with other entity running in IA + * SCU running in ARC processor communicates with other entity running in IA   * core through IPC mechanism which in turn messaging between IA core ad SCU.   * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and   * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with @@ -19,12 +19,13 @@  #include <linux/delay.h>  #include <linux/errno.h>  #include <linux/init.h> -#include <linux/sysdev.h> +#include <linux/device.h>  #include <linux/pm.h>  #include <linux/pci.h>  #include <linux/interrupt.h>  #include <linux/sfi.h> -#include <asm/mrst.h> +#include <linux/module.h> +#include <asm/intel-mid.h>  #include <asm/intel_scu_ipc.h>  /* IPC defines the following message types */ @@ -57,12 +58,48 @@   *    message handler is called within firmware.   */ -#define IPC_BASE_ADDR     0xFF11C000	/* IPC1 base register address */ -#define IPC_MAX_ADDR      0x100		/* Maximum IPC regisers */  #define IPC_WWBUF_SIZE    20		/* IPC Write buffer Size */  #define IPC_RWBUF_SIZE    20		/* IPC Read buffer Size */ -#define IPC_I2C_BASE      0xFF12B000	/* I2C control register base address */ -#define IPC_I2C_MAX_ADDR  0x10		/* Maximum I2C regisers */ +#define IPC_IOC	          0x100		/* IPC command register IOC bit */ + +#define PCI_DEVICE_ID_LINCROFT		0x082a +#define PCI_DEVICE_ID_PENWELL		0x080e +#define PCI_DEVICE_ID_CLOVERVIEW	0x08ea +#define PCI_DEVICE_ID_TANGIER		0x11a0 + +/* intel scu ipc driver data*/ +struct intel_scu_ipc_pdata_t { +	u32 ipc_base; +	u32 i2c_base; +	u32 ipc_len; +	u32 i2c_len; +	u8 irq_mode; +}; + +static struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = { +	.ipc_base = 0xff11c000, +	.i2c_base = 0xff12b000, +	.ipc_len = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 0, +}; + +/* Penwell and Cloverview */ +static struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = { +	.ipc_base = 0xff11c000, +	.i2c_base = 0xff12b000, +	.ipc_len = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 1, +}; + +static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = { +	.ipc_base = 0xff009000, +	.i2c_base  = 0xff00d000, +	.ipc_len  = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 0, +};  static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);  static void ipc_remove(struct pci_dev *pdev); @@ -71,6 +108,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 */ @@ -97,6 +136,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);  } @@ -155,12 +198,36 @@ 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)  { -	int i, nc, bytes, d; +	int nc;  	u32 offset = 0; -	u32 err = 0; +	int err;  	u8 cbuf[IPC_WWBUF_SIZE] = { };  	u32 *wbuf = (u32 *)&cbuf; @@ -173,55 +240,34 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)  		return -ENODEV;  	} -	if (platform != MRST_CPU_CHIP_PENWELL) { -		bytes = 0; -		d = 0; -		for (i = 0; i < count; i++) { -			cbuf[bytes++] = addr[i]; -			cbuf[bytes++] = addr[i] >> 8; -			if (id != IPC_CMD_PCNTRL_R) -				cbuf[bytes++] = data[d++]; -			if (id == IPC_CMD_PCNTRL_M) -				cbuf[bytes++] = data[d++]; -		} -		for (i = 0; i < bytes; i += 4) -			ipc_data_writel(wbuf[i/4], i); -		ipc_command(bytes << 16 |  id << 12 | 0 << 8 | op); -	} else { -		for (nc = 0; nc < count; nc++, offset += 2) { -			cbuf[offset] = addr[nc]; -			cbuf[offset + 1] = addr[nc] >> 8; -		} +	for (nc = 0; nc < count; nc++, offset += 2) { +		cbuf[offset] = addr[nc]; +		cbuf[offset + 1] = addr[nc] >> 8; +	} -		if (id == IPC_CMD_PCNTRL_R) { -			for (nc = 0, offset = 0; nc < count; nc++, offset += 4) -				ipc_data_writel(wbuf[nc], offset); -			ipc_command((count*2) << 16 |  id << 12 | 0 << 8 | op); -		} else if (id == IPC_CMD_PCNTRL_W) { -			for (nc = 0; nc < count; nc++, offset += 1) -				cbuf[offset] = data[nc]; -			for (nc = 0, offset = 0; nc < count; nc++, offset += 4) -				ipc_data_writel(wbuf[nc], offset); -			ipc_command((count*3) << 16 |  id << 12 | 0 << 8 | op); -		} else if (id == IPC_CMD_PCNTRL_M) { -			cbuf[offset] = data[0]; -			cbuf[offset + 1] = data[1]; -			ipc_data_writel(wbuf[0], 0); /* Write wbuff */ -			ipc_command(4 << 16 |  id << 12 | 0 << 8 | op); -		} +	if (id == IPC_CMD_PCNTRL_R) { +		for (nc = 0, offset = 0; nc < count; nc++, offset += 4) +			ipc_data_writel(wbuf[nc], offset); +		ipc_command((count*2) << 16 |  id << 12 | 0 << 8 | op); +	} else if (id == IPC_CMD_PCNTRL_W) { +		for (nc = 0; nc < count; nc++, offset += 1) +			cbuf[offset] = data[nc]; +		for (nc = 0, offset = 0; nc < count; nc++, offset += 4) +			ipc_data_writel(wbuf[nc], offset); +		ipc_command((count*3) << 16 |  id << 12 | 0 << 8 | op); +	} else if (id == IPC_CMD_PCNTRL_M) { +		cbuf[offset] = data[0]; +		cbuf[offset + 1] = data[1]; +		ipc_data_writel(wbuf[0], 0); /* Write wbuff */ +		ipc_command(4 << 16 |  id << 12 | 0 << 8 | op);  	} -	err = busy_loop(); -	if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ +	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); -		if (platform != MRST_CPU_CHIP_PENWELL) { -			for (nc = 0, offset = 2; nc < count; nc++, offset += 3) -				data[nc] = ipc_data_readb(offset); -		} else { -			for (nc = 0; nc < count; nc++) -				data[nc] = ipc_data_readb(nc); -		} +		for (nc = 0; nc < count; nc++) +			data[nc] = ipc_data_readb(nc);  	}  	mutex_unlock(&ipclock);  	return err; @@ -403,7 +449,7 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);   */  int intel_scu_ipc_simple_command(int cmd, int sub)  { -	u32 err = 0; +	int err;  	mutex_lock(&ipclock);  	if (ipcdev.pdev == NULL) { @@ -411,7 +457,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;  } @@ -433,8 +479,7 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command);  int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,  							u32 *out, int outlen)  { -	u32 err = 0; -	int i = 0; +	int i, err;  	mutex_lock(&ipclock);  	if (ipcdev.pdev == NULL) { @@ -446,10 +491,12 @@ 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(); -	for (i = 0; i < outlen; i++) -		*out++ = ipc_data_readl(4 * i); +	if (!err) { +		for (i = 0; i < outlen; i++) +			*out++ = ipc_data_readl(4 * i); +	}  	mutex_unlock(&ipclock);  	return err; @@ -496,155 +543,13 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)  			"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);  		mutex_unlock(&ipclock); -		return -1; +		return -EIO;  	}  	mutex_unlock(&ipclock);  	return 0;  }  EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); -#define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */ -#define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */ -#define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */ -#define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */ -/* IPC inform SCU to get ready for update process */ -#define IPC_CMD_FW_UPDATE_READY  0x10FE -/* IPC inform SCU to go for update process */ -#define IPC_CMD_FW_UPDATE_GO     0x20FE -/* Status code for fw update */ -#define IPC_FW_UPDATE_SUCCESS	0x444f4e45 /* Status code 'DONE' */ -#define IPC_FW_UPDATE_BADN	0x4241444E /* Status code 'BADN' */ -#define IPC_FW_TXHIGH		0x54784849 /* Status code 'IPC_FW_TXHIGH' */ -#define IPC_FW_TXLOW		0x54784c4f /* Status code 'IPC_FW_TXLOW' */ - -struct fw_update_mailbox { -	u32    status; -	u32    scu_flag; -	u32    driver_flag; -}; - - -/** - *	intel_scu_ipc_fw_update	-	 Firmware update utility - *	@buffer: firmware buffer - *	@length: size of firmware buffer - * - *	This function provides an interface to load the firmware into - *	the SCU. Returns 0 on success or -1 on failure - */ -int intel_scu_ipc_fw_update(u8 *buffer, u32 length) -{ -	void __iomem *fw_update_base; -	struct fw_update_mailbox __iomem *mailbox = NULL; -	int retry_cnt = 0; -	u32 status; - -	mutex_lock(&ipclock); -	fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024)); -	if (fw_update_base == NULL) { -		mutex_unlock(&ipclock); -		return -ENOMEM; -	} -	mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR, -					sizeof(struct fw_update_mailbox)); -	if (mailbox == NULL) { -		iounmap(fw_update_base); -		mutex_unlock(&ipclock); -		return -ENOMEM; -	} - -	ipc_command(IPC_CMD_FW_UPDATE_READY); - -	/* Intitialize mailbox */ -	writel(0, &mailbox->status); -	writel(0, &mailbox->scu_flag); -	writel(0, &mailbox->driver_flag); - -	/* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/ -	memcpy_toio(fw_update_base, buffer, 0x800); - -	/* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). -	* Upon receiving this command, SCU will write the 2K MIP header -	* from 0xFFFC0000 into NAND. -	* SCU will write a status code into the Mailbox, and then set scu_flag. -	*/ - -	ipc_command(IPC_CMD_FW_UPDATE_GO); - -	/*Driver stalls until scu_flag is set */ -	while (readl(&mailbox->scu_flag) != 1) { -		rmb(); -		mdelay(1); -	} - -	/* Driver checks Mailbox status. -	 * If the status is 'BADN', then abort (bad NAND). -	 * If the status is 'IPC_FW_TXLOW', then continue. -	 */ -	while (readl(&mailbox->status) != IPC_FW_TXLOW) { -		rmb(); -		mdelay(10); -	} -	mdelay(10); - -update_retry: -	if (retry_cnt > 5) -		goto update_end; - -	if (readl(&mailbox->status) != IPC_FW_TXLOW) -		goto update_end; -	buffer = buffer + 0x800; -	memcpy_toio(fw_update_base, buffer, 0x20000); -	writel(1, &mailbox->driver_flag); -	while (readl(&mailbox->scu_flag) == 1) { -		rmb(); -		mdelay(1); -	} - -	/* check for 'BADN' */ -	if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) -		goto update_end; - -	while (readl(&mailbox->status) != IPC_FW_TXHIGH) { -		rmb(); -		mdelay(10); -	} -	mdelay(10); - -	if (readl(&mailbox->status) != IPC_FW_TXHIGH) -		goto update_end; - -	buffer = buffer + 0x20000; -	memcpy_toio(fw_update_base, buffer, 0x20000); -	writel(0, &mailbox->driver_flag); - -	while (mailbox->scu_flag == 0) { -		rmb(); -		mdelay(1); -	} - -	/* check for 'BADN' */ -	if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) -		goto update_end; - -	if (readl(&mailbox->status) == IPC_FW_TXLOW) { -		++retry_cnt; -		goto update_retry; -	} - -update_end: -	status = readl(&mailbox->status); - -	iounmap(fw_update_base); -	iounmap(mailbox); -	mutex_unlock(&ipclock); - -	if (status == IPC_FW_UPDATE_SUCCESS) -		return 0; -	return -1; -} -EXPORT_SYMBOL(intel_scu_ipc_fw_update); -  /*   * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1   * When ioc bit is set to 1, caller api must wait for interrupt handler called @@ -654,6 +559,9 @@ EXPORT_SYMBOL(intel_scu_ipc_fw_update);   */  static irqreturn_t ioc(int irq, void *dev_id)  { +	if (ipcdev.irq_mode) +		complete(&ipcdev.cmd_complete); +  	return IRQ_HANDLED;  } @@ -668,12 +576,16 @@ static irqreturn_t ioc(int irq, void *dev_id)  static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)  {  	int err; +	struct intel_scu_ipc_pdata_t *pdata;  	resource_size_t pci_resource;  	if (ipcdev.pdev)		/* We support only one SCU */  		return -EBUSY; +	pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data; +  	ipcdev.pdev = pci_dev_get(dev); +	ipcdev.irq_mode = pdata->irq_mode;  	err = pci_enable_device(dev);  	if (err) @@ -687,18 +599,23 @@ 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; -	ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); +	ipcdev.ipc_base = ioremap_nocache(pdata->ipc_base, pdata->ipc_len);  	if (!ipcdev.ipc_base)  		return -ENOMEM; -	ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); +	ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);  	if (!ipcdev.i2c_base) {  		iounmap(ipcdev.ipc_base);  		return -ENOMEM;  	} + +	intel_scu_devices_create(); +  	return 0;  } @@ -720,12 +637,25 @@ static void ipc_remove(struct pci_dev *pdev)  	iounmap(ipcdev.ipc_base);  	iounmap(ipcdev.i2c_base);  	ipcdev.pdev = NULL; +	intel_scu_devices_destroy();  } -static const struct pci_device_id pci_ids[] = { -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, -	{ 0,} +static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { +	{ +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT), +		(kernel_ulong_t)&intel_scu_ipc_lincroft_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PENWELL), +		(kernel_ulong_t)&intel_scu_ipc_penwell_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CLOVERVIEW), +		(kernel_ulong_t)&intel_scu_ipc_penwell_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_TANGIER), +		(kernel_ulong_t)&intel_scu_ipc_tangier_pdata, +	}, { +		0, +	}  };  MODULE_DEVICE_TABLE(pci, pci_ids); @@ -739,7 +669,7 @@ static struct pci_driver ipc_driver = {  static int __init intel_scu_ipc_init(void)  { -	platform = mrst_identify_cpu(); +	platform = intel_mid_identify_cpu();  	if (platform == 0)  		return -ENODEV;  	return  pci_register_driver(&ipc_driver);  | 
