|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * drivers/ata/ahci_tegra.c | 
|  | * | 
|  | * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved. | 
|  | * | 
|  | * Author: | 
|  | *	Mikko Perttunen <mperttunen@nvidia.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/ahci_platform.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/regulator/consumer.h> | 
|  | #include <linux/reset.h> | 
|  |  | 
|  | #include <soc/tegra/fuse.h> | 
|  | #include <soc/tegra/pmc.h> | 
|  |  | 
|  | #include "ahci.h" | 
|  |  | 
|  | #define DRV_NAME "tegra-ahci" | 
|  |  | 
|  | #define SATA_CONFIGURATION_0				0x180 | 
|  | #define SATA_CONFIGURATION_0_EN_FPCI			BIT(0) | 
|  | #define SATA_CONFIGURATION_0_CLK_OVERRIDE			BIT(31) | 
|  |  | 
|  | #define SCFG_OFFSET					0x1000 | 
|  |  | 
|  | #define T_SATA0_CFG_1					0x04 | 
|  | #define T_SATA0_CFG_1_IO_SPACE				BIT(0) | 
|  | #define T_SATA0_CFG_1_MEMORY_SPACE			BIT(1) | 
|  | #define T_SATA0_CFG_1_BUS_MASTER			BIT(2) | 
|  | #define T_SATA0_CFG_1_SERR				BIT(8) | 
|  |  | 
|  | #define T_SATA0_CFG_9					0x24 | 
|  | #define T_SATA0_CFG_9_BASE_ADDRESS			0x40020000 | 
|  |  | 
|  | #define SATA_FPCI_BAR5					0x94 | 
|  | #define SATA_FPCI_BAR5_START_MASK			(0xfffffff << 4) | 
|  | #define SATA_FPCI_BAR5_START				(0x0040020 << 4) | 
|  | #define SATA_FPCI_BAR5_ACCESS_TYPE			(0x1) | 
|  |  | 
|  | #define SATA_INTR_MASK					0x188 | 
|  | #define SATA_INTR_MASK_IP_INT_MASK			BIT(16) | 
|  |  | 
|  | #define T_SATA0_CFG_35					0x94 | 
|  | #define T_SATA0_CFG_35_IDP_INDEX_MASK			(0x7ff << 2) | 
|  | #define T_SATA0_CFG_35_IDP_INDEX			(0x2a << 2) | 
|  |  | 
|  | #define T_SATA0_AHCI_IDP1				0x98 | 
|  | #define T_SATA0_AHCI_IDP1_DATA				(0x400040) | 
|  |  | 
|  | #define T_SATA0_CFG_PHY_1				0x12c | 
|  | #define T_SATA0_CFG_PHY_1_PADS_IDDQ_EN			BIT(23) | 
|  | #define T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN		BIT(22) | 
|  |  | 
|  | #define T_SATA0_NVOOB                                   0x114 | 
|  | #define T_SATA0_NVOOB_COMMA_CNT_MASK                    (0xff << 16) | 
|  | #define T_SATA0_NVOOB_COMMA_CNT                         (0x07 << 16) | 
|  | #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK          (0x3 << 24) | 
|  | #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE               (0x1 << 24) | 
|  | #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK        (0x3 << 26) | 
|  | #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH             (0x3 << 26) | 
|  |  | 
|  | #define T_SATA_CFG_PHY_0                                0x120 | 
|  | #define T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD     BIT(11) | 
|  | #define T_SATA_CFG_PHY_0_MASK_SQUELCH                   BIT(24) | 
|  |  | 
|  | #define T_SATA0_CFG2NVOOB_2				0x134 | 
|  | #define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK	(0x1ff << 18) | 
|  | #define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW	(0xc << 18) | 
|  |  | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR			0x300 | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP	BIT(13) | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP	BIT(14) | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR_SALP			BIT(26) | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM		BIT(17) | 
|  | #define T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ			BIT(30) | 
|  |  | 
|  | #define T_SATA0_BKDOOR_CC				0x4a4 | 
|  | #define T_SATA0_BKDOOR_CC_CLASS_CODE_MASK		(0xffff << 16) | 
|  | #define T_SATA0_BKDOOR_CC_CLASS_CODE			(0x0106 << 16) | 
|  | #define T_SATA0_BKDOOR_CC_PROG_IF_MASK			(0xff << 8) | 
|  | #define T_SATA0_BKDOOR_CC_PROG_IF			(0x01 << 8) | 
|  |  | 
|  | #define T_SATA0_CFG_SATA				0x54c | 
|  | #define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN		BIT(12) | 
|  |  | 
|  | #define T_SATA0_CFG_MISC				0x550 | 
|  |  | 
|  | #define T_SATA0_INDEX					0x680 | 
|  |  | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN1			0x690 | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK		0xff | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT		0 | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK		(0xff << 8) | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT	8 | 
|  |  | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN2			0x694 | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK		0xff | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT		0 | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK		(0xff << 12) | 
|  | #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT	12 | 
|  |  | 
|  | #define T_SATA0_CHX_PHY_CTRL2				0x69c | 
|  | #define T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1		0x23 | 
|  |  | 
|  | #define T_SATA0_CHX_PHY_CTRL11				0x6d0 | 
|  | #define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ		(0x2800 << 16) | 
|  |  | 
|  | #define T_SATA0_CHX_PHY_CTRL17_0			0x6e8 | 
|  | #define T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1	0x55010000 | 
|  | #define T_SATA0_CHX_PHY_CTRL18_0			0x6ec | 
|  | #define T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2	0x55010000 | 
|  | #define T_SATA0_CHX_PHY_CTRL20_0			0x6f4 | 
|  | #define T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1	0x1 | 
|  | #define T_SATA0_CHX_PHY_CTRL21_0			0x6f8 | 
|  | #define T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2	0x1 | 
|  |  | 
|  | /* AUX Registers */ | 
|  | #define SATA_AUX_MISC_CNTL_1_0				0x8 | 
|  | #define SATA_AUX_MISC_CNTL_1_0_DEVSLP_OVERRIDE		BIT(17) | 
|  | #define SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT		BIT(13) | 
|  | #define SATA_AUX_MISC_CNTL_1_0_DESO_SUPPORT		BIT(15) | 
|  |  | 
|  | #define SATA_AUX_RX_STAT_INT_0				0xc | 
|  | #define SATA_AUX_RX_STAT_INT_0_SATA_DEVSLP		BIT(7) | 
|  |  | 
|  | #define SATA_AUX_SPARE_CFG0_0				0x18 | 
|  | #define SATA_AUX_SPARE_CFG0_0_MDAT_TIMER_AFTER_PG_VALID	BIT(14) | 
|  |  | 
|  | #define FUSE_SATA_CALIB					0x124 | 
|  | #define FUSE_SATA_CALIB_MASK				0x3 | 
|  |  | 
|  | struct sata_pad_calibration { | 
|  | u8 gen1_tx_amp; | 
|  | u8 gen1_tx_peak; | 
|  | u8 gen2_tx_amp; | 
|  | u8 gen2_tx_peak; | 
|  | }; | 
|  |  | 
|  | static const struct sata_pad_calibration tegra124_pad_calibration[] = { | 
|  | {0x18, 0x04, 0x18, 0x0a}, | 
|  | {0x0e, 0x04, 0x14, 0x0a}, | 
|  | {0x0e, 0x07, 0x1a, 0x0e}, | 
|  | {0x14, 0x0e, 0x1a, 0x0e}, | 
|  | }; | 
|  |  | 
|  | struct tegra_ahci_ops { | 
|  | int (*init)(struct ahci_host_priv *hpriv); | 
|  | }; | 
|  |  | 
|  | struct tegra_ahci_soc { | 
|  | const char *const		*supply_names; | 
|  | u32				num_supplies; | 
|  | bool				supports_devslp; | 
|  | const struct tegra_ahci_ops	*ops; | 
|  | }; | 
|  |  | 
|  | struct tegra_ahci_priv { | 
|  | struct platform_device	   *pdev; | 
|  | void __iomem		   *sata_regs; | 
|  | void __iomem		   *sata_aux_regs; | 
|  | struct reset_control	   *sata_rst; | 
|  | struct reset_control	   *sata_oob_rst; | 
|  | struct reset_control	   *sata_cold_rst; | 
|  | /* Needs special handling, cannot use ahci_platform */ | 
|  | struct clk		   *sata_clk; | 
|  | struct regulator_bulk_data *supplies; | 
|  | const struct tegra_ahci_soc *soc; | 
|  | }; | 
|  |  | 
|  | static void tegra_ahci_handle_quirks(struct ahci_host_priv *hpriv) | 
|  | { | 
|  | struct tegra_ahci_priv *tegra = hpriv->plat_data; | 
|  | u32 val; | 
|  |  | 
|  | if (tegra->sata_aux_regs && !tegra->soc->supports_devslp) { | 
|  | val = readl(tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0); | 
|  | val &= ~SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT; | 
|  | writel(val, tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int tegra124_ahci_init(struct ahci_host_priv *hpriv) | 
|  | { | 
|  | struct tegra_ahci_priv *tegra = hpriv->plat_data; | 
|  | struct sata_pad_calibration calib; | 
|  | int ret; | 
|  | u32 val; | 
|  |  | 
|  | /* Pad calibration */ | 
|  | ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK]; | 
|  |  | 
|  | writel(BIT(0), tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); | 
|  |  | 
|  | val = readl(tegra->sata_regs + | 
|  | SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); | 
|  | val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; | 
|  | val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK; | 
|  | val |= calib.gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; | 
|  | val |= calib.gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + | 
|  | T_SATA0_CHX_PHY_CTRL1_GEN1); | 
|  |  | 
|  | val = readl(tegra->sata_regs + | 
|  | SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); | 
|  | val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; | 
|  | val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK; | 
|  | val |= calib.gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; | 
|  | val |= calib.gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + | 
|  | T_SATA0_CHX_PHY_CTRL1_GEN2); | 
|  |  | 
|  | writel(T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ, | 
|  | tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11); | 
|  | writel(T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1, | 
|  | tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2); | 
|  |  | 
|  | writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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->num_supplies, | 
|  | tegra->supplies); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SATA, | 
|  | tegra->sata_clk, | 
|  | tegra->sata_rst); | 
|  | if (ret) | 
|  | goto disable_regulators; | 
|  |  | 
|  | reset_control_assert(tegra->sata_oob_rst); | 
|  | reset_control_assert(tegra->sata_cold_rst); | 
|  |  | 
|  | ret = ahci_platform_enable_resources(hpriv); | 
|  | if (ret) | 
|  | goto disable_power; | 
|  |  | 
|  | reset_control_deassert(tegra->sata_cold_rst); | 
|  | reset_control_deassert(tegra->sata_oob_rst); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | disable_power: | 
|  | clk_disable_unprepare(tegra->sata_clk); | 
|  |  | 
|  | tegra_powergate_power_off(TEGRA_POWERGATE_SATA); | 
|  |  | 
|  | disable_regulators: | 
|  | regulator_bulk_disable(tegra->soc->num_supplies, 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); | 
|  | reset_control_assert(tegra->sata_oob_rst); | 
|  | reset_control_assert(tegra->sata_cold_rst); | 
|  |  | 
|  | clk_disable_unprepare(tegra->sata_clk); | 
|  | tegra_powergate_power_off(TEGRA_POWERGATE_SATA); | 
|  |  | 
|  | regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); | 
|  | } | 
|  |  | 
|  | static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) | 
|  | { | 
|  | struct tegra_ahci_priv *tegra = hpriv->plat_data; | 
|  | int ret; | 
|  | u32 val; | 
|  |  | 
|  | ret = tegra_ahci_power_on(hpriv); | 
|  | if (ret) { | 
|  | dev_err(&tegra->pdev->dev, | 
|  | "failed to power on AHCI controller: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Program the following SATA IPFS registers to allow SW accesses to | 
|  | * SATA's MMIO register range. | 
|  | */ | 
|  | val = readl(tegra->sata_regs + SATA_FPCI_BAR5); | 
|  | val &= ~(SATA_FPCI_BAR5_START_MASK | SATA_FPCI_BAR5_ACCESS_TYPE); | 
|  | val |= SATA_FPCI_BAR5_START | SATA_FPCI_BAR5_ACCESS_TYPE; | 
|  | writel(val, tegra->sata_regs + SATA_FPCI_BAR5); | 
|  |  | 
|  | /* Program the following SATA IPFS register to enable the SATA */ | 
|  | val = readl(tegra->sata_regs + SATA_CONFIGURATION_0); | 
|  | val |= SATA_CONFIGURATION_0_EN_FPCI; | 
|  | writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); | 
|  |  | 
|  | /* Electrical settings for better link stability */ | 
|  | val = T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL17_0); | 
|  | val = T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL18_0); | 
|  | val = T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL20_0); | 
|  | val = T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL21_0); | 
|  |  | 
|  | /* For SQUELCH Filter & Gen3 drive getting detected as Gen1 drive */ | 
|  |  | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0); | 
|  | val |= T_SATA_CFG_PHY_0_MASK_SQUELCH; | 
|  | val &= ~T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0); | 
|  |  | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB); | 
|  | val &= ~(T_SATA0_NVOOB_COMMA_CNT_MASK | | 
|  | T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK | | 
|  | T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK); | 
|  | val |= (T_SATA0_NVOOB_COMMA_CNT | | 
|  | T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH | | 
|  | T_SATA0_NVOOB_SQUELCH_FILTER_MODE); | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB); | 
|  |  | 
|  | /* | 
|  | * Change CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW from 83.3 ns to 58.8ns | 
|  | */ | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2); | 
|  | val &= ~T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK; | 
|  | val |= T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2); | 
|  |  | 
|  | if (tegra->soc->ops && tegra->soc->ops->init) | 
|  | tegra->soc->ops->init(hpriv); | 
|  |  | 
|  | /* | 
|  | * Program the following SATA configuration registers to | 
|  | * initialize SATA | 
|  | */ | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); | 
|  | val |= (T_SATA0_CFG_1_IO_SPACE | T_SATA0_CFG_1_MEMORY_SPACE | | 
|  | T_SATA0_CFG_1_BUS_MASTER | T_SATA0_CFG_1_SERR); | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); | 
|  | val = T_SATA0_CFG_9_BASE_ADDRESS; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9); | 
|  |  | 
|  | /* Program Class Code and Programming interface for SATA */ | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); | 
|  | val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); | 
|  |  | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); | 
|  | val &= | 
|  | ~(T_SATA0_BKDOOR_CC_CLASS_CODE_MASK | | 
|  | T_SATA0_BKDOOR_CC_PROG_IF_MASK); | 
|  | val |= T_SATA0_BKDOOR_CC_CLASS_CODE | T_SATA0_BKDOOR_CC_PROG_IF; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); | 
|  |  | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); | 
|  | val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); | 
|  |  | 
|  | /* Enabling LPM capabilities through Backdoor Programming */ | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR); | 
|  | 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); | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + 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 | 
|  | */ | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35); | 
|  | val &= ~T_SATA0_CFG_35_IDP_INDEX_MASK; | 
|  | val |= T_SATA0_CFG_35_IDP_INDEX; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35); | 
|  |  | 
|  | val = T_SATA0_AHCI_IDP1_DATA; | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_IDP1); | 
|  |  | 
|  | val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1); | 
|  | val |= (T_SATA0_CFG_PHY_1_PADS_IDDQ_EN | | 
|  | T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN); | 
|  | writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1); | 
|  |  | 
|  | /* Enabling IPFS Clock Gating */ | 
|  | val = readl(tegra->sata_regs + SATA_CONFIGURATION_0); | 
|  | val &= ~SATA_CONFIGURATION_0_CLK_OVERRIDE; | 
|  | writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); | 
|  |  | 
|  | tegra_ahci_handle_quirks(hpriv); | 
|  |  | 
|  | /* Unmask SATA interrupts */ | 
|  |  | 
|  | val = readl(tegra->sata_regs + SATA_INTR_MASK); | 
|  | val |= SATA_INTR_MASK_IP_INT_MASK; | 
|  | writel(val, tegra->sata_regs + SATA_INTR_MASK); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv) | 
|  | { | 
|  | tegra_ahci_power_off(hpriv); | 
|  | } | 
|  |  | 
|  | static void tegra_ahci_host_stop(struct ata_host *host) | 
|  | { | 
|  | struct ahci_host_priv *hpriv = host->private_data; | 
|  |  | 
|  | tegra_ahci_controller_deinit(hpriv); | 
|  | } | 
|  |  | 
|  | static struct ata_port_operations ahci_tegra_port_ops = { | 
|  | .inherits	= &ahci_ops, | 
|  | .host_stop	= tegra_ahci_host_stop, | 
|  | }; | 
|  |  | 
|  | static const struct ata_port_info ahci_tegra_port_info = { | 
|  | .flags		= AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, | 
|  | .pio_mask	= ATA_PIO4, | 
|  | .udma_mask	= ATA_UDMA6, | 
|  | .port_ops	= &ahci_tegra_port_ops, | 
|  | }; | 
|  |  | 
|  | static const char *const tegra124_supply_names[] = { | 
|  | "avdd", "hvdd", "vddio", "target-5v", "target-12v" | 
|  | }; | 
|  |  | 
|  | static const struct tegra_ahci_ops tegra124_ahci_ops = { | 
|  | .init = tegra124_ahci_init, | 
|  | }; | 
|  |  | 
|  | static const struct tegra_ahci_soc tegra124_ahci_soc = { | 
|  | .supply_names = tegra124_supply_names, | 
|  | .num_supplies = ARRAY_SIZE(tegra124_supply_names), | 
|  | .supports_devslp = false, | 
|  | .ops = &tegra124_ahci_ops, | 
|  | }; | 
|  |  | 
|  | static const struct tegra_ahci_soc tegra210_ahci_soc = { | 
|  | .supports_devslp = false, | 
|  | }; | 
|  |  | 
|  | static const struct of_device_id tegra_ahci_of_match[] = { | 
|  | { | 
|  | .compatible = "nvidia,tegra124-ahci", | 
|  | .data = &tegra124_ahci_soc | 
|  | }, | 
|  | { | 
|  | .compatible = "nvidia,tegra210-ahci", | 
|  | .data = &tegra210_ahci_soc | 
|  | }, | 
|  | {} | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); | 
|  |  | 
|  | static struct scsi_host_template ahci_platform_sht = { | 
|  | AHCI_SHT(DRV_NAME), | 
|  | }; | 
|  |  | 
|  | static int tegra_ahci_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct ahci_host_priv *hpriv; | 
|  | struct tegra_ahci_priv *tegra; | 
|  | struct resource *res; | 
|  | int ret; | 
|  |  | 
|  | hpriv = ahci_platform_get_resources(pdev, 0); | 
|  | if (IS_ERR(hpriv)) | 
|  | return PTR_ERR(hpriv); | 
|  |  | 
|  | tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); | 
|  | if (!tegra) | 
|  | return -ENOMEM; | 
|  |  | 
|  | hpriv->plat_data = tegra; | 
|  |  | 
|  | tegra->pdev = pdev; | 
|  | tegra->soc = of_device_get_match_data(&pdev->dev); | 
|  |  | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 
|  | tegra->sata_regs = devm_ioremap_resource(&pdev->dev, res); | 
|  | if (IS_ERR(tegra->sata_regs)) | 
|  | return PTR_ERR(tegra->sata_regs); | 
|  |  | 
|  | /* | 
|  | * AUX registers is optional. | 
|  | */ | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | 
|  | if (res) { | 
|  | tegra->sata_aux_regs = devm_ioremap_resource(&pdev->dev, res); | 
|  | if (IS_ERR(tegra->sata_aux_regs)) | 
|  | return PTR_ERR(tegra->sata_aux_regs); | 
|  | } | 
|  |  | 
|  | 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"); | 
|  | return PTR_ERR(tegra->sata_rst); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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"); | 
|  | return PTR_ERR(tegra->sata_cold_rst); | 
|  | } | 
|  |  | 
|  | 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"); | 
|  | return PTR_ERR(tegra->sata_clk); | 
|  | } | 
|  |  | 
|  | tegra->supplies = devm_kcalloc(&pdev->dev, | 
|  | tegra->soc->num_supplies, | 
|  | sizeof(*tegra->supplies), GFP_KERNEL); | 
|  | if (!tegra->supplies) | 
|  | return -ENOMEM; | 
|  |  | 
|  | regulator_bulk_set_supply_names(tegra->supplies, | 
|  | tegra->soc->supply_names, | 
|  | tegra->soc->num_supplies); | 
|  |  | 
|  | ret = devm_regulator_bulk_get(&pdev->dev, | 
|  | tegra->soc->num_supplies, | 
|  | tegra->supplies); | 
|  | if (ret) { | 
|  | dev_err(&pdev->dev, "Failed to get regulators\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = tegra_ahci_controller_init(hpriv); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, | 
|  | &ahci_platform_sht); | 
|  | if (ret) | 
|  | goto deinit_controller; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | deinit_controller: | 
|  | tegra_ahci_controller_deinit(hpriv); | 
|  |  | 
|  | return ret; | 
|  | }; | 
|  |  | 
|  | static struct platform_driver tegra_ahci_driver = { | 
|  | .probe = tegra_ahci_probe, | 
|  | .remove = ata_platform_remove_one, | 
|  | .driver = { | 
|  | .name = DRV_NAME, | 
|  | .of_match_table = tegra_ahci_of_match, | 
|  | }, | 
|  | /* LP0 suspend support not implemented */ | 
|  | }; | 
|  | module_platform_driver(tegra_ahci_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); | 
|  | MODULE_DESCRIPTION("Tegra AHCI SATA driver"); | 
|  | MODULE_LICENSE("GPL v2"); |