Re: pci_scan, tulip, 3c59x, multiple detections and others

Petr Vandrovec (vandrove@vc.cvut.cz)
Tue, 28 Sep 1999 23:38:22 +0200


Ok,
so as I promised, this is first proposal, how things could work.
It is not intended for inclusion in kernel, Alan...
It does everything I want except that pci_drv_release and
pci_drv_physbase API is a bit strange - you have to pass two
parameters to it, one and half of other you got from registration
callback, other half of second parameter you know when you were
registrationg your callback with system. For sure it is not
perfect, for example there is no way how to obtain parameter to
pwe_event...
I had to change return value from callback from void* to char*,
otherwise driver could not dictate registration name. You must return
name under which region will be registered, if you return NULL,
initialization failed and all resources passed to callback are
released.
From my _current_ point of view we should end up with

struct registered_device {
void* vbase_addr; /* virtual base of region */
struct pci_dev* pdev; /* or any device? */
struct pci_id_info* dev_info; /* matched scan entry */
int irq; /* unregistered, we are not
ready for IRQ events yet... */
/*
struct resource resource; /* eventually, if we'll use
request_resource; otherwise we can use normal
request_region/release_region */
*/
void* private;
/* do not kill me, just idea how to build tulip with
I/O and MMIO support together
u8 (*readb)(void* base, int offset);
...
*/
};

struct drv_id_info {
const char* name;
int flags;
int pci_class;
struct pci_id_info *pci_dev_tbl;
char *(*probe1)(struct registered_device *dev, void *dev_ptr, int fnd_cnt);
int (*pwr_event)(struct registered_device *dev, int event);
/* CardBus stuff */
};

extern int pci_drv_register(struct drv_id_info *drv_id, void *initial_device);
extern void pci_drv_unregister(struct drv_id_info *drv_id);
extern void pci_dev_release(struct registered_device *dev);

Comments? Ideas? Thanks.

Now current output of /proc/iomem... I'm satisfied:
00000000-0009ffff : System RAM
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000f0000-000fffff : System ROM
00100000-0fffffff : System RAM
00100000-001e6e97 : Kernel code
001e6e98-002077f3 : Kernel data
d8000000-dbffffff : Intel 440BX - 82443BX Host
dc000000-dc003fff : Matrox G400
dd000000-dd7fffff : Matrox G400
e0000000-e1ffffff : Matrox G400
e2000000-e203ffff : DEC DC21142
e3000000-e300007f : DEC DC21142
e3000000-e300007f : eth0
e3001000-e3001fff : Brooktree Bt848
e3002000-e3002fff : Brooktree Bt878 2nd Contr. (?)
e3003000-e3003fff : Brooktree Bt878

And diff. First hunk is tulip, rest are changes to scan code. This scanning
code is not compatible with current network drivers! I added virtual address
into tulip private structure instead of to net_device, as each procedure used
tulip private data anyway, so no speed loss should occur. I do not know what
networking gurus thinks about adding strange fields to net_device.
Best regards,
Petr Vandrovec
vandrove@vc.cvut.cz

diff -urdN linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- linux/drivers/net/tulip.c Mon Sep 27 14:30:23 1999
+++ linux/drivers/net/tulip.c Tue Sep 28 20:33:55 1999
@@ -151,10 +151,6 @@
#define outb writeb
#define outw writew
#define outl writel
-#undef request_region
-#undef release_region
-#define request_region request_mem_region
-#define release_region release_mem_region
#endif

/*
@@ -260,7 +256,7 @@

*/

-static void *tulip_probe1(struct pci_dev *pdev, void *init_dev,
+static char *tulip_probe1(struct pci_dev *pdev, void *init_dev,
long ioaddr, int irq, int chip_idx, int find_cnt);
#ifdef USE_IO_OPS
#define TULIP_IOTYPE PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0
@@ -289,7 +285,7 @@
{ "Digital DS21140 Tulip", { 0x00091011, 0xffffffff },
TULIP_IOTYPE, TULIP_SIZEL, DC21140 },
{ "Digital DS21143 Tulip", { 0x00191011, 0xffffffff },
- TULIP_IOTYPE, TULIP_SIZEB, DC21142 },
+ TULIP_IOTYPE, TULIP_SIZEL, DC21142 },
{ "Lite-On 82c168 PNIC", { 0x000211AD, 0xffffffff },
TULIP_IOTYPE, 256, LC82C168 },
{ "Macronix 98713 PMAC", { 0x051210d9, 0xffffffff },
@@ -501,6 +497,7 @@
int cur_index; /* Current media index. */
int saved_if_port;
struct pci_dev *pci_dev;
+ long vbase_addr;
};

static void parse_eeprom(struct net_device *dev);
@@ -530,7 +527,7 @@
/* A list of all installed Tulip devices. */
static struct net_device *root_tulip_dev = NULL;

-static void *tulip_probe1(struct pci_dev *pdev, void *init_dev,
+static char *tulip_probe1(struct pci_dev *pdev, void *init_dev,
long ioaddr, int irq, int chip_idx, int board_idx)
{
struct net_device *dev;
@@ -578,7 +575,8 @@
pci_write_config_dword(pdev, 0x40, 0x00000000);

printk(KERN_INFO "%s: %s rev %d at %#3lx,",
- dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+ dev->name, tulip_tbl[chip_idx].chip_name, chip_rev,
+ pci_drv_physbase(pci_tbl+chip_idx, pdev));

/* Stop the chip's Tx and Rx processes. */
outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
@@ -674,10 +672,9 @@
printk(", IRQ %d.\n", irq);
last_irq = irq;

- /* We do a request_region() only to register /proc/ioports info. */
- request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
-
- dev->base_addr = ioaddr;
+ /* Caller did request_region()... */
+
+ dev->base_addr = pci_drv_physbase(pci_tbl+chip_idx, pdev);
dev->irq = irq;

tp->pci_dev = pdev;
@@ -685,6 +682,7 @@
tp->revision = chip_rev;
tp->flags = tulip_tbl[chip_idx].flags;
tp->csr0 = csr0;
+ tp->vbase_addr = ioaddr;

/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
And the ASIX must have a burst limit or horrible things happen. */
@@ -858,7 +856,7 @@
if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
pci_write_config_dword(pdev, 0x40, 0x40000000);

- return dev;
+ return dev->name;
}

/* Serial EEPROM section. */
@@ -1154,7 +1152,7 @@
int i;
int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
int retval = 0;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
long mdio_addr = ioaddr + CSR9;

if (tp->chip_id == LC82C168) {
@@ -1212,7 +1210,7 @@
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int i;
int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
long mdio_addr = ioaddr + CSR9;

if (tp->chip_id == LC82C168) {
@@ -1267,7 +1265,7 @@
tulip_open(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int next_tick = 3*HZ;
int i;

@@ -1469,8 +1467,8 @@
/* Set up the transceiver control registers for the selected media type. */
static void select_media(struct net_device *dev, int startup)
{
- long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ long ioaddr = tp->vbase_addr;
struct mediatable *mtable = tp->mtable;
u32 new_csr6;
int i;
@@ -1681,8 +1679,8 @@
*/
static int check_duplex(struct net_device *dev)
{
- long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ long ioaddr = tp->vbase_addr;
int mii_reg1, mii_reg5, negotiated, duplex;

if (tp->full_duplex_lock)
@@ -1729,7 +1727,7 @@
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
u32 csr12 = inl(ioaddr + CSR12);
int next_tick = 2*HZ;

@@ -1887,7 +1885,7 @@
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr12 = inl(ioaddr + CSR12);
int next_tick = 60*HZ;
int new_csr6 = 0;
@@ -1960,7 +1958,7 @@
static void nway_start(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr14 = ((tp->to_advertise & 0x0780) << 9) |
((tp->to_advertise&0x0020)<<1) | 0xffbf;

@@ -1992,7 +1990,7 @@
static void nway_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr12 = inl(ioaddr + CSR12);

if (tp->chip_id == PNIC2) {
@@ -2117,7 +2115,7 @@
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int next_tick = 60*HZ;

if (tulip_debug > 3) {
@@ -2131,7 +2129,7 @@
static void pnic_do_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
u32 phy_reg = inl(ioaddr + 0xB8);
u32 new_csr6 = tp->csr6 & ~0x40C40200;

@@ -2163,7 +2161,7 @@
static void pnic_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int phy_reg = inl(ioaddr + 0xB8);

if (tulip_debug > 1)
@@ -2187,7 +2185,7 @@
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int next_tick = 60*HZ;

if (media_cap[dev->if_port] & MediaIsMII) {
@@ -2252,7 +2250,7 @@
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int next_tick = 60*HZ;

if (tulip_debug > 1)
@@ -2266,7 +2264,7 @@
static void tulip_tx_timeout(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;

if (media_cap[dev->if_port] & MediaIsMII) {
/* Do nothing -- the media monitor should handle this. */
@@ -2455,7 +2453,7 @@

dev->trans_start = jiffies;
/* Trigger an immediate transmit demand. */
- outl(0, dev->base_addr + CSR1);
+ outl(0, tp->vbase_addr + CSR1);

return 0;
}
@@ -2466,7 +2464,7 @@
{
struct net_device *dev = (struct net_device *)dev_instance;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr5, work_budget = max_interrupt_work;

#if defined(__i386__) && defined(SMP_CHECK)
@@ -2495,7 +2493,7 @@

if (tulip_debug > 3)
printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
- dev->name, csr5, inl(dev->base_addr + CSR5));
+ dev->name, csr5, inl(tp->vbase_addr + CSR5));

if (csr5 & (RxIntr | RxNoBuf))
work_budget -= tulip_rx(dev);
@@ -2766,8 +2764,8 @@

static int tulip_close(struct net_device *dev)
{
- long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ long ioaddr = tp->vbase_addr;

dev->start = 0;
dev->tbusy = 1;
@@ -2806,7 +2804,7 @@
static struct net_device_stats *tulip_get_stats(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr8 = inl(ioaddr + CSR8);

if (dev->start && csr8 != 0xffffffff)
@@ -2819,7 +2817,7 @@
static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
u16 *data = (u16 *)&rq->ifr_data;
int phy = tp->phys[0] & 0x1f;
long flags;
@@ -2933,7 +2931,7 @@
static void set_rx_mode(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ long ioaddr = tp->vbase_addr;
int csr6 = inl(ioaddr + CSR6) & ~0x00D5;

tp->csr6 &= ~0x00D5;
@@ -3056,7 +3054,7 @@

static dev_node_t *tulip_attach(dev_locator_t *loc)
{
- struct net_device *dev;
+ char *name;
long ioaddr;
struct pci_dev *pdev;
u8 bus, devfn, irq;
@@ -3089,14 +3087,23 @@
if(node==NULL)
return NULL;

- dev = tulip_probe1(pdev, NULL, ioaddr, irq, DC21142, 0);
- if (dev) {
- strcpy(node->dev_name, dev->name);
+ name = tulip_probe1(pdev, NULL, ioaddr, irq, DC21142, 0);
+ if (name) {
+#ifdef USE_IO_OPS
+ request_region(ioaddr, pci_tbl[DC21142].io_size, name);
+#else
+ request_mem_region(pciaddr & PCI_BASE_ADDRESS_MEM_MASK,
+ pci_tbl[DC21142].io_size, name);
+#endif
+ strcpy(node->dev_name, name);
node->major = node->minor = 0;
node->next = NULL;
MOD_INC_USE_COUNT;
return node;
}
+#ifdef USE_IO_OPS
+ iounmap((void*)ioaddr);
+#endif
kfree(node);
return NULL;
}
@@ -3110,8 +3117,8 @@
if (strcmp((*devp)->name, node->dev_name) == 0) break;
}
if (*devp) {
- long ioaddr = (*devp)->base_addr;
struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
+ long ioaddr = tp->vbase_addr;
int csr6 = inl(ioaddr + CSR6);
/* Disable interrupts, stop the chip, gather stats. */
if (csr6 != 0xffffffff) {
@@ -3135,7 +3142,7 @@
}
if (*devp) {
struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
- long ioaddr = (*devp)->base_addr;
+ long ioaddr = tp->vbase_addr;
pci_write_config_dword(tp->pci_dev, 0x40, 0x0000);
outl(tp->csr0, ioaddr + CSR0);
tulip_init_ring(*devp);
@@ -3160,10 +3167,8 @@
if (*devp) {
struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
unregister_netdev(*devp);
- release_region((*devp)->base_addr, pci_tbl[DC21142].io_size);
-#ifndef USE_IO_OPS
- iounmap((char *)(*devp)->base_addr);
-#endif
+ pci_drv_release(pci_tbl+tp->chip_id, tp->pci_dev,
+ tp->vbase_addr);
kfree(*devp);
if (tp->priv_addr)
kfree(tp->priv_addr);
@@ -3208,10 +3213,7 @@
while (root_tulip_dev) {
struct tulip_private *tp = (struct tulip_private*)root_tulip_dev->priv;
unregister_netdev(root_tulip_dev);
- release_region(root_tulip_dev->base_addr, pci_tbl[tp->chip_id].io_size);
-#ifndef USE_IO_OPS
- iounmap((char *)root_tulip_dev->base_addr);
-#endif
+ pci_drv_release(pci_tbl+tp->chip_id, tp->pci_dev, tp->vbase_addr);
next_dev = tp->next_module;
if (tp->priv_addr)
kfree(tp->priv_addr);
diff -urdN linux/drivers/pci/pcisyms.c linux/drivers/pci/pcisyms.c
--- linux/drivers/pci/pcisyms.c Mon Sep 27 14:30:27 1999
+++ linux/drivers/pci/pcisyms.c Tue Sep 28 18:30:02 1999
@@ -49,6 +49,8 @@

EXPORT_SYMBOL(pci_drv_register);
EXPORT_SYMBOL(pci_drv_unregister);
+EXPORT_SYMBOL(pci_drv_release);
+EXPORT_SYMBOL(pci_drv_physbase);
EXPORT_SYMBOL(acpi_wake);
EXPORT_SYMBOL(acpi_set_pwr_state);
EXPORT_SYMBOL(register_cb_hook);
diff -urdN linux/drivers/pci/scan.c linux/drivers/pci/scan.c
--- linux/drivers/pci/scan.c Mon Sep 27 14:30:27 1999
+++ linux/drivers/pci/scan.c Tue Sep 28 18:49:39 1999
@@ -61,6 +61,10 @@
than half of the drivers for common hardware would benefit from the feature.
*/

+unsigned long pci_drv_physbase(struct pci_id_info* info, struct pci_dev *pdev) {
+ return pdev->resource[(info->pci_flags >> 4) & 7].start;
+}
+
/*
Ideally we would detect and number all cards of a type (e.g. network) in
PCI slot order.
@@ -81,8 +85,6 @@
int chip_idx, cards_found = 0;
struct pci_dev *pdev = NULL;
struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl;
- void *newdev;
- struct resource *res;

if ( ! pci_present())
return -ENODEV;
@@ -94,6 +96,8 @@
int pci_flags, irq;
unsigned long pciaddr;
unsigned long ioaddr;
+ struct resource* res;
+ char* devname;
int free = 0;

pci_id = (pdev->device << 16) | pdev->vendor;
@@ -150,24 +154,29 @@
pci_write_config_word(pdev, PCI_COMMAND, new_command);
}

- newdev = drv_id->probe1(pdev, initial_device,
+ devname = drv_id->probe1(pdev, initial_device,
ioaddr, irq, chip_idx, cards_found);

/*
* Not attached.
*/

- if (newdev == 0)
+ if (!devname)
{
if(free)
iounmap((void *)ioaddr);
continue;
}

- if (newdev && (pci_flags & PCI_COMMAND_MASTER))
+ if (pci_flags & PCI_USES_IO)
+ request_region(pciaddr, pci_tbl[chip_idx].io_size, devname);
+ else
+ request_mem_region(pciaddr, pci_tbl[chip_idx].io_size, devname);
+
+ if (pci_flags & PCI_COMMAND_MASTER)
pci_set_master(pdev);
- if (newdev && (pci_flags & PCI_COMMAND_MASTER) &&
- ! (pci_flags & PCI_NO_MIN_LATENCY)) {
+ if ((pci_flags & PCI_COMMAND_MASTER) &&
+ ! (pci_flags & PCI_NO_MIN_LATENCY)) {
u8 pci_latency;
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
if (pci_latency < min_pci_latency) {
@@ -199,6 +208,17 @@
return;
}

+void pci_drv_release(struct pci_id_info *info, struct pci_dev *pdev, unsigned long ioaddr) {
+ unsigned long phys_base;
+
+ phys_base = pci_drv_physbase(info, pdev);
+ if (info->pci_flags & PCI_USES_IO)
+ release_region(phys_base, info->io_size);
+ else {
+ iounmap((void*)ioaddr);
+ release_mem_region(phys_base, info->io_size);
+ }
+}

/* Change a device from D3 (sleep) to D0 (active).
Return the old power state.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/