| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * SATA specific part of ATA helper library |
| * |
| * Copyright 2003-2004 Red Hat, Inc. All rights reserved. |
| * Copyright 2003-2004 Jeff Garzik |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/libata.h> |
| |
| #include "libata.h" |
| |
| /** |
| * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure |
| * @tf: Taskfile to convert |
| * @pmp: Port multiplier port |
| * @is_cmd: This FIS is for command |
| * @fis: Buffer into which data will output |
| * |
| * Converts a standard ATA taskfile to a Serial ATA |
| * FIS structure (Register - Host to Device). |
| * |
| * LOCKING: |
| * Inherited from caller. |
| */ |
| void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis) |
| { |
| fis[0] = 0x27; /* Register - Host to Device FIS */ |
| fis[1] = pmp & 0xf; /* Port multiplier number*/ |
| if (is_cmd) |
| fis[1] |= (1 << 7); /* bit 7 indicates Command FIS */ |
| |
| fis[2] = tf->command; |
| fis[3] = tf->feature; |
| |
| fis[4] = tf->lbal; |
| fis[5] = tf->lbam; |
| fis[6] = tf->lbah; |
| fis[7] = tf->device; |
| |
| fis[8] = tf->hob_lbal; |
| fis[9] = tf->hob_lbam; |
| fis[10] = tf->hob_lbah; |
| fis[11] = tf->hob_feature; |
| |
| fis[12] = tf->nsect; |
| fis[13] = tf->hob_nsect; |
| fis[14] = 0; |
| fis[15] = tf->ctl; |
| |
| fis[16] = tf->auxiliary & 0xff; |
| fis[17] = (tf->auxiliary >> 8) & 0xff; |
| fis[18] = (tf->auxiliary >> 16) & 0xff; |
| fis[19] = (tf->auxiliary >> 24) & 0xff; |
| } |
| EXPORT_SYMBOL_GPL(ata_tf_to_fis); |
| |
| /** |
| * ata_tf_from_fis - Convert SATA FIS to ATA taskfile |
| * @fis: Buffer from which data will be input |
| * @tf: Taskfile to output |
| * |
| * Converts a serial ATA FIS structure to a standard ATA taskfile. |
| * |
| * LOCKING: |
| * Inherited from caller. |
| */ |
| |
| void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf) |
| { |
| tf->command = fis[2]; /* status */ |
| tf->feature = fis[3]; /* error */ |
| |
| tf->lbal = fis[4]; |
| tf->lbam = fis[5]; |
| tf->lbah = fis[6]; |
| tf->device = fis[7]; |
| |
| tf->hob_lbal = fis[8]; |
| tf->hob_lbam = fis[9]; |
| tf->hob_lbah = fis[10]; |
| |
| tf->nsect = fis[12]; |
| tf->hob_nsect = fis[13]; |
| } |
| EXPORT_SYMBOL_GPL(ata_tf_from_fis); |
| |
| /** |
| * sata_link_scr_lpm - manipulate SControl IPM and SPM fields |
| * @link: ATA link to manipulate SControl for |
| * @policy: LPM policy to configure |
| * @spm_wakeup: initiate LPM transition to active state |
| * |
| * Manipulate the IPM field of the SControl register of @link |
| * according to @policy. If @policy is ATA_LPM_MAX_POWER and |
| * @spm_wakeup is %true, the SPM field is manipulated to wake up |
| * the link. This function also clears PHYRDY_CHG before |
| * returning. |
| * |
| * LOCKING: |
| * EH context. |
| * |
| * RETURNS: |
| * 0 on success, -errno otherwise. |
| */ |
| int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, |
| bool spm_wakeup) |
| { |
| struct ata_eh_context *ehc = &link->eh_context; |
| bool woken_up = false; |
| u32 scontrol; |
| int rc; |
| |
| rc = sata_scr_read(link, SCR_CONTROL, &scontrol); |
| if (rc) |
| return rc; |
| |
| switch (policy) { |
| case ATA_LPM_MAX_POWER: |
| /* disable all LPM transitions */ |
| scontrol |= (0x7 << 8); |
| /* initiate transition to active state */ |
| if (spm_wakeup) { |
| scontrol |= (0x4 << 12); |
| woken_up = true; |
| } |
| break; |
| case ATA_LPM_MED_POWER: |
| /* allow LPM to PARTIAL */ |
| scontrol &= ~(0x1 << 8); |
| scontrol |= (0x6 << 8); |
| break; |
| case ATA_LPM_MED_POWER_WITH_DIPM: |
| case ATA_LPM_MIN_POWER_WITH_PARTIAL: |
| case ATA_LPM_MIN_POWER: |
| if (ata_link_nr_enabled(link) > 0) |
| /* no restrictions on LPM transitions */ |
| scontrol &= ~(0x7 << 8); |
| else { |
| /* empty port, power off */ |
| scontrol &= ~0xf; |
| scontrol |= (0x1 << 2); |
| } |
| break; |
| default: |
| WARN_ON(1); |
| } |
| |
| rc = sata_scr_write(link, SCR_CONTROL, scontrol); |
| if (rc) |
| return rc; |
| |
| /* give the link time to transit out of LPM state */ |
| if (woken_up) |
| msleep(10); |
| |
| /* clear PHYRDY_CHG from SError */ |
| ehc->i.serror &= ~SERR_PHYRDY_CHG; |
| return sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG); |
| } |
| EXPORT_SYMBOL_GPL(sata_link_scr_lpm); |
| |
| /** |
| * ata_slave_link_init - initialize slave link |
| * @ap: port to initialize slave link for |
| * |
| * Create and initialize slave link for @ap. This enables slave |
| * link handling on the port. |
| * |
| * In libata, a port contains links and a link contains devices. |
| * There is single host link but if a PMP is attached to it, |
| * there can be multiple fan-out links. On SATA, there's usually |
| * a single device connected to a link but PATA and SATA |
| * controllers emulating TF based interface can have two - master |
| * and slave. |
| * |
| * However, there are a few controllers which don't fit into this |
| * abstraction too well - SATA controllers which emulate TF |
| * interface with both master and slave devices but also have |
| * separate SCR register sets for each device. These controllers |
| * need separate links for physical link handling |
| * (e.g. onlineness, link speed) but should be treated like a |
| * traditional M/S controller for everything else (e.g. command |
| * issue, softreset). |
| * |
| * slave_link is libata's way of handling this class of |
| * controllers without impacting core layer too much. For |
| * anything other than physical link handling, the default host |
| * link is used for both master and slave. For physical link |
| * handling, separate @ap->slave_link is used. All dirty details |
| * are implemented inside libata core layer. From LLD's POV, the |
| * only difference is that prereset, hardreset and postreset are |
| * called once more for the slave link, so the reset sequence |
| * looks like the following. |
| * |
| * prereset(M) -> prereset(S) -> hardreset(M) -> hardreset(S) -> |
| * softreset(M) -> postreset(M) -> postreset(S) |
| * |
| * Note that softreset is called only for the master. Softreset |
| * resets both M/S by definition, so SRST on master should handle |
| * both (the standard method will work just fine). |
| * |
| * LOCKING: |
| * Should be called before host is registered. |
| * |
| * RETURNS: |
| * 0 on success, -errno on failure. |
| */ |
| int ata_slave_link_init(struct ata_port *ap) |
| { |
| struct ata_link *link; |
| |
| WARN_ON(ap->slave_link); |
| WARN_ON(ap->flags & ATA_FLAG_PMP); |
| |
| link = kzalloc(sizeof(*link), GFP_KERNEL); |
| if (!link) |
| return -ENOMEM; |
| |
| ata_link_init(ap, link, 1); |
| ap->slave_link = link; |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(ata_slave_link_init); |
| |
| /** |
| * sata_lpm_ignore_phy_events - test if PHY event should be ignored |
| * @link: Link receiving the event |
| * |
| * Test whether the received PHY event has to be ignored or not. |
| * |
| * LOCKING: |
| * None: |
| * |
| * RETURNS: |
| * True if the event has to be ignored. |
| */ |
| bool sata_lpm_ignore_phy_events(struct ata_link *link) |
| { |
| unsigned long lpm_timeout = link->last_lpm_change + |
| msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY); |
| |
| /* if LPM is enabled, PHYRDY doesn't mean anything */ |
| if (link->lpm_policy > ATA_LPM_MAX_POWER) |
| return true; |
| |
| /* ignore the first PHY event after the LPM policy changed |
| * as it is might be spurious |
| */ |
| if ((link->flags & ATA_LFLAG_CHANGED) && |
| time_before(jiffies, lpm_timeout)) |
| return true; |
| |
| return false; |
| } |
| EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events); |