/* * Copyright 2017 Broadcom * * 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 version 2 (GPLv2) for more details. * * You should have received a copy of the GNU General Public License * version 2 (GPLv2) along with this source code. */ /* * $Id: $ * $Copyright: (c) 2015 Broadcom Corp. * All Rights Reserved.$ * */ #include /* iProc MDIO register offset */ #define MII_MGMT_CTRL 0x0 #define MII_MGMT_CMD_DATA 0x4 /* iProc MII register with fields definition */ #define MII_MGMT_CTRLr_MDCDIVf_SHFT 0 #define MII_MGMT_CTRLr_MDCDIVf_MASK 0x7f #define MII_MGMT_CTRLr_BSYf_SHFT 8 #define MII_MGMT_CTRLr_BSYf_MASK 0x1 #define MII_MGMT_CMD_DATAr_DATAf_SHFT 0 #define MII_MGMT_CMD_DATAr_DATAf_MASK 0xffff #define MII_MGMT_CMD_DATAr_TAf_SHFT 16 #define MII_MGMT_CMD_DATAr_TAf_MASK 0x3 #define MII_MGMT_CMD_DATAr_RAf_SHFT 18 #define MII_MGMT_CMD_DATAr_RAf_MASK 0x1f #define MII_MGMT_CMD_DATAr_PAf_SHFT 23 #define MII_MGMT_CMD_DATAr_PAf_MASK 0x1f #define MII_MGMT_CMD_DATAr_OPf_SHFT 28 #define MII_MGMT_CMD_DATAr_OPf_MASK 0x3 #define MII_MGMT_CMD_DATAr_SBf_SHFT 30 #define MII_MGMT_CMD_DATAr_SBf_MASK 0x3 /* Register field value set/get */ #define REG_FIELD_SET(_r, _f, _r_val, _f_val) \ _r_val = ((_r_val) & ~(_r##_##_f##_MASK << _r##_##_f##_SHFT)) | \ (((_f_val) & _r##_##_f##_MASK) << _r##_##_f##_SHFT) #define REG_FIELD_GET(_r, _f, _r_val) \ (((_r_val) >> _r##_##_f##_SHFT) & _r##_##_f##_MASK) #define LOG_OUT(_shbde, _lvl, _str, _prm) \ if ((_shbde)->log_func) { \ (_shbde)->log_func(_lvl, _str, _prm); \ } #define LOG_ERR(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_ERR, _str, _prm) #define LOG_WARN(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_WARN, _str, _prm) #define LOG_DBG(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_DBG, _str, _prm) static unsigned int mdio32_read(shbde_mdio_ctrl_t *smc, unsigned int offset) { if (!smc || !smc->io32_read) { return 0; } return smc->io32_read(smc->shbde, smc->regs, smc->base_addr + offset); } static void mdio32_write(shbde_mdio_ctrl_t *smc, unsigned int offset, unsigned int data) { if (!smc || !smc->io32_read) { return; } smc->io32_write(smc->shbde, smc->regs, smc->base_addr + offset, data); } static void wait_usec(shbde_mdio_ctrl_t *smc, int usec) { shbde_hal_t *shbde = smc->shbde; if (shbde && shbde->usleep) { shbde->usleep(usec); } else { int idx; volatile int count; for (idx = 0; idx < usec; idx++) { for (count = 0; count < 100; count++); } } } static int iproc_mdio_wait_for_busy(shbde_mdio_ctrl_t *smc) { int mii_busy; unsigned int reg_val; int count = 1000; /* Wait until MII is not busy */ do { reg_val = mdio32_read(smc, MII_MGMT_CTRL); mii_busy = REG_FIELD_GET(MII_MGMT_CTRLr, BSYf, reg_val); if (!mii_busy) { break; } wait_usec(smc, 10); count --; } while (count > 0); return mii_busy; } int shbde_iproc_mdio_init(shbde_mdio_ctrl_t *smc) { shbde_hal_t *shbde = smc->shbde; unsigned int reg_val = 0; /* Enable the iProc internal MDIO interface */ REG_FIELD_SET(MII_MGMT_CTRLr, MDCDIVf, reg_val, 0x7f); mdio32_write(smc, MII_MGMT_CTRL, reg_val); if (shbde && !shbde->usleep) { LOG_DBG(shbde, "shbde_mdio: no registration of usleep vector", 0); } wait_usec(smc, 100); return 0; } int shbde_iproc_mdio_read(shbde_mdio_ctrl_t *smc, unsigned int phy_addr, unsigned int reg, unsigned int *val) { unsigned int reg_val = 0; REG_FIELD_SET(MII_MGMT_CMD_DATAr, SBf, reg_val, 0x1); REG_FIELD_SET(MII_MGMT_CMD_DATAr, TAf, reg_val, 0x2); REG_FIELD_SET(MII_MGMT_CMD_DATAr, OPf, reg_val, 0x2); REG_FIELD_SET(MII_MGMT_CMD_DATAr, PAf, reg_val, phy_addr); REG_FIELD_SET(MII_MGMT_CMD_DATAr, RAf, reg_val, reg); mdio32_write(smc, MII_MGMT_CMD_DATA, reg_val); if (iproc_mdio_wait_for_busy(smc)) { *val = 0; LOG_DBG(smc->shbde, "shbde_iproc_mdio_read busy", reg); return -1; } reg_val = mdio32_read(smc, MII_MGMT_CMD_DATA); *val = REG_FIELD_GET(MII_MGMT_CMD_DATAr, DATAf, reg_val); return 0; } int shbde_iproc_mdio_write(shbde_mdio_ctrl_t *smc, unsigned int phy_addr, unsigned int reg, unsigned int val) { unsigned int reg_val = 0; REG_FIELD_SET(MII_MGMT_CMD_DATAr, SBf, reg_val, 0x1); REG_FIELD_SET(MII_MGMT_CMD_DATAr, TAf, reg_val, 0x2); REG_FIELD_SET(MII_MGMT_CMD_DATAr, OPf, reg_val, 0x1); REG_FIELD_SET(MII_MGMT_CMD_DATAr, PAf, reg_val, phy_addr); REG_FIELD_SET(MII_MGMT_CMD_DATAr, RAf, reg_val, reg); REG_FIELD_SET(MII_MGMT_CMD_DATAr, DATAf, reg_val, val); mdio32_write(smc, MII_MGMT_CMD_DATA, reg_val); if (iproc_mdio_wait_for_busy(smc)) { LOG_DBG(smc->shbde, "shbde_iproc_mdio_write busy", reg); return -1; } /* Wait for some time for the write to take effect */ wait_usec(smc, 100); return 0; }