162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom SATA3 AHCI Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2009-2015 Broadcom Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/ahci_platform.h>
962306a36Sopenharmony_ci#include <linux/compiler.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/libata.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/reset.h>
2062306a36Sopenharmony_ci#include <linux/string.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "ahci.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define DRV_NAME					"brcm-ahci"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define SATA_TOP_CTRL_VERSION				0x0
2762306a36Sopenharmony_ci#define SATA_TOP_CTRL_BUS_CTRL				0x4
2862306a36Sopenharmony_ci #define MMIO_ENDIAN_SHIFT				0 /* CPU->AHCI */
2962306a36Sopenharmony_ci #define DMADESC_ENDIAN_SHIFT				2 /* AHCI->DDR */
3062306a36Sopenharmony_ci #define DMADATA_ENDIAN_SHIFT				4 /* AHCI->DDR */
3162306a36Sopenharmony_ci #define PIODATA_ENDIAN_SHIFT				6
3262306a36Sopenharmony_ci  #define ENDIAN_SWAP_NONE				0
3362306a36Sopenharmony_ci  #define ENDIAN_SWAP_FULL				2
3462306a36Sopenharmony_ci#define SATA_TOP_CTRL_TP_CTRL				0x8
3562306a36Sopenharmony_ci#define SATA_TOP_CTRL_PHY_CTRL				0xc
3662306a36Sopenharmony_ci #define SATA_TOP_CTRL_PHY_CTRL_1			0x0
3762306a36Sopenharmony_ci  #define SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE	BIT(14)
3862306a36Sopenharmony_ci #define SATA_TOP_CTRL_PHY_CTRL_2			0x4
3962306a36Sopenharmony_ci  #define SATA_TOP_CTRL_2_SW_RST_MDIOREG		BIT(0)
4062306a36Sopenharmony_ci  #define SATA_TOP_CTRL_2_SW_RST_OOB			BIT(1)
4162306a36Sopenharmony_ci  #define SATA_TOP_CTRL_2_SW_RST_RX			BIT(2)
4262306a36Sopenharmony_ci  #define SATA_TOP_CTRL_2_SW_RST_TX			BIT(3)
4362306a36Sopenharmony_ci  #define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET		BIT(14)
4462306a36Sopenharmony_ci #define SATA_TOP_CTRL_PHY_OFFS				0x8
4562306a36Sopenharmony_ci #define SATA_TOP_MAX_PHYS				2
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define SATA_FIRST_PORT_CTRL				0x700
4862306a36Sopenharmony_ci#define SATA_NEXT_PORT_CTRL_OFFSET			0x80
4962306a36Sopenharmony_ci#define SATA_PORT_PCTRL6(reg_base)			(reg_base + 0x18)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* On big-endian MIPS, buses are reversed to big endian, so switch them back */
5262306a36Sopenharmony_ci#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
5362306a36Sopenharmony_ci#define DATA_ENDIAN			 2 /* AHCI->DDR inbound accesses */
5462306a36Sopenharmony_ci#define MMIO_ENDIAN			 2 /* CPU->AHCI outbound accesses */
5562306a36Sopenharmony_ci#else
5662306a36Sopenharmony_ci#define DATA_ENDIAN			 0
5762306a36Sopenharmony_ci#define MMIO_ENDIAN			 0
5862306a36Sopenharmony_ci#endif
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define BUS_CTRL_ENDIAN_CONF				\
6162306a36Sopenharmony_ci	((DATA_ENDIAN << DMADATA_ENDIAN_SHIFT) |	\
6262306a36Sopenharmony_ci	(DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) |		\
6362306a36Sopenharmony_ci	(MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define BUS_CTRL_ENDIAN_NSP_CONF			\
6662306a36Sopenharmony_ci	(0x02 << DMADATA_ENDIAN_SHIFT | 0x02 << DMADESC_ENDIAN_SHIFT)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define BUS_CTRL_ENDIAN_CONF_MASK			\
6962306a36Sopenharmony_ci	(0x3 << MMIO_ENDIAN_SHIFT | 0x3 << DMADESC_ENDIAN_SHIFT |	\
7062306a36Sopenharmony_ci	 0x3 << DMADATA_ENDIAN_SHIFT | 0x3 << PIODATA_ENDIAN_SHIFT)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cienum brcm_ahci_version {
7362306a36Sopenharmony_ci	BRCM_SATA_BCM7425 = 1,
7462306a36Sopenharmony_ci	BRCM_SATA_BCM7445,
7562306a36Sopenharmony_ci	BRCM_SATA_NSP,
7662306a36Sopenharmony_ci	BRCM_SATA_BCM7216,
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cienum brcm_ahci_quirks {
8062306a36Sopenharmony_ci	BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE	= BIT(0),
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct brcm_ahci_priv {
8462306a36Sopenharmony_ci	struct device *dev;
8562306a36Sopenharmony_ci	void __iomem *top_ctrl;
8662306a36Sopenharmony_ci	u32 port_mask;
8762306a36Sopenharmony_ci	u32 quirks;
8862306a36Sopenharmony_ci	enum brcm_ahci_version version;
8962306a36Sopenharmony_ci	struct reset_control *rcdev_rescal;
9062306a36Sopenharmony_ci	struct reset_control *rcdev_ahci;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline u32 brcm_sata_readreg(void __iomem *addr)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * MIPS endianness is configured by boot strap, which also reverses all
9762306a36Sopenharmony_ci	 * bus endianness (i.e., big-endian CPU + big endian bus ==> native
9862306a36Sopenharmony_ci	 * endian I/O).
9962306a36Sopenharmony_ci	 *
10062306a36Sopenharmony_ci	 * Other architectures (e.g., ARM) either do not support big endian, or
10162306a36Sopenharmony_ci	 * else leave I/O in little endian mode.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
10462306a36Sopenharmony_ci		return __raw_readl(addr);
10562306a36Sopenharmony_ci	else
10662306a36Sopenharmony_ci		return readl_relaxed(addr);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic inline void brcm_sata_writereg(u32 val, void __iomem *addr)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	/* See brcm_sata_readreg() comments */
11262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
11362306a36Sopenharmony_ci		__raw_writel(val, addr);
11462306a36Sopenharmony_ci	else
11562306a36Sopenharmony_ci		writel_relaxed(val, addr);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void brcm_sata_alpm_init(struct ahci_host_priv *hpriv)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct brcm_ahci_priv *priv = hpriv->plat_data;
12162306a36Sopenharmony_ci	u32 port_ctrl, host_caps;
12262306a36Sopenharmony_ci	int i;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Enable support for ALPM */
12562306a36Sopenharmony_ci	host_caps = readl(hpriv->mmio + HOST_CAP);
12662306a36Sopenharmony_ci	if (!(host_caps & HOST_CAP_ALPM))
12762306a36Sopenharmony_ci		hpriv->flags |= AHCI_HFLAG_YES_ALPM;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/*
13062306a36Sopenharmony_ci	 * Adjust timeout to allow PLL sufficient time to lock while waking
13162306a36Sopenharmony_ci	 * up from slumber mode.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	for (i = 0, port_ctrl = SATA_FIRST_PORT_CTRL;
13462306a36Sopenharmony_ci	     i < SATA_TOP_MAX_PHYS;
13562306a36Sopenharmony_ci	     i++, port_ctrl += SATA_NEXT_PORT_CTRL_OFFSET) {
13662306a36Sopenharmony_ci		if (priv->port_mask & BIT(i))
13762306a36Sopenharmony_ci			writel(0xff1003fc,
13862306a36Sopenharmony_ci			       hpriv->mmio + SATA_PORT_PCTRL6(port_ctrl));
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
14562306a36Sopenharmony_ci				(port * SATA_TOP_CTRL_PHY_OFFS);
14662306a36Sopenharmony_ci	void __iomem *p;
14762306a36Sopenharmony_ci	u32 reg;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
15062306a36Sopenharmony_ci		return;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* clear PHY_DEFAULT_POWER_STATE */
15362306a36Sopenharmony_ci	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
15462306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
15562306a36Sopenharmony_ci	reg &= ~SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
15662306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* reset the PHY digital logic */
15962306a36Sopenharmony_ci	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
16062306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
16162306a36Sopenharmony_ci	reg &= ~(SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
16262306a36Sopenharmony_ci		 SATA_TOP_CTRL_2_SW_RST_RX);
16362306a36Sopenharmony_ci	reg |= SATA_TOP_CTRL_2_SW_RST_TX;
16462306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
16562306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
16662306a36Sopenharmony_ci	reg |= SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
16762306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
16862306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
16962306a36Sopenharmony_ci	reg &= ~SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
17062306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
17162306a36Sopenharmony_ci	(void)brcm_sata_readreg(p);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
17762306a36Sopenharmony_ci				(port * SATA_TOP_CTRL_PHY_OFFS);
17862306a36Sopenharmony_ci	void __iomem *p;
17962306a36Sopenharmony_ci	u32 reg;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* power-off the PHY digital logic */
18562306a36Sopenharmony_ci	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
18662306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
18762306a36Sopenharmony_ci	reg |= (SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
18862306a36Sopenharmony_ci		SATA_TOP_CTRL_2_SW_RST_RX | SATA_TOP_CTRL_2_SW_RST_TX |
18962306a36Sopenharmony_ci		SATA_TOP_CTRL_2_PHY_GLOBAL_RESET);
19062306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* set PHY_DEFAULT_POWER_STATE */
19362306a36Sopenharmony_ci	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
19462306a36Sopenharmony_ci	reg = brcm_sata_readreg(p);
19562306a36Sopenharmony_ci	reg |= SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
19662306a36Sopenharmony_ci	brcm_sata_writereg(reg, p);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void brcm_sata_phys_enable(struct brcm_ahci_priv *priv)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int i;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
20462306a36Sopenharmony_ci		if (priv->port_mask & BIT(i))
20562306a36Sopenharmony_ci			brcm_sata_phy_enable(priv, i);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void brcm_sata_phys_disable(struct brcm_ahci_priv *priv)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int i;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
21362306a36Sopenharmony_ci		if (priv->port_mask & BIT(i))
21462306a36Sopenharmony_ci			brcm_sata_phy_disable(priv, i);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic u32 brcm_ahci_get_portmask(struct ahci_host_priv *hpriv,
21862306a36Sopenharmony_ci				  struct brcm_ahci_priv *priv)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	u32 impl;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	impl = readl(hpriv->mmio + HOST_PORTS_IMPL);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (fls(impl) > SATA_TOP_MAX_PHYS)
22562306a36Sopenharmony_ci		dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n",
22662306a36Sopenharmony_ci			 impl);
22762306a36Sopenharmony_ci	else if (!impl)
22862306a36Sopenharmony_ci		dev_info(priv->dev, "no ports found\n");
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return impl;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void brcm_sata_init(struct brcm_ahci_priv *priv)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	void __iomem *ctrl = priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL;
23662306a36Sopenharmony_ci	u32 data;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Configure endianness */
23962306a36Sopenharmony_ci	data = brcm_sata_readreg(ctrl);
24062306a36Sopenharmony_ci	data &= ~BUS_CTRL_ENDIAN_CONF_MASK;
24162306a36Sopenharmony_ci	if (priv->version == BRCM_SATA_NSP)
24262306a36Sopenharmony_ci		data |= BUS_CTRL_ENDIAN_NSP_CONF;
24362306a36Sopenharmony_ci	else
24462306a36Sopenharmony_ci		data |= BUS_CTRL_ENDIAN_CONF;
24562306a36Sopenharmony_ci	brcm_sata_writereg(data, ctrl);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic unsigned int brcm_ahci_read_id(struct ata_device *dev,
24962306a36Sopenharmony_ci				      struct ata_taskfile *tf, __le16 *id)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct ata_port *ap = dev->link->ap;
25262306a36Sopenharmony_ci	struct ata_host *host = ap->host;
25362306a36Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
25462306a36Sopenharmony_ci	struct brcm_ahci_priv *priv = hpriv->plat_data;
25562306a36Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
25662306a36Sopenharmony_ci	unsigned int err_mask;
25762306a36Sopenharmony_ci	unsigned long flags;
25862306a36Sopenharmony_ci	int i, rc;
25962306a36Sopenharmony_ci	u32 ctl;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Try to read the device ID and, if this fails, proceed with the
26262306a36Sopenharmony_ci	 * recovery sequence below
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	err_mask = ata_do_dev_read_id(dev, tf, id);
26562306a36Sopenharmony_ci	if (likely(!err_mask))
26662306a36Sopenharmony_ci		return err_mask;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Disable host interrupts */
26962306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
27062306a36Sopenharmony_ci	ctl = readl(mmio + HOST_CTL);
27162306a36Sopenharmony_ci	ctl &= ~HOST_IRQ_EN;
27262306a36Sopenharmony_ci	writel(ctl, mmio + HOST_CTL);
27362306a36Sopenharmony_ci	readl(mmio + HOST_CTL); /* flush */
27462306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Perform the SATA PHY reset sequence */
27762306a36Sopenharmony_ci	brcm_sata_phy_disable(priv, ap->port_no);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Reset the SATA clock */
28062306a36Sopenharmony_ci	ahci_platform_disable_clks(hpriv);
28162306a36Sopenharmony_ci	msleep(10);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ahci_platform_enable_clks(hpriv);
28462306a36Sopenharmony_ci	msleep(10);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Bring the PHY back on */
28762306a36Sopenharmony_ci	brcm_sata_phy_enable(priv, ap->port_no);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Re-initialize and calibrate the PHY */
29062306a36Sopenharmony_ci	for (i = 0; i < hpriv->nports; i++) {
29162306a36Sopenharmony_ci		rc = phy_init(hpriv->phys[i]);
29262306a36Sopenharmony_ci		if (rc)
29362306a36Sopenharmony_ci			goto disable_phys;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		rc = phy_calibrate(hpriv->phys[i]);
29662306a36Sopenharmony_ci		if (rc) {
29762306a36Sopenharmony_ci			phy_exit(hpriv->phys[i]);
29862306a36Sopenharmony_ci			goto disable_phys;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Re-enable host interrupts */
30362306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
30462306a36Sopenharmony_ci	ctl = readl(mmio + HOST_CTL);
30562306a36Sopenharmony_ci	ctl |= HOST_IRQ_EN;
30662306a36Sopenharmony_ci	writel(ctl, mmio + HOST_CTL);
30762306a36Sopenharmony_ci	readl(mmio + HOST_CTL); /* flush */
30862306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return ata_do_dev_read_id(dev, tf, id);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cidisable_phys:
31362306a36Sopenharmony_ci	while (--i >= 0) {
31462306a36Sopenharmony_ci		phy_power_off(hpriv->phys[i]);
31562306a36Sopenharmony_ci		phy_exit(hpriv->phys[i]);
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return AC_ERR_OTHER;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void brcm_ahci_host_stop(struct ata_host *host)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ahci_platform_disable_resources(hpriv);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic struct ata_port_operations ahci_brcm_platform_ops = {
32962306a36Sopenharmony_ci	.inherits	= &ahci_ops,
33062306a36Sopenharmony_ci	.host_stop	= brcm_ahci_host_stop,
33162306a36Sopenharmony_ci	.read_id	= brcm_ahci_read_id,
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic const struct ata_port_info ahci_brcm_port_info = {
33562306a36Sopenharmony_ci	.flags		= AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
33662306a36Sopenharmony_ci	.link_flags	= ATA_LFLAG_NO_DEBOUNCE_DELAY,
33762306a36Sopenharmony_ci	.pio_mask	= ATA_PIO4,
33862306a36Sopenharmony_ci	.udma_mask	= ATA_UDMA6,
33962306a36Sopenharmony_ci	.port_ops	= &ahci_brcm_platform_ops,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int brcm_ahci_suspend(struct device *dev)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct ata_host *host = dev_get_drvdata(dev);
34562306a36Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
34662306a36Sopenharmony_ci	struct brcm_ahci_priv *priv = hpriv->plat_data;
34762306a36Sopenharmony_ci	int ret;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	brcm_sata_phys_disable(priv);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_SLEEP))
35262306a36Sopenharmony_ci		ret = ahci_platform_suspend(dev);
35362306a36Sopenharmony_ci	else
35462306a36Sopenharmony_ci		ret = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	reset_control_assert(priv->rcdev_ahci);
35762306a36Sopenharmony_ci	reset_control_rearm(priv->rcdev_rescal);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return ret;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int __maybe_unused brcm_ahci_resume(struct device *dev)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct ata_host *host = dev_get_drvdata(dev);
36562306a36Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
36662306a36Sopenharmony_ci	struct brcm_ahci_priv *priv = hpriv->plat_data;
36762306a36Sopenharmony_ci	int ret = 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ret = reset_control_deassert(priv->rcdev_ahci);
37062306a36Sopenharmony_ci	if (ret)
37162306a36Sopenharmony_ci		return ret;
37262306a36Sopenharmony_ci	ret = reset_control_reset(priv->rcdev_rescal);
37362306a36Sopenharmony_ci	if (ret)
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Make sure clocks are turned on before re-configuration */
37762306a36Sopenharmony_ci	ret = ahci_platform_enable_clks(hpriv);
37862306a36Sopenharmony_ci	if (ret)
37962306a36Sopenharmony_ci		return ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ret = ahci_platform_enable_regulators(hpriv);
38262306a36Sopenharmony_ci	if (ret)
38362306a36Sopenharmony_ci		goto out_disable_clks;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	brcm_sata_init(priv);
38662306a36Sopenharmony_ci	brcm_sata_phys_enable(priv);
38762306a36Sopenharmony_ci	brcm_sata_alpm_init(hpriv);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Since we had to enable clocks earlier on, we cannot use
39062306a36Sopenharmony_ci	 * ahci_platform_resume() as-is since a second call to
39162306a36Sopenharmony_ci	 * ahci_platform_enable_resources() would bump up the resources
39262306a36Sopenharmony_ci	 * (regulators, clocks, PHYs) count artificially so we copy the part
39362306a36Sopenharmony_ci	 * after ahci_platform_enable_resources().
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	ret = ahci_platform_enable_phys(hpriv);
39662306a36Sopenharmony_ci	if (ret)
39762306a36Sopenharmony_ci		goto out_disable_phys;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ret = ahci_platform_resume_host(dev);
40062306a36Sopenharmony_ci	if (ret)
40162306a36Sopenharmony_ci		goto out_disable_platform_phys;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* We resumed so update PM runtime state */
40462306a36Sopenharmony_ci	pm_runtime_disable(dev);
40562306a36Sopenharmony_ci	pm_runtime_set_active(dev);
40662306a36Sopenharmony_ci	pm_runtime_enable(dev);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ciout_disable_platform_phys:
41162306a36Sopenharmony_ci	ahci_platform_disable_phys(hpriv);
41262306a36Sopenharmony_ciout_disable_phys:
41362306a36Sopenharmony_ci	brcm_sata_phys_disable(priv);
41462306a36Sopenharmony_ci	ahci_platform_disable_regulators(hpriv);
41562306a36Sopenharmony_ciout_disable_clks:
41662306a36Sopenharmony_ci	ahci_platform_disable_clks(hpriv);
41762306a36Sopenharmony_ci	return ret;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic const struct scsi_host_template ahci_platform_sht = {
42162306a36Sopenharmony_ci	AHCI_SHT(DRV_NAME),
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic const struct of_device_id ahci_of_match[] = {
42562306a36Sopenharmony_ci	{.compatible = "brcm,bcm7425-ahci", .data = (void *)BRCM_SATA_BCM7425},
42662306a36Sopenharmony_ci	{.compatible = "brcm,bcm7445-ahci", .data = (void *)BRCM_SATA_BCM7445},
42762306a36Sopenharmony_ci	{.compatible = "brcm,bcm63138-ahci", .data = (void *)BRCM_SATA_BCM7445},
42862306a36Sopenharmony_ci	{.compatible = "brcm,bcm-nsp-ahci", .data = (void *)BRCM_SATA_NSP},
42962306a36Sopenharmony_ci	{.compatible = "brcm,bcm7216-ahci", .data = (void *)BRCM_SATA_BCM7216},
43062306a36Sopenharmony_ci	{ /* sentinel */ }
43162306a36Sopenharmony_ci};
43262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ahci_of_match);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int brcm_ahci_probe(struct platform_device *pdev)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	const struct of_device_id *of_id;
43762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
43862306a36Sopenharmony_ci	struct brcm_ahci_priv *priv;
43962306a36Sopenharmony_ci	struct ahci_host_priv *hpriv;
44062306a36Sopenharmony_ci	struct resource *res;
44162306a36Sopenharmony_ci	int ret;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
44462306a36Sopenharmony_ci	if (!priv)
44562306a36Sopenharmony_ci		return -ENOMEM;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	of_id = of_match_node(ahci_of_match, pdev->dev.of_node);
44862306a36Sopenharmony_ci	if (!of_id)
44962306a36Sopenharmony_ci		return -ENODEV;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	priv->version = (unsigned long)of_id->data;
45262306a36Sopenharmony_ci	priv->dev = dev;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "top-ctrl");
45562306a36Sopenharmony_ci	priv->top_ctrl = devm_ioremap_resource(dev, res);
45662306a36Sopenharmony_ci	if (IS_ERR(priv->top_ctrl))
45762306a36Sopenharmony_ci		return PTR_ERR(priv->top_ctrl);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (priv->version == BRCM_SATA_BCM7216) {
46062306a36Sopenharmony_ci		priv->rcdev_rescal = devm_reset_control_get_optional_shared(
46162306a36Sopenharmony_ci			&pdev->dev, "rescal");
46262306a36Sopenharmony_ci		if (IS_ERR(priv->rcdev_rescal))
46362306a36Sopenharmony_ci			return PTR_ERR(priv->rcdev_rescal);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	priv->rcdev_ahci = devm_reset_control_get_optional(&pdev->dev, "ahci");
46662306a36Sopenharmony_ci	if (IS_ERR(priv->rcdev_ahci))
46762306a36Sopenharmony_ci		return PTR_ERR(priv->rcdev_ahci);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	hpriv = ahci_platform_get_resources(pdev, 0);
47062306a36Sopenharmony_ci	if (IS_ERR(hpriv))
47162306a36Sopenharmony_ci		return PTR_ERR(hpriv);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	hpriv->plat_data = priv;
47462306a36Sopenharmony_ci	hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP | AHCI_HFLAG_NO_WRITE_TO_RO;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	switch (priv->version) {
47762306a36Sopenharmony_ci	case BRCM_SATA_BCM7425:
47862306a36Sopenharmony_ci		hpriv->flags |= AHCI_HFLAG_DELAY_ENGINE;
47962306a36Sopenharmony_ci		fallthrough;
48062306a36Sopenharmony_ci	case BRCM_SATA_NSP:
48162306a36Sopenharmony_ci		hpriv->flags |= AHCI_HFLAG_NO_NCQ;
48262306a36Sopenharmony_ci		priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	default:
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	ret = reset_control_reset(priv->rcdev_rescal);
48962306a36Sopenharmony_ci	if (ret)
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci	ret = reset_control_deassert(priv->rcdev_ahci);
49262306a36Sopenharmony_ci	if (ret)
49362306a36Sopenharmony_ci		return ret;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ret = ahci_platform_enable_clks(hpriv);
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		goto out_reset;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = ahci_platform_enable_regulators(hpriv);
50062306a36Sopenharmony_ci	if (ret)
50162306a36Sopenharmony_ci		goto out_disable_clks;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* Must be first so as to configure endianness including that
50462306a36Sopenharmony_ci	 * of the standard AHCI register space.
50562306a36Sopenharmony_ci	 */
50662306a36Sopenharmony_ci	brcm_sata_init(priv);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Initializes priv->port_mask which is used below */
50962306a36Sopenharmony_ci	priv->port_mask = brcm_ahci_get_portmask(hpriv, priv);
51062306a36Sopenharmony_ci	if (!priv->port_mask) {
51162306a36Sopenharmony_ci		ret = -ENODEV;
51262306a36Sopenharmony_ci		goto out_disable_regulators;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Must be done before ahci_platform_enable_phys() */
51662306a36Sopenharmony_ci	brcm_sata_phys_enable(priv);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	brcm_sata_alpm_init(hpriv);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	ret = ahci_platform_enable_phys(hpriv);
52162306a36Sopenharmony_ci	if (ret)
52262306a36Sopenharmony_ci		goto out_disable_phys;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
52562306a36Sopenharmony_ci				      &ahci_platform_sht);
52662306a36Sopenharmony_ci	if (ret)
52762306a36Sopenharmony_ci		goto out_disable_platform_phys;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	dev_info(dev, "Broadcom AHCI SATA3 registered\n");
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ciout_disable_platform_phys:
53462306a36Sopenharmony_ci	ahci_platform_disable_phys(hpriv);
53562306a36Sopenharmony_ciout_disable_phys:
53662306a36Sopenharmony_ci	brcm_sata_phys_disable(priv);
53762306a36Sopenharmony_ciout_disable_regulators:
53862306a36Sopenharmony_ci	ahci_platform_disable_regulators(hpriv);
53962306a36Sopenharmony_ciout_disable_clks:
54062306a36Sopenharmony_ci	ahci_platform_disable_clks(hpriv);
54162306a36Sopenharmony_ciout_reset:
54262306a36Sopenharmony_ci	reset_control_assert(priv->rcdev_ahci);
54362306a36Sopenharmony_ci	reset_control_rearm(priv->rcdev_rescal);
54462306a36Sopenharmony_ci	return ret;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void brcm_ahci_remove(struct platform_device *pdev)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct ata_host *host = dev_get_drvdata(&pdev->dev);
55062306a36Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
55162306a36Sopenharmony_ci	struct brcm_ahci_priv *priv = hpriv->plat_data;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	brcm_sata_phys_disable(priv);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	ata_platform_remove_one(pdev);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic void brcm_ahci_shutdown(struct platform_device *pdev)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	int ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* All resources releasing happens via devres, but our device, unlike a
56362306a36Sopenharmony_ci	 * proper remove is not disappearing, therefore using
56462306a36Sopenharmony_ci	 * brcm_ahci_suspend() here which does explicit power management is
56562306a36Sopenharmony_ci	 * appropriate.
56662306a36Sopenharmony_ci	 */
56762306a36Sopenharmony_ci	ret = brcm_ahci_suspend(&pdev->dev);
56862306a36Sopenharmony_ci	if (ret)
56962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to shutdown\n");
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ahci_brcm_pm_ops, brcm_ahci_suspend, brcm_ahci_resume);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic struct platform_driver brcm_ahci_driver = {
57562306a36Sopenharmony_ci	.probe = brcm_ahci_probe,
57662306a36Sopenharmony_ci	.remove_new = brcm_ahci_remove,
57762306a36Sopenharmony_ci	.shutdown = brcm_ahci_shutdown,
57862306a36Sopenharmony_ci	.driver = {
57962306a36Sopenharmony_ci		.name = DRV_NAME,
58062306a36Sopenharmony_ci		.of_match_table = ahci_of_match,
58162306a36Sopenharmony_ci		.pm = &ahci_brcm_pm_ops,
58262306a36Sopenharmony_ci	},
58362306a36Sopenharmony_ci};
58462306a36Sopenharmony_cimodule_platform_driver(brcm_ahci_driver);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom SATA3 AHCI Controller Driver");
58762306a36Sopenharmony_ciMODULE_AUTHOR("Brian Norris");
58862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
58962306a36Sopenharmony_ciMODULE_ALIAS("platform:sata-brcmstb");
590