xref: /openbmc/linux/drivers/ata/ahci_brcm.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
13e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2eba68f82SYendapally Reddy Dhananjaya Reddy /*
3eba68f82SYendapally Reddy Dhananjaya Reddy  * Broadcom SATA3 AHCI Controller Driver
4eba68f82SYendapally Reddy Dhananjaya Reddy  *
5eba68f82SYendapally Reddy Dhananjaya Reddy  * Copyright © 2009-2015 Broadcom Corporation
6eba68f82SYendapally Reddy Dhananjaya Reddy  */
7eba68f82SYendapally Reddy Dhananjaya Reddy 
8eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/ahci_platform.h>
9eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/compiler.h>
10eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/device.h>
11eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/init.h>
12eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/interrupt.h>
13eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/io.h>
14eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/kernel.h>
15eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/libata.h>
16eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/module.h>
17eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/of.h>
18eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/platform_device.h>
192b2c47d9SFlorian Fainelli #include <linux/reset.h>
20eba68f82SYendapally Reddy Dhananjaya Reddy #include <linux/string.h>
21eba68f82SYendapally Reddy Dhananjaya Reddy 
22eba68f82SYendapally Reddy Dhananjaya Reddy #include "ahci.h"
23eba68f82SYendapally Reddy Dhananjaya Reddy 
24eba68f82SYendapally Reddy Dhananjaya Reddy #define DRV_NAME					"brcm-ahci"
25eba68f82SYendapally Reddy Dhananjaya Reddy 
26eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_TOP_CTRL_VERSION				0x0
27eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_TOP_CTRL_BUS_CTRL				0x4
28eba68f82SYendapally Reddy Dhananjaya Reddy  #define MMIO_ENDIAN_SHIFT				0 /* CPU->AHCI */
29eba68f82SYendapally Reddy Dhananjaya Reddy  #define DMADESC_ENDIAN_SHIFT				2 /* AHCI->DDR */
30eba68f82SYendapally Reddy Dhananjaya Reddy  #define DMADATA_ENDIAN_SHIFT				4 /* AHCI->DDR */
31eba68f82SYendapally Reddy Dhananjaya Reddy  #define PIODATA_ENDIAN_SHIFT				6
32eba68f82SYendapally Reddy Dhananjaya Reddy   #define ENDIAN_SWAP_NONE				0
33eba68f82SYendapally Reddy Dhananjaya Reddy   #define ENDIAN_SWAP_FULL				2
34eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_TOP_CTRL_TP_CTRL				0x8
35eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_TOP_CTRL_PHY_CTRL				0xc
36eba68f82SYendapally Reddy Dhananjaya Reddy  #define SATA_TOP_CTRL_PHY_CTRL_1			0x0
37eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE	BIT(14)
38eba68f82SYendapally Reddy Dhananjaya Reddy  #define SATA_TOP_CTRL_PHY_CTRL_2			0x4
39eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_2_SW_RST_MDIOREG		BIT(0)
40eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_2_SW_RST_OOB			BIT(1)
41eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_2_SW_RST_RX			BIT(2)
42eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_2_SW_RST_TX			BIT(3)
43eba68f82SYendapally Reddy Dhananjaya Reddy   #define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET		BIT(14)
44eba68f82SYendapally Reddy Dhananjaya Reddy  #define SATA_TOP_CTRL_PHY_OFFS				0x8
45eba68f82SYendapally Reddy Dhananjaya Reddy  #define SATA_TOP_MAX_PHYS				2
46eba68f82SYendapally Reddy Dhananjaya Reddy 
47eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_FIRST_PORT_CTRL				0x700
48eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_NEXT_PORT_CTRL_OFFSET			0x80
49eba68f82SYendapally Reddy Dhananjaya Reddy #define SATA_PORT_PCTRL6(reg_base)			(reg_base + 0x18)
50eba68f82SYendapally Reddy Dhananjaya Reddy 
51eba68f82SYendapally Reddy Dhananjaya Reddy /* On big-endian MIPS, buses are reversed to big endian, so switch them back */
52eba68f82SYendapally Reddy Dhananjaya Reddy #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
53eba68f82SYendapally Reddy Dhananjaya Reddy #define DATA_ENDIAN			 2 /* AHCI->DDR inbound accesses */
54eba68f82SYendapally Reddy Dhananjaya Reddy #define MMIO_ENDIAN			 2 /* CPU->AHCI outbound accesses */
55eba68f82SYendapally Reddy Dhananjaya Reddy #else
56eba68f82SYendapally Reddy Dhananjaya Reddy #define DATA_ENDIAN			 0
57eba68f82SYendapally Reddy Dhananjaya Reddy #define MMIO_ENDIAN			 0
58eba68f82SYendapally Reddy Dhananjaya Reddy #endif
59eba68f82SYendapally Reddy Dhananjaya Reddy 
60eba68f82SYendapally Reddy Dhananjaya Reddy #define BUS_CTRL_ENDIAN_CONF				\
61eba68f82SYendapally Reddy Dhananjaya Reddy 	((DATA_ENDIAN << DMADATA_ENDIAN_SHIFT) |	\
62eba68f82SYendapally Reddy Dhananjaya Reddy 	(DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) |		\
63eba68f82SYendapally Reddy Dhananjaya Reddy 	(MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
64eba68f82SYendapally Reddy Dhananjaya Reddy 
6536fffd6aSFlorian Fainelli #define BUS_CTRL_ENDIAN_NSP_CONF			\
6636fffd6aSFlorian Fainelli 	(0x02 << DMADATA_ENDIAN_SHIFT | 0x02 << DMADESC_ENDIAN_SHIFT)
6736fffd6aSFlorian Fainelli 
6836fffd6aSFlorian Fainelli #define BUS_CTRL_ENDIAN_CONF_MASK			\
6936fffd6aSFlorian Fainelli 	(0x3 << MMIO_ENDIAN_SHIFT | 0x3 << DMADESC_ENDIAN_SHIFT |	\
7036fffd6aSFlorian Fainelli 	 0x3 << DMADATA_ENDIAN_SHIFT | 0x3 << PIODATA_ENDIAN_SHIFT)
7136fffd6aSFlorian Fainelli 
723ee2e6dcSYendapally Reddy Dhananjaya Reddy enum brcm_ahci_version {
733ee2e6dcSYendapally Reddy Dhananjaya Reddy 	BRCM_SATA_BCM7425 = 1,
743ee2e6dcSYendapally Reddy Dhananjaya Reddy 	BRCM_SATA_BCM7445,
753ee2e6dcSYendapally Reddy Dhananjaya Reddy 	BRCM_SATA_NSP,
76c345ec6aSFlorian Fainelli 	BRCM_SATA_BCM7216,
773ee2e6dcSYendapally Reddy Dhananjaya Reddy };
783ee2e6dcSYendapally Reddy Dhananjaya Reddy 
79eba68f82SYendapally Reddy Dhananjaya Reddy enum brcm_ahci_quirks {
801a3d78cbSFlorian Fainelli 	BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE	= BIT(0),
81eba68f82SYendapally Reddy Dhananjaya Reddy };
82eba68f82SYendapally Reddy Dhananjaya Reddy 
83eba68f82SYendapally Reddy Dhananjaya Reddy struct brcm_ahci_priv {
84eba68f82SYendapally Reddy Dhananjaya Reddy 	struct device *dev;
85eba68f82SYendapally Reddy Dhananjaya Reddy 	void __iomem *top_ctrl;
86eba68f82SYendapally Reddy Dhananjaya Reddy 	u32 port_mask;
87eba68f82SYendapally Reddy Dhananjaya Reddy 	u32 quirks;
883ee2e6dcSYendapally Reddy Dhananjaya Reddy 	enum brcm_ahci_version version;
89e8d6f9e5SJim Quinlan 	struct reset_control *rcdev_rescal;
90e8d6f9e5SJim Quinlan 	struct reset_control *rcdev_ahci;
91eba68f82SYendapally Reddy Dhananjaya Reddy };
92eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_readreg(void __iomem * addr)93eba68f82SYendapally Reddy Dhananjaya Reddy static inline u32 brcm_sata_readreg(void __iomem *addr)
94eba68f82SYendapally Reddy Dhananjaya Reddy {
95eba68f82SYendapally Reddy Dhananjaya Reddy 	/*
96eba68f82SYendapally Reddy Dhananjaya Reddy 	 * MIPS endianness is configured by boot strap, which also reverses all
97eba68f82SYendapally Reddy Dhananjaya Reddy 	 * bus endianness (i.e., big-endian CPU + big endian bus ==> native
98eba68f82SYendapally Reddy Dhananjaya Reddy 	 * endian I/O).
99eba68f82SYendapally Reddy Dhananjaya Reddy 	 *
100eba68f82SYendapally Reddy Dhananjaya Reddy 	 * Other architectures (e.g., ARM) either do not support big endian, or
101eba68f82SYendapally Reddy Dhananjaya Reddy 	 * else leave I/O in little endian mode.
102eba68f82SYendapally Reddy Dhananjaya Reddy 	 */
103eba68f82SYendapally Reddy Dhananjaya Reddy 	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
104eba68f82SYendapally Reddy Dhananjaya Reddy 		return __raw_readl(addr);
105eba68f82SYendapally Reddy Dhananjaya Reddy 	else
106eba68f82SYendapally Reddy Dhananjaya Reddy 		return readl_relaxed(addr);
107eba68f82SYendapally Reddy Dhananjaya Reddy }
108eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_writereg(u32 val,void __iomem * addr)109eba68f82SYendapally Reddy Dhananjaya Reddy static inline void brcm_sata_writereg(u32 val, void __iomem *addr)
110eba68f82SYendapally Reddy Dhananjaya Reddy {
111eba68f82SYendapally Reddy Dhananjaya Reddy 	/* See brcm_sata_readreg() comments */
112eba68f82SYendapally Reddy Dhananjaya Reddy 	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
113eba68f82SYendapally Reddy Dhananjaya Reddy 		__raw_writel(val, addr);
114eba68f82SYendapally Reddy Dhananjaya Reddy 	else
115eba68f82SYendapally Reddy Dhananjaya Reddy 		writel_relaxed(val, addr);
116eba68f82SYendapally Reddy Dhananjaya Reddy }
117eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_alpm_init(struct ahci_host_priv * hpriv)118eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_alpm_init(struct ahci_host_priv *hpriv)
119eba68f82SYendapally Reddy Dhananjaya Reddy {
120eba68f82SYendapally Reddy Dhananjaya Reddy 	struct brcm_ahci_priv *priv = hpriv->plat_data;
121da8fa9ccSDoug Berger 	u32 port_ctrl, host_caps;
122eba68f82SYendapally Reddy Dhananjaya Reddy 	int i;
123eba68f82SYendapally Reddy Dhananjaya Reddy 
124eba68f82SYendapally Reddy Dhananjaya Reddy 	/* Enable support for ALPM */
125eba68f82SYendapally Reddy Dhananjaya Reddy 	host_caps = readl(hpriv->mmio + HOST_CAP);
126da8fa9ccSDoug Berger 	if (!(host_caps & HOST_CAP_ALPM))
127da8fa9ccSDoug Berger 		hpriv->flags |= AHCI_HFLAG_YES_ALPM;
128eba68f82SYendapally Reddy Dhananjaya Reddy 
129eba68f82SYendapally Reddy Dhananjaya Reddy 	/*
130eba68f82SYendapally Reddy Dhananjaya Reddy 	 * Adjust timeout to allow PLL sufficient time to lock while waking
131eba68f82SYendapally Reddy Dhananjaya Reddy 	 * up from slumber mode.
132eba68f82SYendapally Reddy Dhananjaya Reddy 	 */
133eba68f82SYendapally Reddy Dhananjaya Reddy 	for (i = 0, port_ctrl = SATA_FIRST_PORT_CTRL;
134eba68f82SYendapally Reddy Dhananjaya Reddy 	     i < SATA_TOP_MAX_PHYS;
135eba68f82SYendapally Reddy Dhananjaya Reddy 	     i++, port_ctrl += SATA_NEXT_PORT_CTRL_OFFSET) {
136eba68f82SYendapally Reddy Dhananjaya Reddy 		if (priv->port_mask & BIT(i))
137eba68f82SYendapally Reddy Dhananjaya Reddy 			writel(0xff1003fc,
138eba68f82SYendapally Reddy Dhananjaya Reddy 			       hpriv->mmio + SATA_PORT_PCTRL6(port_ctrl));
139eba68f82SYendapally Reddy Dhananjaya Reddy 	}
140eba68f82SYendapally Reddy Dhananjaya Reddy }
141eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_phy_enable(struct brcm_ahci_priv * priv,int port)142eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
143eba68f82SYendapally Reddy Dhananjaya Reddy {
144eba68f82SYendapally Reddy Dhananjaya Reddy 	void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
145eba68f82SYendapally Reddy Dhananjaya Reddy 				(port * SATA_TOP_CTRL_PHY_OFFS);
146eba68f82SYendapally Reddy Dhananjaya Reddy 	void __iomem *p;
147eba68f82SYendapally Reddy Dhananjaya Reddy 	u32 reg;
148eba68f82SYendapally Reddy Dhananjaya Reddy 
149eba68f82SYendapally Reddy Dhananjaya Reddy 	if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
150eba68f82SYendapally Reddy Dhananjaya Reddy 		return;
151eba68f82SYendapally Reddy Dhananjaya Reddy 
152eba68f82SYendapally Reddy Dhananjaya Reddy 	/* clear PHY_DEFAULT_POWER_STATE */
153eba68f82SYendapally Reddy Dhananjaya Reddy 	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
154eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
155eba68f82SYendapally Reddy Dhananjaya Reddy 	reg &= ~SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
156eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
157eba68f82SYendapally Reddy Dhananjaya Reddy 
158eba68f82SYendapally Reddy Dhananjaya Reddy 	/* reset the PHY digital logic */
159eba68f82SYendapally Reddy Dhananjaya Reddy 	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
160eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
161eba68f82SYendapally Reddy Dhananjaya Reddy 	reg &= ~(SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
162eba68f82SYendapally Reddy Dhananjaya Reddy 		 SATA_TOP_CTRL_2_SW_RST_RX);
163eba68f82SYendapally Reddy Dhananjaya Reddy 	reg |= SATA_TOP_CTRL_2_SW_RST_TX;
164eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
165eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
166eba68f82SYendapally Reddy Dhananjaya Reddy 	reg |= SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
167eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
168eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
169eba68f82SYendapally Reddy Dhananjaya Reddy 	reg &= ~SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
170eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
171eba68f82SYendapally Reddy Dhananjaya Reddy 	(void)brcm_sata_readreg(p);
172eba68f82SYendapally Reddy Dhananjaya Reddy }
173eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_phy_disable(struct brcm_ahci_priv * priv,int port)174eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port)
175eba68f82SYendapally Reddy Dhananjaya Reddy {
176eba68f82SYendapally Reddy Dhananjaya Reddy 	void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
177eba68f82SYendapally Reddy Dhananjaya Reddy 				(port * SATA_TOP_CTRL_PHY_OFFS);
178eba68f82SYendapally Reddy Dhananjaya Reddy 	void __iomem *p;
179eba68f82SYendapally Reddy Dhananjaya Reddy 	u32 reg;
180eba68f82SYendapally Reddy Dhananjaya Reddy 
181eba68f82SYendapally Reddy Dhananjaya Reddy 	if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
182eba68f82SYendapally Reddy Dhananjaya Reddy 		return;
183eba68f82SYendapally Reddy Dhananjaya Reddy 
184eba68f82SYendapally Reddy Dhananjaya Reddy 	/* power-off the PHY digital logic */
185eba68f82SYendapally Reddy Dhananjaya Reddy 	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
186eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
187eba68f82SYendapally Reddy Dhananjaya Reddy 	reg |= (SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
188eba68f82SYendapally Reddy Dhananjaya Reddy 		SATA_TOP_CTRL_2_SW_RST_RX | SATA_TOP_CTRL_2_SW_RST_TX |
189eba68f82SYendapally Reddy Dhananjaya Reddy 		SATA_TOP_CTRL_2_PHY_GLOBAL_RESET);
190eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
191eba68f82SYendapally Reddy Dhananjaya Reddy 
192eba68f82SYendapally Reddy Dhananjaya Reddy 	/* set PHY_DEFAULT_POWER_STATE */
193eba68f82SYendapally Reddy Dhananjaya Reddy 	p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
194eba68f82SYendapally Reddy Dhananjaya Reddy 	reg = brcm_sata_readreg(p);
195eba68f82SYendapally Reddy Dhananjaya Reddy 	reg |= SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
196eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(reg, p);
197eba68f82SYendapally Reddy Dhananjaya Reddy }
198eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_phys_enable(struct brcm_ahci_priv * priv)199eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_phys_enable(struct brcm_ahci_priv *priv)
200eba68f82SYendapally Reddy Dhananjaya Reddy {
201eba68f82SYendapally Reddy Dhananjaya Reddy 	int i;
202eba68f82SYendapally Reddy Dhananjaya Reddy 
203eba68f82SYendapally Reddy Dhananjaya Reddy 	for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
204eba68f82SYendapally Reddy Dhananjaya Reddy 		if (priv->port_mask & BIT(i))
205eba68f82SYendapally Reddy Dhananjaya Reddy 			brcm_sata_phy_enable(priv, i);
206eba68f82SYendapally Reddy Dhananjaya Reddy }
207eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_phys_disable(struct brcm_ahci_priv * priv)208eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv)
209eba68f82SYendapally Reddy Dhananjaya Reddy {
210eba68f82SYendapally Reddy Dhananjaya Reddy 	int i;
211eba68f82SYendapally Reddy Dhananjaya Reddy 
212eba68f82SYendapally Reddy Dhananjaya Reddy 	for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
213eba68f82SYendapally Reddy Dhananjaya Reddy 		if (priv->port_mask & BIT(i))
214eba68f82SYendapally Reddy Dhananjaya Reddy 			brcm_sata_phy_disable(priv, i);
215eba68f82SYendapally Reddy Dhananjaya Reddy }
216eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_ahci_get_portmask(struct ahci_host_priv * hpriv,struct brcm_ahci_priv * priv)217c0cdf2acSFlorian Fainelli static u32 brcm_ahci_get_portmask(struct ahci_host_priv *hpriv,
218eba68f82SYendapally Reddy Dhananjaya Reddy 				  struct brcm_ahci_priv *priv)
219eba68f82SYendapally Reddy Dhananjaya Reddy {
220eba68f82SYendapally Reddy Dhananjaya Reddy 	u32 impl;
221eba68f82SYendapally Reddy Dhananjaya Reddy 
222c0cdf2acSFlorian Fainelli 	impl = readl(hpriv->mmio + HOST_PORTS_IMPL);
223eba68f82SYendapally Reddy Dhananjaya Reddy 
224eba68f82SYendapally Reddy Dhananjaya Reddy 	if (fls(impl) > SATA_TOP_MAX_PHYS)
225eba68f82SYendapally Reddy Dhananjaya Reddy 		dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n",
226eba68f82SYendapally Reddy Dhananjaya Reddy 			 impl);
227eba68f82SYendapally Reddy Dhananjaya Reddy 	else if (!impl)
228eba68f82SYendapally Reddy Dhananjaya Reddy 		dev_info(priv->dev, "no ports found\n");
229eba68f82SYendapally Reddy Dhananjaya Reddy 
230eba68f82SYendapally Reddy Dhananjaya Reddy 	return impl;
231eba68f82SYendapally Reddy Dhananjaya Reddy }
232eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_sata_init(struct brcm_ahci_priv * priv)233eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_sata_init(struct brcm_ahci_priv *priv)
234eba68f82SYendapally Reddy Dhananjaya Reddy {
2353ee2e6dcSYendapally Reddy Dhananjaya Reddy 	void __iomem *ctrl = priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL;
23636fffd6aSFlorian Fainelli 	u32 data;
2373ee2e6dcSYendapally Reddy Dhananjaya Reddy 
238eba68f82SYendapally Reddy Dhananjaya Reddy 	/* Configure endianness */
23936fffd6aSFlorian Fainelli 	data = brcm_sata_readreg(ctrl);
24036fffd6aSFlorian Fainelli 	data &= ~BUS_CTRL_ENDIAN_CONF_MASK;
24136fffd6aSFlorian Fainelli 	if (priv->version == BRCM_SATA_NSP)
24236fffd6aSFlorian Fainelli 		data |= BUS_CTRL_ENDIAN_NSP_CONF;
24336fffd6aSFlorian Fainelli 	else
24436fffd6aSFlorian Fainelli 		data |= BUS_CTRL_ENDIAN_CONF;
2453ee2e6dcSYendapally Reddy Dhananjaya Reddy 	brcm_sata_writereg(data, ctrl);
246eba68f82SYendapally Reddy Dhananjaya Reddy }
247eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_ahci_read_id(struct ata_device * dev,struct ata_taskfile * tf,__le16 * id)248eb73390aSFlorian Fainelli static unsigned int brcm_ahci_read_id(struct ata_device *dev,
2490561e514SDamien Le Moal 				      struct ata_taskfile *tf, __le16 *id)
250eb73390aSFlorian Fainelli {
251eb73390aSFlorian Fainelli 	struct ata_port *ap = dev->link->ap;
252eb73390aSFlorian Fainelli 	struct ata_host *host = ap->host;
253eb73390aSFlorian Fainelli 	struct ahci_host_priv *hpriv = host->private_data;
254eb73390aSFlorian Fainelli 	struct brcm_ahci_priv *priv = hpriv->plat_data;
255eb73390aSFlorian Fainelli 	void __iomem *mmio = hpriv->mmio;
256eb73390aSFlorian Fainelli 	unsigned int err_mask;
257eb73390aSFlorian Fainelli 	unsigned long flags;
258eb73390aSFlorian Fainelli 	int i, rc;
259eb73390aSFlorian Fainelli 	u32 ctl;
260eb73390aSFlorian Fainelli 
261eb73390aSFlorian Fainelli 	/* Try to read the device ID and, if this fails, proceed with the
262eb73390aSFlorian Fainelli 	 * recovery sequence below
263eb73390aSFlorian Fainelli 	 */
264eb73390aSFlorian Fainelli 	err_mask = ata_do_dev_read_id(dev, tf, id);
265eb73390aSFlorian Fainelli 	if (likely(!err_mask))
266eb73390aSFlorian Fainelli 		return err_mask;
267eb73390aSFlorian Fainelli 
268eb73390aSFlorian Fainelli 	/* Disable host interrupts */
269eb73390aSFlorian Fainelli 	spin_lock_irqsave(&host->lock, flags);
270eb73390aSFlorian Fainelli 	ctl = readl(mmio + HOST_CTL);
271eb73390aSFlorian Fainelli 	ctl &= ~HOST_IRQ_EN;
272eb73390aSFlorian Fainelli 	writel(ctl, mmio + HOST_CTL);
273eb73390aSFlorian Fainelli 	readl(mmio + HOST_CTL); /* flush */
274eb73390aSFlorian Fainelli 	spin_unlock_irqrestore(&host->lock, flags);
275eb73390aSFlorian Fainelli 
276eb73390aSFlorian Fainelli 	/* Perform the SATA PHY reset sequence */
277eb73390aSFlorian Fainelli 	brcm_sata_phy_disable(priv, ap->port_no);
278eb73390aSFlorian Fainelli 
279bf0e5013SFlorian Fainelli 	/* Reset the SATA clock */
280bf0e5013SFlorian Fainelli 	ahci_platform_disable_clks(hpriv);
281bf0e5013SFlorian Fainelli 	msleep(10);
282bf0e5013SFlorian Fainelli 
283bf0e5013SFlorian Fainelli 	ahci_platform_enable_clks(hpriv);
284bf0e5013SFlorian Fainelli 	msleep(10);
285bf0e5013SFlorian Fainelli 
286eb73390aSFlorian Fainelli 	/* Bring the PHY back on */
287eb73390aSFlorian Fainelli 	brcm_sata_phy_enable(priv, ap->port_no);
288eb73390aSFlorian Fainelli 
289eb73390aSFlorian Fainelli 	/* Re-initialize and calibrate the PHY */
290eb73390aSFlorian Fainelli 	for (i = 0; i < hpriv->nports; i++) {
291eb73390aSFlorian Fainelli 		rc = phy_init(hpriv->phys[i]);
292eb73390aSFlorian Fainelli 		if (rc)
293eb73390aSFlorian Fainelli 			goto disable_phys;
294eb73390aSFlorian Fainelli 
295eb73390aSFlorian Fainelli 		rc = phy_calibrate(hpriv->phys[i]);
296eb73390aSFlorian Fainelli 		if (rc) {
297eb73390aSFlorian Fainelli 			phy_exit(hpriv->phys[i]);
298eb73390aSFlorian Fainelli 			goto disable_phys;
299eb73390aSFlorian Fainelli 		}
300eb73390aSFlorian Fainelli 	}
301eb73390aSFlorian Fainelli 
302eb73390aSFlorian Fainelli 	/* Re-enable host interrupts */
303eb73390aSFlorian Fainelli 	spin_lock_irqsave(&host->lock, flags);
304eb73390aSFlorian Fainelli 	ctl = readl(mmio + HOST_CTL);
305eb73390aSFlorian Fainelli 	ctl |= HOST_IRQ_EN;
306eb73390aSFlorian Fainelli 	writel(ctl, mmio + HOST_CTL);
307eb73390aSFlorian Fainelli 	readl(mmio + HOST_CTL); /* flush */
308eb73390aSFlorian Fainelli 	spin_unlock_irqrestore(&host->lock, flags);
309eb73390aSFlorian Fainelli 
310eb73390aSFlorian Fainelli 	return ata_do_dev_read_id(dev, tf, id);
311eb73390aSFlorian Fainelli 
312eb73390aSFlorian Fainelli disable_phys:
313eb73390aSFlorian Fainelli 	while (--i >= 0) {
314eb73390aSFlorian Fainelli 		phy_power_off(hpriv->phys[i]);
315eb73390aSFlorian Fainelli 		phy_exit(hpriv->phys[i]);
316eb73390aSFlorian Fainelli 	}
317eb73390aSFlorian Fainelli 
318eb73390aSFlorian Fainelli 	return AC_ERR_OTHER;
319eb73390aSFlorian Fainelli }
320eb73390aSFlorian Fainelli 
brcm_ahci_host_stop(struct ata_host * host)321eb73390aSFlorian Fainelli static void brcm_ahci_host_stop(struct ata_host *host)
322eb73390aSFlorian Fainelli {
323eb73390aSFlorian Fainelli 	struct ahci_host_priv *hpriv = host->private_data;
324eb73390aSFlorian Fainelli 
325eb73390aSFlorian Fainelli 	ahci_platform_disable_resources(hpriv);
326eb73390aSFlorian Fainelli }
327eb73390aSFlorian Fainelli 
328eb73390aSFlorian Fainelli static struct ata_port_operations ahci_brcm_platform_ops = {
329eb73390aSFlorian Fainelli 	.inherits	= &ahci_ops,
330eb73390aSFlorian Fainelli 	.host_stop	= brcm_ahci_host_stop,
331eb73390aSFlorian Fainelli 	.read_id	= brcm_ahci_read_id,
332eb73390aSFlorian Fainelli };
333eb73390aSFlorian Fainelli 
334eb73390aSFlorian Fainelli static const struct ata_port_info ahci_brcm_port_info = {
335eb73390aSFlorian Fainelli 	.flags		= AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
336b9ba367cSPaul Menzel 	.link_flags	= ATA_LFLAG_NO_DEBOUNCE_DELAY,
337eb73390aSFlorian Fainelli 	.pio_mask	= ATA_PIO4,
338eb73390aSFlorian Fainelli 	.udma_mask	= ATA_UDMA6,
339eb73390aSFlorian Fainelli 	.port_ops	= &ahci_brcm_platform_ops,
340eb73390aSFlorian Fainelli };
341eb73390aSFlorian Fainelli 
brcm_ahci_suspend(struct device * dev)342eba68f82SYendapally Reddy Dhananjaya Reddy static int brcm_ahci_suspend(struct device *dev)
343eba68f82SYendapally Reddy Dhananjaya Reddy {
344eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ata_host *host = dev_get_drvdata(dev);
345eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ahci_host_priv *hpriv = host->private_data;
346eba68f82SYendapally Reddy Dhananjaya Reddy 	struct brcm_ahci_priv *priv = hpriv->plat_data;
3473c696ac4SFlorian Fainelli 	int ret;
348eba68f82SYendapally Reddy Dhananjaya Reddy 
349eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_phys_disable(priv);
350c0cdf2acSFlorian Fainelli 
351ed87ad19SArnd Bergmann 	if (IS_ENABLED(CONFIG_PM_SLEEP))
3523c696ac4SFlorian Fainelli 		ret = ahci_platform_suspend(dev);
353ed87ad19SArnd Bergmann 	else
354ed87ad19SArnd Bergmann 		ret = 0;
3553c696ac4SFlorian Fainelli 
356e8d6f9e5SJim Quinlan 	reset_control_assert(priv->rcdev_ahci);
357e8d6f9e5SJim Quinlan 	reset_control_rearm(priv->rcdev_rescal);
3583c696ac4SFlorian Fainelli 
3593c696ac4SFlorian Fainelli 	return ret;
360eba68f82SYendapally Reddy Dhananjaya Reddy }
361eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_ahci_resume(struct device * dev)362ed87ad19SArnd Bergmann static int __maybe_unused brcm_ahci_resume(struct device *dev)
363eba68f82SYendapally Reddy Dhananjaya Reddy {
364eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ata_host *host = dev_get_drvdata(dev);
365eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ahci_host_priv *hpriv = host->private_data;
366eba68f82SYendapally Reddy Dhananjaya Reddy 	struct brcm_ahci_priv *priv = hpriv->plat_data;
3673c696ac4SFlorian Fainelli 	int ret = 0;
3683c696ac4SFlorian Fainelli 
369e8d6f9e5SJim Quinlan 	ret = reset_control_deassert(priv->rcdev_ahci);
370e8d6f9e5SJim Quinlan 	if (ret)
371e8d6f9e5SJim Quinlan 		return ret;
372e8d6f9e5SJim Quinlan 	ret = reset_control_reset(priv->rcdev_rescal);
3733c696ac4SFlorian Fainelli 	if (ret)
3743c696ac4SFlorian Fainelli 		return ret;
375c0cdf2acSFlorian Fainelli 
376c0cdf2acSFlorian Fainelli 	/* Make sure clocks are turned on before re-configuration */
377c0cdf2acSFlorian Fainelli 	ret = ahci_platform_enable_clks(hpriv);
378c0cdf2acSFlorian Fainelli 	if (ret)
379c0cdf2acSFlorian Fainelli 		return ret;
380eba68f82SYendapally Reddy Dhananjaya Reddy 
38110340f8dSFlorian Fainelli 	ret = ahci_platform_enable_regulators(hpriv);
38210340f8dSFlorian Fainelli 	if (ret)
38310340f8dSFlorian Fainelli 		goto out_disable_clks;
38410340f8dSFlorian Fainelli 
385eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_init(priv);
386eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_phys_enable(priv);
387eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_alpm_init(hpriv);
388c0cdf2acSFlorian Fainelli 
389c0cdf2acSFlorian Fainelli 	/* Since we had to enable clocks earlier on, we cannot use
390c0cdf2acSFlorian Fainelli 	 * ahci_platform_resume() as-is since a second call to
391c0cdf2acSFlorian Fainelli 	 * ahci_platform_enable_resources() would bump up the resources
392c0cdf2acSFlorian Fainelli 	 * (regulators, clocks, PHYs) count artificially so we copy the part
393c0cdf2acSFlorian Fainelli 	 * after ahci_platform_enable_resources().
394c0cdf2acSFlorian Fainelli 	 */
395c0cdf2acSFlorian Fainelli 	ret = ahci_platform_enable_phys(hpriv);
396c0cdf2acSFlorian Fainelli 	if (ret)
397c0cdf2acSFlorian Fainelli 		goto out_disable_phys;
398c0cdf2acSFlorian Fainelli 
399c0cdf2acSFlorian Fainelli 	ret = ahci_platform_resume_host(dev);
400c0cdf2acSFlorian Fainelli 	if (ret)
401c0cdf2acSFlorian Fainelli 		goto out_disable_platform_phys;
402c0cdf2acSFlorian Fainelli 
403c0cdf2acSFlorian Fainelli 	/* We resumed so update PM runtime state */
404c0cdf2acSFlorian Fainelli 	pm_runtime_disable(dev);
405c0cdf2acSFlorian Fainelli 	pm_runtime_set_active(dev);
406c0cdf2acSFlorian Fainelli 	pm_runtime_enable(dev);
407c0cdf2acSFlorian Fainelli 
408c0cdf2acSFlorian Fainelli 	return 0;
409c0cdf2acSFlorian Fainelli 
410c0cdf2acSFlorian Fainelli out_disable_platform_phys:
411c0cdf2acSFlorian Fainelli 	ahci_platform_disable_phys(hpriv);
412c0cdf2acSFlorian Fainelli out_disable_phys:
413c0cdf2acSFlorian Fainelli 	brcm_sata_phys_disable(priv);
41410340f8dSFlorian Fainelli 	ahci_platform_disable_regulators(hpriv);
41510340f8dSFlorian Fainelli out_disable_clks:
416c0cdf2acSFlorian Fainelli 	ahci_platform_disable_clks(hpriv);
417c0cdf2acSFlorian Fainelli 	return ret;
418eba68f82SYendapally Reddy Dhananjaya Reddy }
419eba68f82SYendapally Reddy Dhananjaya Reddy 
420*25df73d9SBart Van Assche static const struct scsi_host_template ahci_platform_sht = {
421eba68f82SYendapally Reddy Dhananjaya Reddy 	AHCI_SHT(DRV_NAME),
422eba68f82SYendapally Reddy Dhananjaya Reddy };
423eba68f82SYendapally Reddy Dhananjaya Reddy 
4243ee2e6dcSYendapally Reddy Dhananjaya Reddy static const struct of_device_id ahci_of_match[] = {
4253ee2e6dcSYendapally Reddy Dhananjaya Reddy 	{.compatible = "brcm,bcm7425-ahci", .data = (void *)BRCM_SATA_BCM7425},
4263ee2e6dcSYendapally Reddy Dhananjaya Reddy 	{.compatible = "brcm,bcm7445-ahci", .data = (void *)BRCM_SATA_BCM7445},
427fb8506f1SFlorian Fainelli 	{.compatible = "brcm,bcm63138-ahci", .data = (void *)BRCM_SATA_BCM7445},
4283ee2e6dcSYendapally Reddy Dhananjaya Reddy 	{.compatible = "brcm,bcm-nsp-ahci", .data = (void *)BRCM_SATA_NSP},
429c345ec6aSFlorian Fainelli 	{.compatible = "brcm,bcm7216-ahci", .data = (void *)BRCM_SATA_BCM7216},
4305e776d7bSGeert Uytterhoeven 	{ /* sentinel */ }
4313ee2e6dcSYendapally Reddy Dhananjaya Reddy };
4323ee2e6dcSYendapally Reddy Dhananjaya Reddy MODULE_DEVICE_TABLE(of, ahci_of_match);
4333ee2e6dcSYendapally Reddy Dhananjaya Reddy 
brcm_ahci_probe(struct platform_device * pdev)434eba68f82SYendapally Reddy Dhananjaya Reddy static int brcm_ahci_probe(struct platform_device *pdev)
435eba68f82SYendapally Reddy Dhananjaya Reddy {
4363ee2e6dcSYendapally Reddy Dhananjaya Reddy 	const struct of_device_id *of_id;
437eba68f82SYendapally Reddy Dhananjaya Reddy 	struct device *dev = &pdev->dev;
438eba68f82SYendapally Reddy Dhananjaya Reddy 	struct brcm_ahci_priv *priv;
439eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ahci_host_priv *hpriv;
440eba68f82SYendapally Reddy Dhananjaya Reddy 	struct resource *res;
441eba68f82SYendapally Reddy Dhananjaya Reddy 	int ret;
442eba68f82SYendapally Reddy Dhananjaya Reddy 
443eba68f82SYendapally Reddy Dhananjaya Reddy 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
444eba68f82SYendapally Reddy Dhananjaya Reddy 	if (!priv)
445eba68f82SYendapally Reddy Dhananjaya Reddy 		return -ENOMEM;
4463ee2e6dcSYendapally Reddy Dhananjaya Reddy 
4473ee2e6dcSYendapally Reddy Dhananjaya Reddy 	of_id = of_match_node(ahci_of_match, pdev->dev.of_node);
4483ee2e6dcSYendapally Reddy Dhananjaya Reddy 	if (!of_id)
4493ee2e6dcSYendapally Reddy Dhananjaya Reddy 		return -ENODEV;
4503ee2e6dcSYendapally Reddy Dhananjaya Reddy 
4517d7b0c85SDamien Le Moal 	priv->version = (unsigned long)of_id->data;
452eba68f82SYendapally Reddy Dhananjaya Reddy 	priv->dev = dev;
453eba68f82SYendapally Reddy Dhananjaya Reddy 
454eba68f82SYendapally Reddy Dhananjaya Reddy 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "top-ctrl");
455eba68f82SYendapally Reddy Dhananjaya Reddy 	priv->top_ctrl = devm_ioremap_resource(dev, res);
456eba68f82SYendapally Reddy Dhananjaya Reddy 	if (IS_ERR(priv->top_ctrl))
457eba68f82SYendapally Reddy Dhananjaya Reddy 		return PTR_ERR(priv->top_ctrl);
458eba68f82SYendapally Reddy Dhananjaya Reddy 
459e8d6f9e5SJim Quinlan 	if (priv->version == BRCM_SATA_BCM7216) {
460e8d6f9e5SJim Quinlan 		priv->rcdev_rescal = devm_reset_control_get_optional_shared(
461e8d6f9e5SJim Quinlan 			&pdev->dev, "rescal");
462e8d6f9e5SJim Quinlan 		if (IS_ERR(priv->rcdev_rescal))
463e8d6f9e5SJim Quinlan 			return PTR_ERR(priv->rcdev_rescal);
464e8d6f9e5SJim Quinlan 	}
465e8d6f9e5SJim Quinlan 	priv->rcdev_ahci = devm_reset_control_get_optional(&pdev->dev, "ahci");
466e8d6f9e5SJim Quinlan 	if (IS_ERR(priv->rcdev_ahci))
467e8d6f9e5SJim Quinlan 		return PTR_ERR(priv->rcdev_ahci);
4686fedae3cSArnd Bergmann 
469c0cdf2acSFlorian Fainelli 	hpriv = ahci_platform_get_resources(pdev, 0);
4701a0600d1SFlorian Fainelli 	if (IS_ERR(hpriv))
4711a0600d1SFlorian Fainelli 		return PTR_ERR(hpriv);
472c0cdf2acSFlorian Fainelli 
4731a3d78cbSFlorian Fainelli 	hpriv->plat_data = priv;
4741a3d78cbSFlorian Fainelli 	hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP | AHCI_HFLAG_NO_WRITE_TO_RO;
4751a3d78cbSFlorian Fainelli 
4761a3d78cbSFlorian Fainelli 	switch (priv->version) {
4771a3d78cbSFlorian Fainelli 	case BRCM_SATA_BCM7425:
4781a3d78cbSFlorian Fainelli 		hpriv->flags |= AHCI_HFLAG_DELAY_ENGINE;
479df561f66SGustavo A. R. Silva 		fallthrough;
4801a3d78cbSFlorian Fainelli 	case BRCM_SATA_NSP:
4811a3d78cbSFlorian Fainelli 		hpriv->flags |= AHCI_HFLAG_NO_NCQ;
4821a3d78cbSFlorian Fainelli 		priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
4831a3d78cbSFlorian Fainelli 		break;
4841a3d78cbSFlorian Fainelli 	default:
4851a3d78cbSFlorian Fainelli 		break;
4861a3d78cbSFlorian Fainelli 	}
4871a3d78cbSFlorian Fainelli 
488e8d6f9e5SJim Quinlan 	ret = reset_control_reset(priv->rcdev_rescal);
489e8d6f9e5SJim Quinlan 	if (ret)
490e8d6f9e5SJim Quinlan 		return ret;
491e8d6f9e5SJim Quinlan 	ret = reset_control_deassert(priv->rcdev_ahci);
4921a0600d1SFlorian Fainelli 	if (ret)
4931a0600d1SFlorian Fainelli 		return ret;
4941a0600d1SFlorian Fainelli 
495c0cdf2acSFlorian Fainelli 	ret = ahci_platform_enable_clks(hpriv);
496c0cdf2acSFlorian Fainelli 	if (ret)
497c0cdf2acSFlorian Fainelli 		goto out_reset;
498c0cdf2acSFlorian Fainelli 
49910340f8dSFlorian Fainelli 	ret = ahci_platform_enable_regulators(hpriv);
50010340f8dSFlorian Fainelli 	if (ret)
50110340f8dSFlorian Fainelli 		goto out_disable_clks;
50210340f8dSFlorian Fainelli 
503c0cdf2acSFlorian Fainelli 	/* Must be first so as to configure endianness including that
504c0cdf2acSFlorian Fainelli 	 * of the standard AHCI register space.
505c0cdf2acSFlorian Fainelli 	 */
506eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_init(priv);
507eba68f82SYendapally Reddy Dhananjaya Reddy 
508c0cdf2acSFlorian Fainelli 	/* Initializes priv->port_mask which is used below */
509c0cdf2acSFlorian Fainelli 	priv->port_mask = brcm_ahci_get_portmask(hpriv, priv);
510c0cdf2acSFlorian Fainelli 	if (!priv->port_mask) {
511c0cdf2acSFlorian Fainelli 		ret = -ENODEV;
51210340f8dSFlorian Fainelli 		goto out_disable_regulators;
513c0cdf2acSFlorian Fainelli 	}
514eba68f82SYendapally Reddy Dhananjaya Reddy 
515c0cdf2acSFlorian Fainelli 	/* Must be done before ahci_platform_enable_phys() */
516eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_phys_enable(priv);
517eba68f82SYendapally Reddy Dhananjaya Reddy 
518eba68f82SYendapally Reddy Dhananjaya Reddy 	brcm_sata_alpm_init(hpriv);
519eba68f82SYendapally Reddy Dhananjaya Reddy 
520c0cdf2acSFlorian Fainelli 	ret = ahci_platform_enable_phys(hpriv);
521c0cdf2acSFlorian Fainelli 	if (ret)
522c0cdf2acSFlorian Fainelli 		goto out_disable_phys;
523c0cdf2acSFlorian Fainelli 
524eba68f82SYendapally Reddy Dhananjaya Reddy 	ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
525eba68f82SYendapally Reddy Dhananjaya Reddy 				      &ahci_platform_sht);
526eba68f82SYendapally Reddy Dhananjaya Reddy 	if (ret)
527c0cdf2acSFlorian Fainelli 		goto out_disable_platform_phys;
528eba68f82SYendapally Reddy Dhananjaya Reddy 
529eba68f82SYendapally Reddy Dhananjaya Reddy 	dev_info(dev, "Broadcom AHCI SATA3 registered\n");
530eba68f82SYendapally Reddy Dhananjaya Reddy 
531eba68f82SYendapally Reddy Dhananjaya Reddy 	return 0;
532c0cdf2acSFlorian Fainelli 
533c0cdf2acSFlorian Fainelli out_disable_platform_phys:
534c0cdf2acSFlorian Fainelli 	ahci_platform_disable_phys(hpriv);
535c0cdf2acSFlorian Fainelli out_disable_phys:
536c0cdf2acSFlorian Fainelli 	brcm_sata_phys_disable(priv);
53710340f8dSFlorian Fainelli out_disable_regulators:
53810340f8dSFlorian Fainelli 	ahci_platform_disable_regulators(hpriv);
539c0cdf2acSFlorian Fainelli out_disable_clks:
540c0cdf2acSFlorian Fainelli 	ahci_platform_disable_clks(hpriv);
541c0cdf2acSFlorian Fainelli out_reset:
542e8d6f9e5SJim Quinlan 	reset_control_assert(priv->rcdev_ahci);
543e8d6f9e5SJim Quinlan 	reset_control_rearm(priv->rcdev_rescal);
544c0cdf2acSFlorian Fainelli 	return ret;
545eba68f82SYendapally Reddy Dhananjaya Reddy }
546eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_ahci_remove(struct platform_device * pdev)547eba68f82SYendapally Reddy Dhananjaya Reddy static void brcm_ahci_remove(struct platform_device *pdev)
548eba68f82SYendapally Reddy Dhananjaya Reddy {
549eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ata_host *host = dev_get_drvdata(&pdev->dev);
550eba68f82SYendapally Reddy Dhananjaya Reddy 	struct ahci_host_priv *hpriv = host->private_data;
551eba68f82SYendapally Reddy Dhananjaya Reddy 	struct brcm_ahci_priv *priv = hpriv->plat_data;
552eba68f82SYendapally Reddy Dhananjaya Reddy 
553c0cdf2acSFlorian Fainelli 	brcm_sata_phys_disable(priv);
554c0cdf2acSFlorian Fainelli 
555ec194bdbSMinghao Chi 	ata_platform_remove_one(pdev);
556eba68f82SYendapally Reddy Dhananjaya Reddy }
557eba68f82SYendapally Reddy Dhananjaya Reddy 
brcm_ahci_shutdown(struct platform_device * pdev)5587de9b168SFlorian Fainelli static void brcm_ahci_shutdown(struct platform_device *pdev)
5597de9b168SFlorian Fainelli {
5607de9b168SFlorian Fainelli 	int ret;
5617de9b168SFlorian Fainelli 
5627de9b168SFlorian Fainelli 	/* All resources releasing happens via devres, but our device, unlike a
5637de9b168SFlorian Fainelli 	 * proper remove is not disappearing, therefore using
5647de9b168SFlorian Fainelli 	 * brcm_ahci_suspend() here which does explicit power management is
5657de9b168SFlorian Fainelli 	 * appropriate.
5667de9b168SFlorian Fainelli 	 */
5677de9b168SFlorian Fainelli 	ret = brcm_ahci_suspend(&pdev->dev);
5687de9b168SFlorian Fainelli 	if (ret)
5697de9b168SFlorian Fainelli 		dev_err(&pdev->dev, "failed to shutdown\n");
5707de9b168SFlorian Fainelli }
5717de9b168SFlorian Fainelli 
572eba68f82SYendapally Reddy Dhananjaya Reddy static SIMPLE_DEV_PM_OPS(ahci_brcm_pm_ops, brcm_ahci_suspend, brcm_ahci_resume);
573eba68f82SYendapally Reddy Dhananjaya Reddy 
574eba68f82SYendapally Reddy Dhananjaya Reddy static struct platform_driver brcm_ahci_driver = {
575eba68f82SYendapally Reddy Dhananjaya Reddy 	.probe = brcm_ahci_probe,
576eba68f82SYendapally Reddy Dhananjaya Reddy 	.remove_new = brcm_ahci_remove,
5777de9b168SFlorian Fainelli 	.shutdown = brcm_ahci_shutdown,
578eba68f82SYendapally Reddy Dhananjaya Reddy 	.driver = {
579eba68f82SYendapally Reddy Dhananjaya Reddy 		.name = DRV_NAME,
580eba68f82SYendapally Reddy Dhananjaya Reddy 		.of_match_table = ahci_of_match,
581eba68f82SYendapally Reddy Dhananjaya Reddy 		.pm = &ahci_brcm_pm_ops,
582eba68f82SYendapally Reddy Dhananjaya Reddy 	},
583eba68f82SYendapally Reddy Dhananjaya Reddy };
584eba68f82SYendapally Reddy Dhananjaya Reddy module_platform_driver(brcm_ahci_driver);
585eba68f82SYendapally Reddy Dhananjaya Reddy 
586eba68f82SYendapally Reddy Dhananjaya Reddy MODULE_DESCRIPTION("Broadcom SATA3 AHCI Controller Driver");
587eba68f82SYendapally Reddy Dhananjaya Reddy MODULE_AUTHOR("Brian Norris");
588eba68f82SYendapally Reddy Dhananjaya Reddy MODULE_LICENSE("GPL");
589eba68f82SYendapally Reddy Dhananjaya Reddy MODULE_ALIAS("platform:sata-brcmstb");
590