1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2015 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <errno.h> 10 #include <linux/libfdt.h> 11 #include <malloc.h> 12 #include <mapmem.h> 13 #include <regmap.h> 14 #include <asm/io.h> 15 #include <dm/of_addr.h> 16 #include <linux/ioport.h> 17 18 DECLARE_GLOBAL_DATA_PTR; 19 20 static struct regmap *regmap_alloc(int count) 21 { 22 struct regmap *map; 23 24 map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count); 25 if (!map) 26 return NULL; 27 map->range_count = count; 28 29 return map; 30 } 31 32 #if CONFIG_IS_ENABLED(OF_PLATDATA) 33 int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count, 34 struct regmap **mapp) 35 { 36 struct regmap_range *range; 37 struct regmap *map; 38 39 map = regmap_alloc(count); 40 if (!map) 41 return -ENOMEM; 42 43 for (range = map->ranges; count > 0; reg += 2, range++, count--) { 44 range->start = *reg; 45 range->size = reg[1]; 46 } 47 48 *mapp = map; 49 50 return 0; 51 } 52 #else 53 int regmap_init_mem(ofnode node, struct regmap **mapp) 54 { 55 struct regmap_range *range; 56 struct regmap *map; 57 int count; 58 int addr_len, size_len, both_len; 59 int len; 60 int index; 61 struct resource r; 62 63 addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); 64 size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node)); 65 both_len = addr_len + size_len; 66 67 len = ofnode_read_size(node, "reg"); 68 if (len < 0) 69 return len; 70 len /= sizeof(fdt32_t); 71 count = len / both_len; 72 if (!count) 73 return -EINVAL; 74 75 map = regmap_alloc(count); 76 if (!map) 77 return -ENOMEM; 78 79 for (range = map->ranges, index = 0; count > 0; 80 count--, range++, index++) { 81 fdt_size_t sz; 82 if (of_live_active()) { 83 of_address_to_resource(ofnode_to_np(node), index, &r); 84 range->start = r.start; 85 range->size = r.end - r.start + 1; 86 } else { 87 range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, 88 ofnode_to_offset(node), "reg", index, 89 addr_len, size_len, &sz, true); 90 range->size = sz; 91 } 92 } 93 94 *mapp = map; 95 96 return 0; 97 } 98 #endif 99 100 void *regmap_get_range(struct regmap *map, unsigned int range_num) 101 { 102 struct regmap_range *range; 103 104 if (range_num >= map->range_count) 105 return NULL; 106 range = &map->ranges[range_num]; 107 108 return map_sysmem(range->start, range->size); 109 } 110 111 int regmap_uninit(struct regmap *map) 112 { 113 free(map); 114 115 return 0; 116 } 117 118 int regmap_read(struct regmap *map, uint offset, uint *valp) 119 { 120 u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE); 121 122 *valp = le32_to_cpu(readl(ptr)); 123 124 return 0; 125 } 126 127 int regmap_write(struct regmap *map, uint offset, uint val) 128 { 129 u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE); 130 131 writel(cpu_to_le32(val), ptr); 132 133 return 0; 134 } 135 136 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) 137 { 138 uint reg; 139 int ret; 140 141 ret = regmap_read(map, offset, ®); 142 if (ret) 143 return ret; 144 145 reg &= ~mask; 146 147 return regmap_write(map, offset, reg | val); 148 } 149