2007-09-25 17:57:13 -07:00
/*
2009-11-08 16:39:55 +01:00
Copyright ( C ) 2004 - 2009 Ivo van Doorn < IvDoorn @ gmail . com >
2007-09-25 17:57:13 -07:00
< http : //rt2x00.serialmonkey.com>
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
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 .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the
Free Software Foundation , Inc . ,
59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
/*
Module : rt2x00pci
Abstract : rt2x00 generic pci device routines .
*/
# include <linux/dma-mapping.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include "rt2x00.h"
# include "rt2x00pci.h"
2008-11-10 19:41:40 +01:00
/*
* Register access .
*/
int rt2x00pci_regbusy_read ( struct rt2x00_dev * rt2x00dev ,
const unsigned int offset ,
const struct rt2x00_field32 field ,
u32 * reg )
{
unsigned int i ;
for ( i = 0 ; i < REGISTER_BUSY_COUNT ; i + + ) {
rt2x00pci_register_read ( rt2x00dev , offset , reg ) ;
if ( ! rt2x00_get_field32 ( * reg , field ) )
return 1 ;
udelay ( REGISTER_BUSY_DELAY ) ;
}
ERROR ( rt2x00dev , " Indirect register access failed: "
" offset=0x%.08x, value=0x%.08x \n " , offset , * reg ) ;
* reg = ~ 0 ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_regbusy_read ) ;
2007-09-25 17:57:13 -07:00
/*
* TX data handlers .
*/
2008-06-06 22:50:28 +02:00
int rt2x00pci_write_tx_data ( struct queue_entry * entry )
2007-09-25 17:57:13 -07:00
{
2008-11-08 15:25:33 +01:00
struct rt2x00_dev * rt2x00dev = entry - > queue - > rt2x00dev ;
2008-05-10 13:46:03 +02:00
struct queue_entry_priv_pci * entry_priv = entry - > priv_data ;
2008-02-05 16:42:23 -05:00
struct skb_frame_desc * skbdesc ;
2007-09-25 17:57:13 -07:00
2008-06-06 22:50:28 +02:00
/*
* This should not happen , we already checked the entry
* was ours . When the hardware disagrees there has been
* a queue corruption !
*/
2008-11-08 15:25:33 +01:00
if ( unlikely ( rt2x00dev - > ops - > lib - > get_entry_state ( entry ) ) ) {
ERROR ( rt2x00dev ,
2008-06-06 22:50:28 +02:00
" Corrupt queue %d, accessing entry which is not ours. \n "
2007-09-25 17:57:13 -07:00
" Please file bug report to %s. \n " ,
2008-04-21 19:00:47 +02:00
entry - > queue - > qid , DRV_PROJECT ) ;
2007-09-25 17:57:13 -07:00
return - EINVAL ;
}
2008-01-24 01:56:25 -08:00
/*
* Fill in skb descriptor
*/
2008-06-06 22:50:28 +02:00
skbdesc = get_skb_frame_desc ( entry - > skb ) ;
2008-05-10 13:46:03 +02:00
skbdesc - > desc = entry_priv - > desc ;
2008-06-06 22:50:28 +02:00
skbdesc - > desc_len = entry - > queue - > desc_size ;
2007-09-25 17:57:13 -07:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_write_tx_data ) ;
/*
2007-11-12 15:02:40 +01:00
* TX / RX data handlers .
2007-09-25 17:57:13 -07:00
*/
void rt2x00pci_rxdone ( struct rt2x00_dev * rt2x00dev )
{
2008-02-05 16:42:23 -05:00
struct data_queue * queue = rt2x00dev - > rx ;
struct queue_entry * entry ;
2008-05-10 13:46:03 +02:00
struct queue_entry_priv_pci * entry_priv ;
2008-06-16 19:56:31 +02:00
struct skb_frame_desc * skbdesc ;
2007-09-25 17:57:13 -07:00
while ( 1 ) {
2008-02-05 16:42:23 -05:00
entry = rt2x00queue_get_entry ( queue , Q_INDEX ) ;
2008-05-10 13:46:03 +02:00
entry_priv = entry - > priv_data ;
2007-09-25 17:57:13 -07:00
2008-11-08 15:25:33 +01:00
if ( rt2x00dev - > ops - > lib - > get_entry_state ( entry ) )
2007-09-25 17:57:13 -07:00
break ;
2008-06-16 19:56:31 +02:00
/*
* Fill in desc fields of the skb descriptor
*/
skbdesc = get_skb_frame_desc ( entry - > skb ) ;
skbdesc - > desc = entry_priv - > desc ;
skbdesc - > desc_len = entry - > queue - > desc_size ;
/*
* Send the frame to rt2x00lib for further processing .
*/
rt2x00lib_rxdone ( rt2x00dev , entry ) ;
2007-09-25 17:57:13 -07:00
}
}
EXPORT_SYMBOL_GPL ( rt2x00pci_rxdone ) ;
/*
* Device initialization handlers .
*/
2008-02-05 16:42:23 -05:00
static int rt2x00pci_alloc_queue_dma ( struct rt2x00_dev * rt2x00dev ,
struct data_queue * queue )
2007-09-25 17:57:13 -07:00
{
2008-05-10 13:46:03 +02:00
struct queue_entry_priv_pci * entry_priv ;
2008-02-17 17:33:24 +01:00
void * addr ;
dma_addr_t dma ;
2007-09-25 17:57:13 -07:00
unsigned int i ;
/*
* Allocate DMA memory for descriptor and buffer .
*/
2008-06-16 19:56:31 +02:00
addr = dma_alloc_coherent ( rt2x00dev - > dev ,
queue - > limit * queue - > desc_size ,
& dma , GFP_KERNEL | GFP_DMA ) ;
2008-02-17 17:33:24 +01:00
if ( ! addr )
2007-09-25 17:57:13 -07:00
return - ENOMEM ;
2008-06-16 19:56:31 +02:00
memset ( addr , 0 , queue - > limit * queue - > desc_size ) ;
2008-02-10 22:46:52 +01:00
2007-09-25 17:57:13 -07:00
/*
2008-02-05 16:42:23 -05:00
* Initialize all queue entries to contain valid addresses .
2007-09-25 17:57:13 -07:00
*/
2008-02-05 16:42:23 -05:00
for ( i = 0 ; i < queue - > limit ; i + + ) {
2008-05-10 13:46:03 +02:00
entry_priv = queue - > entries [ i ] . priv_data ;
2008-06-16 19:56:31 +02:00
entry_priv - > desc = addr + i * queue - > desc_size ;
entry_priv - > desc_dma = dma + i * queue - > desc_size ;
2007-09-25 17:57:13 -07:00
}
return 0 ;
}
2008-02-05 16:42:23 -05:00
static void rt2x00pci_free_queue_dma ( struct rt2x00_dev * rt2x00dev ,
struct data_queue * queue )
2007-09-25 17:57:13 -07:00
{
2008-05-10 13:46:03 +02:00
struct queue_entry_priv_pci * entry_priv =
queue - > entries [ 0 ] . priv_data ;
2008-02-05 16:42:23 -05:00
2008-06-16 19:56:31 +02:00
if ( entry_priv - > desc )
dma_free_coherent ( rt2x00dev - > dev ,
queue - > limit * queue - > desc_size ,
entry_priv - > desc , entry_priv - > desc_dma ) ;
entry_priv - > desc = NULL ;
2007-09-25 17:57:13 -07:00
}
int rt2x00pci_initialize ( struct rt2x00_dev * rt2x00dev )
{
2008-02-05 16:42:23 -05:00
struct data_queue * queue ;
2007-09-25 17:57:13 -07:00
int status ;
/*
* Allocate DMA
*/
2008-02-05 16:42:23 -05:00
queue_for_each ( rt2x00dev , queue ) {
status = rt2x00pci_alloc_queue_dma ( rt2x00dev , queue ) ;
2007-09-25 17:57:13 -07:00
if ( status )
goto exit ;
}
/*
* Register interrupt handler .
*/
2009-03-28 20:51:24 +01:00
status = request_irq ( rt2x00dev - > irq , rt2x00dev - > ops - > lib - > irq_handler ,
IRQF_SHARED , rt2x00dev - > name , rt2x00dev ) ;
2007-09-25 17:57:13 -07:00
if ( status ) {
ERROR ( rt2x00dev , " IRQ %d allocation failed (error %d). \n " ,
2009-03-28 20:51:24 +01:00
rt2x00dev - > irq , status ) ;
2008-05-05 17:24:03 +02:00
goto exit ;
2007-09-25 17:57:13 -07:00
}
return 0 ;
exit :
2008-05-05 17:24:03 +02:00
queue_for_each ( rt2x00dev , queue )
rt2x00pci_free_queue_dma ( rt2x00dev , queue ) ;
2007-09-25 17:57:13 -07:00
return status ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_initialize ) ;
void rt2x00pci_uninitialize ( struct rt2x00_dev * rt2x00dev )
{
2008-02-05 16:42:23 -05:00
struct data_queue * queue ;
2007-09-25 17:57:13 -07:00
/*
* Free irq line .
*/
2008-06-16 19:55:43 +02:00
free_irq ( to_pci_dev ( rt2x00dev - > dev ) - > irq , rt2x00dev ) ;
2007-09-25 17:57:13 -07:00
/*
* Free DMA
*/
2008-02-05 16:42:23 -05:00
queue_for_each ( rt2x00dev , queue )
rt2x00pci_free_queue_dma ( rt2x00dev , queue ) ;
2007-09-25 17:57:13 -07:00
}
EXPORT_SYMBOL_GPL ( rt2x00pci_uninitialize ) ;
/*
* PCI driver handlers .
*/
static void rt2x00pci_free_reg ( struct rt2x00_dev * rt2x00dev )
{
kfree ( rt2x00dev - > rf ) ;
rt2x00dev - > rf = NULL ;
kfree ( rt2x00dev - > eeprom ) ;
rt2x00dev - > eeprom = NULL ;
2008-02-10 22:49:13 +01:00
if ( rt2x00dev - > csr . base ) {
iounmap ( rt2x00dev - > csr . base ) ;
rt2x00dev - > csr . base = NULL ;
2007-09-25 17:57:13 -07:00
}
}
static int rt2x00pci_alloc_reg ( struct rt2x00_dev * rt2x00dev )
{
2008-06-16 19:55:43 +02:00
struct pci_dev * pci_dev = to_pci_dev ( rt2x00dev - > dev ) ;
2007-09-25 17:57:13 -07:00
2008-10-20 21:42:39 -07:00
rt2x00dev - > csr . base = pci_ioremap_bar ( pci_dev , 0 ) ;
2008-02-10 22:49:13 +01:00
if ( ! rt2x00dev - > csr . base )
2007-09-25 17:57:13 -07:00
goto exit ;
rt2x00dev - > eeprom = kzalloc ( rt2x00dev - > ops - > eeprom_size , GFP_KERNEL ) ;
if ( ! rt2x00dev - > eeprom )
goto exit ;
rt2x00dev - > rf = kzalloc ( rt2x00dev - > ops - > rf_size , GFP_KERNEL ) ;
if ( ! rt2x00dev - > rf )
goto exit ;
return 0 ;
exit :
ERROR_PROBE ( " Failed to allocate registers. \n " ) ;
rt2x00pci_free_reg ( rt2x00dev ) ;
return - ENOMEM ;
}
int rt2x00pci_probe ( struct pci_dev * pci_dev , const struct pci_device_id * id )
{
struct rt2x00_ops * ops = ( struct rt2x00_ops * ) id - > driver_data ;
struct ieee80211_hw * hw ;
struct rt2x00_dev * rt2x00dev ;
int retval ;
2009-03-28 20:51:24 +01:00
u16 chip ;
2007-09-25 17:57:13 -07:00
retval = pci_request_regions ( pci_dev , pci_name ( pci_dev ) ) ;
if ( retval ) {
ERROR_PROBE ( " PCI request regions failed. \n " ) ;
return retval ;
}
retval = pci_enable_device ( pci_dev ) ;
if ( retval ) {
ERROR_PROBE ( " Enable device failed. \n " ) ;
goto exit_release_regions ;
}
pci_set_master ( pci_dev ) ;
if ( pci_set_mwi ( pci_dev ) )
ERROR_PROBE ( " MWI not available. \n " ) ;
2009-04-06 19:01:15 -07:00
if ( dma_set_mask ( & pci_dev - > dev , DMA_BIT_MASK ( 32 ) ) ) {
2007-09-25 17:57:13 -07:00
ERROR_PROBE ( " PCI DMA not supported. \n " ) ;
retval = - EIO ;
goto exit_disable_device ;
}
hw = ieee80211_alloc_hw ( sizeof ( struct rt2x00_dev ) , ops - > hw ) ;
if ( ! hw ) {
ERROR_PROBE ( " Failed to allocate hardware. \n " ) ;
retval = - ENOMEM ;
goto exit_disable_device ;
}
pci_set_drvdata ( pci_dev , hw ) ;
rt2x00dev = hw - > priv ;
2008-06-16 19:55:43 +02:00
rt2x00dev - > dev = & pci_dev - > dev ;
2007-09-25 17:57:13 -07:00
rt2x00dev - > ops = ops ;
rt2x00dev - > hw = hw ;
2009-03-28 20:51:24 +01:00
rt2x00dev - > irq = pci_dev - > irq ;
rt2x00dev - > name = pci_name ( pci_dev ) ;
2009-11-08 12:30:14 +01:00
rt2x00_set_chip_intf ( rt2x00dev , RT2X00_CHIP_INTF_PCI ) ;
2009-03-28 20:51:24 +01:00
/*
* Determine RT chipset by reading PCI header .
*/
pci_read_config_word ( pci_dev , PCI_DEVICE_ID , & chip ) ;
rt2x00_set_chip_rt ( rt2x00dev , chip ) ;
2007-09-25 17:57:13 -07:00
retval = rt2x00pci_alloc_reg ( rt2x00dev ) ;
if ( retval )
goto exit_free_device ;
retval = rt2x00lib_probe_dev ( rt2x00dev ) ;
if ( retval )
goto exit_free_reg ;
return 0 ;
exit_free_reg :
rt2x00pci_free_reg ( rt2x00dev ) ;
exit_free_device :
ieee80211_free_hw ( hw ) ;
exit_disable_device :
if ( retval ! = - EBUSY )
pci_disable_device ( pci_dev ) ;
exit_release_regions :
pci_release_regions ( pci_dev ) ;
pci_set_drvdata ( pci_dev , NULL ) ;
return retval ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_probe ) ;
void rt2x00pci_remove ( struct pci_dev * pci_dev )
{
struct ieee80211_hw * hw = pci_get_drvdata ( pci_dev ) ;
struct rt2x00_dev * rt2x00dev = hw - > priv ;
/*
* Free all allocated data .
*/
rt2x00lib_remove_dev ( rt2x00dev ) ;
rt2x00pci_free_reg ( rt2x00dev ) ;
ieee80211_free_hw ( hw ) ;
/*
* Free the PCI device data .
*/
pci_set_drvdata ( pci_dev , NULL ) ;
pci_disable_device ( pci_dev ) ;
pci_release_regions ( pci_dev ) ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_remove ) ;
# ifdef CONFIG_PM
int rt2x00pci_suspend ( struct pci_dev * pci_dev , pm_message_t state )
{
struct ieee80211_hw * hw = pci_get_drvdata ( pci_dev ) ;
struct rt2x00_dev * rt2x00dev = hw - > priv ;
int retval ;
retval = rt2x00lib_suspend ( rt2x00dev , state ) ;
if ( retval )
return retval ;
pci_save_state ( pci_dev ) ;
pci_disable_device ( pci_dev ) ;
return pci_set_power_state ( pci_dev , pci_choose_state ( pci_dev , state ) ) ;
}
EXPORT_SYMBOL_GPL ( rt2x00pci_suspend ) ;
int rt2x00pci_resume ( struct pci_dev * pci_dev )
{
struct ieee80211_hw * hw = pci_get_drvdata ( pci_dev ) ;
struct rt2x00_dev * rt2x00dev = hw - > priv ;
if ( pci_set_power_state ( pci_dev , PCI_D0 ) | |
pci_enable_device ( pci_dev ) | |
pci_restore_state ( pci_dev ) ) {
ERROR ( rt2x00dev , " Failed to resume device. \n " ) ;
return - EIO ;
}
2009-03-28 20:51:58 +01:00
return rt2x00lib_resume ( rt2x00dev ) ;
2007-09-25 17:57:13 -07:00
}
EXPORT_SYMBOL_GPL ( rt2x00pci_resume ) ;
# endif /* CONFIG_PM */
/*
* rt2x00pci module information .
*/
MODULE_AUTHOR ( DRV_PROJECT ) ;
MODULE_VERSION ( DRV_VERSION ) ;
2008-02-05 16:42:23 -05:00
MODULE_DESCRIPTION ( " rt2x00 pci library " ) ;
2007-09-25 17:57:13 -07:00
MODULE_LICENSE ( " GPL " ) ;