162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017 Chen-Yu Tsai. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk-provider.h>
762306a36Sopenharmony_ci#include <linux/clk/sunxi-ng.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "ccu_common.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * sunxi_ccu_set_mmc_timing_mode - Configure the MMC clock timing mode
1462306a36Sopenharmony_ci * @clk: clock to be configured
1562306a36Sopenharmony_ci * @new_mode: true for new timing mode introduced in A83T and later
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Return: %0 on success, %-ENOTSUPP if the clock does not support
1862306a36Sopenharmony_ci * switching modes.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ciint sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct clk_hw *hw = __clk_get_hw(clk);
2362306a36Sopenharmony_ci	struct ccu_common *cm = hw_to_ccu_common(hw);
2462306a36Sopenharmony_ci	unsigned long flags;
2562306a36Sopenharmony_ci	u32 val;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH))
2862306a36Sopenharmony_ci		return -ENOTSUPP;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	spin_lock_irqsave(cm->lock, flags);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	val = readl(cm->base + cm->reg);
3362306a36Sopenharmony_ci	if (new_mode)
3462306a36Sopenharmony_ci		val |= CCU_MMC_NEW_TIMING_MODE;
3562306a36Sopenharmony_ci	else
3662306a36Sopenharmony_ci		val &= ~CCU_MMC_NEW_TIMING_MODE;
3762306a36Sopenharmony_ci	writel(val, cm->base + cm->reg);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	spin_unlock_irqrestore(cm->lock, flags);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunxi_ccu_set_mmc_timing_mode);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/**
4662306a36Sopenharmony_ci * sunxi_ccu_get_mmc_timing_mode: Get the current MMC clock timing mode
4762306a36Sopenharmony_ci * @clk: clock to query
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Return: %0 if the clock is in old timing mode, > %0 if it is in
5062306a36Sopenharmony_ci * new timing mode, and %-ENOTSUPP if the clock does not support
5162306a36Sopenharmony_ci * this function.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ciint sunxi_ccu_get_mmc_timing_mode(struct clk *clk)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct clk_hw *hw = __clk_get_hw(clk);
5662306a36Sopenharmony_ci	struct ccu_common *cm = hw_to_ccu_common(hw);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH))
5962306a36Sopenharmony_ci		return -ENOTSUPP;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return !!(readl(cm->base + cm->reg) & CCU_MMC_NEW_TIMING_MODE);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunxi_ccu_get_mmc_timing_mode);
64