From: Preetham Chandru R <pchandru@xxxxxxxxxx> Add AHCI support for tegra210. 1. Moved tegra124 specifics to tegra124_ahci_init. 2. Separated out the regulators needed for tegra124 and tegra210. 3. Set the LPM capabilities 4. Added support to disable features through DT 5. Created inline functions for read/write and modify to SATA, SATA Config and SATA Aux registers. Signed-off-by: Preetham Chandru R <pchandru@xxxxxxxxxx> --- drivers/ata/ahci_tegra.c | 630 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 500 insertions(+), 130 deletions(-) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 3a62eb2..fa88def 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -33,32 +33,74 @@ #define DRV_NAME "tegra-ahci" +#define SATA_FPCI_BAR5_0 0x94 +#define FPCI_BAR5_START_MASK (0xFFFFFFF << 4) +#define FPCI_BAR5_START (0x0040020 << 4) +#define FPCI_BAR5_ACCESS_TYPE (0x1) + #define SATA_CONFIGURATION_0 0x180 -#define SATA_CONFIGURATION_EN_FPCI BIT(0) +#define SATA_CONFIGURATION_0_EN_FPCI BIT(0) +#define SATA_CONFIGURATION_CLK_OVERRIDE BIT(31) + +#define SATA_INTR_MASK_0 0x188 +#define IP_INT_MASK BIT(16) #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_SATA_CFG_1 0x4 +#define T_SATA_CFG_1_IO_SPACE BIT(0) +#define T_SATA_CFG_1_MEMORY_SPACE BIT(1) +#define T_SATA_CFG_1_BUS_MASTER BIT(2) +#define T_SATA_CFG_1_SERR BIT(8) + +#define T_SATA_CFG_9 0x24 +#define T_SATA_CFG_9_BASE_ADDRESS 0x40020000 -#define T_SATA0_CFG_9 0x24 -#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13 +#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 SATA_FPCI_BAR5 0x94 -#define SATA_FPCI_BAR5_START_SHIFT 4 +#define T_SATA0_AHCI_IDP1 0x98 +#define T_SATA0_AHCI_IDP1_DATA (0x400040) -#define SATA_INTR_MASK 0x188 -#define SATA_INTR_MASK_IP_INT_MASK BIT(16) +#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 FUSE_SATA_CALIB 0x124 +#define FUSE_SATA_CALIB_MASK 0x3 + +#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_SATA_BKDOOR_CC 0x4A4 +#define T_SATA_BKDOOR_CC_CLASS_CODE_MASK (0xFFFF << 16) +#define T_SATA_BKDOOR_CC_CLASS_CODE (0x0106 << 16) +#define T_SATA_BKDOOR_CC_PROG_IF_MASK (0xFF << 8) +#define T_SATA_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_SATA_CFG_SATA 0x54C +#define T_SATA_CFG_SATA_BACKDOOR_PROG_IF_EN BIT(12) #define T_SATA0_CFG_MISC 0x550 @@ -82,8 +124,27 @@ #define T_SATA0_CHX_PHY_CTRL11 0x6d0 #define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16) -#define FUSE_SATA_CALIB 0x124 -#define FUSE_SATA_CALIB_MASK 0x3 +/* Electrical settings for better link stability */ +#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 DEVSLP_OVERRIDE BIT(17) +#define SDS_SUPPORT BIT(13) +#define DESO_SUPPORT BIT(15) + +#define SATA_AUX_RX_STAT_INT_0 0xc +#define SATA_DEVSLP BIT(7) + +#define SATA_AUX_SPARE_CFG0_0 0x18 +#define MDAT_TIMER_AFTER_PG_VALID BIT(14) struct sata_pad_calibration { u8 gen1_tx_amp; @@ -99,15 +160,161 @@ static const struct sata_pad_calibration tegra124_pad_calibration[] = { {0x14, 0x0e, 0x1a, 0x0e}, }; +struct tegra_ahci_ops { + int (*init)(struct ahci_host_priv *); +}; + +struct tegra_ahci_soc { + const char * const *supply_names; + unsigned int num_supplies; + 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[5]; + struct regulator_bulk_data *supplies; + struct tegra_ahci_soc *soc_data; +}; + +static const char * const tegra124_supply_names[] = { + "avdd", "hvdd", "vddio", "target-5v", "target-12v" +}; + +static inline void tegra_ahci_sata_update( + struct tegra_ahci_priv *tegra, + u32 val, u32 mask, u32 offset + + ) +{ + u32 uval = 0; + + uval = readl(tegra->sata_regs + offset); + uval = (uval & ~mask) | (val & mask); + writel(uval, tegra->sata_regs + offset); +} + +static inline void tegra_ahci_scfg_writel( + struct tegra_ahci_priv *tegra, u32 val, u32 offset + + ) +{ + writel(val, tegra->sata_regs + SCFG_OFFSET + offset); +} + +static inline void tegra_ahci_scfg_update( + struct tegra_ahci_priv *tegra, u32 val, u32 mask, u32 offset + + ) +{ + u32 uval = 0; + + uval = readl(tegra->sata_regs + SCFG_OFFSET + offset); + uval = (uval & ~mask) | (val & mask); + writel(uval, tegra->sata_regs + SCFG_OFFSET + offset); +} + +static inline u32 tegra_ahci_aux_readl( + struct tegra_ahci_priv *tegra, u32 offset + + ) +{ + u32 rval = 0; + + rval = readl(tegra->sata_aux_regs + offset); + return rval; +} + +static inline void tegra_ahci_aux_update( + struct tegra_ahci_priv *tegra, u32 val, u32 mask, u32 offset + + ) +{ + u32 uval = 0; + + uval = readl(tegra->sata_aux_regs + offset); + uval = (uval & ~mask) | (val & mask); + writel(uval, tegra->sata_aux_regs + offset); +} + +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; + u32 mask; + + /* Pad calibration */ + + ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val); + if (ret) { + dev_err(&tegra->pdev->dev, + "failed to read calibration fuse: %d\n", ret); + return ret; + } + + calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK]; + + tegra_ahci_scfg_writel(tegra, BIT(0), T_SATA0_INDEX); + + mask = T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; + mask |= 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; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_CHX_PHY_CTRL1_GEN1); + + mask = T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; + mask |= 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; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_CHX_PHY_CTRL1_GEN2); + + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ, + T_SATA0_CHX_PHY_CTRL11 + ); + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1, + T_SATA0_CHX_PHY_CTRL2 + ); + + tegra_ahci_scfg_writel(tegra, 0, T_SATA0_INDEX); + + return 0; +} + +static const char * const tegra210_supply_names[] = { + "dvdd-sata-pll", + "hvdd-sata", + "l0-hvddio-sata", + "l0-dvddio-sata", + "hvdd-pex-pll-e" +}; + +static const struct tegra_ahci_soc tegra124_ahci = { + .supply_names = tegra124_supply_names, + .num_supplies = ARRAY_SIZE(tegra124_supply_names), + .ops = { + .init = tegra124_ahci_init, + }, +}; + +static const struct tegra_ahci_soc tegra210_ahci = { + .supply_names = tegra210_supply_names, + .num_supplies = ARRAY_SIZE(tegra210_supply_names), }; static int tegra_ahci_power_on(struct ahci_host_priv *hpriv) @@ -115,8 +322,10 @@ 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(ARRAY_SIZE(tegra->supplies), - tegra->supplies); + ret = regulator_bulk_enable( + tegra->soc_data->num_supplies, + tegra->supplies + ); if (ret) return ret; @@ -144,8 +353,10 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv) tegra_powergate_power_off(TEGRA_POWERGATE_SATA); disable_regulators: - regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies); - + regulator_bulk_disable( + tegra->soc_data->num_supplies, + tegra->supplies + ); return ret; } @@ -162,131 +373,264 @@ static void tegra_ahci_power_off(struct ahci_host_priv *hpriv) clk_disable_unprepare(tegra->sata_clk); tegra_powergate_power_off(TEGRA_POWERGATE_SATA); - regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies); + regulator_bulk_disable( + tegra->soc_data->num_supplies, + tegra->supplies + ); } -static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) +static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv) { - struct tegra_ahci_priv *tegra = hpriv->plat_data; - int ret; - unsigned int val; - struct sata_pad_calibration calib; - - ret = tegra_ahci_power_on(hpriv); - if (ret) { - dev_err(&tegra->pdev->dev, - "failed to power on AHCI controller: %d\n", ret); - return ret; - } - - val = readl(tegra->sata_regs + SATA_CONFIGURATION_0); - val |= SATA_CONFIGURATION_EN_FPCI; - writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); - - /* Pad calibration */ - - ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val); - if (ret) { - dev_err(&tegra->pdev->dev, - "failed to read calibration fuse: %d\n", 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); + tegra_ahci_power_off(hpriv); +} - 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); +static void tegra_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; - writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); + tegra_ahci_controller_deinit(hpriv); +} - /* Program controller device ID */ +static struct ata_port_operations ahci_tegra_port_ops = { + .inherits = &ahci_ops, + .host_stop = tegra_ahci_host_stop, +}; - 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); +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, +}; - writel(0x01060100, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); +static void tegra_ahci_disable_devslp(struct tegra_ahci_priv *tegra) +{ + u32 val = 0; + u32 mask = SDS_SUPPORT; - 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 = 0xFFFFFFFF & ~SDS_SUPPORT; + tegra_ahci_aux_update(tegra, val, mask, SATA_AUX_MISC_CNTL_1_0); +} - /* Enable IO & memory access, bus master mode */ +static void tegra_ahci_disable_hipm(struct tegra_ahci_priv *tegra) +{ + u32 val = 0; + u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SALP; - 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 = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SALP; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); +} - /* Program SATA MMIO */ +static void tegra_ahci_disable_partial(struct tegra_ahci_priv *tegra) +{ + u32 val = 0; + u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP; - writel(0x10000 << SATA_FPCI_BAR5_START_SHIFT, - tegra->sata_regs + SATA_FPCI_BAR5); + val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); +} - writel(0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT, - tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9); +static void tegra_ahci_disable_slumber(struct tegra_ahci_priv *tegra) +{ + u32 val = 0; + u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP; - /* Unmask SATA interrupts */ + val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); +} - val = readl(tegra->sata_regs + SATA_INTR_MASK); - val |= SATA_INTR_MASK_IP_INT_MASK; - writel(val, tegra->sata_regs + SATA_INTR_MASK); +static void tegra_ahci_disable_ncq(struct tegra_ahci_priv *tegra) +{ + u32 val = 0; + u32 mask = T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ; - return 0; + val = 0xFFFFFFFF & ~T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ; + tegra_ahci_scfg_update(tegra, val, mask, T_SATA0_AHCI_HBA_CAP_BKDR); } -static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv) +static int tegra_ahci_disable_features(struct ahci_host_priv *hpriv) { - tegra_ahci_power_off(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; + 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(tegra); + else if (!strcmp(feature, "hipm")) + tegra_ahci_disable_hipm(tegra); + else if (!strcmp(feature, "ncq")) + tegra_ahci_disable_ncq(tegra); + else if (!strcmp(feature, "dipm")) + ahci_tegra_port_info.flags |= ATA_FLAG_NO_DIPM; + else if (!strcmp(feature, "partial")) + tegra_ahci_disable_partial(tegra); + else if (!strcmp(feature, "slumber")) + tegra_ahci_disable_slumber(tegra); + } + + return ret; } -static void tegra_ahci_host_stop(struct ata_host *host) +static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) { - struct ahci_host_priv *hpriv = host->private_data; + struct tegra_ahci_priv *tegra = hpriv->plat_data; + int ret; + unsigned int val; + unsigned int mask; - tegra_ahci_controller_deinit(hpriv); -} + ret = tegra_ahci_power_on(hpriv); + if (ret) { + dev_err(&tegra->pdev->dev, + "failed to power on AHCI controller: %d\n", ret); + return ret; + } -static struct ata_port_operations ahci_tegra_port_ops = { - .inherits = &ahci_ops, - .host_stop = tegra_ahci_host_stop, -}; + /* 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(tegra, 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(tegra, val, val, SATA_CONFIGURATION_0); + + /* Electrical settings for better link stability */ + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1, + T_SATA0_CHX_PHY_CTRL17_0 + ); + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2, + T_SATA0_CHX_PHY_CTRL18_0); + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1, + T_SATA0_CHX_PHY_CTRL20_0 + ); + tegra_ahci_scfg_writel( + tegra, + T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2, + T_SATA0_CHX_PHY_CTRL21_0 + ); + + /* 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(tegra, val, mask, T_SATA_CFG_PHY_0); + + mask = ( + 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 + ); + tegra_ahci_scfg_update(tegra, 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(tegra, val, mask, T_SATA0_CFG2NVOOB_2); + + if (tegra->soc_data->ops.init) + tegra->soc_data->ops.init(hpriv); + + /* 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(tegra, val, val, T_SATA_CFG_1); + tegra_ahci_scfg_writel(tegra, 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(tegra, 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(tegra, 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(tegra, 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(tegra, 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(tegra, val, mask, T_SATA0_CFG_35); + tegra_ahci_scfg_writel( + tegra, 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(tegra, val, val, T_SATA0_CFG_PHY_1); + + /* + * Indicate Sata only has the capability to enter DevSleep + * from slumber link. + */ + tegra_ahci_aux_update( + tegra, 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(tegra, val, mask, SATA_CONFIGURATION_0); + + tegra_ahci_disable_features(hpriv); + + val = IP_INT_MASK; + tegra_ahci_sata_update(tegra, val, val, SATA_INTR_MASK_0); -static const 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, -}; + return 0; +} static const struct of_device_id tegra_ahci_of_match[] = { - { .compatible = "nvidia,tegra124-ahci" }, + { + .compatible = "nvidia,tegra124-ahci", + .data = &tegra124_ahci + }, + { + .compatible = "nvidia,tegra210-ahci", + .data = &tegra210_ahci + }, {} }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); @@ -300,7 +644,9 @@ static int tegra_ahci_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; struct tegra_ahci_priv *tegra; struct resource *res; + const struct of_device_id *match = NULL; int ret; + unsigned int i; hpriv = ahci_platform_get_resources(pdev); if (IS_ERR(hpriv)) @@ -314,10 +660,23 @@ static int tegra_ahci_probe(struct platform_device *pdev) 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 *)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); + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + 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)) { @@ -343,14 +702,23 @@ static int tegra_ahci_probe(struct platform_device *pdev) return PTR_ERR(tegra->sata_clk); } - tegra->supplies[0].supply = "avdd"; - tegra->supplies[1].supply = "hvdd"; - tegra->supplies[2].supply = "vddio"; - tegra->supplies[3].supply = "target-5v"; - tegra->supplies[4].supply = "target-12v"; - - ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(tegra->supplies), - tegra->supplies); + tegra->supplies = devm_kcalloc( + &pdev->dev, + tegra->soc_data->num_supplies, + sizeof(*tegra->supplies), + GFP_KERNEL + ); + if (IS_ERR(tegra->supplies)) + return PTR_ERR(tegra->supplies); + + for (i = 0; i < tegra->soc_data->num_supplies; i++) + tegra->supplies[i].supply = tegra->soc_data->supply_names[i]; + + ret = devm_regulator_bulk_get( + &pdev->dev, + tegra->soc_data->num_supplies, + tegra->supplies + ); if (ret) { dev_err(&pdev->dev, "Failed to get regulators\n"); return ret; @@ -360,8 +728,10 @@ static int tegra_ahci_probe(struct platform_device *pdev) if (ret) return ret; - ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, - &ahci_platform_sht); + ret = ahci_platform_init_host( + pdev, hpriv, &ahci_tegra_port_info, + &ahci_platform_sht + ); if (ret) goto deinit_controller; @@ -385,5 +755,5 @@ static struct platform_driver tegra_ahci_driver = { module_platform_driver(tegra_ahci_driver); MODULE_AUTHOR("Mikko Perttunen <mperttunen@xxxxxxxxxx>"); -MODULE_DESCRIPTION("Tegra124 AHCI SATA driver"); +MODULE_DESCRIPTION("Tegra AHCI SATA driver"); MODULE_LICENSE("GPL v2"); -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html