+++ /dev/null
-/*
- * Copyright (C) 2018 NXP
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <video/imx-dcss.h>
-#include "dcss-prv.h"
-
-
-struct dcss_pll_data {
- unsigned long vco1;
- unsigned long vco2;
- unsigned long fout;
- int refin;
- int ref_div;
- int fout_request;
- int fout_error;
- int r1;
- int f1;
- int r2;
- int f2;
- int q;
-};
-
-struct dcss_pll_priv {
- struct dcss_soc *dcss;
- void __iomem *base_reg;
- struct dcss_pll_data data;
-};
-
-static const int ref_sel[] = {
- 25000000, 27000000, 27000000, 0
-};
-
-/* These are the specification limits for the SSCG PLL */
-#define PLL_STAGE1_MIN_FREQ 1600000000
-#define PLL_STAGE1_MAX_FREQ 2400000000
-
-#define PLL_STAGE1_REF_MIN_FREQ 25000000
-#define PLL_STAGE1_REF_MAX_FREQ 54000000
-
-#define PLL_STAGE2_MIN_FREQ 1200000000
-#define PLL_STAGE2_MAX_FREQ 2400000000
-
-#define PLL_STAGE2_REF_MIN_FREQ 54000000
-#define PLL_STAGE2_REF_MAX_FREQ 75000000
-
-#define PLL_STAGE2_OUT_MIN_FREQ 20000000
-#define PLL_STAGE2_OUT_MAX_FREQ 75000000
-
-#define PLL_OUT_MAX_FREQ 600000000
-#define PLL_OUT_MIN_FREQ 12000000
-
-#define VIDEO_PLL2_CFG0 0x30360054
-#define VIDEO_PLL2_CFG1 0x30360058
-#define VIDEO_PLL2_CFG2 0x3036005c
-#define VIDEO_PLL2_CFG0_OFFSET 0x0054
-#define VIDEO_PLL2_CFG1_OFFSET 0x0058
-#define VIDEO_PLL2_CFG2_OFFSET 0x005c
-
-/* SYS PLL1/2/3 VIDEO PLL2 DRAM PLL */
-#define SSCG_PLL_LOCK_MASK BIT(31)
-#define SSCG_PLL_CLKE_MASK BIT(25)
-#define SSCG_PLL_VIDEO_PLL2_CLKE_MASK BIT(9)
-#define SSCG_PLL_PD_MASK BIT(7)
-#define SSCG_PLL_BYPASS1_MASK BIT(5)
-#define SSCG_PLL_BYPASS2_MASK BIT(4)
-#define SSCG_PLL_LOCK_SEL_MASK BIT(3)
-#define SSCG_PLL_COUNTCLK_SEL_MASK BIT(2)
-#define SSCG_PLL_REFCLK_SEL_MASK 0x3
-#define SSCG_PLL_REFCLK_SEL_OSC_25M 0
-#define SSCG_PLL_REFCLK_SEL_OSC_27M 1
-#define SSCG_PLL_REFCLK_SEL_HDMI_PHY_27M 2
-#define SSCG_PLL_REFCLK_SEL_CLK_PN 3
-
-#define SSCG_PLL_REF_DIVR1_MASK (0x7 << 25)
-#define SSCG_PLL_REF_DIVR1_SHIFT 25
-#define SSCG_PLL_REF_DIVR1_VAL(n) (((n) << 25) & SSCG_PLL_REF_DIVR1_MASK)
-#define SSCG_PLL_REF_DIVR2_MASK (0x3f << 19)
-#define SSCG_PLL_REF_DIVR2_SHIFT 19
-#define SSCG_PLL_REF_DIVR2_VAL(n) (((n) << 19) & SSCG_PLL_REF_DIVR2_MASK)
-#define SSCG_PLL_FEEDBACK_DIV_F1_MASK (0x3f << 13)
-#define SSCG_PLL_FEEDBACK_DIV_F1_SHIFT 13
-#define SSCG_PLL_FEEDBACK_DIV_F1_VAL(n) (((n) << 13) & \
- SSCG_PLL_FEEDBACK_DIV_F1_MASK)
-#define SSCG_PLL_FEEDBACK_DIV_F2_MASK (0x3f << 7)
-#define SSCG_PLL_FEEDBACK_DIV_F2_SHIFT 7
-#define SSCG_PLL_FEEDBACK_DIV_F2_VAL(n) (((n) << 7) & \
- SSCG_PLL_FEEDBACK_DIV_F2_MASK)
-#define SSCG_PLL_OUTPUT_DIV_VAL_MASK (0x3f << 1)
-#define SSCG_PLL_OUTPUT_DIV_VAL_SHIFT 1
-#define SSCG_PLL_OUTPUT_DIV_VAL(n) (((n) << 1) & \
- SSCG_PLL_OUTPUT_DIV_VAL_MASK)
-#define SSCG_PLL_FILTER_RANGE_MASK 0x1
-
-static void pll_show(struct dcss_pll_priv *pll)
-{
- dev_dbg(pll->dcss->dev,
- "vco1 %lu r %d f %d vco2 %lu (%lu) ref_div %d r %d f %d q %d out %lu",
- pll->data.vco1, pll->data.r1, pll->data.f1, pll->data.vco2,
- pll->data.vco2 / 2, pll->data.ref_div, pll->data.r2,
- pll->data.f2, pll->data.q, pll->data.fout);
-}
-
-static int pll2_check_match(struct dcss_pll_priv *pll,
- struct dcss_pll_data *temp_data)
-{
- if ((pll->data.vco2 <= PLL_STAGE2_MAX_FREQ) &&
- (pll->data.vco2 >= PLL_STAGE2_MIN_FREQ)) {
- /* found new frequency */
- if (pll->data.fout_request == pll->data.fout) {
- *temp_data = pll->data;
- dev_dbg(pll->dcss->dev,
- "found exact match - vco1 %ld r %d f %d vco2 %ld (%ld) ref_div %d r %d f %d q %d out %lu\n",
- pll->data.vco1, pll->data.r1, pll->data.f1,
- pll->data.vco2, pll->data.vco2 / 2,
- pll->data.ref_div, pll->data.r2, pll->data.f2,
- pll->data.q, pll->data.fout);
- return 0;
- } else if (abs(pll->data.fout_error) >
- abs(pll->data.fout - pll->data.fout_request)) {
- pll->data.fout_error = pll->data.fout -
- pll->data.fout_request;
- *temp_data = pll->data; /* copy pll */
- dev_dbg(pll->dcss->dev,
- "found new best match delta %d - vco1 %ld r %d f %d vco2 %ld (%ld) ref_div %d r %d f %d q %d out %lu\n",
- pll->data.fout_error, pll->data.vco1,
- pll->data.r1, pll->data.f1, pll->data.vco2,
- pll->data.vco2 / 2, pll->data.ref_div,
- pll->data.r2, pll->data.f2, pll->data.q,
- pll->data.fout);
- }
- }
- return -1;
-}
-
-static int pll2_find_match(struct dcss_pll_priv *pll,
- struct dcss_pll_data *temp_data)
-{
- const long r_max = 63;
- const long f_max = 63;
- const long q_max = 63;
-
- for (pll->data.r2 = 0; pll->data.r2 <= r_max; pll->data.r2++) {
- pll->data.ref_div = (pll->data.vco1 / (pll->data.r2 + 1));
- if ((pll->data.ref_div <= PLL_STAGE2_REF_MAX_FREQ) &&
- (pll->data.ref_div >= PLL_STAGE2_REF_MIN_FREQ)) {
- for (pll->data.f2 = 0; pll->data.f2 <= f_max;
- pll->data.f2++) {
- for (pll->data.q = 0; pll->data.q < q_max;
- pll->data.q++) {
- pll->data.vco2 =
- (pll->data.vco1 /
- (pll->data.r2 + 1)) *
- 2 * (pll->data.f2 + 1);
- pll->data.fout =
- pll->data.vco2 /
- (2 * (pll->data.q + 1));
-
- if (!pll2_check_match(pll, temp_data))
- return 0;
- }
- }
- }
- }
- return -1;
-}
-/*
- * pll1_find_match starts the iteration over all pll1 valid frequencies. For
- * every valid frequency pll2_find_match is called. Once an exact match is
- * found then match frequency is returned. If no exact match is found then the
- * closest match is returned.
- *
- */
-static int pll1_find_match(struct dcss_pll_priv *pll)
-{
- int ret;
- long ref_div;
- const long r_max = 7;
- const long f_max = 63;
- struct dcss_pll_data temp_data;
-
- temp_data.fout_error = PLL_OUT_MAX_FREQ;
- pll->data.fout_error = PLL_OUT_MAX_FREQ;
-
- for (pll->data.r1 = 0; pll->data.r1 <= r_max; pll->data.r1++) {
- ref_div = (pll->data.refin / (pll->data.r1 + 1));
- if ((ref_div <= PLL_STAGE1_REF_MAX_FREQ) &&
- (ref_div >= PLL_STAGE1_REF_MIN_FREQ)) {
- for (pll->data.f1 = 0; pll->data.f1 <= f_max;
- pll->data.f1++) {
- pll->data.vco1 =
- (pll->data.refin / (pll->data.r1 + 1)) * 2 *
- (pll->data.f1 + 1);
- if ((pll->data.vco1 <= PLL_STAGE1_MAX_FREQ) &&
- (pll->data.vco1 >= PLL_STAGE1_MIN_FREQ)) {
- /*pll_show(pll);*/
- ret = pll2_find_match(pll, &temp_data);
- /* exact match */
- if (ret == 0) {
- pll->data = temp_data;
- return 0;
- }
- }
- }
- }
- }
-
- /* no exact match */
- pll->data = temp_data;
-
- return -1;
-}
-
-/*
- * The PLL typical lifecycle is as follows:
- * dcss_pll_init(0
- * dcss_pll_set_rate() (optional default rate is 594 MHz)
- * dcss_pll_enable()
- * dcss_pll_disable()
- * dcss_pll_exit(0
- *
- * To change the PLL rate:
- * dcss_pll_disable()
- * dcss_pll_set_rate()
- * dcss_pll_enable()
- */
-
-int dcss_pll_init(struct dcss_soc *dcss, unsigned long pll_base)
-{
- struct dcss_pll_priv *pll;
- int f_actual;
-
- pll = devm_kzalloc(dcss->dev, sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return -ENOMEM;
-
- dcss->pll_priv = pll;
- pll->dcss = dcss;
-
- pll->base_reg = devm_ioremap(dcss->dev, pll_base, SZ_4K);
- if (!pll->base_reg) {
- dev_err(pll->dcss->dev,
- "pll: unable to map pll base at 0x%08lx\n",
- pll_base);
- return -ENOMEM;
- }
-
- dcss_pll_set_rate(dcss, 594000000, SSCG_PLL_REFCLK_SEL_OSC_27M,
- &f_actual);
- return 0;
-}
-
-void dcss_pll_exit(struct dcss_soc *dcss)
-{
- dcss_pll_disable(dcss);
-}
-
-int dcss_pll_set_rate(struct dcss_soc *dcss, u32 freq, u32 ref_clk,
- u32 *actual_freq)
-{
- struct dcss_pll_priv *pll = dcss->pll_priv;
- void __iomem *reg = pll->base_reg;
- u32 pll_control_reg, val_cfg2;
-
- dev_dbg(pll->dcss->dev, "initial pll reg1 %x %x %x\n",
- readl(reg + VIDEO_PLL2_CFG0_OFFSET),
- readl(reg + VIDEO_PLL2_CFG1_OFFSET),
- readl(reg + VIDEO_PLL2_CFG2_OFFSET));
-
- if (freq == 27000000) {
- /* use hdmi 27 mhz */
- pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
- pll_control_reg &= ~SSCG_PLL_REFCLK_SEL_MASK;
- pll_control_reg |= ref_clk & SSCG_PLL_REFCLK_SEL_MASK;
- writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, pll_control_reg);
- pll->data.fout = freq;
- } else {
- /* these are settings generate 1188 MHz */
- int ref_freq;
- int match;
-
- if (ref_clk > SSCG_PLL_REFCLK_SEL_HDMI_PHY_27M) {
- dev_dbg(pll->dcss->dev,
- "%s(): ref_clk index is out of range!\n",
- __func__);
- ref_freq = ref_sel[ref_clk];
- } else
- ref_freq = ref_sel[SSCG_PLL_REFCLK_SEL_HDMI_PHY_27M];
-
- pll->data.refin = ref_freq;
- pll->data.fout_request = freq;
-
- match = pll1_find_match(pll);
- dev_dbg(dcss->dev, "pll_find_match found %s match at %lu\n",
- match == 0 ? "exact" : "best", pll->data.fout);
-
- pll_show(pll);
-
- /* reference 2 requires HDMI to be initialized */
- pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
- pll_control_reg &= ~SSCG_PLL_REFCLK_SEL_MASK;
- pll_control_reg |= ref_clk & SSCG_PLL_REFCLK_SEL_MASK;
- writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, pll_control_reg);
-
- val_cfg2 = SSCG_PLL_REF_DIVR1_VAL(pll->data.r1) |
- SSCG_PLL_REF_DIVR2_VAL(pll->data.r2) |
- SSCG_PLL_FEEDBACK_DIV_F1_VAL(pll->data.f1) |
- SSCG_PLL_FEEDBACK_DIV_F2_VAL(pll->data.f2) |
- SSCG_PLL_OUTPUT_DIV_VAL(pll->data.q);
-
- writel(val_cfg2, reg + VIDEO_PLL2_CFG2_OFFSET);
- dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG2_OFFSET, val_cfg2);
-
- }
- *actual_freq = pll->data.fout;
-
- dev_info(pll->dcss->dev,
- "Configured video pll 2 with ref_clk %d freq %d (actual %d)\n",
- ref_clk, freq, *actual_freq);
-
- return 0;
-}
-
-int dcss_pll_enable(struct dcss_soc *dcss)
-{
- struct dcss_pll_priv *pll = dcss->pll_priv;
- void __iomem *base_reg = pll->base_reg;
- u32 reg;
- int lock_count = 0;
-
- reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
-
- if (pll->data.fout != 27000000) {
- /* Clear bypass */
- reg &= ~SSCG_PLL_BYPASS1_MASK;
- reg &= ~SSCG_PLL_BYPASS2_MASK;
- } else {
- /* use bypass mode for 27000000 */
- reg |= SSCG_PLL_BYPASS1_MASK;
- reg |= SSCG_PLL_BYPASS2_MASK;
- }
-
- writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, reg);
-
- /* Clear power down bit */
- reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
- reg &= ~SSCG_PLL_PD_MASK;
-
- /* Enable clk output */
- reg |= SSCG_PLL_VIDEO_PLL2_CLKE_MASK;
- writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, reg);
-
- /* Wait until lock */
- while (!(readl(base_reg + VIDEO_PLL2_CFG0_OFFSET) &
- SSCG_PLL_LOCK_MASK) && pll->data.fout != 27000000) {
- udelay(100);
- if (lock_count++ > (10 * 1000)) {
- dev_err(dcss->dev,
- "failed to lock video pll 2!\n");
- return 1;
- }
- }
-
- return 0;
-}
-
-int dcss_pll_disable(struct dcss_soc *dcss)
-{
- struct dcss_pll_priv *pll = dcss->pll_priv;
- void __iomem *base_reg = pll->base_reg;
- u32 reg;
-
- /* Disable clk output */
- reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
- reg &= ~SSCG_PLL_VIDEO_PLL2_CLKE_MASK;
- writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, reg);
-
- /* Set power down bit */
- reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
- reg |= SSCG_PLL_PD_MASK;
- writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
- dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
- VIDEO_PLL2_CFG0_OFFSET, reg);
-
- return 0;
-}