/* * ahci-tegra.c - AHCI SATA support for TEGRA AHCI device * * Copyright (c) 2011-2018, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * libata documentation is available via 'make {ps|pdf}docs', * as Documentation/DocBook/libata.* * * AHCI hardware documentation: * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include #else #include #endif #include #include #include "../../arch/arm/mach-tegra/iomap.h" #include #include #include #include #include #include #include #define DRV_NAME "tegra-sata" #define DRV_VERSION "1.0" #define ENABLE_AHCI_DBG_PRINT 0 #if ENABLE_AHCI_DBG_PRINT #define AHCI_DBG_PRINT(fmt, arg...) printk(KERN_ERR fmt, ## arg) #else #define AHCI_DBG_PRINT(fmt, arg...) do {} while (0) #endif /* number of AHCI ports */ #define TEGRA_AHCI_NUM_PORTS 1 #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE /* idle timeout for PM in msec */ #define TEGRA_AHCI_DEFAULT_IDLE_TIME 10000 #endif /* Bit 0 (EN_FPCI) to allow FPCI accesses to SATA */ #define SATA_CONFIGURATION_0_OFFSET 0x180 #define EN_FPCI (1 << 0) #define CLK_OVERRIDE (1 << 31) #define SATA_INTR_MASK_0_OFFSET 0x188 #define IP_INT_MASK (1 << 16) /* Need to write 0x00400200 to 0x70020094 */ #define SATA_FPCI_BAR5_0_OFFSET 0x094 #define CPU_IER_SATA_CTL (1 << 23) #define AHCI_BAR5_CONFIG_LOCATION 0x24 #define TEGRA_SATA_BAR5_INIT_PROGRAM 0xFFFFFFFF #define TEGRA_SATA_BAR5_FINAL_PROGRAM 0x40020000 #define FUSE_SATA_CALIB_OFFSET 0x224 #define FUSE_SATA_CALIB_MASK 0x3 #define T_SATA0_CFG_PHY_REG 0x120 #define T_SATA0_CFG_PHY_SQUELCH_MASK (1 << 24) #define PHY_USE_7BIT_ALIGN_DET_FOR_SPD_MASK (1 << 11) #define T_SATA0_CFG_POWER_GATE 0x4ac #define POWER_GATE_SSTS_RESTORED_MASK (1 << 23) #define POWER_GATE_SSTS_RESTORED_YES (1 << 23) #define POWER_GATE_SSTS_RESTORED_NO (0 << 23) #define T_SATA0_DBG0_OFFSET 0x550 #define T_SATA0_INDEX_OFFSET 0x680 #define SATA0_NONE_SELECTED 0 #define SATA0_CH1_SELECTED (1 << 0) #define T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET 0x690 #define SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0 #define SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK (0xff << 0) #define SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8 #define SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK (0xff << 8) #define T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET 0x694 #define SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0 #define SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK (0xff << 0) #define SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12 #define SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK (0xff << 12) #define SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_SHIFT 24 #define SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_MASK (0xf << 24) #define T_SATA0_CFG_LINK_0 0x174 #define T_SATA0_CFG_LINK_0_USE_POSEDGE_SCTL_DET BIT(24) /* AHCI config space defines */ #define TEGRA_PRIVATE_AHCI_CC_BKDR 0x4a4 #define TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE 0x54c #define TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN (1 << 12) #define TEGRA_PRIVATE_AHCI_CC_BKDR_PGM 0x01060100 /* AHCI HBA_CAP */ #define TEGRA_PRIVATE_AHCI_CAP_BKDR 0xa0 #define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 #define AHCI_HBA_PLL_CTRL_0 0xa8 #define CLAMP_TXCLK_ON_SLUMBER (1 << 13) #define CLAMP_TXCLK_ON_DEVSLP (1 << 24) #define SHUTDOWN_TXCLK_ON_DEVSLP (1 << 22) #define SHUTDOWN_TXCLK_ON_SLUMBER (1 << 6) #define NO_CLAMP_SHUT_DOWN (1 << 3) #define TEGRA_SATA_IO_SPACE_OFFSET 4 #define TEGRA_SATA_ENABLE_IO_SPACE (1 << 0) #define TEGRA_SATA_ENABLE_MEM_SPACE (1 << 1) #define TEGRA_SATA_ENABLE_BUS_MASTER (1 << 2) #define TEGRA_SATA_ENABLE_SERR (1 << 8) #define TEGRA_SATA_CORE_CLOCK_FREQ_HZ (102*1000*1000) #define TEGRA_SATA_OOB_CLOCK_FREQ_HZ (204*1000*1000) #define APB_PMC_SATA_PWRGT_0_REG 0x1ac #define CLK_RST_SATA_PLL_CFG0_REG 0x490 #define CLK_RST_SATA_PLL_CFG1_REG 0x494 #define CLK_RST_CONTROLLER_RST_DEVICES_V_0 0x358 #define CLK_RST_CONTROLLER_RST_DEVICES_W_0 0x35c #define CLK_RST_CONTROLLER_RST_DEV_W_CLR_0 0x43c #define CLK_RST_CONTROLLER_RST_DEV_V_CLR_0 0x434 #define CLK_RST_CONTROLLER_CLK_ENB_V_CLR_0 0x444 #define CLK_RST_CONTROLLER_CLK_ENB_V_SET_0 0x440 #define CLK_RST_CONTROLLER_CLK_ENB_V_0 0x360 #define CLK_RST_CONTROLLER_RST_DEV_W_SET 0x438 #define CLK_RST_CONTROLLER_RST_DEV_V_SET 0x430 #define SET_CEC_RESET 0x100 #define CLR_CLK_ENB_SATA_OOB (1 << 27) #define CLR_CLK_ENB_SATA (1 << 28) #define T_SATA0_FIFO 0x170 #define T_SATA0_FIFO_L2P_FIFO_DEPTH_MASK (0xf<<12) #define T_SATA0_FIFO_L2P_FIFO_DEPTH_SHIFT 12 #define CLK_RST_CONTROLLER_PLLE_MISC_0 0x0ec #define CLK_RST_CONTROLLER_PLLE_MISC_0_VALUE 0x00070300 #define CLK_RST_CONTROLLER_PLLE_BASE_0 0xe8 #define PLLE_ENABLE (1 << 30) #define PLLE_ENABLE_T210 (1 << 31) #define CLK_RST_CONTROLLER_PLLE_AUX_0 0x48c #define CLK_RST_CONTROLLER_PLLE_AUX_0_MASK (1 << 1) #define CLR_SATACOLD_RST (1 << 1) #define SWR_SATACOLD_RST (1 << 1) #define SWR_SATA_RST (1 << 28) #define SWR_SATA_OOB_RST (1 << 27) #define DEVSLP_OVERRIDE (1 << 17) #define SDS_SUPPORT (1 << 13) #define DESO_SUPPORT (1 << 15) #define SATA_AUX_PAD_PLL_CNTL_1_REG 0x00 #define SATA_AUX_MISC_CNTL_1_REG 0x08 #define SATA_AUX_SPARE_CFG0_0 0x18 /* for tegra_pmc_sata_pwrgt_update() */ #define PMC_SATA_PG_INFO_MASK (1 << 6) #define PMC_SATA_PG_INFO_ON (1 << 6) #define PMC_SATA_PG_INFO_OFF (0 << 6) #define PLLE_IDDQ_SWCTL_MASK (1 << 4) #define PADPHY_IDDQ_OVERRIDE_VALUE_MASK (1 << 3) #define PADPHY_IDDQ_OVERRIDE_VALUE_ON (1 << 3) #define PADPHY_IDDQ_OVERRIDE_VALUE_OFF (0 << 3) #define PADPHY_IDDQ_SWCTL_MASK (1 << 2) #define PADPHY_IDDQ_SWCTL_ON (1 << 2) #define PADPHY_IDDQ_SWCTL_OFF (0 << 2) #define PADPLL_IDDQ_OVERRIDE_VALUE_MASK (1 << 1) #define PADPLL_IDDQ_OVERRIDE_VALUE_ON (1 << 1) #define PADPLL_IDDQ_OVERRIDE_VALUE_OFF (0 << 1) #define PADPLL_IDDQ_SWCTL_MASK (1 << 0) #define PADPLL_IDDQ_SWCTL_ON (1 << 0) #define PADPLL_IDDQ_SWCTL_OFF (0 << 0) #define START (1 < 8) #define PARTID_VALUE 0x8 #define SAX_MASK (1 << 8) /* for CLK_RST_SATA_PLL_CFG0_REG */ #define PADPLL_RESET_OVERRIDE_VALUE_MASK (1 << 1) #define PADPLL_RESET_OVERRIDE_VALUE_ON (1 << 1) #define PADPLL_RESET_OVERRIDE_VALUE_OFF (0 << 1) #define PADPLL_RESET_SWCTL_MASK (1 << 0) #define PADPLL_RESET_SWCTL_ON (1 << 0) #define PADPLL_RESET_SWCTL_OFF (0 << 0) #define PLLE_IDDQ_SWCTL_ON (1 << 4) #define PLLE_IDDQ_SWCTL_OFF (0 << 4) #define PLLE_SATA_SEQ_ENABLE (1 << 24) #define PLLE_SATA_SEQ_START_STATE (1 << 25) #define SATA_SEQ_PADPLL_PD_INPUT_VALUE (1 << 7) #define SATA_SEQ_LANE_PD_INPUT_VALUE (1 << 6) #define SATA_SEQ_RESET_INPUT_VALUE (1 << 5) #define SATA_PADPLL_SLEEP_IDDQ (1 << 13) #define SATA_PADPLL_USE_LOCKDET (1 << 2) /* for CLK_RST_SATA_PLL_CFG1_REG */ #define IDDQ2LANE_SLUMBER_DLY_MASK (0xffL << 16) #define IDDQ2LANE_SLUMBER_DLY_SHIFT 16 #define IDDQ2LANE_SLUMBER_DLY_3MS (3 << 16) #define IDDQ2LANE_IDDQ_DLY_SHIFT 0 #define IDDQ2LANE_IDDQ_DLY_MASK (0xffL << 0) /* for SATA_AUX_PAD_PLL_CNTL_1_REG */ #define REFCLK_SEL_MASK (3 << 11) #define REFCLK_SEL_INT_CML (0 << 11) #define LOCKDET_FIELD (1 << 6) /* for SATA_AUX_MISC_CNTL_1_REG */ #define NVA2SATA_OOB_ON_POR_MASK (1 << 7) #define NVA2SATA_OOB_ON_POR_YES (1 << 7) #define NVA2SATA_OOB_ON_POR_NO (0 << 7) #define L0_RX_IDLE_T_SAX_SHIFT 5 #define L0_RX_IDLE_T_SAX_MASK (3 << 5) #define L0_RX_IDLE_T_NPG_SHIFT 3 #define L0_RX_IDLE_T_NPG_MASK (3 << 3) #define L0_RX_IDLE_T_MUX_MASK (1 << 2) #define L0_RX_IDLE_T_MUX_FROM_APB_MISC (1 << 2) #define L0_RX_IDLE_T_MUX_FROM_SATA (0 << 2) #define SSTAT_IPM_STATE_MASK 0xF00 #define SSTAT_IPM_SLUMBER_STATE 0x600 #define SATA_AXI_BAR5_START_0 0x54 #define SATA_AXI_BAR5_SZ_0 0x14 #define SATA_AXI_BAR5_START_VALUE 0x70020 #define AXI_BAR5_SIZE_VALUE 0x00008 #define FPCI_BAR5_0_START_VALUE 0x0010000 #define FPCI_BAR5_0_FINAL_VALUE 0x40020100 #define FPCI_BAR5_0_ACCESS_TYPE (1 << 0) /* Spread Settings */ #define SATA0_CHX_PHY_CTRL11_0 0x6D0 #define SATA0_CHX_PHY_CTRL2_0 0x69c #define GEN2_RX_EQ (0x2800 << 16) #define CDR_CNTL_GEN1 0x23 #define CLK_RST_CONTROLLER_PLLE_SS_CNTL_0 0x68 #define PLLE_SSCCENTER (1 << 14) #define PLLE_SSCINVERT (1 << 15) #define PLLE_SSCMAX (0x25) #define PLLE_SSCINCINTRV (0x20 << 24) #define PLLE_SSCINC (1 << 16) #define PLLE_BYPASS_SS (1 << 10) #define PLLE_SSCBYP (1 << 12) #define PLLE_INTERP_RESET (1 << 11) #define SATA_AUX_RX_STAT_INT_0 0x110c #define SATA_RX_STAT_INT_DISABLE (1 << 2) #define SATA_AUX_RX_STAT_INT_0_SATA_DEVSLP (0x1 << 7) #define SATA_AUX_MISC_CNTL_1_0 0x1108 #define SATA_AUX_MISC_CNTL_1_0_DEVSLP_OVERRIDE (0x1 << 17) #define T_SATA0_NVOOB 0x114 #define T_SATA0_NVOOB_COMMA_CNT (0x7 << 28) #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_SHIFT 24 #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK (3 << 24) #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_SHIFT 26 #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK (3 << 26) #define PXSSTS_DEVICE_DETECTED (1 << 0) /*Electrical settings for better link stability */ #define SATA_CHX_PHY_CTRL17_0 0x6e8 #define SATA_CHX_PHY_CTRL18_0 0x6ec #define SATA_CHX_PHY_CTRL20_0 0x6f4 #define SATA_CHX_PHY_CTRL21_0 0x6f8 #define SATA0_CFG_35_0 0x094 #define IDP_INDEX (0x2a << 2) #define SATA0_AHCI_IDP1_0 0x098 #define SATA0_AHCI_IDP1_0_DATA (1 << 6 | 1 << 22) #define SATA0_CFG_PHY_1_0 0x12c #define PAD_IDDQ_EN (1 << 23) #define PAD_PLL_IDDQ_EN (1 << 22) /* SATA Port Registers*/ #define PXSSTS 0X28 #define T_AHCI_PORT_PXSSTS_IPM_MASK (0xF00) #define T_AHCI_PORT_PXSSTS_IPM_SHIFT (8) #define TEGRA_AHCI_READ_LOG_EXT_NOENTRY 0x80 #ifdef CONFIG_PM_GENERIC_DOMAINS_OF static struct of_device_id tegra_sata_pd[] = { { .compatible = "nvidia,tegra210-sata-pd", }, {}, }; #endif enum { AHCI_PCI_BAR = 5, }; enum tegra_ahci_port_runtime_status { TEGRA_AHCI_PORT_RUNTIME_ACTIVE = 1, TEGRA_AHCI_PORT_RUNTIME_PARTIAL = 2, TEGRA_AHCI_PORT_RUNTIME_SLUMBER = 6, TEGRA_AHCI_PORT_RUNTIME_DEVSLP = 8, }; enum port_idle_status { PORT_IS_NOT_IDLE, PORT_IS_IDLE, PORT_IS_IDLE_NOT_SLUMBER, PORT_IS_SLUMBER, }; enum sata_state { SATA_ON, SATA_OFF, SATA_GOING_ON, SATA_GOING_OFF, SATA_ABORT_OFF, }; enum clk_gate_state { CLK_OFF, CLK_ON, }; enum sata_connectors { MINI_SATA, MICRO_SATA, SLIMLINE_SATA, E_SATA, E_SATA_P, SATA_EXPRESS, STANDARD_SATA, }; /* Sata Pad Cntrl Values */ struct sata_pad_cntrl { u8 gen1_tx_amp; u8 gen1_tx_peak; u8 gen2_tx_amp; u8 gen2_tx_peak; }; /* * tegra_ahci_host_priv is the extension of ahci_host_priv * with extra fields: idle_timer, pg_save, pg_state, etc. */ struct tegra_ahci_host_priv { struct ahci_host_priv ahci_host_priv; struct regulator **power_rails; void __iomem *bars_table[6]; struct ata_host *host; struct timer_list idle_timer; struct device *dev; struct platform_device *pdev; void *pg_save; enum sata_state pg_state; enum sata_connectors sata_connector; struct clk *clk_sata; struct clk *clk_sata_oob; struct clk *clk_pllp; struct clk *clk_cml1; struct reset_control *rst_sata; struct reset_control *rst_sata_oob; struct reset_control *rst_sata_cold; enum clk_gate_state clk_state; s16 gen2_rx_eq; int pexp_gpio_high; int pexp_gpio_low; enum tegra_chipid cid; struct tegra_sata_soc_data *soc_data; struct tegra_prod *prod_list; void __iomem *base_list[6]; u32 reg_offset[6]; void __iomem *base_car; struct sata_pad_cntrl pad_val; bool dtContainsPadval; bool skip_rtpm; u8 fifo_depth; }; #ifdef CONFIG_DEBUG_FS static int tegra_ahci_dump_debuginit(void); #endif static int tegra_ahci_init_one(struct platform_device *pdev); static int tegra_ahci_t210_controller_init(void *hpriv, int lp0); static int tegra_ahci_remove_one(struct platform_device *pdev); static void tegra_ahci_shutdown(struct platform_device *pdev); static void tegra_ahci_put_sata_in_iddq(struct ata_host *host); #ifdef CONFIG_PM static bool tegra_ahci_power_un_gate(struct ata_host *host); static bool tegra_ahci_power_gate(struct ata_host *host); static void tegra_ahci_abort_power_gate(struct ata_host *host); static int tegra_ahci_controller_suspend(struct platform_device *pdev); static int tegra_ahci_controller_resume(struct platform_device *pdev); #ifndef CONFIG_TEGRA_SATA_IDLE_POWERGATE static int tegra_ahci_suspend(struct platform_device *pdev, pm_message_t mesg); static int tegra_ahci_resume(struct platform_device *pdev); #else static int tegra_ahci_suspend(struct device *dev); static int tegra_ahci_resume(struct device *dev); #endif static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap); static bool tegra_ahci_are_all_ports_idle(struct ata_host *host); #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE #ifndef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE static void tegra_ahci_iddqlane_config(void); static bool tegra_ahci_pad_resume(struct ata_host *host); static bool tegra_ahci_pad_suspend(struct ata_host *host); static void tegra_ahci_abort_pad_suspend(struct ata_host *host); #endif static int tegra_ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); static int tegra_ahci_port_resume(struct ata_port *ap); static int tegra_ahci_runtime_suspend(struct device *dev); static int tegra_ahci_runtime_resume(struct device *dev); #endif #else #define tegra_ahci_controller_suspend NULL #define tegra_ahci_controller_resume NULL #define tegra_ahci_suspend NULL #define tegra_ahci_resume NULL #endif static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static int tegra_ahci_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc); static struct scsi_host_template ahci_sht = { AHCI_SHT("tegra-sata"), }; static struct ata_port_operations tegra_ahci_ops = { .inherits = &ahci_ops, .qc_issue = tegra_ahci_qc_issue, #ifdef CONFIG_PM #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE .port_suspend = tegra_ahci_port_suspend, .port_resume = tegra_ahci_port_resume, #endif #endif .hardreset = tegra_ahci_hardreset, .softreset = tegra_ahci_softreset, }; static const struct ata_port_info ahci_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = ATA_UDMA6, .port_ops = &tegra_ahci_ops, }; #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE 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) }; #endif static char * const t210_rail_names[] = {"dvdd_sata_pll", "hvdd_sata", "l0_hvddio_sata", "l0_dvddio_sata", "hvdd_pex_pll_e"}; static const struct tegra_sata_soc_data tegra210_sata_data = { .sata_regulator_names = t210_rail_names, .num_sata_regulators = ARRAY_SIZE(t210_rail_names), .controller_init = tegra_ahci_t210_controller_init, }; static const struct of_device_id of_ahci_tegra_match[] = { { .compatible = "nvidia,tegra210-ahci-sata-shield", .data = &tegra210_sata_data, }, {}, }; MODULE_DEVICE_TABLE(of, of_ahci_tegra_match); static const struct of_device_id car_match[] = { { .compatible = "nvidia,tegra210-car", }, {}, }; static struct platform_driver tegra_platform_ahci_driver = { .probe = tegra_ahci_init_one, .remove = tegra_ahci_remove_one, .shutdown = tegra_ahci_shutdown, #ifdef CONFIG_PM #ifndef CONFIG_TEGRA_SATA_IDLE_POWERGATE .suspend = tegra_ahci_suspend, .resume = tegra_ahci_resume, #endif #endif .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_ahci_tegra_match), #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE .pm = &tegra_ahci_dev_rt_ops, #endif }, }; static struct tegra_ahci_host_priv *g_tegra_hpriv; static inline u32 port_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_list[1] + 0x100 + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void port_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_list[1] + 0x100 + offset; AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 bar5_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_list[1] + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void bar5_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_list[1] + offset; AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 clk_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_car + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void clk_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_car+offset; AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 misc_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_list[2] + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void misc_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_list[2] + offset; AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 sata_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_list[3] + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void sata_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_list[3] + offset; AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 scfg_readl(u32 offset) { u32 val; void __iomem *addr = g_tegra_hpriv->base_list[0] + offset; val = readl(addr); AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", addr, val); return val; } static inline void scfg_writel(u32 val, u32 offset) { void __iomem *addr = g_tegra_hpriv->base_list[0] + offset; AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", addr, val); writel(val, addr); readl(addr); } static inline u32 fuse_readl(u32 offset) { u32 val; if (!tegra_fuse_readl(offset, &val)) { pr_err("%s: failed to read fuse register at 0x%x\n", __func__, offset); return 0; } return val; } static struct sata_pad_cntrl sata_calib_pad_val[] = { { /* SATA_CALIB[1:0] = 00 */ 0x18, 0x04, 0x18, 0x0a }, { /* SATA_CALIB[1:0] = 01 */ 0x0e, 0x04, 0x14, 0x0a }, { /* SATA_CALIB[1:0] = 10 */ 0x0e, 0x07, 0x1a, 0x0e }, { /* SATA_CALIB[1:0] = 11 */ 0x14, 0x0e, 0x1a, 0x0e } }; static u32 tegra_ahci_get_port_status(void) { u32 val; val = port_readl(PXSSTS); return val; } static void tegra_ahci_set_pad_cntrl_regs( struct tegra_ahci_host_priv *tegra_hpriv) { int calib_val; int val; int i; int err = 0; if (tegra_hpriv->cid == TEGRA_CHIPID_TEGRA21) { err = tegra_prod_set_by_name( tegra_hpriv->base_list, "prod", tegra_hpriv->prod_list); if (err) { dev_err(tegra_hpriv->dev, "Prod setting from DT failed\n"); } else { scfg_writel(0x55010000, SATA_CHX_PHY_CTRL17_0); scfg_writel(0x55010000, SATA_CHX_PHY_CTRL18_0); scfg_writel(0x1, SATA_CHX_PHY_CTRL20_0); scfg_writel(0x1, SATA_CHX_PHY_CTRL21_0); } return; } calib_val = fuse_readl(FUSE_SATA_CALIB_OFFSET) & FUSE_SATA_CALIB_MASK; val = clk_readl(CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); val &= ~(PLLE_SSCCENTER | PLLE_SSCINVERT); val |= (PLLE_SSCMAX | PLLE_SSCINCINTRV | PLLE_SSCINC); clk_writel(val, CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); val = clk_readl(CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); val &= ~(PLLE_BYPASS_SS | PLLE_SSCBYP); clk_writel(val, CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); udelay(2); val = clk_readl(CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); val &= ~(PLLE_INTERP_RESET); clk_writel(val, CLK_RST_CONTROLLER_PLLE_SS_CNTL_0); for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); val &= ~SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; val |= (sata_calib_pad_val[calib_val].gen1_tx_amp << SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT); scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); val &= ~SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK; val |= (sata_calib_pad_val[calib_val].gen1_tx_peak << SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT); scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); val &= ~SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; val |= (sata_calib_pad_val[calib_val].gen2_tx_amp << SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT); scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); val &= ~SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK; val |= (sata_calib_pad_val[calib_val].gen2_tx_peak << SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT); scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); val = GEN2_RX_EQ; scfg_writel(val, SATA0_CHX_PHY_CTRL11_0); val = CDR_CNTL_GEN1; scfg_writel(val, SATA0_CHX_PHY_CTRL2_0); } scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); } static int tegra_ahci_get_rails(struct tegra_ahci_host_priv *tegra_hpriv) { struct regulator *reg; int i; int ret = 0; tegra_hpriv->power_rails = devm_kzalloc(tegra_hpriv->dev, tegra_hpriv->soc_data->num_sata_regulators * sizeof(struct regulator *), GFP_KERNEL); for (i = 0; i < tegra_hpriv->soc_data->num_sata_regulators; ++i) { reg = devm_regulator_get(tegra_hpriv->dev, tegra_hpriv->soc_data->sata_regulator_names[i]); if (IS_ERR(reg)) { dev_err(tegra_hpriv->dev, "%s:can't get regulator %s\n", __func__, tegra_hpriv->soc_data->sata_regulator_names[i]); WARN_ON(1); ret = PTR_ERR(reg); tegra_hpriv->power_rails[i] = NULL; goto exit; } tegra_hpriv->power_rails[i] = reg; } exit: return ret; } static int tegra_ahci_power_off_rails(struct tegra_ahci_host_priv *tegra_hpriv, int num_reg) { struct regulator *reg; int i; int ret = 0; int rc = 0; for (i = 0; i < num_reg; ++i) { reg = tegra_hpriv->power_rails[i]; if (!IS_ERR(reg)) { ret = regulator_disable(reg); if (ret) { dev_err(tegra_hpriv->dev, "%s: can't disable regulator[%d]\n", __func__, i); WARN_ON(1); rc = ret; } } } return rc; } static int tegra_ahci_power_on_rails(struct tegra_ahci_host_priv *tegra_hpriv) { int i; int ret = 0; for (i = 0; i < tegra_hpriv->soc_data->num_sata_regulators; ++i) { ret = regulator_enable(tegra_hpriv->power_rails[i]); if (ret) { dev_err(tegra_hpriv->dev, "%s: can't enable regulator[%d]\n", __func__, i); WARN_ON(1); tegra_ahci_power_off_rails(tegra_hpriv, i); goto exit; } } exit: return ret; } static void tegra_first_level_clk_gate(void) { if (g_tegra_hpriv->clk_state == CLK_OFF) return; clk_disable_unprepare(g_tegra_hpriv->clk_sata); clk_disable_unprepare(g_tegra_hpriv->clk_sata_oob); if (g_tegra_hpriv->clk_cml1) clk_disable_unprepare(g_tegra_hpriv->clk_cml1); g_tegra_hpriv->clk_state = CLK_OFF; } static int tegra_first_level_clk_ungate(void) { int ret = 0; const char *err_clk_name; if (g_tegra_hpriv->clk_state == CLK_ON) { ret = -1; return ret; } if (clk_prepare_enable(g_tegra_hpriv->clk_sata)) { err_clk_name = "SATA"; goto clk_sata_enb_error; } if (clk_prepare_enable(g_tegra_hpriv->clk_sata_oob)) { err_clk_name = "SATA_OOB"; goto clk_sata_oob_enb_error; } if (g_tegra_hpriv->clk_cml1 && clk_prepare_enable(g_tegra_hpriv->clk_cml1)) { err_clk_name = "cml1"; goto clk_cml1_enb_error; } g_tegra_hpriv->clk_state = CLK_ON; return ret; clk_cml1_enb_error: clk_disable_unprepare(g_tegra_hpriv->clk_sata_oob); clk_sata_oob_enb_error: clk_disable_unprepare(g_tegra_hpriv->clk_sata); clk_sata_enb_error: pr_err("%s: unable to enable %s clock\n", __func__, err_clk_name); return -ENODEV; } static int tegra_request_pexp_gpio(struct tegra_ahci_host_priv *tegra_hpriv) { u32 val = 0; int err = 0; if (gpio_is_valid(tegra_hpriv->pexp_gpio_high)) { val = gpio_request(tegra_hpriv->pexp_gpio_high, "ahci-tegra"); if (val) { pr_err("failed to allocate Port expander gpio\n"); err = -ENODEV; goto exit; } gpio_direction_output(tegra_hpriv->pexp_gpio_high, 1); } if (gpio_is_valid(tegra_hpriv->pexp_gpio_low)) { val = gpio_request(tegra_hpriv->pexp_gpio_low, "ahci-tegra"); if (val) { pr_err("failed to allocate Port expander gpio\n"); err = -ENODEV; goto exit; } gpio_direction_output(tegra_hpriv->pexp_gpio_low, 0); } exit: return err; } static void tegra_free_pexp_gpio(struct tegra_ahci_host_priv *tegra_hpriv) { if (gpio_is_valid(tegra_hpriv->pexp_gpio_high)) gpio_free(tegra_hpriv->pexp_gpio_high); if (gpio_is_valid(tegra_hpriv->pexp_gpio_low)) gpio_free(tegra_hpriv->pexp_gpio_low); } static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc) { 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_t210_controller_init(void *hpriv, int lp0) { struct tegra_ahci_host_priv *tegra_hpriv = hpriv; struct clk *clk_sata = NULL; struct clk *clk_sata_oob = NULL; struct clk *clk_pllp = NULL; struct reset_control *rst_sata = NULL; struct reset_control *rst_sata_oob = NULL; struct reset_control *rst_sata_cold = NULL; int err = 0; u32 val; #if defined(CONFIG_TEGRA_SILICON_PLATFORM) int partition_id; #endif if (!lp0) { err = tegra_ahci_get_rails(tegra_hpriv); if (err) { pr_err("%s: fails to get rails (%d)\n", __func__, err); goto exit; } err = tegra_ahci_power_on_rails(tegra_hpriv); if (err) { pr_err("%s: fails to power on rails (%d)\n", __func__, err); goto exit; } err = tegra_request_pexp_gpio(tegra_hpriv); if (err < 0) { tegra_free_pexp_gpio(tegra_hpriv); goto exit; } tegra_hpriv->clk_cml1 = NULL; /* pll_p is the parent of tegra_sata and tegra_sata_oob */ clk_pllp = clk_get_sys(NULL, "pll_p"); if (IS_ERR_OR_NULL(clk_pllp)) { pr_err("%s: unable to get PLL_P clock\n", __func__); err = PTR_ERR(clk_pllp); goto exit; } tegra_hpriv->clk_pllp = clk_pllp; clk_sata = devm_clk_get(tegra_hpriv->dev, "sata"); if (IS_ERR_OR_NULL(clk_sata)) { pr_err("%s: unable to get SATA clock\n", __func__); err = PTR_ERR(clk_sata); goto exit; } tegra_hpriv->clk_sata = clk_sata; clk_sata_oob = devm_clk_get(tegra_hpriv->dev, "sata_oob"); if (IS_ERR_OR_NULL(clk_sata_oob)) { pr_err("%s: unable to get SATA OOB clock\n", __func__); err = PTR_ERR(clk_sata_oob); goto exit; } tegra_hpriv->clk_sata_oob = clk_sata_oob; rst_sata = devm_reset_control_get(tegra_hpriv->dev, "sata"); if (IS_ERR_OR_NULL(rst_sata)) { pr_err("%s: unable to get SATA reset\n", __func__); err = PTR_ERR(rst_sata); goto exit; } tegra_hpriv->rst_sata = rst_sata; rst_sata_oob = devm_reset_control_get(tegra_hpriv->dev, "sata-oob"); if (IS_ERR_OR_NULL(rst_sata_oob)) { pr_err("%s: unable to get SATA OOB reset\n", __func__); err = PTR_ERR(rst_sata_oob); goto exit; } tegra_hpriv->rst_sata_oob = rst_sata_oob; rst_sata_cold = devm_reset_control_get(tegra_hpriv->dev, "sata-cold"); if (IS_ERR_OR_NULL(rst_sata_cold)) { pr_err("%s: unable to get SATA COLD reset\n", __func__); err = PTR_ERR(rst_sata_cold); goto exit; } tegra_hpriv->rst_sata_cold = rst_sata_cold; } reset_control_assert(tegra_hpriv->rst_sata); reset_control_assert(tegra_hpriv->rst_sata_oob); reset_control_assert(tegra_hpriv->rst_sata_cold); udelay(10); /* need to establish both clocks divisors before setting clk sources */ clk_set_rate(tegra_hpriv->clk_sata, clk_get_rate(tegra_hpriv->clk_sata)/10); clk_set_rate(tegra_hpriv->clk_sata_oob, clk_get_rate(tegra_hpriv->clk_sata_oob)/10); /* set SATA clk and SATA_OOB clk source */ clk_set_parent(tegra_hpriv->clk_sata, tegra_hpriv->clk_pllp); clk_set_parent(tegra_hpriv->clk_sata_oob, tegra_hpriv->clk_pllp); /* Configure SATA clocks */ /* Core clock runs at 102MHz */ if (clk_set_rate(tegra_hpriv->clk_sata, TEGRA_SATA_CORE_CLOCK_FREQ_HZ)) { err = -ENODEV; goto exit; } /* OOB clock runs at 204MHz */ if (clk_set_rate(tegra_hpriv->clk_sata_oob, TEGRA_SATA_OOB_CLOCK_FREQ_HZ)) { err = -ENODEV; goto exit; } if (clk_prepare_enable(tegra_hpriv->clk_sata)) { pr_err("%s: unable to enable SATA clock\n", __func__); err = -ENODEV; goto exit; } if (clk_prepare_enable(tegra_hpriv->clk_sata_oob)) { pr_err("%s: unable to enable SATA OOB clock\n", __func__); err = -ENODEV; goto exit; } err = ahci_platform_enable_resources(hpriv); if (err) { pr_err("%s: unable to enable resources\n", __func__); goto exit; } g_tegra_hpriv->clk_state = CLK_ON; reset_control_deassert(tegra_hpriv->rst_sata); reset_control_deassert(tegra_hpriv->rst_sata_oob); reset_control_deassert(tegra_hpriv->rst_sata_cold); /* select internal CML ref clk * select PLLE as input to IO phy */ val = misc_readl(SATA_AUX_PAD_PLL_CNTL_1_REG); val &= ~REFCLK_SEL_MASK; val |= REFCLK_SEL_INT_CML; misc_writel(val, SATA_AUX_PAD_PLL_CNTL_1_REG); if (!lp0) { #if defined(CONFIG_TEGRA_SILICON_PLATFORM) #ifdef CONFIG_PM_GENERIC_DOMAINS_OF partition_id = tegra_pd_get_powergate_id(tegra_sata_pd); if (partition_id < 0) return -EINVAL; #else partition_id = TEGRA_POWERGATE_SATA; #endif err = tegra_unpowergate_partition(partition_id); if (err) { pr_err("%s: ** failed to turn-on SATA (0x%x) **\n", __func__, err); goto exit; } #endif } /* clear NVA2SATA_OOB_ON_POR in SATA_AUX_MISC_CNTL_1_REG */ val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); val &= ~NVA2SATA_OOB_ON_POR_MASK; misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); /* Revisit: Disable devslp until all devslp bugs are fixed */ val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); val &= ~SDS_SUPPORT; misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); val = sata_readl(SATA_CONFIGURATION_0_OFFSET); val |= EN_FPCI; sata_writel(val, SATA_CONFIGURATION_0_OFFSET); val |= CLK_OVERRIDE; sata_writel(val, SATA_CONFIGURATION_0_OFFSET); /* program sata pad control based on the fuse */ tegra_ahci_set_pad_cntrl_regs(tegra_hpriv); /* * clear bit T_SATA0_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD of * T_SATA0_CFG_PHY_0 */ val = scfg_readl(T_SATA0_CFG_PHY_REG); val |= T_SATA0_CFG_PHY_SQUELCH_MASK; val &= ~PHY_USE_7BIT_ALIGN_DET_FOR_SPD_MASK; scfg_writel(val, T_SATA0_CFG_PHY_REG); val = scfg_readl(T_SATA0_NVOOB); val |= (1 << T_SATA0_NVOOB_SQUELCH_FILTER_MODE_SHIFT); val |= (3 << T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_SHIFT); val |= T_SATA0_NVOOB_COMMA_CNT; scfg_writel(val, T_SATA0_NVOOB); /* * WAR: Before enabling SATA PLL shutdown, lockdet needs to be ignored. * To ignore lockdet, T_SATA0_DBG0_OFFSET register bit 10 needs to * be 1, and bit 8 needs to be 0. */ val = scfg_readl(T_SATA0_DBG0_OFFSET); val |= (1 << 10); val &= ~(1 << 8); scfg_writel(val, T_SATA0_DBG0_OFFSET); /* program class code and programming interface for AHCI */ val = scfg_readl(TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); val |= TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); scfg_writel(TEGRA_PRIVATE_AHCI_CC_BKDR_PGM, TEGRA_PRIVATE_AHCI_CC_BKDR); val &= ~TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); /* Program config space registers: */ /* Enable BUS_MASTER+MEM+IO space, and SERR */ val = scfg_readl(TEGRA_SATA_IO_SPACE_OFFSET); val |= TEGRA_SATA_ENABLE_IO_SPACE | TEGRA_SATA_ENABLE_MEM_SPACE | TEGRA_SATA_ENABLE_BUS_MASTER | TEGRA_SATA_ENABLE_SERR; scfg_writel(val, TEGRA_SATA_IO_SPACE_OFFSET); /* program bar5 space, by first writing 1's to bar5 register */ scfg_writel(TEGRA_SATA_BAR5_INIT_PROGRAM, AHCI_BAR5_CONFIG_LOCATION); /* flush */ val = scfg_readl(AHCI_BAR5_CONFIG_LOCATION); /* then, write the BAR5_FINAL_PROGRAM address */ scfg_writel(TEGRA_SATA_BAR5_FINAL_PROGRAM, AHCI_BAR5_CONFIG_LOCATION); /* flush */ scfg_readl(AHCI_BAR5_CONFIG_LOCATION); sata_writel((FPCI_BAR5_0_FINAL_VALUE >> 8), SATA_FPCI_BAR5_0_OFFSET); val = scfg_readl(T_SATA0_AHCI_HBA_CAP_BKDR); val |= (HOST_CAP_ALPM | HOST_CAP_SSC | HOST_CAP_PART | HOST_CAP_PMP); scfg_writel(val, T_SATA0_AHCI_HBA_CAP_BKDR); val = bar5_readl(AHCI_HBA_PLL_CTRL_0); val |= (SHUTDOWN_TXCLK_ON_SLUMBER | SHUTDOWN_TXCLK_ON_DEVSLP); val &= ~NO_CLAMP_SHUT_DOWN; bar5_writel(val, AHCI_HBA_PLL_CTRL_0); val = scfg_readl(SATA0_CFG_35_0); val |= (IDP_INDEX); scfg_writel(val, SATA0_CFG_35_0); val = scfg_readl(SATA0_AHCI_IDP1_0); val |= SATA0_AHCI_IDP1_0_DATA; scfg_writel(val, SATA0_AHCI_IDP1_0); val = scfg_readl(SATA0_CFG_PHY_1_0); val |= (PAD_IDDQ_EN | PAD_PLL_IDDQ_EN); scfg_writel(val, SATA0_CFG_PHY_1_0); /* set IP_INT_MASK */ val = sata_readl(SATA_INTR_MASK_0_OFFSET); val |= IP_INT_MASK; sata_writel(val, SATA_INTR_MASK_0_OFFSET); /* set fifo l2p depth */ if (tegra_hpriv->fifo_depth != 0) { val = scfg_readl(T_SATA0_FIFO); val &= ~T_SATA0_FIFO_L2P_FIFO_DEPTH_MASK; val |= tegra_hpriv->fifo_depth << T_SATA0_FIFO_L2P_FIFO_DEPTH_SHIFT; scfg_writel(val, T_SATA0_FIFO); } exit: if (err && !lp0) { /* turn off all SATA power rails; ignore returned status */ tegra_ahci_power_off_rails(tegra_hpriv, tegra_hpriv->soc_data->num_sata_regulators); } return err; } static void tegra_ahci_controller_remove(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); struct tegra_ahci_host_priv *tegra_hpriv; int status; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; #ifdef CONFIG_PM /* call tegra_ahci_controller_suspend() to power-down the SATA */ status = tegra_ahci_controller_suspend(pdev); if (status) dev_err(host->dev, "remove: error suspend SATA (0x%x)\n", status); #else /* power off the sata */ #ifdef CONFIG_PM_GENERIC_DOMAINS_OF int partition_id; partition_id = tegra_pd_get_powergate_id(tegra_sata_pd); if (partition_id < 0) return -EINVAL; #else partition_id = TEGRA_POWERGATE_SATA; #endif status = tegra_powergate_partition_with_clk_off(partition_id); if (status) dev_err(host->dev, "remove: error turn-off SATA (0x%x)\n", status); tegra_ahci_power_off_rails(tegra_hpriv, tegra_hpriv->soc_data->num_sata_regulators); #endif } #ifdef CONFIG_PM static int tegra_ahci_controller_suspend(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); struct tegra_ahci_host_priv *tegra_hpriv; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; if (tegra_hpriv->pg_state == SATA_OFF) dev_dbg(host->dev, "suspend: SATA already power gated\n"); else { bool pg_ok; dev_dbg(host->dev, "suspend: power gating SATA...\n"); pg_ok = tegra_ahci_power_gate(host); if (pg_ok) { dev_dbg(host->dev, "suspend: SATA is power gated\n"); } else { tegra_ahci_abort_power_gate(host); return -EBUSY; } } return tegra_ahci_power_off_rails(tegra_hpriv, tegra_hpriv->soc_data->num_sata_regulators); } static int tegra_ahci_controller_resume(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); struct tegra_ahci_host_priv *tegra_hpriv; int err; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; err = tegra_ahci_power_on_rails(tegra_hpriv); if (err) { pr_err("%s: fails to power on rails (%d)\n", __func__, err); return err; } if (tegra_hpriv->pg_state == SATA_ON) { dev_dbg(host->dev, "resume: SATA already powered on\n"); } else { dev_dbg(host->dev, "resume: powering on SATA...\n"); tegra_ahci_power_un_gate(host); } tegra_first_level_clk_gate(); return 0; } #ifndef CONFIG_TEGRA_SATA_IDLE_POWERGATE static int tegra_ahci_suspend(struct platform_device *pdev, pm_message_t mesg) { struct ata_host *host = dev_get_drvdata(&pdev->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 ctl; int rc; dev_dbg(host->dev, "** entering %s: **\n", __func__); if (mesg.event & PM_EVENT_SLEEP) { /* * AHCI spec rev1.1 section 8.3.3: * Software must disable interrupts prior to requesting a * transition of the HBA to D3 state. */ ctl = readl(mmio + HOST_CTL); ctl &= ~HOST_IRQ_EN; writel(ctl, mmio + HOST_CTL); readl(mmio + HOST_CTL); /* flush */ } rc = ata_host_suspend(host, mesg); if (rc) return rc; return tegra_ahci_controller_suspend(pdev); } static int tegra_ahci_resume(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); int rc; u32 val; dev_dbg(host->dev, "** entering %s: **\n", __func__); rc = tegra_ahci_controller_resume(pdev); if (rc != 0) return rc; rc = g_tegra_hpriv->soc_data->controller_init(g_tegra_hpriv, 1); if (rc != 0) { dev_err(host->dev, "TEGRA SATA init failed in resume\n"); tegra_ahci_controller_suspend(pdev); return rc; } if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_reset_controller(host); if (rc) { dev_err(host->dev, "TEGRA SATA reset failed in resume\n"); tegra_ahci_controller_remove(pdev); return rc; } val = misc_readl(SATA_AUX_RX_STAT_INT_0); if (val & SATA_RX_STAT_INT_DISABLE) { val &= ~SATA_RX_STAT_INT_DISABLE; misc_writel(val, SATA_AUX_RX_STAT_INT_0); } ahci_init_controller(host); } ata_host_resume(host); return 0; } #else 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 = ap->host->private_data; struct tegra_ahci_host_priv *tegra_hpriv = host->private_data; struct ata_link *link; struct ata_device *dev; int ret = 0; u32 port_status = 0; int enter_slumber_timeout = 50; enum tegra_ahci_port_runtime_status lpm_state; int i; lpm_state = TEGRA_AHCI_PORT_RUNTIME_ACTIVE; tegra_hpriv->skip_rtpm = false; if (!ata_dev_enabled(ap->link.device)) goto skip; port_status = tegra_ahci_get_port_status(); 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 < enter_slumber_timeout; i++) { port_status = tegra_ahci_get_port_status(); port_status = (port_status & 0xF00) >> 8; 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 (lpm_state == TEGRA_AHCI_PORT_RUNTIME_ACTIVE || port_status == TEGRA_AHCI_PORT_RUNTIME_ACTIVE) { if (ap->pm_mesg.event & PM_EVENT_AUTO) { tegra_hpriv->skip_rtpm = true; return 0; } } skip: if (!ret && !(ap->pflags & ATA_PFLAG_SUSPENDED)) { ret = ahci_ops.port_suspend(ap, mesg); } if (ret == 0) { pm_runtime_mark_last_busy(&tegra_hpriv->pdev->dev); pm_runtime_put_sync_autosuspend(&g_tegra_hpriv->pdev->dev); } return ret; } static int tegra_ahci_port_resume(struct ata_port *ap) { struct ata_host *host = ap->host; struct tegra_ahci_host_priv *tegra_hpriv = host->private_data; struct ata_link *link = NULL; struct scsi_device *sdev = NULL; int ret = 0; if (tegra_hpriv->skip_rtpm) { tegra_hpriv->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; } } ret = pm_runtime_get_sync(&tegra_hpriv->pdev->dev); if (ret < 0) { dev_err(&tegra_hpriv->pdev->dev, "%s(%d) Failed to resume the devcie err=%d\n", __func__, __LINE__, ret); return AC_ERR_SYSTEM; } 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; } else shost_for_each_device(sdev, ap->scsi_host) { if (sdev->request_queue->rpm_status == RPM_SUSPENDED) sdev->request_queue->rpm_status = RPM_ACTIVE; } } 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_suspend_common(struct platform_device *pdev, pm_message_t mesg) { struct ata_host *host = dev_get_drvdata(&pdev->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 ctl; int rc; dev_dbg(host->dev, "** entering %s: **\n", __func__); if (mesg.event & PM_EVENT_SLEEP) { /* * AHCI spec rev1.1 section 8.3.3: * Software must disable interrupts prior to requesting a * transition of the HBA to D3 state. */ ctl = readl(mmio + HOST_CTL); ctl &= ~HOST_IRQ_EN; writel(ctl, mmio + HOST_CTL); readl(mmio + HOST_CTL); /* flush */ } rc = ata_host_suspend(host, mesg); if (rc) return rc; return tegra_ahci_controller_suspend(pdev); } static int tegra_ahci_suspend(struct device *dev) { struct platform_device *pdev = g_tegra_hpriv->pdev; dev_dbg(dev, "Suspending...\n"); return tegra_ahci_suspend_common(pdev, PMSG_SUSPEND); } static int tegra_ahci_runtime_suspend(struct device *dev) { struct platform_device *pdev = g_tegra_hpriv->pdev; struct ata_host *host = dev_get_drvdata(&pdev->dev); int err = 0; bool pg_ok; if (tegra_ahci_are_all_ports_idle(host)) { /* if all ports are in idle, do power-gate */ #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE pg_ok = tegra_ahci_power_gate(host); #else pg_ok = tegra_ahci_pad_suspend(host); #endif if (pg_ok) { dev_dbg(dev, "rt-suspend Done\n"); } else { dev_dbg(dev, "rt-suspend Failed\n"); #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE tegra_ahci_abort_power_gate(host); #else tegra_ahci_abort_pad_suspend(host); #endif err = -EBUSY; } } else { dev_dbg(dev, "Port not idle...\n"); err = -EBUSY; } return err; } static int tegra_ahci_resume(struct device *dev) { struct platform_device *pdev = g_tegra_hpriv->pdev; struct ata_host *host = dev_get_drvdata(&pdev->dev); int rc; u32 val; dev_dbg(host->dev, "** entering %s: **\n", __func__); rc = tegra_ahci_controller_resume(pdev); if (rc != 0) return rc; rc = g_tegra_hpriv->soc_data->controller_init(g_tegra_hpriv, 1); if (rc != 0) { dev_err(host->dev, "TEGRA SATA init failed in resume\n"); tegra_ahci_controller_suspend(pdev); return rc; } if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_reset_controller(host); if (rc) { dev_err(host->dev, "TEGRA SATA reset failed in resume\n"); tegra_ahci_controller_remove(pdev); return rc; } val = misc_readl(SATA_AUX_RX_STAT_INT_0); if (val & SATA_RX_STAT_INT_DISABLE) { val &= ~SATA_RX_STAT_INT_DISABLE; misc_writel(val, SATA_AUX_RX_STAT_INT_0); } ahci_init_controller(host); } ata_host_resume(host); #ifdef CONFIG_PM #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE pm_runtime_disable(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); #endif #endif return 0; } static int tegra_ahci_runtime_resume(struct device *dev) { struct platform_device *pdev = g_tegra_hpriv->pdev; struct ata_host *host = dev_get_drvdata(&pdev->dev); int err = 0; #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE err = tegra_ahci_power_un_gate(host); #else err = tegra_ahci_pad_resume(host); #endif if (err) return 0; else return -EBUSY; } #endif static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { int ret; u32 val; ret = ahci_ops.hardreset(link, class, deadline); if (ret < 0) { val = scfg_readl(T_SATA0_CFG_LINK_0); val |= T_SATA0_CFG_LINK_0_USE_POSEDGE_SCTL_DET; scfg_writel(val, T_SATA0_CFG_LINK_0); } return ret; } static int tegra_ahci_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { int ret; u32 val; ret = ahci_ops.softreset(link, class, deadline); if (ret < 0) { val = scfg_readl(T_SATA0_CFG_LINK_0); val |= T_SATA0_CFG_LINK_0_USE_POSEDGE_SCTL_DET; scfg_writel(val, T_SATA0_CFG_LINK_0); } return ret; } #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE static u16 pg_save_bar5_registers[] = { 0x018, /* T_AHCI_HBA_CCC_PORTS */ 0x004, /* T_AHCI_HBA_GHC */ 0x014, /* T_AHCI_HBA_CCC_CTL - OP (optional) */ 0x01C, /* T_AHCI_HBA_EM_LOC */ 0x020 /* T_AHCI_HBA_EM_CTL - OP */ }; static u16 pg_save_bar5_port_registers[] = { 0x100, /* T_AHCI_PORT_PXCLB */ 0x104, /* T_AHCI_PORT_PXCLBU */ 0x108, /* T_AHCI_PORT_PXFB */ 0x10C, /* T_AHCI_PORT_PXFBU */ 0x114, /* T_AHCI_PORT_PXIE */ 0x118, /* T_AHCI_PORT_PXCMD */ 0x12C, /* T_AHCI_PORT_PXSCTL */ 0x144 /* T_AHCI_PORT_PXDEVSLP */ }; /* * pg_save_bar5_bkdr_registers: * These registers in BAR5 are read only. * To restore back those register values, write the saved value * to the registers specified in pg_restore_bar5_bkdr_registers[]. * These pg_restore_bar5_bkdr_registers[] are in SATA_CONFIG space. */ static u16 pg_save_bar5_bkdr_registers[] = { /* Save and restore via bkdr writes */ 0x000, /* T_AHCI_HBA_CAP */ 0x00C, /* T_AHCI_HBA_PI */ 0x024 /* T_AHCI_HBA_CAP2 */ }; static u16 pg_restore_bar5_bkdr_registers[] = { /* Save and restore via bkdr writes */ 0x300, /* BKDR of T_AHCI_HBA_CAP */ 0x33c, /* BKDR of T_AHCI_HBA_PI */ 0x330 /* BKDR of T_AHCI_HBA_CAP2 */ }; /* These registers are saved for each port */ static u16 pg_save_bar5_bkdr_port_registers[] = { 0x120, /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXTFD */ 0x124, /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXSIG */ 0x128 /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXSSTS */ }; static u16 pg_restore_bar5_bkdr_port_registers[] = { /* Save and restore via bkdr writes */ 0x790, /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXTFD */ 0x794, /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXSIG */ 0x798 /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXSSTS */ }; static u16 pg_save_config_registers[] = { 0x004, /* T_SATA0_CFG_1 */ 0x00C, /* T_SATA0_CFG_3 */ 0x024, /* T_SATA0_CFG_9 */ 0x028, /* T_SATA0_CFG_10 */ 0x030, /* T_SATA0_CFG_12 */ 0x034, /* T_SATA0_CFG_13 */ 0x038, /* T_SATA0_CFG_14 */ 0x03C, /* T_SATA0_CFG_15 */ 0x040, /* T_SATA0_CFG_16 */ 0x044, /* T_SATA0_CFG_17 */ 0x048, /* T_SATA0_CFG_18 */ 0x0B0, /* T_SATA0_MSI_CTRL */ 0x0B4, /* T_SATA0_MSI_ADDR1 */ 0x0B8, /* T_SATA0_MSI_ADDR2 */ 0x0BC, /* T_SATA0_MSI_DATA */ 0x0C0, /* T_SATA0_MSI_QUEUE */ 0x0EC, /* T_SATA0_MSI_MAP */ 0x124, /* T_SATA0_CFG_PHY_POWER */ 0x128, /* T_SATA0_CFG_PHY_POWER_1 */ 0x12C, /* T_SATA0_CFG_PHY_1 */ 0x174, /* T_SATA0_CFG_LINK_0 */ 0x178, /* T_SATA0_CFG_LINK_1 */ 0x1D0, /* MCP_SATA0_CFG_TRANS_0 */ 0x238, /* T_SATA0_ALPM_CTRL */ 0x30C, /* T_SATA0_AHCI_HBA_CYA_0 */ 0x320, /* T_SATA0_AHCI_HBA_SPARE_1 */ 0x324, /* T_SATA0_AHCI_HBA_SPARE_2 */ 0x328, /* T_SATA0_AHCI_HBA_DYN_CLK_CLAMP */ 0x32C, /* T_SATA0_AHCI_CFG_ERR_CTRL */ 0x338, /* T_SATA0_AHCI_HBA_CYA_1 */ 0x340, /* T_SATA0_AHCI_HBA_PRE_STAGING_CONTROL */ 0x430, /* T_SATA0_CFG_FPCI_0 */ 0x494, /* T_SATA0_CFG_ESATA_CTRL */ 0x4A0, /* T_SATA0_CYA1 */ 0x4B0, /* T_SATA0_CFG_GLUE */ 0x534, /* T_SATA0_PHY_CTRL */ 0x540, /* T_SATA0_CTRL */ 0x550, /* T_SATA0_DBG0 */ 0x554 /* T_SATA0_LOW_POWER_COUNT */ }; static u16 pg_save_config_port_registers[] = { /* Save and restore per port */ /* need to have port selected */ 0x530, /* T_SATA0_CHXCFG1 */ 0x684, /* T_SATA0_CHX_MISC */ 0x700, /* T_SATA0_CHXCFG3 */ 0x704, /* T_SATA0_CHXCFG4_CHX */ 0x690, /* T_SATA0_CHX_PHY_CTRL1_GEN1 */ 0x694, /* T_SATA0_CHX_PHY_CTRL1_GEN2 */ 0x698, /* T_SATA0_CHX_PHY_CTRL1_GEN3 */ 0x69C, /* T_SATA0_CHX_PHY_CTRL_2 */ 0x6B0, /* T_SATA0_CHX_PHY_CTRL_3 */ 0x6B4, /* T_SATA0_CHX_PHY_CTRL_4 */ 0x6B8, /* T_SATA0_CHX_PHY_CTRL_5 */ 0x6BC, /* T_SATA0_CHX_PHY_CTRL_6 */ 0x714, /* T_SATA0_PRBS_CHX - OP */ 0x750, /* T_SATA0_CHX_LINK0 */ 0x7F0 /* T_SATA0_CHX_GLUE */ }; static u16 pg_save_ipfs_registers[] = { 0x094, /* SATA_FPCI_BAR5_0 */ 0x0C0, /* SATA_MSI_BAR_SZ_0 */ 0x0C4, /* SATA_MSI_AXI_BAR_ST_0 */ 0x0C8, /* SATA_MSI_FPCI_BAR_ST_0 */ 0x140, /* SATA_MSI_EN_VEC0_0 */ 0x144, /* SATA_MSI_EN_VEC1_0 */ 0x148, /* SATA_MSI_EN_VEC2_0 */ 0x14C, /* SATA_MSI_EN_VEC3_0 */ 0x150, /* SATA_MSI_EN_VEC4_0 */ 0x154, /* SATA_MSI_EN_VEC5_0 */ 0x158, /* SATA_MSI_EN_VEC6_0 */ 0x15C, /* SATA_MSI_EN_VEC7_0 */ 0x180, /* SATA_CONFIGURATION_0 */ 0x184, /* SATA_FPCI_ERROR_MASKS_0 */ 0x188, /* SATA_INTR_MASK_0 */ 0x1A0, /* SATA_CFG_REVID_0 */ 0x198, /* SATA_IPFS_INTR_ENABLE_0 */ 0x1BC, /* SATA_CLKGATE_HYSTERSIS_0 */ 0x1DC /* SATA_SATA_MCCIF_FIFOCTRL_0 */ }; static void tegra_ahci_save_regs(u32 **save_addr, void __iomem *base, u16 reg_array[], u32 regs) { u32 i; u32 *dest = *save_addr; for (i = 0; i < regs; ++i, ++dest) { *dest = readl(base + (u32)reg_array[i]); AHCI_DBG_PRINT("save: [0x%x]=0x%08x\n", (base+(u32)reg_array[i]), *dest); } *save_addr = dest; } static void tegra_ahci_restore_regs(void **save_addr, void __iomem *base, u16 reg_array[], u32 regs) { u32 i; u32 *src = *save_addr; for (i = 0; i < regs; ++i, ++src) { writel(*src, base + (u32)reg_array[i]); AHCI_DBG_PRINT("restore: [0x%x]=0x%08x\n", (base+(u32)reg_array[i]), *src); } *save_addr = src; } static void tegra_ahci_pg_save_registers(struct ata_host *host) { struct tegra_ahci_host_priv *tegra_hpriv; u32 *pg_save; u32 regs; int i; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; pg_save = tegra_hpriv->pg_save; /* * Driver should save/restore the registers in the order of * IPFS, CFG, Ext CFG, BAR5. */ /* save IPFS registers */ regs = ARRAY_SIZE(pg_save_ipfs_registers); tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[3], pg_save_ipfs_registers, regs); /* after the call, pg_save should point to the next address to save */ /* save CONFIG registers */ regs = ARRAY_SIZE(pg_save_config_registers); tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[0], pg_save_config_registers, regs); /* save CONFIG per port registers */ for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); regs = ARRAY_SIZE(pg_save_config_port_registers); tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[0], pg_save_config_port_registers, regs); } scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); /* save BAR5 registers */ regs = ARRAY_SIZE(pg_save_bar5_registers); tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[1], pg_save_bar5_registers, regs); /* save BAR5 port_registers */ regs = ARRAY_SIZE(pg_save_bar5_port_registers); for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[1] + (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_hpriv->base_list[1], pg_save_bar5_bkdr_registers, regs); /* and save bkdr per_port registers */ for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); regs = ARRAY_SIZE(pg_save_bar5_bkdr_port_registers); tegra_ahci_save_regs(&pg_save, tegra_hpriv->base_list[1] + (0x80*i), pg_save_bar5_bkdr_port_registers, regs); } scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); } static void tegra_ahci_pg_restore_registers(struct ata_host *host) { struct tegra_ahci_host_priv *tegra_hpriv; void *pg_save; u32 regs, val; int i; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; pg_save = tegra_hpriv->pg_save; /* * Driver should restore the registers in the order of * IPFS, CFG, Ext CFG, BAR5. */ /* restore IPFS registers */ regs = ARRAY_SIZE(pg_save_ipfs_registers); tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[3], pg_save_ipfs_registers, regs); /* after the call, pg_save should point to the next addr to restore */ /* restore CONFIG registers */ regs = ARRAY_SIZE(pg_save_config_registers); tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[0], pg_save_config_registers, regs); /* restore CONFIG per port registers */ for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); regs = ARRAY_SIZE(pg_save_config_port_registers); tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[0], pg_save_config_port_registers, regs); } scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); /* restore BAR5 registers */ regs = ARRAY_SIZE(pg_save_bar5_registers); tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[1], pg_save_bar5_registers, regs); /* restore BAR5 port_registers */ regs = ARRAY_SIZE(pg_save_bar5_port_registers); for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[1]+(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_hpriv->base_list[0], pg_restore_bar5_bkdr_registers, regs); /* and restore BAR5 bkdr per_port registers */ for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); regs = ARRAY_SIZE(pg_restore_bar5_bkdr_port_registers); tegra_ahci_restore_regs(&pg_save, tegra_hpriv->base_list[0], pg_restore_bar5_bkdr_port_registers, regs); } scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); /* program class code and programming interface for AHCI */ val = scfg_readl(TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); val |= TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); scfg_writel(TEGRA_PRIVATE_AHCI_CC_BKDR_PGM, TEGRA_PRIVATE_AHCI_CC_BKDR); val &= ~TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); } #endif static u32 tegra_ahci_port_error(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); u32 err_status; err_status = readl(port_mmio + PORT_IRQ_STAT); /* excludes PhyRdy and Connect Change status */ err_status &= (PORT_IRQ_ERROR & (~(PORT_IRQ_PHYRDY|PORT_IRQ_CONNECT))); return err_status; } static bool tegra_ahci_check_errors(struct ata_host *host) { int i; struct ata_port *ap; u32 err; for (i = 0; i < host->n_ports; i++) { ap = host->ports[i]; err = tegra_ahci_port_error(ap); if (err) { dev_err(host->dev, "pg-chk-err = 0x%08x on port %d\n", err, i); return true; } } return false; } static void tegra_ahci_put_sata_in_iddq(struct ata_host *host) { struct tegra_ahci_host_priv *tegra_hpriv; u32 val; u32 dat; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; /* * Hw wake up is not needed: * Driver/RM shall place the SATA PHY and SATA PADPLL in IDDQ. * SATA_PADPLL_RESET_SWCTL =1 * SATA_PADPLL_RESET_OVERRIDE_VALUE=1 * SATA_PADPHY_IDDQ_SWCTL=1 * SATA_PADPHY_IDDQ_OVERRIDE_VALUE=1 */ /* Wait for time specified in SATA_LANE_IDDQ2_PADPLL_IDDQ */ val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); dat = (val & IDDQ2LANE_IDDQ_DLY_MASK) >> IDDQ2LANE_IDDQ_DLY_SHIFT; udelay(dat); } static void tegra_ahci_abort_power_gate(struct ata_host *host) { tegra_pmc_sata_pwrgt_update(PMC_SATA_PG_INFO_MASK, PMC_SATA_PG_INFO_OFF); } static bool tegra_ahci_power_gate(struct ata_host *host) { u32 val; u32 dat; struct tegra_ahci_host_priv *tegra_hpriv; int status; int partition_id; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; tegra_pmc_sata_pwrgt_update(PMC_SATA_PG_INFO_MASK, PMC_SATA_PG_INFO_ON); #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE tegra_ahci_pg_save_registers(host); #endif /* * Read SATA_AUX_MISC_CNTL_1_0 register L0_RX_IDLE_T_SAX field and * write that value into same register L0_RX_IDLE_T_NPG field. * And write 1 to L0_RX_IDLE_T_MUX field. */ val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); dat = val; dat &= L0_RX_IDLE_T_SAX_MASK; dat >>= L0_RX_IDLE_T_SAX_SHIFT; dat <<= L0_RX_IDLE_T_NPG_SHIFT; val &= ~L0_RX_IDLE_T_NPG_MASK; val |= dat; val |= L0_RX_IDLE_T_MUX_FROM_APB_MISC; val |= DEVSLP_OVERRIDE; misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); /* abort PG if there are errors occurred */ if (tegra_ahci_check_errors(host)) { dev_err(host->dev, "** pg: errors; abort power gating **\n"); return false; } /* make sure all ports have no outstanding commands and are idle. */ if (!tegra_ahci_are_all_ports_idle(host)) { dev_err(host->dev, "** pg: cmds; abort power gating **\n"); return false; } tegra_ahci_put_sata_in_iddq(host); /* power off the sata */ #ifdef CONFIG_PM_GENERIC_DOMAINS_OF partition_id = tegra_pd_get_powergate_id(tegra_sata_pd); if (partition_id < 0) return false; #else partition_id = TEGRA_POWERGATE_SATA; #endif tegra_first_level_clk_gate(); status = tegra_powergate_partition(partition_id); if (status) { dev_err(host->dev, "** failed to turn-off SATA (0x%x) **\n", status); return false; } tegra_hpriv->pg_state = SATA_OFF; return true; } static bool tegra_ahci_power_un_gate(struct ata_host *host) { u32 val; struct tegra_ahci_host_priv *tegra_hpriv; int status; int powergate_id; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; #ifdef CONFIG_PM_GENERIC_DOMAINS_OF powergate_id = tegra_pd_get_powergate_id(tegra_sata_pd); if (powergate_id < 0) return false; #else powergate_id = TEGRA_POWERGATE_SATA; #endif if (tegra_first_level_clk_ungate() < 0) { dev_err(host->dev, "%s: clk ungate failed\n", __func__); return false; } status = tegra_unpowergate_partition(powergate_id); if (status) { dev_err(host->dev, "** failed to turn-on SATA (0x%x) **\n", status); return false; } #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE /* restore registers */ tegra_ahci_pg_restore_registers(host); #endif tegra_ahci_set_pad_cntrl_regs(tegra_hpriv); /* * 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. */ val = scfg_readl(T_SATA0_CFG_POWER_GATE); val &= ~POWER_GATE_SSTS_RESTORED_MASK; val |= POWER_GATE_SSTS_RESTORED_YES; scfg_writel(val, T_SATA0_CFG_POWER_GATE); /* * Driver needs to switch the rx_idle_t driven source back to from * Sata controller after SAX is power-ungated. */ val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); val &= ~DEVSLP_OVERRIDE; val &= ~L0_RX_IDLE_T_MUX_MASK; val |= L0_RX_IDLE_T_MUX_FROM_SATA; misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); /* * power un-gating process is complete by clearing * APBDEV_PMC_SATA_PWRGT_0.Pmc2sata_pg_info = 0 */ tegra_pmc_sata_pwrgt_update(PMC_SATA_PG_INFO_MASK, PMC_SATA_PG_INFO_OFF); tegra_hpriv->pg_state = SATA_ON; return true; } static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); if (readl(port_mmio + PORT_CMD_ISSUE) || readl(port_mmio + PORT_SCR_ACT)) return PORT_IS_NOT_IDLE; return PORT_IS_IDLE; } /* check if all supported ports are idle (no outstanding commands) */ static bool tegra_ahci_are_all_ports_idle(struct ata_host *host) { int i; struct ata_port *ap; for (i = 0; i < host->n_ports; i++) { ap = host->ports[i]; if (ap && (tegra_ahci_is_port_idle(ap) == PORT_IS_NOT_IDLE)) return false; } return true; } #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE #ifndef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE static void tegra_ahci_abort_pad_suspend(struct ata_host *host) { /*No implementation*/ } static bool tegra_ahci_pad_suspend(struct ata_host *host) { u32 val; struct tegra_ahci_host_priv *tegra_hpriv; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; /* abort PG if there are errors occurred */ if (tegra_ahci_check_errors(host)) { dev_err(host->dev, "** pg: errors; abort power gating **\n"); return false; } /* make sure all ports have no outstanding commands and are idle. */ if (!tegra_ahci_are_all_ports_idle(host)) { dev_err(host->dev, "** pg: cmds; abort power gating **\n"); return false; } /* Set the bits in the CAR to allow HW based low power sequencing. */ val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); val |= PADPLL_RESET_SWCTL_MASK; clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); tegra_ahci_put_sata_in_iddq(host); val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); val |= (SATA_SEQ_PADPLL_PD_INPUT_VALUE | SATA_SEQ_LANE_PD_INPUT_VALUE | SATA_SEQ_RESET_INPUT_VALUE); clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); tegra_first_level_clk_gate(); return true; } static bool tegra_ahci_pad_resume(struct ata_host *host) { u32 val; struct tegra_ahci_host_priv *tegra_hpriv; tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); val &= ~(SATA_SEQ_PADPLL_PD_INPUT_VALUE | SATA_SEQ_LANE_PD_INPUT_VALUE | SATA_SEQ_RESET_INPUT_VALUE); clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); if (tegra_first_level_clk_ungate() < 0) { pr_err("%s: flcg ungate failed\n", __func__); return false; } tegra_ahci_iddqlane_config(); /* deasset PADPLL and wait until it locks. */ val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); val &= ~PADPLL_RESET_OVERRIDE_VALUE_MASK; clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); /* Set the bits in the CAR to allow HW based low power sequencing. */ val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); val &= ~PADPLL_RESET_SWCTL_MASK; clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); /* Second Level Clock Gating*/ val = bar5_readl(AHCI_HBA_PLL_CTRL_0); val |= (CLAMP_TXCLK_ON_SLUMBER | CLAMP_TXCLK_ON_DEVSLP); val &= ~NO_CLAMP_SHUT_DOWN; bar5_writel(val, AHCI_HBA_PLL_CTRL_0); return true; } static void tegra_ahci_iddqlane_config(void) { u32 val; u32 dat; /* wait for SATA_PADPLL_IDDQ2LANE_SLUMBER_DLY = 3 microseconds. */ val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); val &= ~IDDQ2LANE_SLUMBER_DLY_MASK; val |= IDDQ2LANE_SLUMBER_DLY_3MS; clk_writel(val, CLK_RST_SATA_PLL_CFG1_REG); /* wait for delay of IDDQ2LAND_SLUMBER_DLY */ val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); dat = (val & IDDQ2LANE_SLUMBER_DLY_MASK) >> IDDQ2LANE_SLUMBER_DLY_SHIFT; udelay(dat); } #endif #endif #endif static int tegra_ahci_remove_one(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); struct ahci_host_priv *hpriv; BUG_ON(host == NULL); BUG_ON(host->iomap[AHCI_PCI_BAR] == NULL); hpriv = host->private_data; tegra_ahci_controller_remove(pdev); devm_iounmap(&pdev->dev, host->iomap[AHCI_PCI_BAR]); ata_host_detach(host); #ifdef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE /* Free PG save/restore area */ devm_kfree(&pdev->dev, ((struct tegra_ahci_host_priv *)hpriv)->pg_save); #endif devm_kfree(&pdev->dev, hpriv); return 0; } static void tegra_ahci_sata_clk_gate(void) { u32 val; if (!tegra_platform_is_silicon()) return; val = clk_readl(CLK_RST_CONTROLLER_RST_DEV_W_SET); if (val & SET_CEC_RESET) clk_writel(0x108, CLK_RST_CONTROLLER_RST_DEV_V_SET); val = clk_readl(CLK_RST_CONTROLLER_RST_DEV_W_SET); while (val & SET_CEC_RESET) val = clk_readl(CLK_RST_CONTROLLER_RST_DEV_W_SET); } static int tegra_ahci_init_one(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct ata_port_info pi = ahci_port_info; struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv = NULL; struct tegra_ahci_host_priv *tegra_hpriv = NULL; struct tegra_ahci_platform_data *ahci_pdata; struct ata_host *host = NULL; int i, rc = 0; struct resource *res, *irq_res; struct phy *phy = NULL; void __iomem *mmio; enum tegra_chipid cid; #if defined(CONFIG_TEGRA_AHCI_CONTEXT_RESTORE) u32 save_size; #endif cid = tegra_get_chipid(); VPRINTK("ENTER\n"); WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS); ata_print_version_once(&pdev->dev, DRV_VERSION); /* Simple resource validation */ if (pdev->num_resources != 5) { dev_err(dev, "invalid number of resources\n"); dev_err(dev, "not enough SATA resources\n"); return -EINVAL; } /* acquire bar resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-ahci"); if (res == NULL) return -EINVAL; /* acquire IRQ resource */ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq_res == NULL) return -EINVAL; if (irq_res->start <= 0) return -EINVAL; /* allocate sizeof tegra_ahci_host_priv, which contains extra fields */ hpriv = devm_kzalloc(dev, sizeof(struct tegra_ahci_host_priv), GFP_KERNEL); if (!hpriv) { rc = -ENOMEM; goto fail; } hpriv->flags |= (unsigned long)pi.private_data; tegra_hpriv = (struct tegra_ahci_host_priv *)hpriv; tegra_hpriv->dev = dev; if (np) { match = of_match_device(of_match_ptr(of_ahci_tegra_match), &pdev->dev); if (!match) return -ENODEV; /* *of_property_read_u8 does not overwrite the third argument, *if corresponding dt node does not exist.So it is safe to *call this function without checking presence of dt node. */ tegra_hpriv->dtContainsPadval = 1; if (of_property_read_u8(np, "nvidia,gen1-amp", &tegra_hpriv->pad_val.gen1_tx_amp) != 0) tegra_hpriv->dtContainsPadval = 0; if (of_property_read_u8(np, "nvidia,gen2-amp", &tegra_hpriv->pad_val.gen2_tx_amp) != 0) tegra_hpriv->dtContainsPadval = 0; if (of_property_read_u8(np, "nvidia,gen1-peak", &tegra_hpriv->pad_val.gen1_tx_peak) != 0) tegra_hpriv->dtContainsPadval = 0; if (of_property_read_u8(np, "nvidia,gen2-peak", &tegra_hpriv->pad_val.gen2_tx_peak) != 0) tegra_hpriv->dtContainsPadval = 0; if (of_property_read_u8(np, "nvidia,l2p_fifo_depth", &tegra_hpriv->fifo_depth) != 0){ tegra_hpriv->fifo_depth = 0x7; } tegra_hpriv->soc_data = (struct tegra_sata_soc_data *)match->data; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; tegra_hpriv->pexp_gpio_high = of_get_named_gpio(np, "nvidia,pexp-gpio", 0); tegra_hpriv->pexp_gpio_low = of_get_named_gpio(np, "nvidia,pexp-gpio", 1); if (!of_property_read_bool(np, "nvidia,enable-sata-port")) { dev_err(dev, "Not able to find enable-sata-port property\n"); tegra_ahci_sata_clk_gate(); goto fail; } if (of_property_read_u32(np, "nvidia,sata-connector-type", &tegra_hpriv->sata_connector) < 0) { tegra_hpriv->sata_connector = MINI_SATA; } tegra_hpriv->prod_list = devm_tegra_prod_get(dev); if (IS_ERR(tegra_hpriv->prod_list)) { dev_err(dev, "Prod Init failed\n"); tegra_hpriv->prod_list = NULL; } } else { ahci_pdata = tegra_hpriv->dev->platform_data; tegra_hpriv->pexp_gpio_high = ahci_pdata->pexp_gpio_high; tegra_hpriv->pexp_gpio_low = ahci_pdata->pexp_gpio_low; tegra_hpriv->prod_list = NULL; } tegra_hpriv->cid = cid; tegra_hpriv->pdev = pdev; g_tegra_hpriv = tegra_hpriv; /* map clock and reset registers */ np = of_find_matching_node(NULL, car_match); if (!np) { pr_err("%s: cannot find matching CAR node.", __func__); return -ENODEV; } tegra_hpriv->base_car = of_iomap(np, 0); /* * We reserve a table of 6 BARs in tegra_hpriv to store BARs. * Save the mapped AHCI_PCI_BAR address to the table. */ mmio = devm_ioremap(dev, res->start, resource_size(res)); tegra_hpriv->bars_table[AHCI_PCI_BAR] = mmio; hpriv->mmio = mmio; tegra_hpriv->base_list[1] = mmio; tegra_hpriv->reg_offset[1] = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-config"); if (res == NULL) return -EINVAL; tegra_hpriv->base_list[0] = devm_ioremap(dev, res->start, resource_size(res)); tegra_hpriv->reg_offset[0] = res->start; /* map misc/aux registers */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-aux"); if (res == NULL) return -EINVAL; tegra_hpriv->base_list[2] = devm_ioremap(dev, res->start, resource_size(res)); tegra_hpriv->reg_offset[2] = res->start; /* map sata registers */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-ipfs"); if (res == NULL) return -EINVAL; tegra_hpriv->base_list[3] = devm_ioremap(dev, res->start, resource_size(res)); tegra_hpriv->reg_offset[3] = res->start; /* get phy */ if (tegra_platform_is_silicon()) { phy = devm_phy_optional_get(dev, "sata-phy"); if (IS_ERR(phy)) { rc = PTR_ERR(phy); dev_err(dev, "cannot get sata-phy"); goto fail; } hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys), GFP_KERNEL); if (!hpriv->phys) { rc = -ENOMEM; goto fail; } hpriv->phys[0] = phy; hpriv->nports = 1; } else { hpriv->nports = 1; } /* get regulators */ hpriv->target_pwrs = devm_kzalloc(dev, sizeof(*hpriv->target_pwrs), GFP_KERNEL); if (!hpriv->target_pwrs) { rc = -ENOMEM; goto fail; } hpriv->target_pwrs[0] = devm_regulator_get_optional(dev, "target-3v3"); if (IS_ERR(hpriv->target_pwrs[0])) { rc = PTR_ERR(hpriv->target_pwrs[0]); if (rc == -EPROBE_DEFER) goto fail; hpriv->target_pwrs[0] = NULL; } /* Call tegra init routine */ rc = tegra_hpriv->soc_data->controller_init(tegra_hpriv, 0); if (rc != 0) { dev_err(dev, "TEGRA SATA init failed\n"); goto fail; } #if defined(CONFIG_TEGRA_AHCI_CONTEXT_RESTORE) /* Setup PG save/restore area: */ /* calculate the size */ save_size = ARRAY_SIZE(pg_save_ipfs_registers) + ARRAY_SIZE(pg_save_config_registers) + ARRAY_SIZE(pg_save_bar5_registers) + ARRAY_SIZE(pg_save_bar5_bkdr_registers); /* and add save port_registers for all the ports */ save_size += TEGRA_AHCI_NUM_PORTS * (ARRAY_SIZE(pg_save_config_port_registers) + 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_hpriv->pg_save = devm_kzalloc(dev, save_size, GFP_KERNEL); if (!tegra_hpriv->pg_save) { rc = -ENOMEM; goto fail; } #endif /* Suspend the controller if drive is not present */ if (!tegra_ahci_get_port_status()) { dev_dbg(dev, "Drive not present\n"); } /* Disable DIPM */ pi.flags |= ATA_FLAG_NO_DIPM; rc = ahci_platform_init_host(pdev, hpriv, &pi, &ahci_sht); if (rc) goto fail; host = dev_get_drvdata(&pdev->dev); tegra_hpriv->host = host; host->iomap = tegra_hpriv->bars_table; ahci_print_info(host, "TEGRA-SATA"); dev_dbg(dev, "controller init okay\n"); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; /* set initial link pm policy */ ap->target_lpm_policy = ATA_LPM_UNKNOWN; /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) ap->ops = &ata_dummy_port_ops; else ap->target_lpm_policy = ATA_LPM_MIN_POWER; } #ifdef CONFIG_PM #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE rc = pm_runtime_set_active(dev); if (rc) { dev_dbg(dev, "unable to set runtime pm active err=%d\n", rc); } else { dev_dbg(dev, "Set runtime pm active err=%d\n", rc); pm_runtime_set_autosuspend_delay(dev, TEGRA_AHCI_DEFAULT_IDLE_TIME); pm_runtime_use_autosuspend(dev); pm_suspend_ignore_children(dev, true); pm_runtime_get_noresume(&tegra_hpriv->pdev->dev); pm_runtime_enable(dev); } #endif #endif return 0; fail: if (host) { if (host->iomap[AHCI_PCI_BAR]) devm_iounmap(dev, host->iomap[AHCI_PCI_BAR]); ata_host_detach(host); } if (hpriv) devm_kfree(dev, hpriv); return rc; } static void tegra_ahci_shutdown(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); int i = 0; do { /* make sure all ports have no outstanding commands and are idle. */ if (!tegra_ahci_are_all_ports_idle(host)) { mdelay(10); } else break; } while (++i < 50); if (i >= 50) dev_dbg(&pdev->dev, "There are outstanding commands but going \ ahead with shutdown process\n"); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; ahci_ops.port_stop(ap); } } static int __init ahci_init(void) { int ret = 0; ret = platform_driver_register(&tegra_platform_ahci_driver); if (ret < 0) return ret; #ifdef CONFIG_DEBUG_FS ret = tegra_ahci_dump_debuginit(); #endif return ret; } static void __exit ahci_exit(void) { platform_driver_unregister(&tegra_platform_ahci_driver); } #ifdef CONFIG_DEBUG_FS #include #include static void dbg_ahci_dump_regs(struct seq_file *s, void __iomem *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++) { seq_printf(s, "0x%08x: ", base+(i*16)); for (j = 0; j < REGS_PER_LINE; ++j) { seq_printf(s, "0x%08x ", readl(ptr)); ++ptr; } seq_puts(s, "\n"); } #undef REGS_PER_LINE } static int dbg_ahci_dump_show(struct seq_file *s, void *unused) { u32 base; void __iomem *ptr; u32 i; #ifndef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE int rc; #endif #endif int powergate_id; if (g_tegra_hpriv == NULL) return 0; #ifndef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE rc = pm_runtime_get_sync(&g_tegra_hpriv->pdev->dev); if (rc < 0) { dev_err(&g_tegra_hpriv->pdev->dev, "%s(%d) Failed to resume the devcie err=%d\n", __func__, __LINE__, rc); } #endif #endif base = g_tegra_hpriv->reg_offset[0]; ptr = g_tegra_hpriv->base_list[0]; seq_puts(s, "SATA CONFIG Registers:\n"); seq_puts(s, "----------------------\n"); dbg_ahci_dump_regs(s, ptr, base, 0x200); base = g_tegra_hpriv->reg_offset[1]; ptr = g_tegra_hpriv->base_list[1]; seq_puts(s, "\nAHCI HBA Registers:\n"); seq_puts(s, "-------------------\n"); dbg_ahci_dump_regs(s, ptr, base, 64); for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { base = g_tegra_hpriv->reg_offset[1] + 0x100 + (0x80*i); ptr = g_tegra_hpriv->base_list[1] + 0x100 + (0x80*i); seq_printf(s, "\nPort %u Registers:\n", i); seq_puts(s, "---------------\n"); dbg_ahci_dump_regs(s, ptr, base, 20); } #ifdef CONFIG_PM_GENERIC_DOMAINS_OF powergate_id = tegra_pd_get_powergate_id(tegra_sata_pd); if (powergate_id < 0) return -EINVAL; #else powergate_id = TEGRA_POWERGATE_SATA; #endif if (tegra_powergate_is_powered(powergate_id)) seq_puts(s, "\n=== SATA controller is powered on ===\n\n"); else seq_puts(s, "\n=== SATA controller is powered off ===\n\n"); #ifndef CONFIG_TEGRA_AHCI_CONTEXT_RESTORE #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE pm_runtime_mark_last_busy(&g_tegra_hpriv->pdev->dev); pm_runtime_put_sync_autosuspend(&g_tegra_hpriv->pdev->dev); #endif #endif return 0; } #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE static int dbg_ahci_rtpm_dump_show(struct seq_file *s, void *unused) { seq_printf(s, "\n\n device name = %s Runtime Status = %d\n" "Usage count = %d Child_count = %d" " ignore children = %d\n\n", dev_name(g_tegra_hpriv->dev), g_tegra_hpriv->dev->power.runtime_status, atomic_read(&g_tegra_hpriv->dev->power.usage_count), atomic_read(&g_tegra_hpriv->dev->power.child_count), g_tegra_hpriv->dev->power.ignore_children); seq_printf(s, "runtime_error = %d\n\n", g_tegra_hpriv->dev->power.runtime_error); seq_printf(s, "timer_expires = %ld\n\n", g_tegra_hpriv->dev->power.timer_expires); seq_printf(s, "disable_depth = %d\n\n", g_tegra_hpriv->dev->power.disable_depth); seq_printf(s, "idle_notification = %d\n\n", g_tegra_hpriv->dev->power.idle_notification); seq_printf(s, "request_pending = %d\n\n", g_tegra_hpriv->dev->power.request_pending); seq_printf(s, "deferred_resume = %d\n\n", g_tegra_hpriv->dev->power.deferred_resume); seq_printf(s, "run_wake = %d\n\n", g_tegra_hpriv->dev->power.run_wake); seq_printf(s, "runtime_auto = %d\n\n", g_tegra_hpriv->dev->power.runtime_auto); seq_printf(s, "no_callbacks = %d\n\n", g_tegra_hpriv->dev->power.no_callbacks); seq_printf(s, "irq_safe = %d\n\n", g_tegra_hpriv->dev->power.irq_safe); seq_printf(s, "timer_autosuspends = %d\n\n", g_tegra_hpriv->dev->power.timer_autosuspends); seq_printf(s, "last_busy = %ld\n\n", g_tegra_hpriv->dev->power.last_busy); return 0; } static int dbg_ahci_rtpm_dump_open(struct inode *inode, struct file *file) { return single_open(file, dbg_ahci_rtpm_dump_show, &inode->i_private); } static const struct file_operations debug_rtpm_fops = { .open = dbg_ahci_rtpm_dump_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif static int dbg_ahci_dump_open(struct inode *inode, struct file *file) { return single_open(file, dbg_ahci_dump_show, &inode->i_private); } static const struct file_operations debug_fops = { .open = dbg_ahci_dump_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int tegra_ahci_dump_debuginit(void) { (void) debugfs_create_file("tegra_ahci", S_IRUGO, NULL, NULL, &debug_fops); #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE (void) debugfs_create_file("tegra_rtpm_ahci", S_IRUGO, NULL, NULL, &debug_rtpm_fops); #endif return 0; } #endif MODULE_AUTHOR("NVIDIA"); MODULE_DESCRIPTION("Tegra AHCI SATA low-level driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); module_init(ahci_init); module_exit(ahci_exit);