105d2eaacSChen-Yu Tsai /* 205d2eaacSChen-Yu Tsai * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org> 305d2eaacSChen-Yu Tsai * 405d2eaacSChen-Yu Tsai * This program is free software; you can redistribute it and/or 505d2eaacSChen-Yu Tsai * modify it under the terms of the GNU General Public License as 605d2eaacSChen-Yu Tsai * published by the Free Software Foundation; either version 2 of 705d2eaacSChen-Yu Tsai * the License, or (at your option) any later version. 805d2eaacSChen-Yu Tsai */ 905d2eaacSChen-Yu Tsai 1005d2eaacSChen-Yu Tsai #include <linux/clk-provider.h> 1105d2eaacSChen-Yu Tsai #include <linux/spinlock.h> 1205d2eaacSChen-Yu Tsai 1305d2eaacSChen-Yu Tsai #include "ccu_sdm.h" 1405d2eaacSChen-Yu Tsai 1505d2eaacSChen-Yu Tsai bool ccu_sdm_helper_is_enabled(struct ccu_common *common, 1605d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm) 1705d2eaacSChen-Yu Tsai { 1805d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 1905d2eaacSChen-Yu Tsai return false; 2005d2eaacSChen-Yu Tsai 2105d2eaacSChen-Yu Tsai if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable)) 2205d2eaacSChen-Yu Tsai return false; 2305d2eaacSChen-Yu Tsai 2405d2eaacSChen-Yu Tsai return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable); 2505d2eaacSChen-Yu Tsai } 2605d2eaacSChen-Yu Tsai 2705d2eaacSChen-Yu Tsai void ccu_sdm_helper_enable(struct ccu_common *common, 2805d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm, 2905d2eaacSChen-Yu Tsai unsigned long rate) 3005d2eaacSChen-Yu Tsai { 3105d2eaacSChen-Yu Tsai unsigned long flags; 3205d2eaacSChen-Yu Tsai unsigned int i; 3305d2eaacSChen-Yu Tsai u32 reg; 3405d2eaacSChen-Yu Tsai 3505d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 3605d2eaacSChen-Yu Tsai return; 3705d2eaacSChen-Yu Tsai 3805d2eaacSChen-Yu Tsai /* Set the pattern */ 3905d2eaacSChen-Yu Tsai for (i = 0; i < sdm->table_size; i++) 4005d2eaacSChen-Yu Tsai if (sdm->table[i].rate == rate) 4105d2eaacSChen-Yu Tsai writel(sdm->table[i].pattern, 4205d2eaacSChen-Yu Tsai common->base + sdm->tuning_reg); 4305d2eaacSChen-Yu Tsai 4405d2eaacSChen-Yu Tsai /* Make sure SDM is enabled */ 4505d2eaacSChen-Yu Tsai spin_lock_irqsave(common->lock, flags); 4605d2eaacSChen-Yu Tsai reg = readl(common->base + sdm->tuning_reg); 4705d2eaacSChen-Yu Tsai writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg); 4805d2eaacSChen-Yu Tsai spin_unlock_irqrestore(common->lock, flags); 4905d2eaacSChen-Yu Tsai 5005d2eaacSChen-Yu Tsai spin_lock_irqsave(common->lock, flags); 5105d2eaacSChen-Yu Tsai reg = readl(common->base + common->reg); 5205d2eaacSChen-Yu Tsai writel(reg | sdm->enable, common->base + common->reg); 5305d2eaacSChen-Yu Tsai spin_unlock_irqrestore(common->lock, flags); 5405d2eaacSChen-Yu Tsai } 5505d2eaacSChen-Yu Tsai 5605d2eaacSChen-Yu Tsai void ccu_sdm_helper_disable(struct ccu_common *common, 5705d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm) 5805d2eaacSChen-Yu Tsai { 5905d2eaacSChen-Yu Tsai unsigned long flags; 6005d2eaacSChen-Yu Tsai u32 reg; 6105d2eaacSChen-Yu Tsai 6205d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 6305d2eaacSChen-Yu Tsai return; 6405d2eaacSChen-Yu Tsai 6505d2eaacSChen-Yu Tsai spin_lock_irqsave(common->lock, flags); 6605d2eaacSChen-Yu Tsai reg = readl(common->base + common->reg); 6705d2eaacSChen-Yu Tsai writel(reg & ~sdm->enable, common->base + common->reg); 6805d2eaacSChen-Yu Tsai spin_unlock_irqrestore(common->lock, flags); 6905d2eaacSChen-Yu Tsai 7005d2eaacSChen-Yu Tsai spin_lock_irqsave(common->lock, flags); 7105d2eaacSChen-Yu Tsai reg = readl(common->base + sdm->tuning_reg); 7205d2eaacSChen-Yu Tsai writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg); 7305d2eaacSChen-Yu Tsai spin_unlock_irqrestore(common->lock, flags); 7405d2eaacSChen-Yu Tsai } 7505d2eaacSChen-Yu Tsai 7605d2eaacSChen-Yu Tsai /* 7705d2eaacSChen-Yu Tsai * Sigma delta modulation provides a way to do fractional-N frequency 7805d2eaacSChen-Yu Tsai * synthesis, in essence allowing the PLL to output any frequency 7905d2eaacSChen-Yu Tsai * within its operational range. On earlier SoCs such as the A10/A20, 8005d2eaacSChen-Yu Tsai * some PLLs support this. On later SoCs, all PLLs support this. 8105d2eaacSChen-Yu Tsai * 8205d2eaacSChen-Yu Tsai * The datasheets do not explain what the "wave top" and "wave bottom" 8305d2eaacSChen-Yu Tsai * parameters mean or do, nor how to calculate the effective output 8405d2eaacSChen-Yu Tsai * frequency. The only examples (and real world usage) are for the audio 8505d2eaacSChen-Yu Tsai * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio 8605d2eaacSChen-Yu Tsai * peripherals. The author lacks the underlying domain knowledge to 8705d2eaacSChen-Yu Tsai * pursue this. 8805d2eaacSChen-Yu Tsai * 8905d2eaacSChen-Yu Tsai * The goal and function of the following code is to support the two 9005d2eaacSChen-Yu Tsai * clock rates used by the audio subsystem, allowing for proper audio 9105d2eaacSChen-Yu Tsai * playback and capture without any pitch or speed changes. 9205d2eaacSChen-Yu Tsai */ 9305d2eaacSChen-Yu Tsai bool ccu_sdm_helper_has_rate(struct ccu_common *common, 9405d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm, 9505d2eaacSChen-Yu Tsai unsigned long rate) 9605d2eaacSChen-Yu Tsai { 9705d2eaacSChen-Yu Tsai unsigned int i; 9805d2eaacSChen-Yu Tsai 9905d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 10005d2eaacSChen-Yu Tsai return false; 10105d2eaacSChen-Yu Tsai 10205d2eaacSChen-Yu Tsai for (i = 0; i < sdm->table_size; i++) 10305d2eaacSChen-Yu Tsai if (sdm->table[i].rate == rate) 10405d2eaacSChen-Yu Tsai return true; 10505d2eaacSChen-Yu Tsai 10605d2eaacSChen-Yu Tsai return false; 10705d2eaacSChen-Yu Tsai } 10805d2eaacSChen-Yu Tsai 10905d2eaacSChen-Yu Tsai unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, 11005d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm, 11105d2eaacSChen-Yu Tsai u32 m, u32 n) 11205d2eaacSChen-Yu Tsai { 11305d2eaacSChen-Yu Tsai unsigned int i; 11405d2eaacSChen-Yu Tsai u32 reg; 11505d2eaacSChen-Yu Tsai 11605d2eaacSChen-Yu Tsai pr_debug("%s: Read sigma-delta modulation setting\n", 11705d2eaacSChen-Yu Tsai clk_hw_get_name(&common->hw)); 11805d2eaacSChen-Yu Tsai 11905d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 12005d2eaacSChen-Yu Tsai return 0; 12105d2eaacSChen-Yu Tsai 12205d2eaacSChen-Yu Tsai pr_debug("%s: clock is sigma-delta modulated\n", 12305d2eaacSChen-Yu Tsai clk_hw_get_name(&common->hw)); 12405d2eaacSChen-Yu Tsai 12505d2eaacSChen-Yu Tsai reg = readl(common->base + sdm->tuning_reg); 12605d2eaacSChen-Yu Tsai 12705d2eaacSChen-Yu Tsai pr_debug("%s: pattern reg is 0x%x", 12805d2eaacSChen-Yu Tsai clk_hw_get_name(&common->hw), reg); 12905d2eaacSChen-Yu Tsai 13005d2eaacSChen-Yu Tsai for (i = 0; i < sdm->table_size; i++) 13105d2eaacSChen-Yu Tsai if (sdm->table[i].pattern == reg && 13205d2eaacSChen-Yu Tsai sdm->table[i].m == m && sdm->table[i].n == n) 13305d2eaacSChen-Yu Tsai return sdm->table[i].rate; 13405d2eaacSChen-Yu Tsai 13505d2eaacSChen-Yu Tsai /* We can't calculate the effective clock rate, so just fail. */ 13605d2eaacSChen-Yu Tsai return 0; 13705d2eaacSChen-Yu Tsai } 13805d2eaacSChen-Yu Tsai 13905d2eaacSChen-Yu Tsai int ccu_sdm_helper_get_factors(struct ccu_common *common, 14005d2eaacSChen-Yu Tsai struct ccu_sdm_internal *sdm, 14105d2eaacSChen-Yu Tsai unsigned long rate, 14205d2eaacSChen-Yu Tsai unsigned long *m, unsigned long *n) 14305d2eaacSChen-Yu Tsai { 14405d2eaacSChen-Yu Tsai unsigned int i; 14505d2eaacSChen-Yu Tsai 14605d2eaacSChen-Yu Tsai if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 14705d2eaacSChen-Yu Tsai return -EINVAL; 14805d2eaacSChen-Yu Tsai 14905d2eaacSChen-Yu Tsai for (i = 0; i < sdm->table_size; i++) 15005d2eaacSChen-Yu Tsai if (sdm->table[i].rate == rate) { 15105d2eaacSChen-Yu Tsai *m = sdm->table[i].m; 15205d2eaacSChen-Yu Tsai *n = sdm->table[i].n; 15305d2eaacSChen-Yu Tsai return 0; 15405d2eaacSChen-Yu Tsai } 15505d2eaacSChen-Yu Tsai 15605d2eaacSChen-Yu Tsai /* nothing found */ 15705d2eaacSChen-Yu Tsai return -EINVAL; 15805d2eaacSChen-Yu Tsai } 159