18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Cortina Systems Gemini OF physmap add-on 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This SoC has an elaborate flash control register, so we need to 78c2ecf20Sopenharmony_ci * detect and set it up when booting on this platform. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/bitops.h> 178c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 188c2ecf20Sopenharmony_ci#include "physmap-gemini.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * The Flash-relevant parts of the global status register 228c2ecf20Sopenharmony_ci * These would also be relevant for a NAND driver. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#define GLOBAL_STATUS 0x04 258c2ecf20Sopenharmony_ci#define FLASH_TYPE_MASK (0x3 << 24) 268c2ecf20Sopenharmony_ci#define FLASH_TYPE_NAND_2K (0x3 << 24) 278c2ecf20Sopenharmony_ci#define FLASH_TYPE_NAND_512 (0x2 << 24) 288c2ecf20Sopenharmony_ci#define FLASH_TYPE_PARALLEL (0x1 << 24) 298c2ecf20Sopenharmony_ci#define FLASH_TYPE_SERIAL (0x0 << 24) 308c2ecf20Sopenharmony_ci/* if parallel */ 318c2ecf20Sopenharmony_ci#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */ 328c2ecf20Sopenharmony_ci/* if serial */ 338c2ecf20Sopenharmony_ci#define FLASH_ATMEL (1 << 23) /* else STM */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define FLASH_SIZE_MASK (0x3 << 21) 368c2ecf20Sopenharmony_ci#define NAND_256M (0x3 << 21) /* and more */ 378c2ecf20Sopenharmony_ci#define NAND_128M (0x2 << 21) 388c2ecf20Sopenharmony_ci#define NAND_64M (0x1 << 21) 398c2ecf20Sopenharmony_ci#define NAND_32M (0x0 << 21) 408c2ecf20Sopenharmony_ci#define ATMEL_16M (0x3 << 21) /* and more */ 418c2ecf20Sopenharmony_ci#define ATMEL_8M (0x2 << 21) 428c2ecf20Sopenharmony_ci#define ATMEL_4M_2M (0x1 << 21) 438c2ecf20Sopenharmony_ci#define ATMEL_1M (0x0 << 21) /* and less */ 448c2ecf20Sopenharmony_ci#define STM_32M (1 << 22) /* and more */ 458c2ecf20Sopenharmony_ci#define STM_16M (0 << 22) /* and less */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct gemini_flash { 508c2ecf20Sopenharmony_ci struct device *dev; 518c2ecf20Sopenharmony_ci struct pinctrl *p; 528c2ecf20Sopenharmony_ci struct pinctrl_state *enabled_state; 538c2ecf20Sopenharmony_ci struct pinctrl_state *disabled_state; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Static local state */ 578c2ecf20Sopenharmony_cistatic struct gemini_flash *gf; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void gemini_flash_enable_pins(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (IS_ERR(gf->enabled_state)) 648c2ecf20Sopenharmony_ci return; 658c2ecf20Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->enabled_state); 668c2ecf20Sopenharmony_ci if (ret) 678c2ecf20Sopenharmony_ci dev_err(gf->dev, "failed to enable pins\n"); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void gemini_flash_disable_pins(void) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int ret; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (IS_ERR(gf->disabled_state)) 758c2ecf20Sopenharmony_ci return; 768c2ecf20Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->disabled_state); 778c2ecf20Sopenharmony_ci if (ret) 788c2ecf20Sopenharmony_ci dev_err(gf->dev, "failed to disable pins\n"); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic map_word __xipram gemini_flash_map_read(struct map_info *map, 828c2ecf20Sopenharmony_ci unsigned long ofs) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci map_word ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci gemini_flash_enable_pins(); 878c2ecf20Sopenharmony_ci ret = inline_map_read(map, ofs); 888c2ecf20Sopenharmony_ci gemini_flash_disable_pins(); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void __xipram gemini_flash_map_write(struct map_info *map, 948c2ecf20Sopenharmony_ci const map_word datum, 958c2ecf20Sopenharmony_ci unsigned long ofs) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci gemini_flash_enable_pins(); 988c2ecf20Sopenharmony_ci inline_map_write(map, datum, ofs); 998c2ecf20Sopenharmony_ci gemini_flash_disable_pins(); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void __xipram gemini_flash_map_copy_from(struct map_info *map, 1038c2ecf20Sopenharmony_ci void *to, unsigned long from, 1048c2ecf20Sopenharmony_ci ssize_t len) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci gemini_flash_enable_pins(); 1078c2ecf20Sopenharmony_ci inline_map_copy_from(map, to, from, len); 1088c2ecf20Sopenharmony_ci gemini_flash_disable_pins(); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void __xipram gemini_flash_map_copy_to(struct map_info *map, 1128c2ecf20Sopenharmony_ci unsigned long to, 1138c2ecf20Sopenharmony_ci const void *from, ssize_t len) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci gemini_flash_enable_pins(); 1168c2ecf20Sopenharmony_ci inline_map_copy_to(map, to, from, len); 1178c2ecf20Sopenharmony_ci gemini_flash_disable_pins(); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint of_flash_probe_gemini(struct platform_device *pdev, 1218c2ecf20Sopenharmony_ci struct device_node *np, 1228c2ecf20Sopenharmony_ci struct map_info *map) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct regmap *rmap; 1258c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1268c2ecf20Sopenharmony_ci u32 val; 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Multiplatform guard */ 1308c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "cortina,gemini-flash")) 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL); 1348c2ecf20Sopenharmony_ci if (!gf) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci gf->dev = dev; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rmap = syscon_regmap_lookup_by_phandle(np, "syscon"); 1398c2ecf20Sopenharmony_ci if (IS_ERR(rmap)) { 1408c2ecf20Sopenharmony_ci dev_err(dev, "no syscon\n"); 1418c2ecf20Sopenharmony_ci return PTR_ERR(rmap); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = regmap_read(rmap, GLOBAL_STATUS, &val); 1458c2ecf20Sopenharmony_ci if (ret) { 1468c2ecf20Sopenharmony_ci dev_err(dev, "failed to read global status register\n"); 1478c2ecf20Sopenharmony_ci return -ENODEV; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci dev_dbg(dev, "global status reg: %08x\n", val); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * It would be contradictory if a physmap flash was NOT parallel. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) { 1558c2ecf20Sopenharmony_ci dev_err(dev, "flash is not parallel\n"); 1568c2ecf20Sopenharmony_ci return -ENODEV; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Complain if DT data and hardware definition is different. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci if (val & FLASH_WIDTH_16BIT) { 1638c2ecf20Sopenharmony_ci if (map->bankwidth != 2) 1648c2ecf20Sopenharmony_ci dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n", 1658c2ecf20Sopenharmony_ci map->bankwidth * 8); 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci if (map->bankwidth != 1) 1688c2ecf20Sopenharmony_ci dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n", 1698c2ecf20Sopenharmony_ci map->bankwidth * 8); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci gf->p = devm_pinctrl_get(dev); 1738c2ecf20Sopenharmony_ci if (IS_ERR(gf->p)) { 1748c2ecf20Sopenharmony_ci dev_err(dev, "no pinctrl handle\n"); 1758c2ecf20Sopenharmony_ci ret = PTR_ERR(gf->p); 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci gf->enabled_state = pinctrl_lookup_state(gf->p, "enabled"); 1808c2ecf20Sopenharmony_ci if (IS_ERR(gf->enabled_state)) 1818c2ecf20Sopenharmony_ci dev_err(dev, "no enabled pin control state\n"); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled"); 1848c2ecf20Sopenharmony_ci if (IS_ERR(gf->enabled_state)) { 1858c2ecf20Sopenharmony_ci dev_err(dev, "no disabled pin control state\n"); 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->disabled_state); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci dev_err(gf->dev, "failed to disable pins\n"); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci map->read = gemini_flash_map_read; 1938c2ecf20Sopenharmony_ci map->write = gemini_flash_map_write; 1948c2ecf20Sopenharmony_ci map->copy_from = gemini_flash_map_copy_from; 1958c2ecf20Sopenharmony_ci map->copy_to = gemini_flash_map_copy_to; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci dev_info(dev, "initialized Gemini-specific physmap control\n"); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 201