summaryrefslogtreecommitdiff
path: root/drivers/tdm
diff options
context:
space:
mode:
authorJiucheng Xu <Jiucheng.Xu@freescale.com>2014-01-16 07:15:29 (GMT)
committerJose Rivera <German.Rivera@freescale.com>2014-03-24 16:31:46 (GMT)
commit0211617adfe9944b334c549dc161ed7e96f448c2 (patch)
tree06c19241d37cbfdb64ccaac26b9465dae2c42841 /drivers/tdm
parent2944285c4e9c6e798ca2f7f84747827ba608b12a (diff)
downloadlinux-fsl-qoriq-0211617adfe9944b334c549dc161ed7e96f448c2.tar.xz
QE-TDM: Add PM support for QE-TDM
Add suspend and resume function to qe-tdm. Signed-off-by: Jiucheng Xu <Jiucheng.Xu@freescale.com> Signed-off-by: Zhao Qiang <B45475@freescale.com> Change-Id: I81cac61575196957d28071ebf3f77be848494ff7 Reviewed-on: http://git.am.freescale.net:8181/9216 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Xiaobo Xie <X.Xie@freescale.com> Reviewed-by: Jose Rivera <German.Rivera@freescale.com> Reviewed-on: http://git.am.freescale.net:8181/10111
Diffstat (limited to 'drivers/tdm')
-rw-r--r--drivers/tdm/device/fsl_ucc_tdm.c174
-rw-r--r--drivers/tdm/device/fsl_ucc_tdm.h8
2 files changed, 182 insertions, 0 deletions
diff --git a/drivers/tdm/device/fsl_ucc_tdm.c b/drivers/tdm/device/fsl_ucc_tdm.c
index 44204a3..c9a3338 100644
--- a/drivers/tdm/device/fsl_ucc_tdm.c
+++ b/drivers/tdm/device/fsl_ucc_tdm.c
@@ -999,6 +999,177 @@ static int ucc_tdm_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static void store_clk_config(struct ucc_tdm_private *utdm_priv)
+{
+ struct qe_mux *qe_mux_reg = &qe_immr->qmx;
+
+ /* store si clk */
+ utdm_priv->cmxsi1cr_h = in_be32(&qe_mux_reg->cmxsi1cr_h);
+ utdm_priv->cmxsi1cr_l = in_be32(&qe_mux_reg->cmxsi1cr_l);
+
+ /* store si sync */
+ utdm_priv->cmxsi1syr = in_be32(&qe_mux_reg->cmxsi1syr);
+
+ /* store ucc clk */
+ memcpy_fromio(utdm_priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32));
+}
+
+static void resume_clk_config(struct ucc_tdm_private *utdm_priv)
+{
+ struct qe_mux *qe_mux_reg = &qe_immr->qmx;
+
+ memcpy_toio(qe_mux_reg->cmxucr, utdm_priv->cmxucr, 4 * sizeof(u32));
+
+ out_be32(&qe_mux_reg->cmxsi1cr_h, utdm_priv->cmxsi1cr_h);
+ out_be32(&qe_mux_reg->cmxsi1cr_l, utdm_priv->cmxsi1cr_l);
+
+ out_be32(&qe_mux_reg->cmxsi1syr, utdm_priv->cmxsi1syr);
+
+}
+
+static int ucc_tdm_suspend(struct device *dev)
+{
+ struct ucc_tdm_private *utdm_priv = dev_get_drvdata(dev);
+ struct ucc_tdm_info *ut_info;
+ struct ucc_fast __iomem *uf_regs;
+
+ if (!utdm_priv)
+ return -EINVAL;
+
+ ut_info = utdm_priv->ut_info;
+ uf_regs = utdm_priv->uf_regs;
+
+ /* backup gumr guemr*/
+ utdm_priv->gumr = in_be32(&uf_regs->gumr);
+ utdm_priv->guemr = in_8(&uf_regs->guemr);
+
+ utdm_priv->ucc_pram_bak = kmalloc(sizeof(struct ucc_transparent_param),
+ GFP_KERNEL);
+ if (!utdm_priv->ucc_pram_bak)
+ return -ENOMEM;
+
+ /* backup transparent parameter */
+ memcpy_fromio(utdm_priv->ucc_pram_bak, utdm_priv->ucc_pram,
+ sizeof(struct ucc_transparent_param));
+
+ /* store the clk configuration */
+ store_clk_config(utdm_priv);
+
+ /* save power */
+ ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+ dev_dbg(dev, "ucc tdm suspend\n");
+
+ return 0;
+}
+
+static int ucc_tdm_resume(struct device *dev)
+{
+ struct ucc_tdm_private *utdm_priv = dev_get_drvdata(dev);
+ struct ucc_tdm_info *ut_info;
+ struct ucc_fast __iomem *uf_regs;
+ struct ucc_fast_private *uccf;
+ struct ucc_fast_info *uf_info;
+ int ret, i;
+ u32 cecr_subblock, bd_status;
+
+ if (!utdm_priv)
+ return -EINVAL;
+
+ ut_info = utdm_priv->ut_info;
+ uf_info = &ut_info->uf_info;
+ uf_regs = utdm_priv->uf_regs;
+ uccf = utdm_priv->uccf;
+
+ /* restore gumr guemr */
+ out_8(&uf_regs->guemr, utdm_priv->guemr);
+ out_be32(&uf_regs->gumr, utdm_priv->gumr);
+
+ /* Set Virtual Fifo registers */
+ out_be16(&uf_regs->urfs, uf_info->urfs);
+ out_be16(&uf_regs->urfet, uf_info->urfet);
+ out_be16(&uf_regs->urfset, uf_info->urfset);
+ out_be16(&uf_regs->utfs, uf_info->utfs);
+ out_be16(&uf_regs->utfet, uf_info->utfet);
+ out_be16(&uf_regs->utftt, uf_info->utftt);
+ /* utfb, urfb are offsets from MURAM base */
+ out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset);
+ out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset);
+
+ /* tdm Rx Tx and sync clock routing */
+ resume_clk_config(utdm_priv);
+
+ out_be32(&uf_regs->uccm, uf_info->uccm_mask);
+ out_be32(&uf_regs->ucce, 0xffffffff);
+
+ ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+ /* rebuild SIRAM */
+ init_si(utdm_priv);
+
+ /* Write to QE CECR, UCCx channel to Stop Transmission */
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+ ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock,
+ (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+ /* Set UPSMR normal mode */
+ out_be32(&uf_regs->upsmr, 0);
+
+ /* init parameter base */
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+ ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, utdm_priv->ucc_pram_offset);
+
+ utdm_priv->ucc_pram = (struct ucc_transparent_param __iomem *)
+ qe_muram_addr(utdm_priv->ucc_pram_offset);
+
+ /* restore ucc parameter */
+ memcpy_toio(utdm_priv->ucc_pram, utdm_priv->ucc_pram_bak,
+ sizeof(struct ucc_transparent_param));
+ kfree(utdm_priv->ucc_pram_bak);
+
+ /* rebuild BD entry */
+ for (i = 0; i < NUM_OF_BUF; i++) {
+ if (i < (NUM_OF_BUF - 1))
+ bd_status = R_E | R_I | R_CM;
+ else
+ bd_status = R_E | R_I | R_W | R_CM;
+
+ out_be32((u32 *)(utdm_priv->rx_bd + i), bd_status);
+ out_be32(&utdm_priv->rx_bd[i].buf, utdm_priv->dma_rx_addr
+ + i * MAX_RX_BUF_LENGTH);
+
+ if (i < (NUM_OF_BUF - 1))
+ bd_status = T_I;
+ else
+ bd_status = T_I | T_W;
+
+ out_be32((u32 *)(utdm_priv->tx_bd + i), bd_status);
+ out_be32(&utdm_priv->tx_bd[i].buf, utdm_priv->dma_tx_addr
+ + i * MAX_RX_BUF_LENGTH);
+ }
+
+ /* if tdm is busy enable TX and RX */
+ if (utdm_priv->tdm_busy == 1) {
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(
+ utdm_priv->ut_info->uf_info.ucc_num);
+
+ qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+ (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+ ucc_fast_enable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+ /* Enable the TDM port */
+ utdm_priv->si_regs->siglmr1_h |= (0x1 << utdm_priv->tdm_port);
+ }
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ucc_tdm_pm_ops, ucc_tdm_suspend, ucc_tdm_resume);
+#endif
+
static const struct of_device_id fsl_ucc_tdm_of_match[] = {
{
.compatible = "fsl,ucc-tdm",
@@ -1015,6 +1186,9 @@ static struct platform_driver ucc_tdm_driver = {
.owner = THIS_MODULE,
.name = DRV_NAME,
.of_match_table = fsl_ucc_tdm_of_match,
+#ifdef CONFIG_PM
+ .pm = &ucc_tdm_pm_ops,
+#endif
},
};
diff --git a/drivers/tdm/device/fsl_ucc_tdm.h b/drivers/tdm/device/fsl_ucc_tdm.h
index 627eb6c..1a1b161 100644
--- a/drivers/tdm/device/fsl_ucc_tdm.h
+++ b/drivers/tdm/device/fsl_ucc_tdm.h
@@ -153,6 +153,14 @@ struct ucc_tdm_private {
wait_queue_head_t tdm_queue;
bool tdm_queue_flag;
struct tdm_adapter adap;
+#ifdef CONFIG_PM
+ struct ucc_transparent_param *ucc_pram_bak;
+ u32 gumr;
+ u8 guemr;
+ u32 cmxsi1cr_l, cmxsi1cr_h;
+ u32 cmxsi1syr;
+ u32 cmxucr[4];
+#endif
};
#define NUM_OF_BUF 4