]> git.hungrycats.org Git - linux/commitdiff
ppc32: Rework nvram management
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 6 Feb 2004 03:20:56 +0000 (14:20 +1100)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 6 Feb 2004 03:20:56 +0000 (14:20 +1100)
move drivers/macintosh/nvram.c to drivers/char/generic_nvram.c,
update platform hooks,
fix powermac nvram driver for newer machines

arch/ppc/Kconfig
arch/ppc/kernel/ppc_ksyms.c
arch/ppc/kernel/setup.c
arch/ppc/platforms/pmac_nvram.c
drivers/char/Makefile
drivers/char/generic_nvram.c [new file with mode: 0644]
drivers/macintosh/Makefile
include/asm-ppc/machdep.h
include/asm-ppc/nvram.h

index dda6dc91e1b7d6c93f2cc682d0959204e7ab0c97..df86732b720c6130a4b3d7c2c0b2d796e77fc3d0 100644 (file)
@@ -30,6 +30,10 @@ config PPC32
        bool
        default y
 
+# All PPCs use generic nvram driver through ppc_md
+config GENERIC_NVRAM
+       bool
+       default y
 
 source "init/Kconfig"
 
index 8d0360f8d911fe9d34dcf8930955d8366705fd91..6cf2478be1a4bf958ec5a92025b1a652d749f7a7 100644 (file)
@@ -283,14 +283,6 @@ EXPORT_SYMBOL(note_scsi_host);
 #ifdef CONFIG_VT
 EXPORT_SYMBOL(kd_mksound);
 #endif
-#ifdef CONFIG_NVRAM
-EXPORT_SYMBOL(nvram_read_byte);
-EXPORT_SYMBOL(nvram_write_byte);
-#ifdef CONFIG_PPC_PMAC
-EXPORT_SYMBOL(pmac_xpram_read);
-EXPORT_SYMBOL(pmac_xpram_write);
-#endif
-#endif /* CONFIG_NVRAM */
 EXPORT_SYMBOL(to_tm);
 
 EXPORT_SYMBOL(pm_power_off);
index 94c00edb40bf6b1289c4f3a518f7b1ed65a74d85..7d1ecb35a6196fce2a0cb0d1affb53f512c61ad2 100644 (file)
@@ -35,6 +35,7 @@
 #include <asm/system.h>
 #include <asm/pmac_feature.h>
 #include <asm/sections.h>
+#include <asm/nvram.h>
 #include <asm/xmon.h>
 
 #if defined CONFIG_KGDB
@@ -111,6 +112,9 @@ struct screen_info screen_info = {
 
 void machine_restart(char *cmd)
 {
+#ifdef CONFIG_NVRAM
+       nvram_sync();
+#endif
        ppc_md.restart(cmd);
 }
 
@@ -118,6 +122,9 @@ EXPORT_SYMBOL(machine_restart);
 
 void machine_power_off(void)
 {
+#ifdef CONFIG_NVRAM
+       nvram_sync();
+#endif
        ppc_md.power_off();
 }
 
@@ -125,6 +132,9 @@ EXPORT_SYMBOL(machine_power_off);
 
 void machine_halt(void)
 {
+#ifdef CONFIG_NVRAM
+       nvram_sync();
+#endif
        ppc_md.halt();
 }
 
@@ -558,24 +568,30 @@ int __init ppc_setup_l2cr(char *str)
 __setup("l2cr=", ppc_setup_l2cr);
 
 #ifdef CONFIG_NVRAM
-/* Generic nvram hooks we now look into ppc_md.nvram_read_val
- * on pmac too ;)
- * //XX Those 2 could be moved to headers
- */
-unsigned char
-nvram_read_byte(int addr)
+
+/* Generic nvram hooks used by drivers/char/gen_nvram.c */
+unsigned char nvram_read_byte(int addr)
 {
        if (ppc_md.nvram_read_val)
                return ppc_md.nvram_read_val(addr);
        return 0xff;
 }
+EXPORT_SYMBOL(nvram_read_byte);
 
-void
-nvram_write_byte(unsigned char val, int addr)
+void nvram_write_byte(unsigned char val, int addr)
 {
        if (ppc_md.nvram_write_val)
-               ppc_md.nvram_write_val(val, addr);
+               ppc_md.nvram_write_val(addr, val);
+}
+EXPORT_SYMBOL(nvram_write_byte);
+
+void nvram_sync(void)
+{
+       if (ppc_md.nvram_sync)
+               ppc_md.nvram_sync();
 }
+EXPORT_SYMBOL(nvram_sync);
+
 #endif /* CONFIG_NVRAM */
 
 static struct cpu cpu_devices[NR_CPUS];
index d11d2445b54e405afb3ab7dae846702cd6eecf9b..f381f3f745f957ef555bb0ed1f9853a59bf65a76 100644 (file)
@@ -8,8 +8,7 @@
  *  as published by the Free Software Foundation; either version
  *  2 of the License, or (at your option) any later version.
  *
- *  Todo: - cleanup some coding horrors in the flash code
- *        - add support for the OF persistent properties
+ *  Todo: - add support for the OF persistent properties
  */
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/bootmem.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
 #include <asm/sections.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/nvram.h>
-#include <linux/adb.h>
-#include <linux/pmu.h>
 
-#undef DEBUG
+#define DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
 
 #define NVRAM_SIZE             0x2000  /* 8kB of non-volatile RAM */
 
 #define CORE99_SIGNATURE       0x5a
 #define CORE99_ADLER_START     0x14
 
-/* Core99 nvram is a flash */
-#define CORE99_FLASH_STATUS_DONE       0x80
-#define CORE99_FLASH_STATUS_ERR                0x38
-#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0
-#define CORE99_FLASH_CMD_ERASE_SETUP   0x20
-#define CORE99_FLASH_CMD_RESET         0xff
-#define CORE99_FLASH_CMD_WRITE_SETUP   0x40
+/* On Core99, nvram is either a sharp, a micron or an AMD flash */
+#define SM_FLASH_STATUS_DONE   0x80
+#define SM_FLASH_STATUS_ERR            0x38
+#define SM_FLASH_CMD_ERASE_CONFIRM     0xd0
+#define SM_FLASH_CMD_ERASE_SETUP       0x20
+#define SM_FLASH_CMD_RESET             0xff
+#define SM_FLASH_CMD_WRITE_SETUP       0x40
+#define SM_FLASH_CMD_CLEAR_STATUS      0x50
+#define SM_FLASH_CMD_READ_STATUS       0x70
 
 /* CHRP NVRAM header */
 struct chrp_header {
@@ -70,21 +80,110 @@ static volatile unsigned char *nvram_data;
 static int nvram_mult, is_core_99;
 static int core99_bank = 0;
 static int nvram_partitions[3];
-
-/* FIXME: kmalloc fails to allocate the image now that I had to move it
- *        before time_init(). For now, I allocate a static buffer here
- *        but it's a waste of space on all but core99 machines
- */
-#if 0
-static char* nvram_image;
-#else
-static char nvram_image[NVRAM_SIZE] __pmacdata;
-#endif
+static spinlock_t nv_lock = SPIN_LOCK_UNLOCKED;
 
 extern int pmac_newworld;
+extern int system_running;
+
+static int (*core99_write_bank)(int bank, u8* datas);
+static int (*core99_erase_bank)(int bank);
+
+static char *nvram_image __pmacdata;
+
+
+static unsigned char __pmac core99_nvram_read_byte(int addr)
+{
+       if (nvram_image == NULL)
+               return 0xff;
+       return nvram_image[addr];
+}
+
+static void __pmac core99_nvram_write_byte(int addr, unsigned char val)
+{
+       if (nvram_image == NULL)
+               return;
+       nvram_image[addr] = val;
+}
+
+
+static unsigned char __openfirmware direct_nvram_read_byte(int addr)
+{
+       return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]);
+}
+
+static void __openfirmware direct_nvram_write_byte(int addr, unsigned char val)
+{
+       out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val);
+}
+
+
+static unsigned char __pmac indirect_nvram_read_byte(int addr)
+{
+       unsigned char val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&nv_lock, flags);
+       out_8(nvram_addr, addr >> 5);
+       val = in_8(&nvram_data[(addr & 0x1f) << 4]);
+       spin_unlock_irqrestore(&nv_lock, flags);
+
+       return val;
+}
+
+static void __pmac indirect_nvram_write_byte(int addr, unsigned char val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&nv_lock, flags);
+       out_8(nvram_addr, addr >> 5);
+       out_8(&nvram_data[(addr & 0x1f) << 4], val);
+       spin_unlock_irqrestore(&nv_lock, flags);
+}
+
+
+#ifdef CONFIG_ADB_PMU
+
+static void __pmac pmu_nvram_complete(struct adb_request *req)
+{
+       if (req->arg)
+               complete((struct completion *)req->arg);
+}
 
-static u8 __pmac
-chrp_checksum(struct chrp_header* hdr)
+static unsigned char __pmac pmu_nvram_read_byte(int addr)
+{
+       struct adb_request req;
+       DECLARE_COMPLETION(req_complete); 
+       
+       req.arg = system_running ? &req_complete : NULL;
+       if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM,
+                       (addr >> 8) & 0xff, addr & 0xff))
+               return 0xff;
+       if (system_running)
+               wait_for_completion(&req_complete);
+       while (!req.complete)
+               pmu_poll();
+       return req.reply[0];
+}
+
+static void __pmac pmu_nvram_write_byte(int addr, unsigned char val)
+{
+       struct adb_request req;
+       DECLARE_COMPLETION(req_complete); 
+       
+       req.arg = system_running ? &req_complete : NULL;
+       if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM,
+                       (addr >> 8) & 0xff, addr & 0xff, val))
+               return;
+       if (system_running)
+               wait_for_completion(&req_complete);
+       while (!req.complete)
+               pmu_poll();
+}
+
+#endif /* CONFIG_ADB_PMU */
+
+
+static u8 __pmac chrp_checksum(struct chrp_header* hdr)
 {
        u8 *ptr;
        u16 sum = hdr->signature;
@@ -95,8 +194,7 @@ chrp_checksum(struct chrp_header* hdr)
        return sum;
 }
 
-static u32 __pmac
-core99_calc_adler(u8 *buffer)
+static u32 __pmac core99_calc_adler(u8 *buffer)
 {
        int cnt;
        u32 low, high;
@@ -118,86 +216,186 @@ core99_calc_adler(u8 *buffer)
        return (high << 16) | low;
 }
 
-static u32 __pmac
-core99_check(u8* datas)
+static u32 __pmac core99_check(u8* datas)
 {
        struct core99_header* hdr99 = (struct core99_header*)datas;
 
        if (hdr99->hdr.signature != CORE99_SIGNATURE) {
-#ifdef DEBUG
-               printk("Invalid signature\n");
-#endif
+               DBG("Invalid signature\n");
                return 0;
        }
        if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) {
-#ifdef DEBUG
-               printk("Invalid checksum\n");
-#endif
+               DBG("Invalid checksum\n");
                return 0;
        }
        if (hdr99->adler != core99_calc_adler(datas)) {
-#ifdef DEBUG
-               printk("Invalid adler\n");
-#endif
+               DBG("Invalid adler\n");
                return 0;
        }
        return hdr99->generation;
 }
 
-static int __pmac
-core99_erase_bank(int bank)
+static int __pmac sm_erase_bank(int bank)
 {
        int stat, i;
+       unsigned long timeout;
 
        u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 
-       out_8(base, CORE99_FLASH_CMD_ERASE_SETUP);
-       out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM);
-       do { stat = in_8(base); }
-       while(!(stat & CORE99_FLASH_STATUS_DONE));
-       out_8(base, CORE99_FLASH_CMD_RESET);
-       if (stat & CORE99_FLASH_STATUS_ERR) {
-               printk("nvram: flash error 0x%02x on erase !\n", stat);
-               return -ENXIO;
-       }
+               DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank);
+
+       out_8(base, SM_FLASH_CMD_ERASE_SETUP);
+       out_8(base, SM_FLASH_CMD_ERASE_CONFIRM);
+       timeout = 0;
+       do {
+               if (++timeout > 1000000) {
+                       printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n");
+                       break;
+               }
+               out_8(base, SM_FLASH_CMD_READ_STATUS);
+               stat = in_8(base);
+       } while (!(stat & SM_FLASH_STATUS_DONE));
+
+       out_8(base, SM_FLASH_CMD_CLEAR_STATUS);
+       out_8(base, SM_FLASH_CMD_RESET);
+
        for (i=0; i<NVRAM_SIZE; i++)
                if (base[i] != 0xff) {
-                       printk("nvram: flash erase failed !\n");
+                       printk(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n");
                        return -ENXIO;
                }
        return 0;
 }
 
-static int __pmac
-core99_write_bank(int bank, u8* datas)
+static int __pmac sm_write_bank(int bank, u8* datas)
 {
        int i, stat = 0;
+       unsigned long timeout;
 
        u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 
+               DBG("nvram: Sharp/Micron Writing bank %d...\n", bank);
+
        for (i=0; i<NVRAM_SIZE; i++) {
-               out_8(base+i, CORE99_FLASH_CMD_WRITE_SETUP);
+               out_8(base+i, SM_FLASH_CMD_WRITE_SETUP);
+               udelay(1);
                out_8(base+i, datas[i]);
-               do { stat = in_8(base); }
-               while(!(stat & CORE99_FLASH_STATUS_DONE));
-               if (stat & CORE99_FLASH_STATUS_ERR)
+               timeout = 0;
+               do {
+                       if (++timeout > 1000000) {
+                               printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n");
+                               break;
+                       }
+                       out_8(base, SM_FLASH_CMD_READ_STATUS);
+                       stat = in_8(base);
+               } while (!(stat & SM_FLASH_STATUS_DONE));
+               if (!(stat & SM_FLASH_STATUS_DONE))
                        break;
        }
-       out_8(base, CORE99_FLASH_CMD_RESET);
-       if (stat & CORE99_FLASH_STATUS_ERR) {
-               printk("nvram: flash error 0x%02x on write !\n", stat);
-               return -ENXIO;
+       out_8(base, SM_FLASH_CMD_CLEAR_STATUS);
+       out_8(base, SM_FLASH_CMD_RESET);
+       for (i=0; i<NVRAM_SIZE; i++)
+               if (base[i] != datas[i]) {
+                       printk(KERN_ERR "nvram: Sharp/Micron flash write failed !\n");
+                       return -ENXIO;
+               }
+       return 0;
+}
+
+static int __pmac amd_erase_bank(int bank)
+{
+       int i, stat = 0;
+       unsigned long timeout;
+
+       u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
+
+               DBG("nvram: AMD Erasing bank %d...\n", bank);
+
+       /* Unlock 1 */
+       out_8(base+0x555, 0xaa);
+       udelay(1);
+       /* Unlock 2 */
+       out_8(base+0x2aa, 0x55);
+       udelay(1);
+
+       /* Sector-Erase */
+       out_8(base+0x555, 0x80);
+       udelay(1);
+       out_8(base+0x555, 0xaa);
+       udelay(1);
+       out_8(base+0x2aa, 0x55);
+       udelay(1);
+       out_8(base, 0x30);
+       udelay(1);
+
+       timeout = 0;
+       do {
+               if (++timeout > 1000000) {
+                       printk(KERN_ERR "nvram: AMD flash erase timeout !\n");
+                       break;
+               }
+               stat = in_8(base) ^ in_8(base);
+       } while (stat != 0);
+       
+       /* Reset */
+       out_8(base, 0xf0);
+       udelay(1);
+       
+       for (i=0; i<NVRAM_SIZE; i++)
+               if (base[i] != 0xff) {
+                       printk(KERN_ERR "nvram: AMD flash erase failed !\n");
+                       return -ENXIO;
+               }
+       return 0;
+}
+
+static int __pmac amd_write_bank(int bank, u8* datas)
+{
+       int i, stat = 0;
+       unsigned long timeout;
+
+       u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
+
+               DBG("nvram: AMD Writing bank %d...\n", bank);
+
+       for (i=0; i<NVRAM_SIZE; i++) {
+               /* Unlock 1 */
+               out_8(base+0x555, 0xaa);
+               udelay(1);
+               /* Unlock 2 */
+               out_8(base+0x2aa, 0x55);
+               udelay(1);
+
+               /* Write single word */
+               out_8(base+0x555, 0xa0);
+               udelay(1);
+               out_8(base+i, datas[i]);
+               
+               timeout = 0;
+               do {
+                       if (++timeout > 1000000) {
+                               printk(KERN_ERR "nvram: AMD flash write timeout !\n");
+                               break;
+                       }
+                       stat = in_8(base) ^ in_8(base);
+               } while (stat != 0);
+               if (stat != 0)
+                       break;
        }
+
+       /* Reset */
+       out_8(base, 0xf0);
+       udelay(1);
+
        for (i=0; i<NVRAM_SIZE; i++)
                if (base[i] != datas[i]) {
-                       printk("nvram: flash write failed !\n");
+                       printk(KERN_ERR "nvram: AMD flash write failed !\n");
                        return -ENXIO;
                }
        return 0;
 }
 
-static void __init
-lookup_partitions(void)
+static void __init lookup_partitions(void)
 {
        u8 buffer[17];
        int i, offset;
@@ -227,15 +425,49 @@ lookup_partitions(void)
                nvram_partitions[pmac_nvram_XPRAM] = 0x1300;
                nvram_partitions[pmac_nvram_NR] = 0x1400;
        }
+       DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]);
+       DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]);
+       DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]);
+}
+
+static void __pmac core99_nvram_sync(void)
+{
+       struct core99_header* hdr99;
+       unsigned long flags;
+
+       if (!is_core_99 || !nvram_data || !nvram_image)
+               return;
+
+       spin_lock_irqsave(&nv_lock, flags);
+       if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE,
+               NVRAM_SIZE))
+               goto bail;
+
+       DBG("Updating nvram...\n");
+
+       hdr99 = (struct core99_header*)nvram_image;
+       hdr99->generation++;
+       hdr99->hdr.signature = CORE99_SIGNATURE;
+       hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr);
+       hdr99->adler = core99_calc_adler(nvram_image);
+       core99_bank = core99_bank ? 0 : 1;
+       if (core99_erase_bank)
+               if (core99_erase_bank(core99_bank)) {
+                       printk("nvram: Error erasing bank %d\n", core99_bank);
+                       goto bail;
+               }
+       if (core99_write_bank)
+               if (core99_write_bank(core99_bank, nvram_image))
+                       printk("nvram: Error writing bank %d\n", core99_bank);
+ bail:
+       spin_unlock_irqrestore(&nv_lock, flags);
+
 #ifdef DEBUG
-       printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]);
-       printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]);
-       printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]);
+               mdelay(2000);
 #endif
 }
 
-void __init
-pmac_nvram_init(void)
+void __init pmac_nvram_init(void)
 {
        struct device_node *dp;
 
@@ -256,38 +488,65 @@ pmac_nvram_init(void)
                        printk(KERN_ERR "nvram: no address\n");
                        return;
                }
-#if 0
-               nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL);
-               if (!nvram_image) {
-                       printk(KERN_ERR "nvram: can't allocate image\n");
+               nvram_image = alloc_bootmem(NVRAM_SIZE);
+               if (nvram_image == NULL) {
+                       printk(KERN_ERR "nvram: can't allocate ram image\n");
                        return;
                }
-#endif
                nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2);
-#ifdef DEBUG
-               printk("nvram: Checking bank 0...\n");
-#endif
+               nvram_naddrs = 1; /* Make sure we get the correct case */
+
+               DBG("nvram: Checking bank 0...\n");
+
                gen_bank0 = core99_check((u8 *)nvram_data);
                gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE);
                core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0;
-#ifdef DEBUG
-               printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1);
-               printk("nvram: Active bank is: %d\n", core99_bank);
-#endif
+
+               DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1);
+               DBG("nvram: Active bank is: %d\n", core99_bank);
+
                for (i=0; i<NVRAM_SIZE; i++)
                        nvram_image[i] = nvram_data[i + core99_bank*NVRAM_SIZE];
+
+               ppc_md.nvram_read_val   = core99_nvram_read_byte;
+               ppc_md.nvram_write_val  = core99_nvram_write_byte;
+               ppc_md.nvram_sync       = core99_nvram_sync;
+               /* 
+                * Maybe we could be smarter here though making an exclusive list
+                * of known flash chips is a bit nasty as older OF didn't provide us
+                * with a useful "compatible" entry. A solution would be to really
+                * identify the chip using flash id commands and base ourselves on
+                * a list of known chips IDs
+                */
+               if (device_is_compatible(dp, "amd-0137")) {
+                       core99_erase_bank = amd_erase_bank;
+                       core99_write_bank = amd_write_bank;
+               } else {
+                       core99_erase_bank = sm_erase_bank;
+                       core99_write_bank = sm_write_bank;
+               }
        } else if (_machine == _MACH_chrp && nvram_naddrs == 1) {
                nvram_data = ioremap(dp->addrs[0].address + isa_mem_base,
                                     dp->addrs[0].size);
                nvram_mult = 1;
+               ppc_md.nvram_read_val   = direct_nvram_read_byte;
+               ppc_md.nvram_write_val  = direct_nvram_write_byte;
        } else if (nvram_naddrs == 1) {
                nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
                nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE;
+               ppc_md.nvram_read_val   = direct_nvram_read_byte;
+               ppc_md.nvram_write_val  = direct_nvram_write_byte;
        } else if (nvram_naddrs == 2) {
                nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
                nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
+               ppc_md.nvram_read_val   = indirect_nvram_read_byte;
+               ppc_md.nvram_write_val  = indirect_nvram_write_byte;
        } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) {
+#ifdef CONFIG_ADB_PMU
                nvram_naddrs = -1;
+               ppc_md.nvram_read_val   = pmu_nvram_read_byte;
+               ppc_md.nvram_write_val  = pmu_nvram_write_byte;
+#endif /* CONFIG_ADB_PMU */
        } else {
                printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n",
                       nvram_naddrs);
@@ -295,117 +554,31 @@ pmac_nvram_init(void)
        lookup_partitions();
 }
 
-void __pmac
-pmac_nvram_update(void)
-{
-       struct core99_header* hdr99;
-
-       if (!is_core_99 || !nvram_data || !nvram_image)
-               return;
-       if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE,
-               NVRAM_SIZE))
-               return;
-#ifdef DEBUG
-       printk("Updating nvram...\n");
-#endif
-       hdr99 = (struct core99_header*)nvram_image;
-       hdr99->generation++;
-       hdr99->hdr.signature = CORE99_SIGNATURE;
-       hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr);
-       hdr99->adler = core99_calc_adler(nvram_image);
-       core99_bank = core99_bank ? 0 : 1;
-       if (core99_erase_bank(core99_bank)) {
-               printk("nvram: Error erasing bank %d\n", core99_bank);
-               return;
-       }
-       if (core99_write_bank(core99_bank, nvram_image))
-               printk("nvram: Error writing bank %d\n", core99_bank);
-}
-
-unsigned char __pmac
-pmac_nvram_read_byte(int addr)
-{
-       switch (nvram_naddrs) {
-#ifdef CONFIG_ADB_PMU
-       case -1: {
-               struct adb_request req;
-
-               if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
-                               (addr >> 8) & 0xff, addr & 0xff))
-                       break;
-               while (!req.complete)
-                       pmu_poll();
-               return req.reply[0];
-       }
-#endif
-       case 1:
-               if (is_core_99)
-                       return nvram_image[addr];
-               return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult];
-       case 2:
-               *nvram_addr = addr >> 5;
-               eieio();
-               return nvram_data[(addr & 0x1f) << 4];
-       }
-       return 0;
-}
-
-void __pmac
-pmac_nvram_write_byte(int addr, unsigned char val)
-{
-       switch (nvram_naddrs) {
-#ifdef CONFIG_ADB_PMU
-       case -1: {
-               struct adb_request req;
-
-               if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
-                               (addr >> 8) & 0xff, addr & 0xff, val))
-                       break;
-               while (!req.complete)
-                       pmu_poll();
-               break;
-       }
-#endif
-       case 1:
-               if (is_core_99) {
-                       nvram_image[addr] = val;
-                       break;
-               }
-               nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val;
-               break;
-       case 2:
-               *nvram_addr = addr >> 5;
-               eieio();
-               nvram_data[(addr & 0x1f) << 4] = val;
-               break;
-       }
-       eieio();
-}
-
-int __pmac
-pmac_get_partition(int partition)
+int __pmac pmac_get_partition(int partition)
 {
        return nvram_partitions[partition];
 }
 
-u8 __pmac
-pmac_xpram_read(int xpaddr)
+u8 __pmac pmac_xpram_read(int xpaddr)
 {
        int offset = nvram_partitions[pmac_nvram_XPRAM];
 
        if (offset < 0)
-               return 0;
+               return 0xff;
 
-       return pmac_nvram_read_byte(xpaddr + offset);
+       return ppc_md.nvram_read_val(xpaddr + offset);
 }
 
-void __pmac
-pmac_xpram_write(int xpaddr, u8 data)
+void __pmac pmac_xpram_write(int xpaddr, u8 data)
 {
        int offset = nvram_partitions[pmac_nvram_XPRAM];
 
        if (offset < 0)
                return;
 
-       pmac_nvram_write_byte(data, xpaddr + offset);
+       ppc_md.nvram_write_val(xpaddr + offset, data);
 }
+
+EXPORT_SYMBOL(pmac_get_partition);
+EXPORT_SYMBOL(pmac_xpram_read);
+EXPORT_SYMBOL(pmac_xpram_write);
index ca8aa47ef43e974fd165014fcdc52d60092bf17a..302afc35f077041f98bb86f8134278f26b691f07 100644 (file)
@@ -57,7 +57,9 @@ obj-$(CONFIG_SONYPI) += sonypi.o
 obj-$(CONFIG_RTC) += rtc.o
 obj-$(CONFIG_GEN_RTC) += genrtc.o
 obj-$(CONFIG_EFI_RTC) += efirtc.o
-ifeq ($(CONFIG_PPC),)
+ifeq ($(CONFIG_GENERIC_NVRAM),y)
+  obj-$(CONFIG_NVRAM) += generic_nvram.o
+else
   obj-$(CONFIG_NVRAM) += nvram.o
 endif
 obj-$(CONFIG_TOSHIBA) += toshiba.o
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
new file mode 100644 (file)
index 0000000..b4d657d
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Generic /dev/nvram driver for architectures providing some
+ * "generic" hooks, that is :
+ *
+ * nvram_read_byte, nvram_write_byte, nvram_sync
+ *
+ * Note that an additional hook is supported for PowerMac only
+ * for getting the nvram "partition" informations
+ *
+ */
+
+#define NVRAM_VERSION "1.1"
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/nvram.h>
+
+#define NVRAM_SIZE     8192
+
+static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
+{
+       lock_kernel();
+       switch (origin) {
+       case 1:
+               offset += file->f_pos;
+               break;
+       case 2:
+               offset += NVRAM_SIZE;
+               break;
+       }
+       if (offset < 0) {
+               unlock_kernel();
+               return -EINVAL;
+       }
+       file->f_pos = offset;
+       unlock_kernel();
+       return file->f_pos;
+}
+
+static ssize_t read_nvram(struct file *file, char __user *buf,
+                         size_t count, loff_t *ppos)
+{
+       unsigned int i;
+       char __user *p = buf;
+
+       if (verify_area(VERIFY_WRITE, buf, count))
+               return -EFAULT;
+       if (*ppos >= NVRAM_SIZE)
+               return 0;
+       for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
+               if (__put_user(nvram_read_byte(i), p))
+                       return -EFAULT;
+       *ppos = i;
+       return p - buf;
+}
+
+static ssize_t write_nvram(struct file *file, const char __user *buf,
+                          size_t count, loff_t *ppos)
+{
+       unsigned int i;
+       const char __user *p = buf;
+       char c;
+
+       if (verify_area(VERIFY_READ, buf, count))
+               return -EFAULT;
+       if (*ppos >= NVRAM_SIZE)
+               return 0;
+       for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
+               if (__get_user(c, p))
+                       return -EFAULT;
+               nvram_write_byte(c, i);
+       }
+       *ppos = i;
+       return p - buf;
+}
+
+static int nvram_ioctl(struct inode *inode, struct file *file,
+       unsigned int cmd, unsigned long arg)
+{
+       switch(cmd) {
+#ifdef CONFIG_PPC_PMAC
+       case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
+               printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
+       case IOC_NVRAM_GET_OFFSET: {
+               int part, offset;
+
+               if (_machine != _MACH_Pmac)
+                       return -EINVAL;
+               if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
+                       return -EFAULT;
+               if (part < pmac_nvram_OF || part > pmac_nvram_NR)
+                       return -EINVAL;
+               offset = pmac_get_partition(part);
+               if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
+                       return -EFAULT;
+               break;
+       }
+#endif /* CONFIG_PPC_PMAC */
+       case IOC_NVRAM_SYNC:
+               nvram_sync();
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct file_operations nvram_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = nvram_llseek,
+       .read           = read_nvram,
+       .write          = write_nvram,
+       .ioctl          = nvram_ioctl,
+};
+
+static struct miscdevice nvram_dev = {
+       NVRAM_MINOR,
+       "nvram",
+       &nvram_fops
+};
+
+int __init nvram_init(void)
+{
+       printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n",
+               NVRAM_VERSION);
+       return misc_register(&nvram_dev);
+}
+
+void __exit nvram_cleanup(void)
+{
+        misc_deregister( &nvram_dev );
+}
+
+module_init(nvram_init);
+module_exit(nvram_cleanup);
+MODULE_LICENSE("GPL");
index da5aadbd07da24b18ef15c7e1422e03a40ff8983..d8755852471ece0ede3ab25dadd21943d2f06400 100644 (file)
@@ -8,9 +8,6 @@ obj-$(CONFIG_PPC_PMAC)          += macio_asic.o
 
 obj-$(CONFIG_PMAC_PBOOK)       += mediabay.o
 obj-$(CONFIG_MAC_SERIAL)       += macserial.o
-ifneq ($(CONFIG_MAC),y)
-  obj-$(CONFIG_NVRAM)          += nvram.o
-endif
 obj-$(CONFIG_MAC_EMUMOUSEBTN)  += mac_hid.o
 obj-$(CONFIG_INPUT_ADBHID)     += adbhid.o
 obj-$(CONFIG_ANSLCD)           += ans-lcd.o
index 54cb5a93d50854dc9b95ea4014d31d98cbff8f7c..c241973ff78ee135c275b34fe0bea30c9617004a 100644 (file)
@@ -57,6 +57,7 @@ struct machdep_calls {
 
        unsigned char   (*nvram_read_val)(int addr);
        void            (*nvram_write_val)(int addr, unsigned char val);
+       void            (*nvram_sync)(void);
 
        /*
         * optional PCI "hooks"
index d65bb55baf9de0c40b70a7d6b4e3cc4a85c401c4..31ef16e3fc4fca68eacf5d8a4bd7d027d66e5c1d 100644 (file)
@@ -34,23 +34,40 @@ enum {
 /* Return partition offset in nvram */
 extern int     pmac_get_partition(int partition);
 
-/* Direct access to XPRAM */
+/* Direct access to XPRAM on PowerMacs */
 extern u8      pmac_xpram_read(int xpaddr);
 extern void    pmac_xpram_write(int xpaddr, u8 data);
 
+/* Synchronize NVRAM */
+extern void    nvram_sync(void);
+
+/* Normal access to NVRAM */
+extern unsigned char nvram_read_byte(int i);
+extern void nvram_write_byte(unsigned char c, int i);
+
 /* Some offsets in XPRAM */
 #define PMAC_XPRAM_MACHINE_LOC 0xe4
 #define PMAC_XPRAM_SOUND_VOLUME        0x08
 
-/* Machine location structure in XPRAM */
+/* Machine location structure in PowerMac XPRAM */
 struct pmac_machine_location {
        unsigned int    latitude;       /* 2+30 bit Fractional number */
        unsigned int    longitude;      /* 2+30 bit Fractional number */
        unsigned int    delta;          /* mix of GMT delta and DLS */
 };
 
-/* /dev/nvram ioctls */
-#define PMAC_NVRAM_GET_OFFSET  _IOWR('p', 0x40, int) /* Get NVRAM partition offset */
+/*
+ * /dev/nvram ioctls
+ *
+ * Note that PMAC_NVRAM_GET_OFFSET is still supported, but is
+ * definitely obsolete. Do not use it if you can avoid it
+ */
+
+#define OBSOLETE_PMAC_NVRAM_GET_OFFSET \
+                               _IOWR('p', 0x40, int)
+
+#define IOC_NVRAM_GET_OFFSET   _IOWR('p', 0x42, int)   /* Get NVRAM partition offset */
+#define IOC_NVRAM_SYNC         _IO('p', 0x43)          /* Sync NVRAM image */
 
 #endif
 #endif /* __KERNEL__ */