cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ahci_st.c (5995B)


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