/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include "ddp_clkmgr.h" #include "ddp_pwm_mux.h" #include #include #include "ddp_reg.h" /* #define BYPASS_CLK_SELECT */ /* * * dummy function * */ #ifdef BYPASS_CLK_SELECT int disp_pwm_set_pwmmux(unsigned int clk_req) { return 0; } int disp_pwm_clksource_enable(int clk_req) { return 0; } int disp_pwm_clksource_disable(int clk_req) { return 0; } bool disp_pwm_mux_is_osc(void) { return false; } #else /* * * variable for get clock node fromdts * */ static void __iomem *disp_pmw_mux_base; #ifndef MUX_DISPPWM_ADDR /* disp pwm source clock select register address */ #define MUX_DISPPWM_ADDR (disp_pmw_mux_base + 0x80) #endif #ifndef OSC_ULPOSC_ADDR /* rosc control register address */ #define OSC_ULPOSC_ADDR (disp_pmw_osc_base + 0x458) #endif /* clock hard code access API */ #define DRV_Reg32(addr) INREG32(addr) #define clk_readl(addr) DRV_Reg32(addr) #define clk_writel(addr, val) mt_reg_sync_writel(val, addr) static int g_pwm_mux_clock_source = -1; /* * * disp pwm source clock select mux api * */ enum DDP_CLK_ID disp_pwm_get_clkid(unsigned int clk_req) { enum DDP_CLK_ID clkid = -1; switch (clk_req) { case 0: clkid = ULPOSC1_D8; break; case 1: clkid = ULPOSC1_D2; break; case 2: clkid = UNIVPLL2_D4; break; case 3: clkid = CLK26M; /* Bypass config:default 26M */ break; default: clkid = -1; break; } return clkid; } /* * * get disp pwm source mux node * */ #define DTSI_TOPCKGEN "mediatek,topckgen" static int disp_pwm_get_muxbase(void) { int ret = 0; struct device_node *node; if (disp_pmw_mux_base != NULL) { pr_debug("[PWM]TOPCKGEN node exist"); return 0; } node = of_find_compatible_node(NULL, NULL, DTSI_TOPCKGEN); if (!node) { pr_info("[PWM]DISP find TOPCKGEN node failed\n"); return -1; } disp_pmw_mux_base = of_iomap(node, 0); if (!disp_pmw_mux_base) { pr_info("[PWM]DISP TOPCKGEN base failed\n"); return -1; } pr_debug("[PWM]find TOPCKGEN node"); return ret; } static unsigned int disp_pwm_get_pwmmux(void) { unsigned int regsrc = 0; if (MUX_DISPPWM_ADDR != NULL) regsrc = clk_readl(MUX_DISPPWM_ADDR); else pr_info("[PWM]mux addr illegal"); return regsrc; } /* * * disp pwm source clock select mux api * */ int disp_pwm_set_pwmmux(unsigned int clk_req) { unsigned int reg_before, reg_after; int ret = 0; enum DDP_CLK_ID clkid = -1; clkid = disp_pwm_get_clkid(clk_req); ret = disp_pwm_get_muxbase(); reg_before = disp_pwm_get_pwmmux(); pr_debug("[PWM]clk_req=%d clkid=%d", clk_req, clkid); if (clkid != -1) { ddp_clk_prepare_enable(MUX_PWM); ddp_clk_set_parent(MUX_PWM, clkid); ddp_clk_disable_unprepare(MUX_PWM); } reg_after = disp_pwm_get_pwmmux(); g_pwm_mux_clock_source = (reg_after>>24) & 0x3; pr_debug("[PWM]PWM_MUX %x->%x", reg_before, reg_after); return 0; } static void __iomem *disp_pmw_osc_base; /* * * get disp pwm source osc * */ static int get_ulposc_base(void) { int ret = 0; struct device_node *node; if (disp_pmw_osc_base != NULL) { pr_debug("[PWM]SLEEP node exist"); return 0; } node = of_find_compatible_node(NULL, NULL, "mediatek,sleep"); if (!node) { pr_info("[PWM]DISP find SLEEP node failed\n"); return -1; } disp_pmw_osc_base = of_iomap(node, 0); if (!disp_pmw_osc_base) { pr_info("[PWM]DISP find SLEEP base failed\n"); return -1; } return ret; } static int get_ulposc_status(void) { unsigned int regosc; int ret = -1; if (get_ulposc_base() == -1) { pr_info("[PWM]get ULPOSC status fail"); return ret; } regosc = clk_readl(OSC_ULPOSC_ADDR); if ((regosc & 0x5) != 0x5) { pr_debug("[PWM]ULPOSC is off (%x)", regosc); ret = 0; } else { pr_debug("[PWM]ULPOSC is on (%x)", regosc); ret = 1; } return ret; } /* * * hardcode turn on/off ROSC api * */ static int ulposc_on(void) { unsigned int regosc; if (get_ulposc_base() == -1) return -1; regosc = clk_readl(OSC_ULPOSC_ADDR); /* pr_debug("[PWM]ULPOSC config : 0x%08x", regosc); */ regosc = regosc | 0x1; clk_writel(OSC_ULPOSC_ADDR, regosc); /* pr_debug("[PWM]ULPOSC config : 0x%08x after en", regosc); */ udelay(150); regosc = clk_readl(OSC_ULPOSC_ADDR); regosc = regosc | 0x4; clk_writel(OSC_ULPOSC_ADDR, regosc); /* udelay(150); */ /* regosc = clk_readl(OSC_ULPOSC_ADDR); */ /* pr_debug("[PWM]ULPOSC config : 0x%08x after rst 1", regosc); */ return 0; } static int ulposc_off(void) { unsigned int regosc; if (get_ulposc_base() == -1) return -1; regosc = clk_readl(OSC_ULPOSC_ADDR); regosc = regosc & (~0x4); clk_writel(OSC_ULPOSC_ADDR, regosc); udelay(150); regosc = clk_readl(OSC_ULPOSC_ADDR); /* pr_debug("[PWM]ULPOSC config : 0x%08x after cg_en", regosc); */ regosc = regosc & (~0x1); clk_writel(OSC_ULPOSC_ADDR, regosc); /* udelay(150); */ /* regosc = clk_readl(OSC_ULPOSC_ADDR); */ /* pr_debug("[PWM]ULPOSC config : 0x%08x after en", regosc); */ return 0; } static int ulposc_enable(enum DDP_CLK_ID clkid) { ulposc_on(); get_ulposc_status(); return 0; } static int ulposc_disable(enum DDP_CLK_ID clkid) { ulposc_off(); get_ulposc_status(); return 0; } /* * * disp pwm clock source power on /power off api * */ int disp_pwm_clksource_enable(int clk_req) { int ret = 0; enum DDP_CLK_ID clkid = -1; clkid = disp_pwm_get_clkid(clk_req); switch (clkid) { case ULPOSC1_D2: case ULPOSC1_D8: ulposc_enable(clkid); break; default: break; } return ret; } int disp_pwm_clksource_disable(int clk_req) { int ret = 0; enum DDP_CLK_ID clkid = -1; clkid = disp_pwm_get_clkid(clk_req); switch (clkid) { case ULPOSC1_D2: case ULPOSC1_D8: ulposc_disable(clkid); break; default: break; } return ret; } /* * * disp pwm clock source query api * */ bool disp_pwm_mux_is_osc(void) { bool is_osc = false; if (g_pwm_mux_clock_source == 2 || g_pwm_mux_clock_source == 3) is_osc = true; return is_osc; } #endif /* BYPASS_CLK_SELECT */