summaryrefslogtreecommitdiffstats
path: root/u-boot/board/trab/tsc2000.c
diff options
context:
space:
mode:
Diffstat (limited to 'u-boot/board/trab/tsc2000.c')
-rw-r--r--u-boot/board/trab/tsc2000.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/u-boot/board/trab/tsc2000.c b/u-boot/board/trab/tsc2000.c
new file mode 100644
index 0000000..426ed9c
--- /dev/null
+++ b/u-boot/board/trab/tsc2000.c
@@ -0,0 +1,366 @@
+/*
+ * Functions to access the TSC2000 controller on TRAB board (used for scanning
+ * thermo sensors)
+ *
+ * Copyright (C) 2003 Martin Krause, TQ-Systems GmbH, martin.krause@tqs.de
+ *
+ * Copyright (C) 2002 DENX Software Engineering, Wolfgang Denk, wd@denx.de
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/arch/s3c24x0_cpu.h>
+#include <asm/io.h>
+#include <div64.h>
+#include "tsc2000.h"
+
+#include "Pt1000_temp_data.h"
+
+/* helper function */
+#define abs(value) (((value) < 0) ? ((value)*-1) : (value))
+
+/*
+ * Maximal allowed deviation between two immediate meassurments of an analog
+ * thermo channel. 1 DIGIT = 0.0276 °C. This is used to filter sporadic
+ * "jumps" in measurment.
+ */
+#define MAX_DEVIATION 18 /* unit: DIGITs of adc; 18 DIGIT = 0.5 °C */
+
+void tsc2000_spi_init(void)
+{
+ struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
+ struct s3c24x0_spi * const spi = s3c24x0_get_base_spi();
+ int i;
+
+ /* Configure I/O ports. */
+ gpio->pdcon = (gpio->pdcon & 0xF3FFFF) | 0x040000;
+ gpio->pgcon = (gpio->pgcon & 0x0F3FFF) | 0x008000;
+ gpio->pgcon = (gpio->pgcon & 0x0CFFFF) | 0x020000;
+ gpio->pgcon = (gpio->pgcon & 0x03FFFF) | 0x080000;
+
+ CLR_CS_TOUCH();
+
+ spi->ch[0].sppre = 0x1F; /* Baud-rate ca. 514kHz */
+ spi->ch[0].sppin = 0x01; /* SPI-MOSI holds Level after last bit */
+ spi->ch[0].spcon = 0x1A; /* Polling, Prescaler, Master, CPOL=0,
+ CPHA=1 */
+
+ /* Dummy byte ensures clock to be low. */
+ for (i = 0; i < 10; i++) {
+ spi->ch[0].sptdat = 0xFF;
+ }
+ spi_wait_transmit_done();
+}
+
+
+void spi_wait_transmit_done(void)
+{
+ struct s3c24x0_spi * const spi = s3c24x0_get_base_spi();
+
+ while (!(spi->ch[0].spsta & 0x01)) /* wait until transfer is done */
+ ;
+}
+
+
+void tsc2000_write(unsigned short reg, unsigned short data)
+{
+ struct s3c24x0_spi * const spi = s3c24x0_get_base_spi();
+ unsigned int command;
+
+ SET_CS_TOUCH();
+ command = reg;
+ spi->ch[0].sptdat = (command & 0xFF00) >> 8;
+ spi_wait_transmit_done();
+ spi->ch[0].sptdat = (command & 0x00FF);
+ spi_wait_transmit_done();
+ spi->ch[0].sptdat = (data & 0xFF00) >> 8;
+ spi_wait_transmit_done();
+ spi->ch[0].sptdat = (data & 0x00FF);
+ spi_wait_transmit_done();
+
+ CLR_CS_TOUCH();
+}
+
+
+unsigned short tsc2000_read (unsigned short reg)
+{
+ unsigned short command, data;
+ struct s3c24x0_spi * const spi = s3c24x0_get_base_spi();
+
+ SET_CS_TOUCH();
+ command = 0x8000 | reg;
+
+ spi->ch[0].sptdat = (command & 0xFF00) >> 8;
+ spi_wait_transmit_done();
+ spi->ch[0].sptdat = (command & 0x00FF);
+ spi_wait_transmit_done();
+
+ spi->ch[0].sptdat = 0xFF;
+ spi_wait_transmit_done();
+ data = spi->ch[0].sprdat;
+ spi->ch[0].sptdat = 0xFF;
+ spi_wait_transmit_done();
+
+ CLR_CS_TOUCH();
+ return (spi->ch[0].sprdat & 0x0FF) | (data << 8);
+}
+
+
+void tsc2000_set_mux (unsigned int channel)
+{
+ struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
+
+ CLR_MUX1_ENABLE; CLR_MUX2_ENABLE;
+ CLR_MUX3_ENABLE; CLR_MUX4_ENABLE;
+ switch (channel) {
+ case 0:
+ CLR_MUX0; CLR_MUX1;
+ SET_MUX1_ENABLE;
+ break;
+ case 1:
+ SET_MUX0; CLR_MUX1;
+ SET_MUX1_ENABLE;
+ break;
+ case 2:
+ CLR_MUX0; SET_MUX1;
+ SET_MUX1_ENABLE;
+ break;
+ case 3:
+ SET_MUX0; SET_MUX1;
+ SET_MUX1_ENABLE;
+ break;
+ case 4:
+ CLR_MUX0; CLR_MUX1;
+ SET_MUX2_ENABLE;
+ break;
+ case 5:
+ SET_MUX0; CLR_MUX1;
+ SET_MUX2_ENABLE;
+ break;
+ case 6:
+ CLR_MUX0; SET_MUX1;
+ SET_MUX2_ENABLE;
+ break;
+ case 7:
+ SET_MUX0; SET_MUX1;
+ SET_MUX2_ENABLE;
+ break;
+ case 8:
+ CLR_MUX0; CLR_MUX1;
+ SET_MUX3_ENABLE;
+ break;
+ case 9:
+ SET_MUX0; CLR_MUX1;
+ SET_MUX3_ENABLE;
+ break;
+ case 10:
+ CLR_MUX0; SET_MUX1;
+ SET_MUX3_ENABLE;
+ break;
+ case 11:
+ SET_MUX0; SET_MUX1;
+ SET_MUX3_ENABLE;
+ break;
+ case 12:
+ CLR_MUX0; CLR_MUX1;
+ SET_MUX4_ENABLE;
+ break;
+ case 13:
+ SET_MUX0; CLR_MUX1;
+ SET_MUX4_ENABLE;
+ break;
+ case 14:
+ CLR_MUX0; SET_MUX1;
+ SET_MUX4_ENABLE;
+ break;
+ case 15:
+ SET_MUX0; SET_MUX1;
+ SET_MUX4_ENABLE;
+ break;
+ default:
+ CLR_MUX0; CLR_MUX1;
+ }
+}
+
+
+void tsc2000_set_range (unsigned int range)
+{
+ struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
+
+ switch (range) {
+ case 1:
+ CLR_SEL_TEMP_V_0; SET_SEL_TEMP_V_1;
+ CLR_SEL_TEMP_V_2; CLR_SEL_TEMP_V_3;
+ break;
+ case 2:
+ CLR_SEL_TEMP_V_0; CLR_SEL_TEMP_V_1;
+ CLR_SEL_TEMP_V_2; SET_SEL_TEMP_V_3;
+ break;
+ case 3:
+ SET_SEL_TEMP_V_0; CLR_SEL_TEMP_V_1;
+ SET_SEL_TEMP_V_2; CLR_SEL_TEMP_V_3;
+ break;
+ }
+}
+
+
+u16 tsc2000_read_channel (unsigned int channel)
+{
+ u16 res;
+
+ tsc2000_set_mux(channel);
+ udelay(20 * TSC2000_DELAY_BASE);
+
+ tsc2000_write(TSC2000_REG_ADC, 0x2036);
+ adc_wait_conversion_done ();
+ res = tsc2000_read(TSC2000_REG_AUX1);
+ return res;
+}
+
+
+s32 tsc2000_contact_temp (void)
+{
+ long adc_pt1000, offset;
+ long u_pt1000;
+ long contact_temp;
+ long temp1, temp2;
+
+ tsc2000_reg_init ();
+ tsc2000_set_range (3);
+
+ /*
+ * Because of sporadic "jumps" in the measured adc values every
+ * channel is read two times. If there is a significant difference
+ * between the two measurements, then print an error and do a third
+ * measurement, because it is very unlikely that a successive third
+ * measurement goes also wrong.
+ */
+ temp1 = tsc2000_read_channel (14);
+ temp2 = tsc2000_read_channel (14);
+ if (abs(temp2 - temp1) < MAX_DEVIATION)
+ adc_pt1000 = temp2;
+ else {
+ printf ("%s: read adc value (channel 14) exceeded max allowed "
+ "deviation: %d * 0.0276 °C\n",
+ __FUNCTION__, MAX_DEVIATION);
+ printf ("adc value 1: %ld DIGITs\nadc value 2: %ld DIGITs\n",
+ temp1, temp2);
+ adc_pt1000 = tsc2000_read_channel (14);
+ printf ("use (third read) adc value: adc_pt1000 = "
+ "%ld DIGITs\n", adc_pt1000);
+ }
+ debug ("read channel 14 (pt1000 adc value): %ld\n", adc_pt1000);
+
+ temp1 = tsc2000_read_channel (15);
+ temp2 = tsc2000_read_channel (15);
+ if (abs(temp2 - temp1) < MAX_DEVIATION)
+ offset = temp2;
+ else {
+ printf ("%s: read adc value (channel 15) exceeded max allowed "
+ "deviation: %d * 0.0276 °C\n",
+ __FUNCTION__, MAX_DEVIATION);
+ printf ("adc value 1: %ld DIGITs\nadc value 2: %ld DIGITs\n",
+ temp1, temp2);
+ offset = tsc2000_read_channel (15);
+ printf ("use (third read) adc value: offset = %ld DIGITs\n",
+ offset);
+ }
+ debug ("read channel 15 (offset): %ld\n", offset);
+
+ /*
+ * Formula for calculating voltage drop on PT1000 resistor: u_pt1000 =
+ * x_range3 * (adc_raw - offset) / 10. Formula to calculate x_range3:
+ * x_range3 = (2500 * (1000000 + err_vref + err_amp3)) / (4095*6). The
+ * error correction Values err_vref and err_amp3 are assumed as 0 in
+ * u-boot, because this could cause only a very small error (< 1%).
+ */
+ u_pt1000 = (101750 * (adc_pt1000 - offset)) / 10;
+ debug ("u_pt1000: %ld\n", u_pt1000);
+
+ if (tsc2000_interpolate(u_pt1000, Pt1000_temp_table,
+ &contact_temp) == -1) {
+ printf ("%s: error interpolating PT1000 vlaue\n",
+ __FUNCTION__);
+ return (-1000);
+ }
+ debug ("contact_temp: %ld\n", contact_temp);
+
+ return contact_temp;
+}
+
+
+void tsc2000_reg_init (void)
+{
+ struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
+
+ tsc2000_write(TSC2000_REG_ADC, 0x2036);
+ tsc2000_write(TSC2000_REG_REF, 0x0011);
+ tsc2000_write(TSC2000_REG_DACCTL, 0x0000);
+
+ CON_MUX0;
+ CON_MUX1;
+
+ CON_MUX1_ENABLE;
+ CON_MUX2_ENABLE;
+ CON_MUX3_ENABLE;
+ CON_MUX4_ENABLE;
+
+ CON_SEL_TEMP_V_0;
+ CON_SEL_TEMP_V_1;
+ CON_SEL_TEMP_V_2;
+ CON_SEL_TEMP_V_3;
+
+ tsc2000_set_mux(0);
+ tsc2000_set_range(0);
+}
+
+
+int tsc2000_interpolate(long value, long data[][2], long *result)
+{
+ int i;
+ unsigned long long val;
+
+ /* the data is sorted and the first element is upper
+ * limit so we can easily check for out-of-band values
+ */
+ if (data[0][0] < value || data[1][0] > value)
+ return -1;
+
+ i = 1;
+ while (data[i][0] < value)
+ i++;
+
+ /* To prevent overflow we have to store the intermediate
+ result in 'long long'.
+ */
+
+ val = ((unsigned long long)(data[i][1] - data[i-1][1])
+ * (unsigned long long)(value - data[i-1][0]));
+ do_div(val, (data[i][0] - data[i-1][0]));
+ *result = data[i-1][1] + val;
+
+ return 0;
+}
+
+
+void adc_wait_conversion_done(void)
+{
+ while (!(tsc2000_read(TSC2000_REG_ADC) & (1 << 14)));
+}