579 lines
13 KiB
C
Executable File
579 lines
13 KiB
C
Executable File
/*
|
|
* Copyright 2018 NXP
|
|
* Copyright 2018 INPHI
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Inphi is a registered trademark of Inphi Corporation
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/mdio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#define PHY_ID_IN112525 0x02107440
|
|
|
|
#define INPHI_S03_DEVICE_ID_MSB 0x2
|
|
#define INPHI_S03_DEVICE_ID_LSB 0x3
|
|
|
|
#define ALL_LANES 4
|
|
#define INPHI_POLL_DELAY 2500
|
|
|
|
#define PHYCTRL_REG1 0x0012
|
|
#define PHYCTRL_REG2 0x0014
|
|
#define PHYCTRL_REG3 0x0120
|
|
#define PHYCTRL_REG4 0x0121
|
|
#define PHYCTRL_REG5 0x0180
|
|
#define PHYCTRL_REG6 0x0580
|
|
#define PHYCTRL_REG7 0x05C4
|
|
#define PHYCTRL_REG8 0x01C8
|
|
#define PHYCTRL_REG9 0x0521
|
|
|
|
#define PHYSTAT_REG1 0x0021
|
|
#define PHYSTAT_REG2 0x0022
|
|
#define PHYSTAT_REG3 0x0123
|
|
|
|
#define PHYMISC_REG1 0x0025
|
|
#define PHYMISC_REG2 0x002c
|
|
#define PHYMISC_REG3 0x00b3
|
|
#define PHYMISC_REG4 0x0181
|
|
#define PHYMISC_REG5 0x019D
|
|
#define PHYMISC_REG6 0x0198
|
|
#define PHYMISC_REG7 0x0199
|
|
#define PHYMISC_REG8 0x0581
|
|
#define PHYMISC_REG9 0x0598
|
|
#define PHYMISC_REG10 0x059c
|
|
#define PHYMISC_REG20 0x01B0
|
|
#define PHYMISC_REG21 0x01BC
|
|
#define PHYMISC_REG22 0x01C0
|
|
|
|
#define RX_VCO_CODE_OFFSET 5
|
|
|
|
#define mdio_wr(a, b) phy_write_mmd(inphi_phydev, MDIO_MMD_VEND1, (a), (b))
|
|
#define mdio_rd(a) phy_read_mmd(inphi_phydev, MDIO_MMD_VEND1, (a))
|
|
|
|
#define VCO_CODE 390
|
|
|
|
int vco_codes[ALL_LANES] = {
|
|
VCO_CODE,
|
|
VCO_CODE,
|
|
VCO_CODE,
|
|
VCO_CODE
|
|
};
|
|
|
|
static void mykmod_work_handler(struct work_struct *w);
|
|
|
|
static struct workqueue_struct *wq;
|
|
static DECLARE_DELAYED_WORK(mykmod_work, mykmod_work_handler);
|
|
static unsigned long onesec;
|
|
struct phy_device *inphi_phydev;
|
|
|
|
int bit_test(int value, int bit_field)
|
|
{
|
|
int result;
|
|
int bit_mask = (1 << bit_field);
|
|
|
|
result = ((value & bit_mask) == bit_mask);
|
|
return result;
|
|
}
|
|
|
|
int tx_pll_lock_test(int lane)
|
|
{
|
|
int i, val, locked = 1;
|
|
|
|
if (lane == ALL_LANES) {
|
|
for (i = 0; i < ALL_LANES; i++) {
|
|
val = mdio_rd(i * 0x100 + PHYSTAT_REG3);
|
|
locked = locked & bit_test(val, 15);
|
|
}
|
|
} else {
|
|
val = mdio_rd(lane * 0x100 + PHYSTAT_REG3);
|
|
locked = locked & bit_test(val, 15);
|
|
}
|
|
|
|
return locked;
|
|
}
|
|
|
|
void rx_reset_assert(int lane)
|
|
{
|
|
int mask, val;
|
|
|
|
if (lane == ALL_LANES) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
mask = (1 << 15);
|
|
mdio_wr(PHYMISC_REG2, val + mask);
|
|
} else {
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG8);
|
|
mask = (1 << 6);
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask);
|
|
}
|
|
}
|
|
|
|
void rx_reset_de_assert(int lane)
|
|
{
|
|
int mask, val;
|
|
|
|
if (lane == ALL_LANES) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
mask = 0xffff - (1 << 15);
|
|
mdio_wr(PHYMISC_REG2, val & mask);
|
|
} else {
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG8);
|
|
mask = 0xffff - (1 << 6);
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask);
|
|
}
|
|
}
|
|
|
|
void rx_powerdown_assert(int lane)
|
|
{
|
|
int mask, val;
|
|
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG8);
|
|
mask = (1 << 5);
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask);
|
|
}
|
|
|
|
void rx_powerdown_de_assert(int lane)
|
|
{
|
|
int mask, val;
|
|
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG8);
|
|
mask = 0xffff - (1 << 5);
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask);
|
|
}
|
|
|
|
void tx_pll_assert(int lane)
|
|
{
|
|
int val, recal;
|
|
|
|
if (lane == ALL_LANES) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
recal = (1 << 12);
|
|
mdio_wr(PHYMISC_REG2, val | recal);
|
|
} else {
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG4);
|
|
recal = (1 << 15);
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG4, val | recal);
|
|
}
|
|
}
|
|
|
|
void tx_pll_de_assert(int lane)
|
|
{
|
|
int recal, val;
|
|
|
|
if (lane == ALL_LANES) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
recal = 0xefff;
|
|
mdio_wr(PHYMISC_REG2, val & recal);
|
|
} else {
|
|
val = mdio_rd(lane * 0x100 + PHYCTRL_REG4);
|
|
recal = 0x7fff;
|
|
mdio_wr(lane * 0x100 + PHYCTRL_REG4, val & recal);
|
|
}
|
|
}
|
|
|
|
void tx_core_assert(int lane)
|
|
{
|
|
int recal, val, val2, core_reset;
|
|
|
|
if (lane == 4) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
recal = 1 << 10;
|
|
mdio_wr(PHYMISC_REG2, val | recal);
|
|
} else {
|
|
val2 = mdio_rd(PHYMISC_REG3);
|
|
core_reset = (1 << (lane + 8));
|
|
mdio_wr(PHYMISC_REG3, val2 | core_reset);
|
|
}
|
|
}
|
|
|
|
void lol_disable(int lane)
|
|
{
|
|
int val, mask;
|
|
|
|
val = mdio_rd(PHYMISC_REG3);
|
|
mask = 1 << (lane + 4);
|
|
mdio_wr(PHYMISC_REG3, val | mask);
|
|
}
|
|
|
|
void tx_core_de_assert(int lane)
|
|
{
|
|
int val, recal, val2, core_reset;
|
|
|
|
if (lane == ALL_LANES) {
|
|
val = mdio_rd(PHYMISC_REG2);
|
|
recal = 0xffff - (1 << 10);
|
|
mdio_wr(PHYMISC_REG2, val & recal);
|
|
} else {
|
|
val2 = mdio_rd(PHYMISC_REG3);
|
|
core_reset = 0xffff - (1 << (lane + 8));
|
|
mdio_wr(PHYMISC_REG3, val2 & core_reset);
|
|
}
|
|
}
|
|
|
|
void tx_restart(int lane)
|
|
{
|
|
tx_core_assert(lane);
|
|
tx_pll_assert(lane);
|
|
tx_pll_de_assert(lane);
|
|
usleep_range(1500, 1600);
|
|
tx_core_de_assert(lane);
|
|
}
|
|
|
|
void disable_lane(int lane)
|
|
{
|
|
rx_reset_assert(lane);
|
|
rx_powerdown_assert(lane);
|
|
tx_core_assert(lane);
|
|
lol_disable(lane);
|
|
}
|
|
|
|
void toggle_reset(int lane)
|
|
{
|
|
int reg, val, orig;
|
|
|
|
if (lane == ALL_LANES) {
|
|
mdio_wr(PHYMISC_REG2, 0x8000);
|
|
udelay(100);
|
|
mdio_wr(PHYMISC_REG2, 0x0000);
|
|
} else {
|
|
reg = lane * 0x100 + PHYCTRL_REG8;
|
|
val = (1 << 6);
|
|
orig = mdio_rd(reg);
|
|
mdio_wr(reg, orig + val);
|
|
udelay(100);
|
|
mdio_wr(reg, orig);
|
|
}
|
|
}
|
|
|
|
int az_complete_test(int lane)
|
|
{
|
|
int success = 1, value;
|
|
|
|
if (lane == 0 || lane == ALL_LANES) {
|
|
value = mdio_rd(PHYCTRL_REG5);
|
|
success = success & bit_test(value, 2);
|
|
}
|
|
if (lane == 1 || lane == ALL_LANES) {
|
|
value = mdio_rd(PHYCTRL_REG5 + 0x100);
|
|
success = success & bit_test(value, 2);
|
|
}
|
|
if (lane == 2 || lane == ALL_LANES) {
|
|
value = mdio_rd(PHYCTRL_REG5 + 0x200);
|
|
success = success & bit_test(value, 2);
|
|
}
|
|
if (lane == 3 || lane == ALL_LANES) {
|
|
value = mdio_rd(PHYCTRL_REG5 + 0x300);
|
|
success = success & bit_test(value, 2);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void save_az_offsets(int lane)
|
|
{
|
|
int i;
|
|
|
|
#define AZ_OFFSET_LANE_UPDATE(reg, lane) \
|
|
mdio_wr((reg) + (lane) * 0x100, \
|
|
(mdio_rd((reg) + (lane) * 0x100) >> 8))
|
|
|
|
if (lane == ALL_LANES) {
|
|
for (i = 0; i < ALL_LANES; i++) {
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, i);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, i);
|
|
}
|
|
} else {
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, lane);
|
|
AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, lane);
|
|
}
|
|
|
|
mdio_wr(PHYCTRL_REG7, 0x0001);
|
|
}
|
|
|
|
void save_vco_codes(int lane)
|
|
{
|
|
int i;
|
|
|
|
if (lane == ALL_LANES) {
|
|
for (i = 0; i < ALL_LANES; i++) {
|
|
vco_codes[i] = mdio_rd(PHYMISC_REG5 + i * 0x100);
|
|
mdio_wr(PHYMISC_REG5 + i * 0x100,
|
|
vco_codes[i] + RX_VCO_CODE_OFFSET);
|
|
}
|
|
} else {
|
|
vco_codes[lane] = mdio_rd(PHYMISC_REG5 + lane * 0x100);
|
|
mdio_wr(PHYMISC_REG5 + lane * 0x100,
|
|
vco_codes[lane] + RX_VCO_CODE_OFFSET);
|
|
}
|
|
}
|
|
|
|
int inphi_lane_recovery(int lane)
|
|
{
|
|
int i, value, az_pass;
|
|
|
|
switch (lane) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
rx_reset_assert(lane);
|
|
mdelay(20);
|
|
break;
|
|
case ALL_LANES:
|
|
mdio_wr(PHYMISC_REG2, 0x9C00);
|
|
mdelay(20);
|
|
do {
|
|
value = mdio_rd(PHYMISC_REG2);
|
|
udelay(10);
|
|
} while (!bit_test(value, 4));
|
|
break;
|
|
default:
|
|
dev_err(&inphi_phydev->mdio.dev,
|
|
"Incorrect usage of APIs in %s driver\n",
|
|
inphi_phydev->drv->name);
|
|
break;
|
|
}
|
|
|
|
if (lane == ALL_LANES) {
|
|
for (i = 0; i < ALL_LANES; i++)
|
|
mdio_wr(PHYMISC_REG7 + i * 0x100, VCO_CODE);
|
|
} else {
|
|
mdio_wr(PHYMISC_REG7 + lane * 0x100, VCO_CODE);
|
|
}
|
|
|
|
if (lane == ALL_LANES)
|
|
for (i = 0; i < ALL_LANES; i++)
|
|
mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0418);
|
|
else
|
|
mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0418);
|
|
|
|
mdio_wr(PHYCTRL_REG7, 0x0000);
|
|
|
|
rx_reset_de_assert(lane);
|
|
|
|
if (lane == ALL_LANES) {
|
|
for (i = 0; i < ALL_LANES; i++) {
|
|
mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0410);
|
|
mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0412);
|
|
}
|
|
} else {
|
|
mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0410);
|
|
mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0412);
|
|
}
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
mdelay(100);
|
|
az_pass = az_complete_test(lane);
|
|
if (az_pass) {
|
|
save_az_offsets(lane);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!az_pass) {
|
|
pr_info("in112525: AZ calibration fail @ lane=%d\n", lane);
|
|
return -1;
|
|
}
|
|
|
|
if (lane == ALL_LANES) {
|
|
mdio_wr(PHYMISC_REG8, 0x0002);
|
|
mdio_wr(PHYMISC_REG9, 0x2028);
|
|
mdio_wr(PHYCTRL_REG6, 0x0010);
|
|
usleep_range(1000, 1200);
|
|
mdio_wr(PHYCTRL_REG6, 0x0110);
|
|
mdelay(30);
|
|
mdio_wr(PHYMISC_REG9, 0x3020);
|
|
} else {
|
|
mdio_wr(PHYMISC_REG4 + lane * 0x100, 0x0002);
|
|
mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x2028);
|
|
mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0010);
|
|
usleep_range(1000, 1200);
|
|
mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0110);
|
|
mdelay(30);
|
|
mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x3020);
|
|
}
|
|
|
|
if (lane == ALL_LANES) {
|
|
mdio_wr(PHYMISC_REG2, 0x1C00);
|
|
mdio_wr(PHYMISC_REG2, 0x0C00);
|
|
} else {
|
|
tx_restart(lane);
|
|
mdelay(11);
|
|
}
|
|
|
|
if (lane == ALL_LANES) {
|
|
if (bit_test(mdio_rd(PHYMISC_REG2), 6) == 0)
|
|
return -1;
|
|
} else {
|
|
if (tx_pll_lock_test(lane) == 0)
|
|
return -1;
|
|
}
|
|
|
|
save_vco_codes(lane);
|
|
|
|
if (lane == ALL_LANES) {
|
|
mdio_wr(PHYMISC_REG2, 0x0400);
|
|
mdio_wr(PHYMISC_REG2, 0x0000);
|
|
value = mdio_rd(PHYCTRL_REG1);
|
|
value = value & 0xffbf;
|
|
mdio_wr(PHYCTRL_REG2, value);
|
|
} else {
|
|
tx_core_de_assert(lane);
|
|
}
|
|
|
|
if (lane == ALL_LANES) {
|
|
mdio_wr(PHYMISC_REG1, 0x8000);
|
|
mdio_wr(PHYMISC_REG1, 0x0000);
|
|
}
|
|
mdio_rd(PHYMISC_REG1);
|
|
mdio_rd(PHYMISC_REG1);
|
|
usleep_range(1000, 1200);
|
|
mdio_rd(PHYSTAT_REG1);
|
|
mdio_rd(PHYSTAT_REG2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mykmod_work_handler(struct work_struct *w)
|
|
{
|
|
int all_lanes_lock, lane0_lock, lane1_lock, lane2_lock, lane3_lock;
|
|
|
|
lane0_lock = bit_test(mdio_rd(0x123), 15);
|
|
lane1_lock = bit_test(mdio_rd(0x223), 15);
|
|
lane2_lock = bit_test(mdio_rd(0x323), 15);
|
|
lane3_lock = bit_test(mdio_rd(0x423), 15);
|
|
|
|
/* check if the chip had any successful lane lock from the previous
|
|
* stage (e.g. u-boot)
|
|
*/
|
|
all_lanes_lock = lane0_lock | lane1_lock | lane2_lock | lane3_lock;
|
|
|
|
if (!all_lanes_lock) {
|
|
/* start fresh */
|
|
inphi_lane_recovery(ALL_LANES);
|
|
} else {
|
|
if (!lane0_lock)
|
|
inphi_lane_recovery(0);
|
|
if (!lane1_lock)
|
|
inphi_lane_recovery(1);
|
|
if (!lane2_lock)
|
|
inphi_lane_recovery(2);
|
|
if (!lane3_lock)
|
|
inphi_lane_recovery(3);
|
|
}
|
|
|
|
queue_delayed_work(wq, &mykmod_work, onesec);
|
|
}
|
|
|
|
int inphi_probe(struct phy_device *phydev)
|
|
{
|
|
int phy_id = 0, id_lsb = 0, id_msb = 0;
|
|
|
|
/* Read device id from phy registers */
|
|
id_lsb = phy_read_mmd(phydev, MDIO_MMD_VEND1, INPHI_S03_DEVICE_ID_MSB);
|
|
if (id_lsb < 0)
|
|
return -ENXIO;
|
|
|
|
phy_id = id_lsb << 16;
|
|
|
|
id_msb = phy_read_mmd(phydev, MDIO_MMD_VEND1, INPHI_S03_DEVICE_ID_LSB);
|
|
if (id_msb < 0)
|
|
return -ENXIO;
|
|
|
|
phy_id |= id_msb;
|
|
|
|
/* Make sure the device tree binding matched the driver with the
|
|
* right device.
|
|
*/
|
|
if (phy_id != phydev->drv->phy_id) {
|
|
dev_err(&phydev->mdio.dev,
|
|
"Error matching phy with %s driver\n",
|
|
phydev->drv->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* update the local phydev pointer, used inside all APIs */
|
|
inphi_phydev = phydev;
|
|
onesec = msecs_to_jiffies(INPHI_POLL_DELAY);
|
|
|
|
wq = create_singlethread_workqueue("inphi_kmod");
|
|
if (wq) {
|
|
queue_delayed_work(wq, &mykmod_work, onesec);
|
|
} else {
|
|
dev_err(&phydev->mdio.dev,
|
|
"Error creating kernel workqueue for %s driver\n",
|
|
phydev->drv->name);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct phy_driver inphi_driver[] = {
|
|
{
|
|
.phy_id = PHY_ID_IN112525,
|
|
.phy_id_mask = 0x0ff0fff0,
|
|
.name = "Inphi 112525_S03",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.probe = &inphi_probe,
|
|
},
|
|
};
|
|
|
|
module_phy_driver(inphi_driver);
|
|
|
|
static struct mdio_device_id __maybe_unused inphi_tbl[] = {
|
|
{ PHY_ID_IN112525, 0x0ff0fff0},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(mdio, inphi_tbl);
|