2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-02-01 19:53:41 +03:00
/*
* FSI core driver
*
* Copyright ( C ) IBM Corporation 2016
*
2018-06-21 05:34:22 +03:00
* TODO :
* - Rework topology
* - s / chip_id / chip_loc
* - s / cfam / chip ( cfam_id - > chip_id etc . . . )
2017-02-01 19:53:41 +03:00
*/
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>
2018-06-21 05:34:22 +03:00
# include <linux/cdev.h>
# include <linux/fs.h>
# include <linux/uaccess.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 */
2020-05-20 21:17:07 +03:00
# define FSI_SLBUS 0x30 /* W : LBUS Ownership */
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 */
2020-05-20 21:17:07 +03:00
/*
* SLBUS fields
*/
# define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */
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 ;
2018-06-21 05:34:22 +03:00
struct cdev cdev ;
int cdev_idx ;
int id ; /* FSI address */
int link ; /* FSI link# */
u32 cfam_id ;
2018-06-20 08:16:31 +03:00
int chip_id ;
2017-06-07 00:08:37 +03:00
uint32_t size ; /* size of slave address space */
2018-05-29 08:01:07 +03:00
u8 t_send_delay ;
u8 t_echo_delay ;
2017-06-07 00:08:37 +03:00
} ;
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 ;
2018-06-20 08:22:52 +03:00
static dev_t fsi_base_dev ;
static DEFINE_IDA ( fsi_minor_ida ) ;
# define FSI_CHAR_MAX_DEVICES 0x1000
/* Legacy /dev numbering: 4 devices per chip, 16 chips */
# define FSI_CHAR_LEGACY_TOP 64
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 ;
2018-06-18 06:43:35 +03:00
__be32 irq , stat ;
2017-06-07 00:08:52 +03:00
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 ) ) ;
}
2018-05-29 07:44:08 +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 ;
}
2018-05-29 08:01:07 +03:00
static uint32_t fsi_slave_smode ( int id , u8 t_senddly , u8 t_echodly )
2018-05-29 07:44:08 +03:00
{
return FSI_SMODE_WSC | FSI_SMODE_ECRC
| fsi_smode_sid ( id )
2018-05-29 08:01:07 +03:00
| fsi_smode_echodly ( t_echodly - 1 ) | fsi_smode_senddly ( t_senddly - 1 )
2018-05-29 07:44:08 +03:00
| fsi_smode_lbcrr ( 0x8 ) ;
}
2018-05-29 08:01:07 +03:00
static int fsi_slave_set_smode ( struct fsi_slave * slave )
2018-05-29 07:44:08 +03:00
{
uint32_t smode ;
__be32 data ;
/* set our smode register with the slave ID field to 0; this enables
* extended slave addressing
*/
2018-05-29 08:01:07 +03:00
smode = fsi_slave_smode ( slave - > id , slave - > t_send_delay , slave - > t_echo_delay ) ;
2018-05-29 07:44:08 +03:00
data = cpu_to_be32 ( smode ) ;
2018-05-29 08:01:07 +03:00
return fsi_master_write ( slave - > master , slave - > link , slave - > id ,
FSI_SLAVE_BASE + FSI_SMODE ,
& data , sizeof ( data ) ) ;
2018-05-29 07:44:08 +03:00
}
2017-06-07 00:08:52 +03:00
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 ;
2018-05-29 08:01:07 +03:00
uint8_t id , send_delay , echo_delay ;
2017-06-07 00:08:52 +03:00
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 ;
}
}
2018-05-29 08:01:07 +03:00
send_delay = slave - > t_send_delay ;
echo_delay = slave - > t_echo_delay ;
2017-06-07 00:08:52 +03:00
/* getting serious, reset the slave via BREAK */
rc = fsi_master_break ( master , link ) ;
if ( rc )
return rc ;
2018-05-29 08:01:07 +03:00
slave - > t_send_delay = send_delay ;
slave - > t_echo_delay = echo_delay ;
rc = fsi_slave_set_smode ( slave ) ;
2017-06-07 00:08:52 +03:00
if ( rc )
return rc ;
2018-05-29 08:01:07 +03:00
if ( master - > link_config )
master - > link_config ( master , link ,
slave - > t_send_delay ,
slave - > t_echo_delay ) ;
2017-06-07 00:08:52 +03:00
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 ;
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 ;
2018-06-18 06:43:35 +03:00
uint32_t conf ;
__be32 data ;
2017-06-07 00:08:44 +03:00
2018-06-18 06:43:35 +03:00
rc = fsi_slave_read ( slave , ( i + 1 ) * sizeof ( data ) ,
& data , sizeof ( data ) ) ;
2017-06-07 00:08:44 +03:00
if ( rc ) {
dev_warn ( & slave - > dev ,
" error reading slave registers \n " ) ;
return - 1 ;
}
2018-06-18 06:43:35 +03:00
conf = be32_to_cpu ( data ) ;
2017-06-07 00:08:44 +03:00
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 ;
}
2019-11-08 08:19:39 +03:00
static unsigned long aligned_access_size ( size_t offset , size_t count )
{
unsigned long offset_unit , count_unit ;
/* Criteria:
*
* 1. Access size must be less than or equal to the maximum access
* width or the highest power - of - two factor of offset
* 2. Access size must be less than or equal to the amount specified by
* count
*
* The access width is optimal if we can calculate 1 to be strictly
* equal while still satisfying 2.
*/
/* Find 1 by the bottom bit of offset (with a 4 byte access cap) */
offset_unit = BIT ( __builtin_ctzl ( offset | 4 ) ) ;
/* Find 2 by the top bit of count */
count_unit = BIT ( 8 * sizeof ( unsigned long ) - 1 - __builtin_clzl ( count ) ) ;
/* Constrain the maximum access width to the minimum of both criteria */
return BIT ( __builtin_ctzl ( offset_unit | count_unit ) ) ;
}
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 ) {
2019-11-08 08:19:39 +03:00
read_len = aligned_access_size ( off , count - total_len ) ;
2017-06-07 00:08:49 +03:00
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 ) {
2019-11-08 08:19:39 +03:00
write_len = aligned_access_size ( off , count - total_len ) ;
2017-06-07 00:08:49 +03:00
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 ,
} ;
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-06-20 08:22:52 +03:00
fsi_free_minor ( slave - > dev . devt ) ;
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 ;
}
2018-06-21 05:34:22 +03:00
static ssize_t cfam_read ( struct file * filep , char __user * buf , size_t count ,
loff_t * offset )
{
struct fsi_slave * slave = filep - > private_data ;
size_t total_len , read_len ;
loff_t off = * offset ;
ssize_t 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 ) {
__be32 data ;
read_len = min_t ( size_t , count , 4 ) ;
read_len - = off & 0x3 ;
rc = fsi_slave_read ( slave , off , & data , read_len ) ;
if ( rc )
goto fail ;
rc = copy_to_user ( buf + total_len , & data , read_len ) ;
if ( rc ) {
rc = - EFAULT ;
goto fail ;
}
off + = read_len ;
}
rc = count ;
fail :
* offset = off ;
2021-06-03 15:28:12 +03:00
return rc ;
2018-06-21 05:34:22 +03:00
}
static ssize_t cfam_write ( struct file * filep , const char __user * buf ,
size_t count , loff_t * offset )
{
struct fsi_slave * slave = filep - > private_data ;
size_t total_len , write_len ;
loff_t off = * offset ;
ssize_t 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 ) {
__be32 data ;
write_len = min_t ( size_t , count , 4 ) ;
write_len - = off & 0x3 ;
rc = copy_from_user ( & data , buf + total_len , write_len ) ;
if ( rc ) {
rc = - EFAULT ;
goto fail ;
}
rc = fsi_slave_write ( slave , off , & data , write_len ) ;
if ( rc )
goto fail ;
off + = write_len ;
}
rc = count ;
fail :
* offset = off ;
2021-06-03 15:28:12 +03:00
return rc ;
2018-06-21 05:34:22 +03:00
}
static loff_t cfam_llseek ( struct file * file , loff_t offset , int whence )
{
switch ( whence ) {
case SEEK_CUR :
break ;
case SEEK_SET :
file - > f_pos = offset ;
break ;
default :
return - EINVAL ;
}
return offset ;
}
static int cfam_open ( struct inode * inode , struct file * file )
{
struct fsi_slave * slave = container_of ( inode - > i_cdev , struct fsi_slave , cdev ) ;
file - > private_data = slave ;
return 0 ;
}
static const struct file_operations cfam_fops = {
. owner = THIS_MODULE ,
. open = cfam_open ,
. llseek = cfam_llseek ,
. read = cfam_read ,
. write = cfam_write ,
} ;
static ssize_t send_term_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
struct fsi_master * master = slave - > master ;
if ( ! master - > term )
return - ENODEV ;
master - > term ( master , slave - > link , slave - > id ) ;
return count ;
}
static DEVICE_ATTR_WO ( send_term ) ;
2018-05-29 08:01:07 +03:00
static ssize_t slave_send_echo_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
return sprintf ( buf , " %u \n " , slave - > t_send_delay ) ;
}
static ssize_t slave_send_echo_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
struct fsi_master * master = slave - > master ;
unsigned long val ;
int rc ;
if ( kstrtoul ( buf , 0 , & val ) < 0 )
return - EINVAL ;
if ( val < 1 | | val > 16 )
return - EINVAL ;
if ( ! master - > link_config )
return - ENXIO ;
/* Current HW mandates that send and echo delay are identical */
slave - > t_send_delay = val ;
slave - > t_echo_delay = val ;
rc = fsi_slave_set_smode ( slave ) ;
if ( rc < 0 )
return rc ;
if ( master - > link_config )
master - > link_config ( master , slave - > link ,
slave - > t_send_delay ,
slave - > t_echo_delay ) ;
return count ;
}
static DEVICE_ATTR ( send_echo_delays , 0600 ,
slave_send_echo_show , slave_send_echo_store ) ;
2018-06-20 08:16:31 +03:00
static ssize_t chip_id_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
return sprintf ( buf , " %d \n " , slave - > chip_id ) ;
}
static DEVICE_ATTR_RO ( chip_id ) ;
2018-06-21 05:34:22 +03:00
static ssize_t cfam_id_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
return sprintf ( buf , " 0x%x \n " , slave - > cfam_id ) ;
}
static DEVICE_ATTR_RO ( cfam_id ) ;
static struct attribute * cfam_attr [ ] = {
& dev_attr_send_echo_delays . attr ,
& dev_attr_chip_id . attr ,
& dev_attr_cfam_id . attr ,
& dev_attr_send_term . attr ,
NULL ,
} ;
static const struct attribute_group cfam_attr_group = {
. attrs = cfam_attr ,
} ;
static const struct attribute_group * cfam_attr_groups [ ] = {
& cfam_attr_group ,
NULL ,
} ;
static char * cfam_devnode ( struct device * dev , umode_t * mode ,
kuid_t * uid , kgid_t * gid )
{
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
# ifdef CONFIG_FSI_NEW_DEV_NODE
return kasprintf ( GFP_KERNEL , " fsi/cfam%d " , slave - > cdev_idx ) ;
# else
return kasprintf ( GFP_KERNEL , " cfam%d " , slave - > cdev_idx ) ;
# endif
}
static const struct device_type cfam_type = {
. name = " cfam " ,
. devnode = cfam_devnode ,
. groups = cfam_attr_groups
} ;
2018-06-20 08:22:52 +03:00
static char * fsi_cdev_devnode ( struct device * dev , umode_t * mode ,
kuid_t * uid , kgid_t * gid )
{
# ifdef CONFIG_FSI_NEW_DEV_NODE
return kasprintf ( GFP_KERNEL , " fsi/%s " , dev_name ( dev ) ) ;
# else
return kasprintf ( GFP_KERNEL , " %s " , dev_name ( dev ) ) ;
# endif
}
const struct device_type fsi_cdev_type = {
. name = " fsi-cdev " ,
. devnode = fsi_cdev_devnode ,
} ;
EXPORT_SYMBOL_GPL ( fsi_cdev_type ) ;
/* Backward compatible /dev/ numbering in "old style" mode */
static int fsi_adjust_index ( int index )
{
# ifdef CONFIG_FSI_NEW_DEV_NODE
return index ;
# else
return index + 1 ;
# endif
}
static int __fsi_get_new_minor ( struct fsi_slave * slave , enum fsi_dev_type type ,
dev_t * out_dev , int * out_index )
{
int cid = slave - > chip_id ;
int id ;
/* Check if we qualify for legacy numbering */
if ( cid > = 0 & & cid < 16 & & type < 4 ) {
/* Try reserving the legacy number */
id = ( cid < < 4 ) | type ;
id = ida_simple_get ( & fsi_minor_ida , id , id + 1 , GFP_KERNEL ) ;
if ( id > = 0 ) {
* out_index = fsi_adjust_index ( cid ) ;
* out_dev = fsi_base_dev + id ;
return 0 ;
}
/* Other failure */
if ( id ! = - ENOSPC )
return id ;
/* Fallback to non-legacy allocation */
}
id = ida_simple_get ( & fsi_minor_ida , FSI_CHAR_LEGACY_TOP ,
FSI_CHAR_MAX_DEVICES , GFP_KERNEL ) ;
if ( id < 0 )
return id ;
* out_index = fsi_adjust_index ( id ) ;
* out_dev = fsi_base_dev + id ;
return 0 ;
}
int fsi_get_new_minor ( struct fsi_device * fdev , enum fsi_dev_type type ,
dev_t * out_dev , int * out_index )
{
return __fsi_get_new_minor ( fdev - > slave , type , out_dev , out_index ) ;
}
EXPORT_SYMBOL_GPL ( fsi_get_new_minor ) ;
void fsi_free_minor ( dev_t dev )
{
ida_simple_remove ( & fsi_minor_ida , MINOR ( dev ) ) ;
}
EXPORT_SYMBOL_GPL ( fsi_free_minor ) ;
2017-06-07 00:08:38 +03:00
static int fsi_slave_init ( struct fsi_master * master , int link , uint8_t id )
{
2018-06-21 05:34:22 +03:00
uint32_t cfam_id ;
2017-06-07 00:08:42 +03:00
struct fsi_slave * slave ;
uint8_t crc ;
2020-05-20 21:17:07 +03:00
__be32 data , llmode , slbus ;
2017-06-07 00:08:42 +03:00
int rc ;
/* Currently, we only support single slaves on a link, and use the
* full 23 - bit address range
*/
if ( id ! = 0 )
return - EINVAL ;
2018-06-18 06:43:35 +03:00
rc = fsi_master_read ( master , link , id , 0 , & data , sizeof ( data ) ) ;
2017-06-07 00:08:42 +03:00
if ( rc ) {
dev_dbg ( & master - > dev , " can't read slave %02x:%02x %d \n " ,
link , id , rc ) ;
return - ENODEV ;
}
2018-06-21 05:34:22 +03:00
cfam_id = be32_to_cpu ( data ) ;
2017-06-07 00:08:42 +03:00
2018-06-21 05:34:22 +03:00
crc = crc4 ( 0 , cfam_id , 32 ) ;
2017-06-07 00:08:42 +03:00
if ( crc ) {
2018-06-21 05:34:22 +03:00
dev_warn ( & master - > dev , " slave %02x:%02x invalid cfam id CRC! \n " ,
2017-06-07 00:08:42 +03:00
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 " ,
2018-06-21 05:34:22 +03:00
cfam_id , master - > idx , link , id ) ;
2017-06-07 00:08:42 +03:00
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 ;
2018-06-21 05:34:22 +03:00
dev_set_name ( & slave - > dev , " slave@%02x:%02x " , link , id ) ;
slave - > dev . type = & cfam_type ;
2017-06-07 00:08:42 +03:00
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 ;
2018-06-21 05:34:22 +03:00
device_initialize ( & slave - > dev ) ;
slave - > cfam_id = cfam_id ;
slave - > master = master ;
2017-06-07 00:08:42 +03:00
slave - > link = link ;
slave - > id = id ;
slave - > size = FSI_SLAVE_SIZE_23b ;
2018-05-29 08:01:07 +03:00
slave - > t_send_delay = 16 ;
slave - > t_echo_delay = 16 ;
2018-06-20 08:16:31 +03:00
/* Get chip ID if any */
slave - > chip_id = - 1 ;
if ( slave - > dev . of_node ) {
uint32_t prop ;
if ( ! of_property_read_u32 ( slave - > dev . of_node , " chip-id " , & prop ) )
slave - > chip_id = prop ;
}
2018-06-21 05:34:22 +03:00
2020-05-20 21:17:07 +03:00
slbus = cpu_to_be32 ( FSI_SLBUS_FORCE ) ;
rc = fsi_master_write ( master , link , id , FSI_SLAVE_BASE + FSI_SLBUS ,
& slbus , sizeof ( slbus ) ) ;
if ( rc )
dev_warn ( & master - > dev ,
" can't set slbus on slave:%02x:%02x %d \n " , link , id ,
rc ) ;
2019-06-28 11:07:37 +03:00
rc = fsi_slave_set_smode ( slave ) ;
if ( rc ) {
dev_warn ( & master - > dev ,
" can't set smode on slave:%02x:%02x %d \n " ,
link , id , rc ) ;
goto err_free ;
}
2018-06-21 05:34:22 +03:00
/* Allocate a minor in the FSI space */
rc = __fsi_get_new_minor ( slave , fsi_dev_cfam , & slave - > dev . devt ,
& slave - > cdev_idx ) ;
if ( rc )
goto err_free ;
/* Create chardev for userspace access */
cdev_init ( & slave - > cdev , & cfam_fops ) ;
rc = cdev_device_add ( & slave - > cdev , & slave - > dev ) ;
if ( rc ) {
dev_err ( & slave - > dev , " Error %d creating slave device \n " , rc ) ;
2019-06-28 11:07:37 +03:00
goto err_free_ida ;
2018-06-21 05:34:22 +03:00
}
2019-06-28 11:07:37 +03:00
/* Now that we have the cdev registered with the core, any fatal
* failures beyond this point will need to clean up through
* cdev_device_del ( ) . Fortunately though , nothing past here is fatal .
*/
2018-05-29 08:01:07 +03:00
if ( master - > link_config )
master - > link_config ( master , link ,
slave - > t_send_delay ,
slave - > t_echo_delay ) ;
2017-06-07 00:08:42 +03:00
2018-06-21 05:34:22 +03:00
/* Legacy raw file -> to be removed */
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 ) ;
2018-06-20 08:16:31 +03:00
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
2019-06-28 11:07:37 +03:00
return 0 ;
2018-06-21 05:34:22 +03:00
2019-06-28 11:07:37 +03:00
err_free_ida :
fsi_free_minor ( slave - > dev . devt ) ;
err_free :
of_node_put ( slave - > dev . of_node ) ;
kfree ( slave ) ;
2018-06-21 05:34:22 +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
}
2020-06-10 00:39:28 +03:00
static int fsi_master_link_disable ( struct fsi_master * master , int link )
{
if ( master - > link_enable )
return master - > link_enable ( master , link , false ) ;
return 0 ;
}
2017-06-07 00:08:41 +03:00
static int fsi_master_link_enable ( struct fsi_master * master , int link )
{
if ( master - > link_enable )
2020-06-10 00:39:27 +03:00
return master - > link_enable ( master , link , true ) ;
2017-06-07 00:08:41 +03:00
return 0 ;
}
/*
* Issue a break command on this link
*/
static int fsi_master_break ( struct fsi_master * master , int link )
{
2018-05-29 08:01:07 +03:00
int rc = 0 ;
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 )
2018-05-29 08:01:07 +03:00
rc = master - > send_break ( master , link ) ;
if ( master - > link_config )
master - > link_config ( master , link , 16 , 16 ) ;
2017-06-07 00:08:41 +03:00
2018-05-29 08:01:07 +03:00
return rc ;
2017-06-07 00:08:41 +03:00
}
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 ) {
2020-06-10 00:39:28 +03:00
fsi_master_link_disable ( master , link ) ;
2017-06-07 00:08:41 +03:00
dev_dbg ( & master - > dev ,
" break to link %d failed: %d \n " , link , rc ) ;
continue ;
}
2017-06-07 00:08:38 +03:00
2020-06-10 00:39:28 +03:00
rc = fsi_slave_init ( master , link , 0 ) ;
if ( rc )
fsi_master_link_disable ( master , link ) ;
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 )
{
2018-06-21 05:34:22 +03:00
struct fsi_slave * slave = to_fsi_slave ( dev ) ;
2017-06-07 00:08:46 +03:00
device_for_each_child ( dev , NULL , fsi_slave_remove_device ) ;
2018-06-21 05:34:22 +03:00
cdev_device_del ( & slave - > cdev , & slave - > dev ) ;
2018-06-20 08:22:52 +03:00
put_device ( dev ) ;
2017-06-07 00:08:46 +03:00
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 )
{
2018-06-21 11:00:05 +03:00
int rc ;
mutex_lock ( & master - > scan_lock ) ;
2018-02-12 08:15:40 +03:00
fsi_master_unscan ( master ) ;
2018-06-21 11:00:05 +03:00
rc = fsi_master_scan ( master ) ;
mutex_unlock ( & master - > scan_lock ) ;
return rc ;
2018-02-12 08:15:40 +03:00
}
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 ) ;
2019-11-08 08:19:36 +03:00
static struct attribute * master_attrs [ ] = {
& dev_attr_break . attr ,
& dev_attr_rescan . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( master ) ;
2019-11-08 08:19:40 +03:00
static struct class fsi_master_class = {
2019-11-08 08:19:35 +03:00
. name = " fsi-master " ,
2019-11-08 08:19:36 +03:00
. dev_groups = master_groups ,
2019-11-08 08:19:35 +03:00
} ;
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
2018-06-21 11:00:05 +03:00
mutex_init ( & master - > scan_lock ) ;
2017-06-07 00:08:36 +03:00
master - > idx = ida_simple_get ( & master_ida , 0 , INT_MAX , GFP_KERNEL ) ;
dev_set_name ( & master - > dev , " fsi%d " , master - > idx ) ;
2019-11-08 08:19:35 +03:00
master - > dev . class = & fsi_master_class ;
2017-06-07 00:08:36 +03:00
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
2018-02-12 08:15:49 +03:00
np = dev_of_node ( & master - > dev ) ;
2018-06-21 11:00:05 +03:00
if ( ! of_property_read_bool ( np , " no-scan-on-init " ) ) {
mutex_lock ( & master - > scan_lock ) ;
2018-02-12 08:15:49 +03:00
fsi_master_scan ( master ) ;
2018-06-21 11:00:05 +03:00
mutex_unlock ( & master - > scan_lock ) ;
}
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 ;
}
2018-06-21 11:00:05 +03:00
mutex_lock ( & master - > scan_lock ) ;
2017-06-07 00:08:46 +03:00
fsi_master_unscan ( master ) ;
2018-06-21 11:00:05 +03:00
mutex_unlock ( & master - > scan_lock ) ;
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
{
2018-06-20 08:22:52 +03:00
int rc ;
rc = alloc_chrdev_region ( & fsi_base_dev , 0 , FSI_CHAR_MAX_DEVICES , " fsi " ) ;
if ( rc )
return rc ;
rc = bus_register ( & fsi_bus_type ) ;
if ( rc )
goto fail_bus ;
2019-11-08 08:19:35 +03:00
rc = class_register ( & fsi_master_class ) ;
if ( rc )
goto fail_class ;
2018-06-20 08:22:52 +03:00
return 0 ;
2019-11-08 08:19:35 +03:00
fail_class :
bus_unregister ( & fsi_bus_type ) ;
2018-06-20 08:22:52 +03:00
fail_bus :
unregister_chrdev_region ( fsi_base_dev , FSI_CHAR_MAX_DEVICES ) ;
return rc ;
2017-02-01 19:53:41 +03:00
}
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 )
{
2019-11-08 08:19:35 +03:00
class_unregister ( & fsi_master_class ) ;
2017-02-01 19:53:41 +03:00
bus_unregister ( & fsi_bus_type ) ;
2018-06-20 08:22:52 +03:00
unregister_chrdev_region ( fsi_base_dev , FSI_CHAR_MAX_DEVICES ) ;
ida_destroy ( & fsi_minor_ida ) ;
2017-02-01 19:53:41 +03:00
}
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 " ) ;