1b3e79e76SSerge Semin // SPDX-License-Identifier: GPL-2.0-only 2b3e79e76SSerge Semin /* 3b3e79e76SSerge Semin * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4b3e79e76SSerge Semin * 5b3e79e76SSerge Semin * Authors: 6b3e79e76SSerge Semin * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7b3e79e76SSerge Semin * 8b3e79e76SSerge Semin * Baikal-T1 Physically Mapped Internal ROM driver 9b3e79e76SSerge Semin */ 10b3e79e76SSerge Semin #include <linux/bits.h> 11b3e79e76SSerge Semin #include <linux/device.h> 12b3e79e76SSerge Semin #include <linux/kernel.h> 13b3e79e76SSerge Semin #include <linux/mtd/map.h> 14b3e79e76SSerge Semin #include <linux/mtd/xip.h> 15b3e79e76SSerge Semin #include <linux/mux/consumer.h> 16b3e79e76SSerge Semin #include <linux/of.h> 17b3e79e76SSerge Semin #include <linux/of_device.h> 18b3e79e76SSerge Semin #include <linux/platform_device.h> 19b3e79e76SSerge Semin #include <linux/string.h> 20b3e79e76SSerge Semin #include <linux/types.h> 21b3e79e76SSerge Semin 22b3e79e76SSerge Semin #include "physmap-bt1-rom.h" 23b3e79e76SSerge Semin 24b3e79e76SSerge Semin /* 25b3e79e76SSerge Semin * Baikal-T1 SoC ROMs are only accessible by the dword-aligned instructions. 26b3e79e76SSerge Semin * We have to take this into account when implementing the data read-methods. 27b3e79e76SSerge Semin * Note there is no need in bothering with endianness, since both Baikal-T1 28b3e79e76SSerge Semin * CPU and MMIO are LE. 29b3e79e76SSerge Semin */ 30b3e79e76SSerge Semin static map_word __xipram bt1_rom_map_read(struct map_info *map, 31b3e79e76SSerge Semin unsigned long ofs) 32b3e79e76SSerge Semin { 33b3e79e76SSerge Semin void __iomem *src = map->virt + ofs; 3418b9c940SSerge Semin unsigned int shift; 35b3e79e76SSerge Semin map_word ret; 36b3e79e76SSerge Semin u32 data; 37b3e79e76SSerge Semin 38b3e79e76SSerge Semin /* Read data within offset dword. */ 3918b9c940SSerge Semin shift = (uintptr_t)src & 0x3; 40b3e79e76SSerge Semin data = readl_relaxed(src - shift); 41b3e79e76SSerge Semin if (!shift) { 42b3e79e76SSerge Semin ret.x[0] = data; 43b3e79e76SSerge Semin return ret; 44b3e79e76SSerge Semin } 45b3e79e76SSerge Semin ret.x[0] = data >> (shift * BITS_PER_BYTE); 46b3e79e76SSerge Semin 47b3e79e76SSerge Semin /* Read data from the next dword. */ 48b3e79e76SSerge Semin shift = 4 - shift; 49b3e79e76SSerge Semin if (ofs + shift >= map->size) 50b3e79e76SSerge Semin return ret; 51b3e79e76SSerge Semin 52b3e79e76SSerge Semin data = readl_relaxed(src + shift); 53b3e79e76SSerge Semin ret.x[0] |= data << (shift * BITS_PER_BYTE); 54b3e79e76SSerge Semin 55b3e79e76SSerge Semin return ret; 56b3e79e76SSerge Semin } 57b3e79e76SSerge Semin 58b3e79e76SSerge Semin static void __xipram bt1_rom_map_copy_from(struct map_info *map, 59b3e79e76SSerge Semin void *to, unsigned long from, 60b3e79e76SSerge Semin ssize_t len) 61b3e79e76SSerge Semin { 62b3e79e76SSerge Semin void __iomem *src = map->virt + from; 6318b9c940SSerge Semin unsigned int shift, chunk; 64b3e79e76SSerge Semin u32 data; 65b3e79e76SSerge Semin 66b3e79e76SSerge Semin if (len <= 0 || from >= map->size) 67b3e79e76SSerge Semin return; 68b3e79e76SSerge Semin 69b3e79e76SSerge Semin /* Make sure we don't go over the map limit. */ 70b3e79e76SSerge Semin len = min_t(ssize_t, map->size - from, len); 71b3e79e76SSerge Semin 72b3e79e76SSerge Semin /* 73b3e79e76SSerge Semin * Since requested data size can be pretty big we have to implement 74b3e79e76SSerge Semin * the copy procedure as optimal as possible. That's why it's split 75b3e79e76SSerge Semin * up into the next three stages: unaligned head, aligned body, 76b3e79e76SSerge Semin * unaligned tail. 77b3e79e76SSerge Semin */ 7818b9c940SSerge Semin shift = (uintptr_t)src & 0x3; 79b3e79e76SSerge Semin if (shift) { 80b3e79e76SSerge Semin chunk = min_t(ssize_t, 4 - shift, len); 81b3e79e76SSerge Semin data = readl_relaxed(src - shift); 82*68331399SGustavo A. R. Silva memcpy(to, (char *)&data + shift, chunk); 83b3e79e76SSerge Semin src += chunk; 84b3e79e76SSerge Semin to += chunk; 85b3e79e76SSerge Semin len -= chunk; 86b3e79e76SSerge Semin } 87b3e79e76SSerge Semin 88b3e79e76SSerge Semin while (len >= 4) { 89b3e79e76SSerge Semin data = readl_relaxed(src); 90b3e79e76SSerge Semin memcpy(to, &data, 4); 91b3e79e76SSerge Semin src += 4; 92b3e79e76SSerge Semin to += 4; 93b3e79e76SSerge Semin len -= 4; 94b3e79e76SSerge Semin } 95b3e79e76SSerge Semin 96b3e79e76SSerge Semin if (len) { 97b3e79e76SSerge Semin data = readl_relaxed(src); 98b3e79e76SSerge Semin memcpy(to, &data, len); 99b3e79e76SSerge Semin } 100b3e79e76SSerge Semin } 101b3e79e76SSerge Semin 102b3e79e76SSerge Semin int of_flash_probe_bt1_rom(struct platform_device *pdev, 103b3e79e76SSerge Semin struct device_node *np, 104b3e79e76SSerge Semin struct map_info *map) 105b3e79e76SSerge Semin { 106b3e79e76SSerge Semin struct device *dev = &pdev->dev; 107b3e79e76SSerge Semin 108b3e79e76SSerge Semin /* It's supposed to be read-only MTD. */ 109b3e79e76SSerge Semin if (!of_device_is_compatible(np, "mtd-rom")) { 110b3e79e76SSerge Semin dev_info(dev, "No mtd-rom compatible string\n"); 111b3e79e76SSerge Semin return 0; 112b3e79e76SSerge Semin } 113b3e79e76SSerge Semin 114b3e79e76SSerge Semin /* Multiplatform guard. */ 115b3e79e76SSerge Semin if (!of_device_is_compatible(np, "baikal,bt1-int-rom")) 116b3e79e76SSerge Semin return 0; 117b3e79e76SSerge Semin 118b3e79e76SSerge Semin /* Sanity check the device parameters retrieved from DTB. */ 119b3e79e76SSerge Semin if (map->bankwidth != 4) 120b3e79e76SSerge Semin dev_warn(dev, "Bank width is supposed to be 32 bits wide\n"); 121b3e79e76SSerge Semin 122b3e79e76SSerge Semin map->read = bt1_rom_map_read; 123b3e79e76SSerge Semin map->copy_from = bt1_rom_map_copy_from; 124b3e79e76SSerge Semin 125b3e79e76SSerge Semin return 0; 126b3e79e76SSerge Semin } 127