162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/clk-provider.h> 362306a36Sopenharmony_ci#include <linux/io.h> 462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "mcde_drm.h" 762306a36Sopenharmony_ci#include "mcde_display_regs.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* The MCDE internal clock dividers for FIFO A and B */ 1062306a36Sopenharmony_cistruct mcde_clk_div { 1162306a36Sopenharmony_ci struct clk_hw hw; 1262306a36Sopenharmony_ci struct mcde *mcde; 1362306a36Sopenharmony_ci u32 cr; 1462306a36Sopenharmony_ci u32 cr_div; 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int mcde_clk_div_enable(struct clk_hw *hw) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); 2062306a36Sopenharmony_ci struct mcde *mcde = cdiv->mcde; 2162306a36Sopenharmony_ci u32 val; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci spin_lock(&mcde->fifo_crx1_lock); 2462306a36Sopenharmony_ci val = readl(mcde->regs + cdiv->cr); 2562306a36Sopenharmony_ci /* 2662306a36Sopenharmony_ci * Select the PLL72 (LCD) clock as parent 2762306a36Sopenharmony_ci * FIXME: implement other parents. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci val &= ~MCDE_CRX1_CLKSEL_MASK; 3062306a36Sopenharmony_ci val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT; 3162306a36Sopenharmony_ci /* Internal clock */ 3262306a36Sopenharmony_ci val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Clear then set the divider */ 3562306a36Sopenharmony_ci val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK); 3662306a36Sopenharmony_ci val |= cdiv->cr_div; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci writel(val, mcde->regs + cdiv->cr); 3962306a36Sopenharmony_ci spin_unlock(&mcde->fifo_crx1_lock); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, 4562306a36Sopenharmony_ci unsigned long *prate, bool set_parent) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int best_div = 1, div; 4862306a36Sopenharmony_ci struct clk_hw *parent = clk_hw_get_parent(hw); 4962306a36Sopenharmony_ci unsigned long best_prate = 0; 5062306a36Sopenharmony_ci unsigned long best_diff = ~0ul; 5162306a36Sopenharmony_ci int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci for (div = 1; div < max_div; div++) { 5462306a36Sopenharmony_ci unsigned long this_prate, div_rate, diff; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (set_parent) 5762306a36Sopenharmony_ci this_prate = clk_hw_round_rate(parent, rate * div); 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci this_prate = *prate; 6062306a36Sopenharmony_ci div_rate = DIV_ROUND_UP_ULL(this_prate, div); 6162306a36Sopenharmony_ci diff = abs(rate - div_rate); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (diff < best_diff) { 6462306a36Sopenharmony_ci best_div = div; 6562306a36Sopenharmony_ci best_diff = diff; 6662306a36Sopenharmony_ci best_prate = this_prate; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci *prate = best_prate; 7162306a36Sopenharmony_ci return best_div; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, 7562306a36Sopenharmony_ci unsigned long *prate) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int div = mcde_clk_div_choose_div(hw, rate, prate, true); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return DIV_ROUND_UP_ULL(*prate, div); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw, 8362306a36Sopenharmony_ci unsigned long prate) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); 8662306a36Sopenharmony_ci struct mcde *mcde = cdiv->mcde; 8762306a36Sopenharmony_ci u32 cr; 8862306a36Sopenharmony_ci int div; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* 9162306a36Sopenharmony_ci * If the MCDE is not powered we can't access registers. 9262306a36Sopenharmony_ci * It will come up with 0 in the divider register bits, which 9362306a36Sopenharmony_ci * means "divide by 2". 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (!regulator_is_enabled(mcde->epod)) 9662306a36Sopenharmony_ci return DIV_ROUND_UP_ULL(prate, 2); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cr = readl(mcde->regs + cdiv->cr); 9962306a36Sopenharmony_ci if (cr & MCDE_CRX1_BCD) 10062306a36Sopenharmony_ci return prate; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */ 10362306a36Sopenharmony_ci div = cr & MCDE_CRX1_PCD_MASK; 10462306a36Sopenharmony_ci div += 2; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return DIV_ROUND_UP_ULL(prate, div); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, 11062306a36Sopenharmony_ci unsigned long prate) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); 11362306a36Sopenharmony_ci int div = mcde_clk_div_choose_div(hw, rate, &prate, false); 11462306a36Sopenharmony_ci u32 cr = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * We cache the CR bits to set the divide in the state so that 11862306a36Sopenharmony_ci * we can call this before we can even write to the hardware. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci if (div == 1) { 12162306a36Sopenharmony_ci /* Bypass clock divider */ 12262306a36Sopenharmony_ci cr |= MCDE_CRX1_BCD; 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci div -= 2; 12562306a36Sopenharmony_ci cr |= div & MCDE_CRX1_PCD_MASK; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci cdiv->cr_div = cr; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct clk_ops mcde_clk_div_ops = { 13362306a36Sopenharmony_ci .enable = mcde_clk_div_enable, 13462306a36Sopenharmony_ci .recalc_rate = mcde_clk_div_recalc_rate, 13562306a36Sopenharmony_ci .round_rate = mcde_clk_div_round_rate, 13662306a36Sopenharmony_ci .set_rate = mcde_clk_div_set_rate, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciint mcde_init_clock_divider(struct mcde *mcde) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct device *dev = mcde->dev; 14262306a36Sopenharmony_ci struct mcde_clk_div *fifoa; 14362306a36Sopenharmony_ci struct mcde_clk_div *fifob; 14462306a36Sopenharmony_ci const char *parent_name; 14562306a36Sopenharmony_ci struct clk_init_data fifoa_init = { 14662306a36Sopenharmony_ci .name = "fifoa", 14762306a36Sopenharmony_ci .ops = &mcde_clk_div_ops, 14862306a36Sopenharmony_ci .parent_names = &parent_name, 14962306a36Sopenharmony_ci .num_parents = 1, 15062306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 15162306a36Sopenharmony_ci }; 15262306a36Sopenharmony_ci struct clk_init_data fifob_init = { 15362306a36Sopenharmony_ci .name = "fifob", 15462306a36Sopenharmony_ci .ops = &mcde_clk_div_ops, 15562306a36Sopenharmony_ci .parent_names = &parent_name, 15662306a36Sopenharmony_ci .num_parents = 1, 15762306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 15862306a36Sopenharmony_ci }; 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci spin_lock_init(&mcde->fifo_crx1_lock); 16262306a36Sopenharmony_ci parent_name = __clk_get_name(mcde->lcd_clk); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Allocate 2 clocks */ 16562306a36Sopenharmony_ci fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!fifoa) 16762306a36Sopenharmony_ci return -ENOMEM; 16862306a36Sopenharmony_ci fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL); 16962306a36Sopenharmony_ci if (!fifob) 17062306a36Sopenharmony_ci return -ENOMEM; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci fifoa->mcde = mcde; 17362306a36Sopenharmony_ci fifoa->cr = MCDE_CRA1; 17462306a36Sopenharmony_ci fifoa->hw.init = &fifoa_init; 17562306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &fifoa->hw); 17662306a36Sopenharmony_ci if (ret) { 17762306a36Sopenharmony_ci dev_err(dev, "error registering FIFO A clock divider\n"); 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci mcde->fifoa_clk = fifoa->hw.clk; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci fifob->mcde = mcde; 18362306a36Sopenharmony_ci fifob->cr = MCDE_CRB1; 18462306a36Sopenharmony_ci fifob->hw.init = &fifob_init; 18562306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &fifob->hw); 18662306a36Sopenharmony_ci if (ret) { 18762306a36Sopenharmony_ci dev_err(dev, "error registering FIFO B clock divider\n"); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci mcde->fifob_clk = fifob->hw.clk; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 194