Path: blob/master/arch/powerpc/platforms/powermac/nvram.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2002 Benjamin Herrenschmidt ([email protected])3*4* Todo: - add support for the OF persistent properties5*/6#include <linux/export.h>7#include <linux/kernel.h>8#include <linux/stddef.h>9#include <linux/string.h>10#include <linux/nvram.h>11#include <linux/init.h>12#include <linux/delay.h>13#include <linux/errno.h>14#include <linux/adb.h>15#include <linux/pmu.h>16#include <linux/memblock.h>17#include <linux/completion.h>18#include <linux/spinlock.h>19#include <linux/of_address.h>20#include <asm/sections.h>21#include <asm/io.h>22#include <asm/machdep.h>23#include <asm/nvram.h>2425#include "pmac.h"2627#define DEBUG2829#ifdef DEBUG30#define DBG(x...) printk(x)31#else32#define DBG(x...)33#endif3435#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */3637#define CORE99_SIGNATURE 0x5a38#define CORE99_ADLER_START 0x143940/* On Core99, nvram is either a sharp, a micron or an AMD flash */41#define SM_FLASH_STATUS_DONE 0x8042#define SM_FLASH_STATUS_ERR 0x384344#define SM_FLASH_CMD_ERASE_CONFIRM 0xd045#define SM_FLASH_CMD_ERASE_SETUP 0x2046#define SM_FLASH_CMD_RESET 0xff47#define SM_FLASH_CMD_WRITE_SETUP 0x4048#define SM_FLASH_CMD_CLEAR_STATUS 0x5049#define SM_FLASH_CMD_READ_STATUS 0x705051/* CHRP NVRAM header */52struct chrp_header {53u8 signature;54u8 cksum;55u16 len;56char name[12];57u8 data[];58};5960struct core99_header {61struct chrp_header hdr;62u32 adler;63u32 generation;64u32 reserved[2];65};6667/*68* Read and write the non-volatile RAM on PowerMacs and CHRP machines.69*/70static int nvram_naddrs;71static volatile unsigned char __iomem *nvram_data;72static int is_core_99;73static int core99_bank;74static int nvram_partitions[3];75// XXX Turn that into a sem76static DEFINE_RAW_SPINLOCK(nv_lock);7778static int (*core99_write_bank)(int bank, u8* datas);79static int (*core99_erase_bank)(int bank);8081static char *nvram_image;828384static unsigned char core99_nvram_read_byte(int addr)85{86if (nvram_image == NULL)87return 0xff;88return nvram_image[addr];89}9091static void core99_nvram_write_byte(int addr, unsigned char val)92{93if (nvram_image == NULL)94return;95nvram_image[addr] = val;96}9798static ssize_t core99_nvram_read(char *buf, size_t count, loff_t *index)99{100int i;101102if (nvram_image == NULL)103return -ENODEV;104if (*index > NVRAM_SIZE)105return 0;106107i = *index;108if (i + count > NVRAM_SIZE)109count = NVRAM_SIZE - i;110111memcpy(buf, &nvram_image[i], count);112*index = i + count;113return count;114}115116static ssize_t core99_nvram_write(char *buf, size_t count, loff_t *index)117{118int i;119120if (nvram_image == NULL)121return -ENODEV;122if (*index > NVRAM_SIZE)123return 0;124125i = *index;126if (i + count > NVRAM_SIZE)127count = NVRAM_SIZE - i;128129memcpy(&nvram_image[i], buf, count);130*index = i + count;131return count;132}133134static ssize_t core99_nvram_size(void)135{136if (nvram_image == NULL)137return -ENODEV;138return NVRAM_SIZE;139}140141#ifdef CONFIG_PPC32142static volatile unsigned char __iomem *nvram_addr;143static int nvram_mult;144145static ssize_t ppc32_nvram_size(void)146{147return NVRAM_SIZE;148}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 __init 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;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);302303if (memchr_inv(base, 0xff, NVRAM_SIZE)) {304printk(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n");305return -ENXIO;306}307return 0;308}309310static int sm_write_bank(int bank, u8* datas)311{312int i, stat = 0;313unsigned long timeout;314315u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;316317DBG("nvram: Sharp/Micron Writing bank %d...\n", bank);318319for (i=0; i<NVRAM_SIZE; i++) {320out_8(base+i, SM_FLASH_CMD_WRITE_SETUP);321udelay(1);322out_8(base+i, datas[i]);323timeout = 0;324do {325if (++timeout > 1000000) {326printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n");327break;328}329out_8(base, SM_FLASH_CMD_READ_STATUS);330stat = in_8(base);331} while (!(stat & SM_FLASH_STATUS_DONE));332if (!(stat & SM_FLASH_STATUS_DONE))333break;334}335out_8(base, SM_FLASH_CMD_CLEAR_STATUS);336out_8(base, SM_FLASH_CMD_RESET);337if (memcmp(base, datas, NVRAM_SIZE)) {338printk(KERN_ERR "nvram: Sharp/Micron flash write failed !\n");339return -ENXIO;340}341return 0;342}343344static int amd_erase_bank(int bank)345{346int stat = 0;347unsigned long timeout;348349u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;350351DBG("nvram: AMD Erasing bank %d...\n", bank);352353/* Unlock 1 */354out_8(base+0x555, 0xaa);355udelay(1);356/* Unlock 2 */357out_8(base+0x2aa, 0x55);358udelay(1);359360/* Sector-Erase */361out_8(base+0x555, 0x80);362udelay(1);363out_8(base+0x555, 0xaa);364udelay(1);365out_8(base+0x2aa, 0x55);366udelay(1);367out_8(base, 0x30);368udelay(1);369370timeout = 0;371do {372if (++timeout > 1000000) {373printk(KERN_ERR "nvram: AMD flash erase timeout !\n");374break;375}376stat = in_8(base) ^ in_8(base);377} while (stat != 0);378379/* Reset */380out_8(base, 0xf0);381udelay(1);382383if (memchr_inv(base, 0xff, NVRAM_SIZE)) {384printk(KERN_ERR "nvram: AMD flash erase failed !\n");385return -ENXIO;386}387return 0;388}389390static int amd_write_bank(int bank, u8* datas)391{392int i, stat = 0;393unsigned long timeout;394395u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE;396397DBG("nvram: AMD Writing bank %d...\n", bank);398399for (i=0; i<NVRAM_SIZE; i++) {400/* Unlock 1 */401out_8(base+0x555, 0xaa);402udelay(1);403/* Unlock 2 */404out_8(base+0x2aa, 0x55);405udelay(1);406407/* Write single word */408out_8(base+0x555, 0xa0);409udelay(1);410out_8(base+i, datas[i]);411412timeout = 0;413do {414if (++timeout > 1000000) {415printk(KERN_ERR "nvram: AMD flash write timeout !\n");416break;417}418stat = in_8(base) ^ in_8(base);419} while (stat != 0);420if (stat != 0)421break;422}423424/* Reset */425out_8(base, 0xf0);426udelay(1);427428if (memcmp(base, datas, NVRAM_SIZE)) {429printk(KERN_ERR "nvram: AMD flash write failed !\n");430return -ENXIO;431}432return 0;433}434435static void __init lookup_partitions(void)436{437u8 buffer[17];438int i, offset;439struct chrp_header* hdr;440441if (pmac_newworld) {442nvram_partitions[pmac_nvram_OF] = -1;443nvram_partitions[pmac_nvram_XPRAM] = -1;444nvram_partitions[pmac_nvram_NR] = -1;445hdr = (struct chrp_header *)buffer;446447offset = 0;448buffer[16] = 0;449do {450for (i=0;i<16;i++)451buffer[i] = ppc_md.nvram_read_val(offset+i);452if (!strcmp(hdr->name, "common"))453nvram_partitions[pmac_nvram_OF] = offset + 0x10;454if (!strcmp(hdr->name, "APL,MacOS75")) {455nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10;456nvram_partitions[pmac_nvram_NR] = offset + 0x110;457}458offset += (hdr->len * 0x10);459} while(offset < NVRAM_SIZE);460} else {461nvram_partitions[pmac_nvram_OF] = 0x1800;462nvram_partitions[pmac_nvram_XPRAM] = 0x1300;463nvram_partitions[pmac_nvram_NR] = 0x1400;464}465DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]);466DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]);467DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]);468}469470static void core99_nvram_sync(void)471{472struct core99_header* hdr99;473unsigned long flags;474475if (!is_core_99 || !nvram_data || !nvram_image)476return;477478raw_spin_lock_irqsave(&nv_lock, flags);479if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE,480NVRAM_SIZE))481goto bail;482483DBG("Updating nvram...\n");484485hdr99 = (struct core99_header*)nvram_image;486hdr99->generation++;487hdr99->hdr.signature = CORE99_SIGNATURE;488hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr);489hdr99->adler = core99_calc_adler(nvram_image);490core99_bank = core99_bank ? 0 : 1;491if (core99_erase_bank)492if (core99_erase_bank(core99_bank)) {493printk("nvram: Error erasing bank %d\n", core99_bank);494goto bail;495}496if (core99_write_bank)497if (core99_write_bank(core99_bank, nvram_image))498printk("nvram: Error writing bank %d\n", core99_bank);499bail:500raw_spin_unlock_irqrestore(&nv_lock, flags);501502#ifdef DEBUG503mdelay(2000);504#endif505}506507static int __init core99_nvram_setup(struct device_node *dp, unsigned long addr)508{509int i;510u32 gen_bank0, gen_bank1;511512if (nvram_naddrs < 1) {513printk(KERN_ERR "nvram: no address\n");514return -EINVAL;515}516nvram_image = memblock_alloc_or_panic(NVRAM_SIZE, SMP_CACHE_BYTES);517nvram_data = ioremap(addr, NVRAM_SIZE*2);518nvram_naddrs = 1; /* Make sure we get the correct case */519520DBG("nvram: Checking bank 0...\n");521522gen_bank0 = core99_check((u8 *)nvram_data);523gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE);524core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0;525526DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1);527DBG("nvram: Active bank is: %d\n", core99_bank);528529for (i=0; i<NVRAM_SIZE; i++)530nvram_image[i] = nvram_data[i + core99_bank*NVRAM_SIZE];531532ppc_md.nvram_read_val = core99_nvram_read_byte;533ppc_md.nvram_write_val = core99_nvram_write_byte;534ppc_md.nvram_read = core99_nvram_read;535ppc_md.nvram_write = core99_nvram_write;536ppc_md.nvram_size = core99_nvram_size;537ppc_md.nvram_sync = core99_nvram_sync;538ppc_md.machine_shutdown = core99_nvram_sync;539/*540* Maybe we could be smarter here though making an exclusive list541* of known flash chips is a bit nasty as older OF didn't provide us542* with a useful "compatible" entry. A solution would be to really543* identify the chip using flash id commands and base ourselves on544* a list of known chips IDs545*/546if (of_device_is_compatible(dp, "amd-0137")) {547core99_erase_bank = amd_erase_bank;548core99_write_bank = amd_write_bank;549} else {550core99_erase_bank = sm_erase_bank;551core99_write_bank = sm_write_bank;552}553return 0;554}555556int __init pmac_nvram_init(void)557{558struct device_node *dp;559struct resource r1, r2;560unsigned int s1 = 0, s2 = 0;561int err = 0;562563nvram_naddrs = 0;564565dp = of_find_node_by_name(NULL, "nvram");566if (dp == NULL) {567printk(KERN_ERR "Can't find NVRAM device\n");568return -ENODEV;569}570571/* Try to obtain an address */572if (of_address_to_resource(dp, 0, &r1) == 0) {573nvram_naddrs = 1;574s1 = resource_size(&r1);575if (of_address_to_resource(dp, 1, &r2) == 0) {576nvram_naddrs = 2;577s2 = resource_size(&r2);578}579}580581is_core_99 = of_device_is_compatible(dp, "nvram,flash");582if (is_core_99) {583err = core99_nvram_setup(dp, r1.start);584goto bail;585}586587#ifdef CONFIG_PPC32588if (machine_is(chrp) && nvram_naddrs == 1) {589nvram_data = ioremap(r1.start, s1);590nvram_mult = 1;591ppc_md.nvram_read_val = direct_nvram_read_byte;592ppc_md.nvram_write_val = direct_nvram_write_byte;593ppc_md.nvram_size = ppc32_nvram_size;594} else if (nvram_naddrs == 1) {595nvram_data = ioremap(r1.start, s1);596nvram_mult = (s1 + NVRAM_SIZE - 1) / NVRAM_SIZE;597ppc_md.nvram_read_val = direct_nvram_read_byte;598ppc_md.nvram_write_val = direct_nvram_write_byte;599ppc_md.nvram_size = ppc32_nvram_size;600} else if (nvram_naddrs == 2) {601nvram_addr = ioremap(r1.start, s1);602nvram_data = ioremap(r2.start, s2);603ppc_md.nvram_read_val = indirect_nvram_read_byte;604ppc_md.nvram_write_val = indirect_nvram_write_byte;605ppc_md.nvram_size = ppc32_nvram_size;606} else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) {607#ifdef CONFIG_ADB_PMU608nvram_naddrs = -1;609ppc_md.nvram_read_val = pmu_nvram_read_byte;610ppc_md.nvram_write_val = pmu_nvram_write_byte;611ppc_md.nvram_size = ppc32_nvram_size;612#endif /* CONFIG_ADB_PMU */613} else {614printk(KERN_ERR "Incompatible type of NVRAM\n");615err = -ENXIO;616}617#endif /* CONFIG_PPC32 */618bail:619of_node_put(dp);620if (err == 0)621lookup_partitions();622return err;623}624625int pmac_get_partition(int partition)626{627return nvram_partitions[partition];628}629630u8 pmac_xpram_read(int xpaddr)631{632int offset = pmac_get_partition(pmac_nvram_XPRAM);633634if (offset < 0 || xpaddr < 0 || xpaddr > 0x100)635return 0xff;636637return ppc_md.nvram_read_val(xpaddr + offset);638}639640void pmac_xpram_write(int xpaddr, u8 data)641{642int offset = pmac_get_partition(pmac_nvram_XPRAM);643644if (offset < 0 || xpaddr < 0 || xpaddr > 0x100)645return;646647ppc_md.nvram_write_val(xpaddr + offset, data);648}649650EXPORT_SYMBOL(pmac_get_partition);651EXPORT_SYMBOL(pmac_xpram_read);652EXPORT_SYMBOL(pmac_xpram_write);653654655