patch-2.0.30 linux/arch/i386/kernel/bios32.c

Next file: linux/arch/i386/kernel/ksyms.c
Previous file: linux/arch/alpha/kernel/ksyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.29/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
@@ -46,6 +46,11 @@
  * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
  *	moved to drivers/pci/pci.c.
  *
+ * Dec 7, 1996  : Added support for direct configuration access of boards
+ *      with Intel compatible access schemes (tsbogend@alpha.franken.de)
+ *
+ * Feb 3, 1997  : Set internal functions to static, save/restore flags
+ *	avoid dead locks reading broken PCI BIOS, werner@suse.de 
  *
  */
 
@@ -56,6 +61,8 @@
 #include <linux/pci.h>
 
 #include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
 
 #define PCIBIOS_PCI_FUNCTION_ID 	0xb1XX
 #define PCIBIOS_PCI_BIOS_PRESENT 	0xb101
@@ -101,6 +108,7 @@
 	char chars[16];
 };
 
+#ifdef CONFIG_PCI
 /*
  * Physical address of the service directory.  I don't know if we're
  * allowed to have more than one of these or not, so just in case
@@ -114,7 +122,28 @@
 	unsigned short segment;
 } bios32_indirect = { 0, KERNEL_CS };
 
-#ifdef CONFIG_PCI
+
+/*
+ * function table for accessing PCI configuration space
+ */
+struct pci_access {
+    int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
+    int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
+    int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
+    int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
+    int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
+    int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
+    int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
+    int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
+};
+
+/*
+ * pointer to selected PCI access function table
+ */
+static struct pci_access *access_pci = NULL;
+
+
+
 /*
  * Returns the entry point for the given service, NULL on error
  */
@@ -125,7 +154,9 @@
 	unsigned long address;		/* %ebx */
 	unsigned long length;		/* %ecx */
 	unsigned long entry;		/* %edx */
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%edi)"
 		: "=a" (return_code),
 		  "=b" (address),
@@ -134,6 +165,7 @@
 		: "0" (service),
 		  "1" (0),
 		  "D" (&bios32_indirect));
+	restore_flags(flags);
 
 	switch (return_code) {
 		case 0:
@@ -161,11 +193,13 @@
 	unsigned char present_status;
 	unsigned char major_revision;
 	unsigned char minor_revision;
+	unsigned long flags;
 	int pack;
 
 	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
 		pci_indirect.address = pcibios_entry;
 
+		save_flags(flags);
 		__asm__("lcall (%%edi)\n\t"
 			"jc 1f\n\t"
 			"xor %%ah, %%ah\n"
@@ -176,6 +210,7 @@
 			: "1" (PCIBIOS_PCI_BIOS_PRESENT),
 			  "D" (&pci_indirect)
 			: "bx", "cx");
+		restore_flags(flags);
 
 		present_status = (pack >> 16) & 0xff;
 		major_revision = (pack >> 8) & 0xff;
@@ -200,17 +235,15 @@
 	return memory_start;
 }
 
-int pcibios_present(void)
-{
-	return pcibios_entry ? 1 : 0;
-}
 
-int pcibios_find_class (unsigned int class_code, unsigned short index,
+static int pci_bios_find_class (unsigned int class_code, unsigned short index,
 	unsigned char *bus, unsigned char *device_fn)
 {
 	unsigned long bx;
 	unsigned long ret;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__ ("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -221,18 +254,21 @@
 		  "c" (class_code),
 		  "S" ((int) index),
 		  "D" (&pci_indirect));
+	restore_flags(flags);
 	*bus = (bx >> 8) & 0xff;
 	*device_fn = bx & 0xff;
 	return (int) (ret & 0xff00) >> 8;
 }
 
 
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
 	unsigned short index, unsigned char *bus, unsigned char *device_fn)
 {
 	unsigned short bx;
 	unsigned short ret;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -244,17 +280,20 @@
 		  "d" (vendor),
 		  "S" ((int) index),
 		  "D" (&pci_indirect));
+	restore_flags(flags);
 	*bus = (bx >> 8) & 0xff;
 	*device_fn = bx & 0xff;
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_byte(unsigned char bus,
+static int pci_bios_read_config_byte(unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned char *value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -265,15 +304,18 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_word (unsigned char bus,
+static int pci_bios_read_config_word (unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned short *value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -284,15 +326,18 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_dword (unsigned char bus,
+static int pci_bios_read_config_dword (unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned int *value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -303,15 +348,18 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_byte (unsigned char bus,
+static int pci_bios_write_config_byte (unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned char value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -322,15 +370,18 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_word (unsigned char bus,
+static int pci_bios_write_config_word (unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned short value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -341,15 +392,18 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_dword (unsigned char bus,
+static int pci_bios_write_config_dword (unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned int value)
 {
 	unsigned long ret;
 	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long flags;
 
+	save_flags(flags);
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -360,9 +414,421 @@
 		  "b" (bx),
 		  "D" ((long) where),
 		  "S" (&pci_indirect));
+	restore_flags(flags);
 	return (int) (ret & 0xff00) >> 8;
 }
 
+/*
+ * function table for BIOS32 access
+ */
+static struct pci_access pci_bios_access = {
+      pci_bios_find_device,
+      pci_bios_find_class,
+      pci_bios_read_config_byte,
+      pci_bios_read_config_word,
+      pci_bios_read_config_dword,
+      pci_bios_write_config_byte,
+      pci_bios_write_config_word,
+      pci_bios_write_config_dword
+};
+
+
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.  
+ */
+static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
+			   unsigned short index, unsigned char *bus,
+			   unsigned char *devfn)
+{
+    unsigned int curr = 0;
+    struct pci_dev *dev;
+    unsigned long flags;
+
+    save_flags(flags);
+    for (dev = pci_devices; dev; dev = dev->next) {
+	if (dev->vendor == vendor && dev->device == device_id) {
+	    if (curr == index) {
+		*devfn = dev->devfn;
+		*bus = dev->bus->number;
+		restore_flags(flags);
+		return PCIBIOS_SUCCESSFUL;
+	    }
+	    ++curr;
+	}
+    }
+    restore_flags(flags);
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+static int pci_direct_find_class (unsigned int class_code, unsigned short index,
+			  unsigned char *bus, unsigned char *devfn)
+{
+    unsigned int curr = 0;
+    struct pci_dev *dev;
+    unsigned long flags;
+
+    save_flags(flags);
+    for (dev = pci_devices; dev; dev = dev->next) {
+	if (dev->class == class_code) {
+	    if (curr == index) {
+		*devfn = dev->devfn;
+		*bus = dev->bus->number;
+		restore_flags(flags);
+		return PCIBIOS_SUCCESSFUL;
+	    }
+	    ++curr;
+	}
+    }
+    restore_flags(flags);
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+#define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned char *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    switch (where & 3) {
+	case 0: *value = inb(0xCFC);
+		break;
+	case 1: *value = inb(0xCFD);
+		break;
+	case 2: *value = inb(0xCFE);
+		break;
+	case 3: *value = inb(0xCFF);
+		break;
+    }
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    if (where & 2)
+    	*value = inw(0xCFE);
+    else
+    	*value = inw(0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;    
+}
+
+static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned int *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    *value = inl(0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;    
+}
+
+static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned char value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    outb(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned short value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outw(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn, 
+				  unsigned char where, unsigned int value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+/*
+ * functiontable for type 1
+ */
+static struct pci_access pci_direct_conf1 = {
+      pci_direct_find_device,
+      pci_direct_find_class,
+      pci_conf1_read_config_byte,
+      pci_conf1_read_config_word,
+      pci_conf1_read_config_dword,
+      pci_conf1_write_config_byte,
+      pci_conf1_write_config_word,
+      pci_conf1_write_config_dword
+};
+
+/*
+ * Functions for accessing PCI configuration space with type 2 accesses
+ */
+#define IOADDR(devfn, where)   ((0xC000 | ((devfn & 0x78) << 5)) + where)
+#define FUNC(devfn)            (((devfn & 7) << 1) | 0xf0)
+
+static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn, 
+			       unsigned char where, unsigned char *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inb(IOADDR(device_fn,where));
+    outb (0, 0xCF8);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn, 
+				unsigned char where, unsigned short *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inw(IOADDR(device_fn,where));
+    outb (0, 0xCF8);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned int *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inl (IOADDR(device_fn,where));    
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned char value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outb (value, IOADDR(device_fn,where));
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn, 
+				 unsigned char where, unsigned short value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outw (value, IOADDR(device_fn,where));
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn, 
+				  unsigned char where, unsigned int value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outl (value, IOADDR(device_fn,where));    
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+#undef IOADDR
+#undef FUNC
+
+/*
+ * functiontable for type 2
+ */
+static struct pci_access pci_direct_conf2 = {
+      pci_direct_find_device,
+      pci_direct_find_class,
+      pci_conf2_read_config_byte,
+      pci_conf2_read_config_word,
+      pci_conf2_read_config_dword,
+      pci_conf2_write_config_byte,
+      pci_conf2_write_config_word,
+      pci_conf2_write_config_dword
+};
+
+
+static struct pci_access *check_direct_pci(void)
+{
+    unsigned int tmp;
+    unsigned long flags;
+
+    save_flags(flags);
+
+    /*
+     * check if configuration type 1 works
+     */
+    outb (0x01, 0xCFB);
+    tmp = inl (0xCF8);
+    outl (0x80000000, 0xCF8);
+    if (inl (0xCF8) == 0x80000000) {
+	outl (tmp, 0xCF8);
+	restore_flags(flags);
+	printk("pcibios_init: Using configuration type 1\n");
+	return &pci_direct_conf1;
+    }
+    outl (tmp, 0xCF8);
+
+    /*
+     * check if configuration type 2 works
+     */
+    outb (0x00, 0xCFB);
+    outb (0x00, 0xCF8);
+    outb (0x00, 0xCFA);
+    if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) {
+	restore_flags(flags);
+	printk("pcibios_init: Using configuration type 2\n");
+	return &pci_direct_conf2;
+    }
+    restore_flags(flags);
+    printk("pcibios_init: Not supported chipset for direct PCI access !\n");
+    return NULL;
+}
+
+
+/*
+ * access defined pcibios functions via
+ * the function table
+ */
+
+int pcibios_present(void)
+{
+	return access_pci ? 1 : 0;
+}
+
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+	unsigned char *bus, unsigned char *device_fn)
+{
+   if (access_pci && access_pci->find_class)
+      return access_pci->find_class(class_code, index, bus, device_fn);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+	unsigned short index, unsigned char *bus, unsigned char *device_fn)
+{
+    if (access_pci && access_pci->find_device)
+      return access_pci->find_device(vendor, device_id, index, bus, device_fn);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_byte (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned char *value)
+{
+    if (access_pci && access_pci->read_config_byte)
+      return access_pci->read_config_byte(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_word (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+    if (access_pci && access_pci->read_config_word)
+      return access_pci->read_config_word(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_dword (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned int *value)
+{
+    if (access_pci && access_pci->read_config_dword)
+      return access_pci->read_config_dword(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_byte (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned char value)
+{
+    if (access_pci && access_pci->write_config_byte)
+      return access_pci->write_config_byte(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_word (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned short value)
+{
+    if (access_pci && access_pci->write_config_word)
+      return access_pci->write_config_word(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;    
+}
+
+int pcibios_write_config_dword (unsigned char bus,
+	unsigned char device_fn, unsigned char where, unsigned int value)
+{
+    if (access_pci && access_pci->write_config_dword)
+      return access_pci->write_config_dword(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
 const char *pcibios_strerror (int error)
 {
 	static char buf[80];
@@ -398,14 +864,14 @@
 
 unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
 {
-return mem_start;
+    return mem_start;
 }
 
-
 #endif
 
 unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
 {
+#ifdef CONFIG_PCI
 	union bios32 *check;
 	unsigned char sum;
 	int i, length;
@@ -436,14 +902,16 @@
 		printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
 		if (!bios32_entry) {
 			if (check->fields.entry >= 0x100000) {
-				printk("pcibios_init: entry in high memory, unable to access\n");
+				printk("pcibios_init: entry in high memory, trying direct PCI access\n");
+				access_pci = check_direct_pci();
 			} else {
-				bios32_indirect.address = bios32_entry = check->fields.entry;
+				bios32_entry = check->fields.entry;
 				printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+				bios32_indirect.address = bios32_entry;
+				access_pci = &pci_bios_access;
 			}
 		}
 	}
-#ifdef CONFIG_PCI
 	if (bios32_entry) {
 		memory_start = check_pcibios (memory_start, memory_end);
 	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov