// SPDX-License-Identifier: GPL-2.0-only
/*
* UFS Host Controller driver for Exynos specific extensions
*
* Copyright (C) 2014-2015 Samsung Electronics Co., Ltd.
* Author: Seungwon Jeon <essuuj@gmail.com>
* Author: Alim Akhtar <alim.akhtar@samsung.com>
*
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include "ufshcd.h"
#include "ufshcd-pltfrm.h"
#include "ufshci.h"
#include "unipro.h"
#include "ufs-exynos.h"
/*
* Exynos's Vendor specific registers for UFSHCI
*/
#define HCI_TXPRDT_ENTRY_SIZE 0x00
#define PRDT_PREFECT_EN BIT(31)
#define PRDT_SET_SIZE(x) ((x) & 0x1F)
#define HCI_RXPRDT_ENTRY_SIZE 0x04
#define HCI_1US_TO_CNT_VAL 0x0C
#define CNT_VAL_1US_MASK 0x3FF
#define HCI_UTRL_NEXUS_TYPE 0x40
#define HCI_UTMRL_NEXUS_TYPE 0x44
#define HCI_SW_RST 0x50
#define UFS_LINK_SW_RST BIT(0)
#define UFS_UNIPRO_SW_RST BIT(1)
#define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
#define HCI_DATA_REORDER 0x60
#define HCI_UNIPRO_APB_CLK_CTRL 0x68
#define UNIPRO_APB_CLK(v, x) (((v) & ~0xF) | ((x) & 0xF))
#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
#define HCI_GPIO_OUT 0x70
#define HCI_ERR_EN_PA_LAYER 0x78
#define HCI_ERR_EN_DL_LAYER 0x7C
#define HCI_ERR_EN_N_LAYER 0x80
#define HCI_ERR_EN_T_LAYER 0x84
#define HCI_ERR_EN_DME_LAYER 0x88
#define HCI_CLKSTOP_CTRL 0xB0
#define REFCLK_STOP BIT(2)
#define UNIPRO_MCLK_STOP BIT(1)
#define UNIPRO_PCLK_STOP BIT(0)
#define CLK_STOP_MASK (REFCLK_STOP |\
UNIPRO_MCLK_STOP |\
UNIPRO_PCLK_STOP)
#define HCI_MISC 0xB4
#define REFCLK_CTRL_EN BIT(7)
#define UNIPRO_PCLK_CTRL_EN BIT(6)
#define UNIPRO_MCLK_CTRL_EN BIT(5)
#define HCI_CORECLK_CTRL_EN BIT(4)
#define CLK_CTRL_EN_MASK (REFCLK_CTRL_EN |\
UNIPRO_PCLK_CTRL_EN |\
UNIPRO_MCLK_CTRL_EN)
/* Device fatal error */
#define DFES_ERR_EN BIT(31)
#define DFES_DEF_L2_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
#define DFES_DEF_L3_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
UIC_NETWORK_BAD_DEVICEID_ENC |\
UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
#define DFES_DEF_L4_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE |\
UIC_TRANSPORT_UNKNOWN_CPORTID |\
UIC_TRANSPORT_NO_CONNECTION_RX |\
UIC_TRANSPORT_BAD_TC)
enum {
UNIPRO_L1_5 = 0,/* PHY Adapter */
UNIPRO_L2, /* Data Link */
UNIPRO_L3, /* Network */
UNIPRO_L4, /* Transport */
UNIPRO_DME, /* DME */
};
/*
* UNIPRO registers
*/
#define UNIPRO_COMP_VERSION 0x000
#define UNIPRO_DME_PWR_REQ 0x090
#define UNIPRO_DME_PWR_REQ_POWERMODE 0x094
#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER0 0x098
#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER1 0x09C
#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER2 0x0A0
#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER0 0x0A4
#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER1 0x0A8
#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER2 0x0AC
/*
* UFS Protector registers
*/
#define UFSPRSECURITY 0x010
#define NSSMU BIT(14)
#define UFSPSBEGIN0 0x200
#define UFSPSEND0 0x204
#define UFSPSLUN0 0x208
#define UFSPSCTRL0 0x20C
#define CNTR_DIV_VAL 40
static void exynos_ufs_auto_ctrl_hcc(struct exynos_ufs *ufs, bool en);
static void exynos_ufs_ctrl_clkstop(struct exynos_ufs *ufs, bool en);
static inline void exynos_ufs_enable_auto_ctrl_hcc(struct exynos_ufs *ufs)
{
exynos_ufs_auto_ctrl_hcc(ufs, true);
}
static inline void exynos_ufs_disable_auto_ctrl_hcc(struct exynos_ufs *ufs)
{
exynos_ufs_auto_ctrl_hcc(ufs, false);
}
static inline void exynos_ufs_disable_auto_ctrl_hcc_save(
struct exynos_ufs *ufs, u32 *val)
{
*val = hci_readl(ufs, HCI_MISC);
exynos_ufs_auto_ctrl_hcc(ufs, false);
}
static inline void exynos_ufs_auto_ctrl_hcc_restore(
struct exynos_ufs *ufs, u32 *val)
{
hci_writel(ufs, *val, HCI_MISC);
}
static inline void exynos_ufs_gate_clks(struct