summaryrefslogtreecommitdiff
path: root/drivers/tdm/device/fsl_ucc_tdm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tdm/device/fsl_ucc_tdm.c')
-rw-r--r--drivers/tdm/device/fsl_ucc_tdm.c174
1 files changed, 174 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
},
};