2019-01-21 21:10:19 +03:00
// SPDX-License-Identifier: GPL-2.0
2007-10-01 23:20:56 +04:00
/*
* Bitbanged MDIO support .
*
* Author : Scott Wood < scottwood @ freescale . com >
* Copyright ( c ) 2007 Freescale Semiconductor
*
* Based on CPM2 MDIO code which is :
*
* Copyright ( c ) 2003 Intracom S . A .
* by Pantelis Antoniou < panto @ intracom . gr >
*
* 2005 ( c ) MontaVista Software , Inc .
* Vitaly Bordug < vbordug @ ru . mvista . com >
*/
2021-03-15 13:49:05 +03:00
# include <linux/delay.h>
2007-10-01 23:20:56 +04:00
# include <linux/mdio-bitbang.h>
2021-03-15 13:49:05 +03:00
# include <linux/module.h>
2007-10-01 23:20:56 +04:00
# include <linux/types.h>
2010-03-09 12:17:42 +03:00
# define MDIO_READ 2
# define MDIO_WRITE 1
# define MDIO_C45 (1<<15)
# define MDIO_C45_ADDR (MDIO_C45 | 0)
# define MDIO_C45_READ (MDIO_C45 | 3)
# define MDIO_C45_WRITE (MDIO_C45 | 1)
2007-10-01 23:20:56 +04:00
# define MDIO_SETUP_TIME 10
# define MDIO_HOLD_TIME 10
/* Minimum MDC period is 400 ns, plus some margin for error. MDIO_DELAY
* is done twice per period .
*/
# define MDIO_DELAY 250
/* The PHY may take up to 300 ns to produce data, plus some margin
* for error .
*/
# define MDIO_READ_DELAY 350
/* MDIO must already be configured as output. */
static void mdiobb_send_bit ( struct mdiobb_ctrl * ctrl , int val )
{
const struct mdiobb_ops * ops = ctrl - > ops ;
ops - > set_mdio_data ( ctrl , val ) ;
ndelay ( MDIO_DELAY ) ;
ops - > set_mdc ( ctrl , 1 ) ;
ndelay ( MDIO_DELAY ) ;
ops - > set_mdc ( ctrl , 0 ) ;
}
/* MDIO must already be configured as input. */
static int mdiobb_get_bit ( struct mdiobb_ctrl * ctrl )
{
const struct mdiobb_ops * ops = ctrl - > ops ;
ndelay ( MDIO_DELAY ) ;
ops - > set_mdc ( ctrl , 1 ) ;
ndelay ( MDIO_READ_DELAY ) ;
ops - > set_mdc ( ctrl , 0 ) ;
return ops - > get_mdio_data ( ctrl ) ;
}
/* MDIO must already be configured as output. */
static void mdiobb_send_num ( struct mdiobb_ctrl * ctrl , u16 val , int bits )
{
int i ;
for ( i = bits - 1 ; i > = 0 ; i - - )
mdiobb_send_bit ( ctrl , ( val > > i ) & 1 ) ;
}
/* MDIO must already be configured as input. */
static u16 mdiobb_get_num ( struct mdiobb_ctrl * ctrl , int bits )
{
int i ;
u16 ret = 0 ;
for ( i = bits - 1 ; i > = 0 ; i - - ) {
ret < < = 1 ;
ret | = mdiobb_get_bit ( ctrl ) ;
}
return ret ;
}
/* Utility to send the preamble, address, and
* register ( common to read and write ) .
*/
2010-03-09 12:17:42 +03:00
static void mdiobb_cmd ( struct mdiobb_ctrl * ctrl , int op , u8 phy , u8 reg )
2007-10-01 23:20:56 +04:00
{
const struct mdiobb_ops * ops = ctrl - > ops ;
int i ;
ops - > set_mdio_dir ( ctrl , 1 ) ;
/*
* Send a 32 bit preamble ( ' 1 ' s ) with an extra ' 1 ' bit for good
* measure . The IEEE spec says this is a PHY optional
* requirement . The AMD 79 C874 requires one after power up and
* one after a MII communications error . This means that we are
* doing more preambles than we need , but it is safer and will be
* much more robust .
*/
for ( i = 0 ; i < 32 ; i + + )
mdiobb_send_bit ( ctrl , 1 ) ;
2018-04-01 23:52:55 +03:00
/* send the start bit (01) and the read opcode (10) or write (01).
2010-03-09 12:17:42 +03:00
Clause 45 operation uses 00 for the start and 11 , 10 for
read / write */
2007-10-01 23:20:56 +04:00
mdiobb_send_bit ( ctrl , 0 ) ;
2010-03-09 12:17:42 +03:00
if ( op & MDIO_C45 )
mdiobb_send_bit ( ctrl , 0 ) ;
else
mdiobb_send_bit ( ctrl , 1 ) ;
mdiobb_send_bit ( ctrl , ( op > > 1 ) & 1 ) ;
mdiobb_send_bit ( ctrl , ( op > > 0 ) & 1 ) ;
2007-10-01 23:20:56 +04:00
mdiobb_send_num ( ctrl , phy , 5 ) ;
mdiobb_send_num ( ctrl , reg , 5 ) ;
}
2010-03-09 12:17:42 +03:00
/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the
lower 16 bits of the 21 bit address . This transfer is done identically to a
MDIO_WRITE except for a different code . To enable clause 45 mode or
MII_ADDR_C45 into the address . Theoretically clause 45 and normal devices
can exist on the same bus . Normal devices should ignore the MDIO_ADDR
phase . */
static int mdiobb_cmd_addr ( struct mdiobb_ctrl * ctrl , int phy , u32 addr )
{
unsigned int dev_addr = ( addr > > 16 ) & 0x1F ;
unsigned int reg = addr & 0xFFFF ;
mdiobb_cmd ( ctrl , MDIO_C45_ADDR , phy , dev_addr ) ;
/* send the turnaround (10) */
mdiobb_send_bit ( ctrl , 1 ) ;
mdiobb_send_bit ( ctrl , 0 ) ;
mdiobb_send_num ( ctrl , reg , 16 ) ;
ctrl - > ops - > set_mdio_dir ( ctrl , 0 ) ;
mdiobb_get_bit ( ctrl ) ;
return dev_addr ;
}
2007-10-01 23:20:56 +04:00
2021-01-18 18:06:55 +03:00
int mdiobb_read ( struct mii_bus * bus , int phy , int reg )
2007-10-01 23:20:56 +04:00
{
struct mdiobb_ctrl * ctrl = bus - > priv ;
int ret , i ;
2010-03-09 12:17:42 +03:00
if ( reg & MII_ADDR_C45 ) {
reg = mdiobb_cmd_addr ( ctrl , phy , reg ) ;
mdiobb_cmd ( ctrl , MDIO_C45_READ , phy , reg ) ;
} else
2021-04-27 10:09:07 +03:00
mdiobb_cmd ( ctrl , ctrl - > op_c22_read , phy , reg ) ;
2010-03-09 12:17:42 +03:00
2007-10-01 23:20:56 +04:00
ctrl - > ops - > set_mdio_dir ( ctrl , 0 ) ;
2015-05-12 20:33:26 +03:00
/* check the turnaround bit: the PHY should be driving it to zero, if this
* PHY is listed in phy_ignore_ta_mask as having broken TA , skip that
*/
if ( mdiobb_get_bit ( ctrl ) ! = 0 & &
! ( bus - > phy_ignore_ta_mask & ( 1 < < phy ) ) ) {
2007-10-01 23:20:56 +04:00
/* PHY didn't drive TA low -- flush any bits it
* may be trying to send .
*/
for ( i = 0 ; i < 32 ; i + + )
mdiobb_get_bit ( ctrl ) ;
return 0xffff ;
}
ret = mdiobb_get_num ( ctrl , 16 ) ;
mdiobb_get_bit ( ctrl ) ;
return ret ;
}
2021-01-18 18:06:55 +03:00
EXPORT_SYMBOL ( mdiobb_read ) ;
2007-10-01 23:20:56 +04:00
2021-01-18 18:06:55 +03:00
int mdiobb_write ( struct mii_bus * bus , int phy , int reg , u16 val )
2007-10-01 23:20:56 +04:00
{
struct mdiobb_ctrl * ctrl = bus - > priv ;
2010-03-09 12:17:42 +03:00
if ( reg & MII_ADDR_C45 ) {
reg = mdiobb_cmd_addr ( ctrl , phy , reg ) ;
mdiobb_cmd ( ctrl , MDIO_C45_WRITE , phy , reg ) ;
} else
2021-04-27 10:09:07 +03:00
mdiobb_cmd ( ctrl , ctrl - > op_c22_write , phy , reg ) ;
2007-10-01 23:20:56 +04:00
/* send the turnaround (10) */
mdiobb_send_bit ( ctrl , 1 ) ;
mdiobb_send_bit ( ctrl , 0 ) ;
mdiobb_send_num ( ctrl , val , 16 ) ;
ctrl - > ops - > set_mdio_dir ( ctrl , 0 ) ;
mdiobb_get_bit ( ctrl ) ;
return 0 ;
}
2021-01-18 18:06:55 +03:00
EXPORT_SYMBOL ( mdiobb_write ) ;
2007-10-01 23:20:56 +04:00
struct mii_bus * alloc_mdio_bitbang ( struct mdiobb_ctrl * ctrl )
{
struct mii_bus * bus ;
2008-10-09 03:29:57 +04:00
bus = mdiobus_alloc ( ) ;
2007-10-01 23:20:56 +04:00
if ( ! bus )
return NULL ;
__module_get ( ctrl - > ops - > owner ) ;
bus - > read = mdiobb_read ;
bus - > write = mdiobb_write ;
bus - > priv = ctrl ;
2021-04-27 10:09:07 +03:00
if ( ! ctrl - > override_op_c22 ) {
ctrl - > op_c22_read = MDIO_READ ;
ctrl - > op_c22_write = MDIO_WRITE ;
}
2007-10-01 23:20:56 +04:00
return bus ;
}
2008-07-07 18:51:45 +04:00
EXPORT_SYMBOL ( alloc_mdio_bitbang ) ;
2007-10-01 23:20:56 +04:00
void free_mdio_bitbang ( struct mii_bus * bus )
{
struct mdiobb_ctrl * ctrl = bus - > priv ;
module_put ( ctrl - > ops - > owner ) ;
2008-10-09 03:29:57 +04:00
mdiobus_free ( bus ) ;
2007-10-01 23:20:56 +04:00
}
2008-07-07 18:51:45 +04:00
EXPORT_SYMBOL ( free_mdio_bitbang ) ;
2007-10-18 23:20:21 +04:00
2019-01-21 21:10:19 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;