663 lines
18 KiB
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;
|
|
}
|