Home | History | Annotate | Line # | Download | only in s3c2xx0
      1  1.1  nisimura /*-
      2  1.1  nisimura  * Copyright (c) 2012 The NetBSD Foundation, Inc.
      3  1.1  nisimura  * All rights reserved.
      4  1.1  nisimura  *
      5  1.1  nisimura  * This code is derived from software contributed to The NetBSD Foundation
      6  1.1  nisimura  * by Paul Fleischer <paul (at) xpg.dk>
      7  1.1  nisimura  *
      8  1.1  nisimura  * Redistribution and use in source and binary forms, with or without
      9  1.1  nisimura  * modification, are permitted provided that the following conditions
     10  1.1  nisimura  * are met:
     11  1.1  nisimura  * 1. Redistributions of source code must retain the above copyright
     12  1.1  nisimura  *    notice, this list of conditions and the following disclaimer.
     13  1.1  nisimura  * 2. Redistributions in binary form must reproduce the above copyright
     14  1.1  nisimura  *    notice, this list of conditions and the following disclaimer in the
     15  1.1  nisimura  *    documentation and/or other materials provided with the distribution.
     16  1.1  nisimura  *
     17  1.1  nisimura  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     18  1.1  nisimura  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     19  1.1  nisimura  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20  1.1  nisimura  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     21  1.1  nisimura  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  1.1  nisimura  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  1.1  nisimura  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24  1.1  nisimura  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25  1.1  nisimura  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26  1.1  nisimura  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27  1.1  nisimura  * POSSIBILITY OF SUCH DAMAGE.
     28  1.1  nisimura  */
     29  1.1  nisimura #include <sys/cdefs.h>
     30  1.1  nisimura 
     31  1.1  nisimura #include <sys/param.h>
     32  1.1  nisimura #include <sys/systm.h>
     33  1.1  nisimura #include <sys/conf.h>
     34  1.1  nisimura #include <sys/callout.h>
     35  1.1  nisimura #include <sys/kernel.h>
     36  1.1  nisimura 
     37  1.1  nisimura #include <sys/bus.h>
     38  1.1  nisimura 
     39  1.1  nisimura #include <arm/s3c2xx0/s3c24x0var.h>
     40  1.1  nisimura #include <arm/s3c2xx0/s3c2440var.h>
     41  1.1  nisimura #include <arm/s3c2xx0/s3c2440reg.h>
     42  1.1  nisimura 
     43  1.1  nisimura #include <dev/wscons/wsconsio.h>
     44  1.1  nisimura #include <dev/wscons/wsmousevar.h>
     45  1.1  nisimura #include <dev/wscons/tpcalibvar.h>
     46  1.1  nisimura 
     47  1.1  nisimura #include <lib/libsa/qsort.c>
     48  1.1  nisimura 
     49  1.1  nisimura #include <dev/hpc/hpcfbio.h>
     50  1.1  nisimura 
     51  1.1  nisimura #define MAX_SAMPLES 20
     52  1.1  nisimura 
     53  1.1  nisimura struct sstouch_softc {
     54  1.1  nisimura 	device_t		dev;
     55  1.1  nisimura 
     56  1.1  nisimura 	bus_space_tag_t		iot;
     57  1.1  nisimura 	bus_space_handle_t	ioh;
     58  1.1  nisimura 
     59  1.1  nisimura 	uint32_t		next_stylus_intr;
     60  1.1  nisimura 
     61  1.1  nisimura 	device_t		wsmousedev;
     62  1.1  nisimura 
     63  1.1  nisimura 	struct tpcalib_softc	tpcalib;
     64  1.1  nisimura 
     65  1.1  nisimura 	int			sample_count;
     66  1.1  nisimura 	int			samples_x[MAX_SAMPLES];
     67  1.1  nisimura 	int			samples_y[MAX_SAMPLES];
     68  1.1  nisimura 
     69  1.1  nisimura 	callout_t		callout;
     70  1.1  nisimura };
     71  1.1  nisimura 
     72  1.1  nisimura /* Basic Driver Stuff */
     73  1.2       chs static int	sstouch_match	(device_t, cfdata_t, void *);
     74  1.2       chs static void	sstouch_attach	(device_t, device_t, void *);
     75  1.1  nisimura 
     76  1.1  nisimura CFATTACH_DECL_NEW(sstouch, sizeof(struct sstouch_softc), sstouch_match,
     77  1.1  nisimura 	      sstouch_attach, NULL, NULL);
     78  1.1  nisimura 
     79  1.1  nisimura /* wsmousedev */
     80  1.1  nisimura int	sstouch_enable(void *);
     81  1.1  nisimura int	sstouch_ioctl(void *, u_long, void *, int, struct lwp *);
     82  1.1  nisimura void	sstouch_disable(void *);
     83  1.1  nisimura 
     84  1.1  nisimura const struct wsmouse_accessops sstouch_accessops = {
     85  1.1  nisimura 	sstouch_enable,
     86  1.1  nisimura 	sstouch_ioctl,
     87  1.1  nisimura 	sstouch_disable
     88  1.1  nisimura };
     89  1.1  nisimura 
     90  1.1  nisimura /* Interrupt Handlers */
     91  1.1  nisimura int sstouch_tc_intr(void *arg);
     92  1.1  nisimura int sstouch_adc_intr(void *arg);
     93  1.1  nisimura 
     94  1.1  nisimura void sstouch_callout(void *arg);
     95  1.1  nisimura int sstouch_filter_values(int *vals, int val_count);
     96  1.1  nisimura void sstouch_initialize(struct sstouch_softc *sc);
     97  1.1  nisimura 
     98  1.1  nisimura #define STYLUS_DOWN	0
     99  1.1  nisimura #define STYLUS_UP	ADCTSC_UD_SEN
    100  1.1  nisimura 
    101  1.1  nisimura static struct wsmouse_calibcoords default_calib = {
    102  1.1  nisimura 	.minx = 0,
    103  1.1  nisimura 	.miny = 0,
    104  1.1  nisimura 	.maxx = 0,
    105  1.1  nisimura 	.maxy = 0,
    106  1.1  nisimura 	.samplelen = WSMOUSE_CALIBCOORDS_RESET
    107  1.1  nisimura };
    108  1.1  nisimura 
    109  1.1  nisimura /* IMPLEMENTATION PART */
    110  1.1  nisimura int
    111  1.2       chs sstouch_match(device_t parent, cfdata_t match, void *aux)
    112  1.1  nisimura {
    113  1.1  nisimura 	/* XXX: Check CPU type? */
    114  1.1  nisimura 	return 1;
    115  1.1  nisimura }
    116  1.1  nisimura 
    117  1.1  nisimura void
    118  1.2       chs sstouch_attach(device_t parent, device_t self, void *aux)
    119  1.1  nisimura {
    120  1.1  nisimura 	struct sstouch_softc		*sc = device_private(self);
    121  1.2       chs 	struct s3c2xx0_attach_args	*sa = aux;
    122  1.1  nisimura 	struct wsmousedev_attach_args	mas;
    123  1.1  nisimura 
    124  1.1  nisimura 	sc->dev = self;
    125  1.1  nisimura 	sc->iot = sa->sa_iot;
    126  1.1  nisimura 
    127  1.1  nisimura 	if (bus_space_map(sc->iot, S3C2440_ADC_BASE,
    128  1.1  nisimura 			  S3C2440_ADC_SIZE, 0, &sc->ioh)) {
    129  1.1  nisimura 		aprint_error(": failed to map registers");
    130  1.1  nisimura 		return;
    131  1.1  nisimura 	}
    132  1.1  nisimura 
    133  1.1  nisimura 	sc->next_stylus_intr = STYLUS_DOWN;
    134  1.1  nisimura 
    135  1.1  nisimura 
    136  1.1  nisimura 	/* XXX: Is IPL correct? */
    137  1.1  nisimura 	s3c24x0_intr_establish(S3C2440_INT_TC, IPL_BIO, IST_EDGE_RISING,
    138  1.1  nisimura 			       sstouch_tc_intr, sc);
    139  1.1  nisimura 	s3c24x0_intr_establish(S3C2440_INT_ADC, IPL_BIO, IST_EDGE_RISING,
    140  1.1  nisimura 			       sstouch_adc_intr, sc);
    141  1.1  nisimura 
    142  1.1  nisimura 	aprint_normal("\n");
    143  1.1  nisimura 
    144  1.1  nisimura 	mas.accessops = &sstouch_accessops;
    145  1.1  nisimura 	mas.accesscookie = sc;
    146  1.1  nisimura 
    147  1.4   thorpej 	sc->wsmousedev = config_found(self, &mas, wsmousedevprint, CFARGS_NONE);
    148  1.1  nisimura 
    149  1.1  nisimura 	tpcalib_init(&sc->tpcalib);
    150  1.1  nisimura 	tpcalib_ioctl(&sc->tpcalib, WSMOUSEIO_SCALIBCOORDS,
    151  1.1  nisimura 		      (void*)&default_calib, 0, 0);
    152  1.1  nisimura 
    153  1.1  nisimura 	sc->sample_count = 0;
    154  1.1  nisimura 
    155  1.1  nisimura 	/* Add CALLOUT_MPSAFE to avoid holding the global kernel lock */
    156  1.1  nisimura 	callout_init(&sc->callout, 0);
    157  1.1  nisimura 	callout_setfunc(&sc->callout, sstouch_callout, sc);
    158  1.1  nisimura 
    159  1.1  nisimura 	/* Actual initialization is performed by sstouch_initialize(),
    160  1.1  nisimura 	   which is called by sstouch_enable() */
    161  1.1  nisimura }
    162  1.1  nisimura 
    163  1.1  nisimura /* sstouch_tc_intr is the TC interrupt handler.
    164  1.1  nisimura    The TC interrupt is generated when the stylus changes up->down,
    165  1.1  nisimura    or down->up state (depending on configuration of ADC_ADCTSC).
    166  1.1  nisimura */
    167  1.1  nisimura int
    168  1.1  nisimura sstouch_tc_intr(void *arg)
    169  1.1  nisimura {
    170  1.1  nisimura 	struct sstouch_softc *sc = (struct sstouch_softc*)arg;
    171  1.1  nisimura 	uint32_t reg;
    172  1.1  nisimura 
    173  1.1  nisimura 	/*aprint_normal("%s\n", __func__);*/
    174  1.1  nisimura 
    175  1.1  nisimura 	/* Figure out if the stylus was lifted or lowered */
    176  1.1  nisimura 	reg = bus_space_read_4(sc->iot, sc->ioh, ADC_ADCUPDN);
    177  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCUPDN, 0x0);
    178  1.1  nisimura 	if( sc->next_stylus_intr == STYLUS_DOWN && (reg & ADCUPDN_TSC_DN) ) {
    179  1.1  nisimura 		sc->next_stylus_intr = STYLUS_UP;
    180  1.1  nisimura 
    181  1.1  nisimura 		sstouch_callout(sc);
    182  1.1  nisimura 
    183  1.1  nisimura 	} else if (sc->next_stylus_intr == STYLUS_UP && (reg & ADCUPDN_TSC_UP)) {
    184  1.1  nisimura 		uint32_t adctsc = 0;
    185  1.1  nisimura 		sc->next_stylus_intr = STYLUS_DOWN;
    186  1.1  nisimura 
    187  1.1  nisimura 		wsmouse_input(sc->wsmousedev, 0x0, 0, 0, 0, 0, 0);
    188  1.1  nisimura 
    189  1.1  nisimura 		sc->sample_count = 0;
    190  1.1  nisimura 
    191  1.1  nisimura 		adctsc |= ADCTSC_YM_SEN | ADCTSC_YP_SEN | ADCTSC_XP_SEN |
    192  1.1  nisimura 			sc->next_stylus_intr |
    193  1.1  nisimura 			3; /* 3 selects "Waiting for Interrupt Mode" */
    194  1.1  nisimura 		bus_space_write_4(sc->iot, sc->ioh, ADC_ADCTSC, adctsc);
    195  1.1  nisimura 	}
    196  1.1  nisimura 
    197  1.1  nisimura 	return 1;
    198  1.1  nisimura }
    199  1.1  nisimura 
    200  1.1  nisimura /* sstouch_adc_intr is ADC interrupt handler.
    201  1.1  nisimura    ADC interrupt is triggered when the ADC controller has a measurement ready.
    202  1.1  nisimura */
    203  1.1  nisimura int
    204  1.1  nisimura sstouch_adc_intr(void *arg)
    205  1.1  nisimura {
    206  1.1  nisimura 	struct sstouch_softc *sc = (struct sstouch_softc*)arg;
    207  1.1  nisimura 	uint32_t reg;
    208  1.1  nisimura 	uint32_t adctsc = 0;
    209  1.1  nisimura 	int x, y;
    210  1.1  nisimura 
    211  1.1  nisimura 	reg = bus_space_read_4(sc->iot, sc->ioh, ADC_ADCDAT0);
    212  1.1  nisimura 	y = reg & ADCDAT_DATAMASK;
    213  1.1  nisimura 
    214  1.1  nisimura 	reg = bus_space_read_4(sc->iot, sc->ioh, ADC_ADCDAT1);
    215  1.1  nisimura 	x = reg & ADCDAT_DATAMASK;
    216  1.1  nisimura 
    217  1.1  nisimura 
    218  1.1  nisimura 	sc->samples_x[sc->sample_count] = x;
    219  1.1  nisimura 	sc->samples_y[sc->sample_count] = y;
    220  1.1  nisimura 
    221  1.1  nisimura 	sc->sample_count++;
    222  1.1  nisimura 
    223  1.1  nisimura 	x = sstouch_filter_values(sc->samples_x, sc->sample_count);
    224  1.1  nisimura 	y = sstouch_filter_values(sc->samples_y, sc->sample_count);
    225  1.1  nisimura 
    226  1.1  nisimura 	if (x == -1 || y == -1) {
    227  1.1  nisimura 		/* If we do not have enough measurements, make some more. */
    228  1.1  nisimura 		sstouch_callout(sc);
    229  1.1  nisimura 		return 1;
    230  1.1  nisimura 	}
    231  1.1  nisimura 
    232  1.1  nisimura 	sc->sample_count = 0;
    233  1.1  nisimura 
    234  1.1  nisimura 	tpcalib_trans(&sc->tpcalib, x, y, &x, &y);
    235  1.1  nisimura 
    236  1.1  nisimura 	wsmouse_input(sc->wsmousedev, 0x1, x, y, 0, 0,
    237  1.1  nisimura 		      WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
    238  1.1  nisimura 
    239  1.1  nisimura 	/* Schedule a new adc measurement, unless the stylus has been lifed */
    240  1.1  nisimura 	if (sc->next_stylus_intr == STYLUS_UP) {
    241  1.1  nisimura 		callout_schedule(&sc->callout, hz/50);
    242  1.1  nisimura 	}
    243  1.1  nisimura 
    244  1.1  nisimura 	/* Until measurement is to be performed, listen for stylus up-events */
    245  1.1  nisimura 	adctsc |= ADCTSC_YM_SEN | ADCTSC_YP_SEN | ADCTSC_XP_SEN |
    246  1.1  nisimura 		sc->next_stylus_intr |
    247  1.1  nisimura 		3; /* 3 selects "Waiting for Interrupt Mode" */
    248  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCTSC, adctsc);
    249  1.1  nisimura 
    250  1.1  nisimura 
    251  1.1  nisimura 	return 1;
    252  1.1  nisimura }
    253  1.1  nisimura 
    254  1.1  nisimura int
    255  1.1  nisimura sstouch_enable(void *arg)
    256  1.1  nisimura {
    257  1.1  nisimura 	struct sstouch_softc *sc = arg;
    258  1.1  nisimura 
    259  1.1  nisimura 	sstouch_initialize(sc);
    260  1.1  nisimura 
    261  1.1  nisimura 	return 0;
    262  1.1  nisimura }
    263  1.1  nisimura 
    264  1.1  nisimura int
    265  1.1  nisimura sstouch_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
    266  1.1  nisimura {
    267  1.1  nisimura 	struct sstouch_softc *sc = v;
    268  1.1  nisimura 
    269  1.1  nisimura 	aprint_normal("%s\n", __func__);
    270  1.1  nisimura 
    271  1.1  nisimura 	switch (cmd) {
    272  1.1  nisimura 	case WSMOUSEIO_GTYPE:
    273  1.1  nisimura 		*(uint *)data = WSMOUSE_TYPE_PSEUDO;
    274  1.1  nisimura 		break;
    275  1.1  nisimura 	case WSMOUSEIO_GCALIBCOORDS:
    276  1.1  nisimura 	case WSMOUSEIO_SCALIBCOORDS:
    277  1.1  nisimura 		return tpcalib_ioctl(&sc->tpcalib, cmd, data, flag, l);
    278  1.1  nisimura 	default:
    279  1.1  nisimura 		return EPASSTHROUGH;
    280  1.1  nisimura 	}
    281  1.1  nisimura 
    282  1.1  nisimura 	return 0;
    283  1.1  nisimura }
    284  1.1  nisimura 
    285  1.1  nisimura void
    286  1.1  nisimura sstouch_disable(void *arg)
    287  1.1  nisimura {
    288  1.1  nisimura 	struct sstouch_softc *sc = (struct sstouch_softc*)arg;
    289  1.1  nisimura 
    290  1.1  nisimura 	/* By setting ADCCON register to 0, we also disable
    291  1.1  nisimura 	   the prescaler, which should disable any interrupts.
    292  1.1  nisimura 	 */
    293  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCCON, 0);
    294  1.1  nisimura }
    295  1.1  nisimura 
    296  1.1  nisimura void
    297  1.1  nisimura sstouch_callout(void *arg)
    298  1.1  nisimura {
    299  1.1  nisimura 	struct sstouch_softc *sc = (struct sstouch_softc*)arg;
    300  1.1  nisimura 
    301  1.1  nisimura 	/* If stylus is down, perform a measurement */
    302  1.1  nisimura 	if (sc->next_stylus_intr == STYLUS_UP) {
    303  1.1  nisimura 		uint32_t reg;
    304  1.1  nisimura 		bus_space_write_4(sc->iot, sc->ioh, ADC_ADCTSC,
    305  1.1  nisimura 				  ADCTSC_YM_SEN | ADCTSC_YP_SEN |
    306  1.1  nisimura 				  ADCTSC_XP_SEN | ADCTSC_PULL_UP |
    307  1.1  nisimura 				  ADCTSC_AUTO_PST);
    308  1.1  nisimura 
    309  1.1  nisimura 		reg = bus_space_read_4(sc->iot, sc->ioh, ADC_ADCCON);
    310  1.1  nisimura 		bus_space_write_4(sc->iot, sc->ioh, ADC_ADCCON,
    311  1.1  nisimura 				  reg | ADCCON_ENABLE_START);
    312  1.1  nisimura 	}
    313  1.1  nisimura 
    314  1.1  nisimura }
    315  1.1  nisimura 
    316  1.1  nisimura /* Do some very simple filtering on the measured values */
    317  1.1  nisimura int
    318  1.1  nisimura sstouch_filter_values(int *vals, int val_count)
    319  1.1  nisimura {
    320  1.1  nisimura 	int sum = 0;
    321  1.1  nisimura 
    322  1.1  nisimura 	if (val_count < 5)
    323  1.1  nisimura 		return -1;
    324  1.1  nisimura 
    325  1.1  nisimura 	for (int i=0; i<val_count; i++) {
    326  1.1  nisimura 		sum += vals[i];
    327  1.1  nisimura 	}
    328  1.1  nisimura 
    329  1.1  nisimura 	return sum/val_count;
    330  1.1  nisimura }
    331  1.1  nisimura 
    332  1.1  nisimura void
    333  1.1  nisimura sstouch_initialize(struct sstouch_softc *sc)
    334  1.1  nisimura {
    335  1.1  nisimura 	int prescaler;
    336  1.1  nisimura 	uint32_t adccon = 0;
    337  1.1  nisimura 	uint32_t adctsc = 0;
    338  1.1  nisimura 
    339  1.1  nisimura 	/* ADC Conversion rate is calculated by:
    340  1.1  nisimura 	   f(ADC) = PCLK/(prescaler+1)
    341  1.1  nisimura 
    342  1.1  nisimura 	   The ADC can operate at a maximum frequency of 2.5MHz for
    343  1.1  nisimura 	   500 KSPS.
    344  1.1  nisimura 	*/
    345  1.1  nisimura 
    346  1.1  nisimura 	/* Set f(ADC) = 50MHz / 256 = 1,95MHz */
    347  1.1  nisimura 	prescaler = 0xff;
    348  1.1  nisimura 
    349  1.1  nisimura 	adccon |= ((prescaler<<ADCCON_PRSCVL_SHIFT) &
    350  1.1  nisimura 		   ADCCON_PRSCVL_MASK);
    351  1.1  nisimura 	adccon |= ADCCON_PRSCEN;
    352  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCCON, adccon);
    353  1.1  nisimura 
    354  1.1  nisimura 	/* Use Auto Sequential measurement of X and Y positions */
    355  1.1  nisimura 	adctsc |= ADCTSC_YM_SEN | ADCTSC_YP_SEN | ADCTSC_XP_SEN |
    356  1.1  nisimura 		sc->next_stylus_intr |
    357  1.1  nisimura 		3; /* 3 selects "Waiting for Interrupt Mode" */
    358  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCTSC, adctsc);
    359  1.1  nisimura 
    360  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCUPDN, 0x0);
    361  1.1  nisimura 
    362  1.1  nisimura 	/* Time used to measure each X/Y position value? */
    363  1.1  nisimura 	bus_space_write_4(sc->iot, sc->ioh, ADC_ADCDLY, 10000);
    364  1.1  nisimura }
    365