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