162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Serge Semin <Sergey.Semin@baikalelectronics.ru> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Baikal-T1 Physically Mapped Internal ROM driver 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/bits.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/mtd/map.h> 1462306a36Sopenharmony_ci#include <linux/mtd/xip.h> 1562306a36Sopenharmony_ci#include <linux/mux/consumer.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "physmap-bt1-rom.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Baikal-T1 SoC ROMs are only accessible by the dword-aligned instructions. 2562306a36Sopenharmony_ci * We have to take this into account when implementing the data read-methods. 2662306a36Sopenharmony_ci * Note there is no need in bothering with endianness, since both Baikal-T1 2762306a36Sopenharmony_ci * CPU and MMIO are LE. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic map_word __xipram bt1_rom_map_read(struct map_info *map, 3062306a36Sopenharmony_ci unsigned long ofs) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci void __iomem *src = map->virt + ofs; 3362306a36Sopenharmony_ci unsigned int shift; 3462306a36Sopenharmony_ci map_word ret; 3562306a36Sopenharmony_ci u32 data; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Read data within offset dword. */ 3862306a36Sopenharmony_ci shift = (uintptr_t)src & 0x3; 3962306a36Sopenharmony_ci data = readl_relaxed(src - shift); 4062306a36Sopenharmony_ci if (!shift) { 4162306a36Sopenharmony_ci ret.x[0] = data; 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci ret.x[0] = data >> (shift * BITS_PER_BYTE); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Read data from the next dword. */ 4762306a36Sopenharmony_ci shift = 4 - shift; 4862306a36Sopenharmony_ci if (ofs + shift >= map->size) 4962306a36Sopenharmony_ci return ret; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci data = readl_relaxed(src + shift); 5262306a36Sopenharmony_ci ret.x[0] |= data << (shift * BITS_PER_BYTE); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void __xipram bt1_rom_map_copy_from(struct map_info *map, 5862306a36Sopenharmony_ci void *to, unsigned long from, 5962306a36Sopenharmony_ci ssize_t len) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci void __iomem *src = map->virt + from; 6262306a36Sopenharmony_ci unsigned int shift, chunk; 6362306a36Sopenharmony_ci u32 data; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (len <= 0 || from >= map->size) 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Make sure we don't go over the map limit. */ 6962306a36Sopenharmony_ci len = min_t(ssize_t, map->size - from, len); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Since requested data size can be pretty big we have to implement 7362306a36Sopenharmony_ci * the copy procedure as optimal as possible. That's why it's split 7462306a36Sopenharmony_ci * up into the next three stages: unaligned head, aligned body, 7562306a36Sopenharmony_ci * unaligned tail. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci shift = (uintptr_t)src & 0x3; 7862306a36Sopenharmony_ci if (shift) { 7962306a36Sopenharmony_ci chunk = min_t(ssize_t, 4 - shift, len); 8062306a36Sopenharmony_ci data = readl_relaxed(src - shift); 8162306a36Sopenharmony_ci memcpy(to, (char *)&data + shift, chunk); 8262306a36Sopenharmony_ci src += chunk; 8362306a36Sopenharmony_ci to += chunk; 8462306a36Sopenharmony_ci len -= chunk; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci while (len >= 4) { 8862306a36Sopenharmony_ci data = readl_relaxed(src); 8962306a36Sopenharmony_ci memcpy(to, &data, 4); 9062306a36Sopenharmony_ci src += 4; 9162306a36Sopenharmony_ci to += 4; 9262306a36Sopenharmony_ci len -= 4; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (len) { 9662306a36Sopenharmony_ci data = readl_relaxed(src); 9762306a36Sopenharmony_ci memcpy(to, &data, len); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciint of_flash_probe_bt1_rom(struct platform_device *pdev, 10262306a36Sopenharmony_ci struct device_node *np, 10362306a36Sopenharmony_ci struct map_info *map) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* It's supposed to be read-only MTD. */ 10862306a36Sopenharmony_ci if (!of_device_is_compatible(np, "mtd-rom")) { 10962306a36Sopenharmony_ci dev_info(dev, "No mtd-rom compatible string\n"); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Multiplatform guard. */ 11462306a36Sopenharmony_ci if (!of_device_is_compatible(np, "baikal,bt1-int-rom")) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Sanity check the device parameters retrieved from DTB. */ 11862306a36Sopenharmony_ci if (map->bankwidth != 4) 11962306a36Sopenharmony_ci dev_warn(dev, "Bank width is supposed to be 32 bits wide\n"); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci map->read = bt1_rom_map_read; 12262306a36Sopenharmony_ci map->copy_from = bt1_rom_map_copy_from; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 126