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