blob: 955f1cc3a8c120192947afd190db07be9c4d67fe [file] [log] [blame]
/*
* Copyright (C) Mindspeed Technologies, Inc. 2011. All rights reserved.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @file serdes.c
* @brief this C file will contain all required functions to program
* Snowbush SerDes PHY interface.
* @date 10/02/2011
*/
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/types.h>
#include <asm/io.h>
#include <mach/comcerto-2000.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/sizes.h>
#include <mach/serdes-c2000.h>
#define MAX_LANE_OK_WAIT_JIFFIES (200 * HZ) / 1000 /* 200ms */
#define MAX_CMU_OK_WAIT_JIFFIES (2000 * HZ) / 1000 /* 2 Seconds */
/**
* This function Wait for the 'Lane OK' to be signaled by the
* Snowbush Serdes PHY.
* @param sbphy_num SerDes PHY intefrace number.
*/
static int wait_lane_ok(u32 sbphy_num)
{
u32 rd_data = 0, masked_data = 0;
u32 lane_ok_dtctd_mask = 0x00001000;
unsigned long deadline = jiffies + MAX_LANE_OK_WAIT_JIFFIES;
/* Keep looping until you see the lane_ok_o of Serdes */
do
{
rd_data = readl(COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_STS_REG_OFST));
/* Mask lane_ok Status */
masked_data = rd_data & lane_ok_dtctd_mask;
if(masked_data == lane_ok_dtctd_mask) {
/* Lane OK Detected on Serdes Port */
printk(KERN_INFO "Serdes%d: Lane OK Passed\n",sbphy_num);
return 1;
}
cond_resched();
} while (!time_after_eq(jiffies, deadline));
printk(KERN_INFO "Serdes%d: Lane OK Failed\n",sbphy_num);
return 0;
}
/**
* This function wait for the 'CMU OK' to be signaled by the
* Snowbush Serdes PHY.
* @param sbphy_num SerDes PHY intefrace number.
*/
static int wait_cmu_ok(u32 sbphy_num)
{
u32 rd_data = 0, masked_data = 0;
u32 cmu_ok_dtctd_mask = 0x00004000;
int CMU_Offset;
unsigned long deadline = jiffies + MAX_CMU_OK_WAIT_JIFFIES;
CMU_Offset = COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_STS_REG_OFST );
/* Keep looping until you see the cmu_ok_o of Serdes */
do
{
rd_data = readl(CMU_Offset);
/* Mask cmu_ok Status */
masked_data = rd_data & cmu_ok_dtctd_mask;
if(masked_data == cmu_ok_dtctd_mask) {
/* CMU OK Detected on Serdes Port */
//printk(KERN_INFO "Serdes%d: CMU OK Passed\n",sbphy_num);
return 1;
}
cond_resched();
} while (!time_after_eq(jiffies, deadline));
printk(KERN_INFO "Serdes%d: CMU OK Failed\n",sbphy_num);
return 0;
}
/**
* This function wait for the specified configured Snowbush PHY
* (Serdes) to issue it's CMU-OK, and it's Lane to become Ready
* after releasing the CMU & Lane resets.
* @param sbphy_num SerDes PHY intefrace number.
*/
static int wait_sb_cmu_lane_rdy(u32 sbphy_num, u32 type)
{
u32 sd_ctl2_reg_offset;
u32 cmu_rst_mask = 0x00010000;
u32 lane_rst_mask = 0x00000040;
u32 tmp = 0;
sd_ctl2_reg_offset = COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_CTRL2_REG_OFST );
/* Releasing the CMU Reset */
tmp = readl(sd_ctl2_reg_offset);
tmp = tmp & (~cmu_rst_mask);
tmp = tmp | cmu_rst_mask;
writel(tmp, sd_ctl2_reg_offset );
/* Waiting for CMU OK */
if( !wait_cmu_ok(sbphy_num) )
return -1;
if ( type == SD_DEV_TYPE_PCIE )
writel(0xC3, COMCERTO_SERDES_REG(sbphy_num, (SD_COMMON_LANE << 2)));
else
writel(0x03, COMCERTO_SERDES_REG(sbphy_num, (SD_COMMON_LANE << 2)));
/* Releasing the Lane Reset */
tmp = readl(sd_ctl2_reg_offset);
tmp = tmp & (~lane_rst_mask);
tmp = tmp | lane_rst_mask;
writel(tmp, sd_ctl2_reg_offset);
/* Waiting for the Lane Ready */
if (type != SD_DEV_TYPE_PCIE) {
if( !wait_lane_ok(sbphy_num) )
return -1;
}
return 0;
}
/**
* This function initialize the Snowbush PHY (Serdes) for operation
* with the one of the PCIE,SATA or SGMII IP blocks, and then waiting
* until it issue it's CMU-OK, and it's Lane to become Ready after
* releasing the CMU & Lane Resets.
* @param phy_num SerDes PHY intefrace number.
* @param *regs Register file (Array of registers and coresponding
* values to be programmed).
* @param size Number of registers to be programmed.
*/
int serdes_phy_init(int phy_num, struct serdes_regs_s *regs, int size, int type)
{
int ii;
/* Initilize serdes phy registers */
for( ii = 0; ii < size; ii++ )
writel(regs[ii].val, COMCERTO_SERDES_REG(phy_num, regs[ii].ofst));
/* Wait for the initialization of Serdes-1 Port/Lane to become Ready */
return wait_sb_cmu_lane_rdy(phy_num, type);
}
EXPORT_SYMBOL(serdes_phy_init);