xref: /OK3568_Linux_fs/kernel/drivers/ata/ahci_st.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2012 STMicroelectronics Limited
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Authors: Francesco Virlinzi <francesco.virlinzi@st.com>
6*4882a593Smuzhiyun  *	    Alexandre Torgue <alexandre.torgue@st.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/export.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/clk.h>
14*4882a593Smuzhiyun #include <linux/of.h>
15*4882a593Smuzhiyun #include <linux/ahci_platform.h>
16*4882a593Smuzhiyun #include <linux/libata.h>
17*4882a593Smuzhiyun #include <linux/reset.h>
18*4882a593Smuzhiyun #include <linux/io.h>
19*4882a593Smuzhiyun #include <linux/dma-mapping.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "ahci.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define DRV_NAME  "st_ahci"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define ST_AHCI_OOBR			0xbc
26*4882a593Smuzhiyun #define ST_AHCI_OOBR_WE			BIT(31)
27*4882a593Smuzhiyun #define ST_AHCI_OOBR_CWMIN_SHIFT	24
28*4882a593Smuzhiyun #define ST_AHCI_OOBR_CWMAX_SHIFT	16
29*4882a593Smuzhiyun #define ST_AHCI_OOBR_CIMIN_SHIFT	8
30*4882a593Smuzhiyun #define ST_AHCI_OOBR_CIMAX_SHIFT	0
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun struct st_ahci_drv_data {
33*4882a593Smuzhiyun 	struct platform_device *ahci;
34*4882a593Smuzhiyun 	struct reset_control *pwr;
35*4882a593Smuzhiyun 	struct reset_control *sw_rst;
36*4882a593Smuzhiyun 	struct reset_control *pwr_rst;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
st_ahci_configure_oob(void __iomem * mmio)39*4882a593Smuzhiyun static void st_ahci_configure_oob(void __iomem *mmio)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	unsigned long old_val, new_val;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) |
44*4882a593Smuzhiyun 		  (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) |
45*4882a593Smuzhiyun 		  (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) |
46*4882a593Smuzhiyun 		  (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT);
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	old_val = readl(mmio + ST_AHCI_OOBR);
49*4882a593Smuzhiyun 	writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
50*4882a593Smuzhiyun 	writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
51*4882a593Smuzhiyun 	writel(new_val, mmio + ST_AHCI_OOBR);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
st_ahci_deassert_resets(struct ahci_host_priv * hpriv,struct device * dev)54*4882a593Smuzhiyun static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
55*4882a593Smuzhiyun 				struct device *dev)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	struct st_ahci_drv_data *drv_data = hpriv->plat_data;
58*4882a593Smuzhiyun 	int err;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (drv_data->pwr) {
61*4882a593Smuzhiyun 		err = reset_control_deassert(drv_data->pwr);
62*4882a593Smuzhiyun 		if (err) {
63*4882a593Smuzhiyun 			dev_err(dev, "unable to bring out of pwrdwn\n");
64*4882a593Smuzhiyun 			return err;
65*4882a593Smuzhiyun 		}
66*4882a593Smuzhiyun 	}
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (drv_data->sw_rst) {
69*4882a593Smuzhiyun 		err = reset_control_deassert(drv_data->sw_rst);
70*4882a593Smuzhiyun 		if (err) {
71*4882a593Smuzhiyun 			dev_err(dev, "unable to bring out of sw-rst\n");
72*4882a593Smuzhiyun 			return err;
73*4882a593Smuzhiyun 		}
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	if (drv_data->pwr_rst) {
77*4882a593Smuzhiyun 		err = reset_control_deassert(drv_data->pwr_rst);
78*4882a593Smuzhiyun 		if (err) {
79*4882a593Smuzhiyun 			dev_err(dev, "unable to bring out of pwr-rst\n");
80*4882a593Smuzhiyun 			return err;
81*4882a593Smuzhiyun 		}
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
st_ahci_host_stop(struct ata_host * host)87*4882a593Smuzhiyun static void st_ahci_host_stop(struct ata_host *host)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	struct ahci_host_priv *hpriv = host->private_data;
90*4882a593Smuzhiyun 	struct st_ahci_drv_data *drv_data = hpriv->plat_data;
91*4882a593Smuzhiyun 	struct device *dev = host->dev;
92*4882a593Smuzhiyun 	int err;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (drv_data->pwr) {
95*4882a593Smuzhiyun 		err = reset_control_assert(drv_data->pwr);
96*4882a593Smuzhiyun 		if (err)
97*4882a593Smuzhiyun 			dev_err(dev, "unable to pwrdwn\n");
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	ahci_platform_disable_resources(hpriv);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
st_ahci_probe_resets(struct ahci_host_priv * hpriv,struct device * dev)103*4882a593Smuzhiyun static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
104*4882a593Smuzhiyun 				struct device *dev)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	struct st_ahci_drv_data *drv_data = hpriv->plat_data;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
109*4882a593Smuzhiyun 	if (IS_ERR(drv_data->pwr)) {
110*4882a593Smuzhiyun 		dev_info(dev, "power reset control not defined\n");
111*4882a593Smuzhiyun 		drv_data->pwr = NULL;
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
115*4882a593Smuzhiyun 	if (IS_ERR(drv_data->sw_rst)) {
116*4882a593Smuzhiyun 		dev_info(dev, "soft reset control not defined\n");
117*4882a593Smuzhiyun 		drv_data->sw_rst = NULL;
118*4882a593Smuzhiyun 	}
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
121*4882a593Smuzhiyun 	if (IS_ERR(drv_data->pwr_rst)) {
122*4882a593Smuzhiyun 		dev_dbg(dev, "power soft reset control not defined\n");
123*4882a593Smuzhiyun 		drv_data->pwr_rst = NULL;
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	return st_ahci_deassert_resets(hpriv, dev);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun static struct ata_port_operations st_ahci_port_ops = {
130*4882a593Smuzhiyun 	.inherits	= &ahci_platform_ops,
131*4882a593Smuzhiyun 	.host_stop	= st_ahci_host_stop,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun static const struct ata_port_info st_ahci_port_info = {
135*4882a593Smuzhiyun 	.flags          = AHCI_FLAG_COMMON,
136*4882a593Smuzhiyun 	.pio_mask       = ATA_PIO4,
137*4882a593Smuzhiyun 	.udma_mask      = ATA_UDMA6,
138*4882a593Smuzhiyun 	.port_ops       = &st_ahci_port_ops,
139*4882a593Smuzhiyun };
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun static struct scsi_host_template ahci_platform_sht = {
142*4882a593Smuzhiyun 	AHCI_SHT(DRV_NAME),
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun 
st_ahci_probe(struct platform_device * pdev)145*4882a593Smuzhiyun static int st_ahci_probe(struct platform_device *pdev)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
148*4882a593Smuzhiyun 	struct st_ahci_drv_data *drv_data;
149*4882a593Smuzhiyun 	struct ahci_host_priv *hpriv;
150*4882a593Smuzhiyun 	int err;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
153*4882a593Smuzhiyun 	if (!drv_data)
154*4882a593Smuzhiyun 		return -ENOMEM;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	hpriv = ahci_platform_get_resources(pdev, 0);
157*4882a593Smuzhiyun 	if (IS_ERR(hpriv))
158*4882a593Smuzhiyun 		return PTR_ERR(hpriv);
159*4882a593Smuzhiyun 	hpriv->plat_data = drv_data;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	err = st_ahci_probe_resets(hpriv, &pdev->dev);
162*4882a593Smuzhiyun 	if (err)
163*4882a593Smuzhiyun 		return err;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	err = ahci_platform_enable_resources(hpriv);
166*4882a593Smuzhiyun 	if (err)
167*4882a593Smuzhiyun 		return err;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	st_ahci_configure_oob(hpriv->mmio);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	of_property_read_u32(dev->of_node,
172*4882a593Smuzhiyun 			     "ports-implemented", &hpriv->force_port_map);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
175*4882a593Smuzhiyun 				      &ahci_platform_sht);
176*4882a593Smuzhiyun 	if (err) {
177*4882a593Smuzhiyun 		ahci_platform_disable_resources(hpriv);
178*4882a593Smuzhiyun 		return err;
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
st_ahci_suspend(struct device * dev)185*4882a593Smuzhiyun static int st_ahci_suspend(struct device *dev)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct ata_host *host = dev_get_drvdata(dev);
188*4882a593Smuzhiyun 	struct ahci_host_priv *hpriv = host->private_data;
189*4882a593Smuzhiyun 	struct st_ahci_drv_data *drv_data = hpriv->plat_data;
190*4882a593Smuzhiyun 	int err;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	err = ahci_platform_suspend_host(dev);
193*4882a593Smuzhiyun 	if (err)
194*4882a593Smuzhiyun 		return err;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	if (drv_data->pwr) {
197*4882a593Smuzhiyun 		err = reset_control_assert(drv_data->pwr);
198*4882a593Smuzhiyun 		if (err) {
199*4882a593Smuzhiyun 			dev_err(dev, "unable to pwrdwn");
200*4882a593Smuzhiyun 			return err;
201*4882a593Smuzhiyun 		}
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	ahci_platform_disable_resources(hpriv);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
st_ahci_resume(struct device * dev)209*4882a593Smuzhiyun static int st_ahci_resume(struct device *dev)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	struct ata_host *host = dev_get_drvdata(dev);
212*4882a593Smuzhiyun 	struct ahci_host_priv *hpriv = host->private_data;
213*4882a593Smuzhiyun 	int err;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	err = ahci_platform_enable_resources(hpriv);
216*4882a593Smuzhiyun 	if (err)
217*4882a593Smuzhiyun 		return err;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	err = st_ahci_deassert_resets(hpriv, dev);
220*4882a593Smuzhiyun 	if (err) {
221*4882a593Smuzhiyun 		ahci_platform_disable_resources(hpriv);
222*4882a593Smuzhiyun 		return err;
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	st_ahci_configure_oob(hpriv->mmio);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	return ahci_platform_resume_host(dev);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun #endif
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun static const struct of_device_id st_ahci_match[] = {
234*4882a593Smuzhiyun 	{ .compatible = "st,ahci", },
235*4882a593Smuzhiyun 	{},
236*4882a593Smuzhiyun };
237*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, st_ahci_match);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun static struct platform_driver st_ahci_driver = {
240*4882a593Smuzhiyun 	.driver = {
241*4882a593Smuzhiyun 		.name = DRV_NAME,
242*4882a593Smuzhiyun 		.pm = &st_ahci_pm_ops,
243*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(st_ahci_match),
244*4882a593Smuzhiyun 	},
245*4882a593Smuzhiyun 	.probe = st_ahci_probe,
246*4882a593Smuzhiyun 	.remove = ata_platform_remove_one,
247*4882a593Smuzhiyun };
248*4882a593Smuzhiyun module_platform_driver(st_ahci_driver);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
251*4882a593Smuzhiyun MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>");
252*4882a593Smuzhiyun MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver");
253*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
254