Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: sunxi-ng: Add interface to query or configure MMC timing modes.

Starting with the A83T SoC, Allwinner introduced a new timing mode for
its MMC clocks. The new mode changes how the MMC controller sample and
output clocks are delayed to match chip and board specifics. There are
two controls for this, one on the CCU side controlling how the clocks
behave, and one in the MMC controller controlling what inputs to take
and how to route them.

In the old mode, the MMC clock had 2 child clocks providing the output
and sample clocks, which could be delayed by a number of clock cycles
measured from the MMC clock's parent.

With the new mode, the 2 delay clocks are no longer active. Instead,
the delays and associated controls are moved into the MMC controller.
The output of the MMC clock is also halved.

The difference in how things are wired between the modes means that the
clock controls and the MMC controls must match. To achieve this in a
clear, explicit way, we introduce two functions for the MMC driver to
use: one queries the hardware for the current mode set, and the other
allows the MMC driver to request a mode.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Chen-Yu Tsai and committed by
Ulf Hansson
f6f64ed8 cd09780f

+110
+1
drivers/clk/sunxi-ng/Makefile
··· 1 1 # Common objects 2 2 lib-$(CONFIG_SUNXI_CCU) += ccu_common.o 3 + lib-$(CONFIG_SUNXI_CCU) += ccu_mmc_timing.o 3 4 lib-$(CONFIG_SUNXI_CCU) += ccu_reset.o 4 5 5 6 # Base clock types
+4
drivers/clk/sunxi-ng/ccu_common.h
··· 23 23 #define CCU_FEATURE_FIXED_POSTDIV BIT(3) 24 24 #define CCU_FEATURE_ALL_PREDIV BIT(4) 25 25 #define CCU_FEATURE_LOCK_REG BIT(5) 26 + #define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6) 27 + 28 + /* MMC timing mode switch bit */ 29 + #define CCU_MMC_NEW_TIMING_MODE BIT(30) 26 30 27 31 struct device_node; 28 32
+70
drivers/clk/sunxi-ng/ccu_mmc_timing.c
··· 1 + /* 2 + * Copyright (c) 2017 Chen-Yu Tsai. All rights reserved. 3 + * 4 + * This software is licensed under the terms of the GNU General Public 5 + * License version 2, as published by the Free Software Foundation, and 6 + * may be copied, distributed, and modified under those terms. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #include <linux/clk-provider.h> 15 + #include <linux/clk/sunxi-ng.h> 16 + 17 + #include "ccu_common.h" 18 + 19 + /** 20 + * sunxi_ccu_set_mmc_timing_mode: Configure the MMC clock timing mode 21 + * @clk: clock to be configured 22 + * @new_mode: true for new timing mode introduced in A83T and later 23 + * 24 + * Returns 0 on success, -ENOTSUPP if the clock does not support 25 + * switching modes. 26 + */ 27 + int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode) 28 + { 29 + struct clk_hw *hw = __clk_get_hw(clk); 30 + struct ccu_common *cm = hw_to_ccu_common(hw); 31 + unsigned long flags; 32 + u32 val; 33 + 34 + if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH)) 35 + return -ENOTSUPP; 36 + 37 + spin_lock_irqsave(cm->lock, flags); 38 + 39 + val = readl(cm->base + cm->reg); 40 + if (new_mode) 41 + val |= CCU_MMC_NEW_TIMING_MODE; 42 + else 43 + val &= ~CCU_MMC_NEW_TIMING_MODE; 44 + writel(val, cm->base + cm->reg); 45 + 46 + spin_unlock_irqrestore(cm->lock, flags); 47 + 48 + return 0; 49 + } 50 + EXPORT_SYMBOL_GPL(sunxi_ccu_set_mmc_timing_mode); 51 + 52 + /** 53 + * sunxi_ccu_set_mmc_timing_mode: Get the current MMC clock timing mode 54 + * @clk: clock to query 55 + * 56 + * Returns 0 if the clock is in old timing mode, > 0 if it is in 57 + * new timing mode, and -ENOTSUPP if the clock does not support 58 + * this function. 59 + */ 60 + int sunxi_ccu_get_mmc_timing_mode(struct clk *clk) 61 + { 62 + struct clk_hw *hw = __clk_get_hw(clk); 63 + struct ccu_common *cm = hw_to_ccu_common(hw); 64 + 65 + if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH)) 66 + return -ENOTSUPP; 67 + 68 + return !!(readl(cm->base + cm->reg) & CCU_MMC_NEW_TIMING_MODE); 69 + } 70 + EXPORT_SYMBOL_GPL(sunxi_ccu_get_mmc_timing_mode);
+35
include/linux/clk/sunxi-ng.h
··· 1 + /* 2 + * Copyright (c) 2017 Chen-Yu Tsai. All rights reserved. 3 + * 4 + * This software is licensed under the terms of the GNU General Public 5 + * License version 2, as published by the Free Software Foundation, and 6 + * may be copied, distributed, and modified under those terms. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #ifndef _LINUX_CLK_SUNXI_NG_H_ 15 + #define _LINUX_CLK_SUNXI_NG_H_ 16 + 17 + #include <linux/errno.h> 18 + 19 + #ifdef CONFIG_SUNXI_CCU 20 + int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode); 21 + int sunxi_ccu_get_mmc_timing_mode(struct clk *clk); 22 + #else 23 + static inline int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, 24 + bool new_mode) 25 + { 26 + return -ENOTSUPP; 27 + } 28 + 29 + static inline int sunxi_ccu_get_mmc_timing_mode(struct clk *clk) 30 + { 31 + return -ENOTSUPP; 32 + } 33 + #endif 34 + 35 + #endif