2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / kernel / ecard . c
*
* Copyright 1995 - 2001 Russell King
*
* Find all installed expansion cards , and handle interrupts from them .
*
* Created from information from Acorns RiscOS3 PRMs
*
* 08 - Dec - 1996 RMK Added code for the 9 ' th expansion card - the ether
* podule slot .
* 06 - May - 1997 RMK Added blacklist for cards whose loader doesn ' t work .
* 12 - Sep - 1997 RMK Created new handling of interrupt enables / disables
* - cards can now register their own routine to control
* interrupts ( recommended ) .
* 29 - Sep - 1997 RMK Expansion card interrupt hardware not being re - enabled
* on reset from Linux . ( Caused cards not to respond
* under RiscOS without hard reset ) .
* 15 - Feb - 1998 RMK Added DMA support
* 12 - Sep - 1998 RMK Added EASI support
* 10 - Jan - 1999 RMK Run loaders in a simulated RISC OS environment .
* 17 - Apr - 1999 RMK Support for EASI Type C cycles .
*/
# define ECARD_C
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/sched.h>
2017-02-08 18:51:29 +01:00
# include <linux/sched/mm.h>
2005-04-16 15:20:36 -07:00
# include <linux/interrupt.h>
# include <linux/completion.h>
# include <linux/reboot.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/proc_fs.h>
2008-04-29 01:01:49 -07:00
# include <linux/seq_file.h>
2005-04-16 15:20:36 -07:00
# include <linux/device.h>
# include <linux/init.h>
2006-01-12 18:42:23 +00:00
# include <linux/mutex.h>
2007-04-26 00:04:40 -07:00
# include <linux/kthread.h>
2012-03-01 17:50:33 +00:00
# include <linux/irq.h>
2007-05-10 18:40:51 +01:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include <asm/dma.h>
# include <asm/ecard.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
# include <asm/mmu_context.h>
# include <asm/mach/irq.h>
# include <asm/tlbflush.h>
2007-05-03 10:20:47 +01:00
# include "ecard.h"
2005-04-16 15:20:36 -07:00
struct ecard_request {
void ( * fn ) ( struct ecard_request * ) ;
ecard_t * ec ;
unsigned int address ;
unsigned int length ;
unsigned int use_loader ;
void * buffer ;
struct completion * complete ;
} ;
struct expcard_blacklist {
unsigned short manufacturer ;
unsigned short product ;
const char * type ;
2019-05-01 12:10:42 +01:00
void ( * init ) ( ecard_t * ec ) ;
2005-04-16 15:20:36 -07:00
} ;
static ecard_t * cards ;
static ecard_t * slot_to_expcard [ MAX_ECARDS ] ;
static unsigned int ectcr ;
2019-05-01 12:10:42 +01:00
static void atomwide_3p_quirk ( ecard_t * ec ) ;
2005-04-16 15:20:36 -07:00
/* List of descriptions of cards which don't have an extended
* identification , or chunk directories containing a description .
*/
static struct expcard_blacklist __initdata blacklist [ ] = {
2019-05-01 12:10:42 +01:00
{ MANU_ACORN , PROD_ACORN_ETHER1 , " Acorn Ether1 " } ,
{ MANU_ATOMWIDE , PROD_ATOMWIDE_3PSERIAL , NULL , atomwide_3p_quirk } ,
2005-04-16 15:20:36 -07:00
} ;
asmlinkage extern int
ecard_loader_reset ( unsigned long base , loader_t loader ) ;
asmlinkage extern int
ecard_loader_read ( int off , unsigned long base , loader_t loader ) ;
static inline unsigned short ecard_getu16 ( unsigned char * v )
{
return v [ 0 ] | v [ 1 ] < < 8 ;
}
static inline signed long ecard_gets24 ( unsigned char * v )
{
return v [ 0 ] | v [ 1 ] < < 8 | v [ 2 ] < < 16 | ( ( v [ 2 ] & 0x80 ) ? 0xff000000 : 0 ) ;
}
static inline ecard_t * slot_to_ecard ( unsigned int slot )
{
return slot < MAX_ECARDS ? slot_to_expcard [ slot ] : NULL ;
}
/* ===================== Expansion card daemon ======================== */
/*
* Since the loader programs on the expansion cards need to be run
* in a specific environment , create a separate task with this
* environment up , and pass requests to this task as and when we
* need to .
*
* This should allow 99 % of loaders to be called from Linux .
*
* From a security standpoint , we trust the card vendors . This
* may be a misplaced trust .
*/
static void ecard_task_reset ( struct ecard_request * req )
{
struct expansion_card * ec = req - > ec ;
struct resource * res ;
res = ec - > slot_no = = 8
? & ec - > resource [ ECARD_RES_MEMC ]
2007-05-03 10:47:37 +01:00
: ec - > easi
2005-04-16 15:20:36 -07:00
? & ec - > resource [ ECARD_RES_EASI ]
: & ec - > resource [ ECARD_RES_IOCSYNC ] ;
ecard_loader_reset ( res - > start , ec - > loader ) ;
}
static void ecard_task_readbytes ( struct ecard_request * req )
{
struct expansion_card * ec = req - > ec ;
unsigned char * buf = req - > buffer ;
unsigned int len = req - > length ;
unsigned int off = req - > address ;
if ( ec - > slot_no = = 8 ) {
void __iomem * base = ( void __iomem * )
ec - > resource [ ECARD_RES_MEMC ] . start ;
/*
* The card maintains an index which increments the address
* into a 4096 - byte page on each access . We need to keep
* track of the counter .
*/
static unsigned int index ;
unsigned int page ;
page = ( off > > 12 ) * 4 ;
if ( page > 256 * 4 )
return ;
off & = 4095 ;
/*
* If we are reading offset 0 , or our current index is
* greater than the offset , reset the hardware index counter .
*/
if ( off = = 0 | | index > off ) {
writeb ( 0 , base ) ;
index = 0 ;
}
/*
* Increment the hardware index counter until we get to the
* required offset . The read bytes are discarded .
*/
while ( index < off ) {
readb ( base + page ) ;
index + = 1 ;
}
while ( len - - ) {
* buf + + = readb ( base + page ) ;
index + = 1 ;
}
} else {
2007-05-03 10:47:37 +01:00
unsigned long base = ( ec - > easi
2005-04-16 15:20:36 -07:00
? & ec - > resource [ ECARD_RES_EASI ]
: & ec - > resource [ ECARD_RES_IOCSYNC ] ) - > start ;
void __iomem * pbase = ( void __iomem * ) base ;
if ( ! req - > use_loader | | ! ec - > loader ) {
off * = 4 ;
while ( len - - ) {
* buf + + = readb ( pbase + off ) ;
off + = 4 ;
}
} else {
while ( len - - ) {
/*
* The following is required by some
* expansion card loader programs .
*/
* ( unsigned long * ) 0x108 = 0 ;
* buf + + = ecard_loader_read ( off + + , base ,
ec - > loader ) ;
}
}
}
}
static DECLARE_WAIT_QUEUE_HEAD ( ecard_wait ) ;
static struct ecard_request * ecard_req ;
2006-01-12 18:42:23 +00:00
static DEFINE_MUTEX ( ecard_mutex ) ;
2005-04-16 15:20:36 -07:00
/*
* Set up the expansion card daemon ' s page tables .
*/
static void ecard_init_pgtables ( struct mm_struct * mm )
{
mm: do not initialize TLB stack vma's with vma_init()
Commit 2c4541e24c55 ("mm: use vma_init() to initialize VMAs on stack and
data segments") tried to initialize various left-over ad-hoc vma's
"properly", but actually made things worse for the temporary vma's used
for TLB flushing.
vma_init() doesn't actually initialize all of the vma, just a few
fields, so doing something like
- struct vm_area_struct vma = { .vm_mm = tlb->mm, };
+ struct vm_area_struct vma;
+
+ vma_init(&vma, tlb->mm);
was actually very bad: instead of having a nicely initialized vma with
every field but "vm_mm" zeroed, you'd have an entirely uninitialized vma
with only a couple of fields initialized. And they weren't even fields
that the code in question mostly cared about.
The flush_tlb_range() function takes a "struct vma" rather than a
"struct mm_struct", because a few architectures actually care about what
kind of range it is - being able to only do an ITLB flush if it's a
range that doesn't have data accesses enabled, for example. And all the
normal users already have the vma for doing the range invalidation.
But a few people want to call flush_tlb_range() with a range they just
made up, so they also end up using a made-up vma. x86 just has a
special "flush_tlb_mm_range()" function for this, but other
architectures (arm and ia64) do the "use fake vma" thing instead, and
thus got caught up in the vma_init() changes.
At the same time, the TLB flushing code really doesn't care about most
other fields in the vma, so vma_init() is just unnecessary and
pointless.
This fixes things by having an explicit "this is just an initializer for
the TLB flush" initializer macro, which is used by the arm/arm64/ia64
people who mis-use this interface with just a dummy vma.
Fixes: 2c4541e24c55 ("mm: use vma_init() to initialize VMAs on stack and data segments")
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-08-01 13:43:38 -07:00
struct vm_area_struct vma = TLB_FLUSH_VMA ( mm , VM_EXEC ) ;
2005-04-16 15:20:36 -07:00
/* We want to set up the page tables for the following mapping:
* Virtual Physical
* 0x03000000 0x03000000
* 0x03010000 unmapped
* 0x03210000 0x03210000
* 0x03400000 unmapped
* 0x08000000 0x08000000
* 0x10000000 unmapped
*
* FIXME : we don ' t follow this 100 % yet .
*/
pgd_t * src_pgd , * dst_pgd ;
src_pgd = pgd_offset ( mm , ( unsigned long ) IO_BASE ) ;
dst_pgd = pgd_offset ( mm , IO_START ) ;
memcpy ( dst_pgd , src_pgd , sizeof ( pgd_t ) * ( IO_SIZE / PGDIR_SIZE ) ) ;
2011-07-07 11:40:52 +01:00
src_pgd = pgd_offset ( mm , ( unsigned long ) EASI_BASE ) ;
2005-04-16 15:20:36 -07:00
dst_pgd = pgd_offset ( mm , EASI_START ) ;
memcpy ( dst_pgd , src_pgd , sizeof ( pgd_t ) * ( EASI_SIZE / PGDIR_SIZE ) ) ;
flush_tlb_range ( & vma , IO_START , IO_START + IO_SIZE ) ;
flush_tlb_range ( & vma , EASI_START , EASI_START + EASI_SIZE ) ;
}
static int ecard_init_mm ( void )
{
struct mm_struct * mm = mm_alloc ( ) ;
struct mm_struct * active_mm = current - > active_mm ;
if ( ! mm )
return - ENOMEM ;
current - > mm = mm ;
current - > active_mm = mm ;
activate_mm ( active_mm , mm ) ;
mmdrop ( active_mm ) ;
ecard_init_pgtables ( mm ) ;
return 0 ;
}
static int
ecard_task ( void * unused )
{
/*
* Allocate a mm . We ' re not a lazy - TLB kernel task since we need
* to set page table entries where the user space would be . Note
* that this also creates the page tables . Failure is not an
* option here .
*/
if ( ecard_init_mm ( ) )
panic ( " kecardd: unable to alloc mm \n " ) ;
while ( 1 ) {
struct ecard_request * req ;
wait_event_interruptible ( ecard_wait , ecard_req ! = NULL ) ;
req = xchg ( & ecard_req , NULL ) ;
if ( req ! = NULL ) {
req - > fn ( req ) ;
complete ( req - > complete ) ;
}
}
}
/*
* Wake the expansion card daemon to action our request .
*
* FIXME : The test here is not sufficient to detect if the
* kcardd is running .
*/
static void ecard_call ( struct ecard_request * req )
{
2006-09-30 23:28:10 -07:00
DECLARE_COMPLETION_ONSTACK ( completion ) ;
2005-04-16 15:20:36 -07:00
req - > complete = & completion ;
2006-01-12 18:42:23 +00:00
mutex_lock ( & ecard_mutex ) ;
2005-04-16 15:20:36 -07:00
ecard_req = req ;
wake_up ( & ecard_wait ) ;
/*
* Now wait for kecardd to run .
*/
wait_for_completion ( & completion ) ;
2006-01-12 18:42:23 +00:00
mutex_unlock ( & ecard_mutex ) ;
2005-04-16 15:20:36 -07:00
}
/* ======================= Mid-level card control ===================== */
static void
ecard_readbytes ( void * addr , ecard_t * ec , int off , int len , int useld )
{
struct ecard_request req ;
req . fn = ecard_task_readbytes ;
req . ec = ec ;
req . address = off ;
req . length = len ;
req . use_loader = useld ;
req . buffer = addr ;
ecard_call ( & req ) ;
}
int ecard_readchunk ( struct in_chunk_dir * cd , ecard_t * ec , int id , int num )
{
struct ex_chunk_dir excd ;
int index = 16 ;
int useld = 0 ;
if ( ! ec - > cid . cd )
return 0 ;
while ( 1 ) {
ecard_readbytes ( & excd , ec , index , 8 , useld ) ;
index + = 8 ;
if ( c_id ( & excd ) = = 0 ) {
if ( ! useld & & ec - > loader ) {
useld = 1 ;
index = 0 ;
continue ;
}
return 0 ;
}
if ( c_id ( & excd ) = = 0xf0 ) { /* link */
index = c_start ( & excd ) ;
continue ;
}
if ( c_id ( & excd ) = = 0x80 ) { /* loader */
if ( ! ec - > loader ) {
2006-12-13 00:35:56 -08:00
ec - > loader = kmalloc ( c_len ( & excd ) ,
2005-04-16 15:20:36 -07:00
GFP_KERNEL ) ;
if ( ec - > loader )
ecard_readbytes ( ec - > loader , ec ,
( int ) c_start ( & excd ) ,
c_len ( & excd ) , useld ) ;
else
return 0 ;
}
continue ;
}
if ( c_id ( & excd ) = = id & & num - - = = 0 )
break ;
}
if ( c_id ( & excd ) & 0x80 ) {
switch ( c_id ( & excd ) & 0x70 ) {
case 0x70 :
ecard_readbytes ( ( unsigned char * ) excd . d . string , ec ,
( int ) c_start ( & excd ) , c_len ( & excd ) ,
useld ) ;
break ;
case 0x00 :
break ;
}
}
cd - > start_offset = c_start ( & excd ) ;
memcpy ( cd - > d . string , excd . d . string , 256 ) ;
return 1 ;
}
/* ======================= Interrupt control ============================ */
static void ecard_def_irq_enable ( ecard_t * ec , int irqnr )
{
}
static void ecard_def_irq_disable ( ecard_t * ec , int irqnr )
{
}
static int ecard_def_irq_pending ( ecard_t * ec )
{
return ! ec - > irqmask | | readb ( ec - > irqaddr ) & ec - > irqmask ;
}
static void ecard_def_fiq_enable ( ecard_t * ec , int fiqnr )
{
panic ( " ecard_def_fiq_enable called - impossible " ) ;
}
static void ecard_def_fiq_disable ( ecard_t * ec , int fiqnr )
{
panic ( " ecard_def_fiq_disable called - impossible " ) ;
}
static int ecard_def_fiq_pending ( ecard_t * ec )
{
return ! ec - > fiqmask | | readb ( ec - > fiqaddr ) & ec - > fiqmask ;
}
static expansioncard_ops_t ecard_default_ops = {
ecard_def_irq_enable ,
ecard_def_irq_disable ,
ecard_def_irq_pending ,
ecard_def_fiq_enable ,
ecard_def_fiq_disable ,
ecard_def_fiq_pending
} ;
/*
* Enable and disable interrupts from expansion cards .
* ( interrupts are disabled for these functions ) .
*
* They are not meant to be called directly , but via enable / disable_irq .
*/
2010-11-29 10:21:01 +01:00
static void ecard_irq_unmask ( struct irq_data * d )
2005-04-16 15:20:36 -07:00
{
2012-03-01 17:54:11 +00:00
ecard_t * ec = irq_data_get_irq_chip_data ( d ) ;
2005-04-16 15:20:36 -07:00
if ( ec ) {
if ( ! ec - > ops )
ec - > ops = & ecard_default_ops ;
if ( ec - > claimed & & ec - > ops - > irqenable )
2010-11-29 10:21:01 +01:00
ec - > ops - > irqenable ( ec , d - > irq ) ;
2005-04-16 15:20:36 -07:00
else
printk ( KERN_ERR " ecard: rejecting request to "
2010-11-29 10:21:01 +01:00
" enable IRQs for %d \n " , d - > irq ) ;
2005-04-16 15:20:36 -07:00
}
}
2010-11-29 10:21:01 +01:00
static void ecard_irq_mask ( struct irq_data * d )
2005-04-16 15:20:36 -07:00
{
2012-03-01 17:54:11 +00:00
ecard_t * ec = irq_data_get_irq_chip_data ( d ) ;
2005-04-16 15:20:36 -07:00
if ( ec ) {
if ( ! ec - > ops )
ec - > ops = & ecard_default_ops ;
if ( ec - > ops & & ec - > ops - > irqdisable )
2010-11-29 10:21:01 +01:00
ec - > ops - > irqdisable ( ec , d - > irq ) ;
2005-04-16 15:20:36 -07:00
}
}
2006-08-01 22:26:25 +01:00
static struct irq_chip ecard_chip = {
2010-11-29 10:21:01 +01:00
. name = " ECARD " ,
. irq_ack = ecard_irq_mask ,
. irq_mask = ecard_irq_mask ,
. irq_unmask = ecard_irq_unmask ,
2005-04-16 15:20:36 -07:00
} ;
void ecard_enablefiq ( unsigned int fiqnr )
{
ecard_t * ec = slot_to_ecard ( fiqnr ) ;
if ( ec ) {
if ( ! ec - > ops )
ec - > ops = & ecard_default_ops ;
if ( ec - > claimed & & ec - > ops - > fiqenable )
ec - > ops - > fiqenable ( ec , fiqnr ) ;
else
printk ( KERN_ERR " ecard: rejecting request to "
" enable FIQs for %d \n " , fiqnr ) ;
}
}
void ecard_disablefiq ( unsigned int fiqnr )
{
ecard_t * ec = slot_to_ecard ( fiqnr ) ;
if ( ec ) {
if ( ! ec - > ops )
ec - > ops = & ecard_default_ops ;
if ( ec - > ops - > fiqdisable )
ec - > ops - > fiqdisable ( ec , fiqnr ) ;
}
}
static void ecard_dump_irq_state ( void )
{
ecard_t * ec ;
printk ( " Expansion card IRQ state: \n " ) ;
for ( ec = cards ; ec ; ec = ec - > next ) {
2019-04-29 19:24:39 +01:00
const char * claimed ;
2005-04-16 15:20:36 -07:00
if ( ec - > slot_no = = 8 )
continue ;
2019-04-29 19:24:39 +01:00
claimed = ec - > claimed ? " " : " not " ;
2005-04-16 15:20:36 -07:00
if ( ec - > ops & & ec - > ops - > irqpending & &
ec - > ops ! = & ecard_default_ops )
2019-04-29 19:24:39 +01:00
printk ( " %d: %sclaimed irq %spending \n " ,
ec - > slot_no , claimed ,
2005-04-16 15:20:36 -07:00
ec - > ops - > irqpending ( ec ) ? " " : " not " ) ;
else
2019-04-29 19:24:39 +01:00
printk ( " %d: %sclaimed irqaddr %p, mask = %02X, status = %02X \n " ,
ec - > slot_no , claimed ,
2005-04-16 15:20:36 -07:00
ec - > irqaddr , ec - > irqmask , readb ( ec - > irqaddr ) ) ;
}
}
2006-11-23 11:41:32 +00:00
static void ecard_check_lockup ( struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
static unsigned long last ;
static int lockup ;
/*
* If the timer interrupt has not run since the last million
* unrecognised expansion card interrupts , then there is
* something seriously wrong . Disable the expansion card
* interrupts so at least we can continue .
*
* Maybe we ought to start a timer to re - enable them some time
* later ?
*/
if ( last = = jiffies ) {
lockup + = 1 ;
if ( lockup > 1000000 ) {
printk ( KERN_ERR " \n Interrupt lockup detected - "
" disabling all expansion card interrupts \n " ) ;
2010-11-29 10:21:01 +01:00
desc - > irq_data . chip - > irq_mask ( & desc - > irq_data ) ;
2005-04-16 15:20:36 -07:00
ecard_dump_irq_state ( ) ;
}
} else
lockup = 0 ;
/*
* If we did not recognise the source of this interrupt ,
* warn the user , but don ' t flood the user with these messages .
*/
if ( ! last | | time_after ( jiffies , last + 5 * HZ ) ) {
last = jiffies ;
printk ( KERN_WARNING " Unrecognised interrupt from backplane \n " ) ;
ecard_dump_irq_state ( ) ;
}
}
2015-09-14 10:42:37 +02:00
static void ecard_irq_handler ( struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
ecard_t * ec ;
int called = 0 ;
2010-11-29 10:21:01 +01:00
desc - > irq_data . chip - > irq_mask ( & desc - > irq_data ) ;
2005-04-16 15:20:36 -07:00
for ( ec = cards ; ec ; ec = ec - > next ) {
int pending ;
2012-01-18 15:41:44 +00:00
if ( ! ec - > claimed | | ! ec - > irq | | ec - > slot_no = = 8 )
2005-04-16 15:20:36 -07:00
continue ;
if ( ec - > ops & & ec - > ops - > irqpending )
pending = ec - > ops - > irqpending ( ec ) ;
else
pending = ecard_default_ops . irqpending ( ec ) ;
if ( pending ) {
2008-10-09 13:36:24 +01:00
generic_handle_irq ( ec - > irq ) ;
2005-04-16 15:20:36 -07:00
called + + ;
}
}
2010-11-29 10:21:01 +01:00
desc - > irq_data . chip - > irq_unmask ( & desc - > irq_data ) ;
2005-04-16 15:20:36 -07:00
if ( called = = 0 )
ecard_check_lockup ( desc ) ;
}
2011-07-07 10:56:41 +01:00
static void __iomem * __ecard_address ( ecard_t * ec , card_type_t type , card_speed_t speed )
2005-04-16 15:20:36 -07:00
{
2011-07-07 11:07:36 +01:00
void __iomem * address = NULL ;
2005-04-16 15:20:36 -07:00
int slot = ec - > slot_no ;
if ( ec - > slot_no = = 8 )
2011-07-07 11:07:36 +01:00
return ECARD_MEMC8_BASE ;
2005-04-16 15:20:36 -07:00
ectcr & = ~ ( 1 < < slot ) ;
switch ( type ) {
case ECARD_MEMC :
if ( slot < 4 )
2011-07-07 11:07:36 +01:00
address = ECARD_MEMC_BASE + ( slot < < 14 ) ;
2005-04-16 15:20:36 -07:00
break ;
case ECARD_IOC :
if ( slot < 4 )
2011-07-07 11:07:36 +01:00
address = ECARD_IOC_BASE + ( slot < < 14 ) ;
2005-04-16 15:20:36 -07:00
else
2011-07-07 11:07:36 +01:00
address = ECARD_IOC4_BASE + ( ( slot - 4 ) < < 14 ) ;
2005-04-16 15:20:36 -07:00
if ( address )
2011-07-07 11:07:36 +01:00
address + = speed < < 19 ;
2005-04-16 15:20:36 -07:00
break ;
case ECARD_EASI :
2011-07-07 11:07:36 +01:00
address = ECARD_EASI_BASE + ( slot < < 24 ) ;
2005-04-16 15:20:36 -07:00
if ( speed = = ECARD_FAST )
ectcr | = 1 < < slot ;
break ;
2011-07-07 11:07:36 +01:00
2005-04-16 15:20:36 -07:00
default :
break ;
}
# ifdef IOMD_ECTCR
iomd_writeb ( ectcr , IOMD_ECTCR ) ;
# endif
2011-07-07 11:07:36 +01:00
return address ;
2005-04-16 15:20:36 -07:00
}
2008-04-29 01:01:49 -07:00
static int ecard_prints ( struct seq_file * m , ecard_t * ec )
2005-04-16 15:20:36 -07:00
{
2008-04-29 01:01:49 -07:00
seq_printf ( m , " %d: %s " , ec - > slot_no , ec - > easi ? " EASI " : " " ) ;
2005-04-16 15:20:36 -07:00
if ( ec - > cid . id = = 0 ) {
struct in_chunk_dir incd ;
2008-04-29 01:01:49 -07:00
seq_printf ( m , " [%04X:%04X] " ,
2005-04-16 15:20:36 -07:00
ec - > cid . manufacturer , ec - > cid . product ) ;
if ( ! ec - > card_desc & & ec - > cid . cd & &
ecard_readchunk ( & incd , ec , 0xf5 , 0 ) ) {
ec - > card_desc = kmalloc ( strlen ( incd . d . string ) + 1 , GFP_KERNEL ) ;
if ( ec - > card_desc )
strcpy ( ( char * ) ec - > card_desc , incd . d . string ) ;
}
2008-04-29 01:01:49 -07:00
seq_printf ( m , " %s \n " , ec - > card_desc ? ec - > card_desc : " *unknown* " ) ;
2005-04-16 15:20:36 -07:00
} else
2008-04-29 01:01:49 -07:00
seq_printf ( m , " Simple card %d \n " , ec - > cid . id ) ;
2005-04-16 15:20:36 -07:00
2008-04-29 01:01:49 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-04-29 01:01:49 -07:00
static int ecard_devices_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
ecard_t * ec = cards ;
2008-04-29 01:01:49 -07:00
while ( ec ) {
ecard_prints ( m , ec ) ;
2005-04-16 15:20:36 -07:00
ec = ec - > next ;
}
2008-04-29 01:01:49 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static struct proc_dir_entry * proc_bus_ecard_dir = NULL ;
static void ecard_proc_init ( void )
{
2008-04-29 01:01:41 -07:00
proc_bus_ecard_dir = proc_mkdir ( " bus/ecard " , NULL ) ;
2018-05-15 15:57:23 +02:00
proc_create_single ( " devices " , 0 , proc_bus_ecard_dir ,
ecard_devices_proc_show ) ;
2005-04-16 15:20:36 -07:00
}
# define ec_set_resource(ec,nr,st,sz) \
do { \
2008-05-30 17:42:11 +02:00
( ec ) - > resource [ nr ] . name = dev_name ( & ec - > dev ) ; \
2005-04-16 15:20:36 -07:00
( ec ) - > resource [ nr ] . start = st ; \
( ec ) - > resource [ nr ] . end = ( st ) + ( sz ) - 1 ; \
( ec ) - > resource [ nr ] . flags = IORESOURCE_MEM ; \
} while ( 0 )
static void __init ecard_free_card ( struct expansion_card * ec )
{
int i ;
for ( i = 0 ; i < ECARD_NUM_RESOURCES ; i + + )
if ( ec - > resource [ i ] . flags )
release_resource ( & ec - > resource [ i ] ) ;
kfree ( ec ) ;
}
static struct expansion_card * __init ecard_alloc_card ( int type , int slot )
{
struct expansion_card * ec ;
unsigned long base ;
int i ;
2006-03-20 19:46:41 +00:00
ec = kzalloc ( sizeof ( ecard_t ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! ec ) {
ec = ERR_PTR ( - ENOMEM ) ;
goto nomem ;
}
ec - > slot_no = slot ;
2007-05-03 10:47:37 +01:00
ec - > easi = type = = ECARD_EASI ;
2012-01-18 15:41:44 +00:00
ec - > irq = 0 ;
ec - > fiq = 0 ;
2005-04-16 15:20:36 -07:00
ec - > dma = NO_DMA ;
ec - > ops = & ecard_default_ops ;
2009-01-06 10:44:43 -08:00
dev_set_name ( & ec - > dev , " ecard%d " , slot ) ;
2005-04-16 15:20:36 -07:00
ec - > dev . parent = NULL ;
ec - > dev . bus = & ecard_bus_type ;
ec - > dev . dma_mask = & ec - > dma_mask ;
ec - > dma_mask = ( u64 ) 0xffffffff ;
2007-04-02 13:53:15 +01:00
ec - > dev . coherent_dma_mask = ec - > dma_mask ;
2005-04-16 15:20:36 -07:00
if ( slot < 4 ) {
ec_set_resource ( ec , ECARD_RES_MEMC ,
PODSLOT_MEMC_BASE + ( slot < < 14 ) ,
PODSLOT_MEMC_SIZE ) ;
base = PODSLOT_IOC0_BASE + ( slot < < 14 ) ;
} else
base = PODSLOT_IOC4_BASE + ( ( slot - 4 ) < < 14 ) ;
# ifdef CONFIG_ARCH_RPC
if ( slot < 8 ) {
ec_set_resource ( ec , ECARD_RES_EASI ,
PODSLOT_EASI_BASE + ( slot < < 24 ) ,
PODSLOT_EASI_SIZE ) ;
}
if ( slot = = 8 ) {
ec_set_resource ( ec , ECARD_RES_MEMC , NETSLOT_BASE , NETSLOT_SIZE ) ;
} else
# endif
for ( i = 0 ; i < = ECARD_RES_IOCSYNC - ECARD_RES_IOCSLOW ; i + + )
ec_set_resource ( ec , i + ECARD_RES_IOCSLOW ,
base + ( i < < 19 ) , PODSLOT_IOC_SIZE ) ;
for ( i = 0 ; i < ECARD_NUM_RESOURCES ; i + + ) {
if ( ec - > resource [ i ] . flags & &
request_resource ( & iomem_resource , & ec - > resource [ i ] ) ) {
2008-05-02 06:02:41 +02:00
dev_err ( & ec - > dev , " resource(s) not available \n " ) ;
2005-04-16 15:20:36 -07:00
ec - > resource [ i ] . end - = ec - > resource [ i ] . start ;
ec - > resource [ i ] . start = 0 ;
ec - > resource [ i ] . flags = 0 ;
}
}
nomem :
return ec ;
}
2017-06-06 14:14:16 +02:00
static ssize_t irq_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
return sprintf ( buf , " %u \n " , ec - > irq ) ;
}
2017-06-06 14:14:16 +02:00
static DEVICE_ATTR_RO ( irq ) ;
2005-04-16 15:20:36 -07:00
2017-06-06 14:14:16 +02:00
static ssize_t dma_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
return sprintf ( buf , " %u \n " , ec - > dma ) ;
}
2017-06-06 14:14:16 +02:00
static DEVICE_ATTR_RO ( dma ) ;
2005-04-16 15:20:36 -07:00
2017-06-06 14:14:16 +02:00
static ssize_t resource_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
char * str = buf ;
int i ;
for ( i = 0 ; i < ECARD_NUM_RESOURCES ; i + + )
2006-07-03 13:29:03 +01:00
str + = sprintf ( str , " %08x %08x %08lx \n " ,
2005-04-16 15:20:36 -07:00
ec - > resource [ i ] . start ,
ec - > resource [ i ] . end ,
ec - > resource [ i ] . flags ) ;
return str - buf ;
}
2017-06-14 11:54:29 +02:00
static DEVICE_ATTR_RO ( resource ) ;
2005-04-16 15:20:36 -07:00
2017-06-06 14:14:16 +02:00
static ssize_t vendor_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
return sprintf ( buf , " %u \n " , ec - > cid . manufacturer ) ;
}
2017-06-06 14:14:16 +02:00
static DEVICE_ATTR_RO ( vendor ) ;
2005-04-16 15:20:36 -07:00
2017-06-06 14:14:16 +02:00
static ssize_t device_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
return sprintf ( buf , " %u \n " , ec - > cid . product ) ;
}
2017-06-06 14:14:16 +02:00
static DEVICE_ATTR_RO ( device ) ;
2005-04-16 15:20:36 -07:00
2017-06-06 14:14:16 +02:00
static ssize_t type_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
2007-05-03 10:47:37 +01:00
return sprintf ( buf , " %s \n " , ec - > easi ? " EASI " : " IOC " ) ;
2005-04-16 15:20:36 -07:00
}
2017-06-06 14:14:16 +02:00
static DEVICE_ATTR_RO ( type ) ;
static struct attribute * ecard_dev_attrs [ ] = {
& dev_attr_device . attr ,
& dev_attr_dma . attr ,
& dev_attr_irq . attr ,
& dev_attr_resource . attr ,
& dev_attr_type . attr ,
& dev_attr_vendor . attr ,
NULL ,
2005-04-16 15:20:36 -07:00
} ;
2017-06-06 14:14:16 +02:00
ATTRIBUTE_GROUPS ( ecard_dev ) ;
2005-04-16 15:20:36 -07:00
int ecard_request_resources ( struct expansion_card * ec )
{
int i , err = 0 ;
for ( i = 0 ; i < ECARD_NUM_RESOURCES ; i + + ) {
if ( ecard_resource_end ( ec , i ) & &
! request_mem_region ( ecard_resource_start ( ec , i ) ,
ecard_resource_len ( ec , i ) ,
ec - > dev . driver - > name ) ) {
err = - EBUSY ;
break ;
}
}
if ( err ) {
while ( i - - )
if ( ecard_resource_end ( ec , i ) )
release_mem_region ( ecard_resource_start ( ec , i ) ,
ecard_resource_len ( ec , i ) ) ;
}
return err ;
}
EXPORT_SYMBOL ( ecard_request_resources ) ;
void ecard_release_resources ( struct expansion_card * ec )
{
int i ;
for ( i = 0 ; i < ECARD_NUM_RESOURCES ; i + + )
if ( ecard_resource_end ( ec , i ) )
release_mem_region ( ecard_resource_start ( ec , i ) ,
ecard_resource_len ( ec , i ) ) ;
}
EXPORT_SYMBOL ( ecard_release_resources ) ;
2007-05-10 16:46:13 +01:00
void ecard_setirq ( struct expansion_card * ec , const struct expansion_card_ops * ops , void * irq_data )
{
ec - > irq_data = irq_data ;
barrier ( ) ;
ec - > ops = ops ;
}
EXPORT_SYMBOL ( ecard_setirq ) ;
2007-05-10 18:40:51 +01:00
void __iomem * ecardm_iomap ( struct expansion_card * ec , unsigned int res ,
unsigned long offset , unsigned long maxsize )
{
unsigned long start = ecard_resource_start ( ec , res ) ;
unsigned long end = ecard_resource_end ( ec , res ) ;
if ( offset > ( end - start ) )
return NULL ;
start + = offset ;
if ( maxsize & & end - start > maxsize )
end = start + maxsize ;
return devm_ioremap ( & ec - > dev , start , end - start ) ;
}
EXPORT_SYMBOL ( ecardm_iomap ) ;
2019-05-01 12:10:42 +01:00
static void atomwide_3p_quirk ( ecard_t * ec )
{
void __iomem * addr = __ecard_address ( ec , ECARD_IOC , ECARD_SYNC ) ;
unsigned int i ;
/* Disable interrupts on each port */
for ( i = 0x2000 ; i < = 0x2800 ; i + = 0x0400 )
writeb ( 0 , addr + i + 4 ) ;
}
2005-04-16 15:20:36 -07:00
/*
* Probe for an expansion card .
*
* If bit 1 of the first byte of the card is set , then the
* card does not exist .
*/
2012-03-01 17:50:33 +00:00
static int __init ecard_probe ( int slot , unsigned irq , card_type_t type )
2005-04-16 15:20:36 -07:00
{
ecard_t * * ecp ;
ecard_t * ec ;
struct ex_ecid cid ;
2011-07-07 10:56:41 +01:00
void __iomem * addr ;
2005-04-16 15:20:36 -07:00
int i , rc ;
ec = ecard_alloc_card ( type , slot ) ;
if ( IS_ERR ( ec ) ) {
rc = PTR_ERR ( ec ) ;
goto nomem ;
}
rc = - ENODEV ;
2011-07-07 10:56:41 +01:00
if ( ( addr = __ecard_address ( ec , type , ECARD_SYNC ) ) = = NULL )
2005-04-16 15:20:36 -07:00
goto nodev ;
cid . r_zero = 1 ;
ecard_readbytes ( & cid , ec , 0 , 16 , 0 ) ;
if ( cid . r_zero )
goto nodev ;
ec - > cid . id = cid . r_id ;
ec - > cid . cd = cid . r_cd ;
ec - > cid . is = cid . r_is ;
ec - > cid . w = cid . r_w ;
ec - > cid . manufacturer = ecard_getu16 ( cid . r_manu ) ;
ec - > cid . product = ecard_getu16 ( cid . r_prod ) ;
ec - > cid . country = cid . r_country ;
ec - > cid . irqmask = cid . r_irqmask ;
ec - > cid . irqoff = ecard_gets24 ( cid . r_irqoff ) ;
ec - > cid . fiqmask = cid . r_fiqmask ;
ec - > cid . fiqoff = ecard_gets24 ( cid . r_fiqoff ) ;
ec - > fiqaddr =
2011-07-07 10:56:41 +01:00
ec - > irqaddr = addr ;
2005-04-16 15:20:36 -07:00
if ( ec - > cid . is ) {
ec - > irqmask = ec - > cid . irqmask ;
ec - > irqaddr + = ec - > cid . irqoff ;
ec - > fiqmask = ec - > cid . fiqmask ;
ec - > fiqaddr + = ec - > cid . fiqoff ;
} else {
ec - > irqmask = 1 ;
ec - > fiqmask = 4 ;
}
2007-02-05 16:10:25 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( blacklist ) ; i + + )
2005-04-16 15:20:36 -07:00
if ( blacklist [ i ] . manufacturer = = ec - > cid . manufacturer & &
blacklist [ i ] . product = = ec - > cid . product ) {
2019-05-01 12:10:42 +01:00
if ( blacklist [ i ] . type )
ec - > card_desc = blacklist [ i ] . type ;
if ( blacklist [ i ] . init )
blacklist [ i ] . init ( ec ) ;
2005-04-16 15:20:36 -07:00
break ;
}
2012-03-01 17:50:33 +00:00
ec - > irq = irq ;
2005-04-16 15:20:36 -07:00
/*
* hook the interrupt handlers
*/
if ( slot < 8 ) {
2011-03-24 13:35:09 +01:00
irq_set_chip_and_handler ( ec - > irq , & ecard_chip ,
handle_level_irq ) ;
2012-03-01 17:54:11 +00:00
irq_set_chip_data ( ec - > irq , ec ) ;
2015-07-27 15:55:13 -05:00
irq_clear_status_flags ( ec - > irq , IRQ_NOREQUEST ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_ARCH_RPC
/* On RiscPC, only first two slots have DMA capability */
if ( slot < 2 )
ec - > dma = 2 + slot ;
# endif
for ( ecp = & cards ; * ecp ; ecp = & ( * ecp ) - > next ) ;
* ecp = ec ;
slot_to_expcard [ slot ] = ec ;
2012-04-30 16:26:01 +00:00
rc = device_register ( & ec - > dev ) ;
if ( rc )
goto nodev ;
2005-04-16 15:20:36 -07:00
return 0 ;
nodev :
ecard_free_card ( ec ) ;
nomem :
return rc ;
}
/*
* Initialise the expansion card system .
* Locate all hardware - interrupt management and
* actual cards .
*/
static int __init ecard_init ( void )
{
2007-04-26 00:04:40 -07:00
struct task_struct * task ;
2012-03-01 18:46:46 +00:00
int slot , irqbase ;
2012-03-01 17:50:33 +00:00
irqbase = irq_alloc_descs ( - 1 , 0 , 8 , - 1 ) ;
if ( irqbase < 0 )
return irqbase ;
2005-04-16 15:20:36 -07:00
2007-04-26 00:04:40 -07:00
task = kthread_run ( ecard_task , NULL , " kecardd " ) ;
if ( IS_ERR ( task ) ) {
2007-05-03 10:55:46 +01:00
printk ( KERN_ERR " Ecard: unable to create kernel thread: %ld \n " ,
2007-04-26 00:04:40 -07:00
PTR_ERR ( task ) ) ;
2012-03-01 17:50:33 +00:00
irq_free_descs ( irqbase , 8 ) ;
2007-04-26 00:04:40 -07:00
return PTR_ERR ( task ) ;
2005-04-16 15:20:36 -07:00
}
printk ( " Probing expansion cards \n " ) ;
for ( slot = 0 ; slot < 8 ; slot + + ) {
2012-03-01 17:50:33 +00:00
if ( ecard_probe ( slot , irqbase + slot , ECARD_EASI ) = = - ENODEV )
ecard_probe ( slot , irqbase + slot , ECARD_IOC ) ;
2005-04-16 15:20:36 -07:00
}
2012-03-01 17:50:33 +00:00
ecard_probe ( 8 , 11 , ECARD_IOC ) ;
2005-04-16 15:20:36 -07:00
2012-03-01 18:46:46 +00:00
irq_set_chained_handler ( IRQ_EXPANSIONCARD , ecard_irq_handler ) ;
2005-04-16 15:20:36 -07:00
ecard_proc_init ( ) ;
return 0 ;
}
subsys_initcall ( ecard_init ) ;
/*
* ECARD " bus "
*/
static const struct ecard_id *
ecard_match_device ( const struct ecard_id * ids , struct expansion_card * ec )
{
int i ;
for ( i = 0 ; ids [ i ] . manufacturer ! = 65535 ; i + + )
if ( ec - > cid . manufacturer = = ids [ i ] . manufacturer & &
ec - > cid . product = = ids [ i ] . product )
return ids + i ;
return NULL ;
}
static int ecard_drv_probe ( struct device * dev )
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
struct ecard_driver * drv = ECARD_DRV ( dev - > driver ) ;
const struct ecard_id * id ;
int ret ;
id = ecard_match_device ( drv - > id_table , ec ) ;
2008-04-19 16:01:38 +01:00
ec - > claimed = 1 ;
2005-04-16 15:20:36 -07:00
ret = drv - > probe ( ec , id ) ;
if ( ret )
2008-04-19 16:01:38 +01:00
ec - > claimed = 0 ;
2005-04-16 15:20:36 -07:00
return ret ;
}
static int ecard_drv_remove ( struct device * dev )
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
struct ecard_driver * drv = ECARD_DRV ( dev - > driver ) ;
drv - > remove ( ec ) ;
2008-04-19 16:01:38 +01:00
ec - > claimed = 0 ;
2005-04-16 15:20:36 -07:00
2007-05-10 16:46:13 +01:00
/*
* Restore the default operations . We ensure that the
* ops are set before we change the data .
*/
ec - > ops = & ecard_default_ops ;
barrier ( ) ;
ec - > irq_data = NULL ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* Before rebooting , we must make sure that the expansion card is in a
* sensible state , so it can be re - detected . This means that the first
* page of the ROM must be visible . We call the expansion cards reset
* handler , if any .
*/
static void ecard_drv_shutdown ( struct device * dev )
{
struct expansion_card * ec = ECARD_DEV ( dev ) ;
struct ecard_driver * drv = ECARD_DRV ( dev - > driver ) ;
struct ecard_request req ;
2006-01-05 14:30:57 +00:00
if ( dev - > driver ) {
if ( drv - > shutdown )
drv - > shutdown ( ec ) ;
2008-04-19 16:01:38 +01:00
ec - > claimed = 0 ;
2006-01-05 14:30:57 +00:00
}
2005-04-16 15:20:36 -07:00
/*
* If this card has a loader , call the reset handler .
*/
if ( ec - > loader ) {
req . fn = ecard_task_reset ;
req . ec = ec ;
ecard_call ( & req ) ;
}
}
int ecard_register_driver ( struct ecard_driver * drv )
{
drv - > drv . bus = & ecard_bus_type ;
return driver_register ( & drv - > drv ) ;
}
void ecard_remove_driver ( struct ecard_driver * drv )
{
driver_unregister ( & drv - > drv ) ;
}
static int ecard_match ( struct device * _dev , struct device_driver * _drv )
{
struct expansion_card * ec = ECARD_DEV ( _dev ) ;
struct ecard_driver * drv = ECARD_DRV ( _drv ) ;
int ret ;
if ( drv - > id_table ) {
ret = ecard_match_device ( drv - > id_table , ec ) ! = NULL ;
} else {
ret = ec - > cid . id = = drv - > id ;
}
return ret ;
}
struct bus_type ecard_bus_type = {
. name = " ecard " ,
2017-06-06 14:14:16 +02:00
. dev_groups = ecard_dev_groups ,
2005-04-16 15:20:36 -07:00
. match = ecard_match ,
2006-01-05 14:30:57 +00:00
. probe = ecard_drv_probe ,
. remove = ecard_drv_remove ,
. shutdown = ecard_drv_shutdown ,
2005-04-16 15:20:36 -07:00
} ;
static int ecard_bus_init ( void )
{
return bus_register ( & ecard_bus_type ) ;
}
postcore_initcall ( ecard_bus_init ) ;
EXPORT_SYMBOL ( ecard_readchunk ) ;
EXPORT_SYMBOL ( ecard_register_driver ) ;
EXPORT_SYMBOL ( ecard_remove_driver ) ;
EXPORT_SYMBOL ( ecard_bus_type ) ;