Commit 9a2f037c authored by Ben Dooks's avatar Ben Dooks Committed by Jeff Garzik

DM9000: Add mutex to protect access

Add a mutex to serialise access to the chip functions from
entries such as the ethtool and the MII code. This should
reduce the amount of time the spinlock is held to protect
the address register.
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 86c62fab
...@@ -102,6 +102,24 @@ static int watchdog = 5000; ...@@ -102,6 +102,24 @@ static int watchdog = 5000;
module_param(watchdog, int, 0400); module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
/* DM9000 register address locking.
*
* The DM9000 uses an address register to control where data written
* to the data register goes. This means that the address register
* must be preserved over interrupts or similar calls.
*
* During interrupt and other critical calls, a spinlock is used to
* protect the system, but the calls themselves save the address
* in the address register in case they are interrupting another
* access to the device.
*
* For general accesses a lock is provided so that calls which are
* allowed to sleep are serialised so that the address register does
* not need to be saved. This lock also serves to serialise access
* to the EEPROM and PHY access registers which are shared between
* these two devices.
*/
/* Structure/enum declaration ------------------------------- */ /* Structure/enum declaration ------------------------------- */
typedef struct board_info { typedef struct board_info {
...@@ -132,6 +150,8 @@ typedef struct board_info { ...@@ -132,6 +150,8 @@ typedef struct board_info {
struct resource *data_req; struct resource *data_req;
struct resource *irq_res; struct resource *irq_res;
struct mutex addr_lock; /* phy and eeprom access lock */
spinlock_t lock; spinlock_t lock;
struct mii_if_info mii; struct mii_if_info mii;
...@@ -365,26 +385,16 @@ static void dm9000_get_drvinfo(struct net_device *dev, ...@@ -365,26 +385,16 @@ static void dm9000_get_drvinfo(struct net_device *dev,
static int dm9000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int dm9000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
board_info_t *dm = to_dm9000_board(dev); board_info_t *dm = to_dm9000_board(dev);
unsigned long flags;
spin_lock_irqsave(&dm->lock, flags);
mii_ethtool_gset(&dm->mii, cmd); mii_ethtool_gset(&dm->mii, cmd);
spin_lock_irqsave(&dm->lock, flags);
return 0; return 0;
} }
static int dm9000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int dm9000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
board_info_t *dm = to_dm9000_board(dev); board_info_t *dm = to_dm9000_board(dev);
unsigned long flags;
int rc;
spin_lock_irqsave(&dm->lock, flags);
rc = mii_ethtool_sset(&dm->mii, cmd);
spin_lock_irqsave(&dm->lock, flags);
return rc; return mii_ethtool_sset(&dm->mii, cmd);
} }
static int dm9000_nway_reset(struct net_device *dev) static int dm9000_nway_reset(struct net_device *dev)
...@@ -475,6 +485,7 @@ dm9000_probe(struct platform_device *pdev) ...@@ -475,6 +485,7 @@ dm9000_probe(struct platform_device *pdev)
db->dev = &pdev->dev; db->dev = &pdev->dev;
spin_lock_init(&db->lock); spin_lock_init(&db->lock);
mutex_init(&db->addr_lock);
if (pdev->num_resources < 2) { if (pdev->num_resources < 2) {
ret = -ENODEV; ret = -ENODEV;
...@@ -997,8 +1008,10 @@ dm9000_rx(struct net_device *dev) ...@@ -997,8 +1008,10 @@ dm9000_rx(struct net_device *dev)
* Read a word data from EEPROM * Read a word data from EEPROM
*/ */
static void static void
dm9000_read_eeprom(board_info_t * db, int offset, unsigned char *to) dm9000_read_eeprom(board_info_t *db, int offset, unsigned char *to)
{ {
mutex_lock(&db->addr_lock);
iow(db, DM9000_EPAR, offset); iow(db, DM9000_EPAR, offset);
iow(db, DM9000_EPCR, EPCR_ERPRR); iow(db, DM9000_EPCR, EPCR_ERPRR);
mdelay(8); /* according to the datasheet 200us should be enough, mdelay(8); /* according to the datasheet 200us should be enough,
...@@ -1007,6 +1020,8 @@ dm9000_read_eeprom(board_info_t * db, int offset, unsigned char *to) ...@@ -1007,6 +1020,8 @@ dm9000_read_eeprom(board_info_t * db, int offset, unsigned char *to)
to[0] = ior(db, DM9000_EPDRL); to[0] = ior(db, DM9000_EPDRL);
to[1] = ior(db, DM9000_EPDRH); to[1] = ior(db, DM9000_EPDRH);
mutex_unlock(&db->addr_lock);
} }
#ifdef DM9000_PROGRAM_EEPROM #ifdef DM9000_PROGRAM_EEPROM
...@@ -1016,12 +1031,16 @@ dm9000_read_eeprom(board_info_t * db, int offset, unsigned char *to) ...@@ -1016,12 +1031,16 @@ dm9000_read_eeprom(board_info_t * db, int offset, unsigned char *to)
static void static void
write_srom_word(board_info_t * db, int offset, u16 val) write_srom_word(board_info_t * db, int offset, u16 val)
{ {
mutex_lock(&db->addr_lock);
iow(db, DM9000_EPAR, offset); iow(db, DM9000_EPAR, offset);
iow(db, DM9000_EPDRH, ((val >> 8) & 0xff)); iow(db, DM9000_EPDRH, ((val >> 8) & 0xff));
iow(db, DM9000_EPDRL, (val & 0xff)); iow(db, DM9000_EPDRL, (val & 0xff));
iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW); iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
mdelay(8); /* same shit */ mdelay(8); /* same shit */
iow(db, DM9000_EPCR, 0); iow(db, DM9000_EPCR, 0);
mutex_unlock(&db->addr_lock);
} }
/* /*
...@@ -1129,6 +1148,8 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg) ...@@ -1129,6 +1148,8 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
unsigned int reg_save; unsigned int reg_save;
int ret; int ret;
mutex_lock(&db->addr_lock);
spin_lock_irqsave(&db->lock,flags); spin_lock_irqsave(&db->lock,flags);
/* Save previous register address */ /* Save previous register address */
...@@ -1156,6 +1177,7 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg) ...@@ -1156,6 +1177,7 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
writeb(reg_save, db->io_addr); writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags); spin_unlock_irqrestore(&db->lock,flags);
mutex_unlock(&db->addr_lock);
return ret; return ret;
} }
...@@ -1169,6 +1191,8 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value) ...@@ -1169,6 +1191,8 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value)
unsigned long flags; unsigned long flags;
unsigned long reg_save; unsigned long reg_save;
mutex_lock(&db->addr_lock);
spin_lock_irqsave(&db->lock,flags); spin_lock_irqsave(&db->lock,flags);
/* Save previous register address */ /* Save previous register address */
...@@ -1184,7 +1208,7 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value) ...@@ -1184,7 +1208,7 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value)
iow(db, DM9000_EPCR, 0xa); /* Issue phyxcer write command */ iow(db, DM9000_EPCR, 0xa); /* Issue phyxcer write command */
writeb(reg_save, db->io_addr); writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags); spin_unlock_irqrestore(&db->lock, flags);
dm9000_msleep(db, 1); /* Wait write complete */ dm9000_msleep(db, 1); /* Wait write complete */
...@@ -1196,7 +1220,8 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value) ...@@ -1196,7 +1220,8 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value)
/* restore the previous address */ /* restore the previous address */
writeb(reg_save, db->io_addr); writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags); spin_unlock_irqrestore(&db->lock, flags);
mutex_unlock(&db->addr_lock);
} }
static int static int
......
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