/* Copyright (c) 2016 Broadcom Corporation All Rights Reserved <:label-BRCM:2016:DUAL/GPL:standard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ /* * Created on: Aug 2016 * Author: yuval.raviv@broadcom.com */ /* * MDIO common functions */ #include "mdio_drv_common.h" #include "access_macros.h" #include "bcm_map_part.h" #define MDIO_BUSY_RETRY 2000 #define CHECK_MDIO_READY(x) if (!is_mdio_ready(x)) {ret = MDIO_ERROR; goto Exit;} typedef enum { MDIO_CLAUSE_45 = 0, MDIO_CLAUSE_22 = 1, } mdio_clause_t; typedef enum { MDIO22_WRITE = 1, MDIO22_READ = 2, } mdio22_op_t; typedef enum { MDIO45_ADDRESS = 0, MDIO45_WRITE = 1, MDIO45_READINC = 2, MDIO45_READ = 3, } mdio45_op_t; #ifdef _BYTE_ORDER_LITTLE_ENDIAN_ #pragma pack(push, 1) typedef struct { uint32_t data_addr:16; uint32_t reg_dev_addr:5; uint32_t phy_prt_addr:5; uint32_t op_code:2; uint32_t fail:1; uint32_t busy:1; uint32_t reserved:2; } mdio_cmd_reg_t; #pragma pack(pop) #pragma pack(push, 1) typedef struct { uint32_t mdio_clause:1; uint32_t unused:31; } mdio_cfg_reg_t; #pragma pack(pop) #else #pragma pack(push, 1) typedef struct { uint32_t reserved:2; uint32_t busy:1; uint32_t fail:1; uint32_t op_code:2; uint32_t phy_prt_addr:5; uint32_t reg_dev_addr:5; uint32_t data_addr:16; } mdio_cmd_reg_t; #pragma pack(pop) #pragma pack(push, 1) typedef struct { uint32_t unused:31; uint32_t mdio_clause:1; } mdio_cfg_reg_t; #pragma pack(pop) #endif static int32_t is_mdio_ready(uint32_t *p) { uint32_t retry = MDIO_BUSY_RETRY; mdio_cmd_reg_t cmd; do { READ_32(p, cmd); if (!cmd.busy) break; udelay(10); } while (retry--); if (!retry) { printk("MDIO Error: mdio_is_ready() reached maximum retries of %d\n", MDIO_BUSY_RETRY); return 0; } if (cmd.fail) { printk("MDIO Error: MDIO got failure status on phy %d\n", cmd.phy_prt_addr); memset(&cmd, 0, sizeof(cmd)); WRITE_32(p, cmd); return 0; } return 1; } static void mdio_cfg_clause_mode(uint32_t *p, mdio_clause_t mdio_clause) { mdio_cfg_reg_t cfg; READ_32(p, cfg); cfg.mdio_clause = mdio_clause; WRITE_32(p, cfg); } int32_t mdio_cmd_read_22(uint32_t *p, uint32_t addr, uint32_t reg, uint16_t *val) { int ret = MDIO_OK; mdio_cmd_reg_t cmd = {}; mdio_cfg_clause_mode(p + 1, MDIO_CLAUSE_22); cmd.op_code = MDIO22_READ; cmd.phy_prt_addr = addr; cmd.reg_dev_addr = reg; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); READ_32(p, cmd); *val = cmd.data_addr; Exit: return ret; } int32_t mdio_cmd_write_22(uint32_t *p, uint32_t addr, uint32_t reg, uint16_t val) { int ret = MDIO_OK; mdio_cmd_reg_t cmd = {}; mdio_cfg_clause_mode(p + 1, MDIO_CLAUSE_22); cmd.op_code = MDIO22_WRITE; cmd.phy_prt_addr = addr; cmd.reg_dev_addr = reg; cmd.data_addr = val; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); Exit: return ret; } int32_t mdio_cmd_read_45(uint32_t *p, uint32_t addr, uint32_t dev, uint16_t reg, uint16_t *val) { int ret = MDIO_OK; mdio_cmd_reg_t cmd = {}; mdio_cfg_clause_mode(p + 1, MDIO_CLAUSE_45); cmd.op_code = MDIO45_ADDRESS; cmd.phy_prt_addr = addr; cmd.reg_dev_addr = dev; cmd.data_addr = reg; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); cmd.op_code = MDIO45_READ; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); READ_32(p, cmd); *val = cmd.data_addr; Exit: return ret; } int32_t mdio_cmd_write_45(uint32_t *p, uint32_t addr, uint32_t dev, uint16_t reg, uint16_t val) { int ret = MDIO_OK; mdio_cmd_reg_t cmd = {}; mdio_cfg_clause_mode(p + 1, MDIO_CLAUSE_45); cmd.op_code = MDIO45_ADDRESS; cmd.phy_prt_addr = addr; cmd.reg_dev_addr = dev; cmd.data_addr = reg; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); cmd.op_code = MDIO45_WRITE; cmd.data_addr = val; cmd.busy = 1; WRITE_32(p, cmd); CHECK_MDIO_READY(p); Exit: return ret; }