18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * The SC520CDP is an evaluation board for the Elan SC520 processor available 78c2ecf20Sopenharmony_ci * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, 88c2ecf20Sopenharmony_ci * and up to 512 KiB of 8-bit DIL Flash ROM. 98c2ecf20Sopenharmony_ci * For details see https://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <asm/io.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 198c2ecf20Sopenharmony_ci#include <linux/mtd/concat.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci** The Embedded Systems BIOS decodes the first FLASH starting at 238c2ecf20Sopenharmony_ci** 0x8400000. This is a *terrible* place for it because accessing 248c2ecf20Sopenharmony_ci** the flash at this location causes the A22 address line to be high 258c2ecf20Sopenharmony_ci** (that's what 0x8400000 binary's ought to be). But this is the highest 268c2ecf20Sopenharmony_ci** order address line on the raw flash devices themselves!! 278c2ecf20Sopenharmony_ci** This causes the top HALF of the flash to be accessed first. Beyond 288c2ecf20Sopenharmony_ci** the physical limits of the flash, the flash chip aliases over (to 298c2ecf20Sopenharmony_ci** 0x880000 which causes the bottom half to be accessed. This splits the 308c2ecf20Sopenharmony_ci** flash into two and inverts it! If you then try to access this from another 318c2ecf20Sopenharmony_ci** program that does NOT do this insanity, then you *will* access the 328c2ecf20Sopenharmony_ci** first half of the flash, but not find what you expect there. That 338c2ecf20Sopenharmony_ci** stuff is in the *second* half! Similarly, the address used by the 348c2ecf20Sopenharmony_ci** BIOS for the second FLASH bank is also quite a bad choice. 358c2ecf20Sopenharmony_ci** If REPROGRAM_PAR is defined below (the default), then this driver will 368c2ecf20Sopenharmony_ci** choose more useful addresses for the FLASH banks by reprogramming the 378c2ecf20Sopenharmony_ci** responsible PARxx registers in the SC520's MMCR region. This will 388c2ecf20Sopenharmony_ci** cause the settings to be incompatible with the BIOS's settings, which 398c2ecf20Sopenharmony_ci** shouldn't be a problem since you are running Linux, (i.e. the BIOS is 408c2ecf20Sopenharmony_ci** not much use anyway). However, if you need to be compatible with 418c2ecf20Sopenharmony_ci** the BIOS for some reason, just undefine REPROGRAM_PAR. 428c2ecf20Sopenharmony_ci*/ 438c2ecf20Sopenharmony_ci#define REPROGRAM_PAR 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#ifdef REPROGRAM_PAR 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* These are the addresses we want.. */ 508c2ecf20Sopenharmony_ci#define WINDOW_ADDR_0 0x08800000 518c2ecf20Sopenharmony_ci#define WINDOW_ADDR_1 0x09000000 528c2ecf20Sopenharmony_ci#define WINDOW_ADDR_2 0x09800000 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* .. and these are the addresses the BIOS gives us */ 558c2ecf20Sopenharmony_ci#define WINDOW_ADDR_0_BIOS 0x08400000 568c2ecf20Sopenharmony_ci#define WINDOW_ADDR_1_BIOS 0x08c00000 578c2ecf20Sopenharmony_ci#define WINDOW_ADDR_2_BIOS 0x09400000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#else 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define WINDOW_ADDR_0 0x08400000 628c2ecf20Sopenharmony_ci#define WINDOW_ADDR_1 0x08C00000 638c2ecf20Sopenharmony_ci#define WINDOW_ADDR_2 0x09400000 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define WINDOW_SIZE_0 0x00800000 688c2ecf20Sopenharmony_ci#define WINDOW_SIZE_1 0x00800000 698c2ecf20Sopenharmony_ci#define WINDOW_SIZE_2 0x00080000 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct map_info sc520cdp_map[] = { 738c2ecf20Sopenharmony_ci { 748c2ecf20Sopenharmony_ci .name = "SC520CDP Flash Bank #0", 758c2ecf20Sopenharmony_ci .size = WINDOW_SIZE_0, 768c2ecf20Sopenharmony_ci .bankwidth = 4, 778c2ecf20Sopenharmony_ci .phys = WINDOW_ADDR_0 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci { 808c2ecf20Sopenharmony_ci .name = "SC520CDP Flash Bank #1", 818c2ecf20Sopenharmony_ci .size = WINDOW_SIZE_1, 828c2ecf20Sopenharmony_ci .bankwidth = 4, 838c2ecf20Sopenharmony_ci .phys = WINDOW_ADDR_1 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci { 868c2ecf20Sopenharmony_ci .name = "SC520CDP DIL Flash", 878c2ecf20Sopenharmony_ci .size = WINDOW_SIZE_2, 888c2ecf20Sopenharmony_ci .bankwidth = 1, 898c2ecf20Sopenharmony_ci .phys = WINDOW_ADDR_2 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct mtd_info *mymtd[NUM_FLASH_BANKS]; 968c2ecf20Sopenharmony_cistatic struct mtd_info *merged_mtd; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#ifdef REPROGRAM_PAR 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci** The SC520 MMCR (memory mapped control register) region resides 1028c2ecf20Sopenharmony_ci** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers 1038c2ecf20Sopenharmony_ci** are at offset 0x88 in the MMCR: 1048c2ecf20Sopenharmony_ci*/ 1058c2ecf20Sopenharmony_ci#define SC520_MMCR_BASE 0xFFFEF000 1068c2ecf20Sopenharmony_ci#define SC520_MMCR_EXTENT 0x1000 1078c2ecf20Sopenharmony_ci#define SC520_PAR(x) ((0x88/sizeof(unsigned long)) + (x)) 1088c2ecf20Sopenharmony_ci#define NUM_SC520_PAR 16 /* total number of PAR registers */ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci** The highest three bits in a PAR register determine what target 1128c2ecf20Sopenharmony_ci** device is controlled by this PAR. Here, only ROMCS? and BOOTCS 1138c2ecf20Sopenharmony_ci** devices are of interest. 1148c2ecf20Sopenharmony_ci*/ 1158c2ecf20Sopenharmony_ci#define SC520_PAR_BOOTCS (0x4<<29) 1168c2ecf20Sopenharmony_ci#define SC520_PAR_ROMCS0 (0x5<<29) 1178c2ecf20Sopenharmony_ci#define SC520_PAR_ROMCS1 (0x6<<29) 1188c2ecf20Sopenharmony_ci#define SC520_PAR_TRGDEV (0x7<<29) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci** Bits 28 thru 26 determine some attributes for the 1228c2ecf20Sopenharmony_ci** region controlled by the PAR. (We only use non-cacheable) 1238c2ecf20Sopenharmony_ci*/ 1248c2ecf20Sopenharmony_ci#define SC520_PAR_WRPROT (1<<26) /* write protected */ 1258c2ecf20Sopenharmony_ci#define SC520_PAR_NOCACHE (1<<27) /* non-cacheable */ 1268c2ecf20Sopenharmony_ci#define SC520_PAR_NOEXEC (1<<28) /* code execution denied */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci** Bit 25 determines the granularity: 4K or 64K 1318c2ecf20Sopenharmony_ci*/ 1328c2ecf20Sopenharmony_ci#define SC520_PAR_PG_SIZ4 (0<<25) 1338c2ecf20Sopenharmony_ci#define SC520_PAR_PG_SIZ64 (1<<25) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci** Build a value to be written into a PAR register. 1378c2ecf20Sopenharmony_ci** We only need ROM entries, 64K page size: 1388c2ecf20Sopenharmony_ci*/ 1398c2ecf20Sopenharmony_ci#define SC520_PAR_ENTRY(trgdev, address, size) \ 1408c2ecf20Sopenharmony_ci ((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \ 1418c2ecf20Sopenharmony_ci (address) >> 16 | (((size) >> 16) - 1) << 14) 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistruct sc520_par_table 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci unsigned long trgdev; 1468c2ecf20Sopenharmony_ci unsigned long new_par; 1478c2ecf20Sopenharmony_ci unsigned long default_address; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct sc520_par_table par_table[NUM_FLASH_BANKS] = 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci { /* Flash Bank #0: selected by ROMCS0 */ 1538c2ecf20Sopenharmony_ci SC520_PAR_ROMCS0, 1548c2ecf20Sopenharmony_ci SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0), 1558c2ecf20Sopenharmony_ci WINDOW_ADDR_0_BIOS 1568c2ecf20Sopenharmony_ci }, 1578c2ecf20Sopenharmony_ci { /* Flash Bank #1: selected by ROMCS1 */ 1588c2ecf20Sopenharmony_ci SC520_PAR_ROMCS1, 1598c2ecf20Sopenharmony_ci SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1), 1608c2ecf20Sopenharmony_ci WINDOW_ADDR_1_BIOS 1618c2ecf20Sopenharmony_ci }, 1628c2ecf20Sopenharmony_ci { /* DIL (BIOS) Flash: selected by BOOTCS */ 1638c2ecf20Sopenharmony_ci SC520_PAR_BOOTCS, 1648c2ecf20Sopenharmony_ci SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2), 1658c2ecf20Sopenharmony_ci WINDOW_ADDR_2_BIOS 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void sc520cdp_setup_par(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned long __iomem *mmcr; 1738c2ecf20Sopenharmony_ci unsigned long mmcr_val; 1748c2ecf20Sopenharmony_ci int i, j; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* map in SC520's MMCR area */ 1778c2ecf20Sopenharmony_ci mmcr = ioremap(SC520_MMCR_BASE, SC520_MMCR_EXTENT); 1788c2ecf20Sopenharmony_ci if(!mmcr) { /* ioremap failed: skip the PAR reprogramming */ 1798c2ecf20Sopenharmony_ci /* force physical address fields to BIOS defaults: */ 1808c2ecf20Sopenharmony_ci for(i = 0; i < NUM_FLASH_BANKS; i++) 1818c2ecf20Sopenharmony_ci sc520cdp_map[i].phys = par_table[i].default_address; 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci ** Find the PARxx registers that are responsible for activating 1878c2ecf20Sopenharmony_ci ** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a 1888c2ecf20Sopenharmony_ci ** new value from the table. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */ 1918c2ecf20Sopenharmony_ci for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */ 1928c2ecf20Sopenharmony_ci mmcr_val = readl(&mmcr[SC520_PAR(j)]); 1938c2ecf20Sopenharmony_ci /* if target device field matches, reprogram the PAR */ 1948c2ecf20Sopenharmony_ci if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev) 1958c2ecf20Sopenharmony_ci { 1968c2ecf20Sopenharmony_ci writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci if(j == NUM_SC520_PAR) 2018c2ecf20Sopenharmony_ci { /* no matching PAR found: try default BIOS address */ 2028c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Could not find PAR responsible for %s\n", 2038c2ecf20Sopenharmony_ci sc520cdp_map[i].name); 2048c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Trying default address 0x%lx\n", 2058c2ecf20Sopenharmony_ci par_table[i].default_address); 2068c2ecf20Sopenharmony_ci sc520cdp_map[i].phys = par_table[i].default_address; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci iounmap(mmcr); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int __init init_sc520cdp(void) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int i, j, devices_found = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#ifdef REPROGRAM_PAR 2198c2ecf20Sopenharmony_ci /* reprogram PAR registers so flash appears at the desired addresses */ 2208c2ecf20Sopenharmony_ci sc520cdp_setup_par(); 2218c2ecf20Sopenharmony_ci#endif 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FLASH_BANKS; i++) { 2248c2ecf20Sopenharmony_ci printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", 2258c2ecf20Sopenharmony_ci (unsigned long long)sc520cdp_map[i].size, 2268c2ecf20Sopenharmony_ci (unsigned long long)sc520cdp_map[i].phys); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci sc520cdp_map[i].virt = ioremap(sc520cdp_map[i].phys, sc520cdp_map[i].size); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!sc520cdp_map[i].virt) { 2318c2ecf20Sopenharmony_ci printk("Failed to ioremap\n"); 2328c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 2338c2ecf20Sopenharmony_ci if (mymtd[j]) { 2348c2ecf20Sopenharmony_ci map_destroy(mymtd[j]); 2358c2ecf20Sopenharmony_ci iounmap(sc520cdp_map[j].virt); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci return -EIO; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci simple_map_init(&sc520cdp_map[i]); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); 2448c2ecf20Sopenharmony_ci if(!mymtd[i]) 2458c2ecf20Sopenharmony_ci mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); 2468c2ecf20Sopenharmony_ci if(!mymtd[i]) 2478c2ecf20Sopenharmony_ci mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (mymtd[i]) { 2508c2ecf20Sopenharmony_ci mymtd[i]->owner = THIS_MODULE; 2518c2ecf20Sopenharmony_ci ++devices_found; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci else { 2548c2ecf20Sopenharmony_ci iounmap(sc520cdp_map[i].virt); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci if(devices_found >= 2) { 2588c2ecf20Sopenharmony_ci /* Combine the two flash banks into a single MTD device & register it: */ 2598c2ecf20Sopenharmony_ci merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1"); 2608c2ecf20Sopenharmony_ci if(merged_mtd) 2618c2ecf20Sopenharmony_ci mtd_device_register(merged_mtd, NULL, 0); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci if(devices_found == 3) /* register the third (DIL-Flash) device */ 2648c2ecf20Sopenharmony_ci mtd_device_register(mymtd[2], NULL, 0); 2658c2ecf20Sopenharmony_ci return(devices_found ? 0 : -ENXIO); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void __exit cleanup_sc520cdp(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci int i; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (merged_mtd) { 2738c2ecf20Sopenharmony_ci mtd_device_unregister(merged_mtd); 2748c2ecf20Sopenharmony_ci mtd_concat_destroy(merged_mtd); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci if (mymtd[2]) 2778c2ecf20Sopenharmony_ci mtd_device_unregister(mymtd[2]); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FLASH_BANKS; i++) { 2808c2ecf20Sopenharmony_ci if (mymtd[i]) 2818c2ecf20Sopenharmony_ci map_destroy(mymtd[i]); 2828c2ecf20Sopenharmony_ci if (sc520cdp_map[i].virt) { 2838c2ecf20Sopenharmony_ci iounmap(sc520cdp_map[i].virt); 2848c2ecf20Sopenharmony_ci sc520cdp_map[i].virt = NULL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cimodule_init(init_sc520cdp); 2908c2ecf20Sopenharmony_cimodule_exit(cleanup_sc520cdp); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sysgo Real-Time Solutions GmbH"); 2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform"); 295