2019-05-19 13:08:20 +01:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/* hplance.c : the Linux/hp300/lance ethernet driver
*
* Copyright ( C ) 05 / 1998 Peter Maydell < pmaydell @ chiark . greenend . org . uk >
* Based on the Sun Lance driver and the NetBSD HP Lance driver
* Uses the generic 7990. c LANCE code .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/string.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/errno.h>
2020-06-08 21:32:42 -07:00
# include <linux/pgtable.h>
2005-04-16 15:20:36 -07:00
/* Used for the temporal inet entries and routing */
# include <linux/socket.h>
# include <linux/route.h>
# include <linux/dio.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <asm/io.h>
# include "hplance.h"
2014-06-05 11:01:22 +08:00
/* We have 16392 bytes of RAM for the init block and buffers. This places
2005-04-16 15:20:36 -07:00
* an upper limit on the number of buffers we can use . NetBSD uses 8 Rx
2014-06-05 11:01:22 +08:00
* buffers and 2 Tx buffers , it takes ( 8 + 2 ) * 1544 bytes .
2005-04-16 15:20:36 -07:00
*/
# define LANCE_LOG_TX_BUFFERS 1
# define LANCE_LOG_RX_BUFFERS 3
# include "7990.h" /* use generic LANCE code */
/* Our private data structure */
struct hplance_private {
struct lance_private lance ;
} ;
/* function prototypes... This is easy because all the grot is in the
* generic LANCE support . All we have to support is probing for boards ,
2006-09-13 13:24:59 -04:00
* plus board - specific init , open and close actions .
2005-04-16 15:20:36 -07:00
* Oh , and we need to tell the generic code how to read and write LANCE registers . . .
*/
2012-12-06 14:30:56 +00:00
static int hplance_init_one ( struct dio_dev * d , const struct dio_device_id * ent ) ;
static void hplance_init ( struct net_device * dev , struct dio_dev * d ) ;
2012-12-03 09:23:54 -05:00
static void hplance_remove_one ( struct dio_dev * d ) ;
2005-04-16 15:20:36 -07:00
static void hplance_writerap ( void * priv , unsigned short value ) ;
static void hplance_writerdp ( void * priv , unsigned short value ) ;
static unsigned short hplance_readrdp ( void * priv ) ;
static int hplance_open ( struct net_device * dev ) ;
static int hplance_close ( struct net_device * dev ) ;
static struct dio_device_id hplance_dio_tbl [ ] = {
{ DIO_ID_LAN } ,
{ 0 }
} ;
static struct dio_driver hplance_driver = {
. name = " hplance " ,
. id_table = hplance_dio_tbl ,
. probe = hplance_init_one ,
2012-12-03 09:23:54 -05:00
. remove = hplance_remove_one ,
2005-04-16 15:20:36 -07:00
} ;
2009-04-15 12:52:38 +00:00
static const struct net_device_ops hplance_netdev_ops = {
. ndo_open = hplance_open ,
. ndo_stop = hplance_close ,
. ndo_start_xmit = lance_start_xmit ,
2011-08-16 06:29:01 +00:00
. ndo_set_rx_mode = lance_set_multicast ,
2009-04-15 12:52:38 +00:00
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_mac_address = eth_mac_addr ,
# ifdef CONFIG_NET_POLL_CONTROLLER
. ndo_poll_controller = lance_poll ,
# endif
} ;
2005-04-16 15:20:36 -07:00
/* Find all the HP Lance boards and initialise them... */
2012-12-06 14:30:56 +00:00
static int hplance_init_one ( struct dio_dev * d , const struct dio_device_id * ent )
2005-04-16 15:20:36 -07:00
{
struct net_device * dev ;
int err = - ENOMEM ;
dev = alloc_etherdev ( sizeof ( struct hplance_private ) ) ;
if ( ! dev )
goto out ;
err = - EBUSY ;
if ( ! request_mem_region ( dio_resource_start ( d ) ,
dio_resource_len ( d ) , d - > name ) )
goto out_free_netdev ;
hplance_init ( dev , d ) ;
err = register_netdev ( dev ) ;
if ( err )
goto out_release_mem_region ;
dio_set_drvdata ( d , dev ) ;
2006-12-09 10:29:58 +01:00
2012-02-24 03:45:53 +00:00
printk ( KERN_INFO " %s: %s; select code %d, addr %pM, irq %d \n " ,
dev - > name , d - > name , d - > scode , dev - > dev_addr , d - > ipl ) ;
2006-12-09 10:29:58 +01:00
2005-04-16 15:20:36 -07:00
return 0 ;
out_release_mem_region :
release_mem_region ( dio_resource_start ( d ) , dio_resource_len ( d ) ) ;
out_free_netdev :
free_netdev ( dev ) ;
out :
return err ;
}
2012-12-03 09:23:54 -05:00
static void hplance_remove_one ( struct dio_dev * d )
2005-04-16 15:20:36 -07:00
{
struct net_device * dev = dio_get_drvdata ( d ) ;
unregister_netdev ( dev ) ;
release_mem_region ( dio_resource_start ( d ) , dio_resource_len ( d ) ) ;
free_netdev ( dev ) ;
}
/* Initialise a single lance board at the given DIO device */
2012-12-03 09:23:54 -05:00
static void hplance_init ( struct net_device * dev , struct dio_dev * d )
2005-04-16 15:20:36 -07:00
{
2013-12-28 21:11:14 +01:00
unsigned long va = ( d - > resource . start + DIO_VIRADDRBASE ) ;
struct hplance_private * lp ;
2021-11-18 23:10:22 -08:00
u8 addr [ ETH_ALEN ] ;
2013-12-28 21:11:14 +01:00
int i ;
/* reset the board */
out_8 ( va + DIO_IDOFF , 0xff ) ;
udelay ( 100 ) ; /* ariba! ariba! udelay! udelay! */
/* Fill the dev fields */
dev - > base_addr = va ;
dev - > netdev_ops = & hplance_netdev_ops ;
dev - > dma = 0 ;
for ( i = 0 ; i < 6 ; i + + ) {
/* The NVRAM holds our ethernet address, one nibble per byte,
* at bytes NVRAMOFF + 1 , 3 , 5 , 7 , 9. . .
*/
2021-11-18 23:10:22 -08:00
addr [ i ] = ( ( in_8 ( va + HPLANCE_NVRAMOFF + i * 4 + 1 ) & 0xF ) < < 4 )
2013-12-28 21:11:14 +01:00
| ( in_8 ( va + HPLANCE_NVRAMOFF + i * 4 + 3 ) & 0xF ) ;
}
2021-11-18 23:10:22 -08:00
eth_hw_addr_set ( dev , addr ) ;
2013-12-28 21:11:14 +01:00
lp = netdev_priv ( dev ) ;
2013-12-28 21:11:15 +01:00
lp - > lance . name = d - > name ;
2013-12-28 21:11:14 +01:00
lp - > lance . base = va ;
lp - > lance . init_block = ( struct lance_init_block * ) ( va + HPLANCE_MEMOFF ) ; /* CPU addr */
lp - > lance . lance_init_block = NULL ; /* LANCE addr of same RAM */
lp - > lance . busmaster_regval = LE_C3_BSWP ; /* we're bigendian */
lp - > lance . irq = d - > ipl ;
lp - > lance . writerap = hplance_writerap ;
lp - > lance . writerdp = hplance_writerdp ;
lp - > lance . readrdp = hplance_readrdp ;
lp - > lance . lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS ;
lp - > lance . lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS ;
lp - > lance . rx_ring_mod_mask = RX_RING_MOD_MASK ;
lp - > lance . tx_ring_mod_mask = TX_RING_MOD_MASK ;
2005-04-16 15:20:36 -07:00
}
/* This is disgusting. We have to check the DIO status register for ack every
* time we read or write the LANCE registers .
*/
static void hplance_writerap ( void * priv , unsigned short value )
{
struct lance_private * lp = ( struct lance_private * ) priv ;
2021-03-31 16:18:31 +08:00
2005-04-16 15:20:36 -07:00
do {
out_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RAP , value ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
}
static void hplance_writerdp ( void * priv , unsigned short value )
{
struct lance_private * lp = ( struct lance_private * ) priv ;
2021-03-31 16:18:31 +08:00
2005-04-16 15:20:36 -07:00
do {
out_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RDP , value ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
}
static unsigned short hplance_readrdp ( void * priv )
{
struct lance_private * lp = ( struct lance_private * ) priv ;
__u16 value ;
2021-03-31 16:18:31 +08:00
2005-04-16 15:20:36 -07:00
do {
value = in_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RDP ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
return value ;
}
static int hplance_open ( struct net_device * dev )
{
2013-12-28 21:11:14 +01:00
int status ;
struct lance_private * lp = netdev_priv ( dev ) ;
2006-09-13 13:24:59 -04:00
2013-12-28 21:11:14 +01:00
status = lance_open ( dev ) ; /* call generic lance open code */
if ( status )
return status ;
/* enable interrupts at board level. */
out_8 ( lp - > base + HPLANCE_STATUS , LE_IE ) ;
2005-04-16 15:20:36 -07:00
2013-12-28 21:11:14 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static int hplance_close ( struct net_device * dev )
{
2013-12-28 21:11:14 +01:00
struct lance_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
2013-12-28 21:11:14 +01:00
out_8 ( lp - > base + HPLANCE_STATUS , 0 ) ; /* disable interrupts at boardlevel */
lance_close ( dev ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-06-10 01:23:32 +03:00
static int __init hplance_init_module ( void )
2005-04-16 15:20:36 -07:00
{
2006-03-25 03:07:17 -08:00
return dio_register_driver ( & hplance_driver ) ;
2005-04-16 15:20:36 -07:00
}
2008-06-10 01:23:32 +03:00
static void __exit hplance_cleanup_module ( void )
2005-04-16 15:20:36 -07:00
{
2013-12-28 21:11:14 +01:00
dio_unregister_driver ( & hplance_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( hplance_init_module ) ;
module_exit ( hplance_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;