Loading...
   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
/*
 * CODEC
 */

#include <common.h>
#include <post.h>

#include "mpc8xx.h"

/***********************************************/

#define MAX_DUSLIC	4

#define NUM_CHANNELS	2
#define MAX_SLICS	(MAX_DUSLIC * NUM_CHANNELS)

/***********************************************/

#define SOP_READ_CH_0		0xC4  /* Read SOP Register for Channel A  */
#define SOP_READ_CH_1		0xCC  /* Read SOP Register for Channel B  */
#define SOP_WRITE_CH_0		0x44  /* Write SOP Register for Channel A */
#define SOP_WRITE_CH_1		0x4C  /* Write SOP Register for Channel B */

#define COP_READ_CH_0		0xC5
#define COP_READ_CH_1		0xCD
#define COP_WRITE_CH_0		0x45
#define COP_WRITE_CH_1		0x4D

#define POP_READ_CH_0		0xC6
#define POP_READ_CH_1		0xCE
#define POP_WRITE_CH_0		0x46
#define POP_WRITE_CH_1		0x4E

#define RST_CMD_DUSLIC_CHIP	0x40  /* OR 0x48 */
#define RST_CMD_DUSLIC_CH_A	0x41
#define RST_CMD_DUSLIC_CH_B	0x49

#define PCM_RESYNC_CMD_CH_A	0x42
#define PCM_RESYNC_CMD_CH_B	0x4A

#define ACTIVE_HOOK_LEV_4	0
#define ACTIVE_HOOK_LEV_12	1

#define SLIC_P_NORMAL		0x01

/************************************************/

#define CODSP_WR	0x00
#define CODSP_RD	0x80
#define CODSP_OP	0x40
#define CODSP_ADR(x)	(((unsigned char)(x) & 7) << 3)
#define CODSP_M(x)	((unsigned char)(x) & 7)
#define CODSP_CMD(x)	((unsigned char)(x) & 7)

/************************************************/

/* command indication ops */
#define CODSP_M_SLEEP_PWRDN	7
#define CODSP_M_PWRDN_HIZ	0
#define CODSP_M_ANY_ACT		2
#define CODSP_M_RING		5
#define CODSP_M_ACT_MET		6
#define CODSP_M_GND_START	4
#define CODSP_M_RING_PAUSE	1

/* single byte commands */
#define CODSP_CMD_SOFT_RESET	CODSP_CMD(0)
#define CODSP_CMD_RESET_CH	CODSP_CMD(1)
#define CODSP_CMD_RESYNC	CODSP_CMD(2)

/* two byte commands */
#define CODSP_CMD_SOP		CODSP_CMD(4)
#define CODSP_CMD_COP		CODSP_CMD(5)
#define CODSP_CMD_POP		CODSP_CMD(6)

/************************************************/

/* read as 4-bytes */
#define CODSP_INTREG_INT_CH	0x80000000
#define CODSP_INTREG_HOOK	0x40000000
#define CODSP_INTREG_GNDK	0x20000000
#define CODSP_INTREG_GNDP	0x10000000
#define CODSP_INTREG_ICON	0x08000000
#define CODSP_INTREG_VRTLIM	0x04000000
#define CODSP_INTREG_OTEMP	0x02000000
#define CODSP_INTREG_SYNC_FAIL	0x01000000
#define CODSP_INTREG_LM_THRES	0x00800000
#define CODSP_INTREG_READY	0x00400000
#define CODSP_INTREG_RSTAT	0x00200000
#define CODSP_INTREG_LM_OK	0x00100000
#define CODSP_INTREG_IO4_DU	0x00080000
#define CODSP_INTREG_IO3_DU	0x00040000
#define CODSP_INTREG_IO2_DU	0x00020000
#define CODSP_INTREG_IO1_DU	0x00010000
#define CODSP_INTREG_DTMF_OK	0x00008000
#define CODSP_INTREG_DTMF_KEY4	0x00004000
#define CODSP_INTREG_DTMF_KEY3	0x00002000
#define CODSP_INTREG_DTMF_KEY2	0x00001000
#define CODSP_INTREG_DTMF_KEY1	0x00000800
#define CODSP_INTREG_DTMF_KEY0	0x00000400
#define CODSP_INTREG_UTDR_OK	0x00000200
#define CODSP_INTREG_UTDX_OK	0x00000100
#define CODSP_INTREG_EDSP_FAIL	0x00000080
#define CODSP_INTREG_CIS_BOF	0x00000008
#define CODSP_INTREG_CIS_BUF	0x00000004
#define CODSP_INTREG_CIS_REQ	0x00000002
#define CODSP_INTREG_CIS_ACT	0x00000001

/************************************************/

/* ======== SOP REG ADDRESSES =======*/

#define REVISION_ADDR		0x00
#define PCMC1_ADDR		0x05
#define XCR_ADDR		0x06
#define INTREG1_ADDR		0x07
#define INTREG2_ADDR		0x08
#define INTREG3_ADDR		0x09
#define INTREG4_ADDR		0x0A
#define LMRES1_ADDR		0x0D
#define MASK_ADDR		0x11
#define IOCTL3_ADDR		0x14
#define BCR1_ADDR		0x15
#define BCR2_ADDR		0x16
#define BCR3_ADDR		0x17
#define BCR4_ADDR		0x18
#define BCR5_ADDR		0x19
#define DSCR_ADDR		0x1A
#define LMCR1_ADDR		0x1C
#define LMCR2_ADDR		0x1D
#define LMCR3_ADDR		0x1E
#define OFR1_ADDR		0x1F
#define PCMR1_ADDR		0x21
#define PCMX1_ADDR		0x25
#define TSTR3_ADDR		0x2B
#define TSTR4_ADDR		0x2C
#define TSTR5_ADDR		0x2D

/* ========= POP REG ADDRESSES ========*/

#define CIS_DAT_ADDR		0x00

#define LEC_LEN_ADDR		0x3A
#define LEC_POWR_ADDR		0x3B
#define LEC_DELP_ADDR		0x3C
#define LEC_DELQ_ADDR		0x3D
#define LEC_GAIN_XI_ADDR	0x3E
#define LEC_GAIN_RI_ADDR	0x3F
#define LEC_GAIN_XO_ADDR	0x40
#define LEC_RES_1_ADDR		0x41
#define LEC_RES_2_ADDR		0x42

#define NLP_POW_LPF_ADDR	0x30
#define NLP_POW_LPS_ADDR	0x31
#define NLP_BN_LEV_X_ADDR	0x32
#define NLP_BN_LEV_R_ADDR	0x33
#define NLP_BN_INC_ADDR		0x34
#define NLP_BN_DEC_ADDR		0x35
#define NLP_BN_MAX_ADDR		0x36
#define NLP_BN_ADJ_ADDR		0x37
#define NLP_RE_MIN_ERLL_ADDR	0x38
#define NLP_RE_EST_ERLL_ADDR	0x39
#define NLP_SD_LEV_X_ADDR	0x3A
#define NLP_SD_LEV_R_ADDR	0x3B
#define NLP_SD_LEV_BN_ADDR	0x3C
#define NLP_SD_LEV_RE_ADDR	0x3D
#define NLP_SD_OT_DT_ADDR	0x3E
#define NLP_ERL_LIN_LP_ADDR	0x3F
#define NLP_ERL_LEC_LP_ADDR	0x40
#define NLP_CT_LEV_RE_ADDR	0x41
#define NLP_CTRL_ADDR		0x42

#define UTD_CF_H_ADDR		0x4B
#define UTD_CF_L_ADDR		0x4C
#define UTD_BW_H_ADDR		0x4D
#define UTD_BW_L_ADDR		0x4E
#define UTD_NLEV_ADDR		0x4F
#define UTD_SLEV_H_ADDR		0x50
#define UTD_SLEV_L_ADDR		0x51
#define UTD_DELT_ADDR		0x52
#define UTD_RBRK_ADDR		0x53
#define UTD_RTIME_ADDR		0x54
#define UTD_EBRK_ADDR		0x55
#define UTD_ETIME_ADDR		0x56

#define DTMF_LEV_ADDR		0x30
#define DTMF_TWI_ADDR		0x31
#define DTMF_NCF_H_ADDR		0x32
#define DTMF_NCF_L_ADDR		0x33
#define DTMF_NBW_H_ADDR		0x34
#define DTMF_NBW_L_ADDR		0x35
#define DTMF_GAIN_ADDR		0x36
#define DTMF_RES1_ADDR		0x37
#define DTMF_RES2_ADDR		0x38
#define DTMF_RES3_ADDR		0x39

#define CIS_LEV_H_ADDR		0x43
#define CIS_LEV_L_ADDR		0x44
#define CIS_BRS_ADDR		0x45
#define CIS_SEIZ_H_ADDR		0x46
#define CIS_SEIZ_L_ADDR		0x47
#define CIS_MARK_H_ADDR		0x48
#define CIS_MARK_L_ADDR		0x49
#define CIS_LEC_MODE_ADDR	0x4A

/*=====================================*/

#define HOOK_LEV_ACT_START_ADDR 0x89
#define RO1_START_ADDR		0x70
#define RO2_START_ADDR		0x95
#define RO3_START_ADDR		0x96

#define TG1_FREQ_START_ADDR	0x38
#define TG1_GAIN_START_ADDR	0x39
#define TG1_BANDPASS_START_ADDR 0x3B
#define TG1_BANDPASS_END_ADDR	0x3D

#define TG2_FREQ_START_ADDR	0x40
#define TG2_GAIN_START_ADDR	0x41
#define TG2_BANDPASS_START_ADDR 0x43
#define TG2_BANDPASS_END_ADDR	0x45

/*====================================*/

#define PCM_HW_B		0x80
#define PCM_HW_A		0x00
#define PCM_TIME_SLOT_0		0x00   /*  Byte 0 of PCM Frame (by default is assigned to channel A ) */
#define PCM_TIME_SLOT_1		0x01   /*  Byte 1 of PCM Frame (by default is assigned to channel B ) */
#define PCM_TIME_SLOT_4		0x04   /*  Byte 4 of PCM Frame (Corresponds to B1 of the Second GCI ) */

#define	 RX_LEV_ADDR	0x28
#define	 TX_LEV_ADDR	0x30
#define	 Ik1_ADDR	0x83

#define	 AR_ROW		3 /* Is the row (AR Params) of the ac_Coeff array in SMS_CODEC_Defaults struct	*/
#define	 AX_ROW		6 /* Is the row (AX Params) of the ac_Coeff array in SMS_CODEC_Defaults struct	*/
#define	 DCF_ROW	0 /* Is the row (DCF Params) of the dc_Coeff array in SMS_CODEC_Defaults struct */

/* Mark the start byte of Duslic parameters that we use with configurator */
#define	 Ik1_START_BYTE		3
#define	 RX_LEV_START_BYTE	0
#define	 TX_LEV_START_BYTE	0

/************************************************/

#define INTREG4_CIS_ACT		(1 << 0)

#define BCR1_SLEEP		0x20
#define BCR1_REVPOL		0x10
#define BCR1_ACTR		0x08
#define BCR1_ACTL		0x04
#define BCR1_SLIC_MASK		0x03

#define BCR2_HARD_POL_REV	0x40
#define BCR2_TTX		0x20
#define BCR2_TTX_12K		0x10
#define BCR2_HIMAN		0x08
#define BCR2_PDOT		0x01

#define BCR3_PCMX_EN		(1 << 4)

#define BCR5_DTMF_EN		(1 << 0)
#define BCR5_DTMF_SRC		(1 << 1)
#define BCR5_LEC_EN		(1 << 2)
#define BCR5_LEC_OUT		(1 << 3)
#define BCR5_CIS_EN		(1 << 4)
#define BCR5_CIS_AUTO		(1 << 5)
#define BCR5_UTDX_EN		(1 << 6)
#define BCR5_UTDR_EN		(1 << 7)

#define DSCR_TG1_EN		(1 << 0)
#define DSCR_TG2_EN		(1 << 1)
#define DSCR_PTG		(1 << 2)
#define DSCR_COR8		(1 << 3)
#define DSCR_DG_KEY(x)		(((x) & 0x0F) << 4)

#define CIS_LEC_MODE_CIS_V23	(1 << 0)
#define CIS_LEC_MODE_CIS_FRM	(1 << 1)
#define CIS_LEC_MODE_NLP_EN	(1 << 2)
#define CIS_LEC_MODE_UTDR_SUM	(1 << 4)
#define CIS_LEC_MODE_UTDX_SUM	(1 << 5)
#define CIS_LEC_MODE_LEC_FREEZE (1 << 6)
#define CIS_LEC_MODE_LEC_ADAPT	(1 << 7)

#define TSTR4_COR_64		(1 << 5)

#define TSTR3_AC_DLB_8K		(1 << 2)
#define TSTR3_AC_DLB_32K	(1 << 3)
#define TSTR3_AC_DLB_4M		(1 << 5)


#define LMCR1_TEST_EN		(1 << 7)
#define LMCR1_LM_EN		(1 << 6)
#define LMCR1_LM_THM		(1 << 5)
#define LMCR1_LM_ONCE		(1 << 2)
#define LMCR1_LM_MASK		(1 << 1)

#define LMCR2_LM_RECT			(1 << 5)
#define LMCR2_LM_SEL_VDD		0x0D
#define LMCR2_LM_SEL_IO3		0x0A
#define LMCR2_LM_SEL_IO4		0x0B
#define LMCR2_LM_SEL_IO4_MINUS_IO3	0x0F

#define LMCR3_RTR_SEL		(1 << 6)

#define LMCR3_RNG_OFFSET_NONE	0x00
#define LMCR3_RNG_OFFSET_1	0x01
#define LMCR3_RNG_OFFSET_2	0x02
#define LMCR3_RNG_OFFSET_3	0x03

#define TSTR5_DC_HOLD		(1 << 3)

/************************************************/

#define TARGET_ONHOOK_BATH_x100		4600	/* 46.0 Volt */
#define TARGET_ONHOOK_BATL_x100		2500	/* 25.0 Volt */
#define TARGET_V_DIVIDER_RATIO_x100	21376L	/* (R1+R2)/R2 = 213.76 */
#define DIVIDER_RATIO_ACCURx100		(22 * 100)
#define V_AD_x10000			10834L	/* VAD = 1.0834 */
#define TARGET_VDDx100			330	/* VDD = 3.3 * 10 */
#define VDD_MAX_DIFFx100		20	/* VDD Accur = 0.2*100 */

#define RMS_MULTIPLIERx100		111	/* pi/(2xsqrt(2)) = 1.11*/
#define K_INTDC_RECT_ON			4	/* When Rectifier is ON this value is necessary(2^4) */
#define K_INTDC_RECT_OFF		2	/* 2^2 */
#define RNG_FREQ			25
#define SAMPLING_FREQ			(2000L)
#define N_SAMPLES			(SAMPLING_FREQ/RNG_FREQ)     /* for Ring Freq =25Hz (40ms Integration Period)[Sampling rate 2KHz -->1 Sample every 500us] */
#define HOOK_THRESH_RING_START_ADDR	0x8B
#define RING_PARAMS_START_ADDR		0x70

#define V_OUT_BATH_MAX_DIFFx100		300	/* 3.0 x100 */
#define V_OUT_BATL_MAX_DIFFx100		400	/* 4.0 x100 */
#define MAX_V_RING_MEANx100		50
#define TARGET_V_RING_RMSx100		2720
#define V_RMS_RING_MAX_DIFFx100		250

#define LM_OK_SRC_IRG_2			(1 << 4)

/************************************************/

#define PORTB		(((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
#define PORTC		(((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
#define PORTD		(((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pddat)

#define _PORTD_SET(mask, state) \
	do { \
		if (state) \
			PORTD |= mask; \
		else \
			PORTD &= ~mask; \
	} while (0)

#define _PORTB_SET(mask, state) \
	do { \
		if (state) \
			PORTB |= mask; \
		else \
			PORTB &= ~mask; \
	} while (0)

#define _PORTB_TGL(mask) do { PORTB ^= mask; } while (0)
#define _PORTB_GET(mask) (!!(PORTB & mask))

#define _PORTC_GET(mask) (!!(PORTC & mask))

/* port B */
#define SPI_RXD		(1 << (31 - 28))
#define SPI_TXD		(1 << (31 - 29))
#define SPI_CLK		(1 << (31 - 30))

/* port C */
#define COM_HOOK1	(1 << (15 - 9))
#define COM_HOOK2	(1 << (15 - 10))

#ifndef CONFIG_NETTA_SWAPHOOK

#define COM_HOOK3	(1 << (15 - 11))
#define COM_HOOK4	(1 << (15 - 12))

#else

#define COM_HOOK3	(1 << (15 - 12))
#define COM_HOOK4	(1 << (15 - 11))

#endif

/* port D */
#define SPIENC1		(1 << (15 - 9))
#define SPIENC2		(1 << (15 - 10))
#define SPIENC3		(1 << (15 - 11))
#define SPIENC4		(1 << (15 - 14))

#define SPI_DELAY() udelay(1)

static inline unsigned int __SPI_Transfer(unsigned int tx)
{
	unsigned int rx;
	int b;

	rx = 0; b = 8;
	while (--b >= 0) {
		_PORTB_SET(SPI_TXD, tx & 0x80);
		tx <<= 1;
		_PORTB_TGL(SPI_CLK);
		SPI_DELAY();
		rx <<= 1;
		rx |= _PORTB_GET(SPI_RXD);
		_PORTB_TGL(SPI_CLK);
		SPI_DELAY();
	}

	return rx;
}

static const char *codsp_dtmf_map = "D1234567890*#ABC";

static const int spienc_mask_tab[4] = { SPIENC1, SPIENC2, SPIENC3, SPIENC4 };
static const int com_hook_mask_tab[4] = { COM_HOOK1, COM_HOOK2, COM_HOOK3, COM_HOOK4 };

static unsigned int codsp_send(int duslic_id, const unsigned char *cmd, int cmdlen, unsigned char *res, int reslen)
{
	unsigned int rx;
	int i;

	/* just some sanity checks */
	if (cmd == 0 || cmdlen < 0)
		return -1;

	_PORTD_SET(spienc_mask_tab[duslic_id], 0);

	/* first 2 bytes are without response */
	i = 2;
	while (i-- > 0 && cmdlen-- > 0)
		__SPI_Transfer(*cmd++);

	while (cmdlen-- > 0) {
		rx = __SPI_Transfer(*cmd++);
		if (res != 0 && reslen-- > 0)
			*res++ = (unsigned char)rx;
	}
	if (res != 0) {
		while (reslen-- > 0)
			*res++ = __SPI_Transfer(0xFF);
	}

	_PORTD_SET(spienc_mask_tab[duslic_id], 1);

	return 0;
}

/****************************************************************************/

void codsp_set_ciop_m(int duslic_id, int channel, unsigned char m)
{
	unsigned char cmd = CODSP_WR | CODSP_ADR(channel) | CODSP_M(m);
	codsp_send(duslic_id, &cmd, 1, 0, 0);
}

void codsp_reset_chip(int duslic_id)
{
	static const unsigned char cmd = CODSP_WR | CODSP_OP | CODSP_CMD_SOFT_RESET;
	codsp_send(duslic_id, &cmd, 1, 0, 0);
}

void codsp_reset_channel(int duslic_id, int channel)
{
	unsigned char cmd = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_RESET_CH;
	codsp_send(duslic_id, &cmd, 1, 0, 0);
}

void codsp_resync_channel(int duslic_id, int channel)
{
	unsigned char cmd = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_RESYNC;
	codsp_send(duslic_id, &cmd, 1, 0, 0);
}

/****************************************************************************/

void codsp_write_sop_char(int duslic_id, int channel, unsigned char regno, unsigned char val)
{
	unsigned char cmd[3];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;
	cmd[2] = val;

	codsp_send(duslic_id, cmd, 3, 0, 0);
}

void codsp_write_sop_short(int duslic_id, int channel, unsigned char regno, unsigned short val)
{
	unsigned char cmd[4];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;
	cmd[2] = (unsigned char)(val >> 8);
	cmd[3] = (unsigned char)val;

	codsp_send(duslic_id, cmd, 4, 0, 0);
}

void codsp_write_sop_int(int duslic_id, int channel, unsigned char regno, unsigned int val)
{
	unsigned char cmd[5];

	cmd[0] = CODSP_WR | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;
	cmd[2] = (unsigned char)(val >> 24);
	cmd[3] = (unsigned char)(val >> 16);
	cmd[4] = (unsigned char)(val >> 8);
	cmd[5] = (unsigned char)val;

	codsp_send(duslic_id, cmd, 6, 0, 0);
}

unsigned char codsp_read_sop_char(int duslic_id, int channel, unsigned char regno)
{
	unsigned char cmd[3];
	unsigned char res[2];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;

	codsp_send(duslic_id, cmd, 2, res, 2);

	return res[1];
}

unsigned short codsp_read_sop_short(int duslic_id, int channel, unsigned char regno)
{
	unsigned char cmd[2];
	unsigned char res[3];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;

	codsp_send(duslic_id, cmd, 2, res, 3);

	return ((unsigned short)res[1] << 8) | res[2];
}

unsigned int codsp_read_sop_int(int duslic_id, int channel, unsigned char regno)
{
	unsigned char cmd[2];
	unsigned char res[5];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_SOP;
	cmd[1] = regno;

	codsp_send(duslic_id, cmd, 2, res, 5);

	return ((unsigned int)res[1] << 24) | ((unsigned int)res[2] << 16) | ((unsigned int)res[3] << 8) | res[4];
}

/****************************************************************************/

void codsp_write_cop_block(int duslic_id, int channel, unsigned char addr, const unsigned char *block)
{
	unsigned char cmd[10];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;
	memcpy(cmd + 2, block, 8);
	codsp_send(duslic_id, cmd, 10, 0, 0);
}

void codsp_write_cop_char(int duslic_id, int channel, unsigned char addr, unsigned char val)
{
	unsigned char cmd[3];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;
	cmd[2] = val;
	codsp_send(duslic_id, cmd, 3, 0, 0);
}

void codsp_write_cop_short(int duslic_id, int channel, unsigned char addr, unsigned short val)
{
	unsigned char cmd[3];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;
	cmd[2] = (unsigned char)(val >> 8);
	cmd[3] = (unsigned char)val;

	codsp_send(duslic_id, cmd, 4, 0, 0);
}

void codsp_read_cop_block(int duslic_id, int channel, unsigned char addr, unsigned char *block)
{
	unsigned char cmd[2];
	unsigned char res[9];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;
	codsp_send(duslic_id, cmd, 2, res, 9);
	memcpy(block, res + 1, 8);
}

unsigned char codsp_read_cop_char(int duslic_id, int channel, unsigned char addr)
{
	unsigned char cmd[2];
	unsigned char res[2];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;
	codsp_send(duslic_id, cmd, 2, res, 2);
	return res[1];
}

unsigned short codsp_read_cop_short(int duslic_id, int channel, unsigned char addr)
{
	unsigned char cmd[2];
	unsigned char res[3];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR(channel) | CODSP_CMD_COP;
	cmd[1] = addr;

	codsp_send(duslic_id, cmd, 2, res, 3);

	return ((unsigned short)res[1] << 8) | res[2];
}

/****************************************************************************/

#define MAX_POP_BLOCK	50

void codsp_write_pop_block (int duslic_id, int channel, unsigned char addr,
			    const unsigned char *block, int len)
{
	unsigned char cmd[2 + MAX_POP_BLOCK];

	if (len > MAX_POP_BLOCK)	/* truncate */
		len = MAX_POP_BLOCK;

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = addr;
	memcpy (cmd + 2, block, len);
	codsp_send (duslic_id, cmd, 2 + len, 0, 0);
}

void codsp_write_pop_char (int duslic_id, int channel, unsigned char regno,
			   unsigned char val)
{
	unsigned char cmd[3];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;
	cmd[2] = val;

	codsp_send (duslic_id, cmd, 3, 0, 0);
}

void codsp_write_pop_short (int duslic_id, int channel, unsigned char regno,
			    unsigned short val)
{
	unsigned char cmd[4];

	cmd[0] = CODSP_WR | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;
	cmd[2] = (unsigned char) (val >> 8);
	cmd[3] = (unsigned char) val;

	codsp_send (duslic_id, cmd, 4, 0, 0);
}

void codsp_write_pop_int (int duslic_id, int channel, unsigned char regno,
			  unsigned int val)
{
	unsigned char cmd[5];

	cmd[0] = CODSP_WR | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;
	cmd[2] = (unsigned char) (val >> 24);
	cmd[3] = (unsigned char) (val >> 16);
	cmd[4] = (unsigned char) (val >> 8);
	cmd[5] = (unsigned char) val;

	codsp_send (duslic_id, cmd, 6, 0, 0);
}

unsigned char codsp_read_pop_char (int duslic_id, int channel,
				   unsigned char regno)
{
	unsigned char cmd[3];
	unsigned char res[2];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;

	codsp_send (duslic_id, cmd, 2, res, 2);

	return res[1];
}

unsigned short codsp_read_pop_short (int duslic_id, int channel,
				     unsigned char regno)
{
	unsigned char cmd[2];
	unsigned char res[3];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;

	codsp_send (duslic_id, cmd, 2, res, 3);

	return ((unsigned short) res[1] << 8) | res[2];
}

unsigned int codsp_read_pop_int (int duslic_id, int channel,
				 unsigned char regno)
{
	unsigned char cmd[2];
	unsigned char res[5];

	cmd[0] = CODSP_RD | CODSP_OP | CODSP_ADR (channel) | CODSP_CMD_POP;
	cmd[1] = regno;

	codsp_send (duslic_id, cmd, 2, res, 5);

	return (((unsigned int) res[1] << 24) |
		((unsigned int) res[2] << 16) |
		((unsigned int) res[3] <<  8) |
		res[4] );
}
/****************************************************************************/

struct _coeffs {
	unsigned char addr;
	unsigned char values[8];
};

struct _coeffs ac_coeffs[11] = {
	{ 0x60, {0xAD,0xDA,0xB5,0x9B,0xC7,0x2A,0x9D,0x00} }, /* 0x60 IM-Filter part 1 */
	{ 0x68, {0x10,0x00,0xA9,0x82,0x0D,0x77,0x0A,0x00} }, /* 0x68 IM-Filter part 2 */
	{ 0x18, {0x08,0xC0,0xD2,0xAB,0xA5,0xE2,0xAB,0x07} }, /* 0x18 FRR-Filter	      */
	{ 0x28, {0x44,0x93,0xF5,0x92,0x88,0x00,0x00,0x00} }, /* 0x28 AR-Filter	      */
	{ 0x48, {0x96,0x38,0x29,0x96,0xC9,0x2B,0x8B,0x00} }, /* 0x48 LPR-Filter	      */
	{ 0x20, {0x08,0xB0,0xDA,0x9D,0xA7,0xFA,0x93,0x06} }, /* 0x20 FRX-Filter	      */
	{ 0x30, {0xBA,0xAC,0x00,0x01,0x85,0x50,0xC0,0x1A} }, /* 0x30 AX-Filter	      */
	{ 0x50, {0x96,0x38,0x29,0xF5,0xFA,0x2B,0x8B,0x00} }, /* 0x50 LPX-Filter	      */
	{ 0x00, {0x00,0x08,0x08,0x81,0x00,0x80,0x00,0x08} }, /* 0x00 TH-Filter part 1 */
	{ 0x08, {0x81,0x00,0x80,0x00,0xD7,0x33,0xBA,0x01} }, /* 0x08 TH-Filter part 2 */
	{ 0x10, {0xB3,0x6C,0xDC,0xA3,0xA4,0xE5,0x88,0x00} }  /* 0x10 TH-Filter part 3 */
};

struct _coeffs ac_coeffs_0dB[11] = {
	{ 0x60, {0xAC,0x2A,0xB5,0x9A,0xB7,0x2A,0x9D,0x00} },
	{ 0x68, {0x10,0x00,0xA9,0x82,0x0D,0x83,0x0A,0x00} },
	{ 0x18, {0x08,0x20,0xD4,0xA4,0x65,0xEE,0x92,0x07} },
	{ 0x28, {0x2B,0xAB,0x36,0xA5,0x88,0x00,0x00,0x00} },
	{ 0x48, {0xAB,0xE9,0x4E,0x32,0xAB,0x25,0xA5,0x03} },
	{ 0x20, {0x08,0x20,0xDB,0x9C,0xA7,0xFA,0xB4,0x07} },
	{ 0x30, {0xF3,0x10,0x07,0x60,0x85,0x40,0xC0,0x1A} },
	{ 0x50, {0x96,0x38,0x29,0x97,0x39,0x19,0x8B,0x00} },
	{ 0x00, {0x00,0x08,0x08,0x81,0x00,0x80,0x00,0x08} },
	{ 0x08, {0x81,0x00,0x80,0x00,0x47,0x3C,0xD2,0x01} },
	{ 0x10, {0x62,0xDB,0x4A,0x87,0x73,0x28,0x88,0x00} }
};

struct _coeffs dc_coeffs[9] = {
	{ 0x80, {0x25,0x59,0x9C,0x23,0x24,0x23,0x32,0x1C} }, /* 0x80 DC-Parameter     */
	{ 0x70, {0x90,0x30,0x1B,0xC0,0x33,0x43,0xAC,0x02} }, /* 0x70 Ringing	      */
	{ 0x90, {0x3F,0xC3,0x2E,0x3A,0x80,0x90,0x00,0x09} }, /* 0x90 LP-Filters	      */
	{ 0x88, {0xAF,0x80,0x27,0x7B,0x01,0x4C,0x7B,0x02} }, /* 0x88 Hook Levels      */
	{ 0x78, {0x00,0xC0,0x6D,0x7A,0xB3,0x78,0x89,0x00} }, /* 0x78 Ramp Generator   */
	{ 0x58, {0xA5,0x44,0x34,0xDB,0x0E,0xA2,0x2A,0x00} }, /* 0x58 TTX	      */
	{ 0x38, {0x33,0x49,0x9A,0x65,0xBB,0x00,0x00,0x00} }, /* 0x38 TG1	      */
	{ 0x40, {0x33,0x49,0x9A,0x65,0xBB,0x00,0x00,0x00} }, /* 0x40 TG2	      */
	{ 0x98, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} }  /* 0x98 Reserved	      */
};

void program_coeffs(int duslic_id, int channel, struct _coeffs *coeffs, int tab_size)
{
	int i;

	for (i = 0; i < tab_size; i++)
	codsp_write_cop_block(duslic_id, channel, coeffs[i].addr, coeffs[i].values);
}

#define SS_OPEN_CIRCUIT			0
#define SS_RING_PAUSE			1
#define SS_ACTIVE			2
#define SS_ACTIVE_HIGH			3
#define SS_ACTIVE_RING			4
#define SS_RINGING			5
#define SS_ACTIVE_WITH_METERING		6
#define SS_ONHOOKTRNSM			7
#define SS_STANDBY			8
#define SS_MAX				8

static void codsp_set_slic(int duslic_id, int channel, int state)
{
	unsigned char v;

	v = codsp_read_sop_char(duslic_id, channel, BCR1_ADDR);

	switch (state) {

		case SS_ACTIVE:
			codsp_write_sop_char(duslic_id, channel, BCR1_ADDR, (v & ~BCR1_ACTR) | BCR1_ACTL);
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_ANY_ACT);
			break;

		case SS_ACTIVE_HIGH:
			codsp_write_sop_char(duslic_id, channel, BCR1_ADDR, v & ~(BCR1_ACTR | BCR1_ACTL));
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_ANY_ACT);
			break;

		case SS_ACTIVE_RING:
		case SS_ONHOOKTRNSM:
			codsp_write_sop_char(duslic_id, channel, BCR1_ADDR, (v & ~BCR1_ACTL) | BCR1_ACTR);
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_ANY_ACT);
			break;

		case SS_STANDBY:
			codsp_write_sop_char(duslic_id, channel, BCR1_ADDR, v & ~(BCR1_ACTL | BCR1_ACTR));
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_SLEEP_PWRDN);
			break;

		case SS_OPEN_CIRCUIT:
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_PWRDN_HIZ);
			break;

		case SS_RINGING:
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_RING);
			break;

		case SS_RING_PAUSE:
			codsp_set_ciop_m(duslic_id, channel, CODSP_M_RING_PAUSE);
			break;
	}
}

const unsigned char Ring_Sin_28Vrms_25Hz[8] = { 0x90, 0x30, 0x1B, 0xC0, 0xC3, 0x9C, 0x88, 0x00 };
const unsigned char Max_HookRingTh[3] = { 0x7B, 0x41, 0x62 };

void retrieve_slic_state(int slic_id)
{
	int duslic_id = slic_id >> 1;
	int channel = slic_id & 1;

	/* Retrieve the state of the SLICs */
	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, 0x00);

	/* wait at least 1000us to clear the LM_OK and 500us to set the LM_OK ==> for the LM to make the first Measurement */
	udelay(10000);

	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK);
	codsp_set_slic(duslic_id, channel, SS_ACTIVE_HIGH);
	codsp_write_sop_char(duslic_id, channel, LMCR3_ADDR, 0x40);

	/* Program Default Hook Ring thresholds */
	codsp_write_cop_block(duslic_id, channel, dc_coeffs[1].addr, dc_coeffs[1].values);

	/* Now program Hook Threshold while Ring and ac RingTrip to max values */
	codsp_write_cop_block(duslic_id, channel, dc_coeffs[3].addr, dc_coeffs[3].values);

	codsp_write_sop_short(duslic_id, channel, OFR1_ADDR, 0x0000);

	udelay(40000);
}

int wait_level_metering_finish(int duslic_id, int channel)
{
	int cnt;

	for (cnt = 0; cnt < 1000 &&
		(codsp_read_sop_char(duslic_id, channel, INTREG2_ADDR) & LM_OK_SRC_IRG_2) == 0; cnt++) { }

	return cnt != 1000;
}

int measure_on_hook_voltages(int slic_id, long *vdd,
		long *v_oh_H, long *v_oh_L, long *ring_mean_v, long *ring_rms_v)
{
	short LM_Result, Offset_Compensation;	/* Signed 16 bit */
	long int VDD, VDD_diff, V_in, V_out, Divider_Ratio, Vout_diff ;
	unsigned char err_mask = 0;
	int duslic_id = slic_id >> 1;
	int channel = slic_id & 1;
	int i;

	/* measure VDD */
	/* Now select the VDD level Measurement (but first of all Hold the DC characteristic) */
	codsp_write_sop_char(duslic_id, channel, TSTR5_ADDR, TSTR5_DC_HOLD);

	/* Activate Test Mode ==> To Enable DC Hold !!! */
	/* (else the LMRES is treated as Feeding Current and the Feeding voltage changes */
	/* imediatelly (after 500us when the LMRES Registers is updated for the first time after selection of (IO4-IO3) measurement !!!!))*/
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_TEST_EN | LMCR1_LM_THM | LMCR1_LM_MASK);

	udelay(40000);

	/* Now I Can select what to measure by DC Level Meter (select IO4-IO3) */
	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, LMCR2_LM_SEL_VDD);

	/* wait at least 1000us to clear the LM_OK and 500us to set the LM_OK ==> for the LM to make the first Measurement */
	udelay(10000);

	/* Now Read the LM Result Registers */
	LM_Result = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
	VDD = (-1)*((((long int)LM_Result) * 390L ) >> 15) ;	/* VDDx100 */

	*vdd = VDD;

	VDD_diff = VDD - TARGET_VDDx100;

	if (VDD_diff < 0)
		VDD_diff = -VDD_diff;

	if (VDD_diff > VDD_MAX_DIFFx100)
		err_mask |= 1;

	Divider_Ratio = TARGET_V_DIVIDER_RATIO_x100;

	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, 0x00);
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK);

	codsp_set_slic(duslic_id, channel, SS_ACTIVE_HIGH); /* Go back to ONHOOK Voltage */

	udelay(40000);

	codsp_write_sop_char(duslic_id, channel,
		LMCR1_ADDR, LMCR1_TEST_EN | LMCR1_LM_THM | LMCR1_LM_MASK);

	udelay(40000);

	/* Now I Can select what to measure by DC Level Meter (select IO4-IO3) */
	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, LMCR2_LM_SEL_IO4_MINUS_IO3);

	/* wait at least 1000us to clear the LM_OK and 500us to set the LM_OK ==> for the LM to make the first Measurement */
	udelay(10000);

	/* Now Read the LM Result Registers */
	LM_Result = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
	V_in = (-1)* ((((long int)LM_Result) * V_AD_x10000 ) >> 15) ;  /* Vin x 10000*/

	V_out = (V_in * Divider_Ratio) / 10000L ;	/* Vout x100 */

	*v_oh_H = V_out;

	Vout_diff = V_out - TARGET_ONHOOK_BATH_x100;

	if (Vout_diff < 0)
		Vout_diff = -Vout_diff;

	if (Vout_diff > V_OUT_BATH_MAX_DIFFx100)
		err_mask |= 2;

	codsp_set_slic(duslic_id, channel, SS_ACTIVE); /* Go back to ONHOOK Voltage */

	udelay(40000);

	/* Now Read the LM Result Registers */
	LM_Result = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);

	V_in = (-1)* ((((long int)LM_Result) * V_AD_x10000 ) >> 15) ;  /* Vin x 10000*/

	V_out = (V_in * Divider_Ratio) / 10000L ;	/* Vout x100 */

	*v_oh_L = V_out;

	Vout_diff = V_out - TARGET_ONHOOK_BATL_x100;

	if (Vout_diff < 0)
		Vout_diff = -Vout_diff;

	if (Vout_diff > V_OUT_BATL_MAX_DIFFx100)
		err_mask |= 4;

	/* perform ring tests */

	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, 0x00);
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK);

	udelay(40000);

	codsp_write_sop_char(duslic_id, channel, LMCR3_ADDR, LMCR3_RTR_SEL | LMCR3_RNG_OFFSET_NONE);

	/* Now program RO1 =0V , Ring Amplitude and frequency and shift factor K = 1 (LMDC=0x0088)*/
	codsp_write_cop_block(duslic_id, channel, RING_PARAMS_START_ADDR, Ring_Sin_28Vrms_25Hz);

	/* By Default RO1 is selected when ringing RNG-OFFSET = 00 */

	/* Now program Hook Threshold while Ring and ac RingTrip to max values */
	for(i = 0; i < sizeof(Max_HookRingTh); i++)
		codsp_write_cop_char(duslic_id, channel, HOOK_THRESH_RING_START_ADDR + i, Max_HookRingTh[i]);

	codsp_write_sop_short(duslic_id, channel, OFR1_ADDR, 0x0000);

	codsp_set_slic(duslic_id, channel, SS_RING_PAUSE); /* Start Ringing */

	/* select source for the levelmeter to be IO4-IO3 */
	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR, LMCR2_LM_SEL_IO4_MINUS_IO3);

	udelay(40000);

	/* Before Enabling Level Meter Programm the apropriate shift factor K_INTDC=(4 if Rectifier Enabled and 2 if Rectifier Disabled) */
	codsp_write_cop_char(duslic_id, channel, RING_PARAMS_START_ADDR + 7, K_INTDC_RECT_OFF);

	udelay(10000);

	/* Enable LevelMeter to Integrate only once (Rectifier Disabled) */
	codsp_write_sop_char(duslic_id, channel,
			LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_EN | LMCR1_LM_ONCE);

	udelay(40000); /* Integration Period == Ring Period = 40ms (for 25Hz Ring) */

	if (wait_level_metering_finish(duslic_id, channel)) {

		udelay(10000); /* To be sure that Integration Results are Valid wait at least 500us !!! */

		/* Now Read the LM Result Registers (Will be valid until LM_EN becomes zero again( after that the Result is updated every 500us) ) */
		Offset_Compensation = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
		Offset_Compensation = (-1) * ((Offset_Compensation * (1 << K_INTDC_RECT_OFF)) / N_SAMPLES);

		/* Disable LevelMeter ==> In order to be able to restart Integrator again (for the next integration) */
		codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_ONCE);

		/* Now programm Integrator Offset Registers !!! */
		codsp_write_sop_short(duslic_id, channel, OFR1_ADDR, Offset_Compensation);

		codsp_set_slic(duslic_id, channel, SS_RINGING); /* Start Ringing */

		udelay(40000);

		/* Reenable Level Meter Integrator (The Result will be valid after Integration Period=Ring Period and until LN_EN become zero again) */
		codsp_write_sop_char(duslic_id, channel,
				LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_EN | LMCR1_LM_ONCE);

		udelay(40000); /* Integration Period == Ring Period = 40ms (for 25Hz Ring) */

		/* Poll the LM_OK bit to see when Integration Result is Ready */
		if (wait_level_metering_finish(duslic_id, channel)) {

			udelay(10000); /* wait at least 500us to be sure that the Integration Result are valid !!! */

			/* Now Read the LM Result Registers (They will hold their value until LM_EN become zero again */
			/*				    ==>After that Result Regs will be updated every 500us !!!) */
			LM_Result = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
			V_in = (-1) * ( ( (((long int)LM_Result) * V_AD_x10000) / N_SAMPLES) >> (15 - K_INTDC_RECT_OFF)) ;  /* Vin x 10000*/

			V_out = (V_in * Divider_Ratio) / 10000L ;	/* Vout x100 */

			if (V_out < 0)
				V_out= -V_out;

			if (V_out > MAX_V_RING_MEANx100)
				err_mask |= 8;

			*ring_mean_v = V_out;
		} else {
			err_mask |= 8;
			*ring_mean_v = 0;
		}
	} else {
		err_mask |= 8;
		*ring_mean_v = 0;
	}

	/* Disable LevelMeter ==> In order to be able to restart Integrator again (for the next integration) */
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR,
		LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_ONCE);
	codsp_write_sop_short(duslic_id, channel, OFR1_ADDR, 0x0000);

	codsp_set_slic(duslic_id, channel, SS_RING_PAUSE); /* Start Ringing */

	/* Now Enable Rectifier */
	/* select source for the levelmeter to be IO4-IO3 */
	codsp_write_sop_char(duslic_id, channel, LMCR2_ADDR,
		LMCR2_LM_SEL_IO4_MINUS_IO3 | LMCR2_LM_RECT);

	/* Program the apropriate shift factor K_INTDC (in order to avoid Overflow at Integtation Result !!!) */
	codsp_write_cop_char(duslic_id, channel, RING_PARAMS_START_ADDR + 7, K_INTDC_RECT_ON);

	udelay(40000);

	/* Reenable Level Meter Integrator (The Result will be valid after Integration Period=Ring Period and until LN_EN become zero again) */
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR,
			LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_EN | LMCR1_LM_ONCE);

	udelay(40000);

	/* Poll the LM_OK bit to see when Integration Result is Ready */
	if (wait_level_metering_finish(duslic_id, channel)) {

		udelay(10000);

		/* Now Read the LM Result Registers (They will hold their value until LM_EN become zero again */
		/*				    ==>After that Result Regs will be updated every 500us !!!) */
		Offset_Compensation = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
		Offset_Compensation = (-1) * ((Offset_Compensation * (1 << K_INTDC_RECT_ON)) / N_SAMPLES);

		/* Disable LevelMeter ==> In order to be able to restart Integrator again (for the next integration) */
		codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_ONCE);

		/* Now programm Integrator Offset Registers !!! */
		codsp_write_sop_short(duslic_id, channel, OFR1_ADDR, Offset_Compensation);

		/* Be sure that a Ring is generated !!!! */
		codsp_set_slic(duslic_id, channel, SS_RINGING); /* Start Ringing again */

		udelay(40000);

		/* Reenable Level Meter Integrator (The Result will be valid after Integration Period=Ring Period and until LN_EN become zero again) */
		codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR,
				LMCR1_LM_THM | LMCR1_LM_MASK | LMCR1_LM_EN | LMCR1_LM_ONCE);

		udelay(40000);

		/* Poll the LM_OK bit to see when Integration Result is Ready */
		if (wait_level_metering_finish(duslic_id, channel)) {

			udelay(10000);

			/* Now Read the LM Result Registers (They will hold their value until LM_EN become zero again */
			/*				    ==>After that Result Regs will be updated every 500us !!!) */
			LM_Result = codsp_read_sop_short(duslic_id, channel, LMRES1_ADDR);
			V_in = (-1) *  ( ( (((long int)LM_Result) * V_AD_x10000) / N_SAMPLES) >> (15 - K_INTDC_RECT_ON) ) ;  /* Vin x 10000*/

			V_out = (((V_in * Divider_Ratio) / 10000L) * RMS_MULTIPLIERx100) / 100 ;	/* Vout_RMS x100 */
			if (V_out < 0)
				V_out = -V_out;

			Vout_diff = (V_out - TARGET_V_RING_RMSx100);

			if (Vout_diff < 0)
				Vout_diff = -Vout_diff;

			if (Vout_diff > V_RMS_RING_MAX_DIFFx100)
				err_mask |= 16;

			*ring_rms_v = V_out;
		} else {
			err_mask |= 16;
			*ring_rms_v = 0;
		}
	} else {
		err_mask |= 16;
		*ring_rms_v = 0;
	}
	/* Disable LevelMeter ==> In order to be able to restart Integrator again (for the next integration) */
	codsp_write_sop_char(duslic_id, channel, LMCR1_ADDR, LMCR1_LM_THM | LMCR1_LM_MASK);

	retrieve_slic_state(slic_id);

	return(err_mask);
}

int test_dtmf(int slic_id)
{
	unsigned char code;
	unsigned char b;
	unsigned int intreg;
	int duslic_id = slic_id >> 1;
	int channel = slic_id & 1;

	for (code = 0; code < 16; code++) {
		b = codsp_read_sop_char(duslic_id, channel, DSCR_ADDR);
		codsp_write_sop_char(duslic_id, channel, DSCR_ADDR,
			(b & ~(DSCR_PTG | DSCR_DG_KEY(15))) | DSCR_DG_KEY(code) | DSCR_TG1_EN | DSCR_TG2_EN);
		udelay(80000);

		intreg = codsp_read_sop_int(duslic_id, channel, INTREG1_ADDR);
		if ((intreg & CODSP_INTREG_INT_CH) == 0)
			break;

		if ((intreg & CODSP_INTREG_DTMF_OK) == 0 ||
				codsp_dtmf_map[(intreg >> 10) & 15] != codsp_dtmf_map[code])
			break;

		b = codsp_read_sop_char(duslic_id, channel, DSCR_ADDR);
		codsp_write_sop_char(duslic_id, channel, DSCR_ADDR,
				b & ~(DSCR_COR8 | DSCR_TG1_EN | DSCR_TG2_EN));

		udelay(80000);

		intreg = codsp_read_sop_int(duslic_id, channel, INTREG1_ADDR); /* for dtmf_pause irq */
	}

	if (code != 16) {
		b = codsp_read_sop_char(duslic_id, channel, DSCR_ADDR); /* stop dtmf */
		codsp_write_sop_char(duslic_id, channel, DSCR_ADDR,
				b & ~(DSCR_COR8 | DSCR_TG1_EN | DSCR_TG2_EN));
		return(1);
	}

	return(0);
}

void data_up_persist_time(int duslic_id, int channel, int time_ms)
{
	unsigned char b;

	b = codsp_read_sop_char(duslic_id, channel, IOCTL3_ADDR);
	b = (b & 0x0F) | ((time_ms & 0x0F) << 4);
	codsp_write_sop_char(duslic_id, channel, IOCTL3_ADDR, b);
}

static void program_dtmf_params(int duslic_id, int channel)
{
	unsigned char b;

	codsp_write_pop_char(duslic_id, channel, DTMF_LEV_ADDR, 0x10);
	codsp_write_pop_char(duslic_id, channel, DTMF_TWI_ADDR, 0x0C);
	codsp_write_pop_char(duslic_id, channel, DTMF_NCF_H_ADDR, 0x79);
	codsp_write_pop_char(duslic_id, channel, DTMF_NCF_L_ADDR, 0x10);
	codsp_write_pop_char(duslic_id, channel, DTMF_NBW_H_ADDR, 0x02);
	codsp_write_pop_char(duslic_id, channel, DTMF_NBW_L_ADDR, 0xFB);
	codsp_write_pop_char(duslic_id, channel, DTMF_GAIN_ADDR, 0x91);
	codsp_write_pop_char(duslic_id, channel, DTMF_RES1_ADDR, 0x00);
	codsp_write_pop_char(duslic_id, channel, DTMF_RES2_ADDR, 0x00);
	codsp_write_pop_char(duslic_id, channel, DTMF_RES3_ADDR, 0x00);

	b = codsp_read_sop_char(duslic_id, channel, BCR5_ADDR);
	codsp_write_sop_char(duslic_id, channel, BCR5_ADDR, b | BCR5_DTMF_EN);
}

static void codsp_channel_full_reset(int duslic_id, int channel)
{

	program_coeffs(duslic_id, channel, ac_coeffs, sizeof(ac_coeffs) / sizeof(struct _coeffs));
	program_coeffs(duslic_id, channel, dc_coeffs, sizeof(dc_coeffs) / sizeof(struct _coeffs));

	/* program basic configuration registers */
	codsp_write_sop_char(duslic_id, channel, BCR1_ADDR, 0x01);
	codsp_write_sop_char(duslic_id, channel, BCR2_ADDR, 0x41);
	codsp_write_sop_char(duslic_id, channel, BCR3_ADDR, 0x43);
	codsp_write_sop_char(duslic_id, channel, BCR4_ADDR, 0x00);
	codsp_write_sop_char(duslic_id, channel, BCR5_ADDR, 0x00);

	codsp_write_sop_char(duslic_id, channel, DSCR_ADDR, 0x04);		/* PG */

	program_dtmf_params(duslic_id, channel);

	codsp_write_sop_char(duslic_id, channel, LMCR3_ADDR, 0x40);	/* RingTRip_SEL */

	data_up_persist_time(duslic_id, channel, 4);

	codsp_write_sop_char(duslic_id, channel, MASK_ADDR, 0xFF);     /* All interrupts masked */

	codsp_set_slic(duslic_id, channel, SS_ACTIVE_HIGH);
}

static int codsp_chip_full_reset(int duslic_id)
{
	int i, cnt;
	int intreg[NUM_CHANNELS];
	unsigned char pcm_resync;
	unsigned char revision;

	codsp_reset_chip(duslic_id);

	udelay(2000);

	for (i = 0; i < NUM_CHANNELS; i++)
		intreg[i] = codsp_read_sop_int(duslic_id, i, INTREG1_ADDR);

	udelay(1500);

	if (_PORTC_GET(com_hook_mask_tab[duslic_id]) == 0) {
		printf("_HOOK(%d) stayed low\n", duslic_id);
		return -1;
	}

	for (pcm_resync = 0, i = 0; i < NUM_CHANNELS; i++) {
		if (intreg[i] & CODSP_INTREG_SYNC_FAIL)
			pcm_resync |= 1 << i;
	}

	for (cnt = 0; cnt < 5 && pcm_resync; cnt++) {
		for (i = 0; i < NUM_CHANNELS; i++)
			codsp_resync_channel(duslic_id, i);

		udelay(2000);

		pcm_resync = 0;

		for (i = 0; i < NUM_CHANNELS; i++) {
			if (codsp_read_sop_int(duslic_id, i, INTREG1_ADDR) & CODSP_INTREG_SYNC_FAIL)
				pcm_resync |= 1 << i;
		}
	}

	if (cnt == 5) {
		printf("PCM_Resync(%u) not completed\n", duslic_id);
		return -2;
	}

	revision = codsp_read_sop_char(duslic_id, 0, REVISION_ADDR);
	printf("DuSLIC#%d hardware version %d.%d\r\n", duslic_id, (revision & 0xF0) >> 4, revision & 0x0F);

	codsp_write_sop_char(duslic_id, 0, XCR_ADDR, 0x80);	/* EDSP_EN */

	for (i = 0; i < NUM_CHANNELS; i++) {
		codsp_write_sop_char(duslic_id, i, PCMC1_ADDR, 0x01);
		codsp_channel_full_reset(duslic_id, i);
	}

	return 0;
}

int slic_self_test(int duslic_mask)
{
	int slic;
	int i;
	int r;
	long vdd, v_oh_H, v_oh_L, ring_mean_v, ring_rms_v;
	const char *err_txt[] = { "VDD", "V_OH_H", "V_OH_L", "V_RING_MEAN", "V_RING_RMS" };
	int error = 0;

	for (slic = 0; slic < MAX_SLICS; slic++) { /* voltages self test */
		if (duslic_mask & (1 << (slic >> 1))) {
			r = measure_on_hook_voltages(slic, &vdd,
				&v_oh_H, &v_oh_L, &ring_mean_v, &ring_rms_v);

			printf("SLIC %u measured voltages (x100):\n\t"
				    "VDD = %ld\tV_OH_H = %ld\tV_OH_L = %ld\tV_RING_MEAN = %ld\tV_RING_RMS = %ld\n",
				    slic, vdd, v_oh_H, v_oh_L, ring_mean_v, ring_rms_v);

			if (r != 0)
				error |= 1 << slic;

			for (i = 0; i < 5; i++)
				if (r & (1 << i))
					printf("\t%s out of range\n", err_txt[i]);
		}
	}

	for (slic = 0; slic < MAX_SLICS; slic++) { /* voice path self test */
		if (duslic_mask & (1 << (slic >> 1))) {
			printf("SLIC %u VOICE PATH...CHECKING", slic);
			printf("\rSLIC %u VOICE PATH...%s\n", slic,
				(r = test_dtmf(slic)) != 0 ? "FAILED  " : "PASSED  ");

			if (r != 0)
				error |= 1 << slic;
		}
	}

	return(error);
}

#if defined(CONFIG_NETTA_ISDN)

#define SPIENS1		(1 << (31 - 15))
#define SPIENS2		(1 << (31 - 19))

static const int spiens_mask_tab[2] = { SPIENS1, SPIENS2 };
int s_initialized = 0;

static inline unsigned int s_transfer_internal(int s_id, unsigned int address, unsigned int value)
{
	unsigned int rx, v;

	_PORTB_SET(spiens_mask_tab[s_id], 0);

	rx = __SPI_Transfer(address);

	switch (address & 0xF0) {
	case 0x60:	/* write byte register */
	case 0x70:
		rx = __SPI_Transfer(value);
		break;

	case 0xE0:	/* read R6 register */
		v = __SPI_Transfer(0);

		rx = (rx << 8) | v;

		break;

	case 0xF0:	/* read byte register */
		rx = __SPI_Transfer(0);

		break;
	}

	_PORTB_SET(spiens_mask_tab[s_id], 1);

	return rx;
}

static void s_write_BR(int s_id, unsigned int regno, unsigned int val)
{
	unsigned int address;
	unsigned int v;

	address = 0x70 | (regno & 15);
	val &= 0xff;

	v = s_transfer_internal(s_id, address, val);
}

static void s_write_OR(int s_id, unsigned int regno, unsigned int val)
{
	unsigned int address;
	unsigned int v;

	address = 0x70 | (regno & 15);
	val &= 0xff;

	v = s_transfer_internal(s_id, address, val);
}

static void s_write_NR(int s_id, unsigned int regno, unsigned int val)
{
	unsigned int address;
	unsigned int v;

	address = (regno & 7) << 4;
	val &= 0xf;

	v = s_transfer_internal(s_id, address | val, 0x00);
}

#define BR7_IFR			0x08	/* IDL2 free run */
#define BR7_ICSLSB		0x04	/* IDL2 clock speed LSB */

#define BR15_OVRL_REG_EN	0x80
#define OR7_D3VR		0x80	/* disable 3V regulator */

#define OR8_TEME		0x10	/* TE mode enable */
#define OR8_MME			0x08	/* master mode enable */

void s_initialize(void)
{
	int s_id;

	for (s_id = 0; s_id < 2; s_id++) {
		s_write_BR(s_id, 7, BR7_IFR | BR7_ICSLSB);
		s_write_BR(s_id, 15, BR15_OVRL_REG_EN);
		s_write_OR(s_id, 8, OR8_TEME | OR8_MME);
		s_write_OR(s_id, 7, OR7_D3VR);
		s_write_OR(s_id, 6, 0);
		s_write_BR(s_id, 15, 0);
		s_write_NR(s_id, 3, 0);
	}
}

#endif

int board_post_codec(int flags)
{
	int j;
	int r;
	int duslic_mask;

	printf("board_post_dsp\n");

#if defined(CONFIG_NETTA_ISDN)
	if (s_initialized == 0) {
		s_initialize();
		s_initialized = 1;

		printf("s_initialized\n");

		udelay(20000);
	}
#endif
	duslic_mask = 0;

	for (j = 0; j < MAX_DUSLIC; j++) {
		if (codsp_chip_full_reset(j) < 0)
			printf("Error initializing DuSLIC#%d\n", j);
		else
			duslic_mask |= 1 << j;
	}

	if (duslic_mask != 0) {
		printf("Testing SLICs...\n");

		r = slic_self_test(duslic_mask);
		for (j = 0; j < MAX_SLICS; j++) {
			if (duslic_mask & (1 << (j >> 1)))
				printf("SLIC %u...%s\n", j, r & (1 << j) ? "FAULTY" : "OK");
		}
	}
	printf("DuSLIC self test finished\n");

	return 0;	/* return -1 on error */
}