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