summaryrefslogtreecommitdiff
path: root/sound/soc/sof/intel
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/intel')
-rw-r--r--sound/soc/sof/intel/bdw.c26
-rw-r--r--sound/soc/sof/intel/byt.c25
-rw-r--r--sound/soc/sof/intel/cnl.c4
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c102
-rw-r--r--sound/soc/sof/intel/hda-ipc.c17
-rw-r--r--sound/soc/sof/intel/hda.c129
6 files changed, 175 insertions, 128 deletions
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 065cb868bdfa..70d524ef9bc0 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -220,17 +220,20 @@ static void bdw_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_panic_info *panic_info,
u32 *stack, size_t stack_words)
{
- /* first read regsisters */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops));
+ u32 offset = sdev->dsp_oops_offset;
+
+ /* first read registers */
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
/* then get panic info */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops),
- panic_info, sizeof(*panic_info));
+ offset += xoops->arch_hdr.totalsize;
+ sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
/* then get the stack */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) +
- sizeof(*panic_info), stack,
- stack_words * sizeof(u32));
+ offset += sizeof(*panic_info);
+ sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
}
static void bdw_dump(struct snd_sof_dev *sdev, u32 flags)
@@ -283,6 +286,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
SHIM_IMRX, SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
+ spin_lock_irq(&sdev->ipc_lock);
+
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -294,6 +299,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
snd_sof_ipc_reply(sdev, ipcx);
bdw_dsp_done(sdev);
+
+ spin_unlock_irq(&sdev->ipc_lock);
}
ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD);
@@ -485,7 +492,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
- unsigned long flags;
int ret = 0;
/*
@@ -501,8 +507,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
- spin_lock_irqsave(&sdev->ipc_lock, flags);
-
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
@@ -521,8 +525,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
}
msg->reply_error = ret;
-
- spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void bdw_host_done(struct snd_sof_dev *sdev)
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 7bf9143d3106..39d1ae01c45d 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -265,17 +265,20 @@ static void byt_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_panic_info *panic_info,
u32 *stack, size_t stack_words)
{
+ u32 offset = sdev->dsp_oops_offset;
+
/* first read regsisters */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops));
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
/* then get panic info */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops),
- panic_info, sizeof(*panic_info));
+ offset += xoops->arch_hdr.totalsize;
+ sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
/* then get the stack */
- sof_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) +
- sizeof(*panic_info), stack,
- stack_words * sizeof(u32));
+ offset += sizeof(*panic_info);
+ sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
}
static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
@@ -329,6 +332,9 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
SHIM_IMRX,
SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
+
+ spin_lock_irq(&sdev->ipc_lock);
+
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -340,6 +346,8 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
snd_sof_ipc_reply(sdev, ipcx);
byt_dsp_done(sdev);
+
+ spin_unlock_irq(&sdev->ipc_lock);
}
/* new message from DSP */
@@ -383,7 +391,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
- unsigned long flags;
int ret = 0;
/*
@@ -399,8 +406,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
- spin_lock_irqsave(&sdev->ipc_lock, flags);
-
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
@@ -419,8 +424,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
}
msg->reply_error = ret;
-
- spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void byt_host_done(struct snd_sof_dev *sdev)
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 08a1a3d3c08d..b2eba7adcad8 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -64,6 +64,8 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
CNL_DSP_REG_HIPCCTL,
CNL_DSP_REG_HIPCCTL_DONE, 0);
+ spin_lock_irq(&sdev->ipc_lock);
+
/* handle immediate reply from DSP core */
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
@@ -75,6 +77,8 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
cnl_ipc_dsp_done(sdev);
+ spin_unlock_irq(&sdev->ipc_lock);
+
ret = IRQ_HANDLED;
}
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 2c3645736e1f..07bc123112c9 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -161,21 +161,105 @@ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
return 0;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-/*
- * While performing reset, controller may not come back properly and causing
- * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset
- * (init chip) and then again set CGCTL.MISCBDCGE to 1
- */
int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
{
struct hdac_bus *bus = sof_to_bus(sdev);
- int ret;
+ struct hdac_stream *stream;
+ int sd_offset, ret = 0;
+
+ if (bus->chip_init)
+ return 0;
hda_dsp_ctrl_misc_clock_gating(sdev, false);
- ret = snd_hdac_bus_init_chip(bus, full_reset);
+
+ if (full_reset) {
+ /* clear WAKESTS */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
+ SOF_HDA_WAKESTS_INT_MASK,
+ SOF_HDA_WAKESTS_INT_MASK);
+
+ /* reset HDA controller */
+ ret = hda_dsp_ctrl_link_reset(sdev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to reset HDA controller\n");
+ return ret;
+ }
+
+ usleep_range(500, 1000);
+
+ /* exit HDA controller reset */
+ ret = hda_dsp_ctrl_link_reset(sdev, false);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
+ return ret;
+ }
+
+ usleep_range(1000, 1200);
+ }
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* check to see if controller is ready */
+ if (!snd_hdac_chip_readb(bus, GCTL)) {
+ dev_dbg(bus->dev, "controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+
+ /* detect codecs */
+ if (!bus->codec_mask) {
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ }
+#endif
+
+ /* clear stream status */
+ list_for_each_entry(stream, &bus->stream_list, list) {
+ sd_offset = SOF_STREAM_SD_OFFSET(stream);
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ sd_offset +
+ SOF_HDA_ADSP_REG_CL_SD_STS,
+ SOF_HDA_CL_DMA_SD_INT_MASK,
+ SOF_HDA_CL_DMA_SD_INT_MASK);
+ }
+
+ /* clear WAKESTS */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
+ SOF_HDA_WAKESTS_INT_MASK,
+ SOF_HDA_WAKESTS_INT_MASK);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+#endif
+
+ /* clear interrupt status register */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
+ SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* initialize the codec command I/O */
+ snd_hdac_bus_init_cmd_io(bus);
+#endif
+
+ /* enable CIE and GIE interrupts */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
+ SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
+ SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* program the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
+ snd_hdac_chip_writel(bus, DPUBASE,
+ upper_32_bits(bus->posbuf.addr));
+ }
+#endif
+
+ bus->chip_init = true;
+
hda_dsp_ctrl_misc_clock_gating(sdev, true);
return ret;
}
-#endif
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 73ead7070cde..51b285103394 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -72,7 +72,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
- unsigned long flags;
int ret = 0;
/*
@@ -84,7 +83,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
- spin_lock_irqsave(&sdev->ipc_lock, flags);
hdr = msg->msg_data;
if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) {
@@ -123,7 +121,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
out:
msg->reply_error = ret;
- spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static bool hda_dsp_ipc_is_sof(uint32_t msg)
@@ -172,6 +169,18 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
HDA_DSP_REG_HIPCCTL,
HDA_DSP_REG_HIPCCTL_DONE, 0);
+ /*
+ * Make sure the interrupt thread cannot be preempted between
+ * waking up the sender and re-enabling the interrupt. Also
+ * protect against a theoretical race with sof_ipc_tx_message():
+ * if the DSP is fast enough to receive an IPC message, reply to
+ * it, and the host interrupt processing calls this function on
+ * a different core from the one, where the sending is taking
+ * place, the message might not yet be marked as expecting a
+ * reply.
+ */
+ spin_lock_irq(&sdev->ipc_lock);
+
/* handle immediate reply from DSP core - ignore ROM messages */
if (hda_dsp_ipc_is_sof(msg)) {
hda_dsp_ipc_get_reply(sdev);
@@ -187,6 +196,8 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
/* set the done bit */
hda_dsp_ipc_dsp_done(sdev);
+ spin_unlock_irq(&sdev->ipc_lock);
+
ret = IRQ_HANDLED;
}
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 7e3980a2f7ba..faf1a8ada091 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -108,17 +108,21 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_panic_info *panic_info,
u32 *stack, size_t stack_words)
{
+ u32 offset = sdev->dsp_oops_offset;
+
/* first read registers */
- sof_block_read(sdev, sdev->mmio_bar, sdev->dsp_oops_offset, xoops,
- sizeof(*xoops));
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
/* then get panic info */
- sof_block_read(sdev, sdev->mmio_bar, sdev->dsp_oops_offset +
- sizeof(*xoops), panic_info, sizeof(*panic_info));
+ offset += xoops->arch_hdr.totalsize;
+ sof_block_read(sdev, sdev->mmio_bar, offset,
+ panic_info, sizeof(*panic_info));
/* then get the stack */
- sof_block_read(sdev, sdev->mmio_bar, sdev->dsp_oops_offset +
- sizeof(*xoops) + sizeof(*panic_info), stack,
+ offset += sizeof(*panic_info);
+ sof_block_read(sdev, sdev->mmio_bar, offset, stack,
stack_words * sizeof(u32));
}
@@ -223,7 +227,9 @@ static int hda_init(struct snd_sof_dev *sdev)
/* initialise hdac bus */
bus->addr = pci_resource_start(pci, 0);
+#if IS_ENABLED(CONFIG_PCI)
bus->remap_addr = pci_ioremap_bar(pci, 0);
+#endif
if (!bus->remap_addr) {
dev_err(bus->dev, "error: ioremap error\n");
return -ENXIO;
@@ -264,9 +270,12 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
return tplg_filename;
}
+#endif
+
static int hda_init_caps(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_ext_link *hlink;
struct snd_soc_acpi_mach_params *mach_params;
struct snd_soc_acpi_mach *hda_mach;
@@ -274,8 +283,9 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
struct snd_soc_acpi_mach *mach;
const char *tplg_filename;
int codec_num = 0;
- int ret = 0;
int i;
+#endif
+ int ret = 0;
device_disable_async_suspend(bus->dev);
@@ -283,6 +293,14 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (bus->ppcap)
dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
+ ret = hda_dsp_ctrl_init_chip(sdev, true);
+ if (ret < 0) {
+ dev_err(bus->dev, "error: init chip failed with ret: %d\n",
+ ret);
+ return ret;
+ }
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(bus);
@@ -293,12 +311,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return ret;
}
- ret = hda_dsp_ctrl_init_chip(sdev, true);
- if (ret < 0) {
- dev_err(bus->dev, "error: init chip failed with ret: %d\n", ret);
- goto out;
- }
-
/* codec detection */
if (!bus->codec_mask) {
dev_info(bus->dev, "no hda codecs found!\n");
@@ -339,8 +351,10 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
/* use local variable for readability */
tplg_filename = pdata->tplg_filename;
tplg_filename = fixup_tplg_name(sdev, tplg_filename);
- if (!tplg_filename)
- goto out;
+ if (!tplg_filename) {
+ hda_codec_i915_exit(sdev);
+ return ret;
+ }
pdata->tplg_filename = tplg_filename;
}
}
@@ -364,35 +378,10 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
*/
list_for_each_entry(hlink, &bus->hlink_list, list)
snd_hdac_ext_bus_link_put(bus, hlink);
-
- return 0;
-
-out:
- hda_codec_i915_exit(sdev);
- return ret;
-}
-
-#else
-
-static int hda_init_caps(struct snd_sof_dev *sdev)
-{
- /*
- * set CGCTL.MISCBDCGE to 0 during reset and set back to 1
- * when reset finished.
- * TODO: maybe no need for init_caps?
- */
- hda_dsp_ctrl_misc_clock_gating(sdev, 0);
-
- /* clear WAKESTS */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
- SOF_HDA_WAKESTS_INT_MASK,
- SOF_HDA_WAKESTS_INT_MASK);
-
+#endif
return 0;
}
-#endif
-
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
{
@@ -409,9 +398,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *hdev;
struct hdac_bus *bus;
- struct hdac_stream *stream;
const struct sof_intel_dsp_desc *chip;
- int sd_offset, ret = 0;
+ int ret = 0;
/*
* detect DSP by checking class/subclass/prog-id information
@@ -468,7 +456,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
goto hdac_bus_unmap;
/* DSP base */
+#if IS_ENABLED(CONFIG_PCI)
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
+#endif
if (!sdev->bar[HDA_DSP_BAR]) {
dev_err(sdev->dev, "error: ioremap error\n");
ret = -ENXIO;
@@ -558,56 +548,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto free_ipc_irq;
- /* reset HDA controller */
- ret = hda_dsp_ctrl_link_reset(sdev, true);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to reset HDA controller\n");
- goto free_ipc_irq;
- }
-
- /* exit HDA controller reset */
- ret = hda_dsp_ctrl_link_reset(sdev, false);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
- goto free_ipc_irq;
- }
-
- /* clear stream status */
- list_for_each_entry(stream, &bus->stream_list, list) {
- sd_offset = SOF_STREAM_SD_OFFSET(stream);
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_STS,
- SOF_HDA_CL_DMA_SD_INT_MASK,
- SOF_HDA_CL_DMA_SD_INT_MASK);
- }
-
- /* clear WAKESTS */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
- SOF_HDA_WAKESTS_INT_MASK,
- SOF_HDA_WAKESTS_INT_MASK);
-
- /* clear interrupt status register */
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
- SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
-
- /* enable CIE and GIE interrupts */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
- SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
- SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN);
-
- /* re-enable CGCTL.MISCBDCGE after reset */
- hda_dsp_ctrl_misc_clock_gating(sdev, true);
-
- device_disable_async_suspend(&pci->dev);
-
- /* enable DSP features */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_GPROCEN, SOF_HDA_PPCTL_GPROCEN);
-
- /* enable DSP IRQ */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_PIE, SOF_HDA_PPCTL_PIE);
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
/* initialize waitq for code loading */
init_waitqueue_head(&sdev->waitq);