ata: ahci: Add SATA support for Qualcomm MSM chipsets

Add a glue driver for AHCI platform driver to support SATA
controller and PHY on Qualcomm's MSM platforms. The SATA
controller on MSM adheres to AHCI 1.3 specification and SATA
PHY is based on SATA 3.0 specification.

The glue driver acts as a device driver for msm_sata device and
registers AHCI device in its probe, AHCI platform driver then
acts as a device driver for created AHCI device. All the necessary
msm platform specific initialization is handled by msm_sata driver
while keeping ahci_platform generic enough.

Following are done during controller initialization:
 - SATA clocks initialization.
 - SATA regulator initialization.
 - SATA PHY calibration.

Change-Id: I3e36289992c340bc16fcb2a6c52468cc95679fe8
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
This commit is contained in:
Sujit Reddy Thumma 2012-12-31 15:37:16 +05:30 committed by Stephen Boyd
parent 93f6b2ee94
commit bd314dc52d
3 changed files with 790 additions and 0 deletions

View file

@ -97,6 +97,17 @@ config SATA_AHCI_PLATFORM
If unsure, say N.
config SATA_AHCI_MSM
tristate "Qualcomm MSM AHCI SATA support"
depends on ARCH_MSM
select SATA_AHCI_PLATFORM
help
This option enables support for AHCI SATA controller
integrated into Qualcomm MSM chipsets. For more
information please refer to http://www.qualcomm.com/chipsets.
If unsure, say N.
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC

View file

@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o

778
drivers/ata/ahci_msm.c Normal file
View file

@ -0,0 +1,778 @@
/*
* 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");