#define _ASM_X86_INTEL_SCU_IPC_H_
#include <linux/notifier.h>
+#include <asm/mrst.h>
/* IPC defines the following message types */
#define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */
#define IPCMSG_VRTC 0xFA /* Set vRTC device */
#define IPCMSG_FW_UPDATE 0xFE /* Firmware update */
#define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */
+#define IPCMSG_OSC_CLK 0xE6 /* Turn on/off osc clock */
#define IPC_CMD_UMIP_RD 0
#define IPC_CMD_UMIP_WR 1
/* OSNIB-OS No Init Buffer write */
int intel_scu_ipc_write_osnib(u8 *data, int len, int offset, u32 mask);
+/* Penwell has 4 osc clocks */
+#define OSC_CLK_AUDIO 0 /* Audio */
+#define OSC_CLK_CAM0 1 /* Primary camera */
+#define OSC_CLK_CAM1 2 /* Secondary camera */
+#define OSC_CLK_DISP 3 /* Display buffer */
+
+int intel_scu_ipc_osc_clk(u8 clk, unsigned int khz);
+
extern struct blocking_notifier_head intel_scu_notifier;
static inline void intel_scu_notifier_add(struct notifier_block *nb)
on ? MSIC_VPROG_ON : MSIC_VPROG_OFF);
}
-#define IPCMSG_OSC_CLK 0xE6 /* Turn on/off osc clock */
-
-/*
- * Penwell has 4 osc clocks:
- * 0: AUDIO
- * 1,2: CAMERA SENSORS
- * 3: DISP_BUF_CLK
- */
-#define OSC_CLK_CAM0 1
-#define OSC_CLK_CAM1 2
-
-/* 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
- */
-static inline int intel_scu_ipc_osc_clk(u8 clk, u8 on)
-{
- u8 ipc_wbuf[16];
- int ipc_ret;
-
- ipc_wbuf[0] = clk & 0x3;
- ipc_wbuf[1] = on & 1; /* no divider */
-
- 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;
-}
-
#endif
static int mt9e013_flisclk_ctrl(struct v4l2_subdev *sd, int flag)
{
- return intel_scu_ipc_osc_clk(OSC_CLK_CAM0, flag);
+ static const unsigned int clock_khz = 19200;
+ return intel_scu_ipc_osc_clk(OSC_CLK_CAM0, flag ? clock_khz : 0);
}
static int mt9e013_power_ctrl(struct v4l2_subdev *sd, int flag)
static int ov8830_flisclk_ctrl(struct v4l2_subdev *sd, int flag)
{
- return intel_scu_ipc_osc_clk(OSC_CLK_CAM0, flag);
+ static const unsigned int clock_khz = 19200;
+ return intel_scu_ipc_osc_clk(OSC_CLK_CAM0, flag ? clock_khz : 0);
}
static int ov8830_power_ctrl(struct v4l2_subdev *sd, int flag)
static int mt9m114_flisclk_ctrl(struct v4l2_subdev *sd, int flag)
{
- return intel_scu_ipc_osc_clk(OSC_CLK_CAM1, flag);
+ static const unsigned int clock_khz = 19200;
+ return intel_scu_ipc_osc_clk(OSC_CLK_CAM1, flag ? clock_khz : 0);
}
static int mt9e013_reset;
}
EXPORT_SYMBOL_GPL(intel_scu_ipc_write_osnib);
+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) {
+ base_freq = mrst_identify_cpu() == MRST_CPU_CHIP_CLOVERVIEW ?
+ 38400 : 19200;
+ div = base_freq / khz - 1;
+ if (div >= 3 || (div + 1) * 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;
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_osc_clk);
+
/*
* 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