18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * UFS Host driver for Synopsys Designware Core
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors: Joao Pinto <jpinto@synopsys.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "ufshcd.h"
118c2ecf20Sopenharmony_ci#include "unipro.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "ufshcd-dwc.h"
148c2ecf20Sopenharmony_ci#include "ufshci-dwc.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciint ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
178c2ecf20Sopenharmony_ci				const struct ufshcd_dme_attr_val *v, int n)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	int ret = 0;
208c2ecf20Sopenharmony_ci	int attr_node = 0;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	for (attr_node = 0; attr_node < n; attr_node++) {
238c2ecf20Sopenharmony_ci		ret = ufshcd_dme_set_attr(hba, v[attr_node].attr_sel,
248c2ecf20Sopenharmony_ci			ATTR_SET_NOR, v[attr_node].mib_val, v[attr_node].peer);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci		if (ret)
278c2ecf20Sopenharmony_ci			return ret;
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return 0;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/**
358c2ecf20Sopenharmony_ci * ufshcd_dwc_program_clk_div()
368c2ecf20Sopenharmony_ci * This function programs the clk divider value. This value is needed to
378c2ecf20Sopenharmony_ci * provide 1 microsecond tick to unipro layer.
388c2ecf20Sopenharmony_ci * @hba: Private Structure pointer
398c2ecf20Sopenharmony_ci * @divider_val: clock divider value to be programmed
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistatic void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	ufshcd_writel(hba, divider_val, DWC_UFS_REG_HCLKDIV);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/**
488c2ecf20Sopenharmony_ci * ufshcd_dwc_link_is_up()
498c2ecf20Sopenharmony_ci * Check if link is up
508c2ecf20Sopenharmony_ci * @hba: private structure pointer
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	int dme_result = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(VS_POWERSTATE), &dme_result);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (dme_result == UFSHCD_LINK_IS_UP) {
618c2ecf20Sopenharmony_ci		ufshcd_set_link_active(hba);
628c2ecf20Sopenharmony_ci		return 0;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 1;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * ufshcd_dwc_connection_setup()
708c2ecf20Sopenharmony_ci * This function configures both the local side (host) and the peer side
718c2ecf20Sopenharmony_ci * (device) unipro attributes to establish the connection to application/
728c2ecf20Sopenharmony_ci * cport.
738c2ecf20Sopenharmony_ci * This function is not required if the hardware is properly configured to
748c2ecf20Sopenharmony_ci * have this connection setup on reset. But invoking this function does no
758c2ecf20Sopenharmony_ci * harm and should be fine even working with any ufs device.
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * @hba: pointer to drivers private data
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Returns 0 on success non-zero value on failure
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic int ufshcd_dwc_connection_setup(struct ufs_hba *hba)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	static const struct ufshcd_dme_attr_val setup_attrs[] = {
848c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CONNECTIONSTATE), 0, DME_LOCAL },
858c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(N_DEVICEID), 0, DME_LOCAL },
868c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(N_DEVICEID_VALID), 0, DME_LOCAL },
878c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_PEERDEVICEID), 1, DME_LOCAL },
888c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_PEERCPORTID), 0, DME_LOCAL },
898c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_TRAFFICCLASS), 0, DME_LOCAL },
908c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CPORTFLAGS), 0x6, DME_LOCAL },
918c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CPORTMODE), 1, DME_LOCAL },
928c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CONNECTIONSTATE), 1, DME_LOCAL },
938c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CONNECTIONSTATE), 0, DME_PEER },
948c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(N_DEVICEID), 1, DME_PEER },
958c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(N_DEVICEID_VALID), 1, DME_PEER },
968c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_PEERDEVICEID), 1, DME_PEER },
978c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_PEERCPORTID), 0, DME_PEER },
988c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_TRAFFICCLASS), 0, DME_PEER },
998c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CPORTFLAGS), 0x6, DME_PEER },
1008c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CPORTMODE), 1, DME_PEER },
1018c2ecf20Sopenharmony_ci		{ UIC_ARG_MIB(T_CONNECTIONSTATE), 1, DME_PEER }
1028c2ecf20Sopenharmony_ci	};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return ufshcd_dwc_dme_set_attrs(hba, setup_attrs, ARRAY_SIZE(setup_attrs));
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * ufshcd_dwc_link_startup_notify()
1098c2ecf20Sopenharmony_ci * UFS Host DWC specific link startup sequence
1108c2ecf20Sopenharmony_ci * @hba: private structure pointer
1118c2ecf20Sopenharmony_ci * @status: Callback notify status
1128c2ecf20Sopenharmony_ci *
1138c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_ciint ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
1168c2ecf20Sopenharmony_ci					enum ufs_notify_change_status status)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int err = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (status == PRE_CHANGE) {
1218c2ecf20Sopenharmony_ci		ufshcd_dwc_program_clk_div(hba, DWC_UFS_REG_HCLKDIV_DIV_125);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (hba->vops->phy_initialization) {
1248c2ecf20Sopenharmony_ci			err = hba->vops->phy_initialization(hba);
1258c2ecf20Sopenharmony_ci			if (err) {
1268c2ecf20Sopenharmony_ci				dev_err(hba->dev, "Phy setup failed (%d)\n",
1278c2ecf20Sopenharmony_ci									err);
1288c2ecf20Sopenharmony_ci				goto out;
1298c2ecf20Sopenharmony_ci			}
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci	} else { /* POST_CHANGE */
1328c2ecf20Sopenharmony_ci		err = ufshcd_dwc_link_is_up(hba);
1338c2ecf20Sopenharmony_ci		if (err) {
1348c2ecf20Sopenharmony_ci			dev_err(hba->dev, "Link is not up\n");
1358c2ecf20Sopenharmony_ci			goto out;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		err = ufshcd_dwc_connection_setup(hba);
1398c2ecf20Sopenharmony_ci		if (err)
1408c2ecf20Sopenharmony_ci			dev_err(hba->dev, "Connection setup failed (%d)\n",
1418c2ecf20Sopenharmony_ci									err);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciout:
1458c2ecf20Sopenharmony_ci	return err;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_dwc_link_startup_notify);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
1508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core");
1518c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
152