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