18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel IXP4xx OF physmap add-on 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on the ixp4xx.c map driver, originally written by: 78c2ecf20Sopenharmony_ci * Intel Corporation 88c2ecf20Sopenharmony_ci * Deepak Saxena <dsaxena@mvista.com> 98c2ecf20Sopenharmony_ci * Copyright (C) 2002 Intel Corporation 108c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 MontaVista Software, Inc. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h> 178c2ecf20Sopenharmony_ci#include "physmap-ixp4xx.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Read/write a 16 bit word from flash address 'addr'. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * When the cpu is in little-endian mode it swizzles the address lines 238c2ecf20Sopenharmony_ci * ('address coherency') so we need to undo the swizzling to ensure commands 248c2ecf20Sopenharmony_ci * and the like end up on the correct flash address. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * To further complicate matters, due to the way the expansion bus controller 278c2ecf20Sopenharmony_ci * handles 32 bit reads, the byte stream ABCD is stored on the flash as: 288c2ecf20Sopenharmony_ci * D15 D0 298c2ecf20Sopenharmony_ci * +---+---+ 308c2ecf20Sopenharmony_ci * | A | B | 0 318c2ecf20Sopenharmony_ci * +---+---+ 328c2ecf20Sopenharmony_ci * | C | D | 2 338c2ecf20Sopenharmony_ci * +---+---+ 348c2ecf20Sopenharmony_ci * This means that on LE systems each 16 bit word must be swapped. Note that 358c2ecf20Sopenharmony_ci * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI 368c2ecf20Sopenharmony_ci * data and other flash commands which are always in D7-D0. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_BIG_ENDIAN 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline u16 flash_read16(void __iomem *addr) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2))); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic inline void flash_write16(u16 d, void __iomem *addr) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci __raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2)); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define BYTE0(h) ((h) & 0xFF) 518c2ecf20Sopenharmony_ci#define BYTE1(h) (((h) >> 8) & 0xFF) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#else 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline u16 flash_read16(const void __iomem *addr) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return __raw_readw(addr); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline void flash_write16(u16 d, void __iomem *addr) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci __raw_writew(d, addr); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define BYTE0(h) (((h) >> 8) & 0xFF) 668c2ecf20Sopenharmony_ci#define BYTE1(h) ((h) & 0xFF) 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci map_word val; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci val.x[0] = flash_read16(map->virt + ofs); 748c2ecf20Sopenharmony_ci return val; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * The IXP4xx expansion bus only allows 16-bit wide acceses 798c2ecf20Sopenharmony_ci * when attached to a 16-bit wide device (such as the 28F128J3A), 808c2ecf20Sopenharmony_ci * so we can't just memcpy_fromio(). 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic void ixp4xx_copy_from(struct map_info *map, void *to, 838c2ecf20Sopenharmony_ci unsigned long from, ssize_t len) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u8 *dest = (u8 *) to; 868c2ecf20Sopenharmony_ci void __iomem *src = map->virt + from; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (len <= 0) 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (from & 1) { 928c2ecf20Sopenharmony_ci *dest++ = BYTE1(flash_read16(src-1)); 938c2ecf20Sopenharmony_ci src++; 948c2ecf20Sopenharmony_ci --len; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci while (len >= 2) { 988c2ecf20Sopenharmony_ci u16 data = flash_read16(src); 998c2ecf20Sopenharmony_ci *dest++ = BYTE0(data); 1008c2ecf20Sopenharmony_ci *dest++ = BYTE1(data); 1018c2ecf20Sopenharmony_ci src += 2; 1028c2ecf20Sopenharmony_ci len -= 2; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (len > 0) 1068c2ecf20Sopenharmony_ci *dest++ = BYTE0(flash_read16(src)); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci flash_write16(d.x[0], map->virt + adr); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciint of_flash_probe_ixp4xx(struct platform_device *pdev, 1158c2ecf20Sopenharmony_ci struct device_node *np, 1168c2ecf20Sopenharmony_ci struct map_info *map) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Multiplatform guard */ 1218c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "intel,ixp4xx-flash")) 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci map->read = ixp4xx_read16; 1258c2ecf20Sopenharmony_ci map->write = ixp4xx_write16; 1268c2ecf20Sopenharmony_ci map->copy_from = ixp4xx_copy_from; 1278c2ecf20Sopenharmony_ci map->copy_to = NULL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dev_info(dev, "initialized Intel IXP4xx-specific physmap control\n"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 133