2017-02-01 19:53:41 +03:00
/*
* FSI core driver
*
* Copyright ( C ) IBM Corporation 2016
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2017-06-07 00:08:42 +03:00
# include <linux/crc4.h>
2017-02-01 19:53:41 +03:00
# include <linux/device.h>
# include <linux/fsi.h>
2017-06-07 00:08:36 +03:00
# include <linux/idr.h>
2017-02-01 19:53:41 +03:00
# include <linux/module.h>
2018-02-12 08:15:45 +03:00
# include <linux/of.h>
2017-06-07 00:08:42 +03:00
# include <linux/slab.h>
2017-06-07 00:08:44 +03:00
# include <linux/bitops.h>
2017-02-01 19:53:41 +03:00
2017-06-07 00:08:36 +03:00
# include "fsi-master.h"
2017-06-07 00:08:51 +03:00
# define CREATE_TRACE_POINTS
# include <trace/events/fsi.h>
2017-06-07 00:08:44 +03:00
# define FSI_SLAVE_CONF_NEXT_MASK GENMASK(31, 31)
# define FSI_SLAVE_CONF_SLOTS_MASK GENMASK(23, 16)
# define FSI_SLAVE_CONF_SLOTS_SHIFT 16
# define FSI_SLAVE_CONF_VERSION_MASK GENMASK(15, 12)
# define FSI_SLAVE_CONF_VERSION_SHIFT 12
# define FSI_SLAVE_CONF_TYPE_MASK GENMASK(11, 4)
# define FSI_SLAVE_CONF_TYPE_SHIFT 4
# define FSI_SLAVE_CONF_CRC_SHIFT 4
# define FSI_SLAVE_CONF_CRC_MASK GENMASK(3, 0)
# define FSI_SLAVE_CONF_DATA_BITS 28
2017-06-07 00:08:45 +03:00
# define FSI_PEEK_BASE 0x410
2017-06-07 00:08:44 +03:00
static const int engine_page_size = 0x400 ;
2017-06-07 00:08:43 +03:00
# define FSI_SLAVE_BASE 0x800
/*
* FSI slave engine control register offsets
*/
2017-06-07 00:08:52 +03:00
# define FSI_SMODE 0x0 /* R/W: Mode register */
# define FSI_SISC 0x8 /* R/W: Interrupt condition */
# define FSI_SSTAT 0x14 /* R : Slave status */
2017-06-07 00:08:58 +03:00
# define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
2017-06-07 00:08:43 +03:00
/*
* SMODE fields
*/
# define FSI_SMODE_WSC 0x80000000 /* Warm start done */
# define FSI_SMODE_ECRC 0x20000000 /* Hw CRC check */
# define FSI_SMODE_SID_SHIFT 24 /* ID shift */
# define FSI_SMODE_SID_MASK 3 /* ID Mask */
# define FSI_SMODE_ED_SHIFT 20 /* Echo delay shift */
# define FSI_SMODE_ED_MASK 0xf /* Echo delay mask */
# define FSI_SMODE_SD_SHIFT 16 /* Send delay shift */
# define FSI_SMODE_SD_MASK 0xf /* Send delay mask */
# define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
# define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
2017-06-07 00:08:58 +03:00
/*
* LLMODE fields
*/
# define FSI_LLMODE_ASYNC 0x1
2017-06-07 00:08:42 +03:00
# define FSI_SLAVE_SIZE_23b 0x800000
2017-06-07 00:08:36 +03:00
static DEFINE_IDA ( master_ida ) ;
2017-06-07 00:08:37 +03:00
struct fsi_slave {
struct device dev ;
struct fsi_master * master ;
int id ;
int link ;
uint32_t size ; /* size of slave address space */
} ;
2017-06-07 00:08:46 +03:00
# define to_fsi_master(d) container_of(d, struct fsi_master, dev)
2017-06-07 00:08:37 +03:00
# define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
2017-06-07 00:08:52 +03:00
static const int slave_retries = 2 ;
static int discard_errors ;
2017-06-07 00:08:40 +03:00
static int fsi_master_read ( struct fsi_master * master , int link ,
uint8_t slave_id , uint32_t addr , void * val , size_t size ) ;
static int fsi_master_write ( struct fsi_master * master , int link ,
uint8_t slave_id , uint32_t addr , const void * val , size_t size ) ;
2017-06-07 00:08:52 +03:00
static int fsi_master_break ( struct fsi_master * master , int link ) ;
2017-06-07 00:08:45 +03:00
/*
* fsi_device_read ( ) / fsi_device_write ( ) / fsi_device_peek ( )
*
* FSI endpoint - device support
*
* Read / write / peek accessors for a client
*
* Parameters :
* dev : Structure passed to FSI client device drivers on probe ( ) .
* addr : FSI address of given device . Client should pass in its base address
* plus desired offset to access its register space .
* val : For read / peek this is the value read at the specified address . For
* write this is value to write to the specified address .
* The data in val must be FSI bus endian ( big endian ) .
* size : Size in bytes of the operation . Sizes supported are 1 , 2 and 4 bytes .
* Addresses must be aligned on size boundaries or an error will result .
*/
int fsi_device_read ( struct fsi_device * dev , uint32_t addr , void * val ,
size_t size )
{
if ( addr > dev - > size | | size > dev - > size | | addr > dev - > size - size )
return - EINVAL ;
return fsi_slave_read ( dev - > slave , dev - > addr + addr , val , size ) ;
}
EXPORT_SYMBOL_GPL ( fsi_device_read ) ;
int fsi_device_write ( struct fsi_device * dev , uint32_t addr , const void * val ,
size_t size )
{
if ( addr > dev - > size | | size > dev - > size | | addr > dev - > size - size )
return - EINVAL ;
return fsi_slave_write ( dev - > slave , dev - > addr + addr , val , size ) ;
}
EXPORT_SYMBOL_GPL ( fsi_device_write ) ;
int fsi_device_peek ( struct fsi_device * dev , void * val )
{
uint32_t addr = FSI_PEEK_BASE + ( ( dev - > unit - 2 ) * sizeof ( uint32_t ) ) ;
2017-06-07 00:08:40 +03:00
2017-06-07 00:08:45 +03:00
return fsi_slave_read ( dev - > slave , addr , val , sizeof ( uint32_t ) ) ;
}
2017-06-07 00:08:44 +03:00
static void fsi_device_release ( struct device * _device )
{
struct fsi_device * device = to_fsi_dev ( _device ) ;
2018-02-12 08:15:45 +03:00
of_node_put ( device - > dev . of_node ) ;
2017-06-07 00:08:44 +03:00
kfree ( device ) ;
}
static struct fsi_device * fsi_create_device ( struct fsi_slave * slave )
{
struct fsi_device * dev ;
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return NULL ;
dev - > dev . parent = & slave - > dev ;
dev - > dev . bus = & fsi_bus_type ;
dev - > dev . release = fsi_device_release ;
return dev ;
}
2017-06-07 00:08:38 +03:00
/* FSI slave support */
2017-06-07 00:08:40 +03:00
static int fsi_slave_calc_addr ( struct fsi_slave * slave , uint32_t * addrp ,
uint8_t * idp )
{
uint32_t addr = * addrp ;
uint8_t id = * idp ;
if ( addr > slave - > size )
return - EINVAL ;
/* For 23 bit addressing, we encode the extra two bits in the slave
* id ( and the slave ' s actual ID needs to be 0 ) .
*/
if ( addr > 0x1fffff ) {
if ( slave - > id ! = 0 )
return - EINVAL ;
id = ( addr > > 21 ) & 0x3 ;
addr & = 0x1fffff ;
}
* addrp = addr ;
* idp = id ;
return 0 ;
}
2017-10-03 11:31:45 +03:00
static int fsi_slave_report_and_clear_errors ( struct fsi_slave * slave )
2017-06-07 00:08:52 +03:00
{
struct fsi_master * master = slave - > master ;
uint32_t irq , stat ;
int rc , link ;
uint8_t id ;
link = slave - > link ;
id = slave - > id ;
rc = fsi_master_read ( master , link , id , FSI_SLAVE_BASE + FSI_SISC ,
& irq , sizeof ( irq ) ) ;
if ( rc )
return rc ;
rc = fsi_master_read ( master , link , id , FSI_SLAVE_BASE + FSI_SSTAT ,
& stat , sizeof ( stat ) ) ;
if ( rc )
return rc ;
2018-02-12 08:15:46 +03:00
dev_dbg ( & slave - > dev , " status: 0x%08x, sisc: 0x%08x \n " ,
2017-06-07 00:08:52 +03:00
be32_to_cpu ( stat ) , be32_to_cpu ( irq ) ) ;
/* clear interrupts */
return fsi_master_write ( master , link , id , FSI_SLAVE_BASE + FSI_SISC ,
& irq , sizeof ( irq ) ) ;
}
static int fsi_slave_set_smode ( struct fsi_master * master , int link , int id ) ;
2017-10-03 11:31:45 +03:00
static int fsi_slave_handle_error ( struct fsi_slave * slave , bool write ,
uint32_t addr , size_t size )
2017-06-07 00:08:52 +03:00
{
struct fsi_master * master = slave - > master ;
int rc , link ;
uint32_t reg ;
uint8_t id ;
if ( discard_errors )
return - 1 ;
link = slave - > link ;
id = slave - > id ;
dev_dbg ( & slave - > dev , " handling error on %s to 0x%08x[%zd] " ,
write ? " write " : " read " , addr , size ) ;
/* try a simple clear of error conditions, which may fail if we've lost
* communication with the slave
*/
rc = fsi_slave_report_and_clear_errors ( slave ) ;
if ( ! rc )
return 0 ;
/* send a TERM and retry */
if ( master - > term ) {
rc = master - > term ( master , link , id ) ;
if ( ! rc ) {
rc = fsi_master_read ( master , link , id , 0 ,
& reg , sizeof ( reg ) ) ;
if ( ! rc )
rc = fsi_slave_report_and_clear_errors ( slave ) ;
if ( ! rc )
return 0 ;
}
}
/* getting serious, reset the slave via BREAK */
rc = fsi_master_break ( master , link ) ;
if ( rc )
return rc ;
rc = fsi_slave_set_smode ( master , link , id ) ;
if ( rc )
return rc ;
return fsi_slave_report_and_clear_errors ( slave ) ;
}
2017-06-07 00:08:50 +03:00
int fsi_slave_read ( struct fsi_slave * slave , uint32_t addr ,
2017-06-07 00:08:40 +03:00
void * val , size_t size )
{
uint8_t id = slave - > id ;
2017-06-07 00:08:52 +03:00
int rc , err_rc , i ;
2017-06-07 00:08:40 +03:00
rc = fsi_slave_calc_addr ( slave , & addr , & id ) ;
if ( rc )
return rc ;
2017-06-07 00:08:52 +03:00
for ( i = 0 ; i < slave_retries ; i + + ) {
rc = fsi_master_read ( slave - > master , slave - > link ,
id , addr , val , size ) ;
if ( ! rc )
break ;
err_rc = fsi_slave_handle_error ( slave , false , addr , size ) ;
if ( err_rc )
break ;
}
return rc ;
2017-06-07 00:08:40 +03:00
}
2017-06-07 00:08:50 +03:00
EXPORT_SYMBOL_GPL ( fsi_slave_read ) ;
2017-06-07 00:08:40 +03:00
2017-06-07 00:08:50 +03:00
int fsi_slave_write ( struct fsi_slave * slave , uint32_t addr ,
2017-06-07 00:08:40 +03:00
const void * val , size_t size )
{
uint8_t id = slave - > id ;
2017-06-07 00:08:52 +03:00
int rc , err_rc , i ;
2017-06-07 00:08:40 +03:00
rc = fsi_slave_calc_addr ( slave , & addr , & id ) ;
if ( rc )
return rc ;
2017-06-07 00:08:52 +03:00
for ( i = 0 ; i < slave_retries ; i + + ) {
rc = fsi_master_write ( slave - > master , slave - > link ,
id , addr , val , size ) ;
if ( ! rc )
break ;
err_rc = fsi_slave_handle_error ( slave , true , addr , size ) ;
if ( err_rc )
break ;
}
return rc ;
2017-06-07 00:08:40 +03:00
}
2017-06-07 00:08:50 +03:00
EXPORT_SYMBOL_GPL ( fsi_slave_write ) ;
extern int fsi_slave_claim_range ( struct fsi_slave * slave ,
uint32_t addr , uint32_t size )
{
if ( addr + size < addr )
return - EINVAL ;
if ( addr + size > slave - > size )
return - EINVAL ;
/* todo: check for overlapping claims */
return 0 ;
}
EXPORT_SYMBOL_GPL ( fsi_slave_claim_range ) ;
extern void fsi_slave_release_range ( struct fsi_slave * slave ,
uint32_t addr , uint32_t size )
{
}
EXPORT_SYMBOL_GPL ( fsi_slave_release_range ) ;
2017-06-07 00:08:40 +03:00
2018-02-12 08:15:45 +03:00
static bool fsi_device_node_matches ( struct device * dev , struct device_node * np ,
uint32_t addr , uint32_t size )
{
unsigned int len , na , ns ;
const __be32 * prop ;
uint32_t psize ;
na = of_n_addr_cells ( np ) ;
ns = of_n_size_cells ( np ) ;
if ( na ! = 1 | | ns ! = 1 )
return false ;
prop = of_get_property ( np , " reg " , & len ) ;
if ( ! prop | | len ! = 8 )
return false ;
if ( of_read_number ( prop , 1 ) ! = addr )
return false ;
psize = of_read_number ( prop + 1 , 1 ) ;
if ( psize ! = size ) {
dev_warn ( dev ,
" node %s matches probed address, but not size (got 0x%x, expected 0x%x) " ,
of_node_full_name ( np ) , psize , size ) ;
}
return true ;
}
/* Find a matching node for the slave engine at @address, using @size bytes
* of space . Returns NULL if not found , or a matching node with refcount
* already incremented .
*/
static struct device_node * fsi_device_find_of_node ( struct fsi_device * dev )
{
struct device_node * parent , * np ;
parent = dev_of_node ( & dev - > slave - > dev ) ;
if ( ! parent )
return NULL ;
for_each_child_of_node ( parent , np ) {
if ( fsi_device_node_matches ( & dev - > dev , np ,
dev - > addr , dev - > size ) )
return np ;
}
return NULL ;
}
2017-06-07 00:08:44 +03:00
static int fsi_slave_scan ( struct fsi_slave * slave )
{
uint32_t engine_addr ;
uint32_t conf ;
int rc , i ;
/*
* scan engines
*
* We keep the peek mode and slave engines for the core ; so start
* at the third slot in the configuration table . We also need to
* skip the chip ID entry at the start of the address space .
*/
engine_addr = engine_page_size * 3 ;
for ( i = 2 ; i < engine_page_size / sizeof ( uint32_t ) ; i + + ) {
uint8_t slots , version , type , crc ;
struct fsi_device * dev ;
rc = fsi_slave_read ( slave , ( i + 1 ) * sizeof ( conf ) ,
& conf , sizeof ( conf ) ) ;
if ( rc ) {
dev_warn ( & slave - > dev ,
" error reading slave registers \n " ) ;
return - 1 ;
}
conf = be32_to_cpu ( conf ) ;
crc = crc4 ( 0 , conf , 32 ) ;
if ( crc ) {
dev_warn ( & slave - > dev ,
" crc error in slave register at 0x%04x \n " ,
i ) ;
return - 1 ;
}
slots = ( conf & FSI_SLAVE_CONF_SLOTS_MASK )
> > FSI_SLAVE_CONF_SLOTS_SHIFT ;
version = ( conf & FSI_SLAVE_CONF_VERSION_MASK )
> > FSI_SLAVE_CONF_VERSION_SHIFT ;
type = ( conf & FSI_SLAVE_CONF_TYPE_MASK )
> > FSI_SLAVE_CONF_TYPE_SHIFT ;
/*
* Unused address areas are marked by a zero type value ; this
* skips the defined address areas
*/
if ( type ! = 0 & & slots ! = 0 ) {
/* create device */
dev = fsi_create_device ( slave ) ;
if ( ! dev )
return - ENOMEM ;
dev - > slave = slave ;
dev - > engine_type = type ;
dev - > version = version ;
dev - > unit = i ;
dev - > addr = engine_addr ;
dev - > size = slots * engine_page_size ;
dev_dbg ( & slave - > dev ,
" engine[%i]: type %x, version %x, addr %x size %x \n " ,
dev - > unit , dev - > engine_type , version ,
dev - > addr , dev - > size ) ;
dev_set_name ( & dev - > dev , " %02x:%02x:%02x:%02x " ,
slave - > master - > idx , slave - > link ,
slave - > id , i - 2 ) ;
2018-02-12 08:15:45 +03:00
dev - > dev . of_node = fsi_device_find_of_node ( dev ) ;
2017-06-07 00:08:44 +03:00
rc = device_register ( & dev - > dev ) ;
if ( rc ) {
dev_warn ( & slave - > dev , " add failed: %d \n " , rc ) ;
put_device ( & dev - > dev ) ;
}
}
engine_addr + = slots * engine_page_size ;
if ( ! ( conf & FSI_SLAVE_CONF_NEXT_MASK ) )
break ;
}
return 0 ;
}
2017-06-07 00:08:49 +03:00
static ssize_t fsi_slave_sysfs_raw_read ( struct file * file ,
struct kobject * kobj , struct bin_attribute * attr , char * buf ,
loff_t off , size_t count )
{
struct fsi_slave * slave = to_fsi_slave ( kobj_to_dev ( kobj ) ) ;
size_t total_len , read_len ;
int rc ;
if ( off < 0 )
return - EINVAL ;
if ( off > 0xffffffff | | count > 0xffffffff | | off + count > 0xffffffff )
return - EINVAL ;
for ( total_len = 0 ; total_len < count ; total_len + = read_len ) {
read_len = min_t ( size_t , count , 4 ) ;
read_len - = off & 0x3 ;
rc = fsi_slave_read ( slave , off , buf + total_len , read_len ) ;
if ( rc )
return rc ;
off + = read_len ;
}
return count ;
}
static ssize_t fsi_slave_sysfs_raw_write ( struct file * file ,
struct kobject * kobj , struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
struct fsi_slave * slave = to_fsi_slave ( kobj_to_dev ( kobj ) ) ;
size_t total_len , write_len ;
int rc ;
if ( off < 0 )
return - EINVAL ;
if ( off > 0xffffffff | | count > 0xffffffff | | off + count > 0xffffffff )
return - EINVAL ;
for ( total_len = 0 ; total_len < count ; total_len + = write_len ) {
write_len = min_t ( size_t , count , 4 ) ;
write_len - = off & 0x3 ;
rc = fsi_slave_write ( slave , off , buf + total_len , write_len ) ;
if ( rc )
return rc ;
off + = write_len ;
}
return count ;
}
2017-08-02 17:09:32 +03:00
static const struct bin_attribute fsi_slave_raw_attr = {
2017-06-07 00:08:49 +03:00
. attr = {
. name = " raw " ,
. mode = 0600 ,
} ,
. size = 0 ,
. read = fsi_slave_sysfs_raw_read ,
. write = fsi_slave_sysfs_raw_write ,
} ;
static ssize_t fsi_slave_sysfs_term_write ( struct file * file ,
struct kobject * kobj , struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
struct fsi_slave * slave = to_fsi_slave ( kobj_to_dev ( kobj ) ) ;
struct fsi_master * master = slave - > master ;
if ( ! master - > term )
return - ENODEV ;
master - > term ( master , slave - > link , slave - > id ) ;
return count ;
}
2017-08-02 17:09:32 +03:00
static const struct bin_attribute fsi_slave_term_attr = {
2017-06-07 00:08:49 +03:00
. attr = {
. name = " term " ,
. mode = 0200 ,
} ,
. size = 0 ,
. write = fsi_slave_sysfs_term_write ,
} ;
2017-06-07 00:08:43 +03:00
/* Encode slave local bus echo delay */
static inline uint32_t fsi_smode_echodly ( int x )
{
return ( x & FSI_SMODE_ED_MASK ) < < FSI_SMODE_ED_SHIFT ;
}
/* Encode slave local bus send delay */
static inline uint32_t fsi_smode_senddly ( int x )
{
return ( x & FSI_SMODE_SD_MASK ) < < FSI_SMODE_SD_SHIFT ;
}
/* Encode slave local bus clock rate ratio */
static inline uint32_t fsi_smode_lbcrr ( int x )
{
return ( x & FSI_SMODE_LBCRR_MASK ) < < FSI_SMODE_LBCRR_SHIFT ;
}
/* Encode slave ID */
static inline uint32_t fsi_smode_sid ( int x )
{
return ( x & FSI_SMODE_SID_MASK ) < < FSI_SMODE_SID_SHIFT ;
}
2017-06-20 23:43:42 +03:00
static uint32_t fsi_slave_smode ( int id )
2017-06-07 00:08:43 +03:00
{
return FSI_SMODE_WSC | FSI_SMODE_ECRC
| fsi_smode_sid ( id )
| fsi_smode_echodly ( 0xf ) | fsi_smode_senddly ( 0xf )
| fsi_smode_lbcrr ( 0x8 ) ;
}
static int fsi_slave_set_smode ( struct fsi_master * master , int link , int id )
{
uint32_t smode ;
/* set our smode register with the slave ID field to 0; this enables
* extended slave addressing
*/
smode = fsi_slave_smode ( id ) ;
smode = cpu_to_be32 ( smode ) ;
return fsi_master_write ( master , link , id , FSI_SLAVE_BASE + FSI_SMODE ,
& smode , sizeof ( smode ) ) ;
}
2017-06-07 00:08:42 +03:00
static void fsi_slave_release ( struct device * dev )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
2018-02-12 08:15:45 +03:00
of_node_put ( dev - > of_node ) ;
2017-06-07 00:08:42 +03:00
kfree ( slave ) ;
}
2018-02-12 08:15:45 +03:00
static bool fsi_slave_node_matches ( struct device_node * np ,
int link , uint8_t id )
{
unsigned int len , na , ns ;
const __be32 * prop ;
na = of_n_addr_cells ( np ) ;
ns = of_n_size_cells ( np ) ;
/* Ensure we have the correct format for addresses and sizes in
* reg properties
*/
if ( na ! = 2 | | ns ! = 0 )
return false ;
prop = of_get_property ( np , " reg " , & len ) ;
if ( ! prop | | len ! = 8 )
return false ;
return ( of_read_number ( prop , 1 ) = = link ) & &
( of_read_number ( prop + 1 , 1 ) = = id ) ;
}
/* Find a matching node for the slave at (link, id). Returns NULL if none
* found , or a matching node with refcount already incremented .
*/
static struct device_node * fsi_slave_find_of_node ( struct fsi_master * master ,
int link , uint8_t id )
{
struct device_node * parent , * np ;
parent = dev_of_node ( & master - > dev ) ;
if ( ! parent )
return NULL ;
for_each_child_of_node ( parent , np ) {
if ( fsi_slave_node_matches ( np , link , id ) )
return np ;
}
return NULL ;
}
2017-06-07 00:08:38 +03:00
static int fsi_slave_init ( struct fsi_master * master , int link , uint8_t id )
{
2017-06-07 00:08:58 +03:00
uint32_t chip_id , llmode ;
2017-06-07 00:08:42 +03:00
struct fsi_slave * slave ;
uint8_t crc ;
int rc ;
/* Currently, we only support single slaves on a link, and use the
* full 23 - bit address range
*/
if ( id ! = 0 )
return - EINVAL ;
rc = fsi_master_read ( master , link , id , 0 , & chip_id , sizeof ( chip_id ) ) ;
if ( rc ) {
dev_dbg ( & master - > dev , " can't read slave %02x:%02x %d \n " ,
link , id , rc ) ;
return - ENODEV ;
}
chip_id = be32_to_cpu ( chip_id ) ;
crc = crc4 ( 0 , chip_id , 32 ) ;
if ( crc ) {
dev_warn ( & master - > dev , " slave %02x:%02x invalid chip id CRC! \n " ,
link , id ) ;
return - EIO ;
}
2018-02-12 08:15:46 +03:00
dev_dbg ( & master - > dev , " fsi: found chip %08x at %02x:%02x:%02x \n " ,
2017-06-07 00:08:42 +03:00
chip_id , master - > idx , link , id ) ;
2017-06-07 00:08:43 +03:00
rc = fsi_slave_set_smode ( master , link , id ) ;
if ( rc ) {
dev_warn ( & master - > dev ,
" can't set smode on slave:%02x:%02x %d \n " ,
link , id , rc ) ;
return - ENODEV ;
}
2017-06-07 00:08:58 +03:00
/* If we're behind a master that doesn't provide a self-running bus
* clock , put the slave into async mode
*/
if ( master - > flags & FSI_MASTER_FLAG_SWCLOCK ) {
llmode = cpu_to_be32 ( FSI_LLMODE_ASYNC ) ;
rc = fsi_master_write ( master , link , id ,
FSI_SLAVE_BASE + FSI_LLMODE ,
& llmode , sizeof ( llmode ) ) ;
if ( rc )
dev_warn ( & master - > dev ,
" can't set llmode on slave:%02x:%02x %d \n " ,
link , id , rc ) ;
}
2017-06-07 00:08:42 +03:00
/* We can communicate with a slave; create the slave device and
* register .
*/
slave = kzalloc ( sizeof ( * slave ) , GFP_KERNEL ) ;
if ( ! slave )
return - ENOMEM ;
slave - > master = master ;
slave - > dev . parent = & master - > dev ;
2018-02-12 08:15:45 +03:00
slave - > dev . of_node = fsi_slave_find_of_node ( master , link , id ) ;
2017-06-07 00:08:42 +03:00
slave - > dev . release = fsi_slave_release ;
slave - > link = link ;
slave - > id = id ;
slave - > size = FSI_SLAVE_SIZE_23b ;
dev_set_name ( & slave - > dev , " slave@%02x:%02x " , link , id ) ;
rc = device_register ( & slave - > dev ) ;
if ( rc < 0 ) {
dev_warn ( & master - > dev , " failed to create slave device: %d \n " ,
rc ) ;
put_device ( & slave - > dev ) ;
return rc ;
}
2017-06-07 00:08:49 +03:00
rc = device_create_bin_file ( & slave - > dev , & fsi_slave_raw_attr ) ;
if ( rc )
dev_warn ( & slave - > dev , " failed to create raw attr: %d \n " , rc ) ;
rc = device_create_bin_file ( & slave - > dev , & fsi_slave_term_attr ) ;
if ( rc )
dev_warn ( & slave - > dev , " failed to create term attr: %d \n " , rc ) ;
2017-06-07 00:08:44 +03:00
rc = fsi_slave_scan ( slave ) ;
if ( rc )
dev_dbg ( & master - > dev , " failed during slave scan with: %d \n " ,
rc ) ;
2017-06-07 00:08:38 +03:00
2017-06-07 00:08:42 +03:00
return rc ;
2017-06-07 00:08:38 +03:00
}
2017-06-07 00:08:36 +03:00
/* FSI master support */
2017-06-07 00:08:40 +03:00
static int fsi_check_access ( uint32_t addr , size_t size )
{
2018-02-12 08:15:43 +03:00
if ( size = = 4 ) {
if ( addr & 0x3 )
return - EINVAL ;
} else if ( size = = 2 ) {
if ( addr & 0x1 )
return - EINVAL ;
} else if ( size ! = 1 )
2017-06-07 00:08:40 +03:00
return - EINVAL ;
return 0 ;
}
static int fsi_master_read ( struct fsi_master * master , int link ,
uint8_t slave_id , uint32_t addr , void * val , size_t size )
{
int rc ;
2017-06-07 00:08:51 +03:00
trace_fsi_master_read ( master , link , slave_id , addr , size ) ;
2017-06-07 00:08:40 +03:00
rc = fsi_check_access ( addr , size ) ;
2017-06-07 00:08:51 +03:00
if ( ! rc )
rc = master - > read ( master , link , slave_id , addr , val , size ) ;
trace_fsi_master_rw_result ( master , link , slave_id , addr , size ,
false , val , rc ) ;
2017-06-07 00:08:40 +03:00
2017-06-07 00:08:51 +03:00
return rc ;
2017-06-07 00:08:40 +03:00
}
static int fsi_master_write ( struct fsi_master * master , int link ,
uint8_t slave_id , uint32_t addr , const void * val , size_t size )
{
int rc ;
2017-06-07 00:08:51 +03:00
trace_fsi_master_write ( master , link , slave_id , addr , size , val ) ;
2017-06-07 00:08:40 +03:00
rc = fsi_check_access ( addr , size ) ;
2017-06-07 00:08:51 +03:00
if ( ! rc )
rc = master - > write ( master , link , slave_id , addr , val , size ) ;
2017-06-07 00:08:40 +03:00
2017-06-07 00:08:51 +03:00
trace_fsi_master_rw_result ( master , link , slave_id , addr , size ,
true , val , rc ) ;
return rc ;
2017-06-07 00:08:40 +03:00
}
2017-06-07 00:08:41 +03:00
static int fsi_master_link_enable ( struct fsi_master * master , int link )
{
if ( master - > link_enable )
return master - > link_enable ( master , link ) ;
return 0 ;
}
/*
* Issue a break command on this link
*/
static int fsi_master_break ( struct fsi_master * master , int link )
{
2017-06-07 00:08:51 +03:00
trace_fsi_master_break ( master , link ) ;
2017-06-07 00:08:41 +03:00
if ( master - > send_break )
return master - > send_break ( master , link ) ;
return 0 ;
}
2017-06-07 00:08:38 +03:00
static int fsi_master_scan ( struct fsi_master * master )
{
2017-06-07 00:08:41 +03:00
int link , rc ;
for ( link = 0 ; link < master - > n_links ; link + + ) {
rc = fsi_master_link_enable ( master , link ) ;
if ( rc ) {
dev_dbg ( & master - > dev ,
" enable link %d failed: %d \n " , link , rc ) ;
continue ;
}
rc = fsi_master_break ( master , link ) ;
if ( rc ) {
dev_dbg ( & master - > dev ,
" break to link %d failed: %d \n " , link , rc ) ;
continue ;
}
2017-06-07 00:08:38 +03:00
fsi_slave_init ( master , link , 0 ) ;
2017-06-07 00:08:41 +03:00
}
2017-06-07 00:08:38 +03:00
return 0 ;
}
2017-06-07 00:08:46 +03:00
static int fsi_slave_remove_device ( struct device * dev , void * arg )
{
device_unregister ( dev ) ;
return 0 ;
}
static int fsi_master_remove_slave ( struct device * dev , void * arg )
{
device_for_each_child ( dev , NULL , fsi_slave_remove_device ) ;
device_unregister ( dev ) ;
return 0 ;
}
static void fsi_master_unscan ( struct fsi_master * master )
{
device_for_each_child ( & master - > dev , NULL , fsi_master_remove_slave ) ;
}
2018-02-12 08:15:40 +03:00
int fsi_master_rescan ( struct fsi_master * master )
{
fsi_master_unscan ( master ) ;
return fsi_master_scan ( master ) ;
}
EXPORT_SYMBOL_GPL ( fsi_master_rescan ) ;
2017-06-07 00:08:46 +03:00
static ssize_t master_rescan_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct fsi_master * master = to_fsi_master ( dev ) ;
int rc ;
2018-02-12 08:15:40 +03:00
rc = fsi_master_rescan ( master ) ;
2017-06-07 00:08:46 +03:00
if ( rc < 0 )
return rc ;
return count ;
}
static DEVICE_ATTR ( rescan , 0200 , NULL , master_rescan_store ) ;
2017-06-07 00:08:49 +03:00
static ssize_t master_break_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct fsi_master * master = to_fsi_master ( dev ) ;
fsi_master_break ( master , 0 ) ;
return count ;
}
static DEVICE_ATTR ( break , 0200 , NULL , master_break_store ) ;
2017-06-07 00:08:36 +03:00
int fsi_master_register ( struct fsi_master * master )
{
int rc ;
2018-02-12 08:15:49 +03:00
struct device_node * np ;
2017-06-07 00:08:36 +03:00
if ( ! master )
return - EINVAL ;
master - > idx = ida_simple_get ( & master_ida , 0 , INT_MAX , GFP_KERNEL ) ;
dev_set_name ( & master - > dev , " fsi%d " , master - > idx ) ;
rc = device_register ( & master - > dev ) ;
2017-06-07 00:08:38 +03:00
if ( rc ) {
2017-06-07 00:08:36 +03:00
ida_simple_remove ( & master_ida , master - > idx ) ;
2017-06-07 00:08:38 +03:00
return rc ;
}
2017-06-07 00:08:36 +03:00
2017-06-07 00:08:46 +03:00
rc = device_create_file ( & master - > dev , & dev_attr_rescan ) ;
if ( rc ) {
device_unregister ( & master - > dev ) ;
ida_simple_remove ( & master_ida , master - > idx ) ;
return rc ;
}
2017-06-07 00:08:49 +03:00
rc = device_create_file ( & master - > dev , & dev_attr_break ) ;
if ( rc ) {
device_unregister ( & master - > dev ) ;
ida_simple_remove ( & master_ida , master - > idx ) ;
return rc ;
}
2018-02-12 08:15:49 +03:00
np = dev_of_node ( & master - > dev ) ;
if ( ! of_property_read_bool ( np , " no-scan-on-init " ) )
fsi_master_scan ( master ) ;
2017-06-07 00:08:46 +03:00
2017-06-07 00:08:38 +03:00
return 0 ;
2017-06-07 00:08:36 +03:00
}
EXPORT_SYMBOL_GPL ( fsi_master_register ) ;
void fsi_master_unregister ( struct fsi_master * master )
{
if ( master - > idx > = 0 ) {
ida_simple_remove ( & master_ida , master - > idx ) ;
master - > idx = - 1 ;
}
2017-06-07 00:08:46 +03:00
fsi_master_unscan ( master ) ;
2017-06-07 00:08:36 +03:00
device_unregister ( & master - > dev ) ;
}
EXPORT_SYMBOL_GPL ( fsi_master_unregister ) ;
2017-02-01 19:53:41 +03:00
/* FSI core & Linux bus type definitions */
2017-02-01 19:53:43 +03:00
static int fsi_bus_match ( struct device * dev , struct device_driver * drv )
{
struct fsi_device * fsi_dev = to_fsi_dev ( dev ) ;
struct fsi_driver * fsi_drv = to_fsi_drv ( drv ) ;
const struct fsi_device_id * id ;
if ( ! fsi_drv - > id_table )
return 0 ;
for ( id = fsi_drv - > id_table ; id - > engine_type ; id + + ) {
if ( id - > engine_type ! = fsi_dev - > engine_type )
continue ;
if ( id - > version = = FSI_VERSION_ANY | |
id - > version = = fsi_dev - > version )
return 1 ;
}
return 0 ;
}
2017-06-07 00:08:48 +03:00
int fsi_driver_register ( struct fsi_driver * fsi_drv )
{
if ( ! fsi_drv )
return - EINVAL ;
if ( ! fsi_drv - > id_table )
return - EINVAL ;
return driver_register ( & fsi_drv - > drv ) ;
}
EXPORT_SYMBOL_GPL ( fsi_driver_register ) ;
void fsi_driver_unregister ( struct fsi_driver * fsi_drv )
{
driver_unregister ( & fsi_drv - > drv ) ;
}
EXPORT_SYMBOL_GPL ( fsi_driver_unregister ) ;
2017-02-01 19:53:41 +03:00
struct bus_type fsi_bus_type = {
. name = " fsi " ,
2017-02-01 19:53:43 +03:00
. match = fsi_bus_match ,
2017-02-01 19:53:41 +03:00
} ;
EXPORT_SYMBOL_GPL ( fsi_bus_type ) ;
2017-07-11 10:30:39 +03:00
static int __init fsi_init ( void )
2017-02-01 19:53:41 +03:00
{
return bus_register ( & fsi_bus_type ) ;
}
2017-07-11 10:30:39 +03:00
postcore_initcall ( fsi_init ) ;
2017-02-01 19:53:41 +03:00
static void fsi_exit ( void )
{
bus_unregister ( & fsi_bus_type ) ;
}
module_exit ( fsi_exit ) ;
2017-06-07 00:08:52 +03:00
module_param ( discard_errors , int , 0664 ) ;
2017-06-07 00:08:59 +03:00
MODULE_LICENSE ( " GPL " ) ;
2017-06-07 00:08:52 +03:00
MODULE_PARM_DESC ( discard_errors , " Don't invoke error handling on bus accesses " ) ;