blob: 68a78660a2cd9ed3d03e5b81dd22593b1b855c3d [file] [log] [blame]
/*
* Copyright (c) 2015-2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
/*
* Manage the QCA8511 Music Switch.
*
* All definitions in this file are operating system independent!
*/
#include <common.h>
#include <miiphy.h>
#include <asm/arch-ipq806x/athrs17_phy.h>
#include <asm/arch-ipq806x/qca8511.h>
#include <asm/arch-ipq806x/ipq_gmac.h>
#undef DEBUG
#ifdef DEBUG
#define dbg(format, arg...) dbg("DEBUG: " format "\n", ## arg)
#else
#define dbg(format, arg...) do {} while(0)
#endif /* DEBUG */
ipq_gmac_board_cfg_t *gmac_cfg_qca8511;
static uint32_t
qca8511_pp_reg_read(ipq_gmac_board_cfg_t *gmac_cfg, uint32_t reg_addr)
{
uint32_t reg_word_addr;
uint32_t phy_addr, reg_val;
uint16_t phy_val;
uint16_t tmp_val;
uint8_t phy_reg;
/*
* Change reg_addr to 16-bit word address, 32-bit aligned.
*/
reg_word_addr = (reg_addr & 0xfffffffc) ;
dbg("WJL %s: 0-reg_addr=0x%08x, reg_word_addr=0x%08x.\n\n",
__func__, reg_addr, reg_word_addr);
/*
* configure register high address;
*/
phy_addr = 0x18 | (reg_word_addr >> 29);
phy_reg = (reg_word_addr & 0x1f000000) >> 24;
/*
* Bit23-8 of reg address
*/
phy_val = (uint16_t) ((reg_word_addr >> 8) & 0xffff);
miiphy_write(gmac_cfg->phy_name, phy_addr, phy_reg, phy_val);
dbg("WJL %s: 1-w.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val);
/*
* For some registers such as MIBs, since it is read/clear,
* we should read the lower 16-bit register then the higher one
*/
/*
* Read register in lower address
* Bit7-5 of reg address
*/
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7);
/*
* Bit4-0 of reg address.
*/
phy_reg = (uint8_t) (reg_word_addr & 0x1f);
miiphy_read(gmac_cfg->phy_name, phy_addr, phy_reg, &phy_val);
dbg("WJL %s: 2-r.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x, reg_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val, reg_val);
/*
* Read register in higher address
* bit7-5 of reg address
*/
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7);
/*
* Bit4-0 of reg address.
*/
phy_reg = (uint8_t) ((reg_word_addr & 0x1f) | 0x2);
miiphy_read(gmac_cfg->phy_name, phy_addr, phy_reg, &tmp_val);
reg_val = (tmp_val << 16 | phy_val);
dbg("WJL %s: 3-r.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x, reg_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val, reg_val);
return reg_val;
}
static void qca8511_pp_reg_write(ipq_gmac_board_cfg_t *gmac_cfg,
uint32_t reg_addr, uint32_t reg_val)
{
uint32_t reg_word_addr;
uint32_t phy_addr;
uint16_t phy_val;
uint8_t phy_reg;
/*
* change reg_addr to 16-bit word address,
* 32-bit aligned
*/
reg_word_addr = (reg_addr & 0xfffffffc);
dbg("WJL %s: 0-reg_addr=0x%08x, reg_word_addr=0x%08x.\n\n",
__func__, reg_addr, reg_word_addr);
/*
* configure register high address
*/
phy_addr = 0x18 | (reg_word_addr >> 29);
phy_reg = (reg_word_addr & 0x1f000000) >> 24;
/*
* Bit23-8 of reg address.
*/
phy_val = (uint16_t) ((reg_word_addr >> 8) & 0xffff);
miiphy_write(gmac_cfg->phy_name, phy_addr, phy_reg, phy_val);
dbg("WJL %s: 1-w.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val);
/*
* For some registers such as ARL and VLAN, since they include BUSY bit
* in lower address, we should write the higher 16-bit register then the
* lower one
*/
/*
* write register in higher address
* bit7-5 of reg address
*/
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7);
/*
* bit4-0 of reg address
*/
phy_reg = (uint8_t) (reg_word_addr & 0x1f);
/*
* lowest 16bit data
*/
phy_val = (uint16_t) (reg_val & 0xffff);
miiphy_write(gmac_cfg->phy_name, phy_addr, phy_reg, phy_val);
dbg("WJL %s: 2-w.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x, reg_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val, reg_val);
/*
* write register in lower address
* bit7-5 of reg address
*/
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7);
/*
* Bit4-0 of reg address
*/
phy_reg = (uint8_t) ((reg_word_addr & 0x1f) | 0x2);
/*
* Highest 16bit data
*/
phy_val = (uint16_t) ((reg_val >> 16) & 0xffff);
miiphy_write(gmac_cfg->phy_name, phy_addr, phy_reg, phy_val);
dbg("WJL %s: 3-w.phy_addr=0x%04x, phy_reg=0x%04x,"
"phy_val=0x%04x, reg_val=0x%04x.\n\n",
__func__, phy_addr, phy_reg, phy_val, reg_val);
}
static uint32_t qca8511_pp_phy_reg_read(ipq_gmac_board_cfg_t *gmac_cfg,
uint32_t phy_sel, uint32_t phy_addr,
uint8_t reg_addr)
{
uint32_t reg_val;
/*
* B31,MDIO_BUSY
*/
reg_val = (1 << 31);
/*
* B27, MDIO_CMD 0 = Write;1 = Read
*/
reg_val |= (1 << 27);
/*
* B29:28, PHY_SEL MDIO channel for MDIO master.
* It is used to specify PHY group that to be operated.
*/
reg_val |= ((phy_sel & 0x3) << 28);
/*
* B25:21 R/W 0 PHY_ADDR PHY address.
*/
reg_val |= ((phy_addr & 0x1f) << 21);
/*
* b20:16 R/W 0 REG_ADDR PHY register address.
*/
reg_val |= ((reg_addr & 0x1f) << 16);
dbg("WJL %s: 1. phy_sel=0x%x, phy_addr=0x%x,"
"reg_addr=0x%x, reg_val=0x%08x.\n",
__func__, phy_sel, phy_addr,reg_addr, reg_val);
qca8511_pp_reg_write(gmac_cfg, 0x15004, reg_val);
udelay(10);
reg_val = qca8511_pp_reg_read(gmac_cfg, 0x15004);
dbg("WJL %s: 2. phy_sel=0x%x, phy_addr=0x%x,"
"reg_addr=0x%x, reg_val=0x%08x.\n",
__func__, phy_sel, phy_addr,reg_addr, reg_val);
return reg_val;
}
static void qca8511_pp_phy_reg_write(ipq_gmac_board_cfg_t *gmac_cfg,
uint32_t phy_sel, uint32_t phy_addr,
uint32_t reg_addr, uint32_t reg_data)
{
uint32_t reg_val;
/* B31,MDIO_BUSY */
reg_val = (1 << 31);
/*
* b27, MDIO_CMD 0 = Write; 1 = Read;
*/
reg_val |= (0 << 27);
/*
* B29:28, PHY_SEL MDIO channel for MDIO master.
* It is used to specify PHY group that to be operated.
*/
reg_val |= ((phy_sel & 0x3) << 28);
/*
* B25:21 R/W 0 PHY_ADDR PHY address
*/
reg_val |= ((phy_addr & 0x1f) << 21);
/*
* B20:16 R/W 0 REG_ADDR PHY register address
*/
reg_val |= ((reg_addr & 0x1f) << 16);
/*
* b15:0 R/WW 0 MDIO_DATA When write, these bits are data written
* to PHY register. When read, these bits are data read
* out from PHY register.
*/
reg_val |= (reg_data & 0xffff);
dbg("WJL %s: 1. phy_sel=0x%x, phy_addr=0x%x,"
"reg_addr=0x%x, reg_val=0x%08x.\n",
__func__, phy_sel, phy_addr,reg_addr, reg_val);
qca8511_pp_reg_write(gmac_cfg, 0x15004, reg_val);
udelay(10);
/*
* if b31-MDIO_BUSY is reset to 0?
*/
reg_val = qca8511_pp_reg_read(gmac_cfg, 0x15004);
dbg("WJL %s: 2. phy_sel=0x%x, phy_addr=0x%x,"
"reg_addr=0x%x, reg_val=0x%08x.\n",
__func__, phy_sel, phy_addr,reg_addr, reg_val);
return ;
}
static int do_qca8511_pp_reg_read(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
ulong addr, readval;
int size;
if ((argc < 2) || (argc > 3))
return CMD_RET_USAGE;
/*
* Check for size specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
return 1;
/*
* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
readval = qca8511_pp_reg_read(gmac_cfg_qca8511, addr);
printf("WJL %s: addr=0x%08lx, readval=0x%08lx.\n\n",
__func__, addr, readval);
return 0;
}
static int do_qca8511_pp_reg_write( cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
ulong addr, writeval;
int size;
if ((argc < 3) || (argc > 4))
return CMD_RET_USAGE;
/*
* Check for size specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
return 1;
/*
* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
/*
* Get the value to write.
*/
writeval = simple_strtoul(argv[2], NULL, 16);
if (argc == 4)
simple_strtoul(argv[3], NULL, 16);
printf("WJL %s: addr=0x%08lx, writeval=0x%08lx.\n\n",
__func__,addr, writeval);
qca8511_pp_reg_write(gmac_cfg_qca8511, addr, writeval);
dbg("\n");
return 0;
}
static int do_qca8511_pp_phy_reg_read( cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
ulong phy_sel, phy_addr, reg_addr, reg_val;
int size;
if (argc != 4)
return CMD_RET_USAGE;
/*
* Check for size specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
return 1;
/*
* Address is specified since argc > 1
*/
phy_sel = simple_strtoul(argv[1], NULL, 16);
phy_addr = simple_strtoul(argv[2], NULL, 16);
reg_addr = simple_strtoul(argv[3], NULL, 16);
reg_val = qca8511_pp_phy_reg_read(gmac_cfg_qca8511, phy_sel,
phy_addr, reg_addr);
printf("WJL %s: phy_sel=0x%lx, phy_addr=0x%lx, reg_addr=0x%lx,"
"reg_val=0x%08lx, size=%d.\n\n",
__func__, phy_sel, phy_addr, reg_addr, reg_val, size);
return 0;
}
static int do_qca8511_pp_phy_reg_write( cmd_tbl_t *cmdtp,
int flag, int argc, char * const argv[])
{
ulong phy_sel, phy_addr, reg_addr, reg_data;
int size;
if (argc != 5)
return CMD_RET_USAGE;
/*
* Check for size specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
return 1;
/*
* Address is specified since argc > 1
*/
phy_sel = simple_strtoul(argv[1], NULL, 16);
phy_addr = simple_strtoul(argv[2], NULL, 16);
reg_addr = simple_strtoul(argv[3], NULL, 16);
reg_data = simple_strtoul(argv[4], NULL, 16);
printf("WJL %s: phy_sel=0x%lx, phy_addr=0x%lx, reg_addr=0x%lx,"
"reg_data=0x%08lx, size=%d.\n",
__func__, phy_sel, phy_addr, reg_addr, reg_data, size);
qca8511_pp_phy_reg_write(gmac_cfg_qca8511, phy_sel, phy_addr,
reg_addr, reg_data);
dbg("\n");
return 0;
}
/*********************************************************************
*
* FUNCTION DESCRIPTION: This function invokes RGMII,
* SGMII switch init routines.
* INPUT : ipq_gmac_board_cfg_t *
* OUTPUT: NONE
*
**********************************************************************/
int ipq_qca8511_init(ipq_gmac_board_cfg_t *gmac_cfg)
{
uint i;
gmac_cfg_qca8511 = gmac_cfg;
qca8511_pp_reg_write(gmac_cfg, QCA8511_QSGMII_1_CTRL(QSGMII_1_CTRL0),
QSGMII_1_CH0_DUPLEX_MODE |
QSGMII_1_CH0_LINK |
QSGMII_1_CH0_SPEED_MODE(FORCE_1000) |
QSGMII_1_CH0_MR_AN_EN |
QSGMII_1_RSVD16 |
QSGMII_1_CH0_FORCED_MODE |
QSGMII_1_CH_MODE_CTRL(QSGMII_MAC)|
QSGMII_1_CH0_PAUSE_SG_TX_EN |
QSGMII_1_CH0_ASYM_PAUSE |
QSGMII_1_CH0_PAUSE |
QSGMII_1_RSVD30 |
QSGMII_1_RSVD31);
qca8511_pp_reg_write(gmac_cfg, QCA8511_QSGMII_2_CTRL(QSGMII_2_CTRL0),
QSGMII_2_CH1_DUPLEX_MODE |
QSGMII_2_CH1_LINK |
QSGMII_2_CH1_SPEED_MODE(FORCE_1000) |
QSGMII_2_CH1_MR_AN_EN |
QSGMII_2_RSVD16 |
QSGMII_2_CH1_FORCED_MODE |
QSGMII_2_CH_RSVD(2) |
QSGMII_2_CH1_PAUSE_SG_TX_EN |
QSGMII_2_CH1_ASYM_PAUSE |
QSGMII_2_CH1_PAUSE |
QSGMII_2_RSVD30 |
QSGMII_2_RSVD31);
qca8511_pp_reg_write(gmac_cfg, QCA8511_QSGMII_3_CTRL(QSGMII_3_CTRL0),
QSGMII_3_CH2_DUPLEX_MODE |
QSGMII_3_CH2_LINK |
QSGMII_3_CH2_SPEED_MODE(FORCE_1000) |
QSGMII_3_CH2_MR_AN_EN |
QSGMII_3_RSVD16 |
QSGMII_3_CH2_FORCED_MODE |
QSGMII_2_CH_RSVD(2) |
QSGMII_3_CH2_PAUSE_SG_TX_EN |
QSGMII_3_CH2_ASYM_PAUSE |
QSGMII_3_CH2_PAUSE |
QSGMII_2_RSVD30 |
QSGMII_2_RSVD31);
qca8511_pp_reg_write(gmac_cfg, QCA8511_QSGMII_4_CTRL(QSGMII_3_CTRL0),
QSGMII_4_CH3_DUPLEX_MODE |
QSGMII_4_CH3_LINK |
QSGMII_4_CH3_SPEED_MODE(FORCE_1000) |
QSGMII_4_CH3_MR_AN_EN |
QSGMII_4_RSVD16 |
QSGMII_4_CH3_FORCED_MODE |
QSGMII_4_CH_RSVD(2) |
QSGMII_4_CH3_PAUSE_SG_TX_EN |
QSGMII_4_CH3_ASYM_PAUSE |
QSGMII_4_CH3_PAUSE |
QSGMII_4_RSVD30 |
QSGMII_4_RSVD31);
qca8511_pp_reg_write(gmac_cfg, QCA8511_SGMII_CTRL0(SGMII_CTRL0_PORT8),
SGMII_CTRL0_DUPLEX(1) |
SGMII_CTRL0_SPEED_MODE(2) |
SGMII_CTRL0_MR_AN_EN |
SGMII_CTRL0_RSVD16 |
SGMII_CTRL0_MODECTRL(2) |
SGMII_CTRL0_PAUSE_SG_TX_EN |
SGMII_CTRL0_ASYM_PAUSE_EN |
SGMII_CTRL0_PAUSE_EN |
SGMII_CTRL0_HALF_DUPLEX_EN |
SGMII_CTRL0_FULL_DUPLEX_EN);
/*
* Configure 8511 Ports
*/
for (i = STATUS_PORT1; i < STATUS_PORT5; i++) {
qca8511_pp_reg_write(gmac_cfg, QCA8511_PORT_STATUS_CFG(i),
QCA8511_PORT_CFG_SPEED(FORCE_1000) |
QCA8511_PORT_CFG_TX_MAC_EN |
QCA8511_PORT_CFG_RX_MAC_EN |
QCA8511_PORT_CFG_DUPLEX_MODE);
}
qca8511_pp_reg_write(gmac_cfg, QCA8511_GLOBAL_CTRL1,
GLOBAL_CTL1_MAC25XG_4P3G_EN |
GLOBAL_CTL1_MAC25XG_3P125G_EN |
GLOBAL_CTL1_MAC26SG_1P25G_EN |
GLOBAL_CTL1_MAC27SG_3P125G_EN |
GLOBAL_CTL1_MAC28SG_3P125G_EN |
GLOBAL_CTL1_MAC29SG_3P125G_EN |
GLOBAL_CTL1_RSVD22 |
GLOBAL_CTL1_SPI1_EN |
GLOBAL_CTL1_TO_EXT_INT_EN |
GLOBAL_CTL1_LED_CLK_EN_CFG |
GLOBAL_CTL1_RSVD28 |
GLOBAL_CTL1_RSVD29 |
GLOBAL_CTL1_TWO_WIRE_LED_EN);
qca8511_pp_reg_write(gmac_cfg, QCA8511_XAUI_SGMII_SERDES13_CTRL0,
SGMII_CTRL0_RSVD(3) |
SGMII_CTRL0_XAUI_EN_PLL(3) |
SGMII_CTRL0_XAUI_DEEMP_CH0(1) |
SGMII_CTRL0_XAUI_DEEMP_CH2(1) |
SGMII_CTRL0_XAUI_DEEMP_CH3(1) |
SGMII_CTRL0_XAUI_EN_SD(0xf));
qca8511_pp_reg_write(gmac_cfg, QCA8511_XAUI_SGMII_SERDES13_CTRL1,
SGMII_CTRL1_RSVD4(3) |
SGMII_CTRL1_XAUI_EN_RX(0xf) |
SGMII_CTRL1_XAUI_EN_TX(0xf) |
SGMII_CTRL1_XAUI_DEEMP_CH1(1) |
SGMII_CTRL1_XAUI_REG(9) |
SGMII_CTRL1_RSVD23(1) |
SGMII_CTRL1_RSVD25(2));
printf("QCA8511 Init done....\n");
return 0;
}
U_BOOT_CMD(
mprd, 3, 1, do_qca8511_pp_reg_read,
"qca8511 packet processor register display",
"[.b, .w, .l] address [# of objects]"
);
U_BOOT_CMD(
mprw, 4, 1, do_qca8511_pp_reg_write,
"qca8511 packet processor register write (fill)",
"[.b, .w, .l] address value [count]"
);
U_BOOT_CMD(
mphyrd, 5, 1, do_qca8511_pp_phy_reg_read,
"qca8511 packet processor PHY register display",
"[.b, .w, .l] phy_sel phy_addr reg_addr [# of objects]"
);
U_BOOT_CMD(
mphyrw, 6, 1, do_qca8511_pp_phy_reg_write,
"qca8511 packet processor PHY register write (fill)",
"[.b, .w, .l] phy_sel phy_addr reg_addr reg_data [count]"
);