18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Atlantic Network Driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 aQuantia Corporation
58c2ecf20Sopenharmony_ci * Copyright (C) 2019-2020 Marvell International Ltd.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "aq_phy.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define HW_ATL_PTP_DISABLE_MSK	BIT(10)
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cibool aq_mdio_busy_wait(struct aq_hw_s *aq_hw)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	int err = 0;
158c2ecf20Sopenharmony_ci	u32 val;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_mdio_busy_get, aq_hw,
188c2ecf20Sopenharmony_ci					val, val == 0U, 10U, 100000U);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	if (err < 0)
218c2ecf20Sopenharmony_ci		return false;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	return true;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciu16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	u16 phy_addr = aq_hw->phy_id << 5 | mmd;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/* Set Address register. */
318c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
328c2ecf20Sopenharmony_ci				   HW_ATL_MDIO_ADDRESS_SHIFT);
338c2ecf20Sopenharmony_ci	/* Send Address command. */
348c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
358c2ecf20Sopenharmony_ci				   (3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
368c2ecf20Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
378c2ecf20Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Send Read command. */
428c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
438c2ecf20Sopenharmony_ci				   (1 << HW_ATL_MDIO_OP_MODE_SHIFT) |
448c2ecf20Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
458c2ecf20Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
468c2ecf20Sopenharmony_ci	/* Read result. */
478c2ecf20Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return (u16)hw_atl_glb_mdio_iface5_get(aq_hw);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_civoid aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	u16 phy_addr = aq_hw->phy_id << 5 | mmd;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* Set Address register. */
578c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
588c2ecf20Sopenharmony_ci				   HW_ATL_MDIO_ADDRESS_SHIFT);
598c2ecf20Sopenharmony_ci	/* Send Address command. */
608c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
618c2ecf20Sopenharmony_ci				   (3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
628c2ecf20Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
638c2ecf20Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface3_set(aq_hw, (data & HW_ATL_MDIO_WRITE_DATA_MSK) <<
688c2ecf20Sopenharmony_ci				   HW_ATL_MDIO_WRITE_DATA_SHIFT);
698c2ecf20Sopenharmony_ci	/* Send Write command. */
708c2ecf20Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
718c2ecf20Sopenharmony_ci				   (2 << HW_ATL_MDIO_OP_MODE_SHIFT) |
728c2ecf20Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
738c2ecf20Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciu16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int err = 0;
818c2ecf20Sopenharmony_ci	u32 val;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
848c2ecf20Sopenharmony_ci					val, val == 1U, 10U, 100000U);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (err < 0) {
878c2ecf20Sopenharmony_ci		err = 0xffff;
888c2ecf20Sopenharmony_ci		goto err_exit;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	err = aq_mdio_read_word(aq_hw, mmd, address);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cierr_exit:
968c2ecf20Sopenharmony_ci	return err;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_civoid aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	int err = 0;
1028c2ecf20Sopenharmony_ci	u32 val;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
1058c2ecf20Sopenharmony_ci					val, val == 1U, 10U, 100000U);
1068c2ecf20Sopenharmony_ci	if (err < 0)
1078c2ecf20Sopenharmony_ci		return;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	aq_mdio_write_word(aq_hw, mmd, address, data);
1108c2ecf20Sopenharmony_ci	hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cibool aq_phy_init_phy_id(struct aq_hw_s *aq_hw)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u16 val;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (aq_hw->phy_id = 0; aq_hw->phy_id < HW_ATL_PHY_ID_MAX;
1188c2ecf20Sopenharmony_ci	     ++aq_hw->phy_id) {
1198c2ecf20Sopenharmony_ci		/* PMA Standard Device Identifier 2: Address 1.3 */
1208c2ecf20Sopenharmony_ci		val = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if (val != 0xffff)
1238c2ecf20Sopenharmony_ci			return true;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return false;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cibool aq_phy_init(struct aq_hw_s *aq_hw)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	u32 dev_id;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (aq_hw->phy_id == HW_ATL_PHY_ID_MAX)
1348c2ecf20Sopenharmony_ci		if (!aq_phy_init_phy_id(aq_hw))
1358c2ecf20Sopenharmony_ci			return false;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* PMA Standard Device Identifier:
1388c2ecf20Sopenharmony_ci	 * Address 1.2 = MSW,
1398c2ecf20Sopenharmony_ci	 * Address 1.3 = LSW
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	dev_id = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 2);
1428c2ecf20Sopenharmony_ci	dev_id <<= 16;
1438c2ecf20Sopenharmony_ci	dev_id |= aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (dev_id == 0xffffffff) {
1468c2ecf20Sopenharmony_ci		aq_hw->phy_id = HW_ATL_PHY_ID_MAX;
1478c2ecf20Sopenharmony_ci		return false;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return true;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_civoid aq_phy_disable_ptp(struct aq_hw_s *aq_hw)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	static const u16 ptp_registers[] = {
1568c2ecf20Sopenharmony_ci		0x031e,
1578c2ecf20Sopenharmony_ci		0x031d,
1588c2ecf20Sopenharmony_ci		0x031c,
1598c2ecf20Sopenharmony_ci		0x031b,
1608c2ecf20Sopenharmony_ci	};
1618c2ecf20Sopenharmony_ci	u16 val;
1628c2ecf20Sopenharmony_ci	int i;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ptp_registers); i++) {
1658c2ecf20Sopenharmony_ci		val = aq_phy_read_reg(aq_hw, MDIO_MMD_VEND1,
1668c2ecf20Sopenharmony_ci				      ptp_registers[i]);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		aq_phy_write_reg(aq_hw, MDIO_MMD_VEND1,
1698c2ecf20Sopenharmony_ci				 ptp_registers[i],
1708c2ecf20Sopenharmony_ci				 val & ~HW_ATL_PTP_DISABLE_MSK);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci}
173