2014-07-16 20:10:08 +04:00
/*
* Copyright ( c ) 2014 , 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 .
*/
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/time.h>
# include <linux/delay.h>
# include <linux/clk.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/phy/phy.h>
struct qcom_ipq806x_sata_phy {
void __iomem * mmio ;
struct clk * cfg_clk ;
struct device * dev ;
} ;
# define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
# define SATA_PHY_P0_PARAM0 0x200
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
# define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
# define SATA_PHY_P0_PARAM1 0x204
# define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
# define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
# define SATA_PHY_P0_PARAM2 0x208
# define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
# define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
# define SATA_PHY_P0_PARAM3 0x20C
# define SATA_PHY_SSC_EN 0x8
# define SATA_PHY_P0_PARAM4 0x210
# define SATA_PHY_REF_SSP_EN 0x2
# define SATA_PHY_RESET 0x1
static int qcom_ipq806x_sata_phy_init ( struct phy * generic_phy )
{
struct qcom_ipq806x_sata_phy * phy = phy_get_drvdata ( generic_phy ) ;
u32 reg ;
/* Setting SSC_EN to 1 */
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM3 ) ;
reg = reg | SATA_PHY_SSC_EN ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM3 ) ;
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM0 ) &
~ ( SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK ) ;
reg | = SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3 ( 0xf ) ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM0 ) ;
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM1 ) &
~ ( SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK ) ;
reg | = SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3 ( 0x55 ) |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2 ( 0x55 ) |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1 ( 0x55 ) ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM1 ) ;
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM2 ) &
~ SATA_PHY_P0_PARAM2_RX_EQ_MASK ;
reg | = SATA_PHY_P0_PARAM2_RX_EQ ( 0x3 ) ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM2 ) ;
/* Setting PHY_RESET to 1 */
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
reg = reg | SATA_PHY_RESET ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
/* Setting REF_SSP_EN to 1 */
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
/* make sure all changes complete before we let the PHY out of reset */
mb ( ) ;
/* sleep for max. 50us more to combine processor wakeups */
usleep_range ( 20 , 20 + 50 ) ;
/* Clearing PHY_RESET to 0 */
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
reg = reg & ~ SATA_PHY_RESET ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
return 0 ;
}
static int qcom_ipq806x_sata_phy_exit ( struct phy * generic_phy )
{
struct qcom_ipq806x_sata_phy * phy = phy_get_drvdata ( generic_phy ) ;
u32 reg ;
/* Setting PHY_RESET to 1 */
reg = readl_relaxed ( phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
reg = reg | SATA_PHY_RESET ;
writel_relaxed ( reg , phy - > mmio + SATA_PHY_P0_PARAM4 ) ;
return 0 ;
}
2015-07-15 10:33:51 +03:00
static const struct phy_ops qcom_ipq806x_sata_phy_ops = {
2014-07-16 20:10:08 +04:00
. init = qcom_ipq806x_sata_phy_init ,
. exit = qcom_ipq806x_sata_phy_exit ,
. owner = THIS_MODULE ,
} ;
static int qcom_ipq806x_sata_phy_probe ( struct platform_device * pdev )
{
struct qcom_ipq806x_sata_phy * phy ;
struct device * dev = & pdev - > dev ;
struct resource * res ;
struct phy_provider * phy_provider ;
struct phy * generic_phy ;
int ret ;
phy = devm_kzalloc ( dev , sizeof ( * phy ) , GFP_KERNEL ) ;
if ( ! phy )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
phy - > mmio = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( phy - > mmio ) )
return PTR_ERR ( phy - > mmio ) ;
2014-11-19 18:28:21 +03:00
generic_phy = devm_phy_create ( dev , NULL , & qcom_ipq806x_sata_phy_ops ) ;
2014-07-16 20:10:08 +04:00
if ( IS_ERR ( generic_phy ) ) {
dev_err ( dev , " %s: failed to create phy \n " , __func__ ) ;
return PTR_ERR ( generic_phy ) ;
}
phy - > dev = dev ;
phy_set_drvdata ( generic_phy , phy ) ;
platform_set_drvdata ( pdev , phy ) ;
phy - > cfg_clk = devm_clk_get ( dev , " cfg " ) ;
if ( IS_ERR ( phy - > cfg_clk ) ) {
dev_err ( dev , " Failed to get sata cfg clock \n " ) ;
return PTR_ERR ( phy - > cfg_clk ) ;
}
ret = clk_prepare_enable ( phy - > cfg_clk ) ;
if ( ret )
return ret ;
phy_provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
if ( IS_ERR ( phy_provider ) ) {
clk_disable_unprepare ( phy - > cfg_clk ) ;
dev_err ( dev , " %s: failed to register phy \n " , __func__ ) ;
return PTR_ERR ( phy_provider ) ;
}
return 0 ;
}
static int qcom_ipq806x_sata_phy_remove ( struct platform_device * pdev )
{
struct qcom_ipq806x_sata_phy * phy = platform_get_drvdata ( pdev ) ;
clk_disable_unprepare ( phy - > cfg_clk ) ;
return 0 ;
}
static const struct of_device_id qcom_ipq806x_sata_phy_of_match [ ] = {
{ . compatible = " qcom,ipq806x-sata-phy " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , qcom_ipq806x_sata_phy_of_match ) ;
static struct platform_driver qcom_ipq806x_sata_phy_driver = {
. probe = qcom_ipq806x_sata_phy_probe ,
. remove = qcom_ipq806x_sata_phy_remove ,
. driver = {
. name = " qcom-ipq806x-sata-phy " ,
. of_match_table = qcom_ipq806x_sata_phy_of_match ,
}
} ;
module_platform_driver ( qcom_ipq806x_sata_phy_driver ) ;
MODULE_DESCRIPTION ( " QCOM IPQ806x SATA PHY driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;