// SPDX-License-Identifier: GPL-2.0+ /* Copyright (c) 2016 Broadcom Corporation All Rights Reserved */ /* * 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 #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); cfg.free_run_clk_enable = 1; #if defined(CONFIG_BCM96846) || defined(CONFIG_BCM96856) || defined(CONFIG_BCM96878) 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; }