/* * drivers/ata/ahci_tegra.c * * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "ahci_tegra.h" #include "linux/pinctrl/consumer.h" #ifdef CONFIG_DEBUG_FS #include #include #define TEGRA_AHCI_DUMP_REGS(s, fmt, args...) { if (s != NULL)\ seq_printf(s, fmt, ##args); else printk(fmt, ##args); } #define TEGRA_AHCI_DUMP_STRING(s, str) { if (s != NULL)\ seq_puts(s, str); else printk(str); } #endif static int tegra_ahci_power_on(struct ahci_host_priv *hpriv); static int tegra_ahci_enable_clks(struct ahci_host_priv *hpriv); static void tegra_ahci_disable_clks(struct ahci_host_priv *hpriv); static void tegra_ahci_power_off(struct ahci_host_priv *hpriv); static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv); static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv); static int tegra_ahci_quirks(struct ahci_host_priv *hpriv); static int tegra_ahci_disable_features(struct ahci_host_priv *hpriv); static struct ahci_host_priv * tegra_ahci_platform_get_resources(struct tegra_ahci_priv *); static int tegra194_ahci_platform_get_resources(struct tegra_ahci_priv *tegra); static int tegra186_ahci_platform_get_resources(struct tegra_ahci_priv *tegra); static int tegra210_ahci_platform_get_resources(struct tegra_ahci_priv *tegra); #ifdef CONFIG_PM static int tegra_ahci_runtime_suspend(struct device *dev); static int tegra_ahci_runtime_resume(struct device *dev); #endif #ifdef CONFIG_PM_SLEEP static int tegra_ahci_suspend(struct device *dev); static int tegra_ahci_resume(struct device *dev); #endif static void tegra_ahci_shutdown(struct platform_device *pdev); static char * const tegra_rail_names[] = {}; static const struct tegra_ahci_soc_data tegra194_ahci_data = { .sata_regulator_names = tegra_rail_names, .num_sata_regulators = ARRAY_SIZE(tegra_rail_names), .ops = { .tegra_ahci_power_on = tegra_ahci_power_on, .tegra_ahci_power_off = tegra_ahci_power_off, .tegra_ahci_quirks = tegra_ahci_quirks, .tegra_ahci_platform_get_resources = tegra194_ahci_platform_get_resources, }, .reg = { .t_satao_nvoob_comma_cnt_mask = (0XFF << 16), .t_satao_nvoob_comma_cnt = (0X07 << 16), }, .enable_pose_edge = false, }; static const struct tegra_ahci_soc_data tegra186_ahci_data = { .sata_regulator_names = tegra_rail_names, .num_sata_regulators = ARRAY_SIZE(tegra_rail_names), .ops = { .tegra_ahci_power_on = tegra_ahci_power_on, .tegra_ahci_power_off = tegra_ahci_power_off, .tegra_ahci_quirks = tegra_ahci_quirks, .tegra_ahci_platform_get_resources = tegra186_ahci_platform_get_resources, }, .reg = { .t_satao_nvoob_comma_cnt_mask = (0XFF << 16), .t_satao_nvoob_comma_cnt = (0X07 << 16), }, .enable_pose_edge = true, }; static const struct tegra_ahci_soc_data tegra210_ahci_data = { .sata_regulator_names = tegra_rail_names, .num_sata_regulators = ARRAY_SIZE(tegra_rail_names), .ops = { .tegra_ahci_power_on = tegra_ahci_power_on, .tegra_ahci_power_off = tegra_ahci_power_off, .tegra_ahci_quirks = tegra_ahci_quirks, .tegra_ahci_platform_get_resources = tegra210_ahci_platform_get_resources, }, .reg = { .t_satao_nvoob_comma_cnt_mask = (0X7 << 28), .t_satao_nvoob_comma_cnt = (0X7 << 28), }, .enable_pose_edge = true, }; static const struct of_device_id tegra_ahci_of_match[] = { { .compatible = "nvidia,tegra194-ahci-sata", .data = &tegra194_ahci_data, }, { .compatible = "nvidia,tegra186-ahci-sata", .data = &tegra186_ahci_data, }, { .compatible = "nvidia,tegra210-ahci-sata", .data = &tegra210_ahci_data, }, {} }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); static void tegra_ahci_host_stop(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; tegra_ahci_controller_deinit(hpriv); } static int tegra_ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) { struct ata_host *host = ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; struct ata_link *link; struct ata_device *dev; int ret = 0; u32 port_status = 0; enum tegra_ahci_port_runtime_status lpm_state; int i; lpm_state = TEGRA_AHCI_PORT_RUNTIME_ACTIVE; tegra->skip_rtpm = false; if (!ata_dev_enabled(ap->link.device)) goto skip; port_status = tegra_ahci_bar5_readl(hpriv, T_AHCI_PORT_PXSSTS); port_status = (port_status & T_AHCI_PORT_PXSSTS_IPM_MASK) >> T_AHCI_PORT_PXSSTS_IPM_SHIFT; ata_for_each_link(link, ap, PMP_FIRST) { if (link->flags & ATA_LFLAG_NO_LPM) { ata_link_info(link, "No LPM on this link\n"); continue; } ata_for_each_dev(dev, link, ENABLED) { bool hipm = ata_id_has_hipm(dev->id); bool dipm = ata_id_has_dipm(dev->id) && (!(link->ap->flags & ATA_FLAG_NO_DIPM)); if (ap->target_lpm_policy == ATA_LPM_MIN_POWER) { if ((hpriv->cap2 & HOST_CAP2_SDS) && (hpriv->cap2 & HOST_CAP2_SADM) && (link->device->flags & ATA_DFLAG_DEVSLP)) lpm_state = TEGRA_AHCI_PORT_RUNTIME_DEVSLP; else lpm_state = TEGRA_AHCI_PORT_RUNTIME_SLUMBER; } else if (ap->target_lpm_policy == ATA_LPM_MED_POWER) { lpm_state = TEGRA_AHCI_PORT_RUNTIME_PARTIAL; } if (hipm || dipm) { for (i = 0; i < TEGRA_AHCI_LPM_TIMEOUT; i++) { port_status = tegra_ahci_bar5_readl( hpriv, T_AHCI_PORT_PXSSTS); port_status = (port_status & T_AHCI_PORT_PXSSTS_IPM_MASK) >> T_AHCI_PORT_PXSSTS_IPM_SHIFT; if (port_status < lpm_state) mdelay(10); else break; } if (port_status < lpm_state) { ata_link_err(link, "Link didn't enter LPM\n"); if (ap->pm_mesg.event & PM_EVENT_AUTO) ret = -EBUSY; } else { if (!(port_status == TEGRA_AHCI_PORT_RUNTIME_ACTIVE)) ata_link_info(link, "Link entered LPM\n"); } } else { ata_dev_info(dev, "does not support HIPM/DIPM\n"); } } } if (!ret && (lpm_state == TEGRA_AHCI_PORT_RUNTIME_ACTIVE || port_status == TEGRA_AHCI_PORT_RUNTIME_ACTIVE)) { if (ap->pm_mesg.event & PM_EVENT_AUTO) { tegra->skip_rtpm = true; dev_info(&tegra->pdev->dev, "Skip powergating SATA Controller\n"); return 0; } } skip: if (!ret && !(ap->pflags & ATA_PFLAG_SUSPENDED)) { ret = ahci_ops.port_suspend(ap, mesg); } return ret; } static int tegra_ahci_port_resume(struct ata_port *ap) { struct ata_host *host = ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; struct ata_link *link = NULL; #ifdef CONFIG_PM struct scsi_device *sdev = NULL; #endif int ret = 0; if (tegra->skip_rtpm) { tegra->skip_rtpm = false; if (ap->pm_mesg.event & PM_EVENT_AUTO) { ata_for_each_link(link, ap, HOST_FIRST) { link->eh_info.action &= ~ATA_EH_RESET; } ata_eh_thaw_port(ap); return 0; } } if (ap->pm_mesg.event & PM_EVENT_RESUME) { if (ap->pm_mesg.event & PM_EVENT_AUTO) ata_for_each_link(link, ap, HOST_FIRST) { link->eh_info.action &= ~ATA_EH_RESET; } #ifdef CONFIG_PM else shost_for_each_device(sdev, ap->scsi_host) { if (sdev->request_queue->rpm_status == RPM_SUSPENDED) sdev->request_queue->rpm_status = RPM_ACTIVE; } #endif } ret = ahci_ops.port_resume(ap); if ((ap->pm_mesg.event & PM_EVENT_AUTO) && (ap->pm_mesg.event & PM_EVENT_RESUME)) ata_eh_thaw_port(ap); return ret; } static int tegra_ahci_compliance_mode_testing; static ssize_t tegra_ahci_compliance_mode_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; u32 val; u32 mask; switch (buf[0]) { case '1': dev_info(dev, "Starting compliance mode." " unregistering from SATA subsystem\n"); tegra_ahci_compliance_mode_testing = 1; ata_host_detach(host); dev_info(dev, "compliance Testing can be started\n"); break; case '2': dev_info(dev, "Starting Rx compliance Test\n"); tegra_ahci_compliance_mode_testing = 2; tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA1, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA2, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); val = tegra_ahci_scfg_readl(hpriv, T_SATA_CFG_PHY_0); dev_info(dev, "SATA0_CFG_PHY_0_0 DONT_INSERT_ALIGNS_IN_BIST_L= %x\n", val); val = tegra_ahci_scfg_readl(hpriv, T_SATA0_SPARE_2); dev_info(dev, "SATA0_SPARE_2_0 REM_TWO_ALIGNS_IN_BIST_L = %x\n", val); val = tegra_ahci_scfg_readl(hpriv, T_SATA0_CHXCFG4_CHX); dev_info(dev, " SATA0_CHXCFG4_CHX_0 PHY_ALIGN_DWORD_CNT = %x\n", val); tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_CH1, T_SATA0_INDEX); mask = T_SATA0_CFG_PHY_0_DONT_INSERT_ALIGNS_IN_BIST_L; val = (u32) ~mask; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA_CFG_PHY_0); tegra_ahci_scfg_update(hpriv, T_SATA0_SPARE_2_REM_TWO_ALIGNS_IN_BIST_L, T_SATA0_SPARE_2_REM_TWO_ALIGNS_IN_BIST_L, T_SATA0_SPARE_2); tegra_ahci_scfg_writel(hpriv, T_SATA0_CHXCFG4_CHX_DATA, T_SATA0_CHXCFG4_CHX); tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_NONE_SELECTED, T_SATA0_INDEX); val = tegra_ahci_scfg_readl(hpriv, T_SATA_CFG_PHY_0); dev_info(dev, "SATA0_CFG_PHY_0_0 DONT_INSERT_ALIGNS_IN_BIST_L= %x\n", val); val = tegra_ahci_scfg_readl(hpriv, T_SATA0_SPARE_2); dev_info(dev, "SATA0_SPARE_2_0 REM_TWO_ALIGNS_IN_BIST_L = %x\n", val); val = tegra_ahci_scfg_readl(hpriv, T_SATA0_CHXCFG4_CHX); dev_info(dev, " SATA0_CHXCFG4_CHX_0 PHY_ALIGN_DWORD_CNT = %x\n", val); break; case '3': dev_info(dev, "Starting Tx compliance Test: HFTP\n"); tegra_ahci_compliance_mode_testing = 3; tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA1, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA3, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); break; case '4': dev_info(dev, "Starting Tx compliance Test: MFTP\n"); tegra_ahci_compliance_mode_testing = 4; tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_MFTP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA1, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_MFTP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA3, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); break; case '5': dev_info(dev, "Starting Tx compliance Test: LFTP\n"); tegra_ahci_compliance_mode_testing = 5; tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_LFTP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA1, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_LFTP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA3, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); break; case '6': dev_info(dev, "Starting Tx compliance Test: LBP\n"); tegra_ahci_compliance_mode_testing = 4; tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_LBP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA1, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_DWORD_DATA_LBP, T_SATA0_AHCI_HBA_BIST_DWORD); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL_DATA3, T_SATA0_AHCI_HBA_BIST_OVERRIDE_CTL); break; default: dev_info(dev, "unknown options...\n"); } return count; } static ssize_t tegra_ahci_compliance_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", tegra_ahci_compliance_mode_testing); } static DEVICE_ATTR(tegra_ahci_compliance_mode_testing, S_IWUSR | S_IRUGO, tegra_ahci_compliance_mode_show, tegra_ahci_compliance_mode_set); static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc) { if (qc->tf.command == ATA_CMD_SET_FEATURES && qc->tf.feature == SATA_FPDMA_OFFSET) { WARN(1, "SATA_FPDMA_OFFSET Feature is not supported"); return AC_ERR_INVALID; } else if (qc->tf.command == ATA_CMD_READ_LOG_EXT && qc->tf.lbal == ATA_LOG_SATA_NCQ) { u8 *buf = (u8 *) page_address((const struct page *)qc->sg->page_link); /* * Since our SATA Controller does not support this command * don't send this command to the drive instead complete * the function here and indicate to the upper layer * that there is no entries in the buffer. */ buf += qc->sg->offset; buf[0] = TEGRA_AHCI_READ_LOG_EXT_NOENTRY; qc->complete_fn(qc); return 0; } return ahci_ops.qc_issue(qc); } static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_host *host = link->ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; u32 val; u32 mask; ret = ahci_ops.hardreset(link, class, deadline); if (ret < 0 && tegra->soc_data->enable_pose_edge) { mask = val = T_SATA0_CFG_LINK_0_USE_POSEDGE_SCTL_DET; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_CFG_LINK_0); } return ret; } static int tegra_ahci_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_host *host = link->ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; u32 val; u32 mask; ret = ahci_ops.softreset(link, class, deadline); if (ret < 0 && tegra->soc_data->enable_pose_edge) { mask = val = T_SATA0_CFG_LINK_0_USE_POSEDGE_SCTL_DET; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_CFG_LINK_0); } return ret; } static int tegra_ahci_umh(unsigned long long block, char *block_dev) { char *envp[] = { "HOME=/", "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin", NULL }; char *argv[TEGRA_BADBLK_MAX_ARGUMENTS + 1]; char buf1[TEGRA_BADBLK_STRING_LENGTH] = { }; char buf2[TEGRA_BADBLK_STRING_LENGTH] = { }; int ret = 0; snprintf(buf1, sizeof(buf1), "%llu", block); snprintf(buf2, sizeof(buf2), "%s", block_dev); argv[TEGRA_BADBLK_COMMAND] = "/system/bin/badblk.sh"; argv[TEGRA_BADBLK_COMMAND_PARAM1] = buf1; argv[TEGRA_BADBLK_COMMAND_PARAM2] = buf2; argv[TEGRA_BADBLK_MAX_ARGUMENTS] = NULL; ret = call_usermodehelper(argv[TEGRA_BADBLK_COMMAND], argv, envp, UMH_WAIT_PROC); if (ret == -ENOENT) { argv[TEGRA_BADBLK_COMMAND] = "/home/ubuntu/badblk.sh"; envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ret = call_usermodehelper(argv[TEGRA_BADBLK_COMMAND], argv, envp, UMH_WAIT_PROC); } return ret; } static void tegra_ahci_badblk_manage(struct work_struct *work) { struct tegra_ahci_badblk_priv *badblk = container_of(work, struct tegra_ahci_badblk_priv, badblk_work); struct tegra_ahci_priv *tegra = container_of(badblk, struct tegra_ahci_priv, badblk); struct tegra_ahci_badblk_info *temp = NULL; struct platform_device *pdev = tegra->pdev; int ret = 0; spin_lock(&tegra->badblk.badblk_lock); temp = tegra->badblk.head; if (!temp) { spin_unlock(&tegra->badblk.badblk_lock); return; } tegra->badblk.head = temp->next; spin_unlock(&tegra->badblk.badblk_lock); ret = tegra_ahci_umh(temp->block, temp->block_dev); devm_kfree(&pdev->dev, temp); } static void tegra_ahci_unbind(struct work_struct *work) { struct tegra_ahci_priv *tegra = container_of(to_delayed_work(work), struct tegra_ahci_priv, work); struct platform_device *pdev = tegra->pdev; struct ata_host *host = platform_get_drvdata(pdev); int i = 0; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; unsigned long flags; spin_lock_irqsave(ap->lock, flags); if (ap && ((ap->pflags & ATA_PFLAG_LOADING) || (ap->pflags & ATA_PFLAG_INITIALIZING))) { INIT_DELAYED_WORK(&tegra->work, tegra_ahci_unbind); schedule_delayed_work(&tegra->work, msecs_to_jiffies(1000)); spin_unlock_irqrestore(ap->lock, flags); return; } spin_unlock_irqrestore(ap->lock, flags); } tegra_ahci_shutdown(pdev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pdev->dev.driver->pm = NULL; pdev->dev.driver->shutdown = NULL; } static char *tegra_ahci_get_disk_name(struct scsi_cmnd *scsicmd) { if (scsicmd && scsicmd->request && scsicmd->request->rq_disk) return scsicmd->request->rq_disk->disk_name; else return NULL; } /** * tegra_ata_tf_read_block - Read block address from ATA taskfile * @tf: ATA taskfile of interest * @dev: ATA device @tf belongs to * * LOCKING: * None. * * Read block address from @tf. This function can handle all * three address formats - LBA, LBA48 and CHS. tf->protocol and * flags select the address format to use. * * RETURNS: * Block address read from @tf. */ u64 tegra_ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) { u64 block = 0; if (tf->flags & ATA_TFLAG_LBA) { if (tf->flags & ATA_TFLAG_LBA48) { block |= (u64)tf->hob_lbah << 40; block |= (u64)tf->hob_lbam << 32; block |= (u64)tf->hob_lbal << 24; } else block |= (tf->device & 0xf) << 24; block |= tf->lbah << 16; block |= tf->lbam << 8; block |= tf->lbal; } else { u32 cyl, head, sect; cyl = tf->lbam | (tf->lbah << 8); head = tf->device & 0xf; sect = tf->lbal; if (!sect) { ata_dev_warn(dev, "device reported invalid CHS sector 0\n"); sect = 1; /* oh well */ } block = ((u64)cyl * dev->heads + head) * dev->sectors + sect - 1; } return block; } static void tegra_ahci_schedule_badblk_work(struct ata_queued_cmd *qc, struct ata_device *dev) { unsigned long long sector; unsigned long long block; char *disk_name = NULL; struct tegra_ahci_badblk_info *temp = NULL; struct ata_host *host = qc->ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; disk_name = tegra_ahci_get_disk_name(qc->scsicmd); sector = tegra_ata_tf_read_block(&qc->tf, dev); block = (sector * 512) / 4096; if (spin_trylock(&tegra->badblk.badblk_lock)) { temp = devm_kzalloc(&pdev->dev, sizeof(struct tegra_ahci_badblk_info), GFP_ATOMIC); if (temp) { temp->block = block; if (disk_name) strncpy(temp->block_dev, disk_name, 99); temp->next = tegra->badblk.head; tegra->badblk.head = temp; schedule_work(&tegra->badblk.badblk_work); } spin_unlock(&tegra->badblk.badblk_lock); } } static void tegra_ahci_error_handler(struct ata_port *ap) { int tag; for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); if ((qc->flags & ATA_QCFLAG_FAILED) && (qc->result_tf.feature & (ATA_UNC | ATA_AMNF))) { struct ata_link *link; struct ata_device *dev; ata_for_each_link(link, qc->ap, PMP_FIRST) ata_for_each_dev(dev, link, ENABLED) tegra_ahci_schedule_badblk_work(qc, dev); } } ahci_ops.error_handler(ap); if (!ata_dev_enabled(ap->link.device)) { if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) { struct ata_host *host = ap->host; struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; INIT_DELAYED_WORK(&tegra->work, tegra_ahci_unbind); schedule_delayed_work(&tegra->work, msecs_to_jiffies(1000)); } } } static struct ata_port_operations ahci_tegra_port_ops = { .inherits = &ahci_ops, .qc_issue = tegra_ahci_qc_issue, .host_stop = tegra_ahci_host_stop, .port_suspend = tegra_ahci_port_suspend, .port_resume = tegra_ahci_port_resume, .hardreset = tegra_ahci_hardreset, .softreset = tegra_ahci_softreset, .error_handler = tegra_ahci_error_handler, }; static struct ata_port_info ahci_tegra_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_tegra_port_ops, }; static struct scsi_host_template ahci_platform_sht = { AHCI_SHT(DRV_NAME), }; #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM) static void tegra_ahci_pg_save_registers(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; u32 *pg_save; u32 regs; int i; pg_save = tegra->pg_save; /* save BAR5 registers */ regs = ARRAY_SIZE(pg_save_bar5_registers); tegra_ahci_save_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI], pg_save_bar5_registers, regs); /* save BAR5 port_registers */ regs = ARRAY_SIZE(pg_save_bar5_port_registers); for (i = 0; i < hpriv->nports; ++i) tegra_ahci_save_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI] + (0x80*i), pg_save_bar5_port_registers, regs); /* save bkdr registers */ regs = ARRAY_SIZE(pg_save_bar5_bkdr_registers); tegra_ahci_save_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI], pg_save_bar5_bkdr_registers, regs); /* and save bkdr per_port registers */ for (i = 0; i < hpriv->nports; ++i) { tegra_ahci_scfg_writel(hpriv, (1 << i), T_SATA0_INDEX); regs = ARRAY_SIZE(pg_save_bar5_bkdr_port_registers); tegra_ahci_save_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI] + (0x80*i), pg_save_bar5_bkdr_port_registers, regs); } tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_NONE_SELECTED, T_SATA0_INDEX); } static void tegra_ahci_pg_restore_registers(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; void *pg_save; u32 regs; int i; pg_save = tegra->pg_save; tegra_ahci_controller_init(hpriv); /* restore BAR5 registers */ regs = ARRAY_SIZE(pg_save_bar5_registers); tegra_ahci_restore_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI], pg_save_bar5_registers, regs); /* restore BAR5 port_registers */ regs = ARRAY_SIZE(pg_save_bar5_port_registers); for (i = 0; i < hpriv->nports; ++i) tegra_ahci_restore_regs(&pg_save, tegra->base_list[TEGRA_SATA_AHCI]+(0x80*i), pg_save_bar5_port_registers, regs); /* restore bkdr registers */ regs = ARRAY_SIZE(pg_restore_bar5_bkdr_registers); tegra_ahci_restore_regs(&pg_save, tegra->base_list[TEGRA_SATA_CONFIG], pg_restore_bar5_bkdr_registers, regs); /* and restore BAR5 bkdr per_port registers */ for (i = 0; i < hpriv->nports; ++i) { tegra_ahci_scfg_writel(hpriv, (1 << i), T_SATA0_INDEX); regs = ARRAY_SIZE(pg_restore_bar5_bkdr_port_registers); tegra_ahci_restore_regs(&pg_save, tegra->base_list[TEGRA_SATA_CONFIG], pg_restore_bar5_bkdr_port_registers, regs); } tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_NONE_SELECTED, T_SATA0_INDEX); } static int tegra_ahci_pg_save_restore_init(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; u32 save_size; int ret = 0; /* Setup PG save/restore area: */ /* calculate the size */ save_size = ARRAY_SIZE(pg_save_bar5_registers) + ARRAY_SIZE(pg_save_bar5_bkdr_registers); /* and add save port_registers for all the ports */ save_size += ARRAY_SIZE(pg_save_bar5_port_registers) + ARRAY_SIZE(pg_save_bar5_bkdr_port_registers); /* * save_size is number of registers times number of bytes per * register to get total save size. */ save_size *= sizeof(u32); tegra->pg_save = devm_kzalloc(dev, save_size, GFP_KERNEL); if (!tegra->pg_save) ret = -ENOMEM; return 0; } static bool tegra_ahci_is_link_in_devslp(struct ahci_host_priv *hpriv) { u32 rval = 0; rval = tegra_ahci_aux_readl(hpriv, SATA_AUX_RX_STAT_INT_0); if (rval & SATA_DEVSLP) return true; return false; } static void tegra_ahci_override_devslp(struct ahci_host_priv *hpriv, bool override) { u32 val = 0; u32 mask = DEVSLP_OVERRIDE; if (override) val = DEVSLP_OVERRIDE; else val = 0xFFFFFF & ~DEVSLP_OVERRIDE; tegra_ahci_aux_update(hpriv, val, mask, SATA_AUX_MISC_CNTL_1_0); } static int tegra_ahci_elpg_enter(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret = 0; int i; /* * 1. Program the UPHY_LANE registers to put UPHY to IDDQ */ for (i = 0; i < hpriv->nports; i++) { if (!hpriv->phys[i]) continue; phy_power_off(hpriv->phys[i]); } /* 2. Program a register in the PMC to indicate to SATA that it is * entering power gating. This shall drive the pmc2sata_pg_info * signal */ tegra_pmc_sata_pwrgt_update(PMC_IMPL_SATA_PWRGT_0_PG_INFO, PMC_IMPL_SATA_PWRGT_0_PG_INFO); /* * 3. Do the context save procedure for SATA */ tegra_ahci_pg_save_registers(host); /* * 4. Check the assertion status of DEVSLP and * set the DEVSLP override with the following SATA AUX * registers accordingly. */ tegra->devslp_override = tegra_ahci_is_link_in_devslp(hpriv); if (tegra->devslp_override) tegra_ahci_override_devslp(hpriv, true); /* 5. Powergate */ tegra_ahci_disable_clks(hpriv); return ret; } static int tegra_ahci_elpg_exit(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret = 0; u32 val; u32 mask; int i; /* 1. unpowergate */ ret = tegra_ahci_enable_clks(hpriv); if (ret) return ret; /* 2. Restore SATA Registers */ tegra_ahci_pg_restore_registers(host); /* * During the restoration of the registers, the driver would now need to * restore the register T_SATA0_CFG_POWER_GATE_SSTS_RESTORED after the * ssts_det, ssts_spd are restored. This register is used to tell the * controller whether a drive existed earlier or not and move the PHY * state machines into either HR_slumber or not. */ tegra_ahci_scfg_update(hpriv, T_SATA0_CFG_POWER_GATE_SSTS_RESTORED, T_SATA0_CFG_POWER_GATE_SSTS_RESTORED, T_SATA0_CFG_POWER_GATE); /* 3. If devslp asserted, de-assert devslp */ if (tegra->devslp_override) { tegra_ahci_override_devslp(hpriv, false); val = mask = MDAT_TIMER_AFTER_PG_VALID; tegra_ahci_aux_update(hpriv, val, mask, SATA_AUX_SPARE_CFG0_0); } /* 4. Program a register in the PMC to indicate to SATA that it is * entering power gating. This shall drive the pmc2sata_pg_info * signal */ tegra_pmc_sata_pwrgt_update(PMC_IMPL_SATA_PWRGT_0_PG_INFO, ~PMC_IMPL_SATA_PWRGT_0_PG_INFO); if (tegra->devslp_override) { tegra->devslp_override = false; val = mask = T_SATA0_CFG_POWER_GATE_POWER_UNGATE_COMP; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_CFG_POWER_GATE); } /* * 5. Program the UPHY_LANE registers to bring up UPHY from IDDQ */ for (i = 0; i < hpriv->nports; i++) { if (!hpriv->phys[i]) continue; ret = phy_power_on(hpriv->phys[i]); if (ret) { phy_exit(hpriv->phys[i]); goto disable_phys; } } return 0; disable_phys: while (--i >= 0) { phy_power_off(hpriv->phys[i]); phy_exit(hpriv->phys[i]); } return ret; } #endif #ifdef CONFIG_PM static int tegra_ahci_runtime_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); int ret = 0; ret = tegra_ahci_elpg_enter(host); return ret; } static int tegra_ahci_runtime_resume(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); int ret = 0; ret = tegra_ahci_elpg_exit(host); return ret; } #endif #ifdef CONFIG_PM_SLEEP static int tegra_ahci_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; int i; if (!pm_runtime_suspended(dev)) { ret = ahci_platform_suspend_host(dev); if (ret) return ret; ret = tegra_ahci_elpg_enter(host); if (ret) return ret; } /* * If DEVSLP is asserted, PAD driver should enable the pull-up * of the GPIO pin by programming the following register before * entering LP0. */ if (tegra->devslp_override) { /* Yet to add the code - http://nvbugs/200132422 */ tegra->devslp_pinmux_override = true; } /* Place uphy to reset */ for (i = 0; i < hpriv->nports; i++) { if (!hpriv->phys[i]) continue; phy_exit(hpriv->phys[i]); } return 0; } static int tegra_ahci_resume(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; int i; for (i = 0; i < hpriv->nports; i++) { if (!hpriv->phys[i]) continue; ret = phy_init(hpriv->phys[i]); if (ret) goto disable_phys; } ret = tegra_ahci_elpg_exit(host); if (ret) goto disable_phys; /* * If DEVSLP is supported and GPIO pin is assigned to SATA, * PAD driver should disable the pull-up of the GPIO pin * by programming the following register after exiting LP0 * and the DEVSLP override in SATA AUX register has been set. */ if (tegra->devslp_pinmux_override) { /* Yet to add the code - http://nvbugs/200132422 */ tegra->devslp_pinmux_override = false; } ret = ahci_platform_resume_host(dev); if (ret) goto elpg_entry; /* We resumed so update PM runtime state */ pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; elpg_entry: tegra_ahci_elpg_enter(host); disable_phys: while (--i >= 0) phy_exit(hpriv->phys[i]); return ret; } #endif static const struct dev_pm_ops tegra_ahci_dev_rt_ops = { SET_SYSTEM_SLEEP_PM_OPS(tegra_ahci_suspend, tegra_ahci_resume) SET_RUNTIME_PM_OPS(tegra_ahci_runtime_suspend, tegra_ahci_runtime_resume, NULL) }; static int tegra_ahci_enable_clks(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; ret = clk_prepare_enable(tegra->sata_clk); if (ret) return ret; ret = clk_prepare_enable(tegra->sata_oob_clk); if (ret) clk_disable_unprepare(tegra->sata_clk); return ret; } static void tegra_ahci_disable_clks(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; clk_disable_unprepare(tegra->sata_oob_clk); clk_disable_unprepare(tegra->sata_clk); } static int tegra_ahci_power_on(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; ret = regulator_bulk_enable(tegra->soc_data->num_sata_regulators, tegra->supplies); if (ret) return ret; reset_control_assert(tegra->sata_rst); if (tegra->sata_oob_rst) reset_control_assert(tegra->sata_oob_rst); reset_control_assert(tegra->sata_cold_rst); /* set SATA clk and SATA_OOB clk source */ ret = clk_set_parent(tegra->sata_clk, tegra->pllp_uphy_clk); if (ret) goto disable_regulators; ret = clk_set_parent(tegra->sata_oob_clk, tegra->pllp_clk); if (ret) goto disable_regulators; ret = clk_set_rate(tegra->sata_clk, TEGRA_SATA_CORE_CLOCK_FREQ_HZ); if (ret) goto disable_regulators; ret = clk_set_rate(tegra->sata_oob_clk, TEGRA_SATA_OOB_CLOCK_FREQ_HZ); if (ret) goto disable_regulators; ret = ahci_platform_enable_resources(hpriv); if (ret) goto disable_regulators; reset_control_deassert(tegra->sata_rst); if (tegra->sata_oob_rst) reset_control_deassert(tegra->sata_oob_rst); reset_control_deassert(tegra->sata_cold_rst); ret = clk_prepare_enable(tegra->sata_clk); if (ret) goto disable_regulators; ret = clk_prepare_enable(tegra->sata_oob_clk); if (ret) goto disable_sata_clk; return 0; disable_sata_clk: clk_disable_unprepare(tegra->sata_clk); disable_regulators: regulator_bulk_disable(tegra->soc_data->num_sata_regulators, tegra->supplies); return ret; } static void tegra_ahci_power_off(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; ahci_platform_disable_resources(hpriv); reset_control_assert(tegra->sata_rst); if (tegra->sata_oob_rst) reset_control_assert(tegra->sata_oob_rst); reset_control_assert(tegra->sata_cold_rst); clk_disable_unprepare(tegra->sata_clk); clk_disable_unprepare(tegra->sata_oob_clk); regulator_bulk_disable(tegra->soc_data->num_sata_regulators, tegra->supplies); } static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; unsigned int val; unsigned int mask; int ret = 0; /* Program the following SATA IPFS registers * to allow SW accesses to SATA.s MMIO Register */ mask = FPCI_BAR5_START_MASK | FPCI_BAR5_ACCESS_TYPE; val = FPCI_BAR5_START | FPCI_BAR5_ACCESS_TYPE; tegra_ahci_sata_update(hpriv, val, mask, SATA_FPCI_BAR5_0); /* Program the following SATA IPFS register to enable the SATA */ val = SATA_CONFIGURATION_0_EN_FPCI; tegra_ahci_sata_update(hpriv, val, val, SATA_CONFIGURATION_0); /* Electrical settings for better link stability */ tegra_ahci_scfg_writel(hpriv, T_SATA_CHX_PHY_CTRL17_RX_EQ_CTRL_L_GEN1, T_SATA_CHX_PHY_CTRL17); tegra_ahci_scfg_writel(hpriv, T_SATA_CHX_PHY_CTRL18_RX_EQ_CTRL_L_GEN2, T_SATA_CHX_PHY_CTRL18); tegra_ahci_scfg_writel(hpriv, T_SATA_CHX_PHY_CTRL20_RX_EQ_CTRL_H_GEN1, T_SATA_CHX_PHY_CTRL20); tegra_ahci_scfg_writel(hpriv, T_SATA_CHX_PHY_CTRL21_RX_EQ_CTRL_H_GEN2, T_SATA_CHX_PHY_CTRL21); /* tegra ahci quirks */ ret = tegra->soc_data->ops.tegra_ahci_quirks(hpriv); if (ret) return ret; /* Configure SATA Configuration registers*/ /* Program the following SATA configuration registers * to initialize SATA */ val = (T_SATA_CFG_1_IO_SPACE | T_SATA_CFG_1_MEMORY_SPACE | T_SATA_CFG_1_BUS_MASTER | T_SATA_CFG_1_SERR); tegra_ahci_scfg_update(hpriv, val, val, T_SATA_CFG_1); tegra_ahci_scfg_writel(hpriv, T_SATA_CFG_9_BASE_ADDRESS, T_SATA_CFG_9); /* Program Class Code and Programming interface for SATA */ val = T_SATA_CFG_SATA_BACKDOOR_PROG_IF_EN; tegra_ahci_scfg_update(hpriv, val, val, T_SATA_CFG_SATA); mask = T_SATA_BKDOOR_CC_CLASS_CODE_MASK | T_SATA_BKDOOR_CC_PROG_IF_MASK; val = T_SATA_BKDOOR_CC_CLASS_CODE | T_SATA_BKDOOR_CC_PROG_IF; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA_BKDOOR_CC); mask = T_SATA_CFG_SATA_BACKDOOR_PROG_IF_EN; val = (u32) ~T_SATA_CFG_SATA_BACKDOOR_PROG_IF_EN; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA_CFG_SATA); /* Enabling LPM capabilities through Backdoor Programming */ val = (T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP | T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP | T_SATA0_AHCI_HBA_CAP_BKDR_SALP | T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM); tegra_ahci_scfg_update(hpriv, val, val, T_SATA0_AHCI_HBA_CAP_BKDR); /* SATA Second Level Clock Gating configuration */ /* Enabling Gating of Tx/Rx clocks and driving Pad IDDQ and Lane * IDDQ Signals */ mask = T_SATA0_CFG_35_IDP_INDEX_MASK; val = T_SATA0_CFG_35_IDP_INDEX; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_CFG_35); tegra_ahci_scfg_writel(hpriv, T_SATA0_AHCI_IDP1_DATA, T_SATA0_AHCI_IDP1); val = (T_SATA0_CFG_PHY_1_PADS_IDDQ_EN | T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN); tegra_ahci_scfg_update(hpriv, val, val, T_SATA0_CFG_PHY_1); /* * Indicate Sata only has the capability to enter DevSleep * from slumber link. */ tegra_ahci_aux_update(hpriv, DESO_SUPPORT, DESO_SUPPORT, SATA_AUX_MISC_CNTL_1_0); /* Enabling IPFS Clock Gating */ mask = SATA_CONFIGURATION_CLK_OVERRIDE; val = (u32) ~SATA_CONFIGURATION_CLK_OVERRIDE; tegra_ahci_sata_update(hpriv, val, mask, SATA_CONFIGURATION_0); val = IP_INT_MASK; tegra_ahci_sata_update(hpriv, val, val, SATA_INTR_MASK_0); ret = tegra_ahci_disable_features(hpriv); return ret; } static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv) { #ifdef CONFIG_PM struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; if (tegra_platform_is_silicon()) tegra_ahci_runtime_suspend(dev); pm_runtime_disable(dev); #endif } static void tegra_ahci_disable_devslp(struct ahci_host_priv *hpriv) { u32 val = 0; u32 mask = SDS_SUPPORT; val = 0xFFFFFFFF & ~SDS_SUPPORT; tegra_ahci_aux_update(hpriv, val, mask, SATA_AUX_MISC_CNTL_1_0); } static void tegra_ahci_disable_hipm(struct ahci_host_priv *hpriv) { u32 val = 0; u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SALP; val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SALP; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); } static void tegra_ahci_disable_partial(struct ahci_host_priv *hpriv) { u32 val = 0; u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP; val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); } static void tegra_ahci_disable_slumber(struct ahci_host_priv *hpriv) { u32 val = 0; u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP; val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); } static void tegra_ahci_disable_ncq(struct ahci_host_priv *hpriv) { u32 val = 0; u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ; val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); } static int tegra_ahci_disable_features(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct property *prop; const char *feature; bool devslp_enabled = true; int ret = 0; if (of_property_count_strings(np, "nvidia,disable-features") <= 0) return 0; of_property_for_each_string(np, "nvidia,disable-features", prop, feature) { if (!strcmp(feature, "devslp")) { tegra_ahci_disable_devslp(hpriv); devslp_enabled = false; } else if (!strcmp(feature, "hipm")) { tegra_ahci_disable_hipm(hpriv); } else if (!strcmp(feature, "ncq")) { tegra_ahci_disable_ncq(hpriv); } else if (!strcmp(feature, "dipm")) { ahci_tegra_port_info.flags |= ATA_FLAG_NO_DIPM; } else if (!strcmp(feature, "partial")) { tegra_ahci_disable_partial(hpriv); } else if (!strcmp(feature, "slumber")) { tegra_ahci_disable_slumber(hpriv); } } if (devslp_enabled) { if (gpio_is_valid(tegra->devslp_gpio)) { ret = gpio_request(tegra->devslp_gpio, "ahci-tegra"); if (ret != 0) { dev_err(&pdev->dev, "gpio request failed\n"); return ret; } gpio_direction_output(tegra->devslp_gpio, 1); } if (tegra->devslp_pin) { ret = pinctrl_select_state(tegra->devslp_pin, tegra->devslp_active); if (ret < 0) { dev_err(&pdev->dev, "setting devslp pin state failed\n"); return ret; } } } return ret; } static int tegra_ahci_quirks(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; u32 t_satao_nvoob_comma_cnt_mask = tegra->soc_data->reg.t_satao_nvoob_comma_cnt_mask; u32 t_satao_nvoob_comma_cnt = tegra->soc_data->reg.t_satao_nvoob_comma_cnt; unsigned int val; unsigned int mask; int ret = 0; /* SATA WARS */ /* For SQUELCH Filter & Gen3 drive getting detected as Gen1 drive */ mask = T_SATA_CFG_PHY_0_MASK_SQUELCH | T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD; val = T_SATA_CFG_PHY_0_MASK_SQUELCH; val &= ~T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA_CFG_PHY_0); mask = (t_satao_nvoob_comma_cnt_mask | T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK | T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK); val = (t_satao_nvoob_comma_cnt | T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH | T_SATA0_NVOOB_SQUELCH_FILTER_MODE); tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_NVOOB); /* Change CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW from 83.3 ns to 58.8ns */ mask = T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK; val = T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW; tegra_ahci_scfg_update(hpriv, val, mask, T_SATA0_CFG2NVOOB_2); if (tegra && tegra->prod_list) { tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_CH1, T_SATA0_INDEX); ret = tegra_prod_set_by_name(tegra->base_list, "prod", tegra->prod_list); if (ret) dev_err(dev, "Prod setting from DT failed\n"); tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_NONE_SELECTED, T_SATA0_INDEX); } return ret; } static int tegra_ahci_platform_get_clks_resets(struct tegra_ahci_priv *tegra) { struct platform_device *pdev = tegra->pdev; int ret = 0; tegra->sata_clk = devm_clk_get(&pdev->dev, "sata"); if (IS_ERR(tegra->sata_clk)) { dev_err(&pdev->dev, "Failed to get sata clock\n"); ret = PTR_ERR(tegra->sata_clk); goto err_out; } tegra->sata_oob_clk = devm_clk_get(&pdev->dev, "sata-oob"); if (IS_ERR(tegra->sata_oob_clk)) { dev_err(&pdev->dev, "Failed to get sata_oob clock\n"); ret = PTR_ERR(tegra->sata_oob_clk); goto err_out; } tegra->pllp_clk = devm_clk_get(&pdev->dev, "pllp"); if (IS_ERR(tegra->pllp_clk)) { dev_err(&pdev->dev, "Failed to get pllp clock\n"); ret = PTR_ERR(tegra->pllp_clk); goto err_out; } tegra->sata_rst = devm_reset_control_get(&pdev->dev, "sata"); if (IS_ERR(tegra->sata_rst)) { dev_err(&pdev->dev, "Failed to get sata reset\n"); ret = PTR_ERR(tegra->sata_rst); goto err_out; } tegra->sata_cold_rst = devm_reset_control_get(&pdev->dev, "sata-cold"); if (IS_ERR(tegra->sata_cold_rst)) { dev_err(&pdev->dev, "Failed to get sata-cold reset\n"); ret = PTR_ERR(tegra->sata_cold_rst); goto err_out; } return 0; err_out: return ret; } static int tegra_ahci_platform_get_memory_resources(struct tegra_ahci_priv *tegra) { struct platform_device *pdev = tegra->pdev; struct resource *res; int ret = 0; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-ahci"); tegra->base_list[TEGRA_SATA_AHCI] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tegra->base_list[TEGRA_SATA_AHCI])) { ret = PTR_ERR(tegra->base_list[TEGRA_SATA_AHCI]); goto err_out; } tegra->res[TEGRA_SATA_AHCI] = res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-config"); tegra->base_list[TEGRA_SATA_CONFIG] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tegra->base_list[TEGRA_SATA_CONFIG])) { ret = PTR_ERR(tegra->base_list[TEGRA_SATA_CONFIG]); goto err_out; } tegra->res[TEGRA_SATA_CONFIG] = res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-ipfs"); tegra->base_list[TEGRA_SATA_IPFS] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tegra->base_list[TEGRA_SATA_IPFS])) { ret = PTR_ERR(tegra->base_list[TEGRA_SATA_IPFS]); goto err_out; } tegra->res[TEGRA_SATA_IPFS] = res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-aux"); tegra->base_list[TEGRA_SATA_AUX] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tegra->base_list[TEGRA_SATA_AUX])) { ret = PTR_ERR(tegra->base_list[TEGRA_SATA_AUX]); goto err_out; } tegra->res[TEGRA_SATA_AUX] = res; return 0; err_out: return ret; } static int tegra_ahci_set_lpm(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; struct ata_host *host = dev_get_drvdata(dev); struct device_node *np = dev->of_node; struct property *prop; const char *feature; enum ata_lpm_policy policy = ATA_LPM_MAX_POWER; int ret = 0; int i; if (of_property_count_strings(np, "nvidia,link-flags") <= 0) return 0; of_property_for_each_string(np, "nvidia,link-flags", prop, feature) { if (!strcmp(feature, "min_power")) policy = ATA_LPM_MIN_POWER; else if (!strcmp(feature, "max_power")) policy = ATA_LPM_MAX_POWER; else if (!strcmp(feature, "med_power")) policy = ATA_LPM_MED_POWER; } if (policy > ATA_LPM_MAX_POWER) { for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; ap->target_lpm_policy = policy; ata_port_schedule_eh(ap); } } return ret; } static int tegra194_ahci_platform_get_resources(struct tegra_ahci_priv *tegra) { tegra->pllp_uphy_clk = tegra->pllp_clk; tegra->sata_oob_rst = NULL; return 0; } static int tegra186_ahci_platform_get_resources(struct tegra_ahci_priv *tegra) { struct platform_device *pdev = tegra->pdev; tegra->pllp_uphy_clk = devm_clk_get(&pdev->dev, "pllp-uphy"); if (IS_ERR(tegra->pllp_uphy_clk)) { dev_err(&pdev->dev, "Failed to get pllp_uphy clock\n"); return PTR_ERR(tegra->pllp_uphy_clk); } tegra->sata_oob_rst = NULL; return 0; } static int tegra210_ahci_platform_get_resources(struct tegra_ahci_priv *tegra) { struct platform_device *pdev = tegra->pdev; tegra->pllp_uphy_clk = tegra->pllp_clk; tegra->sata_oob_rst = devm_reset_control_get(&pdev->dev, "sata-oob"); if (IS_ERR(tegra->sata_oob_rst)) { dev_err(&pdev->dev, "Failed to get sata-oob reset\n"); return PTR_ERR(tegra->sata_oob_rst); } return 0; } static struct ahci_host_priv * tegra_ahci_platform_get_resources(struct tegra_ahci_priv *tegra) { struct ahci_host_priv *hpriv; struct platform_device *pdev = tegra->pdev; struct device *dev = &pdev->dev; struct phy *phy = NULL; int ret = -ENOMEM; int i; int sz; hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) { ret = PTR_ERR(hpriv); goto err_out; } hpriv->plat_data = tegra; tegra->devslp_pin = devm_pinctrl_get(dev); if (IS_ERR(tegra->devslp_pin)) { ret = PTR_ERR(tegra->devslp_pin); dev_err(dev, "failed to get pinctrl: %d\n", ret); goto err_out; } tegra->devslp_active = pinctrl_lookup_state(tegra->devslp_pin, "devslp_active"); if (IS_ERR(tegra->devslp_active)) { dev_info(dev, "Missing devslp-active state\n"); tegra->devslp_pin = NULL; } else { tegra->devslp_pullup = pinctrl_lookup_state(tegra->devslp_pin, "devslp_pullup"); if (IS_ERR(tegra->devslp_pullup)) { dev_info(dev, "Missing devslp-pullup state\n"); tegra->devslp_pin = NULL; } } tegra->devslp_gpio = of_get_named_gpio(dev->of_node, "gpios", 0); ret = tegra_ahci_platform_get_memory_resources(tegra); if (ret) goto err_out; else hpriv->mmio = tegra->base_list[TEGRA_SATA_AHCI]; sz = sizeof(*hpriv->target_pwrs); hpriv->target_pwrs = devm_kzalloc(dev, sz, GFP_KERNEL); if (!hpriv->target_pwrs) { ret = -ENOMEM; goto err_out; } hpriv->target_pwrs[0] = devm_regulator_get_optional(dev, "target-3v3"); if (IS_ERR(hpriv->target_pwrs[0])) { ret = PTR_ERR(hpriv->target_pwrs[0]); if (ret == -EPROBE_DEFER) goto err_out; hpriv->target_pwrs[0] = NULL; } ret = tegra_ahci_platform_get_clks_resets(tegra); if (ret) goto err_out; if (tegra_platform_is_silicon()) { phy = devm_phy_optional_get(dev, "sata-phy"); if (!IS_ERR(phy)) { hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys), GFP_KERNEL); if (!hpriv->phys) { ret = -ENOMEM; goto err_out; } hpriv->phys[0] = phy; hpriv->nports = 1; } else { ret = PTR_ERR(phy); goto err_out; } } else { hpriv->nports = 1; } tegra->supplies = devm_kzalloc(dev, sizeof(*tegra->supplies) * tegra->soc_data->num_sata_regulators, GFP_KERNEL); if (IS_ERR(tegra->supplies)) { dev_err(dev, "memory allocation failed for tegra supplies\n"); ret = PTR_ERR(tegra->supplies); goto err_out; } for (i = 0; i < tegra->soc_data->num_sata_regulators; i++) tegra->supplies[i].supply = tegra->soc_data->sata_regulator_names[i]; ret = devm_regulator_bulk_get(dev, tegra->soc_data->num_sata_regulators, tegra->supplies); if (ret) { dev_err(&pdev->dev, "Failed to get regulators\n"); goto err_out; } ret = tegra->soc_data->ops.tegra_ahci_platform_get_resources(tegra); if (ret) goto err_out; tegra->prod_list = devm_tegra_prod_get(dev); if (IS_ERR(tegra->prod_list)) { dev_err(dev, "Prod Init failed\n"); tegra->prod_list = NULL; } #ifdef CONFIG_PM ret = tegra_ahci_pg_save_restore_init(hpriv); if (ret) { dev_err(&pdev->dev, "Failed to allocate memory for save and restore\n"); goto err_out; } #endif return hpriv; err_out: return ERR_PTR(ret); } static void tegra_ahci_shutdown(struct platform_device *pdev) { if (!pm_runtime_suspended(&pdev->dev)) ata_platform_remove_one(pdev); } static int tegra_ahci_probe(struct platform_device *pdev) { struct ahci_host_priv *hpriv; struct tegra_ahci_priv *tegra; const struct of_device_id *match = NULL; int ret; tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); if (!tegra) return -ENOMEM; tegra->pdev = pdev; match = of_match_device(of_match_ptr(tegra_ahci_of_match), &pdev->dev); if (!match) return -ENODEV; tegra->soc_data = (struct tegra_ahci_soc_data *)match->data; hpriv = tegra_ahci_platform_get_resources(tegra); if (IS_ERR(hpriv)) return PTR_ERR(hpriv); if (tegra_platform_is_silicon()) { ret = tegra->soc_data->ops.tegra_ahci_power_on(hpriv); if (ret) return ret; } ret = tegra_ahci_controller_init(hpriv); if (ret) goto poweroff_controller; INIT_WORK(&tegra->badblk.badblk_work, tegra_ahci_badblk_manage); tegra->badblk.head = NULL; spin_lock_init(&tegra->badblk.badblk_lock); ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, &ahci_platform_sht); if (ret) goto poweroff_controller; ret = tegra_ahci_set_lpm(hpriv); if (ret) { dev_err(&pdev->dev, "Failed to set lpm\n"); goto poweroff_controller; } ret = pm_runtime_set_active(&pdev->dev); if (ret) dev_dbg(&pdev->dev, "unable to set runtime pm active err=%d\n", ret); else pm_runtime_enable(&pdev->dev); ret = device_create_file(&pdev->dev, &dev_attr_tegra_ahci_compliance_mode_testing); if (ret) dev_warn(&pdev->dev, "Failed to create compliance mode attricute err=%d\n", ret); #ifdef CONFIG_DEBUG_FS tegra_ahci_dump_debuginit(hpriv); #endif return 0; poweroff_controller: if (tegra_platform_is_silicon()) tegra->soc_data->ops.tegra_ahci_power_off(hpriv); return ret; }; static struct platform_driver tegra_ahci_driver = { .probe = tegra_ahci_probe, .remove = ata_platform_remove_one, .shutdown = tegra_ahci_shutdown, .driver = { .name = "tegra-ahci", .of_match_table = tegra_ahci_of_match, #ifdef CONFIG_PM .pm = &tegra_ahci_dev_rt_ops, #endif }, }; module_platform_driver(tegra_ahci_driver); #ifdef CONFIG_DEBUG_FS static void tegra_ahci_dbg_print_regs(struct seq_file *s, u32 *ptr, u32 base, u32 regs) { #define REGS_PER_LINE 4 u32 i, j; u32 lines = regs / REGS_PER_LINE; for (i = 0; i < lines; i++) { TEGRA_AHCI_DUMP_REGS(s, "0x%08x: ", base+(i*16)); for (j = 0; j < REGS_PER_LINE; ++j) { TEGRA_AHCI_DUMP_REGS(s, "0x%08x ", readl(ptr)); ++ptr; } TEGRA_AHCI_DUMP_STRING(s, "\n"); } #undef REGS_PER_LINE } int tegra_ahci_dbg_dump_show(struct seq_file *s, void *data) { struct ahci_host_priv *hpriv = NULL; struct tegra_ahci_priv *tegra = NULL; u32 base; u32 *ptr; u32 i; if (s) hpriv = s->private; else hpriv = data; tegra = hpriv->plat_data; tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_CH1, T_SATA0_INDEX); base = tegra->res[TEGRA_SATA_CONFIG]->start; ptr = tegra->base_list[TEGRA_SATA_CONFIG]; TEGRA_AHCI_DUMP_STRING(s, "SATA CONFIG Registers:\n"); TEGRA_AHCI_DUMP_STRING(s, "----------------------\n"); tegra_ahci_dbg_print_regs(s, ptr, base, 0x400); base = tegra->res[TEGRA_SATA_AHCI]->start; ptr = hpriv->mmio; TEGRA_AHCI_DUMP_STRING(s, "\nAHCI HBA Registers:\n"); TEGRA_AHCI_DUMP_STRING(s, "-------------------\n"); tegra_ahci_dbg_print_regs(s, ptr, base, 64); for (i = 0; i < hpriv->nports; ++i) { base = (tegra->res[TEGRA_SATA_AHCI]->start) + 0x100 + (0x80*i); ptr = hpriv->mmio + 0x100; TEGRA_AHCI_DUMP_REGS(s, "\nPort %u Registers:\n", i); TEGRA_AHCI_DUMP_STRING(s, "---------------\n"); tegra_ahci_dbg_print_regs(s, ptr, base, 20); } tegra_ahci_scfg_writel(hpriv, T_SATA0_INDEX_NONE_SELECTED, T_SATA0_INDEX); return 0; } static int tegra_ahci_dbg_dump_open(struct inode *inode, struct file *file) { return single_open(file, tegra_ahci_dbg_dump_show, inode->i_private); } static const struct file_operations debug_fops = { .open = tegra_ahci_dbg_dump_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; int tegra_ahci_dump_debuginit(void *data) { (void) debugfs_create_file("tegra_ahci", S_IRUGO, NULL, data, &debug_fops); return 0; } EXPORT_SYMBOL(tegra_ahci_dump_debuginit); #endif MODULE_DESCRIPTION("Tegra AHCI SATA driver"); MODULE_LICENSE("GPL v2");