// SPDX-License-Identifier: GPL-2.0
/*
* Renesas SDHI
*
* Copyright (C) 2015-19 Renesas Electronics Corporation
* Copyright (C) 2016-19 Sang Engineering, Wolfram Sang
* Copyright (C) 2016-17 Horms Solutions, Simon Horman
* Copyright (C) 2009 Magnus Damm
*
* Based on "Compaq ASIC3 support":
*
* Copyright 2001 Compaq Computer Corporation.
* Copyright 2004-2005 Phil Blundell
* Copyright 2007-2008 OpenedHand Ltd.
*
* Authors: Phil Blundell <pb@handhelds.org>,
* Samuel Ortiz <sameo@openedhand.com>
*
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/mfd/tmio.h>
#include <linux/sh_dma.h>
#include <linux/delay.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl-state.h>
#include <linux/regulator/consumer.h>
#include <linux/sys_soc.h>
#include "renesas_sdhi.h"
#include "tmio_mmc.h"
#define HOST_MODE 0xe4
#define SDHI_VER_GEN2_SDR50 0x490c
#define SDHI_VER_RZ_A1 0x820b
/* very old datasheets said 0x490c for SDR104, too. They are wrong! */
#define SDHI_VER_GEN2_SDR104 0xcb0d
#define SDHI_VER_GEN3_SD 0xcc10
#define SDHI_VER_GEN3_SDMMC 0xcd10
static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
{
u32 val;
/*
* see also
* renesas_sdhi_of_data :: dma_buswidth
*/
switch (sd_ctrl_read16(host, CTL_VERSION)) {
case SDHI_VER_GEN2_SDR50:
val = (width == 32) ? 0x0001 : 0x0000;
break;
case SDHI_VER_GEN2_SDR104:
val = (width == 32) ? 0x0000 : 0x0001;
break;
case SDHI_VER_GEN3_SD:
case SDHI_VER_GEN3_SDMMC:
if (width == 64)
val = 0x0000;
else if (width == 32)
val = 0x0101;
else
val = 0x0001;
break;
default:
/* nothing to do */
return;
}
sd_ctrl_write16(host, HOST_MODE, val);
}
static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
{
struct mmc_host *mmc = host->mmc;
struct renesas_sdhi *priv = host_to_priv(host);
int ret = clk_prepare_enable(priv->clk);
if (ret < 0)
return ret;
ret = clk_prepare_enable(priv->clk_cd);
if (ret < 0) {
clk_disable_unprepare(priv->clk);
return ret;
}
/*
* The clock driver may not know what maximum frequency
* actually works, so it should be set with the max-frequency
* property which will already have been read to f_max. If it
* was missing, assume the current frequency is the maximum.
*/
if (!mmc->f_max)
mmc->f_max = clk_get_rate(priv->clk);
/*
* Minimum frequency is the minimum input clock frequency
* divided by our maximum divider.
*/
mmc->f_min = max(clk_round_rate(priv->clk, 1) / 512, 1L);
/* enable 16bit data access on SDBUF as default */
renesas_sdhi_sdbuf_width(host, 16);
return 0;
}
static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
unsigned int new_clock)
{
struct renesas_sdhi *priv = host_to_priv(host);
unsigned int freq, diff, best_freq = 0, diff_min = ~0;
int i;
/* tested only on R-Car Gen2+ currently; may work for others */
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
return clk_get_rate(priv->clk);
/*
* We want the bus clock to be as close as possible to, but no
* greater than, new_clock. As we can divide by 1 << i for
* any i in [0, 9] we want the input clock to be as close as
* possible, but no greater than, new_clock << i.
*/
for (i = min(9, ilog2(UINT_MAX / new_clock)); i >= 0; i--) {
freq = clk_round_rate(priv->clk, new_clock << i);
if (freq > (new_clock << i)) {
/* Too fast; look for a slightly slower option */
freq = clk_round_rate(priv->clk,
(new_clock << i) / 4 * 3);
if (freq > (new_clock << i))
continue;
}
diff = new_clock - (freq >> i);
if (diff <= diff_min) {
best_freq = freq;
diff_min = diff;
}
}
clk_set_rate(priv->clk, best_freq);
return clk_get_rate(priv->clk);
}
static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
unsigned int new_clock)
{
u32 clk = 0, clock;
sd_ctrl_write16