162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Atlantic Network Driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2018-2019 aQuantia Corporation
562306a36Sopenharmony_ci * Copyright (C) 2019-2020 Marvell International Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "aq_phy.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define HW_ATL_PTP_DISABLE_MSK	BIT(10)
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cibool aq_mdio_busy_wait(struct aq_hw_s *aq_hw)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	int err = 0;
1562306a36Sopenharmony_ci	u32 val;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_mdio_busy_get, aq_hw,
1862306a36Sopenharmony_ci					val, val == 0U, 10U, 100000U);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	if (err < 0)
2162306a36Sopenharmony_ci		return false;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	return true;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciu16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	u16 phy_addr = aq_hw->phy_id << 5 | mmd;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/* Set Address register. */
3162306a36Sopenharmony_ci	hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
3262306a36Sopenharmony_ci				   HW_ATL_MDIO_ADDRESS_SHIFT);
3362306a36Sopenharmony_ci	/* Send Address command. */
3462306a36Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
3562306a36Sopenharmony_ci				   (3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
3662306a36Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
3762306a36Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Send Read command. */
4262306a36Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
4362306a36Sopenharmony_ci				   (1 << HW_ATL_MDIO_OP_MODE_SHIFT) |
4462306a36Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
4562306a36Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
4662306a36Sopenharmony_ci	/* Read result. */
4762306a36Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return (u16)hw_atl_glb_mdio_iface5_get(aq_hw);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_civoid aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	u16 phy_addr = aq_hw->phy_id << 5 | mmd;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Set Address register. */
5762306a36Sopenharmony_ci	hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
5862306a36Sopenharmony_ci				   HW_ATL_MDIO_ADDRESS_SHIFT);
5962306a36Sopenharmony_ci	/* Send Address command. */
6062306a36Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
6162306a36Sopenharmony_ci				   (3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
6262306a36Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
6362306a36Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	hw_atl_glb_mdio_iface3_set(aq_hw, (data & HW_ATL_MDIO_WRITE_DATA_MSK) <<
6862306a36Sopenharmony_ci				   HW_ATL_MDIO_WRITE_DATA_SHIFT);
6962306a36Sopenharmony_ci	/* Send Write command. */
7062306a36Sopenharmony_ci	hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
7162306a36Sopenharmony_ci				   (2 << HW_ATL_MDIO_OP_MODE_SHIFT) |
7262306a36Sopenharmony_ci				   ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
7362306a36Sopenharmony_ci				    HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	aq_mdio_busy_wait(aq_hw);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciu16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	int err = 0;
8162306a36Sopenharmony_ci	u32 val;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
8462306a36Sopenharmony_ci					val, val == 1U, 10U, 100000U);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (err < 0) {
8762306a36Sopenharmony_ci		err = 0xffff;
8862306a36Sopenharmony_ci		goto err_exit;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	err = aq_mdio_read_word(aq_hw, mmd, address);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cierr_exit:
9662306a36Sopenharmony_ci	return err;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_civoid aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int err = 0;
10262306a36Sopenharmony_ci	u32 val;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
10562306a36Sopenharmony_ci					val, val == 1U, 10U, 100000U);
10662306a36Sopenharmony_ci	if (err < 0)
10762306a36Sopenharmony_ci		return;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	aq_mdio_write_word(aq_hw, mmd, address, data);
11062306a36Sopenharmony_ci	hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cibool aq_phy_init_phy_id(struct aq_hw_s *aq_hw)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	u16 val;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for (aq_hw->phy_id = 0; aq_hw->phy_id < HW_ATL_PHY_ID_MAX;
11862306a36Sopenharmony_ci	     ++aq_hw->phy_id) {
11962306a36Sopenharmony_ci		/* PMA Standard Device Identifier 2: Address 1.3 */
12062306a36Sopenharmony_ci		val = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		if (val != 0xffff)
12362306a36Sopenharmony_ci			return true;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return false;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cibool aq_phy_init(struct aq_hw_s *aq_hw)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	u32 dev_id;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (aq_hw->phy_id == HW_ATL_PHY_ID_MAX)
13462306a36Sopenharmony_ci		if (!aq_phy_init_phy_id(aq_hw))
13562306a36Sopenharmony_ci			return false;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* PMA Standard Device Identifier:
13862306a36Sopenharmony_ci	 * Address 1.2 = MSW,
13962306a36Sopenharmony_ci	 * Address 1.3 = LSW
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	dev_id = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 2);
14262306a36Sopenharmony_ci	dev_id <<= 16;
14362306a36Sopenharmony_ci	dev_id |= aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (dev_id == 0xffffffff) {
14662306a36Sopenharmony_ci		aq_hw->phy_id = HW_ATL_PHY_ID_MAX;
14762306a36Sopenharmony_ci		return false;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return true;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_civoid aq_phy_disable_ptp(struct aq_hw_s *aq_hw)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	static const u16 ptp_registers[] = {
15662306a36Sopenharmony_ci		0x031e,
15762306a36Sopenharmony_ci		0x031d,
15862306a36Sopenharmony_ci		0x031c,
15962306a36Sopenharmony_ci		0x031b,
16062306a36Sopenharmony_ci	};
16162306a36Sopenharmony_ci	u16 val;
16262306a36Sopenharmony_ci	int i;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ptp_registers); i++) {
16562306a36Sopenharmony_ci		val = aq_phy_read_reg(aq_hw, MDIO_MMD_VEND1,
16662306a36Sopenharmony_ci				      ptp_registers[i]);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		aq_phy_write_reg(aq_hw, MDIO_MMD_VEND1,
16962306a36Sopenharmony_ci				 ptp_registers[i],
17062306a36Sopenharmony_ci				 val & ~HW_ATL_PTP_DISABLE_MSK);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci}
173