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_mvebu.c (6817B)


      1/*
      2 * AHCI glue platform driver for Marvell EBU SOCs
      3 *
      4 * Copyright (C) 2014 Marvell
      5 *
      6 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
      7 * Marcin Wojtas <mw@semihalf.com>
      8 *
      9 * This file is licensed under the terms of the GNU General Public
     10 * License version 2.  This program is licensed "as is" without any
     11 * warranty of any kind, whether express or implied.
     12 */
     13
     14#include <linux/ahci_platform.h>
     15#include <linux/kernel.h>
     16#include <linux/mbus.h>
     17#include <linux/module.h>
     18#include <linux/of_device.h>
     19#include <linux/platform_device.h>
     20#include "ahci.h"
     21
     22#define DRV_NAME "ahci-mvebu"
     23
     24#define AHCI_VENDOR_SPECIFIC_0_ADDR  0xa0
     25#define AHCI_VENDOR_SPECIFIC_0_DATA  0xa4
     26
     27#define AHCI_WINDOW_CTRL(win)	(0x60 + ((win) << 4))
     28#define AHCI_WINDOW_BASE(win)	(0x64 + ((win) << 4))
     29#define AHCI_WINDOW_SIZE(win)	(0x68 + ((win) << 4))
     30
     31struct ahci_mvebu_plat_data {
     32	int (*plat_config)(struct ahci_host_priv *hpriv);
     33	unsigned int flags;
     34};
     35
     36static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv,
     37				   const struct mbus_dram_target_info *dram)
     38{
     39	int i;
     40
     41	for (i = 0; i < 4; i++) {
     42		writel(0, hpriv->mmio + AHCI_WINDOW_CTRL(i));
     43		writel(0, hpriv->mmio + AHCI_WINDOW_BASE(i));
     44		writel(0, hpriv->mmio + AHCI_WINDOW_SIZE(i));
     45	}
     46
     47	for (i = 0; i < dram->num_cs; i++) {
     48		const struct mbus_dram_window *cs = dram->cs + i;
     49
     50		writel((cs->mbus_attr << 8) |
     51		       (dram->mbus_dram_target_id << 4) | 1,
     52		       hpriv->mmio + AHCI_WINDOW_CTRL(i));
     53		writel(cs->base >> 16, hpriv->mmio + AHCI_WINDOW_BASE(i));
     54		writel(((cs->size - 1) & 0xffff0000),
     55		       hpriv->mmio + AHCI_WINDOW_SIZE(i));
     56	}
     57}
     58
     59static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv)
     60{
     61	/*
     62	 * Enable the regret bit to allow the SATA unit to regret a
     63	 * request that didn't receive an acknowlegde and avoid a
     64	 * deadlock
     65	 */
     66	writel(0x4, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_ADDR);
     67	writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
     68}
     69
     70static int ahci_mvebu_armada_380_config(struct ahci_host_priv *hpriv)
     71{
     72	const struct mbus_dram_target_info *dram;
     73	int rc = 0;
     74
     75	dram = mv_mbus_dram_info();
     76	if (dram)
     77		ahci_mvebu_mbus_config(hpriv, dram);
     78	else
     79		rc = -ENODEV;
     80
     81	ahci_mvebu_regret_option(hpriv);
     82
     83	return rc;
     84}
     85
     86static int ahci_mvebu_armada_3700_config(struct ahci_host_priv *hpriv)
     87{
     88	u32 reg;
     89
     90	writel(0, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_ADDR);
     91
     92	reg = readl(hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
     93	reg |= BIT(6);
     94	writel(reg, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
     95
     96	return 0;
     97}
     98
     99/**
    100 * ahci_mvebu_stop_engine
    101 *
    102 * @ap:	Target ata port
    103 *
    104 * Errata Ref#226 - SATA Disk HOT swap issue when connected through
    105 * Port Multiplier in FIS-based Switching mode.
    106 *
    107 * To avoid the issue, according to design, the bits[11:8, 0] of
    108 * register PxFBS are cleared when Port Command and Status (0x18) bit[0]
    109 * changes its value from 1 to 0, i.e. falling edge of Port
    110 * Command and Status bit[0] sends PULSE that resets PxFBS
    111 * bits[11:8; 0].
    112 *
    113 * This function is used to override function of "ahci_stop_engine"
    114 * from libahci.c by adding the mvebu work around(WA) to save PxFBS
    115 * value before the PxCMD ST write of 0, then restore PxFBS value.
    116 *
    117 * Return: 0 on success; Error code otherwise.
    118 */
    119static int ahci_mvebu_stop_engine(struct ata_port *ap)
    120{
    121	void __iomem *port_mmio = ahci_port_base(ap);
    122	u32 tmp, port_fbs;
    123
    124	tmp = readl(port_mmio + PORT_CMD);
    125
    126	/* check if the HBA is idle */
    127	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
    128		return 0;
    129
    130	/* save the port PxFBS register for later restore */
    131	port_fbs = readl(port_mmio + PORT_FBS);
    132
    133	/* setting HBA to idle */
    134	tmp &= ~PORT_CMD_START;
    135	writel(tmp, port_mmio + PORT_CMD);
    136
    137	/*
    138	 * bit #15 PxCMD signal doesn't clear PxFBS,
    139	 * restore the PxFBS register right after clearing the PxCMD ST,
    140	 * no need to wait for the PxCMD bit #15.
    141	 */
    142	writel(port_fbs, port_mmio + PORT_FBS);
    143
    144	/* wait for engine to stop. This could be as long as 500 msec */
    145	tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
    146				PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
    147	if (tmp & PORT_CMD_LIST_ON)
    148		return -EIO;
    149
    150	return 0;
    151}
    152
    153#ifdef CONFIG_PM_SLEEP
    154static int ahci_mvebu_suspend(struct platform_device *pdev, pm_message_t state)
    155{
    156	return ahci_platform_suspend_host(&pdev->dev);
    157}
    158
    159static int ahci_mvebu_resume(struct platform_device *pdev)
    160{
    161	struct ata_host *host = platform_get_drvdata(pdev);
    162	struct ahci_host_priv *hpriv = host->private_data;
    163	const struct ahci_mvebu_plat_data *pdata = hpriv->plat_data;
    164
    165	pdata->plat_config(hpriv);
    166
    167	return ahci_platform_resume_host(&pdev->dev);
    168}
    169#else
    170#define ahci_mvebu_suspend NULL
    171#define ahci_mvebu_resume NULL
    172#endif
    173
    174static const struct ata_port_info ahci_mvebu_port_info = {
    175	.flags	   = AHCI_FLAG_COMMON,
    176	.pio_mask  = ATA_PIO4,
    177	.udma_mask = ATA_UDMA6,
    178	.port_ops  = &ahci_platform_ops,
    179};
    180
    181static struct scsi_host_template ahci_platform_sht = {
    182	AHCI_SHT(DRV_NAME),
    183};
    184
    185static int ahci_mvebu_probe(struct platform_device *pdev)
    186{
    187	const struct ahci_mvebu_plat_data *pdata;
    188	struct ahci_host_priv *hpriv;
    189	int rc;
    190
    191	pdata = of_device_get_match_data(&pdev->dev);
    192	if (!pdata)
    193		return -EINVAL;
    194
    195	hpriv = ahci_platform_get_resources(pdev, 0);
    196	if (IS_ERR(hpriv))
    197		return PTR_ERR(hpriv);
    198
    199	hpriv->flags |= pdata->flags;
    200	hpriv->plat_data = (void *)pdata;
    201
    202	rc = ahci_platform_enable_resources(hpriv);
    203	if (rc)
    204		return rc;
    205
    206	hpriv->stop_engine = ahci_mvebu_stop_engine;
    207
    208	rc = pdata->plat_config(hpriv);
    209	if (rc)
    210		goto disable_resources;
    211
    212	rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info,
    213				     &ahci_platform_sht);
    214	if (rc)
    215		goto disable_resources;
    216
    217	return 0;
    218
    219disable_resources:
    220	ahci_platform_disable_resources(hpriv);
    221	return rc;
    222}
    223
    224static const struct ahci_mvebu_plat_data ahci_mvebu_armada_380_plat_data = {
    225	.plat_config = ahci_mvebu_armada_380_config,
    226};
    227
    228static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
    229	.plat_config = ahci_mvebu_armada_3700_config,
    230	.flags = AHCI_HFLAG_SUSPEND_PHYS,
    231};
    232
    233static const struct of_device_id ahci_mvebu_of_match[] = {
    234	{
    235		.compatible = "marvell,armada-380-ahci",
    236		.data = &ahci_mvebu_armada_380_plat_data,
    237	},
    238	{
    239		.compatible = "marvell,armada-3700-ahci",
    240		.data = &ahci_mvebu_armada_3700_plat_data,
    241	},
    242	{ /* sentinel */ }
    243};
    244MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match);
    245
    246static struct platform_driver ahci_mvebu_driver = {
    247	.probe = ahci_mvebu_probe,
    248	.remove = ata_platform_remove_one,
    249	.suspend = ahci_mvebu_suspend,
    250	.resume = ahci_mvebu_resume,
    251	.driver = {
    252		.name = DRV_NAME,
    253		.of_match_table = ahci_mvebu_of_match,
    254	},
    255};
    256module_platform_driver(ahci_mvebu_driver);
    257
    258MODULE_DESCRIPTION("Marvell EBU AHCI SATA driver");
    259MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>, Marcin Wojtas <mw@semihalf.com>");
    260MODULE_LICENSE("GPL");
    261MODULE_ALIAS("platform:ahci_mvebu");