satip-axe/kernel/drivers/stm/miphy.c

663 lines
18 KiB
C

/*
* STMicroelectronics MiPHY driver
*
* Copyright (C) 2009 STMicroelectronics Limited
* Author: Pawel Moll <pawel.moll@st.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <asm/processor.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stm/platform.h>
#include <linux/stm/sysconf.h>
#include <linux/stm/miphy.h>
#include "miphy.h"
#define MIPHY_RESET 0x00
#define RST_RX (1<<4)
#define MIPHY_STATUS 0x01
#define MIPHY_CONTROL 0x02
#define DIS_LINK_RST (1<<4)
#define MIPHY_INT_STATUS 0x04
#define BITUNLOCK_INT (1<<1)
#define SYMBUNLOCK_INT (1<<2)
#define FIFOOVERLAP_INT (1<<3)
#define MIPHY_BOUNDARY_1 0x10
#define POWERSEL_SEL (1<<2)
#define SPDSEL_SEL (1<<0)
#define MIPHY_BOUNDARY_3 0x12
#define RX_LSPD (1<<5)
#define MIPHY_COMPENS_CONTROL_1 0x40
#define MIPHY_IDLL_TEST 0x72
#define START_CLK_HF (1<<6)
#define STOP_CLK_HF (1<<7)
#define MIPHY_DES_BITLOCK_CFG 0x85
#define CLEAR_BIT_UNLOCK_FLAG (1<<0)
#define UPDATE_TRANS_DENSITY (1<<1)
#define MIPHY_DES_BITLOCK 0x86
#define MIPHY_DES_BITLOCK_STATUS 0x88
#define BIT_LOCK (1<<0)
#define BIT_LOCK_FAILED (1<<1)
#define BIT_UNLOCK (1<<2)
#define TRANS_DENSITY_UPDATED (1<<3)
#define MIPHY_VERSION 0xfb
#define MIPHY_REVISION 0xfc
static struct class *miphy_class;
static DEFINE_MUTEX(miphy_list_mutex);
static LIST_HEAD(miphy_list);
struct stm_miphy {
struct stm_miphy_device *dev;
int port;
enum miphy_mode mode;
struct list_head node;
struct device *owner;
struct device *device;
};
/* Start functions for miphy port
* Ideally all the start functions should be identical, however
* they tend to be different in physical arrangement.
* The physicall (IP) arrangement lead to different start functions.
*/
/*
* MiPhy Port 0 Start function for SATA
*/
static void tap_miphy_start_port0(const struct miphy_if_ops *ops)
{
int timeout;
void (*reg_write)(int port, u8 addr, u8 data) = ops->reg_write;
u8 (*reg_read)(int port, u8 addr) = ops->reg_read;
/* TODO: Get rid of this */
if (cpu_data->type == CPU_STX7108) {
/*Force SATA port 1 in Slumber Mode */
reg_write(1, 0x11, 0x8);
/*Force Power Mode selection from MiPHY soft register 0x11 */
reg_write(1, 0x10, 0x4);
}
/* Force Macro1 in reset and request PLL calibration reset */
/* Force PLL calibration reset, PLL reset and assert
* Deserializer Reset */
reg_write(0, 0x00, 0x16);
reg_write(0, 0x11, 0x0);
/* Force macro1 to use rx_lspd, tx_lspd (by default rx_lspd
* and tx_lspd set for Gen1) */
reg_write(0, 0x10, 0x1);
/* Force Recovered clock on first I-DLL phase & all
* Deserializers in HP mode */
/* Force Rx_Clock on first I-DLL phase on macro1 */
reg_write(0, 0x72, 0x40);
/* Force Des in HP mode on macro1 */
reg_write(0, 0x12, 0x00);
/* Wait for HFC_READY = 0 */
timeout = 50; /* Jeeeezzzzz.... */
while (timeout-- && (reg_read(0, 0x01) & 0x3))
udelay(2000);
if (timeout < 0)
pr_err("%s(): HFC_READY timeout!\n", __func__);
/* Restart properly Process compensation & PLL Calibration */
/* Set properly comsr definition for 30 MHz ref clock */
reg_write(0, 0x41, 0x1E);
/* comsr compensation reference */
reg_write(0, 0x42, 0x28);
/* Set properly comsr definition for 30 MHz ref clock */
reg_write(0, 0x41, 0x1E);
/* comsr cal gives more suitable results in fast PVT for comsr
used by TX buffer to build slopes making TX rise/fall fall
times. */
reg_write(0, 0x42, 0x33);
/* Force VCO current to value defined by address 0x5A */
reg_write(0, 0x51, 0x2);
/* Force VCO current to value defined by address 0x5A */
reg_write(0, 0x5A, 0xF);
/* Enable auto load compensation for pll_i_bias */
reg_write(0, 0x47, 0x2A);
/* Force restart compensation and enable auto load for
* Comzc_Tx, Comzc_Rx & Comsr on macro1 */
reg_write(0, 0x40, 0x13);
/* Wait for comzc & comsr done */
while ((reg_read(0, 0x40) & 0xC) != 0xC)
cpu_relax();
/* Recommended settings for swing & slew rate FOR SATA GEN 1
* from CPG */
reg_write(0, 0x20, 0x00);
/* (Tx Swing target 500-550mV peak-to-peak diff) */
reg_write(0, 0x21, 0x2);
/* (Tx Slew target120-140 ps rising/falling time) */
reg_write(0, 0x22, 0x4);
/* Force Macro1 in partial mode & release pll cal reset */
reg_write(0, 0x00, 0x10);
udelay(10);
#if 0
/* SSC Settings. SSC will be enabled through Link */
reg_write(0, 0x53, 0x00); /* pll_offset */
reg_write(0, 0x54, 0x00); /* pll_offset */
reg_write(0, 0x55, 0x00); /* pll_offset */
reg_write(0, 0x56, 0x04); /* SSC Ampl=0.48% */
reg_write(0, 0x57, 0x11); /* SSC Ampl=0.48% */
reg_write(0, 0x58, 0x00); /* SSC Freq=31KHz */
reg_write(0, 0x59, 0xF1); /* SSC Freq=31KHz */
/*SSC Settings complete*/
#endif
reg_write(0, 0x50, 0x8D);
reg_write(0, 0x50, 0x8D);
/* Wait for phy_ready */
/* When phy is in ready state ( register 0x01 of macro1 to 0x13) */
while ((reg_read(0, 0x01) & 0x03) != 0x03)
cpu_relax();
/* Enable macro1 to use rx_lspd & tx_lspd from link interface */
reg_write(0, 0x10, 0x00);
/* Release Rx_Clock on first I-DLL phase on macro1 */
reg_write(0, 0x72, 0x00);
/* Deassert deserializer reset */
reg_write(0, 0x00, 0x00);
/* des_bit_lock_en is set */
reg_write(0, 0x02, 0x08);
/* bit lock detection strength */
reg_write(0, 0x86, 0x61);
}
/*
* MiPhy Port 1 Start function for SATA
*/
static void tap_miphy_start_port1(const struct miphy_if_ops *ops)
{
int timeout;
void (*reg_write)(int port, u8 addr, u8 data) = ops->reg_write;
u8 (*reg_read)(int port, u8 addr) = ops->reg_read;
/* Force PLL calibration reset, PLL reset and assert Deserializer
* Reset */
reg_write(1, 0x00, 0x2);
/* Force restart compensation and enable auto load for Comzc_Tx,
* Comzc_Rx & Comsr on macro2 */
reg_write(1, 0x40, 0x13);
/* Force PLL reset */
reg_write(0, 0x00, 0x2);
/* Set properly comsr definition for 30 MHz ref clock */
reg_write(0, 0x41, 0x1E);
/* to get more optimum result on comsr calibration giving faster
* rise/fall time in SATA spec Gen1 useful for some corner case.*/
reg_write(0, 0x42, 0x33);
/* Force restart compensation and enable auto load for Comzc_Tx,
* Comzc_Rx & Comsr on macro1 */
reg_write(0, 0x40, 0x13);
/*Wait for HFC_READY = 0*/
timeout = 50; /* Jeeeezzzzz.... */
while (timeout-- && (reg_read(0, 0x01) & 0x3))
udelay(2000);
if (timeout < 0)
pr_err("%s(): HFC_READY timeout!\n", __func__);
reg_write(1, 0x11, 0x0);
/* Force macro2 to use rx_lspd, tx_lspd (by default rx_lspd and
* tx_lspd set for Gen1) */
reg_write(1, 0x10, 0x1);
/* Force Rx_Clock on first I-DLL phase on macro2*/
reg_write(1, 0x72, 0x40);
/* Force Des in HP mode on macro2 */
reg_write(1, 0x12, 0x00);
while ((reg_read(1, 0x40) & 0xC) != 0xC)
cpu_relax();
/*RECOMMENDED SETTINGS for Swing & slew rate FOR SATA GEN 1 from CPG*/
reg_write(1, 0x20, 0x00);
/*(Tx Swing target 500-550mV peak-to-peak diff) */
reg_write(1, 0x21, 0x2);
/*(Tx Slew target120-140 ps rising/falling time) */
reg_write(1, 0x22, 0x4);
/*Force Macr21 in partial mode & release pll cal reset */
reg_write(1, 0x00, 0x10);
udelay(10);
/* Release PLL reset */
reg_write(0, 0x00, 0x0);
/* Wait for phy_ready */
/* When phy is in ready state ( register 0x01 of macro1 to 0x13)*/
while ((reg_read(1, 0x01) & 0x03) != 0x03)
cpu_relax();
/* Enable macro1 to use rx_lspd & tx_lspd from link interface */
reg_write(1, 0x10, 0x00);
/* Release Rx_Clock on first I-DLL phase on macro1 */
reg_write(1, 0x72, 0x00);
/* Deassert deserializer reset */
reg_write(1, 0x00, 0x00);
/*des_bit_lock_en is set */
reg_write(1, 0x02, 0x08);
/*bit lock detection strength */
reg_write(1, 0x86, 0x61);
}
static int tap_miphy_sata_start(int port, struct stm_miphy_device *miphy_dev)
{
int rval = 0;
switch (port) {
case 0:
tap_miphy_start_port0(miphy_dev->ops);
break;
case 1:
tap_miphy_start_port1(miphy_dev->ops);
break;
default:
rval = -EINVAL;
}
return rval;
}
static int tap_miphy_pcie_start(int port, struct stm_miphy_device *miphy_dev)
{
/* The tap interface versions do not support PCIE */
return -1;
}
/*
* MiPhy Port 0 & 1 Start function for SATA
* only for 7108 CUT2
*/
static int mp_miphy_sata_start(int port, struct stm_miphy_device *miphy_dev)
{
unsigned int regvalue;
int timeout;
void (*reg_write)(int port, u8 addr, u8 data);
u8 (*reg_read)(int port, u8 addr);
if (port < 0 || port > 1)
return -EINVAL;
reg_write = miphy_dev->ops->reg_write;
reg_read = miphy_dev->ops->reg_read;
/* Force PLL calibration reset, PLL reset
* and assert Deserializer Reset */
reg_write(port, 0x00, 0x16);
reg_write(port, 0x11, 0x0);
/* Force macro1 to use rx_lspd, tx_lspd
* (by default rx_lspd and tx_lspd set for Gen1) */
reg_write(port, 0x10, 0x1);
/* Force Rx_Clock on first I-DLL phase on macro1 */
reg_write(port, 0x72, 0x40);
/* Force Des in HP mode on macro1 */
reg_write(port, 0x12, 0x00);
/*Wait for HFC_READY = 0*/
timeout = 50;
while (timeout-- && (reg_read(port, 0x01) & 0x3))
udelay(2000);
if (timeout < 0)
pr_err("%s(): HFC_READY timeout!\n", __func__);
/*Set properly comsr definition for 30 MHz ref clock */
reg_write(port, 0x41, 0x1E);
/*Set properly comsr definition for 30 MHz ref clock */
reg_write(port, 0x42, 0x33);
/* Force VCO current to value defined by address 0x5A
* and disable PCIe100Mref bit */
reg_write(port, 0x51, 0x2);
/* Enable auto load compensation for pll_i_bias */
reg_write(port, 0x47, 0x2A);
/* Force restart compensation and enable auto load for
* Comzc_Tx, Comzc_Rx & Comsr on macro1 */
reg_write(port, 0x40, 0x13);
while ((reg_read(port, 0x40) & 0xC) != 0xC)
cpu_relax();
/* STOS_SemaphoreWait(MiPHY_Int); Wait for Compensation
* Completion Interrupt : Not using MiPHY Interrupt for now */
/* Recommended Settings for Swing & slew rate FOR SATA GEN 1 from CCI
* conf gen sel = 00b to program Gen1 banked registers &
* VDDT filter ON */
reg_write(port, 0x20, 0x10);
/*(Tx Swing target 500-550mV peak-to-peak diff) */
reg_write(port, 0x21, 0x3);
/*(Tx Slew target120-140 ps rising/falling time) */
reg_write(port, 0x22, 0x4);
/*Force Macro1 in partial mode & release pll cal reset */
reg_write(port, 0x00, 0x10);
udelay(100);
/* SSC Settings. SSC will be enabled through Link */
/* pll_offset */
reg_write(port, 0x53, 0x00);
/* pll_offset */
reg_write(port, 0x54, 0x00);
/* pll_offset */
reg_write(port, 0x55, 0x00);
/* SSC Ampl.=0.4% */
reg_write(port, 0x56, 0x03);
/* SSC Ampl.=0.4% */
reg_write(port, 0x57, 0x63);
/* SSC Freq=31KHz */
reg_write(port, 0x58, 0x00);
/* SSC Freq=31KHz */
reg_write(port, 0x59, 0xF1);
/*SSC Settings complete*/
reg_write(port, 0x50, 0x8D);
/*MIPHY PLL ratio */
reg_read(port, 0x52);
/* Wait for phy_ready */
/* When phy is in ready state ( register 0x01 reads 0x13)*/
regvalue = reg_read(port, 0x01);
timeout = 50;
while (timeout-- && ((regvalue & 0x03) != 0x03)) {
regvalue = reg_read(port, 0x01);
udelay(2000);
}
if (timeout < 0)
pr_err("%s(): HFC_READY timeout!\n", __func__);
if ((regvalue & 0x03) == 0x03) {
/* Enable macro1 to use rx_lspd &
* tx_lspd from link interface */
reg_write(port, 0x10, 0x00);
/* Release Rx_Clock on first I-DLL phase on macro1 */
reg_write(port, 0x72, 0x00);
/* Assert deserializer reset */
reg_write(port, 0x00, 0x10);
/* des_bit_lock_en is set */
reg_write(port, 0x02, 0x08);
/* bit lock detection strength */
reg_write(port, 0x86, 0x61);
/* Deassert deserializer reset */
reg_write(port, 0x00, 0x00);
}
return 0;
}
static int mp_miphy_pcie_start(int port, struct stm_miphy_device *miphy_dev)
{
/* The hardware sets everything up for us, so far nothing to do here */
return 0;
}
/****************************************************************************
* MiPHY Generic functions available for other drivers
*/
static void stm_miphy_write(struct stm_miphy *miphy, u8 addr, u8 data)
{
struct stm_miphy_device *dev = miphy->dev;
int port = miphy->port;
dev->ops->reg_write(port, addr, data);
}
static u8 stm_miphy_read(struct stm_miphy *miphy, u8 addr)
{
struct stm_miphy_device *dev = miphy->dev;
int port = miphy->port;
return dev->ops->reg_read(port, addr);
}
#ifdef DEBUG
/* Pretty-ish print of Miphy registers, helpful for debugging */
void stm_miphy_dump_registers(struct stm_miphy *miphy)
{
printk(KERN_INFO "MIPHY_RESET (0x0): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_RESET));
printk(KERN_INFO "MIPHY_STATUS (0x1): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_STATUS));
printk(KERN_INFO "MIPHY_CONTROL (0x1): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_CONTROL));
printk(KERN_INFO "MIPHY_INT_STATUS (0x4): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_INT_STATUS));
printk(KERN_INFO "MIPHY_BOUNDARY_1 (0x10): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_BOUNDARY_1));
printk(KERN_INFO "MIPHY_BOUNDARY_3 (0x12): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_BOUNDARY_3));
printk(KERN_INFO "MIPHY_COMPENS_CONTROL_1 (0x40): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_COMPENS_CONTROL_1));
printk(KERN_INFO "MIPHY_IDLL_TEST (0x72): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_IDLL_TEST));
printk(KERN_INFO "MIPHY_DES_BITLOCK_CFG (0x85): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_DES_BITLOCK_CFG));
printk(KERN_INFO "MIPHY_DES_BITLOCK (0x86): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_DES_BITLOCK));
printk(KERN_INFO "MIPHY_DES_BITLOCK_STATUS (0x88): 0x%.2x\n",
stm_miphy_read(miphy, MIPHY_DES_BITLOCK_STATUS));
}
#endif /* DEBUG */
int stm_miphy_sata_status(struct stm_miphy *miphy)
{
u8 miphy_int_status;
struct stm_miphy_device *dev = miphy->dev;
mutex_lock(&dev->mutex);
miphy_int_status = stm_miphy_read(miphy, 0x4);
mutex_unlock(&dev->mutex);
return (miphy_int_status & 0x8) || (miphy_int_status & 0x2);
}
EXPORT_SYMBOL(stm_miphy_sata_status);
void stm_miphy_assert_deserializer(struct stm_miphy *miphy, int assert)
{
struct stm_miphy_device *dev = miphy->dev;
mutex_lock(&dev->mutex);
stm_miphy_write(miphy, 0x00, assert ? 0x10 : 0x00);
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL(stm_miphy_assert_deserializer);
int stm_miphy_start(struct stm_miphy *miphy)
{
struct stm_miphy_device *dev;
int port;
int rval = -ENODEV;
dev = miphy->dev;
port = miphy->port;
mutex_lock(&dev->mutex);
switch (miphy->mode) {
case SATA_MODE:
switch (dev->type) {
case TAP_IF:
rval = tap_miphy_sata_start(port, dev);
break;
case UPORT_IF:
rval = mp_miphy_sata_start(port, dev);
break;
case DUMMY_IF:
break;
default:
BUG();
}
break;
case PCIE_MODE:
switch (dev->type) {
case TAP_IF:
rval = tap_miphy_pcie_start(port, dev);
break;
case UPORT_IF:
rval = mp_miphy_pcie_start(port, dev);
break;
case DUMMY_IF:
break;
default:
BUG();
}
break;
default:
BUG();
}
/* Clear the contents of interrupt control register,
excluding fifooverlap_int */
stm_miphy_write(miphy, MIPHY_INT_STATUS, 0x77);
mutex_unlock(&dev->mutex);
return rval;
}
EXPORT_SYMBOL(stm_miphy_start);
struct stm_miphy *stm_miphy_claim(int port, enum miphy_mode mode,
struct device *owner)
{
struct stm_miphy *iterator;
struct stm_miphy *miphy = NULL;
u8 miphy_version, miphy_revision;
if (!owner)
return NULL;
mutex_lock(&miphy_list_mutex);
list_for_each_entry(iterator, &miphy_list, node)
if (iterator->port == port) {
miphy = iterator;
break;
}
if (!miphy) { /* Not found */
pr_warning("MiPhy %d not available\n", port);
goto _on_error;
}
if (miphy->owner) { /* already claimed */
pr_err("MiPhy %d already claimed by %s\n",
port, dev_name(miphy->owner));
miphy = NULL;
goto _on_error;
}
if (miphy->mode != mode) {
pr_err("MiPHY %d is not in the correct mode\n", port);
miphy = NULL;
goto _on_error;
}
miphy->owner = owner;
miphy_version = stm_miphy_read(miphy, MIPHY_VERSION);
miphy_revision = stm_miphy_read(miphy, MIPHY_REVISION);
pr_info("MiPHY%d, c%d.%d Claimed by %s \n",
(miphy_version >> 4) & 0x7,
(miphy_version & 0xf),
miphy_revision, dev_name(miphy->owner));
stm_miphy_start(miphy);
_on_error:
mutex_unlock(&miphy_list_mutex);
return miphy;
}
EXPORT_SYMBOL(stm_miphy_claim);
void stm_miphy_release(struct stm_miphy *miphy)
{
/* FIXME */
}
EXPORT_SYMBOL(stm_miphy_release);
void stm_miphy_freeze(struct stm_miphy *miphy)
{
/* Nothing to do */
}
EXPORT_SYMBOL(stm_miphy_freeze);
void stm_miphy_thaw(struct stm_miphy *miphy)
{
stm_miphy_start(miphy);
}
EXPORT_SYMBOL(stm_miphy_thaw);
int miphy_if_register(struct stm_miphy_device *miphy_dev,
enum miphy_if_type type,
int miphy_first, int miphy_count,
enum miphy_mode *modes,
struct device *parent,
const struct miphy_if_ops *ops)
{
struct stm_miphy *miphy, *new_miphys;
int miphy_num;
if (!miphy_class) {
miphy_class = class_create(THIS_MODULE, "stm-miphy");
if (IS_ERR(miphy_class)) {
printk(KERN_ERR "stm-miphy: can't register class\n");
return PTR_ERR(miphy_class);
}
}
new_miphys = kzalloc(sizeof(*miphy) * miphy_count, GFP_KERNEL);
if (!new_miphys)
return -ENOMEM;
for (miphy_num = miphy_first, miphy = new_miphys;
miphy_num < miphy_first + miphy_count;
miphy_num++, miphy++) {
miphy->dev = miphy_dev;
miphy->port = miphy_num;
miphy->mode = *modes++;
mutex_lock(&miphy_list_mutex);
list_add(&miphy->node, &miphy_list);
mutex_unlock(&miphy_list_mutex);
miphy->device = device_create(miphy_class, parent, 0, miphy,
"stm-miphy%d", miphy_num);
if (IS_ERR(miphy->device))
return PTR_ERR(miphy->device);
}
memset(miphy_dev, 0, sizeof(*miphy_dev));
mutex_init(&miphy_dev->mutex);
miphy_dev->ops = ops;
miphy_dev->type = type;
return 0;
}
int miphy_if_unregister(struct stm_miphy_device *miphy_dev)
{
/* TODO */
return 0;
}