162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cortina Systems Gemini OF physmap add-on 462306a36Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This SoC has an elaborate flash control register, so we need to 762306a36Sopenharmony_ci * detect and set it up when booting on this platform. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/mtd/map.h> 1262306a36Sopenharmony_ci#include <linux/mtd/xip.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/bitops.h> 1762306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1862306a36Sopenharmony_ci#include "physmap-gemini.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * The Flash-relevant parts of the global status register 2262306a36Sopenharmony_ci * These would also be relevant for a NAND driver. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define GLOBAL_STATUS 0x04 2562306a36Sopenharmony_ci#define FLASH_TYPE_MASK (0x3 << 24) 2662306a36Sopenharmony_ci#define FLASH_TYPE_NAND_2K (0x3 << 24) 2762306a36Sopenharmony_ci#define FLASH_TYPE_NAND_512 (0x2 << 24) 2862306a36Sopenharmony_ci#define FLASH_TYPE_PARALLEL (0x1 << 24) 2962306a36Sopenharmony_ci#define FLASH_TYPE_SERIAL (0x0 << 24) 3062306a36Sopenharmony_ci/* if parallel */ 3162306a36Sopenharmony_ci#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */ 3262306a36Sopenharmony_ci/* if serial */ 3362306a36Sopenharmony_ci#define FLASH_ATMEL (1 << 23) /* else STM */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define FLASH_SIZE_MASK (0x3 << 21) 3662306a36Sopenharmony_ci#define NAND_256M (0x3 << 21) /* and more */ 3762306a36Sopenharmony_ci#define NAND_128M (0x2 << 21) 3862306a36Sopenharmony_ci#define NAND_64M (0x1 << 21) 3962306a36Sopenharmony_ci#define NAND_32M (0x0 << 21) 4062306a36Sopenharmony_ci#define ATMEL_16M (0x3 << 21) /* and more */ 4162306a36Sopenharmony_ci#define ATMEL_8M (0x2 << 21) 4262306a36Sopenharmony_ci#define ATMEL_4M_2M (0x1 << 21) 4362306a36Sopenharmony_ci#define ATMEL_1M (0x0 << 21) /* and less */ 4462306a36Sopenharmony_ci#define STM_32M (1 << 22) /* and more */ 4562306a36Sopenharmony_ci#define STM_16M (0 << 22) /* and less */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct gemini_flash { 5062306a36Sopenharmony_ci struct device *dev; 5162306a36Sopenharmony_ci struct pinctrl *p; 5262306a36Sopenharmony_ci struct pinctrl_state *enabled_state; 5362306a36Sopenharmony_ci struct pinctrl_state *disabled_state; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Static local state */ 5762306a36Sopenharmony_cistatic struct gemini_flash *gf; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void gemini_flash_enable_pins(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (IS_ERR(gf->enabled_state)) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->enabled_state); 6662306a36Sopenharmony_ci if (ret) 6762306a36Sopenharmony_ci dev_err(gf->dev, "failed to enable pins\n"); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void gemini_flash_disable_pins(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int ret; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (IS_ERR(gf->disabled_state)) 7562306a36Sopenharmony_ci return; 7662306a36Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->disabled_state); 7762306a36Sopenharmony_ci if (ret) 7862306a36Sopenharmony_ci dev_err(gf->dev, "failed to disable pins\n"); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic map_word __xipram gemini_flash_map_read(struct map_info *map, 8262306a36Sopenharmony_ci unsigned long ofs) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci map_word ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci gemini_flash_enable_pins(); 8762306a36Sopenharmony_ci ret = inline_map_read(map, ofs); 8862306a36Sopenharmony_ci gemini_flash_disable_pins(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void __xipram gemini_flash_map_write(struct map_info *map, 9462306a36Sopenharmony_ci const map_word datum, 9562306a36Sopenharmony_ci unsigned long ofs) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci gemini_flash_enable_pins(); 9862306a36Sopenharmony_ci inline_map_write(map, datum, ofs); 9962306a36Sopenharmony_ci gemini_flash_disable_pins(); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void __xipram gemini_flash_map_copy_from(struct map_info *map, 10362306a36Sopenharmony_ci void *to, unsigned long from, 10462306a36Sopenharmony_ci ssize_t len) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci gemini_flash_enable_pins(); 10762306a36Sopenharmony_ci inline_map_copy_from(map, to, from, len); 10862306a36Sopenharmony_ci gemini_flash_disable_pins(); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void __xipram gemini_flash_map_copy_to(struct map_info *map, 11262306a36Sopenharmony_ci unsigned long to, 11362306a36Sopenharmony_ci const void *from, ssize_t len) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci gemini_flash_enable_pins(); 11662306a36Sopenharmony_ci inline_map_copy_to(map, to, from, len); 11762306a36Sopenharmony_ci gemini_flash_disable_pins(); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint of_flash_probe_gemini(struct platform_device *pdev, 12162306a36Sopenharmony_ci struct device_node *np, 12262306a36Sopenharmony_ci struct map_info *map) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct regmap *rmap; 12562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 12662306a36Sopenharmony_ci u32 val; 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Multiplatform guard */ 13062306a36Sopenharmony_ci if (!of_device_is_compatible(np, "cortina,gemini-flash")) 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!gf) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci gf->dev = dev; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci rmap = syscon_regmap_lookup_by_phandle(np, "syscon"); 13962306a36Sopenharmony_ci if (IS_ERR(rmap)) { 14062306a36Sopenharmony_ci dev_err(dev, "no syscon\n"); 14162306a36Sopenharmony_ci return PTR_ERR(rmap); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = regmap_read(rmap, GLOBAL_STATUS, &val); 14562306a36Sopenharmony_ci if (ret) { 14662306a36Sopenharmony_ci dev_err(dev, "failed to read global status register\n"); 14762306a36Sopenharmony_ci return -ENODEV; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci dev_dbg(dev, "global status reg: %08x\n", val); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * It would be contradictory if a physmap flash was NOT parallel. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) { 15562306a36Sopenharmony_ci dev_err(dev, "flash is not parallel\n"); 15662306a36Sopenharmony_ci return -ENODEV; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Complain if DT data and hardware definition is different. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if (val & FLASH_WIDTH_16BIT) { 16362306a36Sopenharmony_ci if (map->bankwidth != 2) 16462306a36Sopenharmony_ci dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n", 16562306a36Sopenharmony_ci map->bankwidth * 8); 16662306a36Sopenharmony_ci } else { 16762306a36Sopenharmony_ci if (map->bankwidth != 1) 16862306a36Sopenharmony_ci dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n", 16962306a36Sopenharmony_ci map->bankwidth * 8); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci gf->p = devm_pinctrl_get(dev); 17362306a36Sopenharmony_ci if (IS_ERR(gf->p)) { 17462306a36Sopenharmony_ci dev_err(dev, "no pinctrl handle\n"); 17562306a36Sopenharmony_ci ret = PTR_ERR(gf->p); 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci gf->enabled_state = pinctrl_lookup_state(gf->p, "enabled"); 18062306a36Sopenharmony_ci if (IS_ERR(gf->enabled_state)) 18162306a36Sopenharmony_ci dev_err(dev, "no enabled pin control state\n"); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled"); 18462306a36Sopenharmony_ci if (IS_ERR(gf->enabled_state)) { 18562306a36Sopenharmony_ci dev_err(dev, "no disabled pin control state\n"); 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci ret = pinctrl_select_state(gf->p, gf->disabled_state); 18862306a36Sopenharmony_ci if (ret) 18962306a36Sopenharmony_ci dev_err(gf->dev, "failed to disable pins\n"); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci map->read = gemini_flash_map_read; 19362306a36Sopenharmony_ci map->write = gemini_flash_map_write; 19462306a36Sopenharmony_ci map->copy_from = gemini_flash_map_copy_from; 19562306a36Sopenharmony_ci map->copy_to = gemini_flash_map_copy_to; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci dev_info(dev, "initialized Gemini-specific physmap control\n"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 201