2015-10-06 16:37:46 -04:00
/**
* Register map access API - ENCX24J600 support
*
* Copyright 2015 Gridpoint
*
* Author : Jon Ringle < jringle @ gridpoint . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/regmap.h>
# include <linux/spi/spi.h>
# include "encx24j600_hw.h"
static inline bool is_bits_set ( int value , int mask )
{
return ( value & mask ) = = mask ;
}
static int encx24j600_switch_bank ( struct encx24j600_context * ctx ,
2016-12-12 14:29:09 +01:00
int bank )
2015-10-06 16:37:46 -04:00
{
int ret = 0 ;
int bank_opcode = BANK_SELECT ( bank ) ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
ret = spi_write ( ctx - > spi , & bank_opcode , 1 ) ;
if ( ret = = 0 )
ctx - > bank = bank ;
return ret ;
}
static int encx24j600_cmdn ( struct encx24j600_context * ctx , u8 opcode ,
2016-12-12 14:29:09 +01:00
const void * buf , size_t len )
2015-10-06 16:37:46 -04:00
{
struct spi_message m ;
struct spi_transfer t [ 2 ] = { { . tx_buf = & opcode , . len = 1 , } ,
{ . tx_buf = buf , . len = len } , } ;
spi_message_init ( & m ) ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
return spi_sync ( ctx - > spi , & m ) ;
}
static void regmap_lock_mutex ( void * context )
{
struct encx24j600_context * ctx = context ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
mutex_lock ( & ctx - > mutex ) ;
}
static void regmap_unlock_mutex ( void * context )
{
struct encx24j600_context * ctx = context ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
mutex_unlock ( & ctx - > mutex ) ;
}
static int regmap_encx24j600_sfr_read ( void * context , u8 reg , u8 * val ,
size_t len )
{
struct encx24j600_context * ctx = context ;
u8 banked_reg = reg & ADDR_MASK ;
u8 bank = ( ( reg & BANK_MASK ) > > BANK_SHIFT ) ;
u8 cmd = RCRU ;
int ret = 0 ;
int i = 0 ;
u8 tx_buf [ 2 ] ;
if ( reg < 0x80 ) {
cmd = RCRCODE | banked_reg ;
if ( ( banked_reg < 0x16 ) & & ( ctx - > bank ! = bank ) )
ret = encx24j600_switch_bank ( ctx , bank ) ;
if ( unlikely ( ret ) )
return ret ;
} else {
/* Translate registers that are more effecient using
* 3 - byte SPI commands
*/
switch ( reg ) {
case EGPRDPT :
cmd = RGPRDPT ; break ;
case EGPWRPT :
cmd = RGPWRPT ; break ;
case ERXRDPT :
cmd = RRXRDPT ; break ;
case ERXWRPT :
cmd = RRXWRPT ; break ;
case EUDARDPT :
cmd = RUDARDPT ; break ;
case EUDAWRPT :
cmd = RUDAWRPT ; break ;
case EGPDATA :
case ERXDATA :
case EUDADATA :
default :
return - EINVAL ;
}
}
tx_buf [ i + + ] = cmd ;
if ( cmd = = RCRU )
tx_buf [ i + + ] = reg ;
ret = spi_write_then_read ( ctx - > spi , tx_buf , i , val , len ) ;
return ret ;
}
static int regmap_encx24j600_sfr_update ( struct encx24j600_context * ctx ,
u8 reg , u8 * val , size_t len ,
u8 unbanked_cmd , u8 banked_code )
{
u8 banked_reg = reg & ADDR_MASK ;
u8 bank = ( ( reg & BANK_MASK ) > > BANK_SHIFT ) ;
u8 cmd = unbanked_cmd ;
struct spi_message m ;
struct spi_transfer t [ 3 ] = { { . tx_buf = & cmd , . len = sizeof ( cmd ) , } ,
{ . tx_buf = & reg , . len = sizeof ( reg ) , } ,
{ . tx_buf = val , . len = len } , } ;
if ( reg < 0x80 ) {
int ret = 0 ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
cmd = banked_code | banked_reg ;
if ( ( banked_reg < 0x16 ) & & ( ctx - > bank ! = bank ) )
ret = encx24j600_switch_bank ( ctx , bank ) ;
if ( unlikely ( ret ) )
return ret ;
} else {
/* Translate registers that are more effecient using
* 3 - byte SPI commands
*/
switch ( reg ) {
case EGPRDPT :
cmd = WGPRDPT ; break ;
case EGPWRPT :
cmd = WGPWRPT ; break ;
case ERXRDPT :
cmd = WRXRDPT ; break ;
case ERXWRPT :
cmd = WRXWRPT ; break ;
case EUDARDPT :
cmd = WUDARDPT ; break ;
case EUDAWRPT :
cmd = WUDAWRPT ; break ;
case EGPDATA :
case ERXDATA :
case EUDADATA :
default :
return - EINVAL ;
}
}
spi_message_init ( & m ) ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
if ( cmd = = unbanked_cmd ) {
t [ 1 ] . tx_buf = & reg ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
}
spi_message_add_tail ( & t [ 2 ] , & m ) ;
return spi_sync ( ctx - > spi , & m ) ;
}
static int regmap_encx24j600_sfr_write ( void * context , u8 reg , u8 * val ,
size_t len )
{
struct encx24j600_context * ctx = context ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
return regmap_encx24j600_sfr_update ( ctx , reg , val , len , WCRU , WCRCODE ) ;
}
static int regmap_encx24j600_sfr_set_bits ( struct encx24j600_context * ctx ,
u8 reg , u8 val )
{
return regmap_encx24j600_sfr_update ( ctx , reg , & val , 1 , BFSU , BFSCODE ) ;
}
static int regmap_encx24j600_sfr_clr_bits ( struct encx24j600_context * ctx ,
u8 reg , u8 val )
{
return regmap_encx24j600_sfr_update ( ctx , reg , & val , 1 , BFCU , BFCCODE ) ;
}
static int regmap_encx24j600_reg_update_bits ( void * context , unsigned int reg ,
unsigned int mask ,
unsigned int val )
{
struct encx24j600_context * ctx = context ;
int ret = 0 ;
unsigned int set_mask = mask & val ;
unsigned int clr_mask = mask & ~ val ;
if ( ( reg > = 0x40 & & reg < 0x6c ) | | reg > = 0x80 )
return - EINVAL ;
if ( set_mask & 0xff )
ret = regmap_encx24j600_sfr_set_bits ( ctx , reg , set_mask ) ;
set_mask = ( set_mask & 0xff00 ) > > 8 ;
if ( ( set_mask & 0xff ) & & ( ret = = 0 ) )
ret = regmap_encx24j600_sfr_set_bits ( ctx , reg + 1 , set_mask ) ;
if ( ( clr_mask & 0xff ) & & ( ret = = 0 ) )
ret = regmap_encx24j600_sfr_clr_bits ( ctx , reg , clr_mask ) ;
clr_mask = ( clr_mask & 0xff00 ) > > 8 ;
if ( ( clr_mask & 0xff ) & & ( ret = = 0 ) )
ret = regmap_encx24j600_sfr_clr_bits ( ctx , reg + 1 , clr_mask ) ;
return ret ;
}
int regmap_encx24j600_spi_write ( void * context , u8 reg , const u8 * data ,
size_t count )
{
struct encx24j600_context * ctx = context ;
if ( reg < 0xc0 )
return encx24j600_cmdn ( ctx , reg , data , count ) ;
2016-12-12 14:29:09 +01:00
/* SPI 1-byte command. Ignore data */
return spi_write ( ctx - > spi , & reg , 1 ) ;
2015-10-06 16:37:46 -04:00
}
EXPORT_SYMBOL_GPL ( regmap_encx24j600_spi_write ) ;
int regmap_encx24j600_spi_read ( void * context , u8 reg , u8 * data , size_t count )
{
struct encx24j600_context * ctx = context ;
if ( reg = = RBSEL & & count > 1 )
count = 1 ;
return spi_write_then_read ( ctx - > spi , & reg , sizeof ( reg ) , data , count ) ;
}
EXPORT_SYMBOL_GPL ( regmap_encx24j600_spi_read ) ;
static int regmap_encx24j600_write ( void * context , const void * data ,
size_t len )
{
u8 * dout = ( u8 * ) data ;
u8 reg = dout [ 0 ] ;
+ + dout ;
- - len ;
if ( reg > 0xa0 )
return regmap_encx24j600_spi_write ( context , reg , dout , len ) ;
if ( len > 2 )
return - EINVAL ;
return regmap_encx24j600_sfr_write ( context , reg , dout , len ) ;
}
static int regmap_encx24j600_read ( void * context ,
const void * reg_buf , size_t reg_size ,
void * val , size_t val_size )
{
u8 reg = * ( const u8 * ) reg_buf ;
if ( reg_size ! = 1 ) {
pr_err ( " %s: reg=%02x reg_size=%zu \n " , __func__ , reg , reg_size ) ;
return - EINVAL ;
}
if ( reg > 0xa0 )
return regmap_encx24j600_spi_read ( context , reg , val , val_size ) ;
if ( val_size > 2 ) {
pr_err ( " %s: reg=%02x val_size=%zu \n " , __func__ , reg , val_size ) ;
return - EINVAL ;
}
return regmap_encx24j600_sfr_read ( context , reg , val , val_size ) ;
}
static bool encx24j600_regmap_readable ( struct device * dev , unsigned int reg )
{
if ( ( reg < 0x36 ) | |
( ( reg > = 0x40 ) & & ( reg < 0x4c ) ) | |
( ( reg > = 0x52 ) & & ( reg < 0x56 ) ) | |
( ( reg > = 0x60 ) & & ( reg < 0x66 ) ) | |
( ( reg > = 0x68 ) & & ( reg < 0x80 ) ) | |
( ( reg > = 0x86 ) & & ( reg < 0x92 ) ) | |
( reg = = 0xc8 ) )
return true ;
else
return false ;
}
static bool encx24j600_regmap_writeable ( struct device * dev , unsigned int reg )
{
if ( ( reg < 0x12 ) | |
( ( reg > = 0x14 ) & & ( reg < 0x1a ) ) | |
( ( reg > = 0x1c ) & & ( reg < 0x36 ) ) | |
( ( reg > = 0x40 ) & & ( reg < 0x4c ) ) | |
( ( reg > = 0x52 ) & & ( reg < 0x56 ) ) | |
( ( reg > = 0x60 ) & & ( reg < 0x68 ) ) | |
( ( reg > = 0x6c ) & & ( reg < 0x80 ) ) | |
( ( reg > = 0x86 ) & & ( reg < 0x92 ) ) | |
( ( reg > = 0xc0 ) & & ( reg < 0xc8 ) ) | |
( ( reg > = 0xca ) & & ( reg < 0xf0 ) ) )
return true ;
else
return false ;
}
static bool encx24j600_regmap_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case ERXHEAD :
case EDMACS :
case ETXSTAT :
case ETXWIRE :
case ECON1 : /* Can be modified via single byte cmds */
case ECON2 : /* Can be modified via single byte cmds */
case ESTAT :
case EIR : /* Can be modified via single byte cmds */
case MIRD :
case MISTAT :
return true ;
default :
break ;
}
return false ;
}
static bool encx24j600_regmap_precious ( struct device * dev , unsigned int reg )
{
/* single byte cmds are precious */
if ( ( ( reg > = 0xc0 ) & & ( reg < 0xc8 ) ) | |
( ( reg > = 0xca ) & & ( reg < 0xf0 ) ) )
return true ;
else
return false ;
}
static int regmap_encx24j600_phy_reg_read ( void * context , unsigned int reg ,
unsigned int * val )
{
struct encx24j600_context * ctx = context ;
int ret ;
unsigned int mistat ;
reg = MIREGADR_VAL | ( reg & PHREG_MASK ) ;
ret = regmap_write ( ctx - > regmap , MIREGADR , reg ) ;
if ( unlikely ( ret ) )
goto err_out ;
ret = regmap_write ( ctx - > regmap , MICMD , MIIRD ) ;
if ( unlikely ( ret ) )
goto err_out ;
usleep_range ( 26 , 100 ) ;
while ( ( ret = regmap_read ( ctx - > regmap , MISTAT , & mistat ) ! = 0 ) & &
( mistat & BUSY ) )
cpu_relax ( ) ;
if ( unlikely ( ret ) )
goto err_out ;
ret = regmap_write ( ctx - > regmap , MICMD , 0 ) ;
if ( unlikely ( ret ) )
goto err_out ;
ret = regmap_read ( ctx - > regmap , MIRD , val ) ;
err_out :
if ( ret )
pr_err ( " %s: error %d reading reg %02x \n " , __func__ , ret ,
reg & PHREG_MASK ) ;
return ret ;
}
static int regmap_encx24j600_phy_reg_write ( void * context , unsigned int reg ,
unsigned int val )
{
struct encx24j600_context * ctx = context ;
int ret ;
unsigned int mistat ;
reg = MIREGADR_VAL | ( reg & PHREG_MASK ) ;
ret = regmap_write ( ctx - > regmap , MIREGADR , reg ) ;
if ( unlikely ( ret ) )
goto err_out ;
ret = regmap_write ( ctx - > regmap , MIWR , val ) ;
if ( unlikely ( ret ) )
goto err_out ;
usleep_range ( 26 , 100 ) ;
while ( ( ret = regmap_read ( ctx - > regmap , MISTAT , & mistat ) ! = 0 ) & &
( mistat & BUSY ) )
cpu_relax ( ) ;
err_out :
if ( ret )
pr_err ( " %s: error %d writing reg %02x=%04x \n " , __func__ , ret ,
reg & PHREG_MASK , val ) ;
return ret ;
}
static bool encx24j600_phymap_readable ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case PHCON1 :
case PHSTAT1 :
case PHANA :
case PHANLPA :
case PHANE :
case PHCON2 :
case PHSTAT2 :
case PHSTAT3 :
return true ;
default :
return false ;
}
}
static bool encx24j600_phymap_writeable ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case PHCON1 :
case PHCON2 :
case PHANA :
return true ;
case PHSTAT1 :
case PHSTAT2 :
case PHSTAT3 :
case PHANLPA :
case PHANE :
default :
return false ;
}
}
static bool encx24j600_phymap_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case PHSTAT1 :
case PHSTAT2 :
case PHSTAT3 :
case PHANLPA :
case PHANE :
case PHCON2 :
return true ;
default :
return false ;
}
}
static struct regmap_config regcfg = {
. name = " reg " ,
. reg_bits = 8 ,
. val_bits = 16 ,
. max_register = 0xee ,
. reg_stride = 2 ,
. cache_type = REGCACHE_RBTREE ,
. val_format_endian = REGMAP_ENDIAN_LITTLE ,
. readable_reg = encx24j600_regmap_readable ,
. writeable_reg = encx24j600_regmap_writeable ,
. volatile_reg = encx24j600_regmap_volatile ,
. precious_reg = encx24j600_regmap_precious ,
. lock = regmap_lock_mutex ,
. unlock = regmap_unlock_mutex ,
} ;
static struct regmap_bus regmap_encx24j600 = {
. write = regmap_encx24j600_write ,
. read = regmap_encx24j600_read ,
. reg_update_bits = regmap_encx24j600_reg_update_bits ,
} ;
static struct regmap_config phycfg = {
. name = " phy " ,
. reg_bits = 8 ,
. val_bits = 16 ,
. max_register = 0x1f ,
. cache_type = REGCACHE_RBTREE ,
. val_format_endian = REGMAP_ENDIAN_LITTLE ,
. readable_reg = encx24j600_phymap_readable ,
. writeable_reg = encx24j600_phymap_writeable ,
. volatile_reg = encx24j600_phymap_volatile ,
} ;
2016-12-12 14:29:09 +01:00
2015-10-06 16:37:46 -04:00
static struct regmap_bus phymap_encx24j600 = {
. reg_write = regmap_encx24j600_phy_reg_write ,
. reg_read = regmap_encx24j600_phy_reg_read ,
} ;
void devm_regmap_init_encx24j600 ( struct device * dev ,
struct encx24j600_context * ctx )
{
mutex_init ( & ctx - > mutex ) ;
regcfg . lock_arg = ctx ;
ctx - > regmap = devm_regmap_init ( dev , & regmap_encx24j600 , ctx , & regcfg ) ;
ctx - > phymap = devm_regmap_init ( dev , & phymap_encx24j600 , ctx , & phycfg ) ;
}
EXPORT_SYMBOL_GPL ( devm_regmap_init_encx24j600 ) ;
MODULE_LICENSE ( " GPL " ) ;