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