/** * notify security component of hdcp and hdmi cable status * * @hdcp HDCP status: true if phase1 is enabled * @cable HDMI connection status: true if connected * * Returns: none */ void ps_hdmi_update_security_hdmi_hdcp_status(bool hdcp, bool cable) { #define IA_SCU_CMD 0XE8 #define SCU_CHAABI_CMD 0X85 #define CHAABI_MSG_SIZE 16 uint8_t in_buf[CHAABI_MSG_SIZE]; uint32_t out_buf[CHAABI_MSG_SIZE/sizeof(uint32_t)]; pr_debug("hdcp: enter %s\n", __func__); /* init * do not care about out_buf. */ memset(in_buf, 0, CHAABI_MSG_SIZE); /* chaabi msg use byte 3 for command */ in_buf[3] = SCU_CHAABI_CMD; /* chaabi msg use bits 1:0 of byte 4 for status */ if (cable) in_buf[4] |= 1 << 0; if (hdcp) in_buf[4] |= 1 << 1; #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) /* no sub-cmd, so set "sub" argument to 0 */ intel_scu_ipc_command(IA_SCU_CMD, 0, in_buf, sizeof(in_buf), out_buf, sizeof(out_buf)/sizeof(uint32_t)); #else intel_scu_ipc_command(IA_SCU_CMD, 0, (u32 *)in_buf, sizeof(in_buf), out_buf, sizeof(out_buf)/sizeof(uint32_t)); #endif pr_debug("hdcp: leave %s\n", __func__); return; }
int intel_scu_ipc_read_osnib(u8 *data, int len, int offset) { int i, ret = 0; u32 oshob_base; u8 *ptr; void __iomem *oshob_addr; void __iomem *osnibr_addr; ret = intel_scu_ipc_command(IPCMSG_GET_HOBADDR, 0, NULL, 0, &oshob_base, 1); if (ret < 0) { pr_err("ipc_read_osnib failed!\n"); goto exit; } pr_info("OSHOB addr values is %x\n", oshob_base); oshob_addr = ioremap_nocache(oshob_base, OSHOB_SIZE); if (!oshob_addr) { pr_err("ioremap failed!\n"); ret = -ENOMEM; goto exit; } osnibr_addr = oshob_addr + OSNIB_OFFSET; ptr = data; for (i = 0; i < len; i++) { *ptr = readb(osnibr_addr + offset + i); pr_info("addr=%8x, offset=%2x, value=%2x\n", (u32)(osnibr_addr+offset+i), offset+i, *ptr); ptr++; } iounmap(oshob_addr); exit: return ret; }
static void __iomem *get_oshob_addr(void) { int ret; u32 oshob_base; u16 oshob_size; void __iomem *oshob_addr; ret = intel_scu_ipc_command(IPCMSG_GET_HOBADDR, 0, NULL, 0, &oshob_base, 1); if (ret < 0) { pr_err("ipc_read_oshob address failed!!\n"); return NULL; } oshob_size = intel_scu_ipc_get_oshob_size(); pr_debug("OSHOB addr is 0x%x size is %d\n", oshob_base, oshob_size); if (oshob_size == 0) { pr_err("size of oshob is null!!\n"); return NULL; } oshob_addr = ioremap_nocache(oshob_base, oshob_size); if (!oshob_addr) { pr_err("ioremap of oshob address failed!!\n"); return NULL; } return oshob_addr; /* Return OSHOB base address */ }
static int pmic_scu_ipc_battery_property_get(struct battery_property *prop) { u32 data[3]; u8 *p = (u8 *)&data[1]; int err = intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3); prop->capacity = data[0]; prop->crnt = *p++; prop->volt = *p++; prop->prot = *p++; prop->prot2 = *p++; prop->timer = *p++; return err; }
u32 bi_scu_version(void) { int ret = 0; u32 fw_version; ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0, &fw_version, 1); if (ret < 0) { printk(KERN_ERR "%s[%d] ret=0x%08x ", __FILE__, __LINE__, ret); return (u32)ret; } scu_version = fw_version; return scu_version; }
u32 bi_ifw_version(void) { int ret = 0; struct scu_ipc_version version; version.count = 16; ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0, (u32 *)version.data, 4); if (ret < 0) { printk(KERN_ERR "%s[%d] ret=0x%08x ", __FILE__, __LINE__, ret); return (u32)ret; } ifw_version = version.data[15]; ifw_version = (ifw_version << 8) + version.data[14]; return ifw_version; }
int intel_scu_ipc_osc_clk(u8 clk, unsigned int khz) { /* SCU IPC COMMAND(osc clk on/off) definition: * ipc_wbuf[0] = clock to act on {0, 1, 2, 3} * ipc_wbuf[1] = * bit 0 - 1:on 0:off * bit 1 - if 1, read divider setting from bits 3:2 as follows: * bit [3:2] - 00: clk/1, 01: clk/2, 10: clk/4, 11: reserved */ unsigned int base_freq; unsigned int div; u8 ipc_wbuf[16]; int ipc_ret; if (clk > 3) return -EINVAL; ipc_wbuf[0] = clk; ipc_wbuf[1] = 0; if (khz) { #ifdef CONFIG_CTP_CRYSTAL_38M4 base_freq = 38400; #else base_freq = 19200; #endif div = fls(base_freq / khz) - 1; if (div >= 3 || (1 << div) * khz != base_freq) return -EINVAL; /* Allow only exact frequencies */ ipc_wbuf[1] = 0x03 | (div << 2); } ipc_ret = intel_scu_ipc_command(IPCMSG_OSC_CLK, 0, (u32 *)ipc_wbuf, 2, NULL, 0); if (ipc_ret != 0) pr_err("%s: failed to set osc clk(%d) output\n", __func__, clk); return ipc_ret; }
/* * IPC operations */ static int watchdog_set_ipc(int soft_threshold, int threshold) { u32 *ipc_wbuf; u8 cbuf[16] = { '\0' }; int ipc_ret = 0; ipc_wbuf = (u32 *)&cbuf; ipc_wbuf[0] = soft_threshold; ipc_wbuf[1] = threshold; ipc_ret = intel_scu_ipc_command( IPC_SET_WATCHDOG_TIMER, 0, ipc_wbuf, 2, NULL, 0); if (ipc_ret != 0) pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret); return ipc_ret; };
static void __iomem *get_oshob_addr(void) { int ret; u32 oshob_base; void __iomem *oshob_addr; ret = intel_scu_ipc_command(IPCMSG_GET_HOBADDR, 0, NULL, 0, &oshob_base, 1); if (ret < 0) { pr_err("ipc_read_oshob address failed!!\n"); return NULL; } pr_debug("OSHOB addr is 0x%x\n", oshob_base); oshob_addr = ioremap_nocache(oshob_base, OSHOB_SIZE); if (!oshob_addr) { pr_err("ioremap of oshob address failed!!\n"); return NULL; } return oshob_addr; /* Return OSHOB base address */ }
static inline int wdt_command(int sub, u32 *in, int inlen) { return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0); }
static int pmic_scu_ipc_battery_cc_read(u32 *value) { return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD, NULL, 0, value, 1); }
int intel_scu_ipc_write_osnib(u8 *data, int len, int offset) { int i; int ret = 0; u32 posnibw, oshob_base; u8 osnib_data[OSNIB_SIZE]; u8 chksum = 0; void __iomem *oshob_addr, *osnibw_addr, *osnibr_addr; ret = intel_scu_ipc_command(IPCMSG_GET_HOBADDR, 0, NULL, 0, &oshob_base, 1); if (ret < 0) { pr_err("ipc_get_hobaddr failed!!\n"); goto exit; } pr_info("OSHOB addr values is %x\n", oshob_base); intel_scu_ipc_lock(); oshob_addr = ioremap_nocache(oshob_base, OSHOB_SIZE); if (!oshob_addr) { pr_err("ioremap failed!\n"); ret = -ENOMEM; goto exit; } /*Dump osnib data for generate chksum */ osnibr_addr = oshob_addr + OSNIB_OFFSET; for (i = 0; i < OSNIB_SIZE; i++) osnib_data[i] = readb(osnibr_addr + i); memcpy(osnib_data + offset, data, len); /* generate chksum */ for (i = 0; i < OSNIB_SIZE - 1; i++) chksum += osnib_data[i]; osnib_data[OSNIB_SIZE - 1] = ~chksum + 1; posnibw = readl(oshob_addr + POSNIBW_OFFSET); if (posnibw == 0) { /* workaround here for BZ 2914 */ posnibw = 0xFFFF3400; pr_err("ERR: posnibw from oshob is 0, manually set it here\n"); } pr_info("POSNIB: %x\n", posnibw); osnibw_addr = ioremap_nocache(posnibw, OSNIB_SIZE); if (!osnibw_addr) { pr_err("ioremap failed!\n"); ret = -ENOMEM; goto unmap_oshob_addr; } for (i = 0; i < OSNIB_SIZE; i++) writeb(*(osnib_data + i), (osnibw_addr + i)); ret = intel_scu_ipc_raw_cmd(IPCMSG_WRITE_OSNIB, 0, NULL, 0, NULL, 0, 0, 0xFFFFFFFF); if (ret < 0) pr_err("ipc_write_osnib failed!!\n"); iounmap(osnibw_addr); unmap_oshob_addr: iounmap(oshob_addr); exit: intel_scu_ipc_unlock(); return ret; }
/** * scu_ipc_ioctl - control ioctls for the SCU * @fp: file handle of the SCU device * @cmd: ioctl coce * @arg: pointer to user passed structure * * Support the I/O and firmware flashing interfaces of the SCU */ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; struct scu_ipc_data data; void __user *argp = (void __user *)arg; int platform; /* Only IOCTL cmd allowed to pass through without capability check */ /* is getting fw version info, all others need to check to prevent */ /* arbitrary access to all sort of bit of the hardware exposed here*/ if (cmd != INTEL_SCU_IPC_FW_REVISION_GET && !capable(CAP_SYS_RAWIO)) return -EPERM; platform = intel_mid_identify_cpu(); switch (cmd) { case INTEL_SCU_IPC_READ_FBMODE_FROM_OSNIB: { u8 fb_mode; ret = intel_scu_ipc_read_osnib_fb_mode(&fb_mode); if (ret < 0) { pr_err("read fb_mode from ipc failed!!\n"); return ret; } ret = copy_to_user(argp, &fb_mode, 1); break; } case INTEL_SCU_IPC_WRITE_FBMODE_TO_OSNIB: { u8 fb_mode; ret = copy_from_user(&fb_mode, (u8 *)arg, 1); if (ret < 0) { pr_err("copy fb_mode from user failed!!\n"); return ret; } ret = intel_scu_ipc_write_osnib_fb_mode(fb_mode); break; } case INTEL_SCU_IPC_READ_BOS_FROM_OSNIB: { u8 resync_reason; ret = intel_scu_ipc_read_osnib_bos(&resync_reason); if (ret < 0) return ret; ret = copy_to_user(argp, &resync_reason, 1); break; } case INTEL_SCU_IPC_WRITE_BOS_TO_OSNIB: { u8 data; ret = copy_from_user(&data, (u8 *)arg, 1); if (ret < 0) { pr_err("copy from user failed!!\n"); return ret; } ret = intel_scu_ipc_write_osnib_bos(data); break; } case INTEL_SCU_IPC_READ_RR_FROM_OSNIB: { u8 reboot_reason; ret = intel_scu_ipc_read_osnib_rr(&reboot_reason); if (ret < 0) return ret; ret = copy_to_user(argp, &reboot_reason, 1); break; } case INTEL_SCU_IPC_WRITE_RR_TO_OSNIB: { u8 data; ret = copy_from_user(&data, (u8 *)arg, 1); if (ret < 0) { pr_err("copy from user failed!!\n"); return ret; } ret = intel_scu_ipc_write_osnib_rr(data); break; } case INTEL_SCU_IPC_WRITE_ALARM_FLAG_TO_OSNIB: { u8 flag, data; ret = copy_from_user(&flag, (u8 *)arg, 1); if (ret < 0) { pr_err("copy from user failed!!\n"); return ret; } ret = intel_scu_ipc_read_osnib(&data, 1, OSNIB_ALARM_OFFSET); if (ret < 0) return ret; if (flag) data = data | 0x1; /* set alarm flag */ else data = data & 0xFE; /* clear alarm flag */ ret = intel_scu_ipc_write_osnib(&data, 1, OSNIB_ALARM_OFFSET); break; } case INTEL_SCU_IPC_READ_VBATTCRIT: { u32 value; pr_debug("cmd = INTEL_SCU_IPC_READ_VBATTCRIT"); ret = intel_scu_ipc_read_mip((u8 *)&value, 4, 0x318, 1); if (ret < 0) return ret; pr_debug("VBATTCRIT VALUE = %x\n", value); ret = copy_to_user(argp, &value, 4); break; } case INTEL_SCU_IPC_FW_REVISION_GET: { struct scu_ipc_version version; if (copy_from_user(&version, argp, sizeof(u32))) return -EFAULT; if (version.count > 16) return -EINVAL; ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0, (u32 *)version.data, 4); if (ret < 0) return ret; if (copy_to_user(argp + sizeof(u32), version.data, version.count)) ret = -EFAULT; break; } case INTEL_SCU_IPC_OSC_CLK_CNTL: { struct osc_clk_t osc_clk; if (copy_from_user(&osc_clk, argp, sizeof(struct osc_clk_t))) return -EFAULT; ret = intel_scu_ipc_osc_clk(osc_clk.id, osc_clk.khz); if (ret) pr_err("%s: failed to set osc clk\n", __func__); break; } case INTEL_SCU_IPC_READ_BYTE_IN_OSNIB: { struct osnib_data read_data; ret = copy_from_user(&read_data, (u8 *)arg, sizeof(struct scu_ipc_data)); if (ret < 0) { pr_err("copy from user failed!!\n"); return ret; } if ((read_data.offset-OSNIB_OFFSET) >= 32) return -EINVAL; ret = intel_scu_ipc_read_osnib_byte(read_data.offset, &read_data.data); if (ret < 0) return ret; ret = copy_to_user(argp, &read_data, sizeof(struct scu_ipc_data)); break; } case INTEL_SCU_IPC_WRITE_BYTE_IN_OSNIB: { struct osnib_data write_data; ret = copy_from_user(&write_data, (u8 *)arg, sizeof(struct scu_ipc_data)); if (ret < 0) { pr_err("copy from user failed!!\n"); return ret; } if ((write_data.offset-OSNIB_OFFSET) >= 32) return -EINVAL; ret = intel_scu_ipc_write_osnib_byte(write_data.offset, write_data.data); if (ret < 0) return ret; break; } default: if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) return -EFAULT; ret = scu_reg_access(cmd, &data); if (ret < 0) return ret; if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) return -EFAULT; return 0; } return ret; }