18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Error Location Module 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define DRIVER_NAME "omap-elm" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_data/elm.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define ELM_SYSCONFIG 0x010 208c2ecf20Sopenharmony_ci#define ELM_IRQSTATUS 0x018 218c2ecf20Sopenharmony_ci#define ELM_IRQENABLE 0x01c 228c2ecf20Sopenharmony_ci#define ELM_LOCATION_CONFIG 0x020 238c2ecf20Sopenharmony_ci#define ELM_PAGE_CTRL 0x080 248c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_0 0x400 258c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_1 0x404 268c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_2 0x408 278c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_3 0x40c 288c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_4 0x410 298c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_5 0x414 308c2ecf20Sopenharmony_ci#define ELM_SYNDROME_FRAGMENT_6 0x418 318c2ecf20Sopenharmony_ci#define ELM_LOCATION_STATUS 0x800 328c2ecf20Sopenharmony_ci#define ELM_ERROR_LOCATION_0 0x880 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* ELM Interrupt Status Register */ 358c2ecf20Sopenharmony_ci#define INTR_STATUS_PAGE_VALID BIT(8) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* ELM Interrupt Enable Register */ 388c2ecf20Sopenharmony_ci#define INTR_EN_PAGE_MASK BIT(8) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* ELM Location Configuration Register */ 418c2ecf20Sopenharmony_ci#define ECC_BCH_LEVEL_MASK 0x3 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* ELM syndrome */ 448c2ecf20Sopenharmony_ci#define ELM_SYNDROME_VALID BIT(16) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* ELM_LOCATION_STATUS Register */ 478c2ecf20Sopenharmony_ci#define ECC_CORRECTABLE_MASK BIT(8) 488c2ecf20Sopenharmony_ci#define ECC_NB_ERRORS_MASK 0x1f 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* ELM_ERROR_LOCATION_0-15 Registers */ 518c2ecf20Sopenharmony_ci#define ECC_ERROR_LOCATION_MASK 0x1fff 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ELM_ECC_SIZE 0x7ff 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define SYNDROME_FRAGMENT_REG_SIZE 0x40 568c2ecf20Sopenharmony_ci#define ERROR_LOCATION_SIZE 0x100 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct elm_registers { 598c2ecf20Sopenharmony_ci u32 elm_irqenable; 608c2ecf20Sopenharmony_ci u32 elm_sysconfig; 618c2ecf20Sopenharmony_ci u32 elm_location_config; 628c2ecf20Sopenharmony_ci u32 elm_page_ctrl; 638c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; 648c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; 658c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; 668c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; 678c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; 688c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; 698c2ecf20Sopenharmony_ci u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct elm_info { 738c2ecf20Sopenharmony_ci struct device *dev; 748c2ecf20Sopenharmony_ci void __iomem *elm_base; 758c2ecf20Sopenharmony_ci struct completion elm_completion; 768c2ecf20Sopenharmony_ci struct list_head list; 778c2ecf20Sopenharmony_ci enum bch_ecc bch_type; 788c2ecf20Sopenharmony_ci struct elm_registers elm_regs; 798c2ecf20Sopenharmony_ci int ecc_steps; 808c2ecf20Sopenharmony_ci int ecc_syndrome_size; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic LIST_HEAD(elm_devices); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void elm_write_reg(struct elm_info *info, int offset, u32 val) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci writel(val, info->elm_base + offset); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic u32 elm_read_reg(struct elm_info *info, int offset) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return readl(info->elm_base + offset); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * elm_config - Configure ELM module 978c2ecf20Sopenharmony_ci * @dev: ELM device 988c2ecf20Sopenharmony_ci * @bch_type: Type of BCH ecc 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ciint elm_config(struct device *dev, enum bch_ecc bch_type, 1018c2ecf20Sopenharmony_ci int ecc_steps, int ecc_step_size, int ecc_syndrome_size) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u32 reg_val; 1048c2ecf20Sopenharmony_ci struct elm_info *info = dev_get_drvdata(dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!info) { 1078c2ecf20Sopenharmony_ci dev_err(dev, "Unable to configure elm - device not probed?\n"); 1088c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci /* ELM cannot detect ECC errors for chunks > 1KB */ 1118c2ecf20Sopenharmony_ci if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { 1128c2ecf20Sopenharmony_ci dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci /* ELM support 8 error syndrome process */ 1168c2ecf20Sopenharmony_ci if (ecc_steps > ERROR_VECTOR_MAX) { 1178c2ecf20Sopenharmony_ci dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); 1228c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); 1238c2ecf20Sopenharmony_ci info->bch_type = bch_type; 1248c2ecf20Sopenharmony_ci info->ecc_steps = ecc_steps; 1258c2ecf20Sopenharmony_ci info->ecc_syndrome_size = ecc_syndrome_size; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(elm_config); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * elm_configure_page_mode - Enable/Disable page mode 1338c2ecf20Sopenharmony_ci * @info: elm info 1348c2ecf20Sopenharmony_ci * @index: index number of syndrome fragment vector 1358c2ecf20Sopenharmony_ci * @enable: enable/disable flag for page mode 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Enable page mode for syndrome fragment index 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic void elm_configure_page_mode(struct elm_info *info, int index, 1408c2ecf20Sopenharmony_ci bool enable) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci u32 reg_val; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, ELM_PAGE_CTRL); 1458c2ecf20Sopenharmony_ci if (enable) 1468c2ecf20Sopenharmony_ci reg_val |= BIT(index); /* enable page mode */ 1478c2ecf20Sopenharmony_ci else 1488c2ecf20Sopenharmony_ci reg_val &= ~BIT(index); /* disable page mode */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_PAGE_CTRL, reg_val); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * elm_load_syndrome - Load ELM syndrome reg 1558c2ecf20Sopenharmony_ci * @info: elm info 1568c2ecf20Sopenharmony_ci * @err_vec: elm error vectors 1578c2ecf20Sopenharmony_ci * @ecc: buffer with calculated ecc 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Load syndrome fragment registers with calculated ecc in reverse order. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic void elm_load_syndrome(struct elm_info *info, 1628c2ecf20Sopenharmony_ci struct elm_errorvec *err_vec, u8 *ecc) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i, offset; 1658c2ecf20Sopenharmony_ci u32 val; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (i = 0; i < info->ecc_steps; i++) { 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* Check error reported */ 1708c2ecf20Sopenharmony_ci if (err_vec[i].error_reported) { 1718c2ecf20Sopenharmony_ci elm_configure_page_mode(info, i, true); 1728c2ecf20Sopenharmony_ci offset = ELM_SYNDROME_FRAGMENT_0 + 1738c2ecf20Sopenharmony_ci SYNDROME_FRAGMENT_REG_SIZE * i; 1748c2ecf20Sopenharmony_ci switch (info->bch_type) { 1758c2ecf20Sopenharmony_ci case BCH8_ECC: 1768c2ecf20Sopenharmony_ci /* syndrome fragment 0 = ecc[9-12B] */ 1778c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[9]); 1788c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* syndrome fragment 1 = ecc[5-8B] */ 1818c2ecf20Sopenharmony_ci offset += 4; 1828c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[5]); 1838c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* syndrome fragment 2 = ecc[1-4B] */ 1868c2ecf20Sopenharmony_ci offset += 4; 1878c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[1]); 1888c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* syndrome fragment 3 = ecc[0B] */ 1918c2ecf20Sopenharmony_ci offset += 4; 1928c2ecf20Sopenharmony_ci val = ecc[0]; 1938c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci case BCH4_ECC: 1968c2ecf20Sopenharmony_ci /* syndrome fragment 0 = ecc[20-52b] bits */ 1978c2ecf20Sopenharmony_ci val = ((__force u32)cpu_to_be32(*(u32 *)&ecc[3]) >> 4) | 1988c2ecf20Sopenharmony_ci ((ecc[2] & 0xf) << 28); 1998c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* syndrome fragment 1 = ecc[0-20b] bits */ 2028c2ecf20Sopenharmony_ci offset += 4; 2038c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 12; 2048c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case BCH16_ECC: 2078c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[22]); 2088c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2098c2ecf20Sopenharmony_ci offset += 4; 2108c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[18]); 2118c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2128c2ecf20Sopenharmony_ci offset += 4; 2138c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[14]); 2148c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2158c2ecf20Sopenharmony_ci offset += 4; 2168c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[10]); 2178c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2188c2ecf20Sopenharmony_ci offset += 4; 2198c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[6]); 2208c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2218c2ecf20Sopenharmony_ci offset += 4; 2228c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[2]); 2238c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2248c2ecf20Sopenharmony_ci offset += 4; 2258c2ecf20Sopenharmony_ci val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 16; 2268c2ecf20Sopenharmony_ci elm_write_reg(info, offset, val); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci default: 2298c2ecf20Sopenharmony_ci pr_err("invalid config bch_type\n"); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Update ecc pointer with ecc byte size */ 2348c2ecf20Sopenharmony_ci ecc += info->ecc_syndrome_size; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/** 2398c2ecf20Sopenharmony_ci * elm_start_processing - start elm syndrome processing 2408c2ecf20Sopenharmony_ci * @info: elm info 2418c2ecf20Sopenharmony_ci * @err_vec: elm error vectors 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * Set syndrome valid bit for syndrome fragment registers for which 2448c2ecf20Sopenharmony_ci * elm syndrome fragment registers are loaded. This enables elm module 2458c2ecf20Sopenharmony_ci * to start processing syndrome vectors. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void elm_start_processing(struct elm_info *info, 2488c2ecf20Sopenharmony_ci struct elm_errorvec *err_vec) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int i, offset; 2518c2ecf20Sopenharmony_ci u32 reg_val; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * Set syndrome vector valid, so that ELM module 2558c2ecf20Sopenharmony_ci * will process it for vectors error is reported 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci for (i = 0; i < info->ecc_steps; i++) { 2588c2ecf20Sopenharmony_ci if (err_vec[i].error_reported) { 2598c2ecf20Sopenharmony_ci offset = ELM_SYNDROME_FRAGMENT_6 + 2608c2ecf20Sopenharmony_ci SYNDROME_FRAGMENT_REG_SIZE * i; 2618c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, offset); 2628c2ecf20Sopenharmony_ci reg_val |= ELM_SYNDROME_VALID; 2638c2ecf20Sopenharmony_ci elm_write_reg(info, offset, reg_val); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * elm_error_correction - locate correctable error position 2708c2ecf20Sopenharmony_ci * @info: elm info 2718c2ecf20Sopenharmony_ci * @err_vec: elm error vectors 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * On completion of processing by elm module, error location status 2748c2ecf20Sopenharmony_ci * register updated with correctable/uncorrectable error information. 2758c2ecf20Sopenharmony_ci * In case of correctable errors, number of errors located from 2768c2ecf20Sopenharmony_ci * elm location status register & read the positions from 2778c2ecf20Sopenharmony_ci * elm error location register. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic void elm_error_correction(struct elm_info *info, 2808c2ecf20Sopenharmony_ci struct elm_errorvec *err_vec) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int i, j, errors = 0; 2838c2ecf20Sopenharmony_ci int offset; 2848c2ecf20Sopenharmony_ci u32 reg_val; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci for (i = 0; i < info->ecc_steps; i++) { 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Check error reported */ 2898c2ecf20Sopenharmony_ci if (err_vec[i].error_reported) { 2908c2ecf20Sopenharmony_ci offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; 2918c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, offset); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Check correctable error or not */ 2948c2ecf20Sopenharmony_ci if (reg_val & ECC_CORRECTABLE_MASK) { 2958c2ecf20Sopenharmony_ci offset = ELM_ERROR_LOCATION_0 + 2968c2ecf20Sopenharmony_ci ERROR_LOCATION_SIZE * i; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Read count of correctable errors */ 2998c2ecf20Sopenharmony_ci err_vec[i].error_count = reg_val & 3008c2ecf20Sopenharmony_ci ECC_NB_ERRORS_MASK; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Update the error locations in error vector */ 3038c2ecf20Sopenharmony_ci for (j = 0; j < err_vec[i].error_count; j++) { 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, offset); 3068c2ecf20Sopenharmony_ci err_vec[i].error_loc[j] = reg_val & 3078c2ecf20Sopenharmony_ci ECC_ERROR_LOCATION_MASK; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Update error location register */ 3108c2ecf20Sopenharmony_ci offset += 4; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci errors += err_vec[i].error_count; 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci err_vec[i].error_uncorrectable = true; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Clearing interrupts for processed error vectors */ 3198c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Disable page mode */ 3228c2ecf20Sopenharmony_ci elm_configure_page_mode(info, i, false); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/** 3288c2ecf20Sopenharmony_ci * elm_decode_bch_error_page - Locate error position 3298c2ecf20Sopenharmony_ci * @dev: device pointer 3308c2ecf20Sopenharmony_ci * @ecc_calc: calculated ECC bytes from GPMC 3318c2ecf20Sopenharmony_ci * @err_vec: elm error vectors 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * Called with one or more error reported vectors & vectors with 3348c2ecf20Sopenharmony_ci * error reported is updated in err_vec[].error_reported 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_civoid elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, 3378c2ecf20Sopenharmony_ci struct elm_errorvec *err_vec) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct elm_info *info = dev_get_drvdata(dev); 3408c2ecf20Sopenharmony_ci u32 reg_val; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Enable page mode interrupt */ 3438c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, ELM_IRQSTATUS); 3448c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); 3458c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Load valid ecc byte to syndrome fragment register */ 3488c2ecf20Sopenharmony_ci elm_load_syndrome(info, err_vec, ecc_calc); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Enable syndrome processing for which syndrome fragment is updated */ 3518c2ecf20Sopenharmony_ci elm_start_processing(info, err_vec); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Wait for ELM module to finish locating error correction */ 3548c2ecf20Sopenharmony_ci wait_for_completion(&info->elm_completion); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Disable page mode interrupt */ 3578c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, ELM_IRQENABLE); 3588c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); 3598c2ecf20Sopenharmony_ci elm_error_correction(info, err_vec); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(elm_decode_bch_error_page); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic irqreturn_t elm_isr(int this_irq, void *dev_id) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci u32 reg_val; 3668c2ecf20Sopenharmony_ci struct elm_info *info = dev_id; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci reg_val = elm_read_reg(info, ELM_IRQSTATUS); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* All error vectors processed */ 3718c2ecf20Sopenharmony_ci if (reg_val & INTR_STATUS_PAGE_VALID) { 3728c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQSTATUS, 3738c2ecf20Sopenharmony_ci reg_val & INTR_STATUS_PAGE_VALID); 3748c2ecf20Sopenharmony_ci complete(&info->elm_completion); 3758c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return IRQ_NONE; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int elm_probe(struct platform_device *pdev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int ret = 0; 3848c2ecf20Sopenharmony_ci struct resource *res, *irq; 3858c2ecf20Sopenharmony_ci struct elm_info *info; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 3888c2ecf20Sopenharmony_ci if (!info) 3898c2ecf20Sopenharmony_ci return -ENOMEM; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 3948c2ecf20Sopenharmony_ci if (!irq) { 3958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no irq resource defined\n"); 3968c2ecf20Sopenharmony_ci return -ENODEV; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4008c2ecf20Sopenharmony_ci info->elm_base = devm_ioremap_resource(&pdev->dev, res); 4018c2ecf20Sopenharmony_ci if (IS_ERR(info->elm_base)) 4028c2ecf20Sopenharmony_ci return PTR_ERR(info->elm_base); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, 4058c2ecf20Sopenharmony_ci pdev->name, info); 4068c2ecf20Sopenharmony_ci if (ret) { 4078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failure requesting %pr\n", irq); 4088c2ecf20Sopenharmony_ci return ret; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4128c2ecf20Sopenharmony_ci if (pm_runtime_get_sync(&pdev->dev) < 0) { 4138c2ecf20Sopenharmony_ci ret = -EINVAL; 4148c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 4158c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't enable clock\n"); 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci init_completion(&info->elm_completion); 4218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->list); 4228c2ecf20Sopenharmony_ci list_add(&info->list, &elm_devices); 4238c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int elm_remove(struct platform_device *pdev) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 4308c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * elm_context_save 4378c2ecf20Sopenharmony_ci * saves ELM configurations to preserve them across Hardware powered-down 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic int elm_context_save(struct elm_info *info) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct elm_registers *regs = &info->elm_regs; 4428c2ecf20Sopenharmony_ci enum bch_ecc bch_type = info->bch_type; 4438c2ecf20Sopenharmony_ci u32 offset = 0, i; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); 4468c2ecf20Sopenharmony_ci regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); 4478c2ecf20Sopenharmony_ci regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); 4488c2ecf20Sopenharmony_ci regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); 4498c2ecf20Sopenharmony_ci for (i = 0; i < ERROR_VECTOR_MAX; i++) { 4508c2ecf20Sopenharmony_ci offset = i * SYNDROME_FRAGMENT_REG_SIZE; 4518c2ecf20Sopenharmony_ci switch (bch_type) { 4528c2ecf20Sopenharmony_ci case BCH16_ECC: 4538c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, 4548c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_6 + offset); 4558c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, 4568c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_5 + offset); 4578c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, 4588c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_4 + offset); 4598c2ecf20Sopenharmony_ci fallthrough; 4608c2ecf20Sopenharmony_ci case BCH8_ECC: 4618c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, 4628c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_3 + offset); 4638c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, 4648c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_2 + offset); 4658c2ecf20Sopenharmony_ci fallthrough; 4668c2ecf20Sopenharmony_ci case BCH4_ECC: 4678c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, 4688c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_1 + offset); 4698c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, 4708c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_0 + offset); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci default: 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs 4768c2ecf20Sopenharmony_ci * to be saved for all BCH schemes*/ 4778c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, 4788c2ecf20Sopenharmony_ci ELM_SYNDROME_FRAGMENT_6 + offset); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/** 4848c2ecf20Sopenharmony_ci * elm_context_restore 4858c2ecf20Sopenharmony_ci * writes configurations saved duing power-down back into ELM registers 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_cistatic int elm_context_restore(struct elm_info *info) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct elm_registers *regs = &info->elm_regs; 4908c2ecf20Sopenharmony_ci enum bch_ecc bch_type = info->bch_type; 4918c2ecf20Sopenharmony_ci u32 offset = 0, i; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); 4948c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); 4958c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); 4968c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); 4978c2ecf20Sopenharmony_ci for (i = 0; i < ERROR_VECTOR_MAX; i++) { 4988c2ecf20Sopenharmony_ci offset = i * SYNDROME_FRAGMENT_REG_SIZE; 4998c2ecf20Sopenharmony_ci switch (bch_type) { 5008c2ecf20Sopenharmony_ci case BCH16_ECC: 5018c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, 5028c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_6[i]); 5038c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, 5048c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_5[i]); 5058c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, 5068c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_4[i]); 5078c2ecf20Sopenharmony_ci fallthrough; 5088c2ecf20Sopenharmony_ci case BCH8_ECC: 5098c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, 5108c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_3[i]); 5118c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, 5128c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_2[i]); 5138c2ecf20Sopenharmony_ci fallthrough; 5148c2ecf20Sopenharmony_ci case BCH4_ECC: 5158c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, 5168c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_1[i]); 5178c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, 5188c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_0[i]); 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci default: 5218c2ecf20Sopenharmony_ci return -EINVAL; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ 5248c2ecf20Sopenharmony_ci elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, 5258c2ecf20Sopenharmony_ci regs->elm_syndrome_fragment_6[i] & 5268c2ecf20Sopenharmony_ci ELM_SYNDROME_VALID); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int elm_suspend(struct device *dev) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct elm_info *info = dev_get_drvdata(dev); 5348c2ecf20Sopenharmony_ci elm_context_save(info); 5358c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int elm_resume(struct device *dev) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct elm_info *info = dev_get_drvdata(dev); 5428c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 5438c2ecf20Sopenharmony_ci elm_context_restore(info); 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5518c2ecf20Sopenharmony_cistatic const struct of_device_id elm_of_match[] = { 5528c2ecf20Sopenharmony_ci { .compatible = "ti,am3352-elm" }, 5538c2ecf20Sopenharmony_ci {}, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, elm_of_match); 5568c2ecf20Sopenharmony_ci#endif 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic struct platform_driver elm_driver = { 5598c2ecf20Sopenharmony_ci .driver = { 5608c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 5618c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(elm_of_match), 5628c2ecf20Sopenharmony_ci .pm = &elm_pm_ops, 5638c2ecf20Sopenharmony_ci }, 5648c2ecf20Sopenharmony_ci .probe = elm_probe, 5658c2ecf20Sopenharmony_ci .remove = elm_remove, 5668c2ecf20Sopenharmony_ci}; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cimodule_platform_driver(elm_driver); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ELM driver for BCH error correction"); 5718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments"); 5728c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 5738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 574