Commit 58e5528e authored by Michael Buesch's avatar Michael Buesch Committed by John W. Linville

[PATCH] bcm43xx: init routine rewrite

Rewrite of the bcm43xx initialization routines.
This fixes several issues:
* up-down-up-down-up... stale data issue
  (May fix some DHCP issues)
* Fix the init vs IRQ handler race (and remove the workaround)
* Fix init for cards with multiple cores (APHY)
  As softmac has no internal PHY handling (unlike dscape),
  this adds the file "phymode" to sysfs.
  The active PHY can be selected by writing either a, b or g
  to this file. Current PHY can be determined by reading from it.
* Fix the controller restart code.
  Controller restart can now also be triggered through
  echo 1 > /debug/bcm43xx/ethX/restart
Signed-off-by: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 3234faa8
...@@ -504,6 +504,12 @@ struct bcm43xx_phyinfo { ...@@ -504,6 +504,12 @@ struct bcm43xx_phyinfo {
* This lock is only used by bcm43xx_phy_{un}lock() * This lock is only used by bcm43xx_phy_{un}lock()
*/ */
spinlock_t lock; spinlock_t lock;
/* Firmware. */
const struct firmware *ucode;
const struct firmware *pcm;
const struct firmware *initvals0;
const struct firmware *initvals1;
}; };
...@@ -593,12 +599,14 @@ struct bcm43xx_coreinfo { ...@@ -593,12 +599,14 @@ struct bcm43xx_coreinfo {
u8 available:1, u8 available:1,
enabled:1, enabled:1,
initialized:1; initialized:1;
/** core_id ID number */
u16 id;
/** core_rev revision number */ /** core_rev revision number */
u8 rev; u8 rev;
/** Index number for _switch_core() */ /** Index number for _switch_core() */
u8 index; u8 index;
/** core_id ID number */
u16 id;
/** Core-specific data. */
void *priv;
}; };
/* Additional information for each 80211 core. */ /* Additional information for each 80211 core. */
...@@ -647,7 +655,10 @@ enum { ...@@ -647,7 +655,10 @@ enum {
BCM43xx_STAT_RESTARTING, /* controller_restart() called. */ BCM43xx_STAT_RESTARTING, /* controller_restart() called. */
}; };
#define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status) #define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status)
#define bcm43xx_set_status(bcm, stat) atomic_set(&(bcm)->init_status, (stat)) #define bcm43xx_set_status(bcm, stat) do { \
atomic_set(&(bcm)->init_status, (stat)); \
smp_wmb(); \
} while (0)
/* *** THEORY OF LOCKING *** /* *** THEORY OF LOCKING ***
* *
...@@ -721,10 +732,6 @@ struct bcm43xx_private { ...@@ -721,10 +732,6 @@ struct bcm43xx_private {
struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ]; struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];
/* Additional information, specific to the 80211 cores. */ /* Additional information, specific to the 80211 cores. */
struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ]; struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];
/* Index of the current 80211 core. If current_core is not
* an 80211 core, this is -1.
*/
int current_80211_core_idx;
/* Number of available 80211 cores. */ /* Number of available 80211 cores. */
int nr_80211_available; int nr_80211_available;
...@@ -761,12 +768,6 @@ struct bcm43xx_private { ...@@ -761,12 +768,6 @@ struct bcm43xx_private {
struct bcm43xx_key key[54]; struct bcm43xx_key key[54];
u8 default_key_idx; u8 default_key_idx;
/* Firmware. */
const struct firmware *ucode;
const struct firmware *pcm;
const struct firmware *initvals0;
const struct firmware *initvals1;
/* Random Number Generator. */ /* Random Number Generator. */
struct hwrng rng; struct hwrng rng;
char rng_name[20 + 1]; char rng_name[20 + 1];
...@@ -829,34 +830,33 @@ int bcm43xx_using_pio(struct bcm43xx_private *bcm) ...@@ -829,34 +830,33 @@ int bcm43xx_using_pio(struct bcm43xx_private *bcm)
* any of these functions. * any of these functions.
*/ */
static inline static inline
struct bcm43xx_coreinfo_80211 *
bcm43xx_current_80211_priv(struct bcm43xx_private *bcm)
{
assert(bcm->current_core->id == BCM43xx_COREID_80211);
return bcm->current_core->priv;
}
static inline
struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm) struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)
{ {
assert(bcm43xx_using_pio(bcm)); assert(bcm43xx_using_pio(bcm));
assert(bcm->current_80211_core_idx >= 0); return &(bcm43xx_current_80211_priv(bcm)->pio);
assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio);
} }
static inline static inline
struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm) struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)
{ {
assert(!bcm43xx_using_pio(bcm)); assert(!bcm43xx_using_pio(bcm));
assert(bcm->current_80211_core_idx >= 0); return &(bcm43xx_current_80211_priv(bcm)->dma);
assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma);
} }
static inline static inline
struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm) struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)
{ {
assert(bcm->current_80211_core_idx >= 0); return &(bcm43xx_current_80211_priv(bcm)->phy);
assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy);
} }
static inline static inline
struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm) struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)
{ {
assert(bcm->current_80211_core_idx >= 0); return &(bcm43xx_current_80211_priv(bcm)->radio);
assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio);
} }
......
...@@ -316,6 +316,40 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf, ...@@ -316,6 +316,40 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf,
return res; return res;
} }
static ssize_t restart_write_file(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
ssize_t buf_size;
ssize_t res;
unsigned long flags;
buf_size = min(count, sizeof (really_big_buffer) - 1);
down(&big_buffer_sem);
if (copy_from_user(buf, user_buf, buf_size)) {
res = -EFAULT;
goto out_up;
}
bcm43xx_lock_irqsafe(bcm, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
res = -EFAULT;
goto out_unlock;
}
if (count > 0 && buf[0] == '1') {
bcm43xx_controller_restart(bcm, "manually restarted");
res = count;
} else
res = -EINVAL;
out_unlock:
bcm43xx_unlock_irqsafe(bcm, flags);
out_up:
up(&big_buffer_sem);
return res;
}
#undef fappend #undef fappend
...@@ -349,6 +383,11 @@ static struct file_operations txstat_fops = { ...@@ -349,6 +383,11 @@ static struct file_operations txstat_fops = {
.open = open_file_generic, .open = open_file_generic,
}; };
static struct file_operations restart_fops = {
.write = restart_write_file,
.open = open_file_generic,
};
void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
{ {
...@@ -400,6 +439,10 @@ void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) ...@@ -400,6 +439,10 @@ void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
bcm, &txstat_fops); bcm, &txstat_fops);
if (!e->dentry_txstat) if (!e->dentry_txstat)
printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir); printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);
e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir,
bcm, &restart_fops);
if (!e->dentry_restart)
printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir);
} }
void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
...@@ -415,6 +458,7 @@ void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) ...@@ -415,6 +458,7 @@ void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
debugfs_remove(e->dentry_devinfo); debugfs_remove(e->dentry_devinfo);
debugfs_remove(e->dentry_tsf); debugfs_remove(e->dentry_tsf);
debugfs_remove(e->dentry_txstat); debugfs_remove(e->dentry_txstat);
debugfs_remove(e->dentry_restart);
debugfs_remove(e->subdir); debugfs_remove(e->subdir);
kfree(e->xmitstatus_buffer); kfree(e->xmitstatus_buffer);
kfree(e->xmitstatus_print_buffer); kfree(e->xmitstatus_print_buffer);
......
...@@ -20,6 +20,7 @@ struct bcm43xx_dfsentry { ...@@ -20,6 +20,7 @@ struct bcm43xx_dfsentry {
struct dentry *dentry_spromdump; struct dentry *dentry_spromdump;
struct dentry *dentry_tsf; struct dentry *dentry_tsf;
struct dentry *dentry_txstat; struct dentry *dentry_txstat;
struct dentry *dentry_restart;
struct bcm43xx_private *bcm; struct bcm43xx_private *bcm;
......
This diff is collapsed.
...@@ -133,6 +133,9 @@ void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm); ...@@ -133,6 +133,9 @@ void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm);
int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core); int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);
int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
int phytype);
void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy); void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
void bcm43xx_mac_suspend(struct bcm43xx_private *bcm); void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
......
...@@ -309,6 +309,70 @@ static DEVICE_ATTR(shortpreamble, 0644, ...@@ -309,6 +309,70 @@ static DEVICE_ATTR(shortpreamble, 0644,
bcm43xx_attr_preamble_show, bcm43xx_attr_preamble_show,
bcm43xx_attr_preamble_store); bcm43xx_attr_preamble_store);
static ssize_t bcm43xx_attr_phymode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
int phytype;
int err = -EINVAL;
if (count < 1)
goto out;
switch (buf[0]) {
case 'a': case 'A':
phytype = BCM43xx_PHYTYPE_A;
break;
case 'b': case 'B':
phytype = BCM43xx_PHYTYPE_B;
break;
case 'g': case 'G':
phytype = BCM43xx_PHYTYPE_G;
break;
default:
goto out;
}
bcm43xx_lock_noirq(bcm);
err = bcm43xx_select_wireless_core(bcm, phytype);
bcm43xx_unlock_noirq(bcm);
if (err == -ESRCH)
err = -ENODEV;
out:
return err ? err : count;
}
static ssize_t bcm43xx_attr_phymode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
ssize_t count = 0;
bcm43xx_lock_noirq(bcm);
switch (bcm43xx_current_phy(bcm)->type) {
case BCM43xx_PHYTYPE_A:
snprintf(buf, PAGE_SIZE, "A");
break;
case BCM43xx_PHYTYPE_B:
snprintf(buf, PAGE_SIZE, "B");
break;
case BCM43xx_PHYTYPE_G:
snprintf(buf, PAGE_SIZE, "G");
break;
default:
assert(0);
}
bcm43xx_unlock_noirq(bcm);
return count;
}
static DEVICE_ATTR(phymode, 0644,
bcm43xx_attr_phymode_show,
bcm43xx_attr_phymode_store);
int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
{ {
struct device *dev = &bcm->pci_dev->dev; struct device *dev = &bcm->pci_dev->dev;
...@@ -325,9 +389,14 @@ int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) ...@@ -325,9 +389,14 @@ int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
err = device_create_file(dev, &dev_attr_shortpreamble); err = device_create_file(dev, &dev_attr_shortpreamble);
if (err) if (err)
goto err_remove_interfmode; goto err_remove_interfmode;
err = device_create_file(dev, &dev_attr_phymode);
if (err)
goto err_remove_shortpreamble;
out: out:
return err; return err;
err_remove_shortpreamble:
device_remove_file(dev, &dev_attr_shortpreamble);
err_remove_interfmode: err_remove_interfmode:
device_remove_file(dev, &dev_attr_interference); device_remove_file(dev, &dev_attr_interference);
err_remove_sprom: err_remove_sprom:
...@@ -339,6 +408,7 @@ void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm) ...@@ -339,6 +408,7 @@ void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
{ {
struct device *dev = &bcm->pci_dev->dev; struct device *dev = &bcm->pci_dev->dev;
device_remove_file(dev, &dev_attr_phymode);
device_remove_file(dev, &dev_attr_shortpreamble); device_remove_file(dev, &dev_attr_shortpreamble);
device_remove_file(dev, &dev_attr_interference); device_remove_file(dev, &dev_attr_interference);
device_remove_file(dev, &dev_attr_sprom); device_remove_file(dev, &dev_attr_sprom);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment