// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 Linaro Ltd
* Author: Ulf Hansson <ulf.hansson@linaro.org>
*
* Copyright (C) 2014 Intel Corp, All Rights Reserved.
* Author: Yi Sun <yi.y.sun@intel.com>
*
* Copyright (C) 2020 Genesys Logic, Inc.
* Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
*
* Copyright (C) 2020 Linaro Limited
* Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
*
* Copyright (C) 2022 Genesys Logic, Inc.
* Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
*
* Copyright (C) 2023 Genesys Logic, Inc.
* Authors: Victor Shih <victor.shih@genesyslogic.com.tw>
*
* Support for SD UHS-II cards
*/
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sd_uhs2.h>
#include "card.h"
#include "core.h"
#include "bus.h"
#include "sd.h"
#include "sd_ops.h"
#include "mmc_ops.h"
#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US (1 * 1000)
#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100
static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
struct sd_uhs2_wait_active_state_data {
struct mmc_host *host;
struct mmc_command *cmd;
};
static int sd_uhs2_power_up(struct mmc_host *host)
{
if (host->ios.power_mode == MMC_POWER_ON)
return 0;
host->ios.vdd = fls(host->ocr_avail) - 1;
host->ios.clock = host->f_init;
host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
host->ios.power_mode = MMC_POWER_ON;
return host->ops->uhs2_control(host, UHS2_SET_IOS);
}
static int sd_uhs2_power_off(struct mmc_host *host)
{
int err;
if (host->ios.power_mode == MMC_POWER_OFF)
return 0;
host->ios.vdd = 0;
host->ios.clock = 0;
host->ios.power_mode = MMC_POWER_OFF;
host->uhs2_sd_tran = false;
err = host->ops->uhs2_control(host, UHS2_SET_IOS);
if (err)
return err;
/* For consistency, let's restore the initial timing. */
host->ios.timing = MMC_TIMING_LEGACY;
return 0;
}
/*
* Run the phy initialization sequence, which mainly relies on the UHS-II host
* to check that we reach the expected electrical state, between the host and
* the card.
*/
static int sd_uhs2_phy_init(struct mmc_host *host)
{
int err;
err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
if (err) {
pr_err("%s: failed to initial phy for UHS-II!\n",
mmc_hostname(host));
}
return err;
}
/*
* sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embedded in
* mmc_command structure
* @cmd: MMC command to executed
* @uhs2_cmd: UHS2 command corresponded to MMC command
* @header: Header field of UHS-II command cxpacket
* @arg: Argument field of UHS-II command packet
* @payload: Payload field of UHS-II command packet
* @plen: Payload length
* @resp: Response buffer is allocated by caller and it is used to keep
* the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
* should be null and SD-TRAN command response should be stored in
* resp of mmc_command.
* @resp_len: Response buffer length
*
* The uhs2_command structure contains message packets which are transmited/
* received on UHS-II bus. This function fills in the contents of uhs2_command
* structure and embededs UHS2 command into mmc_command structure, which is used
* in legacy SD operation functions.
*
*/
static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
struct uhs2_command *uhs2_cmd,
u8 plen, u8 resp_len