18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/mtd/maps/ixp4xx.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * MTD Map file for IXP4XX based systems. Please do not make per-board 68c2ecf20Sopenharmony_ci * changes in here. If your board needs special setup, do it in your 78c2ecf20Sopenharmony_ci * platform level code in arch/arm/mach-ixp4xx/board-setup.c 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Original Author: Intel Corporation 108c2ecf20Sopenharmony_ci * Maintainer: Deepak Saxena <dsaxena@mvista.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2002 Intel Corporation 138c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 MontaVista Software, Inc. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/string.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/ioport.h> 248c2ecf20Sopenharmony_ci#include <linux/device.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 288c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 298c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/io.h> 328c2ecf20Sopenharmony_ci#include <asm/mach/flash.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/reboot.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Read/write a 16 bit word from flash address 'addr'. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * When the cpu is in little-endian mode it swizzles the address lines 408c2ecf20Sopenharmony_ci * ('address coherency') so we need to undo the swizzling to ensure commands 418c2ecf20Sopenharmony_ci * and the like end up on the correct flash address. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * To further complicate matters, due to the way the expansion bus controller 448c2ecf20Sopenharmony_ci * handles 32 bit reads, the byte stream ABCD is stored on the flash as: 458c2ecf20Sopenharmony_ci * D15 D0 468c2ecf20Sopenharmony_ci * +---+---+ 478c2ecf20Sopenharmony_ci * | A | B | 0 488c2ecf20Sopenharmony_ci * +---+---+ 498c2ecf20Sopenharmony_ci * | C | D | 2 508c2ecf20Sopenharmony_ci * +---+---+ 518c2ecf20Sopenharmony_ci * This means that on LE systems each 16 bit word must be swapped. Note that 528c2ecf20Sopenharmony_ci * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI 538c2ecf20Sopenharmony_ci * data and other flash commands which are always in D7-D0. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci#ifndef __ARMEB__ 568c2ecf20Sopenharmony_ci#ifndef CONFIG_MTD_CFI_BE_BYTE_SWAP 578c2ecf20Sopenharmony_ci# error CONFIG_MTD_CFI_BE_BYTE_SWAP required 588c2ecf20Sopenharmony_ci#endif 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline u16 flash_read16(void __iomem *addr) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2))); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void flash_write16(u16 d, void __iomem *addr) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci __raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2)); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define BYTE0(h) ((h) & 0xFF) 718c2ecf20Sopenharmony_ci#define BYTE1(h) (((h) >> 8) & 0xFF) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#else 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline u16 flash_read16(const void __iomem *addr) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return __raw_readw(addr); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline void flash_write16(u16 d, void __iomem *addr) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci __raw_writew(d, addr); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define BYTE0(h) (((h) >> 8) & 0xFF) 868c2ecf20Sopenharmony_ci#define BYTE1(h) ((h) & 0xFF) 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci map_word val; 928c2ecf20Sopenharmony_ci val.x[0] = flash_read16(map->virt + ofs); 938c2ecf20Sopenharmony_ci return val; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* 978c2ecf20Sopenharmony_ci * The IXP4xx expansion bus only allows 16-bit wide acceses 988c2ecf20Sopenharmony_ci * when attached to a 16-bit wide device (such as the 28F128J3A), 998c2ecf20Sopenharmony_ci * so we can't just memcpy_fromio(). 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void ixp4xx_copy_from(struct map_info *map, void *to, 1028c2ecf20Sopenharmony_ci unsigned long from, ssize_t len) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u8 *dest = (u8 *) to; 1058c2ecf20Sopenharmony_ci void __iomem *src = map->virt + from; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (len <= 0) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (from & 1) { 1118c2ecf20Sopenharmony_ci *dest++ = BYTE1(flash_read16(src-1)); 1128c2ecf20Sopenharmony_ci src++; 1138c2ecf20Sopenharmony_ci --len; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci while (len >= 2) { 1178c2ecf20Sopenharmony_ci u16 data = flash_read16(src); 1188c2ecf20Sopenharmony_ci *dest++ = BYTE0(data); 1198c2ecf20Sopenharmony_ci *dest++ = BYTE1(data); 1208c2ecf20Sopenharmony_ci src += 2; 1218c2ecf20Sopenharmony_ci len -= 2; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (len > 0) 1258c2ecf20Sopenharmony_ci *dest++ = BYTE0(flash_read16(src)); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * Unaligned writes are ignored, causing the 8-bit 1308c2ecf20Sopenharmony_ci * probe to fail and proceed to the 16-bit probe (which succeeds). 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci if (!(adr & 1)) 1358c2ecf20Sopenharmony_ci flash_write16(d.x[0], map->virt + adr); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* 1398c2ecf20Sopenharmony_ci * Fast write16 function without the probing check above 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci flash_write16(d.x[0], map->virt + adr); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct ixp4xx_flash_info { 1478c2ecf20Sopenharmony_ci struct mtd_info *mtd; 1488c2ecf20Sopenharmony_ci struct map_info map; 1498c2ecf20Sopenharmony_ci struct resource *res; 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int ixp4xx_flash_remove(struct platform_device *dev) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct flash_platform_data *plat = dev_get_platdata(&dev->dev); 1578c2ecf20Sopenharmony_ci struct ixp4xx_flash_info *info = platform_get_drvdata(dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if(!info) 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (info->mtd) { 1638c2ecf20Sopenharmony_ci mtd_device_unregister(info->mtd); 1648c2ecf20Sopenharmony_ci map_destroy(info->mtd); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (plat->exit) 1688c2ecf20Sopenharmony_ci plat->exit(); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int ixp4xx_flash_probe(struct platform_device *dev) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct flash_platform_data *plat = dev_get_platdata(&dev->dev); 1768c2ecf20Sopenharmony_ci struct ixp4xx_flash_info *info; 1778c2ecf20Sopenharmony_ci struct mtd_part_parser_data ppdata = { 1788c2ecf20Sopenharmony_ci .origin = dev->resource->start, 1798c2ecf20Sopenharmony_ci }; 1808c2ecf20Sopenharmony_ci int err = -1; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!plat) 1838c2ecf20Sopenharmony_ci return -ENODEV; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (plat->init) { 1868c2ecf20Sopenharmony_ci err = plat->init(); 1878c2ecf20Sopenharmony_ci if (err) 1888c2ecf20Sopenharmony_ci return err; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci info = devm_kzalloc(&dev->dev, sizeof(struct ixp4xx_flash_info), 1928c2ecf20Sopenharmony_ci GFP_KERNEL); 1938c2ecf20Sopenharmony_ci if(!info) { 1948c2ecf20Sopenharmony_ci err = -ENOMEM; 1958c2ecf20Sopenharmony_ci goto Error; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Tell the MTD layer we're not 1:1 mapped so that it does 2028c2ecf20Sopenharmony_ci * not attempt to do a direct access on us. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci info->map.phys = NO_XIP; 2058c2ecf20Sopenharmony_ci info->map.size = resource_size(dev->resource); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * We only support 16-bit accesses for now. If and when 2098c2ecf20Sopenharmony_ci * any board use 8-bit access, we'll fixup the driver to 2108c2ecf20Sopenharmony_ci * handle that. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci info->map.bankwidth = 2; 2138c2ecf20Sopenharmony_ci info->map.name = dev_name(&dev->dev); 2148c2ecf20Sopenharmony_ci info->map.read = ixp4xx_read16; 2158c2ecf20Sopenharmony_ci info->map.write = ixp4xx_probe_write16; 2168c2ecf20Sopenharmony_ci info->map.copy_from = ixp4xx_copy_from; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci info->map.virt = devm_ioremap_resource(&dev->dev, dev->resource); 2198c2ecf20Sopenharmony_ci if (IS_ERR(info->map.virt)) { 2208c2ecf20Sopenharmony_ci err = PTR_ERR(info->map.virt); 2218c2ecf20Sopenharmony_ci goto Error; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci info->mtd = do_map_probe(plat->map_name, &info->map); 2258c2ecf20Sopenharmony_ci if (!info->mtd) { 2268c2ecf20Sopenharmony_ci printk(KERN_ERR "IXP4XXFlash: map_probe failed\n"); 2278c2ecf20Sopenharmony_ci err = -ENXIO; 2288c2ecf20Sopenharmony_ci goto Error; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci info->mtd->dev.parent = &dev->dev; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Use the fast version */ 2338c2ecf20Sopenharmony_ci info->map.write = ixp4xx_write16; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci err = mtd_device_parse_register(info->mtd, probes, &ppdata, 2368c2ecf20Sopenharmony_ci plat->parts, plat->nr_parts); 2378c2ecf20Sopenharmony_ci if (err) { 2388c2ecf20Sopenharmony_ci printk(KERN_ERR "Could not parse partitions\n"); 2398c2ecf20Sopenharmony_ci goto Error; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciError: 2458c2ecf20Sopenharmony_ci ixp4xx_flash_remove(dev); 2468c2ecf20Sopenharmony_ci return err; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct platform_driver ixp4xx_flash_driver = { 2508c2ecf20Sopenharmony_ci .probe = ixp4xx_flash_probe, 2518c2ecf20Sopenharmony_ci .remove = ixp4xx_flash_remove, 2528c2ecf20Sopenharmony_ci .driver = { 2538c2ecf20Sopenharmony_ci .name = "IXP4XX-Flash", 2548c2ecf20Sopenharmony_ci }, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cimodule_platform_driver(ixp4xx_flash_driver); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems"); 2618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Deepak Saxena"); 2628c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:IXP4XX-Flash"); 263