| /* |
| * 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 <s3c2400.h> |
| #include "tsc2000.h" |
| |
| void spi_init(void) |
| { |
| S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); |
| S3C24X0_SPI * const spi = S3C24X0_GetBase_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(); |
| } |
| |
| |
| static void spi_wait_transmit_done(void) |
| { |
| S3C24X0_SPI * const spi = S3C24X0_GetBase_SPI(); |
| |
| while (!(spi->ch[0].SPSTA & 0x01)); /* wait until transfer is done */ |
| } |
| |
| |
| static void tsc2000_write(unsigned short reg, unsigned short data) |
| { |
| S3C24X0_SPI * const spi = S3C24X0_GetBase_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(); |
| } |
| |
| |
| static unsigned short tsc2000_read (unsigned short reg) |
| { |
| unsigned short command, data; |
| S3C24X0_SPI * const spi = S3C24X0_GetBase_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); |
| } |
| |
| |
| static void tsc2000_set_mux (unsigned int channel) |
| { |
| S3C24X0_GPIO * const gpio = S3C24X0_GetBase_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; |
| } |
| } |
| |
| |
| static void tsc2000_set_range (unsigned int range) |
| { |
| S3C24X0_GPIO * const gpio = S3C24X0_GetBase_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; |
| } |
| } |
| |
| |
| static u16 tsc2000_read_channel (unsigned int channel) |
| { |
| u16 res; |
| |
| tsc2000_set_mux(channel); |
| udelay(3 * 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; |
| |
| |
| tsc2000_reg_init (); |
| tsc2000_set_range (3); |
| |
| adc_pt1000 = tsc2000_read_channel (14); |
| debug ("read channel 14 (pt1000 adc value): %ld\n", adc_pt1000); |
| |
| offset = tsc2000_read_channel (15); |
| 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) |
| { |
| S3C24X0_GPIO * const gpio = S3C24X0_GetBase_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); |
| } |
| |
| |
| static int tsc2000_interpolate(long value, long data[][2], long *result) |
| { |
| int i; |
| |
| /* 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'. |
| */ |
| |
| *result = data[i-1][1] + |
| ((unsigned long long)(data[i][1] - data[i-1][1]) |
| * (unsigned long long)(value - data[i-1][0])) |
| / (data[i][0] - data[i-1][0]); |
| |
| return 0; |
| } |
| |
| |
| static void adc_wait_conversion_done(void) |
| { |
| while (!(tsc2000_read(TSC2000_REG_ADC) & (1 << 14))); |
| } |