18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Provides ACPI support for IDE drives. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Intel Corp. 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Randy Dunlap 78c2ecf20Sopenharmony_ci * Copyright (C) 2006 SUSE Linux Products GmbH 88c2ecf20Sopenharmony_ci * Copyright (C) 2006 Hannes Reinecke 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/ata.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/ide.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/dmi.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define REGS_PER_GTF 7 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct GTM_buffer { 268c2ecf20Sopenharmony_ci u32 PIO_speed0; 278c2ecf20Sopenharmony_ci u32 DMA_speed0; 288c2ecf20Sopenharmony_ci u32 PIO_speed1; 298c2ecf20Sopenharmony_ci u32 DMA_speed1; 308c2ecf20Sopenharmony_ci u32 GTM_flags; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct ide_acpi_drive_link { 348c2ecf20Sopenharmony_ci acpi_handle obj_handle; 358c2ecf20Sopenharmony_ci u8 idbuff[512]; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct ide_acpi_hwif_link { 398c2ecf20Sopenharmony_ci ide_hwif_t *hwif; 408c2ecf20Sopenharmony_ci acpi_handle obj_handle; 418c2ecf20Sopenharmony_ci struct GTM_buffer gtm; 428c2ecf20Sopenharmony_ci struct ide_acpi_drive_link master; 438c2ecf20Sopenharmony_ci struct ide_acpi_drive_link slave; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#undef DEBUGGING 478c2ecf20Sopenharmony_ci/* note: adds function name and KERN_DEBUG */ 488c2ecf20Sopenharmony_ci#ifdef DEBUGGING 498c2ecf20Sopenharmony_ci#define DEBPRINT(fmt, args...) \ 508c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: " fmt, __func__, ## args) 518c2ecf20Sopenharmony_ci#else 528c2ecf20Sopenharmony_ci#define DEBPRINT(fmt, args...) do {} while (0) 538c2ecf20Sopenharmony_ci#endif /* DEBUGGING */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool ide_noacpi; 568c2ecf20Sopenharmony_cimodule_param_named(noacpi, ide_noacpi, bool, 0); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(noacpi, "disable IDE ACPI support"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool ide_acpigtf; 608c2ecf20Sopenharmony_cimodule_param_named(acpigtf, ide_acpigtf, bool, 0); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(acpigtf, "enable IDE ACPI _GTF support"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic bool ide_acpionboot; 648c2ecf20Sopenharmony_cimodule_param_named(acpionboot, ide_acpionboot, bool, 0); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(acpionboot, "call IDE ACPI methods on boot"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic bool ide_noacpi_psx; 688c2ecf20Sopenharmony_cistatic int no_acpi_psx(const struct dmi_system_id *id) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci ide_noacpi_psx = true; 718c2ecf20Sopenharmony_ci printk(KERN_NOTICE"%s detected - disable ACPI _PSx.\n", id->ident); 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const struct dmi_system_id ide_acpi_dmi_table[] = { 768c2ecf20Sopenharmony_ci /* Bug 9673. */ 778c2ecf20Sopenharmony_ci /* We should check if this is because ACPI NVS isn't save/restored. */ 788c2ecf20Sopenharmony_ci { 798c2ecf20Sopenharmony_ci .callback = no_acpi_psx, 808c2ecf20Sopenharmony_ci .ident = "HP nx9005", 818c2ecf20Sopenharmony_ci .matches = { 828c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies Ltd."), 838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_VERSION, "KAM1.60") 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci }, 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci { } /* terminate list */ 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciint ide_acpi_init(void) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci dmi_check_system(ide_acpi_dmi_table); 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cibool ide_port_acpi(ide_hwif_t *hwif) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return ide_noacpi == 0 && hwif->acpidata; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic acpi_handle acpi_get_child(acpi_handle handle, u64 addr) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct acpi_device *adev; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!handle || acpi_bus_get_device(handle, &adev)) 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci adev = acpi_find_child_device(adev, addr, false); 1098c2ecf20Sopenharmony_ci return adev ? adev->handle : NULL; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * ide_get_dev_handle - finds acpi_handle and PCI device.function 1148c2ecf20Sopenharmony_ci * @dev: device to locate 1158c2ecf20Sopenharmony_ci * @handle: returned acpi_handle for @dev 1168c2ecf20Sopenharmony_ci * @pcidevfn: return PCI device.func for @dev 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * Returns the ACPI object handle to the corresponding PCI device. 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Returns 0 on success, <0 on error. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistatic int ide_get_dev_handle(struct device *dev, acpi_handle *handle, 1238c2ecf20Sopenharmony_ci u64 *pcidevfn) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 1268c2ecf20Sopenharmony_ci unsigned int bus, devnum, func; 1278c2ecf20Sopenharmony_ci u64 addr; 1288c2ecf20Sopenharmony_ci acpi_handle dev_handle; 1298c2ecf20Sopenharmony_ci acpi_status status; 1308c2ecf20Sopenharmony_ci struct acpi_device_info *dinfo = NULL; 1318c2ecf20Sopenharmony_ci int ret = -ENODEV; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci bus = pdev->bus->number; 1348c2ecf20Sopenharmony_ci devnum = PCI_SLOT(pdev->devfn); 1358c2ecf20Sopenharmony_ci func = PCI_FUNC(pdev->devfn); 1368c2ecf20Sopenharmony_ci /* ACPI _ADR encoding for PCI bus: */ 1378c2ecf20Sopenharmony_ci addr = (u64)(devnum << 16 | func); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci DEBPRINT("ENTER: pci %02x:%02x.%01x\n", bus, devnum, func); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dev_handle = ACPI_HANDLE(dev); 1428c2ecf20Sopenharmony_ci if (!dev_handle) { 1438c2ecf20Sopenharmony_ci DEBPRINT("no acpi handle for device\n"); 1448c2ecf20Sopenharmony_ci goto err; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci status = acpi_get_object_info(dev_handle, &dinfo); 1488c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 1498c2ecf20Sopenharmony_ci DEBPRINT("get_object_info for device failed\n"); 1508c2ecf20Sopenharmony_ci goto err; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && 1538c2ecf20Sopenharmony_ci dinfo->address == addr) { 1548c2ecf20Sopenharmony_ci *pcidevfn = addr; 1558c2ecf20Sopenharmony_ci *handle = dev_handle; 1568c2ecf20Sopenharmony_ci } else { 1578c2ecf20Sopenharmony_ci DEBPRINT("get_object_info for device has wrong " 1588c2ecf20Sopenharmony_ci " address: %llu, should be %u\n", 1598c2ecf20Sopenharmony_ci dinfo ? (unsigned long long)dinfo->address : -1ULL, 1608c2ecf20Sopenharmony_ci (unsigned int)addr); 1618c2ecf20Sopenharmony_ci goto err; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci DEBPRINT("for dev=0x%x.%x, addr=0x%llx, *handle=0x%p\n", 1658c2ecf20Sopenharmony_ci devnum, func, (unsigned long long)addr, *handle); 1668c2ecf20Sopenharmony_ci ret = 0; 1678c2ecf20Sopenharmony_cierr: 1688c2ecf20Sopenharmony_ci kfree(dinfo); 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/** 1738c2ecf20Sopenharmony_ci * ide_acpi_hwif_get_handle - Get ACPI object handle for a given hwif 1748c2ecf20Sopenharmony_ci * @hwif: device to locate 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * Retrieves the object handle for a given hwif. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Returns handle on success, 0 on error. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistatic acpi_handle ide_acpi_hwif_get_handle(ide_hwif_t *hwif) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct device *dev = hwif->gendev.parent; 1838c2ecf20Sopenharmony_ci acpi_handle dev_handle; 1848c2ecf20Sopenharmony_ci u64 pcidevfn; 1858c2ecf20Sopenharmony_ci acpi_handle chan_handle; 1868c2ecf20Sopenharmony_ci int err; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci DEBPRINT("ENTER: device %s\n", hwif->name); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!dev) { 1918c2ecf20Sopenharmony_ci DEBPRINT("no PCI device for %s\n", hwif->name); 1928c2ecf20Sopenharmony_ci return NULL; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci err = ide_get_dev_handle(dev, &dev_handle, &pcidevfn); 1968c2ecf20Sopenharmony_ci if (err < 0) { 1978c2ecf20Sopenharmony_ci DEBPRINT("ide_get_dev_handle failed (%d)\n", err); 1988c2ecf20Sopenharmony_ci return NULL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* get child objects of dev_handle == channel objects, 2028c2ecf20Sopenharmony_ci * + _their_ children == drive objects */ 2038c2ecf20Sopenharmony_ci /* channel is hwif->channel */ 2048c2ecf20Sopenharmony_ci chan_handle = acpi_get_child(dev_handle, hwif->channel); 2058c2ecf20Sopenharmony_ci DEBPRINT("chan adr=%d: handle=0x%p\n", 2068c2ecf20Sopenharmony_ci hwif->channel, chan_handle); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return chan_handle; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * do_drive_get_GTF - get the drive bootup default taskfile settings 2138c2ecf20Sopenharmony_ci * @drive: the drive for which the taskfile settings should be retrieved 2148c2ecf20Sopenharmony_ci * @gtf_length: number of bytes of _GTF data returned at @gtf_address 2158c2ecf20Sopenharmony_ci * @gtf_address: buffer containing _GTF taskfile arrays 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * The _GTF method has no input parameters. 2188c2ecf20Sopenharmony_ci * It returns a variable number of register set values (registers 2198c2ecf20Sopenharmony_ci * hex 1F1..1F7, taskfiles). 2208c2ecf20Sopenharmony_ci * The <variable number> is not known in advance, so have ACPI-CA 2218c2ecf20Sopenharmony_ci * allocate the buffer as needed and return it, then free it later. 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * The returned @gtf_length and @gtf_address are only valid if the 2248c2ecf20Sopenharmony_ci * function return value is 0. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic int do_drive_get_GTF(ide_drive_t *drive, 2278c2ecf20Sopenharmony_ci unsigned int *gtf_length, unsigned long *gtf_address, 2288c2ecf20Sopenharmony_ci unsigned long *obj_loc) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci acpi_status status; 2318c2ecf20Sopenharmony_ci struct acpi_buffer output; 2328c2ecf20Sopenharmony_ci union acpi_object *out_obj; 2338c2ecf20Sopenharmony_ci int err = -ENODEV; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci *gtf_length = 0; 2368c2ecf20Sopenharmony_ci *gtf_address = 0UL; 2378c2ecf20Sopenharmony_ci *obj_loc = 0UL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!drive->acpidata->obj_handle) { 2408c2ecf20Sopenharmony_ci DEBPRINT("No ACPI object found for %s\n", drive->name); 2418c2ecf20Sopenharmony_ci goto out; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Setting up output buffer */ 2458c2ecf20Sopenharmony_ci output.length = ACPI_ALLOCATE_BUFFER; 2468c2ecf20Sopenharmony_ci output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* _GTF has no input parameters */ 2498c2ecf20Sopenharmony_ci err = -EIO; 2508c2ecf20Sopenharmony_ci status = acpi_evaluate_object(drive->acpidata->obj_handle, "_GTF", 2518c2ecf20Sopenharmony_ci NULL, &output); 2528c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 2538c2ecf20Sopenharmony_ci printk(KERN_DEBUG 2548c2ecf20Sopenharmony_ci "%s: Run _GTF error: status = 0x%x\n", 2558c2ecf20Sopenharmony_ci __func__, status); 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!output.length || !output.pointer) { 2608c2ecf20Sopenharmony_ci DEBPRINT("Run _GTF: " 2618c2ecf20Sopenharmony_ci "length or ptr is NULL (0x%llx, 0x%p)\n", 2628c2ecf20Sopenharmony_ci (unsigned long long)output.length, 2638c2ecf20Sopenharmony_ci output.pointer); 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci out_obj = output.pointer; 2688c2ecf20Sopenharmony_ci if (out_obj->type != ACPI_TYPE_BUFFER) { 2698c2ecf20Sopenharmony_ci DEBPRINT("Run _GTF: error: " 2708c2ecf20Sopenharmony_ci "expected object type of ACPI_TYPE_BUFFER, " 2718c2ecf20Sopenharmony_ci "got 0x%x\n", out_obj->type); 2728c2ecf20Sopenharmony_ci err = -ENOENT; 2738c2ecf20Sopenharmony_ci kfree(output.pointer); 2748c2ecf20Sopenharmony_ci goto out; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!out_obj->buffer.length || !out_obj->buffer.pointer || 2788c2ecf20Sopenharmony_ci out_obj->buffer.length % REGS_PER_GTF) { 2798c2ecf20Sopenharmony_ci printk(KERN_ERR 2808c2ecf20Sopenharmony_ci "%s: unexpected GTF length (%d) or addr (0x%p)\n", 2818c2ecf20Sopenharmony_ci __func__, out_obj->buffer.length, 2828c2ecf20Sopenharmony_ci out_obj->buffer.pointer); 2838c2ecf20Sopenharmony_ci err = -ENOENT; 2848c2ecf20Sopenharmony_ci kfree(output.pointer); 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci *gtf_length = out_obj->buffer.length; 2898c2ecf20Sopenharmony_ci *gtf_address = (unsigned long)out_obj->buffer.pointer; 2908c2ecf20Sopenharmony_ci *obj_loc = (unsigned long)out_obj; 2918c2ecf20Sopenharmony_ci DEBPRINT("returning gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n", 2928c2ecf20Sopenharmony_ci *gtf_length, *gtf_address, *obj_loc); 2938c2ecf20Sopenharmony_ci err = 0; 2948c2ecf20Sopenharmony_ciout: 2958c2ecf20Sopenharmony_ci return err; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/** 2998c2ecf20Sopenharmony_ci * do_drive_set_taskfiles - write the drive taskfile settings from _GTF 3008c2ecf20Sopenharmony_ci * @drive: the drive to which the taskfile command should be sent 3018c2ecf20Sopenharmony_ci * @gtf_length: total number of bytes of _GTF taskfiles 3028c2ecf20Sopenharmony_ci * @gtf_address: location of _GTF taskfile arrays 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * Write {gtf_address, length gtf_length} in groups of 3058c2ecf20Sopenharmony_ci * REGS_PER_GTF bytes. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic int do_drive_set_taskfiles(ide_drive_t *drive, 3088c2ecf20Sopenharmony_ci unsigned int gtf_length, 3098c2ecf20Sopenharmony_ci unsigned long gtf_address) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int rc = 0, err; 3128c2ecf20Sopenharmony_ci int gtf_count = gtf_length / REGS_PER_GTF; 3138c2ecf20Sopenharmony_ci int ix; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci DEBPRINT("total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n", 3168c2ecf20Sopenharmony_ci gtf_length, gtf_length, gtf_count, gtf_address); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* send all taskfile registers (0x1f1-0x1f7) *in*that*order* */ 3198c2ecf20Sopenharmony_ci for (ix = 0; ix < gtf_count; ix++) { 3208c2ecf20Sopenharmony_ci u8 *gtf = (u8 *)(gtf_address + ix * REGS_PER_GTF); 3218c2ecf20Sopenharmony_ci struct ide_cmd cmd; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci DEBPRINT("(0x1f1-1f7): " 3248c2ecf20Sopenharmony_ci "hex: %02x %02x %02x %02x %02x %02x %02x\n", 3258c2ecf20Sopenharmony_ci gtf[0], gtf[1], gtf[2], 3268c2ecf20Sopenharmony_ci gtf[3], gtf[4], gtf[5], gtf[6]); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!ide_acpigtf) { 3298c2ecf20Sopenharmony_ci DEBPRINT("_GTF execution disabled\n"); 3308c2ecf20Sopenharmony_ci continue; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* convert GTF to taskfile */ 3348c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 3358c2ecf20Sopenharmony_ci memcpy(&cmd.tf.feature, gtf, REGS_PER_GTF); 3368c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; 3378c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci err = ide_no_data_taskfile(drive, &cmd); 3408c2ecf20Sopenharmony_ci if (err) { 3418c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: ide_no_data_taskfile failed: %u\n", 3428c2ecf20Sopenharmony_ci __func__, err); 3438c2ecf20Sopenharmony_ci rc = err; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return rc; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/** 3518c2ecf20Sopenharmony_ci * ide_acpi_exec_tfs - get then write drive taskfile settings 3528c2ecf20Sopenharmony_ci * @drive: the drive for which the taskfile settings should be 3538c2ecf20Sopenharmony_ci * written. 3548c2ecf20Sopenharmony_ci * 3558c2ecf20Sopenharmony_ci * According to the ACPI spec this should be called after _STM 3568c2ecf20Sopenharmony_ci * has been evaluated for the interface. Some ACPI vendors interpret 3578c2ecf20Sopenharmony_ci * that as a hard requirement and modify the taskfile according 3588c2ecf20Sopenharmony_ci * to the Identify Drive information passed down with _STM. 3598c2ecf20Sopenharmony_ci * So one should really make sure to call this only after _STM has 3608c2ecf20Sopenharmony_ci * been executed. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ciint ide_acpi_exec_tfs(ide_drive_t *drive) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int ret; 3658c2ecf20Sopenharmony_ci unsigned int gtf_length; 3668c2ecf20Sopenharmony_ci unsigned long gtf_address; 3678c2ecf20Sopenharmony_ci unsigned long obj_loc; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci DEBPRINT("call get_GTF, drive=%s port=%d\n", drive->name, drive->dn); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ret = do_drive_get_GTF(drive, >f_length, >f_address, &obj_loc); 3728c2ecf20Sopenharmony_ci if (ret < 0) { 3738c2ecf20Sopenharmony_ci DEBPRINT("get_GTF error (%d)\n", ret); 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci DEBPRINT("call set_taskfiles, drive=%s\n", drive->name); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret = do_drive_set_taskfiles(drive, gtf_length, gtf_address); 3808c2ecf20Sopenharmony_ci kfree((void *)obj_loc); 3818c2ecf20Sopenharmony_ci if (ret < 0) { 3828c2ecf20Sopenharmony_ci DEBPRINT("set_taskfiles error (%d)\n", ret); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci DEBPRINT("ret=%d\n", ret); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * ide_acpi_get_timing - get the channel (controller) timings 3928c2ecf20Sopenharmony_ci * @hwif: target IDE interface (channel) 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * This function executes the _GTM ACPI method for the target channel. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_civoid ide_acpi_get_timing(ide_hwif_t *hwif) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci acpi_status status; 4008c2ecf20Sopenharmony_ci struct acpi_buffer output; 4018c2ecf20Sopenharmony_ci union acpi_object *out_obj; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Setting up output buffer for _GTM */ 4048c2ecf20Sopenharmony_ci output.length = ACPI_ALLOCATE_BUFFER; 4058c2ecf20Sopenharmony_ci output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* _GTM has no input parameters */ 4088c2ecf20Sopenharmony_ci status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_GTM", 4098c2ecf20Sopenharmony_ci NULL, &output); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci DEBPRINT("_GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n", 4128c2ecf20Sopenharmony_ci status, output.pointer, 4138c2ecf20Sopenharmony_ci (unsigned long long)output.length); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 4168c2ecf20Sopenharmony_ci DEBPRINT("Run _GTM error: status = 0x%x\n", status); 4178c2ecf20Sopenharmony_ci return; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (!output.length || !output.pointer) { 4218c2ecf20Sopenharmony_ci DEBPRINT("Run _GTM: length or ptr is NULL (0x%llx, 0x%p)\n", 4228c2ecf20Sopenharmony_ci (unsigned long long)output.length, 4238c2ecf20Sopenharmony_ci output.pointer); 4248c2ecf20Sopenharmony_ci kfree(output.pointer); 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci out_obj = output.pointer; 4298c2ecf20Sopenharmony_ci if (out_obj->type != ACPI_TYPE_BUFFER) { 4308c2ecf20Sopenharmony_ci DEBPRINT("Run _GTM: error: " 4318c2ecf20Sopenharmony_ci "expected object type of ACPI_TYPE_BUFFER, " 4328c2ecf20Sopenharmony_ci "got 0x%x\n", out_obj->type); 4338c2ecf20Sopenharmony_ci kfree(output.pointer); 4348c2ecf20Sopenharmony_ci return; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!out_obj->buffer.length || !out_obj->buffer.pointer || 4388c2ecf20Sopenharmony_ci out_obj->buffer.length != sizeof(struct GTM_buffer)) { 4398c2ecf20Sopenharmony_ci printk(KERN_ERR 4408c2ecf20Sopenharmony_ci "%s: unexpected _GTM length (0x%x)[should be 0x%zx] or " 4418c2ecf20Sopenharmony_ci "addr (0x%p)\n", 4428c2ecf20Sopenharmony_ci __func__, out_obj->buffer.length, 4438c2ecf20Sopenharmony_ci sizeof(struct GTM_buffer), out_obj->buffer.pointer); 4448c2ecf20Sopenharmony_ci kfree(output.pointer); 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci memcpy(&hwif->acpidata->gtm, out_obj->buffer.pointer, 4498c2ecf20Sopenharmony_ci sizeof(struct GTM_buffer)); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%zx\n", 4528c2ecf20Sopenharmony_ci out_obj->buffer.pointer, out_obj->buffer.length, 4538c2ecf20Sopenharmony_ci sizeof(struct GTM_buffer)); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci DEBPRINT("_GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", 4568c2ecf20Sopenharmony_ci hwif->acpidata->gtm.PIO_speed0, 4578c2ecf20Sopenharmony_ci hwif->acpidata->gtm.DMA_speed0, 4588c2ecf20Sopenharmony_ci hwif->acpidata->gtm.PIO_speed1, 4598c2ecf20Sopenharmony_ci hwif->acpidata->gtm.DMA_speed1, 4608c2ecf20Sopenharmony_ci hwif->acpidata->gtm.GTM_flags); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci kfree(output.pointer); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/** 4668c2ecf20Sopenharmony_ci * ide_acpi_push_timing - set the channel (controller) timings 4678c2ecf20Sopenharmony_ci * @hwif: target IDE interface (channel) 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * This function executes the _STM ACPI method for the target channel. 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * _STM requires Identify Drive data, which has to passed as an argument. 4728c2ecf20Sopenharmony_ci * Unfortunately drive->id is a mangled version which we can't readily 4738c2ecf20Sopenharmony_ci * use; hence we'll get the information afresh. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_civoid ide_acpi_push_timing(ide_hwif_t *hwif) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci acpi_status status; 4788c2ecf20Sopenharmony_ci struct acpi_object_list input; 4798c2ecf20Sopenharmony_ci union acpi_object in_params[3]; 4808c2ecf20Sopenharmony_ci struct ide_acpi_drive_link *master = &hwif->acpidata->master; 4818c2ecf20Sopenharmony_ci struct ide_acpi_drive_link *slave = &hwif->acpidata->slave; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Give the GTM buffer + drive Identify data to the channel via the 4848c2ecf20Sopenharmony_ci * _STM method: */ 4858c2ecf20Sopenharmony_ci /* setup input parameters buffer for _STM */ 4868c2ecf20Sopenharmony_ci input.count = 3; 4878c2ecf20Sopenharmony_ci input.pointer = in_params; 4888c2ecf20Sopenharmony_ci in_params[0].type = ACPI_TYPE_BUFFER; 4898c2ecf20Sopenharmony_ci in_params[0].buffer.length = sizeof(struct GTM_buffer); 4908c2ecf20Sopenharmony_ci in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm; 4918c2ecf20Sopenharmony_ci in_params[1].type = ACPI_TYPE_BUFFER; 4928c2ecf20Sopenharmony_ci in_params[1].buffer.length = ATA_ID_WORDS * 2; 4938c2ecf20Sopenharmony_ci in_params[1].buffer.pointer = (u8 *)&master->idbuff; 4948c2ecf20Sopenharmony_ci in_params[2].type = ACPI_TYPE_BUFFER; 4958c2ecf20Sopenharmony_ci in_params[2].buffer.length = ATA_ID_WORDS * 2; 4968c2ecf20Sopenharmony_ci in_params[2].buffer.pointer = (u8 *)&slave->idbuff; 4978c2ecf20Sopenharmony_ci /* Output buffer: _STM has no output */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_STM", 5008c2ecf20Sopenharmony_ci &input, NULL); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5038c2ecf20Sopenharmony_ci DEBPRINT("Run _STM error: status = 0x%x\n", status); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci DEBPRINT("_STM status: %d\n", status); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/** 5098c2ecf20Sopenharmony_ci * ide_acpi_set_state - set the channel power state 5108c2ecf20Sopenharmony_ci * @hwif: target IDE interface 5118c2ecf20Sopenharmony_ci * @on: state, on/off 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * This function executes the _PS0/_PS3 ACPI method to set the power state. 5148c2ecf20Sopenharmony_ci * ACPI spec requires _PS0 when IDE power on and _PS3 when power off 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_civoid ide_acpi_set_state(ide_hwif_t *hwif, int on) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci ide_drive_t *drive; 5198c2ecf20Sopenharmony_ci int i; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (ide_noacpi_psx) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci DEBPRINT("ENTER:\n"); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* channel first and then drives for power on and verse versa for power off */ 5278c2ecf20Sopenharmony_ci if (on) 5288c2ecf20Sopenharmony_ci acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ide_port_for_each_present_dev(i, drive, hwif) { 5318c2ecf20Sopenharmony_ci if (drive->acpidata->obj_handle) 5328c2ecf20Sopenharmony_ci acpi_bus_set_power(drive->acpidata->obj_handle, 5338c2ecf20Sopenharmony_ci on ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!on) 5378c2ecf20Sopenharmony_ci acpi_bus_set_power(hwif->acpidata->obj_handle, 5388c2ecf20Sopenharmony_ci ACPI_STATE_D3_COLD); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/** 5428c2ecf20Sopenharmony_ci * ide_acpi_init_port - initialize the ACPI link for an IDE interface 5438c2ecf20Sopenharmony_ci * @hwif: target IDE interface (channel) 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * The ACPI spec is not quite clear when the drive identify buffer 5468c2ecf20Sopenharmony_ci * should be obtained. Calling IDENTIFY DEVICE during shutdown 5478c2ecf20Sopenharmony_ci * is not the best of ideas as the drive might already being put to 5488c2ecf20Sopenharmony_ci * sleep. And obviously we can't call it during resume. 5498c2ecf20Sopenharmony_ci * So we get the information during startup; but this means that 5508c2ecf20Sopenharmony_ci * any changes during run-time will be lost after resume. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_civoid ide_acpi_init_port(ide_hwif_t *hwif) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci hwif->acpidata = kzalloc(sizeof(struct ide_acpi_hwif_link), GFP_KERNEL); 5558c2ecf20Sopenharmony_ci if (!hwif->acpidata) 5568c2ecf20Sopenharmony_ci return; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci hwif->acpidata->obj_handle = ide_acpi_hwif_get_handle(hwif); 5598c2ecf20Sopenharmony_ci if (!hwif->acpidata->obj_handle) { 5608c2ecf20Sopenharmony_ci DEBPRINT("no ACPI object for %s found\n", hwif->name); 5618c2ecf20Sopenharmony_ci kfree(hwif->acpidata); 5628c2ecf20Sopenharmony_ci hwif->acpidata = NULL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_civoid ide_acpi_port_init_devices(ide_hwif_t *hwif) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci ide_drive_t *drive; 5698c2ecf20Sopenharmony_ci int i, err; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (hwif->acpidata == NULL) 5728c2ecf20Sopenharmony_ci return; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* 5758c2ecf20Sopenharmony_ci * The ACPI spec mandates that we send information 5768c2ecf20Sopenharmony_ci * for both drives, regardless whether they are connected 5778c2ecf20Sopenharmony_ci * or not. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci hwif->devices[0]->acpidata = &hwif->acpidata->master; 5808c2ecf20Sopenharmony_ci hwif->devices[1]->acpidata = &hwif->acpidata->slave; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* get _ADR info for each device */ 5838c2ecf20Sopenharmony_ci ide_port_for_each_present_dev(i, drive, hwif) { 5848c2ecf20Sopenharmony_ci acpi_handle dev_handle; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci DEBPRINT("ENTER: %s at channel#: %d port#: %d\n", 5878c2ecf20Sopenharmony_ci drive->name, hwif->channel, drive->dn & 1); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* TBD: could also check ACPI object VALID bits */ 5908c2ecf20Sopenharmony_ci dev_handle = acpi_get_child(hwif->acpidata->obj_handle, 5918c2ecf20Sopenharmony_ci drive->dn & 1); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci DEBPRINT("drive %s handle 0x%p\n", drive->name, dev_handle); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci drive->acpidata->obj_handle = dev_handle; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* send IDENTIFY for each device */ 5998c2ecf20Sopenharmony_ci ide_port_for_each_present_dev(i, drive, hwif) { 6008c2ecf20Sopenharmony_ci err = taskfile_lib_get_identify(drive, drive->acpidata->idbuff); 6018c2ecf20Sopenharmony_ci if (err) 6028c2ecf20Sopenharmony_ci DEBPRINT("identify device %s failed (%d)\n", 6038c2ecf20Sopenharmony_ci drive->name, err); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (ide_noacpi || ide_acpionboot == 0) { 6078c2ecf20Sopenharmony_ci DEBPRINT("ACPI methods disabled on boot\n"); 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* ACPI _PS0 before _STM */ 6128c2ecf20Sopenharmony_ci ide_acpi_set_state(hwif, 1); 6138c2ecf20Sopenharmony_ci /* 6148c2ecf20Sopenharmony_ci * ACPI requires us to call _STM on startup 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci ide_acpi_get_timing(hwif); 6178c2ecf20Sopenharmony_ci ide_acpi_push_timing(hwif); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ide_port_for_each_present_dev(i, drive, hwif) { 6208c2ecf20Sopenharmony_ci ide_acpi_exec_tfs(drive); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci} 623