📄 libata-core.c
字号:
}/** * ata_xfer_mode2mask - Find matching xfer_mask for XFER_* * @xfer_mode: XFER_* of interest * * Return matching xfer_mask for @xfer_mode. * * LOCKING: * None. * * RETURNS: * Matching xfer_mask, 0 if no match found. */static unsigned int ata_xfer_mode2mask(u8 xfer_mode){ const struct ata_xfer_ent *ent; for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits) return 1 << (ent->shift + xfer_mode - ent->base); return 0;}/** * ata_xfer_mode2shift - Find matching xfer_shift for XFER_* * @xfer_mode: XFER_* of interest * * Return matching xfer_shift for @xfer_mode. * * LOCKING: * None. * * RETURNS: * Matching xfer_shift, -1 if no match found. */static int ata_xfer_mode2shift(unsigned int xfer_mode){ const struct ata_xfer_ent *ent; for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits) return ent->shift; return -1;}/** * ata_mode_string - convert xfer_mask to string * @xfer_mask: mask of bits supported; only highest bit counts. * * Determine string which represents the highest speed * (highest bit in @modemask). * * LOCKING: * None. * * RETURNS: * Constant C string representing highest speed listed in * @mode_mask, or the constant C string "<n/a>". */static const char *ata_mode_string(unsigned int xfer_mask){ static const char * const xfer_mode_str[] = { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5", "PIO6", "MWDMA0", "MWDMA1", "MWDMA2", "MWDMA3", "MWDMA4", "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44", "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7", }; int highbit; highbit = fls(xfer_mask) - 1; if (highbit >= 0 && highbit < ARRAY_SIZE(xfer_mode_str)) return xfer_mode_str[highbit]; return "<n/a>";}static const char *sata_spd_string(unsigned int spd){ static const char * const spd_str[] = { "1.5 Gbps", "3.0 Gbps", }; if (spd == 0 || (spd - 1) >= ARRAY_SIZE(spd_str)) return "<unknown>"; return spd_str[spd - 1];}void ata_dev_disable(struct ata_device *dev){ if (ata_dev_enabled(dev)) { if (ata_msg_drv(dev->link->ap)) ata_dev_printk(dev, KERN_WARNING, "disabled\n"); ata_acpi_on_disable(dev); ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET); dev->class++; }}static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy){ struct ata_link *link = dev->link; struct ata_port *ap = link->ap; u32 scontrol; unsigned int err_mask; int rc; /* * disallow DIPM for drivers which haven't set * ATA_FLAG_IPM. This is because when DIPM is enabled, * phy ready will be set in the interrupt status on * state changes, which will cause some drivers to * think there are errors - additionally drivers will * need to disable hot plug. */ if (!(ap->flags & ATA_FLAG_IPM) || !ata_dev_enabled(dev)) { ap->pm_policy = NOT_AVAILABLE; return -EINVAL; } /* * For DIPM, we will only enable it for the * min_power setting. * * Why? Because Disks are too stupid to know that * If the host rejects a request to go to SLUMBER * they should retry at PARTIAL, and instead it * just would give up. So, for medium_power to * work at all, we need to only allow HIPM. */ rc = sata_scr_read(link, SCR_CONTROL, &scontrol); if (rc) return rc; switch (policy) { case MIN_POWER: /* no restrictions on IPM transitions */ scontrol &= ~(0x3 << 8); rc = sata_scr_write(link, SCR_CONTROL, scontrol); if (rc) return rc; /* enable DIPM */ if (dev->flags & ATA_DFLAG_DIPM) err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_DIPM); break; case MEDIUM_POWER: /* allow IPM to PARTIAL */ scontrol &= ~(0x1 << 8); scontrol |= (0x2 << 8); rc = sata_scr_write(link, SCR_CONTROL, scontrol); if (rc) return rc; /* * we don't have to disable DIPM since IPM flags * disallow transitions to SLUMBER, which effectively * disable DIPM if it does not support PARTIAL */ break; case NOT_AVAILABLE: case MAX_PERFORMANCE: /* disable all IPM transitions */ scontrol |= (0x3 << 8); rc = sata_scr_write(link, SCR_CONTROL, scontrol); if (rc) return rc; /* * we don't have to disable DIPM since IPM flags * disallow all transitions which effectively * disable DIPM anyway. */ break; } /* FIXME: handle SET FEATURES failure */ (void) err_mask; return 0;}/** * ata_dev_enable_pm - enable SATA interface power management * @dev: device to enable power management * @policy: the link power management policy * * Enable SATA Interface power management. This will enable * Device Interface Power Management (DIPM) for min_power * policy, and then call driver specific callbacks for * enabling Host Initiated Power management. * * Locking: Caller. * Returns: -EINVAL if IPM is not supported, 0 otherwise. */void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy){ int rc = 0; struct ata_port *ap = dev->link->ap; /* set HIPM first, then DIPM */ if (ap->ops->enable_pm) rc = ap->ops->enable_pm(ap, policy); if (rc) goto enable_pm_out; rc = ata_dev_set_dipm(dev, policy);enable_pm_out: if (rc) ap->pm_policy = MAX_PERFORMANCE; else ap->pm_policy = policy; return /* rc */; /* hopefully we can use 'rc' eventually */}#ifdef CONFIG_PM/** * ata_dev_disable_pm - disable SATA interface power management * @dev: device to disable power management * * Disable SATA Interface power management. This will disable * Device Interface Power Management (DIPM) without changing * policy, call driver specific callbacks for disabling Host * Initiated Power management. * * Locking: Caller. * Returns: void */static void ata_dev_disable_pm(struct ata_device *dev){ struct ata_port *ap = dev->link->ap; ata_dev_set_dipm(dev, MAX_PERFORMANCE); if (ap->ops->disable_pm) ap->ops->disable_pm(ap);}#endif /* CONFIG_PM */void ata_lpm_schedule(struct ata_port *ap, enum link_pm policy){ ap->pm_policy = policy; ap->link.eh_info.action |= ATA_EHI_LPM; ap->link.eh_info.flags |= ATA_EHI_NO_AUTOPSY; ata_port_schedule_eh(ap);}#ifdef CONFIG_PMstatic void ata_lpm_enable(struct ata_host *host){ struct ata_link *link; struct ata_port *ap; struct ata_device *dev; int i; for (i = 0; i < host->n_ports; i++) { ap = host->ports[i]; ata_port_for_each_link(link, ap) { ata_link_for_each_dev(dev, link) ata_dev_disable_pm(dev); } }}static void ata_lpm_disable(struct ata_host *host){ int i; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; ata_lpm_schedule(ap, ap->pm_policy); }}#endif /* CONFIG_PM *//** * ata_devchk - PATA device presence detection * @ap: ATA channel to examine * @device: Device to examine (starting at zero) * * This technique was originally described in * Hale Landis's ATADRVR (www.ata-atapi.com), and * later found its way into the ATA/ATAPI spec. * * Write a pattern to the ATA shadow registers, * and if a device is present, it will respond by * correctly storing and echoing back the * ATA shadow register contents. * * LOCKING: * caller. */static unsigned int ata_devchk(struct ata_port *ap, unsigned int device){ struct ata_ioports *ioaddr = &ap->ioaddr; u8 nsect, lbal; ap->ops->dev_select(ap, device); iowrite8(0x55, ioaddr->nsect_addr); iowrite8(0xaa, ioaddr->lbal_addr); iowrite8(0xaa, ioaddr->nsect_addr); iowrite8(0x55, ioaddr->lbal_addr); iowrite8(0x55, ioaddr->nsect_addr); iowrite8(0xaa, ioaddr->lbal_addr); nsect = ioread8(ioaddr->nsect_addr); lbal = ioread8(ioaddr->lbal_addr); if ((nsect == 0x55) && (lbal == 0xaa)) return 1; /* we found a device */ return 0; /* nothing found */}/** * ata_dev_classify - determine device type based on ATA-spec signature * @tf: ATA taskfile register set for device to be identified * * Determine from taskfile register contents whether a device is * ATA or ATAPI, as per "Signature and persistence" section * of ATA/PI spec (volume 1, sect 5.14). * * LOCKING: * None. * * RETURNS: * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP or * %ATA_DEV_UNKNOWN the event of failure. */unsigned int ata_dev_classify(const struct ata_taskfile *tf){ /* Apple's open source Darwin code hints that some devices only * put a proper signature into the LBA mid/high registers, * So, we only check those. It's sufficient for uniqueness. * * ATA/ATAPI-7 (d1532v1r1: Feb. 19, 2003) specified separate * signatures for ATA and ATAPI devices attached on SerialATA, * 0x3c/0xc3 and 0x69/0x96 respectively. However, SerialATA * spec has never mentioned about using different signatures * for ATA/ATAPI devices. Then, Serial ATA II: Port * Multiplier specification began to use 0x69/0x96 to identify * port multpliers and 0x3c/0xc3 to identify SEMB device. * ATA/ATAPI-7 dropped descriptions about 0x3c/0xc3 and * 0x69/0x96 shortly and described them as reserved for * SerialATA. * * We follow the current spec and consider that 0x69/0x96 * identifies a port multiplier and 0x3c/0xc3 a SEMB device. */ if ((tf->lbam == 0) && (tf->lbah == 0)) { DPRINTK("found ATA device by sig\n"); return ATA_DEV_ATA; } if ((tf->lbam == 0x14) && (tf->lbah == 0xeb)) { DPRINTK("found ATAPI device by sig\n"); return ATA_DEV_ATAPI; } if ((tf->lbam == 0x69) && (tf->lbah == 0x96)) { DPRINTK("found PMP device by sig\n"); return ATA_DEV_PMP; } if ((tf->lbam == 0x3c) && (tf->lbah == 0xc3)) { printk(KERN_INFO "ata: SEMB device ignored\n"); return ATA_DEV_SEMB_UNSUP; /* not yet */ } DPRINTK("unknown device\n"); return ATA_DEV_UNKNOWN;}/** * ata_dev_try_classify - Parse returned ATA device signature * @dev: ATA device to classify (starting at zero) * @present: device seems present * @r_err: Value of error register on completion * * After an event -- SRST, E.D.D., or SATA COMRESET -- occurs, * an ATA/ATAPI-defined set of values is placed in the ATA * shadow registers, indicating the results of device detection * and diagnostics. * * Select the ATA device, and read the values from the ATA shadow * registers. Then parse according to the Error register value, * and the spec-defined values examined by ata_dev_classify(). * * LOCKING: * caller. * * RETURNS: * Device type - %ATA_DEV_ATA, %ATA_DEV_ATAPI or %ATA_DEV_NONE. */unsigned int ata_dev_try_classify(struct ata_device *dev, int present, u8 *r_err){ struct ata_port *ap = dev->link->ap; struct ata_taskfile tf; unsigned int class; u8 err; ap->ops->dev_select(ap, dev->devno); memset(&tf, 0, sizeof(tf)); ap->ops->tf_read(ap, &tf); err = tf.feature; if (r_err) *r_err = err; /* see if device passed diags: if master then continue and warn later */ if (err == 0 && dev->devno == 0) /* diagnostic fail : do nothing _YET_ */ dev->horkage |= ATA_HORKAGE_DIAGNOSTIC; else if (err == 1) /* do nothing */ ; else if ((dev->devno == 0) && (err == 0x81)) /* do nothing */ ; else return ATA_DEV_NONE; /* determine if device is ATA or ATAPI */ class = ata_dev_classify(&tf); if (class == ATA_DEV_UNKNOWN) { /* If the device failed diagnostic, it's likely to * have reported incorrect device signature too. * Assume ATA device if the device seems present but * device signature is invalid with diagnostic * failure. */ if (present && (dev->horkage & ATA_HORKAGE_DIAGNOSTIC)) class = ATA_DEV_ATA; else class = ATA_DEV_NONE; } else if ((class == ATA_DEV_ATA) && (ata_chk_status(ap) == 0)) class = ATA_DEV_NONE; return class;}/** * ata_id_string - Convert IDENTIFY DEVICE page into string * @id: IDENTIFY DEVICE results we will examine * @s: string into which data is output * @ofs: offset into identify device page * @len: length of string to return. must be an even number. * * The strings in the IDENTIFY DEVICE page are broken up into * 16-bit chunks. Run through the string, and output each * 8-bit chunk linearly, regardless of platform. * * LOCKING: * caller. */void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len){ unsigned int c; while (len > 0) { c = id[ofs] >> 8; *s = c; s++; c = id[ofs] & 0xff; *s = c; s++; ofs++; len -= 2; }}/** * ata_id_c_string - Convert IDENTIFY DEVICE page into C string * @id: IDENTIFY DEVICE results we will examine * @s: string into which data is output * @ofs: offset into identify device page * @len: length of string to return. must be an odd number. * * This function is identical to ata_id_string except that it * trims trailing spaces and terminates the resulting string with * null. @len must be actual maximum length (even number) + 1. * * LOCKING: * caller. */void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len){ unsigned char *p;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -