Path: blob/master/arch/powerpc/platforms/powermac/nvram.c
10818 views
/*1* Copyright (C) 2002 Benjamin Herrenschmidt ([email protected])2*3* This program is free software; you can redistribute it and/or4* modify it under the terms of the GNU General Public License5* as published by the Free Software Foundation; either version6* 2 of the License, or (at your option) any later version.7*8* Todo: - add support for the OF persistent properties9*/10#include <linux/module.h>11#include <linux/kernel.h>12#include <linux/stddef.h>13#include <linux/string.h>14#include <linux/nvram.h>15#include <linux/init.h>16#include <linux/delay.h>17#include <linux/errno.h>18#include <linux/adb.h>19#include <linux/pmu.h>20#include <linux/bootmem.h>21#include <linux/completion.h>22#include <linux/spinlock.h>23#include <asm/sections.h>24#include <asm/io.h>25#include <asm/system.h>26#include <asm/prom.h>27#include <asm/machdep.h>28#include <asm/nvram.h>2930#include "pmac.h"3132#define DEBUG3334#ifdef DEBUG35#define DBG(x...) printk(x)36#else37#define DBG(x...)38#endif3940#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */4142#define CORE99_SIGNATURE 0x5a43#define CORE99_ADLER_START 0x144445/* On Core99, nvram is either a sharp, a micron or an AMD flash */46#define SM_FLASH_STATUS_DONE 0x8047#define SM_FLASH_STATUS_ERR 0x384849#define SM_FLASH_CMD_ERASE_CONFIRM 0xd050#define SM_FLASH_CMD_ERASE_SETUP 0x2051#define SM_FLASH_CMD_RESET 0xff52#define SM_FLASH_CMD_WRITE_SETUP 0x4053#define SM_FLASH_CMD_CLEAR_STATUS 0x5054#define SM_FLASH_CMD_READ_STATUS 0x705556/* CHRP NVRAM header */57struct chrp_header {58u8 signature;59u8 cksum;60u16 len;61char name[12];62u8 data[0];63};6465struct core99_header {66struct chrp_header hdr;67u32 adler;68u32 generation;69u32 reserved[2];70};7172/*73* Read and write the non-volatile RAM on PowerMacs and CHRP machines.74*/75static int nvram_naddrs;76static volatile unsigned char __iomem *nvram_data;77static int is_core_99;78static int core99_bank = 0;79static int nvram_partitions[3];80// XXX Turn that into a sem81static DEFINE_RAW_SPINLOCK(nv_lock);8283static int (*core99_write_bank)(int bank, u8* datas);84static int (*core99_erase_bank)(int bank);8586static char *nvram_image;878889static unsigned char core99_nvram_read_byte(int addr)90{91if (nvram_image == NULL)92return 0xff;93return nvram_image[addr];94}9596static void core99_nvram_write_byte(int addr, unsigned char val)97{98if (nvram_image == NULL)99return;100nvram_image[addr] = val;101}102103static ssize_t core99_nvram_read(char *buf, size_t count, loff_t *index)104{105int i;106107if (nvram_image == NULL)108return -ENODEV;109if (*index > NVRAM_SIZE)110return 0;111112i = *index;113if (i + count > NVRAM_SIZE)114count = NVRAM_SIZE - i;115116memcpy(buf, &nvram_image[i], count);117*index = i + count;118return count;119}120121static ssize_t core99_nvram_write(char *buf, size_t count, loff_t *index)122{123int i;124125if (nvram_image == NULL)126return -ENODEV;127if (*index > NVRAM_SIZE)128return 0;129130i = *index;131if (i + count > NVRAM_SIZE)132count = NVRAM_SIZE - i;133134memcpy(&nvram_image[i], buf, count);135*index = i + count;136return count;137}138139static ssize_t core99_nvram_size(void)140{141if (nvram_image == NULL)142return -ENODEV;143return NVRAM_SIZE;144}145146#ifdef CONFIG_PPC32147static volatile unsigned char __iomem *nvram_addr;148static int nvram_mult;149150static unsigned char direct_nvram_read_byte(int addr)151{152return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]);153}154155static void direct_nvram_write_byte(int addr, unsigned char val)156{157out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val);158}159160161static unsigned char indirect_nvram_read_byte(int addr)162{163unsigned char val;164unsigned long flags;165166raw_spin_lock_irqsave(&nv_lock, flags);167out_8(nvram_addr, addr >> 5);168val = in_8(&nvram_data[(addr & 0x1f) << 4]);169raw_spin_unlock_irqrestore(&nv_lock, flags);170171return val;172}173174static void indirect_nvram_write_byte(int addr, unsigned char val)175{176unsigned long flags;177178raw_spin_lock_irqsave(&nv_lock, flags);179out_8(nvram_addr, addr >> 5);180out_8(&nvram_data[(addr & 0x1f) << 4], val);181raw_spin_unlock_irqrestore(&nv_lock, flags);182}183184185#ifdef CONFIG_ADB_PMU186187static void pmu_nvram_complete(struct adb_request *req)188{189if (req->arg)190complete((struct completion *)req->arg);191}192193static unsigned char pmu_nvram_read_byte(int addr)194{195struct adb_request req;196DECLARE_COMPLETION_ONSTACK(req_complete);197198req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL;199if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM,200(addr >> 8) & 0xff, addr & 0xff))201return 0xff;202if (system_state == SYSTEM_RUNNING)203wait_for_completion(&req_complete);204while (!req.complete)205pmu_poll();206return req.reply[0];207}208209static void pmu_nvram_write_byte(int addr, unsigned char val)210{211struct adb_request req;212DECLARE_COMPLETION_ONSTACK(req_complete);213214req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL;215if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM,216(addr >> 8) & 0xff, addr & 0xff, val))217return;218if (system_state == SYSTEM_RUNNING)219wait_for_completion(&req_complete);220while (!req.complete)221pmu_poll();222}223224#endif /* CONFIG_ADB_PMU */225#endif /* CONFIG_PPC32 */226227static u8 chrp_checksum(struct chrp_header* hdr)228{229u8 *ptr;230u16 sum = hdr->signature;231for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++)232sum += *ptr;233while (sum > 0xFF)234sum = (sum & 0xFF) + (sum>>8);235return sum;236}237238static u32 core99_calc_adler(u8 *buffer)239{240int cnt;241u32 low, high;242243buffer += CORE99_ADLER_START;244low = 1;245high = 0;246for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) {247if ((cnt % 5000) == 0) {248high %= 65521UL;249high %= 65521UL;250}251low += buffer[cnt];252high += low;253}254low %= 65521UL;255high %= 65521UL;256257return (high << 16) | low;258}259260static u32 core99_check(u8* datas)261{262struct core99_header* hdr99 = (struct core99_header*)datas;263264if (hdr99->hdr.signature != CORE99_SIGNATURE) {265DBG("Invalid signature\n");266return 0;267}268if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) {269DBG("Invalid checksum\n");270return 0;271}272if (hdr99->adler != core99_calc_adler(datas)) {273DBG("Invalid adler\n");274return 0;275}276return hdr99->generation;277}278279static int sm_erase_bank(int bank)280{281int stat, i;282unsigned long timeout;283284u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;285286DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank);287288out_8(base, SM_FLASH_CMD_ERASE_SETUP);289out_8(base, SM_FLASH_CMD_ERASE_CONFIRM);290timeout = 0;291do {292if (++timeout > 1000000) {293printk(KERN_ERR "nvram: Sharp/Micron flash erase timeout !\n");294break;295}296out_8(base, SM_FLASH_CMD_READ_STATUS);297stat = in_8(base);298} while (!(stat & SM_FLASH_STATUS_DONE));299300out_8(base, SM_FLASH_CMD_CLEAR_STATUS);301out_8(base, SM_FLASH_CMD_RESET);302303for (i=0; i<NVRAM_SIZE; i++)304if (base[i] != 0xff) {305printk(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n");306return -ENXIO;307}308return 0;309}310311static int sm_write_bank(int bank, u8* datas)312{313int i, stat = 0;314unsigned long timeout;315316u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;317318DBG("nvram: Sharp/Micron Writing bank %d...\n", bank);319320for (i=0; i<NVRAM_SIZE; i++) {321out_8(base+i, SM_FLASH_CMD_WRITE_SETUP);322udelay(1);323out_8(base+i, datas[i]);324timeout = 0;325do {326if (++timeout > 1000000) {327printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n");328break;329}330out_8(base, SM_FLASH_CMD_READ_STATUS);331stat = in_8(base);332} while (!(stat & SM_FLASH_STATUS_DONE));333if (!(stat & SM_FLASH_STATUS_DONE))334break;335}336out_8(base, SM_FLASH_CMD_CLEAR_STATUS);337out_8(base, SM_FLASH_CMD_RESET);338for (i=0; i<NVRAM_SIZE; i++)339if (base[i] != datas[i]) {340printk(KERN_ERR "nvram: Sharp/Micron flash write failed !\n");341return -ENXIO;342}343return 0;344}345346static int amd_erase_bank(int bank)347{348int i, stat = 0;349unsigned long timeout;350351u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;352353DBG("nvram: AMD Erasing bank %d...\n", bank);354355/* Unlock 1 */356out_8(base+0x555, 0xaa);357udelay(1);358/* Unlock 2 */359out_8(base+0x2aa, 0x55);360udelay(1);361362/* Sector-Erase */363out_8(base+0x555, 0x80);364udelay(1);365out_8(base+0x555, 0xaa);366udelay(1);367out_8(base+0x2aa, 0x55);368udelay(1);369out_8(base, 0x30);370udelay(1);371372timeout = 0;373do {374if (++timeout > 1000000) {375printk(KERN_ERR "nvram: AMD flash erase timeout !\n");376break;377}378stat = in_8(base) ^ in_8(base);379} while (stat != 0);380381/* Reset */382out_8(base, 0xf0);383udelay(1);384385for (i=0; i<NVRAM_SIZE; i++)386if (base[i] != 0xff) {387printk(KERN_ERR "nvram: AMD flash erase failed !\n");388return -ENXIO;389}390return 0;391}392393static int amd_write_bank(int bank, u8* datas)394{395int i, stat = 0;396unsigned long timeout;397398u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;399400DBG("nvram: AMD Writing bank %d...\n", bank);401402for (i=0; i<NVRAM_SIZE; i++) {403/* Unlock 1 */404out_8(base+0x555, 0xaa);405udelay(1);406/* Unlock 2 */407out_8(base+0x2aa, 0x55);408udelay(1);409410/* Write single word */411out_8(base+0x555, 0xa0);412udelay(1);413out_8(base+i, datas[i]);414415timeout = 0;416do {417if (++timeout > 1000000) {418printk(KERN_ERR "nvram: AMD flash write timeout !\n");419break;420}421stat = in_8(base) ^ in_8(base);422} while (stat != 0);423if (stat != 0)424break;425}426427/* Reset */428out_8(base, 0xf0);429udelay(1);430431for (i=0; i<NVRAM_SIZE; i++)432if (base[i] != datas[i]) {433printk(KERN_ERR "nvram: AMD flash write failed !\n");434return -ENXIO;435}436return 0;437}438439static void __init lookup_partitions(void)440{441u8 buffer[17];442int i, offset;443struct chrp_header* hdr;444445if (pmac_newworld) {446nvram_partitions[pmac_nvram_OF] = -1;447nvram_partitions[pmac_nvram_XPRAM] = -1;448nvram_partitions[pmac_nvram_NR] = -1;449hdr = (struct chrp_header *)buffer;450451offset = 0;452buffer[16] = 0;453do {454for (i=0;i<16;i++)455buffer[i] = ppc_md.nvram_read_val(offset+i);456if (!strcmp(hdr->name, "common"))457nvram_partitions[pmac_nvram_OF] = offset + 0x10;458if (!strcmp(hdr->name, "APL,MacOS75")) {459nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10;460nvram_partitions[pmac_nvram_NR] = offset + 0x110;461}462offset += (hdr->len * 0x10);463} while(offset < NVRAM_SIZE);464} else {465nvram_partitions[pmac_nvram_OF] = 0x1800;466nvram_partitions[pmac_nvram_XPRAM] = 0x1300;467nvram_partitions[pmac_nvram_NR] = 0x1400;468}469DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]);470DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]);471DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]);472}473474static void core99_nvram_sync(void)475{476struct core99_header* hdr99;477unsigned long flags;478479if (!is_core_99 || !nvram_data || !nvram_image)480return;481482raw_spin_lock_irqsave(&nv_lock, flags);483if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE,484NVRAM_SIZE))485goto bail;486487DBG("Updating nvram...\n");488489hdr99 = (struct core99_header*)nvram_image;490hdr99->generation++;491hdr99->hdr.signature = CORE99_SIGNATURE;492hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr);493hdr99->adler = core99_calc_adler(nvram_image);494core99_bank = core99_bank ? 0 : 1;495if (core99_erase_bank)496if (core99_erase_bank(core99_bank)) {497printk("nvram: Error erasing bank %d\n", core99_bank);498goto bail;499}500if (core99_write_bank)501if (core99_write_bank(core99_bank, nvram_image))502printk("nvram: Error writing bank %d\n", core99_bank);503bail:504raw_spin_unlock_irqrestore(&nv_lock, flags);505506#ifdef DEBUG507mdelay(2000);508#endif509}510511static int __init core99_nvram_setup(struct device_node *dp, unsigned long addr)512{513int i;514u32 gen_bank0, gen_bank1;515516if (nvram_naddrs < 1) {517printk(KERN_ERR "nvram: no address\n");518return -EINVAL;519}520nvram_image = alloc_bootmem(NVRAM_SIZE);521if (nvram_image == NULL) {522printk(KERN_ERR "nvram: can't allocate ram image\n");523return -ENOMEM;524}525nvram_data = ioremap(addr, NVRAM_SIZE*2);526nvram_naddrs = 1; /* Make sure we get the correct case */527528DBG("nvram: Checking bank 0...\n");529530gen_bank0 = core99_check((u8 *)nvram_data);531gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE);532core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0;533534DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1);535DBG("nvram: Active bank is: %d\n", core99_bank);536537for (i=0; i<NVRAM_SIZE; i++)538nvram_image[i] = nvram_data[i + core99_bank*NVRAM_SIZE];539540ppc_md.nvram_read_val = core99_nvram_read_byte;541ppc_md.nvram_write_val = core99_nvram_write_byte;542ppc_md.nvram_read = core99_nvram_read;543ppc_md.nvram_write = core99_nvram_write;544ppc_md.nvram_size = core99_nvram_size;545ppc_md.nvram_sync = core99_nvram_sync;546ppc_md.machine_shutdown = core99_nvram_sync;547/*548* Maybe we could be smarter here though making an exclusive list549* of known flash chips is a bit nasty as older OF didn't provide us550* with a useful "compatible" entry. A solution would be to really551* identify the chip using flash id commands and base ourselves on552* a list of known chips IDs553*/554if (of_device_is_compatible(dp, "amd-0137")) {555core99_erase_bank = amd_erase_bank;556core99_write_bank = amd_write_bank;557} else {558core99_erase_bank = sm_erase_bank;559core99_write_bank = sm_write_bank;560}561return 0;562}563564int __init pmac_nvram_init(void)565{566struct device_node *dp;567struct resource r1, r2;568unsigned int s1 = 0, s2 = 0;569int err = 0;570571nvram_naddrs = 0;572573dp = of_find_node_by_name(NULL, "nvram");574if (dp == NULL) {575printk(KERN_ERR "Can't find NVRAM device\n");576return -ENODEV;577}578579/* Try to obtain an address */580if (of_address_to_resource(dp, 0, &r1) == 0) {581nvram_naddrs = 1;582s1 = (r1.end - r1.start) + 1;583if (of_address_to_resource(dp, 1, &r2) == 0) {584nvram_naddrs = 2;585s2 = (r2.end - r2.start) + 1;586}587}588589is_core_99 = of_device_is_compatible(dp, "nvram,flash");590if (is_core_99) {591err = core99_nvram_setup(dp, r1.start);592goto bail;593}594595#ifdef CONFIG_PPC32596if (machine_is(chrp) && nvram_naddrs == 1) {597nvram_data = ioremap(r1.start, s1);598nvram_mult = 1;599ppc_md.nvram_read_val = direct_nvram_read_byte;600ppc_md.nvram_write_val = direct_nvram_write_byte;601} else if (nvram_naddrs == 1) {602nvram_data = ioremap(r1.start, s1);603nvram_mult = (s1 + NVRAM_SIZE - 1) / NVRAM_SIZE;604ppc_md.nvram_read_val = direct_nvram_read_byte;605ppc_md.nvram_write_val = direct_nvram_write_byte;606} else if (nvram_naddrs == 2) {607nvram_addr = ioremap(r1.start, s1);608nvram_data = ioremap(r2.start, s2);609ppc_md.nvram_read_val = indirect_nvram_read_byte;610ppc_md.nvram_write_val = indirect_nvram_write_byte;611} else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) {612#ifdef CONFIG_ADB_PMU613nvram_naddrs = -1;614ppc_md.nvram_read_val = pmu_nvram_read_byte;615ppc_md.nvram_write_val = pmu_nvram_write_byte;616#endif /* CONFIG_ADB_PMU */617} else {618printk(KERN_ERR "Incompatible type of NVRAM\n");619err = -ENXIO;620}621#endif /* CONFIG_PPC32 */622bail:623of_node_put(dp);624if (err == 0)625lookup_partitions();626return err;627}628629int pmac_get_partition(int partition)630{631return nvram_partitions[partition];632}633634u8 pmac_xpram_read(int xpaddr)635{636int offset = pmac_get_partition(pmac_nvram_XPRAM);637638if (offset < 0 || xpaddr < 0 || xpaddr > 0x100)639return 0xff;640641return ppc_md.nvram_read_val(xpaddr + offset);642}643644void pmac_xpram_write(int xpaddr, u8 data)645{646int offset = pmac_get_partition(pmac_nvram_XPRAM);647648if (offset < 0 || xpaddr < 0 || xpaddr > 0x100)649return;650651ppc_md.nvram_write_val(xpaddr + offset, data);652}653654EXPORT_SYMBOL(pmac_get_partition);655EXPORT_SYMBOL(pmac_xpram_read);656EXPORT_SYMBOL(pmac_xpram_write);657658659