xref: /openbmc/linux/drivers/ata/ahci_st.c (revision 9a1e75e1)
176884cb2SLee Jones /*
276884cb2SLee Jones  * Copyright (C) 2012 STMicroelectronics Limited
376884cb2SLee Jones  *
476884cb2SLee Jones  * Authors: Francesco Virlinzi <francesco.virlinzi@st.com>
576884cb2SLee Jones  *	    Alexandre Torgue <alexandre.torgue@st.com>
676884cb2SLee Jones  *
776884cb2SLee Jones  * This program is free software; you can redistribute it and/or modify
876884cb2SLee Jones  * it under the terms of the GNU General Public License version 2 as
976884cb2SLee Jones  * published by the Free Software Foundation.
1076884cb2SLee Jones  */
1176884cb2SLee Jones 
1276884cb2SLee Jones #include <linux/init.h>
1376884cb2SLee Jones #include <linux/module.h>
1476884cb2SLee Jones #include <linux/export.h>
1576884cb2SLee Jones #include <linux/platform_device.h>
1676884cb2SLee Jones #include <linux/clk.h>
1776884cb2SLee Jones #include <linux/of.h>
1876884cb2SLee Jones #include <linux/ahci_platform.h>
1976884cb2SLee Jones #include <linux/libata.h>
2076884cb2SLee Jones #include <linux/reset.h>
2176884cb2SLee Jones #include <linux/io.h>
2276884cb2SLee Jones #include <linux/dma-mapping.h>
2376884cb2SLee Jones 
2476884cb2SLee Jones #include "ahci.h"
2576884cb2SLee Jones 
26018d5ef2SAkinobu Mita #define DRV_NAME  "st_ahci"
27018d5ef2SAkinobu Mita 
2876884cb2SLee Jones #define ST_AHCI_OOBR			0xbc
2976884cb2SLee Jones #define ST_AHCI_OOBR_WE			BIT(31)
3076884cb2SLee Jones #define ST_AHCI_OOBR_CWMIN_SHIFT	24
3176884cb2SLee Jones #define ST_AHCI_OOBR_CWMAX_SHIFT	16
3276884cb2SLee Jones #define ST_AHCI_OOBR_CIMIN_SHIFT	8
3376884cb2SLee Jones #define ST_AHCI_OOBR_CIMAX_SHIFT	0
3476884cb2SLee Jones 
3576884cb2SLee Jones struct st_ahci_drv_data {
3676884cb2SLee Jones 	struct platform_device *ahci;
3776884cb2SLee Jones 	struct reset_control *pwr;
3876884cb2SLee Jones 	struct reset_control *sw_rst;
3976884cb2SLee Jones 	struct reset_control *pwr_rst;
4076884cb2SLee Jones 	struct ahci_host_priv *hpriv;
4176884cb2SLee Jones };
4276884cb2SLee Jones 
4376884cb2SLee Jones static void st_ahci_configure_oob(void __iomem *mmio)
4476884cb2SLee Jones {
4576884cb2SLee Jones 	unsigned long old_val, new_val;
4676884cb2SLee Jones 
4776884cb2SLee Jones 	new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) |
4876884cb2SLee Jones 		  (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) |
4976884cb2SLee Jones 		  (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) |
5076884cb2SLee Jones 		  (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT);
5176884cb2SLee Jones 
5276884cb2SLee Jones 	old_val = readl(mmio + ST_AHCI_OOBR);
5376884cb2SLee Jones 	writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
5476884cb2SLee Jones 	writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
5576884cb2SLee Jones 	writel(new_val, mmio + ST_AHCI_OOBR);
5676884cb2SLee Jones }
5776884cb2SLee Jones 
5876884cb2SLee Jones static int st_ahci_deassert_resets(struct device *dev)
5976884cb2SLee Jones {
6076884cb2SLee Jones 	struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
6176884cb2SLee Jones 	int err;
6276884cb2SLee Jones 
6376884cb2SLee Jones 	if (drv_data->pwr) {
6476884cb2SLee Jones 		err = reset_control_deassert(drv_data->pwr);
6576884cb2SLee Jones 		if (err) {
6676884cb2SLee Jones 			dev_err(dev, "unable to bring out of pwrdwn\n");
6776884cb2SLee Jones 			return err;
6876884cb2SLee Jones 		}
6976884cb2SLee Jones 	}
7076884cb2SLee Jones 
7176884cb2SLee Jones 	if (drv_data->sw_rst) {
7276884cb2SLee Jones 		err = reset_control_deassert(drv_data->sw_rst);
7376884cb2SLee Jones 		if (err) {
7476884cb2SLee Jones 			dev_err(dev, "unable to bring out of sw-rst\n");
7576884cb2SLee Jones 			return err;
7676884cb2SLee Jones 		}
7776884cb2SLee Jones 	}
7876884cb2SLee Jones 
7976884cb2SLee Jones 	if (drv_data->pwr_rst) {
8076884cb2SLee Jones 		err = reset_control_deassert(drv_data->pwr_rst);
8176884cb2SLee Jones 		if (err) {
8276884cb2SLee Jones 			dev_err(dev, "unable to bring out of pwr-rst\n");
8376884cb2SLee Jones 			return err;
8476884cb2SLee Jones 		}
8576884cb2SLee Jones 	}
8676884cb2SLee Jones 
8776884cb2SLee Jones 	return 0;
8876884cb2SLee Jones }
8976884cb2SLee Jones 
90b032378bSBartlomiej Zolnierkiewicz static void st_ahci_host_stop(struct ata_host *host)
91a8237084SLee Jones {
92b032378bSBartlomiej Zolnierkiewicz 	struct ahci_host_priv *hpriv = host->private_data;
93b032378bSBartlomiej Zolnierkiewicz 	struct device *dev = host->dev;
94a8237084SLee Jones 	struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
95a8237084SLee Jones 	int err;
96a8237084SLee Jones 
97a8237084SLee Jones 	if (drv_data->pwr) {
98a8237084SLee Jones 		err = reset_control_assert(drv_data->pwr);
99a8237084SLee Jones 		if (err)
10033081b34SBartlomiej Zolnierkiewicz 			dev_err(dev, "unable to pwrdwn\n");
101a8237084SLee Jones 	}
102a8237084SLee Jones 
103a8237084SLee Jones 	ahci_platform_disable_resources(hpriv);
104a8237084SLee Jones }
105a8237084SLee Jones 
10676884cb2SLee Jones static int st_ahci_probe_resets(struct platform_device *pdev)
10776884cb2SLee Jones {
10876884cb2SLee Jones 	struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev);
10976884cb2SLee Jones 
11076884cb2SLee Jones 	drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn");
11176884cb2SLee Jones 	if (IS_ERR(drv_data->pwr)) {
11276884cb2SLee Jones 		dev_info(&pdev->dev, "power reset control not defined\n");
11376884cb2SLee Jones 		drv_data->pwr = NULL;
11476884cb2SLee Jones 	}
11576884cb2SLee Jones 
11676884cb2SLee Jones 	drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst");
11776884cb2SLee Jones 	if (IS_ERR(drv_data->sw_rst)) {
11876884cb2SLee Jones 		dev_info(&pdev->dev, "soft reset control not defined\n");
11976884cb2SLee Jones 		drv_data->sw_rst = NULL;
12076884cb2SLee Jones 	}
12176884cb2SLee Jones 
12276884cb2SLee Jones 	drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst");
12376884cb2SLee Jones 	if (IS_ERR(drv_data->pwr_rst)) {
12476884cb2SLee Jones 		dev_dbg(&pdev->dev, "power soft reset control not defined\n");
12576884cb2SLee Jones 		drv_data->pwr_rst = NULL;
12676884cb2SLee Jones 	}
12776884cb2SLee Jones 
12876884cb2SLee Jones 	return st_ahci_deassert_resets(&pdev->dev);
12976884cb2SLee Jones }
13076884cb2SLee Jones 
131b032378bSBartlomiej Zolnierkiewicz static struct ata_port_operations st_ahci_port_ops = {
132b032378bSBartlomiej Zolnierkiewicz 	.inherits	= &ahci_platform_ops,
133b032378bSBartlomiej Zolnierkiewicz 	.host_stop	= st_ahci_host_stop,
134b032378bSBartlomiej Zolnierkiewicz };
135b032378bSBartlomiej Zolnierkiewicz 
13676884cb2SLee Jones static const struct ata_port_info st_ahci_port_info = {
13776884cb2SLee Jones 	.flags          = AHCI_FLAG_COMMON,
13876884cb2SLee Jones 	.pio_mask       = ATA_PIO4,
13976884cb2SLee Jones 	.udma_mask      = ATA_UDMA6,
140b032378bSBartlomiej Zolnierkiewicz 	.port_ops       = &st_ahci_port_ops,
14176884cb2SLee Jones };
14276884cb2SLee Jones 
143018d5ef2SAkinobu Mita static struct scsi_host_template ahci_platform_sht = {
144018d5ef2SAkinobu Mita 	AHCI_SHT(DRV_NAME),
145018d5ef2SAkinobu Mita };
146018d5ef2SAkinobu Mita 
14776884cb2SLee Jones static int st_ahci_probe(struct platform_device *pdev)
14876884cb2SLee Jones {
14976884cb2SLee Jones 	struct st_ahci_drv_data *drv_data;
15076884cb2SLee Jones 	struct ahci_host_priv *hpriv;
15176884cb2SLee Jones 	int err;
15276884cb2SLee Jones 
15376884cb2SLee Jones 	drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
15476884cb2SLee Jones 	if (!drv_data)
15576884cb2SLee Jones 		return -ENOMEM;
15676884cb2SLee Jones 
15776884cb2SLee Jones 	platform_set_drvdata(pdev, drv_data);
15876884cb2SLee Jones 
15976884cb2SLee Jones 	hpriv = ahci_platform_get_resources(pdev);
16076884cb2SLee Jones 	if (IS_ERR(hpriv))
16176884cb2SLee Jones 		return PTR_ERR(hpriv);
16276884cb2SLee Jones 
16376884cb2SLee Jones 	drv_data->hpriv = hpriv;
16476884cb2SLee Jones 
16576884cb2SLee Jones 	err = st_ahci_probe_resets(pdev);
16676884cb2SLee Jones 	if (err)
16776884cb2SLee Jones 		return err;
16876884cb2SLee Jones 
16976884cb2SLee Jones 	err = ahci_platform_enable_resources(hpriv);
17076884cb2SLee Jones 	if (err)
17176884cb2SLee Jones 		return err;
17276884cb2SLee Jones 
1739a1e75e1SPeter Griffin 	st_ahci_configure_oob(drv_data->hpriv->mmio);
1749a1e75e1SPeter Griffin 
175018d5ef2SAkinobu Mita 	err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
176018d5ef2SAkinobu Mita 				      &ahci_platform_sht);
17776884cb2SLee Jones 	if (err) {
17876884cb2SLee Jones 		ahci_platform_disable_resources(hpriv);
17976884cb2SLee Jones 		return err;
18076884cb2SLee Jones 	}
18176884cb2SLee Jones 
18276884cb2SLee Jones 	return 0;
18376884cb2SLee Jones }
18476884cb2SLee Jones 
18576884cb2SLee Jones #ifdef CONFIG_PM_SLEEP
18676884cb2SLee Jones static int st_ahci_suspend(struct device *dev)
18776884cb2SLee Jones {
18876884cb2SLee Jones 	struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
18976884cb2SLee Jones 	struct ahci_host_priv *hpriv = drv_data->hpriv;
19076884cb2SLee Jones 	int err;
19176884cb2SLee Jones 
19233081b34SBartlomiej Zolnierkiewicz 	err = ahci_platform_suspend_host(dev);
19333081b34SBartlomiej Zolnierkiewicz 	if (err)
19433081b34SBartlomiej Zolnierkiewicz 		return err;
195761a8c27SLee Jones 
19676884cb2SLee Jones 	if (drv_data->pwr) {
19776884cb2SLee Jones 		err = reset_control_assert(drv_data->pwr);
19876884cb2SLee Jones 		if (err) {
19976884cb2SLee Jones 			dev_err(dev, "unable to pwrdwn");
20076884cb2SLee Jones 			return err;
20176884cb2SLee Jones 		}
20276884cb2SLee Jones 	}
20376884cb2SLee Jones 
20476884cb2SLee Jones 	ahci_platform_disable_resources(hpriv);
20576884cb2SLee Jones 
20676884cb2SLee Jones 	return 0;
20776884cb2SLee Jones }
20876884cb2SLee Jones 
20976884cb2SLee Jones static int st_ahci_resume(struct device *dev)
21076884cb2SLee Jones {
21176884cb2SLee Jones 	struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
21276884cb2SLee Jones 	struct ahci_host_priv *hpriv = drv_data->hpriv;
21376884cb2SLee Jones 	int err;
21476884cb2SLee Jones 
21576884cb2SLee Jones 	err = ahci_platform_enable_resources(hpriv);
21676884cb2SLee Jones 	if (err)
21776884cb2SLee Jones 		return err;
21876884cb2SLee Jones 
21976884cb2SLee Jones 	err = st_ahci_deassert_resets(dev);
22076884cb2SLee Jones 	if (err) {
22176884cb2SLee Jones 		ahci_platform_disable_resources(hpriv);
22276884cb2SLee Jones 		return err;
22376884cb2SLee Jones 	}
22476884cb2SLee Jones 
2259a1e75e1SPeter Griffin 	st_ahci_configure_oob(drv_data->hpriv->mmio);
2269a1e75e1SPeter Griffin 
227761a8c27SLee Jones 	return ahci_platform_resume_host(dev);
22876884cb2SLee Jones }
22976884cb2SLee Jones #endif
23076884cb2SLee Jones 
23176884cb2SLee Jones static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
23276884cb2SLee Jones 
23309de99dbSKiran Padwal static const struct of_device_id st_ahci_match[] = {
23476884cb2SLee Jones 	{ .compatible = "st,ahci", },
23576884cb2SLee Jones 	{},
23676884cb2SLee Jones };
23776884cb2SLee Jones MODULE_DEVICE_TABLE(of, st_ahci_match);
23876884cb2SLee Jones 
23976884cb2SLee Jones static struct platform_driver st_ahci_driver = {
24076884cb2SLee Jones 	.driver = {
241018d5ef2SAkinobu Mita 		.name = DRV_NAME,
24276884cb2SLee Jones 		.pm = &st_ahci_pm_ops,
24376884cb2SLee Jones 		.of_match_table = of_match_ptr(st_ahci_match),
24476884cb2SLee Jones 	},
24576884cb2SLee Jones 	.probe = st_ahci_probe,
246a8237084SLee Jones 	.remove = ata_platform_remove_one,
24776884cb2SLee Jones };
24876884cb2SLee Jones module_platform_driver(st_ahci_driver);
24976884cb2SLee Jones 
25076884cb2SLee Jones MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
25176884cb2SLee Jones MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>");
2524a2e5123SLee Jones MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver");
25376884cb2SLee Jones MODULE_LICENSE("GPL v2");
254