#include "ice.h"
#include "ice_cpi.h"
#include "ice_txclk.h"
#define ICE_PHY0 0
#define ICE_PHY1 1
struct dpll_pin *
ice_txclk_get_pin(struct ice_pf *pf, enum ice_e825c_ref_clk ref_clk)
{
switch (ref_clk) {
case ICE_REF_CLK_SYNCE:
return pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].pin;
case ICE_REF_CLK_EREF0:
return pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin;
case ICE_REF_CLK_ENET:
default:
return NULL;
}
}
static int ice_txclk_enable_peer(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
bool peer_clk_in_use;
u8 port_num, phy;
int err;
if (clk == ICE_REF_CLK_ENET)
return 0;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't enable tx-clk on peer: no controlling PF\n");
return -EINVAL;
}
port_num = pf->ptp.port.port_num;
phy = port_num / pf->hw.ptp.ports_per_phy;
peer_clk_in_use = true;
mutex_lock(&ctrl_pf->dplls.lock);
if (clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0)
peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf,
ICE_PHY1,
clk);
else if (clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1)
peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf,
ICE_PHY0,
clk);
if ((clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0 && !peer_clk_in_use) ||
(clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1 && !peer_clk_in_use)) {
u8 peer_phy = phy ? ICE_PHY0 : ICE_PHY1;
err = ice_cpi_ena_dis_clk_ref(&pf->hw, peer_phy, clk, true);
if (err) {
mutex_unlock(&ctrl_pf->dplls.lock);
dev_err(ice_pf_to_dev(pf),
"Failed to enable the %u TX clock for the %u PHY\n",
clk, peer_phy);
return err;
}
}
mutex_unlock(&ctrl_pf->dplls.lock);
return 0;
}
#define ICE_REFCLK_USER_TO_AQ_IDX(x) ((x) + 1)
int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
struct ice_port_info *port_info;
bool clk_in_use;
u8 port_num, phy;
int err;
if (pf->ptp.port.tx_clk == clk)
return 0;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: no controlling PF\n");
return -EINVAL;
}
if (!test_bit(ICE_FLAG_DPLL, ctrl_pf->flags)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: ctrl PF DPLL not available\n");
return -EOPNOTSUPP;
}
port_num = pf->ptp.port.port_num;
phy = port_num / pf->hw.ptp.ports_per_phy;
port_info = pf->hw.port_info;
mutex_lock(&ctrl_pf->dplls.lock);
clk_in_use = ice_txclk_any_port_uses(ctrl_pf, phy, clk);
if (!clk_in_use) {
err = ice_cpi_ena_dis_clk_ref(&pf->hw, phy, clk, true);
if (err) {
mutex_unlock(&ctrl_pf->dplls.lock);
dev_err(ice_pf_to_dev(pf), "Failed to enable the %u TX clock for the %u PHY\n",
clk, phy);
return err;
}
}
mutex_unlock(&ctrl_pf->dplls.lock);
if (!clk_in_use) {
err = ice_txclk_enable_peer(pf, clk);
if (err)
return err;
}
err = ice_aq_set_link_restart_an(port_info, true, NULL,
ICE_REFCLK_USER_TO_AQ_IDX(clk));
if (err) {
dev_err(ice_pf_to_dev(pf),
"AN restart AQ command failed with err %d\n",
err);
return err;
}
mutex_lock(&pf->dplls.lock);
if (pf->ptp.port.tx_clk_req == clk)
pf->dplls.txclk_switch_requested = false;
mutex_unlock(&pf->dplls.lock);
return 0;
}
void ice_txclk_update_and_notify(struct ice_pf *pf)
{
struct ice_ptp_port *ptp_port = &pf->ptp.port;
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
struct dpll_pin *old_pin = NULL;
struct dpll_pin *new_pin = NULL;
struct ice_hw *hw = &pf->hw;
enum ice_e825c_ref_clk clk;
bool notify_dpll = false;
int err;
u8 phy;
phy = ptp_port->port_num / hw->ptp.ports_per_phy;
down_read(&pf->dplls.txclk_notify_rwsem);
mutex_lock(&pf->dplls.lock);
if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
if (pf->dplls.txclk_switch_requested) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
if (pf->ptp.port.tx_clk == pf->ptp.port.tx_clk_req) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
err = ice_get_serdes_ref_sel_e825c(hw,
ptp_port->port_num,
&clk);
if (err) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
if (clk != pf->ptp.port.tx_clk_req) {
dev_warn(ice_pf_to_dev(pf),
"Failed to switch tx-clk for phy %d and clk %u (current: %u)\n",
phy, pf->ptp.port.tx_clk_req, clk);
old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk_req);
new_pin = ice_txclk_get_pin(pf, clk);
pf->ptp.port.tx_clk = clk;
pf->ptp.port.tx_clk_req = clk;
if (!IS_ERR_OR_NULL(ctrl_pf)) {
if (ctrl_pf != pf)
mutex_lock(&ctrl_pf->dplls.lock);
for (int i = 0; i < ICE_REF_CLK_MAX; i++) {
if (clk == i)
set_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
else
clear_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
}
if (ctrl_pf != pf)
mutex_unlock(&ctrl_pf->dplls.lock);
}
goto err_notify;
}
old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk);
pf->ptp.port.tx_clk = clk;
pf->ptp.port.tx_clk_req = clk;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: no controlling PF\n");
goto err_notify;
}
if (ctrl_pf != pf)
mutex_lock(&ctrl_pf->dplls.lock);
for (int i = 0; i < ICE_REF_CLK_MAX; i++)
if (clk == i)
set_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
else
clear_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
if (ctrl_pf != pf)
mutex_unlock(&ctrl_pf->dplls.lock);
err_notify:
if (!IS_ERR_OR_NULL(pf->dplls.txc.dpll)) {
enum dpll_lock_status new_lock = ice_txclk_lock_status(clk);
if (pf->dplls.txc.dpll_state != new_lock) {
pf->dplls.txc.dpll_state = new_lock;
notify_dpll = true;
}
}
mutex_unlock(&pf->dplls.lock);
if (old_pin)
dpll_pin_change_ntf(old_pin);
if (new_pin)
dpll_pin_change_ntf(new_pin);
if (notify_dpll && !IS_ERR_OR_NULL(pf->dplls.txc.dpll))
dpll_device_change_ntf(pf->dplls.txc.dpll);
out:
up_read(&pf->dplls.txclk_notify_rwsem);
}