| /* |
| * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| /* |
| * SATA init module. |
| * To be used with SATA interface on MSM targets. |
| */ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/io.h> |
| #include <linux/err.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/delay.h> |
| #include <linux/clk.h> |
| #include <linux/iopoll.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/ahci_platform.h> |
| #include <mach/clk.h> |
| |
| /* PHY registers */ |
| #define UNIPHY_PLL_REFCLK_CFG 0x000 |
| #define UNIPHY_PLL_POSTDIV1_CFG 0x004 |
| #define UNIPHY_PLL_CHGPUMP_CFG 0x008 |
| #define UNIPHY_PLL_VCOLPF_CFG 0x00C |
| #define UNIPHY_PLL_VREG_CFG 0x010 |
| #define UNIPHY_PLL_PWRGEN_CFG 0x014 |
| #define UNIPHY_PLL_DMUX_CFG 0x018 |
| #define UNIPHY_PLL_AMUX_CFG 0x01C |
| #define UNIPHY_PLL_GLB_CFG 0x020 |
| #define UNIPHY_PLL_POSTDIV2_CFG 0x024 |
| #define UNIPHY_PLL_POSTDIV3_CFG 0x028 |
| #define UNIPHY_PLL_LPFR_CFG 0x02C |
| #define UNIPHY_PLL_LPFC1_CFG 0x030 |
| #define UNIPHY_PLL_LPFC2_CFG 0x034 |
| #define UNIPHY_PLL_SDM_CFG0 0x038 |
| #define UNIPHY_PLL_SDM_CFG1 0x03C |
| #define UNIPHY_PLL_SDM_CFG2 0x040 |
| #define UNIPHY_PLL_SDM_CFG3 0x044 |
| #define UNIPHY_PLL_SDM_CFG4 0x048 |
| #define UNIPHY_PLL_SSC_CFG0 0x04C |
| #define UNIPHY_PLL_SSC_CFG1 0x050 |
| #define UNIPHY_PLL_SSC_CFG2 0x054 |
| #define UNIPHY_PLL_SSC_CFG3 0x058 |
| #define UNIPHY_PLL_LKDET_CFG0 0x05C |
| #define UNIPHY_PLL_LKDET_CFG1 0x060 |
| #define UNIPHY_PLL_LKDET_CFG2 0x064 |
| #define UNIPHY_PLL_TEST_CFG 0x068 |
| #define UNIPHY_PLL_CAL_CFG0 0x06C |
| #define UNIPHY_PLL_CAL_CFG1 0x070 |
| #define UNIPHY_PLL_CAL_CFG2 0x074 |
| #define UNIPHY_PLL_CAL_CFG3 0x078 |
| #define UNIPHY_PLL_CAL_CFG4 0x07C |
| #define UNIPHY_PLL_CAL_CFG5 0x080 |
| #define UNIPHY_PLL_CAL_CFG6 0x084 |
| #define UNIPHY_PLL_CAL_CFG7 0x088 |
| #define UNIPHY_PLL_CAL_CFG8 0x08C |
| #define UNIPHY_PLL_CAL_CFG9 0x090 |
| #define UNIPHY_PLL_CAL_CFG10 0x094 |
| #define UNIPHY_PLL_CAL_CFG11 0x098 |
| #define UNIPHY_PLL_EFUSE_CFG 0x09C |
| #define UNIPHY_PLL_DEBUG_BUS_SEL 0x0A0 |
| #define UNIPHY_PLL_CTRL_42 0x0A4 |
| #define UNIPHY_PLL_CTRL_43 0x0A8 |
| #define UNIPHY_PLL_CTRL_44 0x0AC |
| #define UNIPHY_PLL_CTRL_45 0x0B0 |
| #define UNIPHY_PLL_CTRL_46 0x0B4 |
| #define UNIPHY_PLL_CTRL_47 0x0B8 |
| #define UNIPHY_PLL_CTRL_48 0x0BC |
| #define UNIPHY_PLL_STATUS 0x0C0 |
| #define UNIPHY_PLL_DEBUG_BUS0 0x0C4 |
| #define UNIPHY_PLL_DEBUG_BUS1 0x0C8 |
| #define UNIPHY_PLL_DEBUG_BUS2 0x0CC |
| #define UNIPHY_PLL_DEBUG_BUS3 0x0D0 |
| #define UNIPHY_PLL_CTRL_54 0x0D4 |
| |
| #define SATA_PHY_SER_CTRL 0x100 |
| #define SATA_PHY_TX_DRIV_CTRL0 0x104 |
| #define SATA_PHY_TX_DRIV_CTRL1 0x108 |
| #define SATA_PHY_TX_DRIV_CTRL2 0x10C |
| #define SATA_PHY_TX_DRIV_CTRL3 0x110 |
| #define SATA_PHY_TX_RESV0 0x114 |
| #define SATA_PHY_TX_RESV1 0x118 |
| #define SATA_PHY_TX_IMCAL0 0x11C |
| #define SATA_PHY_TX_IMCAL1 0x120 |
| #define SATA_PHY_TX_IMCAL2 0x124 |
| #define SATA_PHY_RX_IMCAL0 0x128 |
| #define SATA_PHY_RX_IMCAL1 0x12C |
| #define SATA_PHY_RX_IMCAL2 0x130 |
| #define SATA_PHY_RX_TERM 0x134 |
| #define SATA_PHY_RX_TERM_RESV 0x138 |
| #define SATA_PHY_EQUAL 0x13C |
| #define SATA_PHY_EQUAL_RESV 0x140 |
| #define SATA_PHY_OOB_TERM 0x144 |
| #define SATA_PHY_CDR_CTRL0 0x148 |
| #define SATA_PHY_CDR_CTRL1 0x14C |
| #define SATA_PHY_CDR_CTRL2 0x150 |
| #define SATA_PHY_CDR_CTRL3 0x154 |
| #define SATA_PHY_CDR_CTRL4 0x158 |
| #define SATA_PHY_FA_LOAD0 0x15C |
| #define SATA_PHY_FA_LOAD1 0x160 |
| #define SATA_PHY_CDR_CTRL_RESV 0x164 |
| #define SATA_PHY_PI_CTRL0 0x168 |
| #define SATA_PHY_PI_CTRL1 0x16C |
| #define SATA_PHY_DESER_RESV 0x170 |
| #define SATA_PHY_RX_RESV0 0x174 |
| #define SATA_PHY_AD_TPA_CTRL 0x178 |
| #define SATA_PHY_REFCLK_CTRL 0x17C |
| #define SATA_PHY_POW_DWN_CTRL0 0x180 |
| #define SATA_PHY_POW_DWN_CTRL1 0x184 |
| #define SATA_PHY_TX_DATA_CTRL 0x188 |
| #define SATA_PHY_BIST_GEN0 0x18C |
| #define SATA_PHY_BIST_GEN1 0x190 |
| #define SATA_PHY_BIST_GEN2 0x194 |
| #define SATA_PHY_BIST_GEN3 0x198 |
| #define SATA_PHY_LBK_CTRL 0x19C |
| #define SATA_PHY_TEST_DEBUG_CTRL 0x1A0 |
| #define SATA_PHY_ALIGNP 0x1A4 |
| #define SATA_PHY_PRBS_CFG0 0x1A8 |
| #define SATA_PHY_PRBS_CFG1 0x1AC |
| #define SATA_PHY_PRBS_CFG2 0x1B0 |
| #define SATA_PHY_PRBS_CFG3 0x1B4 |
| #define SATA_PHY_CHAN_COMP_CHK_CNT 0x1B8 |
| #define SATA_PHY_RESET_CTRL 0x1BC |
| #define SATA_PHY_RX_CLR 0x1C0 |
| #define SATA_PHY_RX_EBUF_CTRL 0x1C4 |
| #define SATA_PHY_ID0 0x1C8 |
| #define SATA_PHY_ID1 0x1CC |
| #define SATA_PHY_ID2 0x1D0 |
| #define SATA_PHY_ID3 0x1D4 |
| #define SATA_PHY_RX_CHK_ERR_CNT0 0x1D8 |
| #define SATA_PHY_RX_CHK_ERR_CNT1 0x1DC |
| #define SATA_PHY_RX_CHK_STAT 0x1E0 |
| #define SATA_PHY_TX_IMCAL_STAT 0x1E4 |
| #define SATA_PHY_RX_IMCAL_STAT 0x1E8 |
| #define SATA_PHY_RX_EBUF_STAT 0x1EC |
| #define SATA_PHY_DEBUG_BUS_STAT0 0x1F0 |
| #define SATA_PHY_DEBUG_BUS_STAT1 0x1F4 |
| #define SATA_PHY_DEBUG_BUS_STAT2 0x1F8 |
| #define SATA_PHY_DEBUG_BUS_STAT3 0x1FC |
| |
| #define AHCI_HOST_CAP 0x00 |
| #define AHCI_HOST_CAP_MASK 0x1F |
| #define AHCI_HOST_CAP_PMP (1 << 17) |
| |
| struct msm_sata_hba { |
| struct platform_device *ahci_pdev; |
| struct clk *slave_iface_clk; |
| struct clk *bus_clk; |
| struct clk *iface_clk; |
| struct clk *src_clk; |
| struct clk *rxoob_clk; |
| struct clk *pmalive_clk; |
| struct clk *cfg_clk; |
| struct regulator *clk_pwr; |
| struct regulator *pmp_pwr; |
| void __iomem *phy_base; |
| void __iomem *ahci_base; |
| }; |
| |
| static inline void msm_sata_delay_us(unsigned int delay) |
| { |
| /* sleep for max. 50us more to combine processor wakeups */ |
| usleep_range(delay, delay + 50); |
| } |
| |
| static int msm_sata_clk_get_prepare_enable_set_rate(struct device *dev, |
| const char *name, struct clk **out_clk, int rate) |
| { |
| int ret = 0; |
| struct clk *clk; |
| |
| clk = devm_clk_get(dev, name); |
| if (IS_ERR(clk)) { |
| ret = PTR_ERR(clk); |
| dev_err(dev, "failed to get clk: %s err = %d\n", name, ret); |
| goto out; |
| } |
| |
| if (rate >= 0) { |
| ret = clk_set_rate(clk, rate); |
| if (ret) { |
| dev_err(dev, "failed to set rate: %d clk: %s err = %d\n", |
| rate, name, ret); |
| goto out; |
| } |
| } |
| |
| ret = clk_prepare_enable(clk); |
| if (ret) |
| dev_err(dev, "failed to enable clk: %s err = %d\n", name, ret); |
| out: |
| if (!ret) |
| *out_clk = clk; |
| |
| return ret; |
| } |
| |
| static int msm_sata_clk_get_prepare_enable(struct device *dev, |
| const char *name, struct clk **out_clk) |
| { |
| return msm_sata_clk_get_prepare_enable_set_rate(dev, name, out_clk, -1); |
| } |
| |
| static void msm_sata_clk_put_unprepare_disable(struct clk **clk) |
| { |
| if (*clk) { |
| clk_disable_unprepare(*clk); |
| clk_put(*clk); |
| *clk = NULL; |
| } |
| } |
| |
| static int msm_sata_hard_reset(struct device *dev) |
| { |
| int ret; |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| ret = clk_reset(hba->iface_clk, CLK_RESET_ASSERT); |
| if (ret) { |
| dev_err(dev, "iface_clk assert failed %d\n", ret); |
| goto out; |
| } |
| |
| ret = clk_reset(hba->iface_clk, CLK_RESET_DEASSERT); |
| if (ret) { |
| dev_err(dev, "iface_clk de-assert failed %d\n", ret); |
| goto out; |
| } |
| out: |
| return ret; |
| } |
| |
| static int msm_sata_clk_init(struct device *dev) |
| { |
| int ret = 0; |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| /* Enable AHB clock for system fabric slave port connected to SATA */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "slave_iface_clk", &hba->slave_iface_clk); |
| if (ret) |
| goto out; |
| |
| /* Enable AHB clock for system fabric and SATA core interface */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "iface_clk", &hba->iface_clk); |
| if (ret) |
| goto put_dis_slave_iface_clk; |
| |
| /* Enable AXI clock for SATA AXI master and slave interfaces */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "bus_clk", &hba->bus_clk); |
| if (ret) |
| goto put_dis_iface_clk; |
| |
| /* Enable the source clock for pmalive, rxoob and phy ref clocks */ |
| ret = msm_sata_clk_get_prepare_enable_set_rate(dev, |
| "src_clk", &hba->src_clk, 100000000); |
| if (ret) |
| goto put_dis_bus_clk; |
| |
| /* |
| * Enable RX OOB detection clock. The clock rate is |
| * same as PHY reference clock (100MHz). |
| */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "core_rxoob_clk", &hba->rxoob_clk); |
| if (ret) |
| goto put_dis_src_clk; |
| |
| /* |
| * Enable power management always-on clock. The clock rate |
| * is same as PHY reference clock (100MHz). |
| */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "core_pmalive_clk", &hba->pmalive_clk); |
| if (ret) |
| goto put_dis_rxoob_clk; |
| |
| /* Enable PHY configuration AHB clock, fixed 64MHz clock */ |
| ret = msm_sata_clk_get_prepare_enable(dev, |
| "cfg_clk", &hba->cfg_clk); |
| if (ret) |
| goto put_dis_pmalive_clk; |
| |
| return ret; |
| |
| put_dis_pmalive_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk); |
| put_dis_rxoob_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk); |
| put_dis_src_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->src_clk); |
| put_dis_bus_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->bus_clk); |
| put_dis_iface_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->iface_clk); |
| put_dis_slave_iface_clk: |
| msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk); |
| out: |
| return ret; |
| } |
| |
| static void msm_sata_clk_deinit(struct device *dev) |
| { |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| msm_sata_clk_put_unprepare_disable(&hba->cfg_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->src_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->bus_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->iface_clk); |
| msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk); |
| } |
| |
| static int msm_sata_vreg_get_enable_set_vdd(struct device *dev, |
| const char *name, struct regulator **out_vreg, |
| int min_uV, int max_uV, int hpm_uA) |
| { |
| int ret = 0; |
| struct regulator *vreg; |
| |
| vreg = devm_regulator_get(dev, name); |
| if (IS_ERR(vreg)) { |
| ret = PTR_ERR(vreg); |
| dev_err(dev, "Regulator: %s get failed, err=%d\n", name, ret); |
| goto out; |
| } |
| |
| if (regulator_count_voltages(vreg) > 0) { |
| ret = regulator_set_voltage(vreg, min_uV, max_uV); |
| if (ret) { |
| dev_err(dev, "Regulator: %s set voltage failed, err=%d\n", |
| name, ret); |
| goto err; |
| } |
| |
| ret = regulator_set_optimum_mode(vreg, hpm_uA); |
| if (ret < 0) { |
| dev_err(dev, "Regulator: %s set optimum mode(uA_load=%d) failed, err=%d\n", |
| name, hpm_uA, ret); |
| goto err; |
| } else { |
| /* |
| * regulator_set_optimum_mode() can return non zero |
| * value even for success case. |
| */ |
| ret = 0; |
| } |
| } |
| |
| ret = regulator_enable(vreg); |
| if (ret) |
| dev_err(dev, "Regulator: %s enable failed, err=%d\n", |
| name, ret); |
| err: |
| if (!ret) |
| *out_vreg = vreg; |
| else |
| devm_regulator_put(vreg); |
| out: |
| return ret; |
| } |
| |
| static int msm_sata_vreg_put_disable(struct device *dev, |
| struct regulator *reg, const char *name, int max_uV) |
| { |
| int ret; |
| |
| if (!reg) |
| return 0; |
| |
| ret = regulator_disable(reg); |
| if (ret) { |
| dev_err(dev, "Regulator: %s disable failed err=%d\n", |
| name, ret); |
| goto err; |
| } |
| |
| if (regulator_count_voltages(reg) > 0) { |
| ret = regulator_set_voltage(reg, 0, max_uV); |
| if (ret < 0) { |
| dev_err(dev, "Regulator: %s set voltage to 0 failed, err=%d\n", |
| name, ret); |
| goto err; |
| } |
| |
| ret = regulator_set_optimum_mode(reg, 0); |
| if (ret < 0) { |
| dev_err(dev, "Regulator: %s set optimum mode(uA_load = 0) failed, err=%d\n", |
| name, ret); |
| goto err; |
| } else { |
| /* |
| * regulator_set_optimum_mode() can return non zero |
| * value even for success case. |
| */ |
| ret = 0; |
| } |
| } |
| |
| err: |
| devm_regulator_put(reg); |
| return ret; |
| } |
| |
| static int msm_sata_vreg_init(struct device *dev) |
| { |
| int ret = 0; |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| /* |
| * The SATA clock generator needs 3.3V supply and can consume |
| * max. 850mA during functional mode. |
| */ |
| ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_ext_3p3v", |
| &hba->clk_pwr, 3300000, 3300000, 850000); |
| if (ret) |
| goto out; |
| |
| /* Add 1ms regulator ramp-up delay */ |
| msm_sata_delay_us(1000); |
| |
| /* Read AHCI capability register to check if PMP is supported.*/ |
| if (readl_relaxed(hba->ahci_base + |
| AHCI_HOST_CAP) & AHCI_HOST_CAP_PMP) { |
| /* Power up port-multiplier */ |
| ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_pmp_pwr", |
| &hba->pmp_pwr, 1800000, 1800000, 200000); |
| if (ret) { |
| msm_sata_vreg_put_disable(dev, hba->clk_pwr, |
| "sata_ext_3p3v", 3300000); |
| goto out; |
| } |
| |
| /* Add 1ms regulator ramp-up delay */ |
| msm_sata_delay_us(1000); |
| } |
| |
| out: |
| return ret; |
| } |
| |
| static void msm_sata_vreg_deinit(struct device *dev) |
| { |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| msm_sata_vreg_put_disable(dev, hba->clk_pwr, |
| "sata_ext_3p3v", 3300000); |
| |
| if (hba->pmp_pwr) |
| msm_sata_vreg_put_disable(dev, hba->pmp_pwr, |
| "sata_pmp_pwr", 1800000); |
| } |
| |
| static void msm_sata_phy_deinit(struct device *dev) |
| { |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| /* Power down PHY */ |
| writel_relaxed(0xF8, hba->phy_base + SATA_PHY_POW_DWN_CTRL0); |
| writel_relaxed(0xFE, hba->phy_base + SATA_PHY_POW_DWN_CTRL1); |
| |
| /* Power down PLL block */ |
| writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_GLB_CFG); |
| mb(); |
| |
| devm_iounmap(dev, hba->phy_base); |
| } |
| |
| static int msm_sata_phy_init(struct device *dev) |
| { |
| int ret = 0; |
| u32 reg = 0; |
| struct platform_device *pdev = to_platform_device(dev); |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| struct resource *mem; |
| |
| mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); |
| if (!mem) { |
| dev_err(dev, "no mmio space\n"); |
| return -EINVAL; |
| } |
| |
| hba->phy_base = devm_ioremap(dev, mem->start, resource_size(mem)); |
| if (!hba->phy_base) { |
| dev_err(dev, "failed to allocate memory for SATA PHY\n"); |
| return -ENOMEM; |
| } |
| |
| /* SATA phy initialization */ |
| |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_SER_CTRL); |
| |
| writel_relaxed(0xB1, hba->phy_base + SATA_PHY_POW_DWN_CTRL0); |
| mb(); |
| msm_sata_delay_us(10); |
| |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0); |
| writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1); |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0); |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0); |
| writel_relaxed(0x02, hba->phy_base + SATA_PHY_TX_IMCAL2); |
| |
| /* Write UNIPHYPLL registers to configure PLL */ |
| writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_REFCLK_CFG); |
| writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_PWRGEN_CFG); |
| |
| writel_relaxed(0x0A, hba->phy_base + UNIPHY_PLL_CAL_CFG0); |
| writel_relaxed(0xF3, hba->phy_base + UNIPHY_PLL_CAL_CFG8); |
| writel_relaxed(0x01, hba->phy_base + UNIPHY_PLL_CAL_CFG9); |
| writel_relaxed(0xED, hba->phy_base + UNIPHY_PLL_CAL_CFG10); |
| writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_CAL_CFG11); |
| |
| writel_relaxed(0x36, hba->phy_base + UNIPHY_PLL_SDM_CFG0); |
| writel_relaxed(0x0D, hba->phy_base + UNIPHY_PLL_SDM_CFG1); |
| writel_relaxed(0xA3, hba->phy_base + UNIPHY_PLL_SDM_CFG2); |
| writel_relaxed(0xF0, hba->phy_base + UNIPHY_PLL_SDM_CFG3); |
| writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SDM_CFG4); |
| |
| writel_relaxed(0x19, hba->phy_base + UNIPHY_PLL_SSC_CFG0); |
| writel_relaxed(0xE1, hba->phy_base + UNIPHY_PLL_SSC_CFG1); |
| writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SSC_CFG2); |
| writel_relaxed(0x11, hba->phy_base + UNIPHY_PLL_SSC_CFG3); |
| |
| writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_LKDET_CFG0); |
| writel_relaxed(0xFF, hba->phy_base + UNIPHY_PLL_LKDET_CFG1); |
| |
| writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_GLB_CFG); |
| mb(); |
| msm_sata_delay_us(40); |
| |
| writel_relaxed(0x03, hba->phy_base + UNIPHY_PLL_GLB_CFG); |
| mb(); |
| msm_sata_delay_us(400); |
| |
| writel_relaxed(0x05, hba->phy_base + UNIPHY_PLL_LKDET_CFG2); |
| mb(); |
| |
| /* poll for ready status, timeout after 1 sec */ |
| ret = readl_poll_timeout(hba->phy_base + UNIPHY_PLL_STATUS, reg, |
| (reg & 1 << 0), 100, 1000000); |
| if (ret) { |
| dev_err(dev, "poll timeout UNIPHY_PLL_STATUS\n"); |
| goto out; |
| } |
| |
| ret = readl_poll_timeout(hba->phy_base + SATA_PHY_TX_IMCAL_STAT, reg, |
| (reg & 1 << 0), 100, 1000000); |
| if (ret) { |
| dev_err(dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); |
| goto out; |
| } |
| |
| ret = readl_poll_timeout(hba->phy_base + SATA_PHY_RX_IMCAL_STAT, reg, |
| (reg & 1 << 0), 100, 1000000); |
| if (ret) { |
| dev_err(dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); |
| goto out; |
| } |
| |
| /* SATA phy calibrated succesfully, power up to functional mode */ |
| writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1); |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0); |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0); |
| |
| writel_relaxed(0x00, hba->phy_base + SATA_PHY_POW_DWN_CTRL1); |
| writel_relaxed(0x59, hba->phy_base + SATA_PHY_CDR_CTRL0); |
| writel_relaxed(0x04, hba->phy_base + SATA_PHY_CDR_CTRL1); |
| writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL2); |
| writel_relaxed(0x00, hba->phy_base + SATA_PHY_PI_CTRL0); |
| writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL3); |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0); |
| |
| writel_relaxed(0x11, hba->phy_base + SATA_PHY_TX_DATA_CTRL); |
| writel_relaxed(0x43, hba->phy_base + SATA_PHY_ALIGNP); |
| writel_relaxed(0x04, hba->phy_base + SATA_PHY_OOB_TERM); |
| |
| writel_relaxed(0x01, hba->phy_base + SATA_PHY_EQUAL); |
| writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL0); |
| writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL1); |
| mb(); |
| |
| dev_dbg(dev, "SATA PHY powered up in functional mode\n"); |
| |
| out: |
| /* power down PHY in case of failure */ |
| if (ret) |
| msm_sata_phy_deinit(dev); |
| |
| return ret; |
| } |
| |
| int msm_sata_init(struct device *ahci_dev, void __iomem *mmio) |
| { |
| int ret; |
| struct device *dev = ahci_dev->parent; |
| struct msm_sata_hba *hba = dev_get_drvdata(dev); |
| |
| /* Save ahci mmio to access vendor specific registers */ |
| hba->ahci_base = mmio; |
| |
| ret = msm_sata_clk_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA clk init failed with err=%d\n", ret); |
| goto out; |
| } |
| |
| ret = msm_sata_vreg_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA vreg init failed with err=%d\n", ret); |
| msm_sata_clk_deinit(dev); |
| goto out; |
| } |
| |
| ret = msm_sata_phy_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA PHY init failed with err=%d\n", ret); |
| msm_sata_vreg_deinit(dev); |
| msm_sata_clk_deinit(dev); |
| goto out; |
| } |
| |
| out: |
| return ret; |
| } |
| |
| void msm_sata_deinit(struct device *ahci_dev) |
| { |
| struct device *dev = ahci_dev->parent; |
| |
| msm_sata_phy_deinit(dev); |
| msm_sata_vreg_deinit(dev); |
| msm_sata_clk_deinit(dev); |
| } |
| |
| static int msm_sata_suspend(struct device *ahci_dev) |
| { |
| msm_sata_deinit(ahci_dev); |
| |
| return 0; |
| } |
| |
| static int msm_sata_resume(struct device *ahci_dev) |
| { |
| int ret; |
| struct device *dev = ahci_dev->parent; |
| |
| ret = msm_sata_clk_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA clk init failed with err=%d\n", ret); |
| /* |
| * If clock initialization failed, that means ahci driver |
| * cannot access any register going further. Since there is |
| * no check within ahci driver to check for clock failures, |
| * panic here instead of making an unclocked register access. |
| */ |
| BUG(); |
| } |
| |
| /* Issue asynchronous reset to reset PHY */ |
| ret = msm_sata_hard_reset(dev); |
| if (ret) |
| goto out; |
| |
| ret = msm_sata_vreg_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA vreg init failed with err=%d\n", ret); |
| /* Do not turn off clks, AHCI driver might do register access */ |
| goto out; |
| } |
| |
| ret = msm_sata_phy_init(dev); |
| if (ret) { |
| dev_err(dev, "SATA PHY init failed with err=%d\n", ret); |
| /* Do not turn off clks, AHCI driver might do register access */ |
| msm_sata_vreg_deinit(dev); |
| goto out; |
| } |
| out: |
| return ret; |
| } |
| |
| static struct ahci_platform_data msm_ahci_pdata = { |
| .init = msm_sata_init, |
| .exit = msm_sata_deinit, |
| .suspend = msm_sata_suspend, |
| .resume = msm_sata_resume, |
| }; |
| |
| static int __devinit msm_sata_probe(struct platform_device *pdev) |
| { |
| struct platform_device *ahci; |
| struct msm_sata_hba *hba; |
| int ret = 0; |
| |
| hba = devm_kzalloc(&pdev->dev, sizeof(struct msm_sata_hba), GFP_KERNEL); |
| if (!hba) { |
| dev_err(&pdev->dev, "no memory\n"); |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| platform_set_drvdata(pdev, hba); |
| |
| ahci = platform_device_alloc("ahci", pdev->id); |
| if (!ahci) { |
| dev_err(&pdev->dev, "couldn't allocate ahci device\n"); |
| ret = -ENOMEM; |
| goto err_free; |
| } |
| |
| dma_set_coherent_mask(&ahci->dev, pdev->dev.coherent_dma_mask); |
| |
| ahci->dev.parent = &pdev->dev; |
| ahci->dev.dma_mask = pdev->dev.dma_mask; |
| ahci->dev.dma_parms = pdev->dev.dma_parms; |
| hba->ahci_pdev = ahci; |
| |
| ret = platform_device_add_resources(ahci, pdev->resource, |
| pdev->num_resources); |
| if (ret) { |
| dev_err(&pdev->dev, "couldn't add resources to ahci device\n"); |
| goto err_put_device; |
| } |
| |
| ahci->dev.platform_data = &msm_ahci_pdata; |
| ret = platform_device_add(ahci); |
| if (ret) { |
| dev_err(&pdev->dev, "failed to register ahci device\n"); |
| goto err_put_device; |
| } |
| |
| return 0; |
| |
| err_put_device: |
| platform_device_put(ahci); |
| err_free: |
| devm_kfree(&pdev->dev, hba); |
| err: |
| return ret; |
| } |
| |
| static int __devexit msm_sata_remove(struct platform_device *pdev) |
| { |
| struct msm_sata_hba *hba = platform_get_drvdata(pdev); |
| |
| platform_device_unregister(hba->ahci_pdev); |
| |
| return 0; |
| } |
| |
| static struct platform_driver msm_sata_driver = { |
| .probe = msm_sata_probe, |
| .remove = __devexit_p(msm_sata_remove), |
| .driver = { |
| .name = "msm_sata", |
| }, |
| }; |
| |
| module_platform_driver(msm_sata_driver); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DESCRIPTION("AHCI platform MSM Glue Layer"); |