1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
276884cb2SLee Jones /*
376884cb2SLee Jones * Copyright (C) 2012 STMicroelectronics Limited
476884cb2SLee Jones *
576884cb2SLee Jones * Authors: Francesco Virlinzi <francesco.virlinzi@st.com>
676884cb2SLee Jones * Alexandre Torgue <alexandre.torgue@st.com>
776884cb2SLee Jones */
876884cb2SLee Jones
976884cb2SLee Jones #include <linux/init.h>
1076884cb2SLee Jones #include <linux/module.h>
1176884cb2SLee Jones #include <linux/export.h>
1276884cb2SLee Jones #include <linux/platform_device.h>
1376884cb2SLee Jones #include <linux/clk.h>
1476884cb2SLee Jones #include <linux/of.h>
1576884cb2SLee Jones #include <linux/ahci_platform.h>
1676884cb2SLee Jones #include <linux/libata.h>
1776884cb2SLee Jones #include <linux/reset.h>
1876884cb2SLee Jones #include <linux/io.h>
1976884cb2SLee Jones #include <linux/dma-mapping.h>
2076884cb2SLee Jones
2176884cb2SLee Jones #include "ahci.h"
2276884cb2SLee Jones
23018d5ef2SAkinobu Mita #define DRV_NAME "st_ahci"
24018d5ef2SAkinobu Mita
2576884cb2SLee Jones #define ST_AHCI_OOBR 0xbc
2676884cb2SLee Jones #define ST_AHCI_OOBR_WE BIT(31)
2776884cb2SLee Jones #define ST_AHCI_OOBR_CWMIN_SHIFT 24
2876884cb2SLee Jones #define ST_AHCI_OOBR_CWMAX_SHIFT 16
2976884cb2SLee Jones #define ST_AHCI_OOBR_CIMIN_SHIFT 8
3076884cb2SLee Jones #define ST_AHCI_OOBR_CIMAX_SHIFT 0
3176884cb2SLee Jones
3276884cb2SLee Jones struct st_ahci_drv_data {
3376884cb2SLee Jones struct platform_device *ahci;
3476884cb2SLee Jones struct reset_control *pwr;
3576884cb2SLee Jones struct reset_control *sw_rst;
3676884cb2SLee Jones struct reset_control *pwr_rst;
3776884cb2SLee Jones };
3876884cb2SLee Jones
st_ahci_configure_oob(void __iomem * mmio)3976884cb2SLee Jones static void st_ahci_configure_oob(void __iomem *mmio)
4076884cb2SLee Jones {
4176884cb2SLee Jones unsigned long old_val, new_val;
4276884cb2SLee Jones
4376884cb2SLee Jones new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) |
4476884cb2SLee Jones (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) |
4576884cb2SLee Jones (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) |
4676884cb2SLee Jones (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT);
4776884cb2SLee Jones
4876884cb2SLee Jones old_val = readl(mmio + ST_AHCI_OOBR);
4976884cb2SLee Jones writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
5076884cb2SLee Jones writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
5176884cb2SLee Jones writel(new_val, mmio + ST_AHCI_OOBR);
5276884cb2SLee Jones }
5376884cb2SLee Jones
st_ahci_deassert_resets(struct ahci_host_priv * hpriv,struct device * dev)54e0e2674bSPeter Griffin static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
55e0e2674bSPeter Griffin struct device *dev)
5676884cb2SLee Jones {
57e0e2674bSPeter Griffin struct st_ahci_drv_data *drv_data = hpriv->plat_data;
5876884cb2SLee Jones int err;
5976884cb2SLee Jones
6076884cb2SLee Jones if (drv_data->pwr) {
6176884cb2SLee Jones err = reset_control_deassert(drv_data->pwr);
6276884cb2SLee Jones if (err) {
6376884cb2SLee Jones dev_err(dev, "unable to bring out of pwrdwn\n");
6476884cb2SLee Jones return err;
6576884cb2SLee Jones }
6676884cb2SLee Jones }
6776884cb2SLee Jones
6876884cb2SLee Jones if (drv_data->sw_rst) {
6976884cb2SLee Jones err = reset_control_deassert(drv_data->sw_rst);
7076884cb2SLee Jones if (err) {
7176884cb2SLee Jones dev_err(dev, "unable to bring out of sw-rst\n");
7276884cb2SLee Jones return err;
7376884cb2SLee Jones }
7476884cb2SLee Jones }
7576884cb2SLee Jones
7676884cb2SLee Jones if (drv_data->pwr_rst) {
7776884cb2SLee Jones err = reset_control_deassert(drv_data->pwr_rst);
7876884cb2SLee Jones if (err) {
7976884cb2SLee Jones dev_err(dev, "unable to bring out of pwr-rst\n");
8076884cb2SLee Jones return err;
8176884cb2SLee Jones }
8276884cb2SLee Jones }
8376884cb2SLee Jones
8476884cb2SLee Jones return 0;
8576884cb2SLee Jones }
8676884cb2SLee Jones
st_ahci_host_stop(struct ata_host * host)87b032378bSBartlomiej Zolnierkiewicz static void st_ahci_host_stop(struct ata_host *host)
88a8237084SLee Jones {
89b032378bSBartlomiej Zolnierkiewicz struct ahci_host_priv *hpriv = host->private_data;
90e0e2674bSPeter Griffin struct st_ahci_drv_data *drv_data = hpriv->plat_data;
91b032378bSBartlomiej Zolnierkiewicz struct device *dev = host->dev;
92a8237084SLee Jones int err;
93a8237084SLee Jones
94a8237084SLee Jones if (drv_data->pwr) {
95a8237084SLee Jones err = reset_control_assert(drv_data->pwr);
96a8237084SLee Jones if (err)
9733081b34SBartlomiej Zolnierkiewicz dev_err(dev, "unable to pwrdwn\n");
98a8237084SLee Jones }
99a8237084SLee Jones
100a8237084SLee Jones ahci_platform_disable_resources(hpriv);
101a8237084SLee Jones }
102a8237084SLee Jones
st_ahci_probe_resets(struct ahci_host_priv * hpriv,struct device * dev)103e0e2674bSPeter Griffin static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
104e0e2674bSPeter Griffin struct device *dev)
10576884cb2SLee Jones {
106e0e2674bSPeter Griffin struct st_ahci_drv_data *drv_data = hpriv->plat_data;
10776884cb2SLee Jones
108e0e2674bSPeter Griffin drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
10976884cb2SLee Jones if (IS_ERR(drv_data->pwr)) {
110e0e2674bSPeter Griffin dev_info(dev, "power reset control not defined\n");
11176884cb2SLee Jones drv_data->pwr = NULL;
11276884cb2SLee Jones }
11376884cb2SLee Jones
114e0e2674bSPeter Griffin drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
11576884cb2SLee Jones if (IS_ERR(drv_data->sw_rst)) {
116e0e2674bSPeter Griffin dev_info(dev, "soft reset control not defined\n");
11776884cb2SLee Jones drv_data->sw_rst = NULL;
11876884cb2SLee Jones }
11976884cb2SLee Jones
120e0e2674bSPeter Griffin drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
12176884cb2SLee Jones if (IS_ERR(drv_data->pwr_rst)) {
122e0e2674bSPeter Griffin dev_dbg(dev, "power soft reset control not defined\n");
12376884cb2SLee Jones drv_data->pwr_rst = NULL;
12476884cb2SLee Jones }
12576884cb2SLee Jones
126e0e2674bSPeter Griffin return st_ahci_deassert_resets(hpriv, dev);
12776884cb2SLee Jones }
12876884cb2SLee Jones
129b032378bSBartlomiej Zolnierkiewicz static struct ata_port_operations st_ahci_port_ops = {
130b032378bSBartlomiej Zolnierkiewicz .inherits = &ahci_platform_ops,
131b032378bSBartlomiej Zolnierkiewicz .host_stop = st_ahci_host_stop,
132b032378bSBartlomiej Zolnierkiewicz };
133b032378bSBartlomiej Zolnierkiewicz
13476884cb2SLee Jones static const struct ata_port_info st_ahci_port_info = {
13576884cb2SLee Jones .flags = AHCI_FLAG_COMMON,
13676884cb2SLee Jones .pio_mask = ATA_PIO4,
13776884cb2SLee Jones .udma_mask = ATA_UDMA6,
138b032378bSBartlomiej Zolnierkiewicz .port_ops = &st_ahci_port_ops,
13976884cb2SLee Jones };
14076884cb2SLee Jones
14125df73d9SBart Van Assche static const struct scsi_host_template ahci_platform_sht = {
142018d5ef2SAkinobu Mita AHCI_SHT(DRV_NAME),
143018d5ef2SAkinobu Mita };
144018d5ef2SAkinobu Mita
st_ahci_probe(struct platform_device * pdev)14576884cb2SLee Jones static int st_ahci_probe(struct platform_device *pdev)
14676884cb2SLee Jones {
14776884cb2SLee Jones struct st_ahci_drv_data *drv_data;
14876884cb2SLee Jones struct ahci_host_priv *hpriv;
14976884cb2SLee Jones int err;
15076884cb2SLee Jones
15176884cb2SLee Jones drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
15276884cb2SLee Jones if (!drv_data)
15376884cb2SLee Jones return -ENOMEM;
15476884cb2SLee Jones
15516af2d65SKunihiko Hayashi hpriv = ahci_platform_get_resources(pdev, 0);
15676884cb2SLee Jones if (IS_ERR(hpriv))
15776884cb2SLee Jones return PTR_ERR(hpriv);
158e0e2674bSPeter Griffin hpriv->plat_data = drv_data;
15976884cb2SLee Jones
160e0e2674bSPeter Griffin err = st_ahci_probe_resets(hpriv, &pdev->dev);
16176884cb2SLee Jones if (err)
16276884cb2SLee Jones return err;
16376884cb2SLee Jones
16476884cb2SLee Jones err = ahci_platform_enable_resources(hpriv);
16576884cb2SLee Jones if (err)
16676884cb2SLee Jones return err;
16776884cb2SLee Jones
168e0e2674bSPeter Griffin st_ahci_configure_oob(hpriv->mmio);
1699a1e75e1SPeter Griffin
170018d5ef2SAkinobu Mita err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
171018d5ef2SAkinobu Mita &ahci_platform_sht);
17276884cb2SLee Jones if (err) {
17376884cb2SLee Jones ahci_platform_disable_resources(hpriv);
17476884cb2SLee Jones return err;
17576884cb2SLee Jones }
17676884cb2SLee Jones
17776884cb2SLee Jones return 0;
17876884cb2SLee Jones }
17976884cb2SLee Jones
18076884cb2SLee Jones #ifdef CONFIG_PM_SLEEP
st_ahci_suspend(struct device * dev)18176884cb2SLee Jones static int st_ahci_suspend(struct device *dev)
18276884cb2SLee Jones {
183e0e2674bSPeter Griffin struct ata_host *host = dev_get_drvdata(dev);
184e0e2674bSPeter Griffin struct ahci_host_priv *hpriv = host->private_data;
185e0e2674bSPeter Griffin struct st_ahci_drv_data *drv_data = hpriv->plat_data;
18676884cb2SLee Jones int err;
18776884cb2SLee Jones
18833081b34SBartlomiej Zolnierkiewicz err = ahci_platform_suspend_host(dev);
18933081b34SBartlomiej Zolnierkiewicz if (err)
19033081b34SBartlomiej Zolnierkiewicz return err;
191761a8c27SLee Jones
19276884cb2SLee Jones if (drv_data->pwr) {
19376884cb2SLee Jones err = reset_control_assert(drv_data->pwr);
19476884cb2SLee Jones if (err) {
19576884cb2SLee Jones dev_err(dev, "unable to pwrdwn");
19676884cb2SLee Jones return err;
19776884cb2SLee Jones }
19876884cb2SLee Jones }
19976884cb2SLee Jones
20076884cb2SLee Jones ahci_platform_disable_resources(hpriv);
20176884cb2SLee Jones
20276884cb2SLee Jones return 0;
20376884cb2SLee Jones }
20476884cb2SLee Jones
st_ahci_resume(struct device * dev)20576884cb2SLee Jones static int st_ahci_resume(struct device *dev)
20676884cb2SLee Jones {
207e0e2674bSPeter Griffin struct ata_host *host = dev_get_drvdata(dev);
208e0e2674bSPeter Griffin struct ahci_host_priv *hpriv = host->private_data;
20976884cb2SLee Jones int err;
21076884cb2SLee Jones
21176884cb2SLee Jones err = ahci_platform_enable_resources(hpriv);
21276884cb2SLee Jones if (err)
21376884cb2SLee Jones return err;
21476884cb2SLee Jones
215e0e2674bSPeter Griffin err = st_ahci_deassert_resets(hpriv, dev);
21676884cb2SLee Jones if (err) {
21776884cb2SLee Jones ahci_platform_disable_resources(hpriv);
21876884cb2SLee Jones return err;
21976884cb2SLee Jones }
22076884cb2SLee Jones
221e0e2674bSPeter Griffin st_ahci_configure_oob(hpriv->mmio);
2229a1e75e1SPeter Griffin
223761a8c27SLee Jones return ahci_platform_resume_host(dev);
22476884cb2SLee Jones }
22576884cb2SLee Jones #endif
22676884cb2SLee Jones
22776884cb2SLee Jones static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
22876884cb2SLee Jones
22909de99dbSKiran Padwal static const struct of_device_id st_ahci_match[] = {
23076884cb2SLee Jones { .compatible = "st,ahci", },
2315e776d7bSGeert Uytterhoeven { /* sentinel */ }
23276884cb2SLee Jones };
23376884cb2SLee Jones MODULE_DEVICE_TABLE(of, st_ahci_match);
23476884cb2SLee Jones
23576884cb2SLee Jones static struct platform_driver st_ahci_driver = {
23676884cb2SLee Jones .driver = {
237018d5ef2SAkinobu Mita .name = DRV_NAME,
23876884cb2SLee Jones .pm = &st_ahci_pm_ops,
23917cc1ee6SDamien Le Moal .of_match_table = st_ahci_match,
24076884cb2SLee Jones },
24176884cb2SLee Jones .probe = st_ahci_probe,
242*a7eb54d4SUwe Kleine-König .remove_new = ata_platform_remove_one,
24376884cb2SLee Jones };
24476884cb2SLee Jones module_platform_driver(st_ahci_driver);
24576884cb2SLee Jones
24676884cb2SLee Jones MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
24776884cb2SLee Jones MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>");
2484a2e5123SLee Jones MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver");
24976884cb2SLee Jones MODULE_LICENSE("GPL v2");
250