162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * B53 register access through Switch Register Access Bridge Registers 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 762306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 862306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1162306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1262306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1362306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1462306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1562306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1662306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/platform_data/b53.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "b53_priv.h" 2862306a36Sopenharmony_ci#include "b53_serdes.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* command and status register of the SRAB */ 3162306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT 0x2c 3262306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT_RST BIT(2) 3362306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT_WRITE BIT(1) 3462306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT_GORDYN BIT(0) 3562306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT_PAGE 24 3662306a36Sopenharmony_ci#define B53_SRAB_CMDSTAT_REG 16 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* high order word of write data to switch registe */ 3962306a36Sopenharmony_ci#define B53_SRAB_WD_H 0x30 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* low order word of write data to switch registe */ 4262306a36Sopenharmony_ci#define B53_SRAB_WD_L 0x34 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* high order word of read data from switch register */ 4562306a36Sopenharmony_ci#define B53_SRAB_RD_H 0x38 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* low order word of read data from switch register */ 4862306a36Sopenharmony_ci#define B53_SRAB_RD_L 0x3c 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* command and status register of the SRAB */ 5162306a36Sopenharmony_ci#define B53_SRAB_CTRLS 0x40 5262306a36Sopenharmony_ci#define B53_SRAB_CTRLS_HOST_INTR BIT(1) 5362306a36Sopenharmony_ci#define B53_SRAB_CTRLS_RCAREQ BIT(3) 5462306a36Sopenharmony_ci#define B53_SRAB_CTRLS_RCAGNT BIT(4) 5562306a36Sopenharmony_ci#define B53_SRAB_CTRLS_SW_INIT_DONE BIT(6) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* the register captures interrupt pulses from the switch */ 5862306a36Sopenharmony_ci#define B53_SRAB_INTR 0x44 5962306a36Sopenharmony_ci#define B53_SRAB_INTR_P(x) BIT(x) 6062306a36Sopenharmony_ci#define B53_SRAB_SWITCH_PHY BIT(8) 6162306a36Sopenharmony_ci#define B53_SRAB_1588_SYNC BIT(9) 6262306a36Sopenharmony_ci#define B53_SRAB_IMP1_SLEEP_TIMER BIT(10) 6362306a36Sopenharmony_ci#define B53_SRAB_P7_SLEEP_TIMER BIT(11) 6462306a36Sopenharmony_ci#define B53_SRAB_IMP0_SLEEP_TIMER BIT(12) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Port mux configuration registers */ 6762306a36Sopenharmony_ci#define B53_MUX_CONFIG_P5 0x00 6862306a36Sopenharmony_ci#define MUX_CONFIG_SGMII 0 6962306a36Sopenharmony_ci#define MUX_CONFIG_MII_LITE 1 7062306a36Sopenharmony_ci#define MUX_CONFIG_RGMII 2 7162306a36Sopenharmony_ci#define MUX_CONFIG_GMII 3 7262306a36Sopenharmony_ci#define MUX_CONFIG_GPHY 4 7362306a36Sopenharmony_ci#define MUX_CONFIG_INTERNAL 5 7462306a36Sopenharmony_ci#define MUX_CONFIG_MASK 0x7 7562306a36Sopenharmony_ci#define B53_MUX_CONFIG_P4 0x04 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct b53_srab_port_priv { 7862306a36Sopenharmony_ci int irq; 7962306a36Sopenharmony_ci bool irq_enabled; 8062306a36Sopenharmony_ci struct b53_device *dev; 8162306a36Sopenharmony_ci unsigned int num; 8262306a36Sopenharmony_ci phy_interface_t mode; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct b53_srab_priv { 8662306a36Sopenharmony_ci void __iomem *regs; 8762306a36Sopenharmony_ci void __iomem *mux_config; 8862306a36Sopenharmony_ci struct b53_srab_port_priv port_intrs[B53_N_PORTS]; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int b53_srab_request_grant(struct b53_device *dev) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 9462306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 9562306a36Sopenharmony_ci u32 ctrls; 9662306a36Sopenharmony_ci int i; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ctrls = readl(regs + B53_SRAB_CTRLS); 9962306a36Sopenharmony_ci ctrls |= B53_SRAB_CTRLS_RCAREQ; 10062306a36Sopenharmony_ci writel(ctrls, regs + B53_SRAB_CTRLS); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i = 0; i < 20; i++) { 10362306a36Sopenharmony_ci ctrls = readl(regs + B53_SRAB_CTRLS); 10462306a36Sopenharmony_ci if (ctrls & B53_SRAB_CTRLS_RCAGNT) 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci usleep_range(10, 100); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (WARN_ON(i == 5)) 10962306a36Sopenharmony_ci return -EIO; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void b53_srab_release_grant(struct b53_device *dev) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 11762306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 11862306a36Sopenharmony_ci u32 ctrls; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ctrls = readl(regs + B53_SRAB_CTRLS); 12162306a36Sopenharmony_ci ctrls &= ~B53_SRAB_CTRLS_RCAREQ; 12262306a36Sopenharmony_ci writel(ctrls, regs + B53_SRAB_CTRLS); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 12862306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 12962306a36Sopenharmony_ci int i; 13062306a36Sopenharmony_ci u32 cmdstat; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* set register address */ 13362306a36Sopenharmony_ci cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) | 13462306a36Sopenharmony_ci (reg << B53_SRAB_CMDSTAT_REG) | 13562306a36Sopenharmony_ci B53_SRAB_CMDSTAT_GORDYN | 13662306a36Sopenharmony_ci op; 13762306a36Sopenharmony_ci writel(cmdstat, regs + B53_SRAB_CMDSTAT); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* check if operation completed */ 14062306a36Sopenharmony_ci for (i = 0; i < 5; ++i) { 14162306a36Sopenharmony_ci cmdstat = readl(regs + B53_SRAB_CMDSTAT); 14262306a36Sopenharmony_ci if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN)) 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci usleep_range(10, 100); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (WARN_ON(i == 5)) 14862306a36Sopenharmony_ci return -EIO; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 15662306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 15762306a36Sopenharmony_ci int ret = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 16062306a36Sopenharmony_ci if (ret) 16162306a36Sopenharmony_ci goto err; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, 0); 16462306a36Sopenharmony_ci if (ret) 16562306a36Sopenharmony_ci goto err; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci *val = readl(regs + B53_SRAB_RD_L) & 0xff; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cierr: 17062306a36Sopenharmony_ci b53_srab_release_grant(dev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 17862306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 17962306a36Sopenharmony_ci int ret = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci goto err; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, 0); 18662306a36Sopenharmony_ci if (ret) 18762306a36Sopenharmony_ci goto err; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci *val = readl(regs + B53_SRAB_RD_L) & 0xffff; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cierr: 19262306a36Sopenharmony_ci b53_srab_release_grant(dev); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 20062306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 20162306a36Sopenharmony_ci int ret = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci goto err; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, 0); 20862306a36Sopenharmony_ci if (ret) 20962306a36Sopenharmony_ci goto err; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci *val = readl(regs + B53_SRAB_RD_L); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cierr: 21462306a36Sopenharmony_ci b53_srab_release_grant(dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 22262306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 22362306a36Sopenharmony_ci int ret = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci goto err; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, 0); 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci goto err; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci *val = readl(regs + B53_SRAB_RD_L); 23462306a36Sopenharmony_ci *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cierr: 23762306a36Sopenharmony_ci b53_srab_release_grant(dev); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return ret; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 24562306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 24662306a36Sopenharmony_ci int ret = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 24962306a36Sopenharmony_ci if (ret) 25062306a36Sopenharmony_ci goto err; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, 0); 25362306a36Sopenharmony_ci if (ret) 25462306a36Sopenharmony_ci goto err; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci *val = readl(regs + B53_SRAB_RD_L); 25762306a36Sopenharmony_ci *val += (u64)readl(regs + B53_SRAB_RD_H) << 32; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cierr: 26062306a36Sopenharmony_ci b53_srab_release_grant(dev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 26862306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 26962306a36Sopenharmony_ci int ret = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 27262306a36Sopenharmony_ci if (ret) 27362306a36Sopenharmony_ci goto err; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci writel(value, regs + B53_SRAB_WD_L); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cierr: 28062306a36Sopenharmony_ci b53_srab_release_grant(dev); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg, 28662306a36Sopenharmony_ci u16 value) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 28962306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 29062306a36Sopenharmony_ci int ret = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci goto err; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci writel(value, regs + B53_SRAB_WD_L); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cierr: 30162306a36Sopenharmony_ci b53_srab_release_grant(dev); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg, 30762306a36Sopenharmony_ci u32 value) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 31062306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 31162306a36Sopenharmony_ci int ret = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 31462306a36Sopenharmony_ci if (ret) 31562306a36Sopenharmony_ci goto err; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci writel(value, regs + B53_SRAB_WD_L); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cierr: 32262306a36Sopenharmony_ci b53_srab_release_grant(dev); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg, 32862306a36Sopenharmony_ci u64 value) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 33162306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 33262306a36Sopenharmony_ci int ret = 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci goto err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci writel((u32)value, regs + B53_SRAB_WD_L); 33962306a36Sopenharmony_ci writel((u16)(value >> 32), regs + B53_SRAB_WD_H); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cierr: 34462306a36Sopenharmony_ci b53_srab_release_grant(dev); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg, 35062306a36Sopenharmony_ci u64 value) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 35362306a36Sopenharmony_ci u8 __iomem *regs = priv->regs; 35462306a36Sopenharmony_ci int ret = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = b53_srab_request_grant(dev); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci goto err; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci writel((u32)value, regs + B53_SRAB_WD_L); 36162306a36Sopenharmony_ci writel((u32)(value >> 32), regs + B53_SRAB_WD_H); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cierr: 36662306a36Sopenharmony_ci b53_srab_release_grant(dev); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic irqreturn_t b53_srab_port_thread(int irq, void *dev_id) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct b53_srab_port_priv *port = dev_id; 37462306a36Sopenharmony_ci struct b53_device *dev = port->dev; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (port->mode == PHY_INTERFACE_MODE_SGMII) 37762306a36Sopenharmony_ci b53_port_event(dev->ds, port->num); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return IRQ_HANDLED; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic irqreturn_t b53_srab_port_isr(int irq, void *dev_id) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct b53_srab_port_priv *port = dev_id; 38562306a36Sopenharmony_ci struct b53_device *dev = port->dev; 38662306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Acknowledge the interrupt */ 38962306a36Sopenharmony_ci writel(BIT(port->num), priv->regs + B53_SRAB_INTR); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_B53_SERDES) 39562306a36Sopenharmony_cistatic u8 b53_srab_serdes_map_lane(struct b53_device *dev, int port) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 39862306a36Sopenharmony_ci struct b53_srab_port_priv *p = &priv->port_intrs[port]; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (p->mode != PHY_INTERFACE_MODE_SGMII) 40162306a36Sopenharmony_ci return B53_INVALID_LANE; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci switch (port) { 40462306a36Sopenharmony_ci case 5: 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci case 4: 40762306a36Sopenharmony_ci return 1; 40862306a36Sopenharmony_ci default: 40962306a36Sopenharmony_ci return B53_INVALID_LANE; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci#endif 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int b53_srab_irq_enable(struct b53_device *dev, int port) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 41762306a36Sopenharmony_ci struct b53_srab_port_priv *p = &priv->port_intrs[port]; 41862306a36Sopenharmony_ci int ret = 0; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Interrupt is optional and was not specified, do not make 42162306a36Sopenharmony_ci * this fatal 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci if (p->irq == -ENXIO) 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ret = request_threaded_irq(p->irq, b53_srab_port_isr, 42762306a36Sopenharmony_ci b53_srab_port_thread, 0, 42862306a36Sopenharmony_ci dev_name(dev->dev), p); 42962306a36Sopenharmony_ci if (!ret) 43062306a36Sopenharmony_ci p->irq_enabled = true; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void b53_srab_irq_disable(struct b53_device *dev, int port) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 43862306a36Sopenharmony_ci struct b53_srab_port_priv *p = &priv->port_intrs[port]; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (p->irq_enabled) { 44162306a36Sopenharmony_ci free_irq(p->irq, p); 44262306a36Sopenharmony_ci p->irq_enabled = false; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void b53_srab_phylink_get_caps(struct b53_device *dev, int port, 44762306a36Sopenharmony_ci struct phylink_config *config) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 45062306a36Sopenharmony_ci struct b53_srab_port_priv *p = &priv->port_intrs[port]; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci switch (p->mode) { 45362306a36Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 45462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_B53_SERDES) 45562306a36Sopenharmony_ci /* If p->mode indicates SGMII mode, that essentially means we 45662306a36Sopenharmony_ci * are using a serdes. As the serdes for the capabilities. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci b53_serdes_phylink_get_caps(dev, port, config); 45962306a36Sopenharmony_ci#endif 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci case PHY_INTERFACE_MODE_NA: 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 46662306a36Sopenharmony_ci /* If we support RGMII, support all RGMII modes, since 46762306a36Sopenharmony_ci * that dictates the PHY delay settings. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci phy_interface_set_rgmii(config->supported_interfaces); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci default: 47362306a36Sopenharmony_ci /* Some other mode (e.g. MII, GMII etc) */ 47462306a36Sopenharmony_ci __set_bit(p->mode, config->supported_interfaces); 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic const struct b53_io_ops b53_srab_ops = { 48062306a36Sopenharmony_ci .read8 = b53_srab_read8, 48162306a36Sopenharmony_ci .read16 = b53_srab_read16, 48262306a36Sopenharmony_ci .read32 = b53_srab_read32, 48362306a36Sopenharmony_ci .read48 = b53_srab_read48, 48462306a36Sopenharmony_ci .read64 = b53_srab_read64, 48562306a36Sopenharmony_ci .write8 = b53_srab_write8, 48662306a36Sopenharmony_ci .write16 = b53_srab_write16, 48762306a36Sopenharmony_ci .write32 = b53_srab_write32, 48862306a36Sopenharmony_ci .write48 = b53_srab_write48, 48962306a36Sopenharmony_ci .write64 = b53_srab_write64, 49062306a36Sopenharmony_ci .irq_enable = b53_srab_irq_enable, 49162306a36Sopenharmony_ci .irq_disable = b53_srab_irq_disable, 49262306a36Sopenharmony_ci .phylink_get_caps = b53_srab_phylink_get_caps, 49362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_B53_SERDES) 49462306a36Sopenharmony_ci .phylink_mac_select_pcs = b53_serdes_phylink_mac_select_pcs, 49562306a36Sopenharmony_ci .serdes_map_lane = b53_srab_serdes_map_lane, 49662306a36Sopenharmony_ci .serdes_link_set = b53_serdes_link_set, 49762306a36Sopenharmony_ci#endif 49862306a36Sopenharmony_ci}; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct of_device_id b53_srab_of_match[] = { 50162306a36Sopenharmony_ci { .compatible = "brcm,bcm53010-srab" }, 50262306a36Sopenharmony_ci { .compatible = "brcm,bcm53011-srab" }, 50362306a36Sopenharmony_ci { .compatible = "brcm,bcm53012-srab" }, 50462306a36Sopenharmony_ci { .compatible = "brcm,bcm53018-srab" }, 50562306a36Sopenharmony_ci { .compatible = "brcm,bcm53019-srab" }, 50662306a36Sopenharmony_ci { .compatible = "brcm,bcm5301x-srab" }, 50762306a36Sopenharmony_ci { .compatible = "brcm,bcm11360-srab", .data = (void *)BCM583XX_DEVICE_ID }, 50862306a36Sopenharmony_ci { .compatible = "brcm,bcm58522-srab", .data = (void *)BCM58XX_DEVICE_ID }, 50962306a36Sopenharmony_ci { .compatible = "brcm,bcm58525-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51062306a36Sopenharmony_ci { .compatible = "brcm,bcm58535-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51162306a36Sopenharmony_ci { .compatible = "brcm,bcm58622-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51262306a36Sopenharmony_ci { .compatible = "brcm,bcm58623-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51362306a36Sopenharmony_ci { .compatible = "brcm,bcm58625-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51462306a36Sopenharmony_ci { .compatible = "brcm,bcm88312-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51562306a36Sopenharmony_ci { .compatible = "brcm,cygnus-srab", .data = (void *)BCM583XX_DEVICE_ID }, 51662306a36Sopenharmony_ci { .compatible = "brcm,nsp-srab", .data = (void *)BCM58XX_DEVICE_ID }, 51762306a36Sopenharmony_ci { .compatible = "brcm,omega-srab", .data = (void *)BCM583XX_DEVICE_ID }, 51862306a36Sopenharmony_ci { /* sentinel */ }, 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, b53_srab_of_match); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void b53_srab_intr_set(struct b53_srab_priv *priv, bool set) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci u32 reg; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci reg = readl(priv->regs + B53_SRAB_CTRLS); 52762306a36Sopenharmony_ci if (set) 52862306a36Sopenharmony_ci reg |= B53_SRAB_CTRLS_HOST_INTR; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci reg &= ~B53_SRAB_CTRLS_HOST_INTR; 53162306a36Sopenharmony_ci writel(reg, priv->regs + B53_SRAB_CTRLS); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic void b53_srab_prepare_irq(struct platform_device *pdev) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct b53_device *dev = platform_get_drvdata(pdev); 53762306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 53862306a36Sopenharmony_ci struct b53_srab_port_priv *port; 53962306a36Sopenharmony_ci unsigned int i; 54062306a36Sopenharmony_ci char *name; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Clear all pending interrupts */ 54362306a36Sopenharmony_ci writel(0xffffffff, priv->regs + B53_SRAB_INTR); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (i = 0; i < B53_N_PORTS; i++) { 54662306a36Sopenharmony_ci port = &priv->port_intrs[i]; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* There is no port 6 */ 54962306a36Sopenharmony_ci if (i == 6) 55062306a36Sopenharmony_ci continue; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "link_state_p%d", i); 55362306a36Sopenharmony_ci if (!name) 55462306a36Sopenharmony_ci return; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci port->num = i; 55762306a36Sopenharmony_ci port->dev = dev; 55862306a36Sopenharmony_ci port->irq = platform_get_irq_byname_optional(pdev, name); 55962306a36Sopenharmony_ci kfree(name); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci b53_srab_intr_set(priv, true); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic void b53_srab_mux_init(struct platform_device *pdev) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct b53_device *dev = platform_get_drvdata(pdev); 56862306a36Sopenharmony_ci struct b53_srab_priv *priv = dev->priv; 56962306a36Sopenharmony_ci struct b53_srab_port_priv *p; 57062306a36Sopenharmony_ci unsigned int port; 57162306a36Sopenharmony_ci u32 reg, off = 0; 57262306a36Sopenharmony_ci int ret; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (dev->pdata && dev->pdata->chip_id != BCM58XX_DEVICE_ID) 57562306a36Sopenharmony_ci return; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci priv->mux_config = devm_platform_ioremap_resource(pdev, 1); 57862306a36Sopenharmony_ci if (IS_ERR(priv->mux_config)) 57962306a36Sopenharmony_ci return; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Obtain the port mux configuration so we know which lanes 58262306a36Sopenharmony_ci * actually map to SerDes lanes 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci for (port = 5; port > 3; port--, off += 4) { 58562306a36Sopenharmony_ci p = &priv->port_intrs[port]; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci reg = readl(priv->mux_config + B53_MUX_CONFIG_P5 + off); 58862306a36Sopenharmony_ci switch (reg & MUX_CONFIG_MASK) { 58962306a36Sopenharmony_ci case MUX_CONFIG_SGMII: 59062306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_SGMII; 59162306a36Sopenharmony_ci ret = b53_serdes_init(dev, port); 59262306a36Sopenharmony_ci if (ret) 59362306a36Sopenharmony_ci continue; 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci case MUX_CONFIG_MII_LITE: 59662306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_MII; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci case MUX_CONFIG_GMII: 59962306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_GMII; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case MUX_CONFIG_RGMII: 60262306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_RGMII; 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case MUX_CONFIG_INTERNAL: 60562306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_INTERNAL; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci default: 60862306a36Sopenharmony_ci p->mode = PHY_INTERFACE_MODE_NA; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (p->mode != PHY_INTERFACE_MODE_NA) 61362306a36Sopenharmony_ci dev_info(&pdev->dev, "Port %d mode: %s\n", 61462306a36Sopenharmony_ci port, phy_modes(p->mode)); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int b53_srab_probe(struct platform_device *pdev) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct b53_platform_data *pdata = pdev->dev.platform_data; 62162306a36Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 62262306a36Sopenharmony_ci const struct of_device_id *of_id = NULL; 62362306a36Sopenharmony_ci struct b53_srab_priv *priv; 62462306a36Sopenharmony_ci struct b53_device *dev; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (dn) 62762306a36Sopenharmony_ci of_id = of_match_node(b53_srab_of_match, dn); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (of_id) { 63062306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 63162306a36Sopenharmony_ci if (!pdata) 63262306a36Sopenharmony_ci return -ENOMEM; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci pdata->chip_id = (u32)(unsigned long)of_id->data; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 63862306a36Sopenharmony_ci if (!priv) 63962306a36Sopenharmony_ci return -ENOMEM; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci priv->regs = devm_platform_ioremap_resource(pdev, 0); 64262306a36Sopenharmony_ci if (IS_ERR(priv->regs)) 64362306a36Sopenharmony_ci return PTR_ERR(priv->regs); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, priv); 64662306a36Sopenharmony_ci if (!dev) 64762306a36Sopenharmony_ci return -ENOMEM; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (pdata) 65062306a36Sopenharmony_ci dev->pdata = pdata; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci b53_srab_prepare_irq(pdev); 65562306a36Sopenharmony_ci b53_srab_mux_init(pdev); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return b53_switch_register(dev); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int b53_srab_remove(struct platform_device *pdev) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct b53_device *dev = platform_get_drvdata(pdev); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (!dev) 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci b53_srab_intr_set(dev->priv, false); 66862306a36Sopenharmony_ci b53_switch_remove(dev); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void b53_srab_shutdown(struct platform_device *pdev) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct b53_device *dev = platform_get_drvdata(pdev); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!dev) 67862306a36Sopenharmony_ci return; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci b53_switch_shutdown(dev); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic struct platform_driver b53_srab_driver = { 68662306a36Sopenharmony_ci .probe = b53_srab_probe, 68762306a36Sopenharmony_ci .remove = b53_srab_remove, 68862306a36Sopenharmony_ci .shutdown = b53_srab_shutdown, 68962306a36Sopenharmony_ci .driver = { 69062306a36Sopenharmony_ci .name = "b53-srab-switch", 69162306a36Sopenharmony_ci .of_match_table = b53_srab_of_match, 69262306a36Sopenharmony_ci }, 69362306a36Sopenharmony_ci}; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cimodule_platform_driver(b53_srab_driver); 69662306a36Sopenharmony_ciMODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>"); 69762306a36Sopenharmony_ciMODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver"); 69862306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 699