Skip to content

Commit

Permalink
net: aquantia: add support for Phy access
Browse files Browse the repository at this point in the history
GPIO PIN control and access is done by direct phy manipulation.
Here we add an aq_phy module which is able to access phy registers
via MDIO access mailbox.

Access is controlled via HW semaphore.

Co-developed-by: Nikita Danilov <nikita.danilov@aquantia.com>
Signed-off-by: Nikita Danilov <nikita.danilov@aquantia.com>
Signed-off-by: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Dmitry Bezrukov authored and David S. Miller committed Oct 24, 2019
1 parent 84989af commit dbcd680
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/aquantia/atlantic/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ atlantic-objs := aq_main.o \
aq_drvinfo.o \
aq_filters.o \
aq_ptp.o \
aq_phy.o \
hw_atl/hw_atl_a0.o \
hw_atl/hw_atl_b0.o \
hw_atl/hw_atl_utils.o \
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ struct aq_hw_s {
u32 rpc_tid;
struct hw_atl_utils_fw_rpc rpc;
s64 ptp_clk_offset;
u16 phy_id;
};

struct aq_ring_s;
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "aq_hw.h"
#include "aq_pci_func.h"
#include "aq_main.h"
#include "aq_phy.h"
#include "aq_ptp.h"
#include "aq_filters.h"

Expand Down Expand Up @@ -337,6 +338,11 @@ int aq_nic_init(struct aq_nic_s *self)
if (err < 0)
goto err_exit;

if (self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) {
self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX;
err = aq_phy_init(self->aq_hw);
}

for (i = 0U, aq_vec = self->aq_vec[0];
self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i])
aq_vec_init(aq_vec, self->aq_hw_ops, self->aq_hw);
Expand Down
147 changes: 147 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_phy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/* aQuantia Corporation Network Driver
* Copyright (C) 2018-2019 aQuantia Corporation. All rights reserved
*/

#include "aq_phy.h"

bool aq_mdio_busy_wait(struct aq_hw_s *aq_hw)
{
int err = 0;
u32 val;

err = readx_poll_timeout_atomic(hw_atl_mdio_busy_get, aq_hw,
val, val == 0U, 10U, 100000U);

if (err < 0)
return false;

return true;
}

u16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr)
{
u16 phy_addr = aq_hw->phy_id << 5 | mmd;

/* Set Address register. */
hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
HW_ATL_MDIO_ADDRESS_SHIFT);
/* Send Address command. */
hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
(3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
HW_ATL_MDIO_PHY_ADDRESS_SHIFT));

aq_mdio_busy_wait(aq_hw);

/* Send Read command. */
hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
(1 << HW_ATL_MDIO_OP_MODE_SHIFT) |
((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
HW_ATL_MDIO_PHY_ADDRESS_SHIFT));
/* Read result. */
aq_mdio_busy_wait(aq_hw);

return (u16)hw_atl_glb_mdio_iface5_get(aq_hw);
}

void aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data)
{
u16 phy_addr = aq_hw->phy_id << 5 | mmd;

/* Set Address register. */
hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) <<
HW_ATL_MDIO_ADDRESS_SHIFT);
/* Send Address command. */
hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
(3 << HW_ATL_MDIO_OP_MODE_SHIFT) |
((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
HW_ATL_MDIO_PHY_ADDRESS_SHIFT));

aq_mdio_busy_wait(aq_hw);

hw_atl_glb_mdio_iface3_set(aq_hw, (data & HW_ATL_MDIO_WRITE_DATA_MSK) <<
HW_ATL_MDIO_WRITE_DATA_SHIFT);
/* Send Write command. */
hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK |
(2 << HW_ATL_MDIO_OP_MODE_SHIFT) |
((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) <<
HW_ATL_MDIO_PHY_ADDRESS_SHIFT));

aq_mdio_busy_wait(aq_hw);
}

u16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address)
{
int err = 0;
u32 val;

err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
val, val == 1U, 10U, 100000U);

if (err < 0) {
err = 0xffff;
goto err_exit;
}

err = aq_mdio_read_word(aq_hw, mmd, address);

hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);

err_exit:
return err;
}

void aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data)
{
int err = 0;
u32 val;

err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw,
val, val == 1U, 10U, 100000U);
if (err < 0)
return;

aq_mdio_write_word(aq_hw, mmd, address, data);
hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO);
}

bool aq_phy_init_phy_id(struct aq_hw_s *aq_hw)
{
u16 val;

for (aq_hw->phy_id = 0; aq_hw->phy_id < HW_ATL_PHY_ID_MAX;
++aq_hw->phy_id) {
/* PMA Standard Device Identifier 2: Address 1.3 */
val = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);

if (val != 0xffff)
return true;
}

return false;
}

bool aq_phy_init(struct aq_hw_s *aq_hw)
{
u32 dev_id;

if (aq_hw->phy_id == HW_ATL_PHY_ID_MAX)
if (!aq_phy_init_phy_id(aq_hw))
return false;

/* PMA Standard Device Identifier:
* Address 1.2 = MSW,
* Address 1.3 = LSW
*/
dev_id = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 2);
dev_id <<= 16;
dev_id |= aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3);

if (dev_id == 0xffffffff) {
aq_hw->phy_id = HW_ATL_PHY_ID_MAX;
return false;
}

return true;
}
32 changes: 32 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_phy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* aQuantia Corporation Network Driver
* Copyright (C) 2018-2019 aQuantia Corporation. All rights reserved
*/

#ifndef AQ_PHY_H
#define AQ_PHY_H

#include <linux/mdio.h>

#include "hw_atl/hw_atl_llh.h"
#include "hw_atl/hw_atl_llh_internal.h"
#include "aq_hw_utils.h"
#include "aq_hw.h"

#define HW_ATL_PHY_ID_MAX 32U

bool aq_mdio_busy_wait(struct aq_hw_s *aq_hw);

u16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr);

void aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data);

u16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address);

void aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data);

bool aq_phy_init_phy_id(struct aq_hw_s *aq_hw);

bool aq_phy_init(struct aq_hw_s *aq_hw);

#endif /* AQ_PHY_H */
62 changes: 62 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,11 @@ u32 hw_atl_sem_ram_get(struct aq_hw_s *self)
return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
}

u32 hw_atl_sem_mdio_get(struct aq_hw_s *self)
{
return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_MDIO);
}

u32 hw_atl_scrpad_get(struct aq_hw_s *aq_hw, u32 scratch_scp)
{
return aq_hw_read_reg(aq_hw,
Expand All @@ -1659,3 +1664,60 @@ u32 hw_atl_scrpad25_get(struct aq_hw_s *self)
{
return hw_atl_scrpad_get(self, 0x18);
}

void hw_atl_glb_mdio_iface1_set(struct aq_hw_s *aq_hw, u32 value)
{
aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(1), value);
}

u32 hw_atl_glb_mdio_iface1_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(1));
}

void hw_atl_glb_mdio_iface2_set(struct aq_hw_s *aq_hw, u32 value)
{
aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(2), value);
}

u32 hw_atl_glb_mdio_iface2_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(2));
}

void hw_atl_glb_mdio_iface3_set(struct aq_hw_s *aq_hw, u32 value)
{
aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(3), value);
}

u32 hw_atl_glb_mdio_iface3_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(3));
}

void hw_atl_glb_mdio_iface4_set(struct aq_hw_s *aq_hw, u32 value)
{
aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(4), value);
}

u32 hw_atl_glb_mdio_iface4_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(4));
}

void hw_atl_glb_mdio_iface5_set(struct aq_hw_s *aq_hw, u32 value)
{
aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(5), value);
}

u32 hw_atl_glb_mdio_iface5_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(5));
}

u32 hw_atl_mdio_busy_get(struct aq_hw_s *aq_hw)
{
return aq_hw_read_reg_bit(aq_hw, HW_ATL_MDIO_BUSY_ADR,
HW_ATL_MDIO_BUSY_MSK,
HW_ATL_MDIO_BUSY_SHIFT);
}
35 changes: 35 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,9 +767,44 @@ void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
u32 *ipv6_dest);

/* set Global MDIO Interface 1 */
void hw_atl_glb_mdio_iface1_set(struct aq_hw_s *hw, u32 value);

/* get Global MDIO Interface 1 */
u32 hw_atl_glb_mdio_iface1_get(struct aq_hw_s *hw);

/* set Global MDIO Interface 2 */
void hw_atl_glb_mdio_iface2_set(struct aq_hw_s *hw, u32 value);

/* get Global MDIO Interface 2 */
u32 hw_atl_glb_mdio_iface2_get(struct aq_hw_s *hw);

/* set Global MDIO Interface 3 */
void hw_atl_glb_mdio_iface3_set(struct aq_hw_s *hw, u32 value);

/* get Global MDIO Interface 3 */
u32 hw_atl_glb_mdio_iface3_get(struct aq_hw_s *hw);

/* set Global MDIO Interface 4 */
void hw_atl_glb_mdio_iface4_set(struct aq_hw_s *hw, u32 value);

/* get Global MDIO Interface 4 */
u32 hw_atl_glb_mdio_iface4_get(struct aq_hw_s *hw);

/* set Global MDIO Interface 5 */
void hw_atl_glb_mdio_iface5_set(struct aq_hw_s *hw, u32 value);

/* get Global MDIO Interface 5 */
u32 hw_atl_glb_mdio_iface5_get(struct aq_hw_s *hw);

u32 hw_atl_mdio_busy_get(struct aq_hw_s *aq_hw);

/* get global microprocessor ram semaphore */
u32 hw_atl_sem_ram_get(struct aq_hw_s *self);

/* get global microprocessor mdio semaphore */
u32 hw_atl_sem_mdio_get(struct aq_hw_s *self);

/* get global microprocessor scratch pad register */
u32 hw_atl_scrpad_get(struct aq_hw_s *aq_hw, u32 scratch_scp);

Expand Down
Loading

0 comments on commit dbcd680

Please sign in to comment.