linux/drivers/ata/ahci_seattle.c
<<
>>
Prefs
   1/*
   2 * AMD Seattle AHCI SATA driver
   3 *
   4 * Copyright (c) 2015, Advanced Micro Devices
   5 * Author: Brijesh Singh <brijesh.singh@amd.com>
   6 *
   7 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/pm.h>
  22#include <linux/device.h>
  23#include <linux/of_device.h>
  24#include <linux/platform_device.h>
  25#include <linux/libata.h>
  26#include <linux/ahci_platform.h>
  27#include <linux/acpi.h>
  28#include <linux/pci_ids.h>
  29#include "ahci.h"
  30
  31/* SGPIO Control Register definition
  32 *
  33 * Bit          Type            Description
  34 * 31           RW              OD7.2 (activity)
  35 * 30           RW              OD7.1 (locate)
  36 * 29           RW              OD7.0 (fault)
  37 * 28...8       RW              OD6.2...OD0.0 (3bits per port, 1 bit per LED)
  38 * 7            RO              SGPIO feature flag
  39 * 6:4          RO              Reserved
  40 * 3:0          RO              Number of ports (0 means no port supported)
  41 */
  42#define ACTIVITY_BIT_POS(x)             (8 + (3 * x))
  43#define LOCATE_BIT_POS(x)               (ACTIVITY_BIT_POS(x) + 1)
  44#define FAULT_BIT_POS(x)                (LOCATE_BIT_POS(x) + 1)
  45
  46#define ACTIVITY_MASK                   0x00010000
  47#define LOCATE_MASK                     0x00080000
  48#define FAULT_MASK                      0x00400000
  49
  50#define DRV_NAME "ahci-seattle"
  51
  52static ssize_t seattle_transmit_led_message(struct ata_port *ap, u32 state,
  53                                            ssize_t size);
  54
  55struct seattle_plat_data {
  56        void __iomem *sgpio_ctrl;
  57};
  58
  59static struct ata_port_operations ahci_port_ops = {
  60        .inherits               = &ahci_ops,
  61};
  62
  63static const struct ata_port_info ahci_port_info = {
  64        .flags          = AHCI_FLAG_COMMON,
  65        .pio_mask       = ATA_PIO4,
  66        .udma_mask      = ATA_UDMA6,
  67        .port_ops       = &ahci_port_ops,
  68};
  69
  70static struct ata_port_operations ahci_seattle_ops = {
  71        .inherits               = &ahci_ops,
  72        .transmit_led_message   = seattle_transmit_led_message,
  73};
  74
  75static const struct ata_port_info ahci_port_seattle_info = {
  76        .flags          = AHCI_FLAG_COMMON | ATA_FLAG_EM | ATA_FLAG_SW_ACTIVITY,
  77        .link_flags     = ATA_LFLAG_SW_ACTIVITY,
  78        .pio_mask       = ATA_PIO4,
  79        .udma_mask      = ATA_UDMA6,
  80        .port_ops       = &ahci_seattle_ops,
  81};
  82
  83static struct scsi_host_template ahci_platform_sht = {
  84        AHCI_SHT(DRV_NAME),
  85};
  86
  87static ssize_t seattle_transmit_led_message(struct ata_port *ap, u32 state,
  88                                            ssize_t size)
  89{
  90        struct ahci_host_priv *hpriv = ap->host->private_data;
  91        struct ahci_port_priv *pp = ap->private_data;
  92        struct seattle_plat_data *plat_data = hpriv->plat_data;
  93        unsigned long flags;
  94        int pmp;
  95        struct ahci_em_priv *emp;
  96        u32 val;
  97
  98        /* get the slot number from the message */
  99        pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
 100        if (pmp >= EM_MAX_SLOTS)
 101                return -EINVAL;
 102        emp = &pp->em_priv[pmp];
 103
 104        val = ioread32(plat_data->sgpio_ctrl);
 105        if (state & ACTIVITY_MASK)
 106                val |= 1 << ACTIVITY_BIT_POS((ap->port_no));
 107        else
 108                val &= ~(1 << ACTIVITY_BIT_POS((ap->port_no)));
 109
 110        if (state & LOCATE_MASK)
 111                val |= 1 << LOCATE_BIT_POS((ap->port_no));
 112        else
 113                val &= ~(1 << LOCATE_BIT_POS((ap->port_no)));
 114
 115        if (state & FAULT_MASK)
 116                val |= 1 << FAULT_BIT_POS((ap->port_no));
 117        else
 118                val &= ~(1 << FAULT_BIT_POS((ap->port_no)));
 119
 120        iowrite32(val, plat_data->sgpio_ctrl);
 121
 122        spin_lock_irqsave(ap->lock, flags);
 123
 124        /* save off new led state for port/slot */
 125        emp->led_state = state;
 126
 127        spin_unlock_irqrestore(ap->lock, flags);
 128
 129        return size;
 130}
 131
 132static const struct ata_port_info *ahci_seattle_get_port_info(
 133                struct platform_device *pdev, struct ahci_host_priv *hpriv)
 134{
 135        struct device *dev = &pdev->dev;
 136        struct seattle_plat_data *plat_data;
 137        u32 val;
 138
 139        plat_data = devm_kzalloc(dev, sizeof(*plat_data), GFP_KERNEL);
 140        if (!plat_data)
 141                return &ahci_port_info;
 142
 143        plat_data->sgpio_ctrl = devm_ioremap_resource(dev,
 144                              platform_get_resource(pdev, IORESOURCE_MEM, 1));
 145        if (IS_ERR(plat_data->sgpio_ctrl))
 146                return &ahci_port_info;
 147
 148        val = ioread32(plat_data->sgpio_ctrl);
 149
 150        if (!(val & 0xf))
 151                return &ahci_port_info;
 152
 153        hpriv->em_loc = 0;
 154        hpriv->em_buf_sz = 4;
 155        hpriv->em_msg_type = EM_MSG_TYPE_LED;
 156        hpriv->plat_data = plat_data;
 157
 158        dev_info(dev, "SGPIO LED control is enabled.\n");
 159        return &ahci_port_seattle_info;
 160}
 161
 162static int ahci_seattle_probe(struct platform_device *pdev)
 163{
 164        int rc;
 165        struct ahci_host_priv *hpriv;
 166
 167        hpriv = ahci_platform_get_resources(pdev);
 168        if (IS_ERR(hpriv))
 169                return PTR_ERR(hpriv);
 170
 171        rc = ahci_platform_enable_resources(hpriv);
 172        if (rc)
 173                return rc;
 174
 175        rc = ahci_platform_init_host(pdev, hpriv,
 176                                     ahci_seattle_get_port_info(pdev, hpriv),
 177                                     &ahci_platform_sht);
 178        if (rc)
 179                goto disable_resources;
 180
 181        return 0;
 182disable_resources:
 183        ahci_platform_disable_resources(hpriv);
 184        return rc;
 185}
 186
 187static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
 188                         ahci_platform_resume);
 189
 190static const struct acpi_device_id ahci_acpi_match[] = {
 191        { "AMDI0600", 0 },
 192        {}
 193};
 194MODULE_DEVICE_TABLE(acpi, ahci_acpi_match);
 195
 196static struct platform_driver ahci_seattle_driver = {
 197        .probe = ahci_seattle_probe,
 198        .remove = ata_platform_remove_one,
 199        .driver = {
 200                .name = DRV_NAME,
 201                .acpi_match_table = ahci_acpi_match,
 202                .pm = &ahci_pm_ops,
 203        },
 204};
 205module_platform_driver(ahci_seattle_driver);
 206
 207MODULE_DESCRIPTION("Seattle AHCI SATA platform driver");
 208MODULE_AUTHOR("Brijesh Singh <brijesh.singh@amd.com>");
 209MODULE_LICENSE("GPL");
 210MODULE_ALIAS("platform:" DRV_NAME);
 211