]>
Commit | Line | Data |
---|---|---|
04c37578 MA |
1 | /* |
2 | * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com> | |
3 | * Mansoor Ahamed <mansoor.ahamed@ti.com> | |
4 | * | |
5 | * BCH Error Location Module (ELM) support. | |
6 | * | |
7 | * NOTE: | |
8 | * 1. Supports only continuous mode. Dont see need for page mode in uboot | |
9 | * 2. Supports only syndrome polynomial 0. i.e. poly local variable is | |
10 | * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial | |
11 | * sets in uboot | |
12 | * | |
1a459660 | 13 | * SPDX-License-Identifier: GPL-2.0+ |
04c37578 MA |
14 | */ |
15 | ||
16 | #include <common.h> | |
17 | #include <asm/io.h> | |
18 | #include <asm/errno.h> | |
19 | #include <asm/arch/cpu.h> | |
5bf299bc | 20 | #include <asm/omap_gpmc.h> |
04c37578 MA |
21 | #include <asm/arch/elm.h> |
22 | ||
23 | #define ELM_DEFAULT_POLY (0) | |
24 | ||
25 | struct elm *elm_cfg; | |
26 | ||
27 | /** | |
28 | * elm_load_syndromes - Load BCH syndromes based on nibble selection | |
29 | * @syndrome: BCH syndrome | |
30 | * @nibbles: | |
31 | * @poly: Syndrome Polynomial set to use | |
32 | * | |
33 | * Load BCH syndromes based on nibble selection | |
34 | */ | |
35 | static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly) | |
36 | { | |
37 | u32 *ptr; | |
38 | u32 val; | |
39 | ||
40 | /* reg 0 */ | |
41 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; | |
42 | val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | | |
43 | (syndrome[3] << 24); | |
44 | writel(val, ptr); | |
45 | /* reg 1 */ | |
46 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; | |
47 | val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | | |
48 | (syndrome[7] << 24); | |
49 | writel(val, ptr); | |
50 | ||
51 | /* BCH 8-bit with 26 nibbles (4*8=32) */ | |
52 | if (nibbles > 13) { | |
53 | /* reg 2 */ | |
54 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; | |
55 | val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | | |
56 | (syndrome[11] << 24); | |
57 | writel(val, ptr); | |
58 | /* reg 3 */ | |
59 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; | |
60 | val = syndrome[12] | (syndrome[13] << 8) | | |
61 | (syndrome[14] << 16) | (syndrome[15] << 24); | |
62 | writel(val, ptr); | |
63 | } | |
64 | ||
65 | /* BCH 16-bit with 52 nibbles (7*8=56) */ | |
66 | if (nibbles > 26) { | |
67 | /* reg 4 */ | |
68 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; | |
69 | val = syndrome[16] | (syndrome[17] << 8) | | |
70 | (syndrome[18] << 16) | (syndrome[19] << 24); | |
71 | writel(val, ptr); | |
72 | ||
73 | /* reg 5 */ | |
74 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; | |
75 | val = syndrome[20] | (syndrome[21] << 8) | | |
76 | (syndrome[22] << 16) | (syndrome[23] << 24); | |
77 | writel(val, ptr); | |
78 | ||
79 | /* reg 6 */ | |
80 | ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; | |
81 | val = syndrome[24] | (syndrome[25] << 8) | | |
82 | (syndrome[26] << 16) | (syndrome[27] << 24); | |
83 | writel(val, ptr); | |
84 | } | |
85 | } | |
86 | ||
87 | /** | |
88 | * elm_check_errors - Check for BCH errors and return error locations | |
89 | * @syndrome: BCH syndrome | |
90 | * @nibbles: | |
91 | * @error_count: Returns number of errrors in the syndrome | |
92 | * @error_locations: Returns error locations (in decimal) in this array | |
93 | * | |
94 | * Check the provided syndrome for BCH errors and return error count | |
95 | * and locations in the array passed. Returns -1 if error is not correctable, | |
96 | * else returns 0 | |
97 | */ | |
98 | int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count, | |
99 | u32 *error_locations) | |
100 | { | |
101 | u8 poly = ELM_DEFAULT_POLY; | |
102 | s8 i; | |
103 | u32 location_status; | |
104 | ||
105 | elm_load_syndromes(syndrome, nibbles, poly); | |
106 | ||
107 | /* start processing */ | |
108 | writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) | |
109 | | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), | |
110 | &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); | |
111 | ||
112 | /* wait for processing to complete */ | |
113 | while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) | |
114 | ; | |
115 | /* clear status */ | |
116 | writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), | |
117 | &elm_cfg->irqstatus); | |
118 | ||
119 | /* check if correctable */ | |
120 | location_status = readl(&elm_cfg->error_location[poly].location_status); | |
121 | if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) | |
122 | return -1; | |
123 | ||
124 | /* get error count */ | |
125 | *error_count = readl(&elm_cfg->error_location[poly].location_status) & | |
126 | ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; | |
127 | ||
128 | for (i = 0; i < *error_count; i++) { | |
129 | error_locations[i] = | |
130 | readl(&elm_cfg->error_location[poly].error_location_x[i]); | |
131 | } | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | ||
137 | /** | |
138 | * elm_config - Configure ELM module | |
139 | * @level: 4 / 8 / 16 bit BCH | |
140 | * | |
141 | * Configure ELM module based on BCH level. | |
142 | * Set mode as continuous mode. | |
143 | * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. | |
144 | * Also, the mode is set only for syndrome 0 | |
145 | */ | |
146 | int elm_config(enum bch_level level) | |
147 | { | |
148 | u32 val; | |
149 | u8 poly = ELM_DEFAULT_POLY; | |
150 | u32 buffer_size = 0x7FF; | |
151 | ||
152 | /* config size and level */ | |
153 | val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; | |
154 | val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & | |
155 | ELM_LOCATION_CONFIG_ECC_SIZE_MASK); | |
156 | writel(val, &elm_cfg->location_config); | |
157 | ||
158 | /* config continous mode */ | |
159 | /* enable interrupt generation for syndrome polynomial set */ | |
160 | writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), | |
161 | &elm_cfg->irqenable); | |
162 | /* set continuous mode for the syndrome polynomial set */ | |
163 | writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), | |
164 | &elm_cfg->page_ctrl); | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | /** | |
170 | * elm_reset - Do a soft reset of ELM | |
171 | * | |
172 | * Perform a soft reset of ELM and return after reset is done. | |
173 | */ | |
174 | void elm_reset(void) | |
175 | { | |
176 | /* initiate reset */ | |
177 | writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), | |
178 | &elm_cfg->sysconfig); | |
179 | ||
180 | /* wait for reset complete and normal operation */ | |
181 | while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != | |
182 | ELM_SYSSTATUS_RESETDONE) | |
183 | ; | |
184 | } | |
185 | ||
186 | /** | |
187 | * elm_init - Initialize ELM module | |
188 | * | |
189 | * Initialize ELM support. Currently it does only base address init | |
190 | * and ELM reset. | |
191 | */ | |
192 | void elm_init(void) | |
193 | { | |
194 | elm_cfg = (struct elm *)ELM_BASE; | |
195 | elm_reset(); | |
196 | } |