blob: fe9ee7346fe73e0f0a3a51d5992f864ce054e2d9 [file] [log] [blame]
/*
* 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 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 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 = msm_sata_remove,
.driver = {
.name = "msm_sata",
},
};
module_platform_driver(msm_sata_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("AHCI platform MSM Glue Layer");