/* 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" #if defined(DSL_DEVICES) /* TODO_DSL: SF2 based is little endian, no need to byte swap, but need to figure out how to use access_macros.h instead */ #define WRITE_32(a, r) ( *(volatile uint32_t*)(a) = *(uint32_t*)&(r) ) #define READ_32(a, r) ( *(volatile uint32_t*)&(r) = *(volatile uint32_t*) (a) ) #else #include "access_macros.h" #endif #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; #if defined(CONFIG_BCM963178) || defined(CONFIG_BCM947622) #ifndef _BYTE_ORDER_LITTLE_ENDIAN_ #define _BYTE_ORDER_LITTLE_ENDIAN_ #endif #endif #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 unused1:3; uint32_t mdio_clk_divider:8; uint32_t unused2:1; uint32_t free_run_clk_enable:1; uint32_t unused3:18; } 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 unused3:18; uint32_t free_run_clk_enable:1; uint32_t unused2:1; uint32_t mdio_clk_divider:8; uint32_t unused1:3; 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); #if defined(CONFIG_BCM963158) || defined(CONFIG_BCM963178) || defined(CONFIG_BCM947622) || defined(CONFIG_BCM96846) || \ defined(CONFIG_BCM96856) || defined(CONFIG_BCM96878) || defined(CONFIG_BCM96855) cfg.free_run_clk_enable = 1; #endif #if defined(CONFIG_BCM96846) || defined(CONFIG_BCM96856) cfg.mdio_clk_divider = 12; #endif 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; }