1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Cobalt NOR flash functions |
4 | * |
5 | * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. |
6 | * All rights reserved. |
7 | */ |
8 | |
9 | #include <linux/mtd/mtd.h> |
10 | #include <linux/mtd/map.h> |
11 | #include <linux/mtd/cfi.h> |
12 | #include <linux/time.h> |
13 | |
14 | #include "cobalt-flash.h" |
15 | |
16 | #define ADRS(offset) (COBALT_BUS_FLASH_BASE + offset) |
17 | |
18 | static struct map_info cobalt_flash_map = { |
19 | .name = "cobalt-flash" , |
20 | .bankwidth = 2, /* 16 bits */ |
21 | .size = 0x4000000, /* 64MB */ |
22 | .phys = 0, /* offset */ |
23 | }; |
24 | |
25 | static map_word flash_read16(struct map_info *map, unsigned long offset) |
26 | { |
27 | map_word r; |
28 | |
29 | r.x[0] = cobalt_bus_read32(bar1: map->virt, ADRS(offset)); |
30 | if (offset & 0x2) |
31 | r.x[0] >>= 16; |
32 | else |
33 | r.x[0] &= 0x0000ffff; |
34 | |
35 | return r; |
36 | } |
37 | |
38 | static void flash_write16(struct map_info *map, const map_word datum, |
39 | unsigned long offset) |
40 | { |
41 | u16 data = (u16)datum.x[0]; |
42 | |
43 | cobalt_bus_write16(bar1: map->virt, ADRS(offset), data); |
44 | } |
45 | |
46 | static void flash_copy_from(struct map_info *map, void *to, |
47 | unsigned long from, ssize_t len) |
48 | { |
49 | u32 src = from; |
50 | u8 *dest = to; |
51 | u32 data; |
52 | |
53 | while (len) { |
54 | data = cobalt_bus_read32(bar1: map->virt, ADRS(src)); |
55 | do { |
56 | *dest = data >> (8 * (src & 3)); |
57 | src++; |
58 | dest++; |
59 | len--; |
60 | } while (len && (src % 4)); |
61 | } |
62 | } |
63 | |
64 | static void flash_copy_to(struct map_info *map, unsigned long to, |
65 | const void *from, ssize_t len) |
66 | { |
67 | const u8 *src = from; |
68 | u32 dest = to; |
69 | |
70 | pr_info("%s: offset 0x%x: length %zu\n" , __func__, dest, len); |
71 | while (len) { |
72 | u16 data; |
73 | |
74 | do { |
75 | data = *src << (8 * (dest & 1)); |
76 | src++; |
77 | dest++; |
78 | len--; |
79 | } while (len && (dest % 2)); |
80 | |
81 | cobalt_bus_write16(bar1: map->virt, ADRS(dest - 2), data); |
82 | } |
83 | } |
84 | |
85 | int cobalt_flash_probe(struct cobalt *cobalt) |
86 | { |
87 | struct map_info *map = &cobalt_flash_map; |
88 | struct mtd_info *mtd; |
89 | |
90 | BUG_ON(!map_bankwidth_supported(map->bankwidth)); |
91 | map->virt = cobalt->bar1; |
92 | map->read = flash_read16; |
93 | map->write = flash_write16; |
94 | map->copy_from = flash_copy_from; |
95 | map->copy_to = flash_copy_to; |
96 | |
97 | mtd = do_map_probe(name: "cfi_probe" , map); |
98 | cobalt->mtd = mtd; |
99 | if (!mtd) { |
100 | cobalt_err("Probe CFI flash failed!\n" ); |
101 | return -1; |
102 | } |
103 | |
104 | mtd->owner = THIS_MODULE; |
105 | mtd->dev.parent = &cobalt->pci_dev->dev; |
106 | mtd_device_register(mtd, NULL, 0); |
107 | return 0; |
108 | } |
109 | |
110 | void cobalt_flash_remove(struct cobalt *cobalt) |
111 | { |
112 | if (cobalt->mtd) { |
113 | mtd_device_unregister(master: cobalt->mtd); |
114 | map_destroy(mtd: cobalt->mtd); |
115 | } |
116 | } |
117 | |