diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 217 |
1 files changed, 208 insertions, 9 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 00d510858e28..2286410671e7 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -49,6 +49,20 @@ | |||
49 | #define GPMC_ECC_CONTROL 0x1f8 | 49 | #define GPMC_ECC_CONTROL 0x1f8 |
50 | #define GPMC_ECC_SIZE_CONFIG 0x1fc | 50 | #define GPMC_ECC_SIZE_CONFIG 0x1fc |
51 | #define GPMC_ECC1_RESULT 0x200 | 51 | #define GPMC_ECC1_RESULT 0x200 |
52 | #define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ | ||
53 | |||
54 | /* GPMC ECC control settings */ | ||
55 | #define GPMC_ECC_CTRL_ECCCLEAR 0x100 | ||
56 | #define GPMC_ECC_CTRL_ECCDISABLE 0x000 | ||
57 | #define GPMC_ECC_CTRL_ECCREG1 0x001 | ||
58 | #define GPMC_ECC_CTRL_ECCREG2 0x002 | ||
59 | #define GPMC_ECC_CTRL_ECCREG3 0x003 | ||
60 | #define GPMC_ECC_CTRL_ECCREG4 0x004 | ||
61 | #define GPMC_ECC_CTRL_ECCREG5 0x005 | ||
62 | #define GPMC_ECC_CTRL_ECCREG6 0x006 | ||
63 | #define GPMC_ECC_CTRL_ECCREG7 0x007 | ||
64 | #define GPMC_ECC_CTRL_ECCREG8 0x008 | ||
65 | #define GPMC_ECC_CTRL_ECCREG9 0x009 | ||
52 | 66 | ||
53 | #define GPMC_CS0_OFFSET 0x60 | 67 | #define GPMC_CS0_OFFSET 0x60 |
54 | #define GPMC_CS_SIZE 0x30 | 68 | #define GPMC_CS_SIZE 0x30 |
@@ -755,8 +769,7 @@ static int __init gpmc_init(void) | |||
755 | irq++; | 769 | irq++; |
756 | } | 770 | } |
757 | 771 | ||
758 | ret = request_irq(gpmc_irq, | 772 | ret = request_irq(gpmc_irq, gpmc_handle_irq, IRQF_SHARED, "gpmc", NULL); |
759 | gpmc_handle_irq, IRQF_SHARED, "gpmc", gpmc_base); | ||
760 | if (ret) | 773 | if (ret) |
761 | pr_err("gpmc: irq-%d could not claim: err %d\n", | 774 | pr_err("gpmc: irq-%d could not claim: err %d\n", |
762 | gpmc_irq, ret); | 775 | gpmc_irq, ret); |
@@ -861,8 +874,9 @@ int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size) | |||
861 | gpmc_ecc_used = cs; | 874 | gpmc_ecc_used = cs; |
862 | 875 | ||
863 | /* clear ecc and enable bits */ | 876 | /* clear ecc and enable bits */ |
864 | val = ((0x00000001<<8) | 0x00000001); | 877 | gpmc_write_reg(GPMC_ECC_CONTROL, |
865 | gpmc_write_reg(GPMC_ECC_CONTROL, val); | 878 | GPMC_ECC_CTRL_ECCCLEAR | |
879 | GPMC_ECC_CTRL_ECCREG1); | ||
866 | 880 | ||
867 | /* program ecc and result sizes */ | 881 | /* program ecc and result sizes */ |
868 | val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F)); | 882 | val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F)); |
@@ -870,13 +884,15 @@ int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size) | |||
870 | 884 | ||
871 | switch (mode) { | 885 | switch (mode) { |
872 | case GPMC_ECC_READ: | 886 | case GPMC_ECC_READ: |
873 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | 887 | case GPMC_ECC_WRITE: |
888 | gpmc_write_reg(GPMC_ECC_CONTROL, | ||
889 | GPMC_ECC_CTRL_ECCCLEAR | | ||
890 | GPMC_ECC_CTRL_ECCREG1); | ||
874 | break; | 891 | break; |
875 | case GPMC_ECC_READSYN: | 892 | case GPMC_ECC_READSYN: |
876 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x100); | 893 | gpmc_write_reg(GPMC_ECC_CONTROL, |
877 | break; | 894 | GPMC_ECC_CTRL_ECCCLEAR | |
878 | case GPMC_ECC_WRITE: | 895 | GPMC_ECC_CTRL_ECCDISABLE); |
879 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | ||
880 | break; | 896 | break; |
881 | default: | 897 | default: |
882 | printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode); | 898 | printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode); |
@@ -920,3 +936,186 @@ int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) | |||
920 | return 0; | 936 | return 0; |
921 | } | 937 | } |
922 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); | 938 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); |
939 | |||
940 | #ifdef CONFIG_ARCH_OMAP3 | ||
941 | |||
942 | /** | ||
943 | * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality | ||
944 | * @cs: chip select number | ||
945 | * @nsectors: how many 512-byte sectors to process | ||
946 | * @nerrors: how many errors to correct per sector (4 or 8) | ||
947 | * | ||
948 | * This function must be executed before any call to gpmc_enable_hwecc_bch. | ||
949 | */ | ||
950 | int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors) | ||
951 | { | ||
952 | /* check if ecc module is in use */ | ||
953 | if (gpmc_ecc_used != -EINVAL) | ||
954 | return -EINVAL; | ||
955 | |||
956 | /* support only OMAP3 class */ | ||
957 | if (!cpu_is_omap34xx()) { | ||
958 | printk(KERN_ERR "BCH ecc is not supported on this CPU\n"); | ||
959 | return -EINVAL; | ||
960 | } | ||
961 | |||
962 | /* | ||
963 | * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. | ||
964 | * Other chips may be added if confirmed to work. | ||
965 | */ | ||
966 | if ((nerrors == 4) && | ||
967 | (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { | ||
968 | printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n"); | ||
969 | return -EINVAL; | ||
970 | } | ||
971 | |||
972 | /* sanity check */ | ||
973 | if (nsectors > 8) { | ||
974 | printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n", | ||
975 | nsectors); | ||
976 | return -EINVAL; | ||
977 | } | ||
978 | |||
979 | return 0; | ||
980 | } | ||
981 | EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch); | ||
982 | |||
983 | /** | ||
984 | * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality | ||
985 | * @cs: chip select number | ||
986 | * @mode: read/write mode | ||
987 | * @dev_width: device bus width(1 for x16, 0 for x8) | ||
988 | * @nsectors: how many 512-byte sectors to process | ||
989 | * @nerrors: how many errors to correct per sector (4 or 8) | ||
990 | */ | ||
991 | int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, | ||
992 | int nerrors) | ||
993 | { | ||
994 | unsigned int val; | ||
995 | |||
996 | /* check if ecc module is in use */ | ||
997 | if (gpmc_ecc_used != -EINVAL) | ||
998 | return -EINVAL; | ||
999 | |||
1000 | gpmc_ecc_used = cs; | ||
1001 | |||
1002 | /* clear ecc and enable bits */ | ||
1003 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x1); | ||
1004 | |||
1005 | /* | ||
1006 | * When using BCH, sector size is hardcoded to 512 bytes. | ||
1007 | * Here we are using wrapping mode 6 both for reading and writing, with: | ||
1008 | * size0 = 0 (no additional protected byte in spare area) | ||
1009 | * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) | ||
1010 | */ | ||
1011 | gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12)); | ||
1012 | |||
1013 | /* BCH configuration */ | ||
1014 | val = ((1 << 16) | /* enable BCH */ | ||
1015 | (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ | ||
1016 | (0x06 << 8) | /* wrap mode = 6 */ | ||
1017 | (dev_width << 7) | /* bus width */ | ||
1018 | (((nsectors-1) & 0x7) << 4) | /* number of sectors */ | ||
1019 | (cs << 1) | /* ECC CS */ | ||
1020 | (0x1)); /* enable ECC */ | ||
1021 | |||
1022 | gpmc_write_reg(GPMC_ECC_CONFIG, val); | ||
1023 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | ||
1024 | return 0; | ||
1025 | } | ||
1026 | EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch); | ||
1027 | |||
1028 | /** | ||
1029 | * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes | ||
1030 | * @cs: chip select number | ||
1031 | * @dat: The pointer to data on which ecc is computed | ||
1032 | * @ecc: The ecc output buffer | ||
1033 | */ | ||
1034 | int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc) | ||
1035 | { | ||
1036 | int i; | ||
1037 | unsigned long nsectors, reg, val1, val2; | ||
1038 | |||
1039 | if (gpmc_ecc_used != cs) | ||
1040 | return -EINVAL; | ||
1041 | |||
1042 | nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; | ||
1043 | |||
1044 | for (i = 0; i < nsectors; i++) { | ||
1045 | |||
1046 | reg = GPMC_ECC_BCH_RESULT_0 + 16*i; | ||
1047 | |||
1048 | /* Read hw-computed remainder */ | ||
1049 | val1 = gpmc_read_reg(reg + 0); | ||
1050 | val2 = gpmc_read_reg(reg + 4); | ||
1051 | |||
1052 | /* | ||
1053 | * Add constant polynomial to remainder, in order to get an ecc | ||
1054 | * sequence of 0xFFs for a buffer filled with 0xFFs; and | ||
1055 | * left-justify the resulting polynomial. | ||
1056 | */ | ||
1057 | *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF); | ||
1058 | *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF); | ||
1059 | *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); | ||
1060 | *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF); | ||
1061 | *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF); | ||
1062 | *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF); | ||
1063 | *ecc++ = 0x7f ^ ((val1 & 0xF) << 4); | ||
1064 | } | ||
1065 | |||
1066 | gpmc_ecc_used = -EINVAL; | ||
1067 | return 0; | ||
1068 | } | ||
1069 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4); | ||
1070 | |||
1071 | /** | ||
1072 | * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes | ||
1073 | * @cs: chip select number | ||
1074 | * @dat: The pointer to data on which ecc is computed | ||
1075 | * @ecc: The ecc output buffer | ||
1076 | */ | ||
1077 | int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc) | ||
1078 | { | ||
1079 | int i; | ||
1080 | unsigned long nsectors, reg, val1, val2, val3, val4; | ||
1081 | |||
1082 | if (gpmc_ecc_used != cs) | ||
1083 | return -EINVAL; | ||
1084 | |||
1085 | nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; | ||
1086 | |||
1087 | for (i = 0; i < nsectors; i++) { | ||
1088 | |||
1089 | reg = GPMC_ECC_BCH_RESULT_0 + 16*i; | ||
1090 | |||
1091 | /* Read hw-computed remainder */ | ||
1092 | val1 = gpmc_read_reg(reg + 0); | ||
1093 | val2 = gpmc_read_reg(reg + 4); | ||
1094 | val3 = gpmc_read_reg(reg + 8); | ||
1095 | val4 = gpmc_read_reg(reg + 12); | ||
1096 | |||
1097 | /* | ||
1098 | * Add constant polynomial to remainder, in order to get an ecc | ||
1099 | * sequence of 0xFFs for a buffer filled with 0xFFs. | ||
1100 | */ | ||
1101 | *ecc++ = 0xef ^ (val4 & 0xFF); | ||
1102 | *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); | ||
1103 | *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); | ||
1104 | *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); | ||
1105 | *ecc++ = 0xed ^ (val3 & 0xFF); | ||
1106 | *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); | ||
1107 | *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); | ||
1108 | *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); | ||
1109 | *ecc++ = 0x97 ^ (val2 & 0xFF); | ||
1110 | *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); | ||
1111 | *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); | ||
1112 | *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); | ||
1113 | *ecc++ = 0xb5 ^ (val1 & 0xFF); | ||
1114 | } | ||
1115 | |||
1116 | gpmc_ecc_used = -EINVAL; | ||
1117 | return 0; | ||
1118 | } | ||
1119 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8); | ||
1120 | |||
1121 | #endif /* CONFIG_ARCH_OMAP3 */ | ||