rendered paste body/* * TSC2200 SSP communication driver * * Copyright (C) 2006 Paul Sokolovsky * Copyright (C) 2005-2006 Pawel Kolodziejski * Copyright (C) 2003 Joshua Wise * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * */#include <linux/module.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/adc.h>#include <linux/soc/tsc2200.h>#include <asm/arch/hardware.h>#include <asm/irq.h>#include <asm/arch/pxa-regs.h>#include <asm/arch/ssp.h>unsigned char pin_ctrl_cmd[] = { TSC2200_DATAREG_X, TSC2200_DATAREG_Y, TSC2200_DATAREG_Z1, TSC2200_DATAREG_Z2, TSC2200_DATAREG_BAT1, TSC2200_DATAREG_BAT2, TSC2200_DATAREG_AUX1, TSC2200_DATAREG_AUX2, TSC2200_DATAREG_TEMP1, TSC2200_DATAREG_TEMP2,};int tsc2200_sense(struct device *dev, struct adc_request *req){ struct tsc2200_ssp_platform_data *params = dev->platform_data; struct tsc2200_ts_data *devdata = dev->platform_data; int i; if (req->num_senses <= 0) return -EINVAL; down(&dev->sem); for (i = 0; i < req->num_senses; i++) { int pin_id = req->senses[i].pin_id; /* If this is custom pin, try to mux it in */ if (pin_id >= TSC2200_PIN_CUSTOM_START && params->mux_custom) { pin_id = params->mux_custom(pin_id); } if (pin_id < 0) { req->senses[i].value = (unsigned)-1; } else { tsc2200_write(devdata->tsc2200_dev, TSC2200_CTRLREG_ADC, TSC2200_CTRLREG_ADC_AD1 | TSC2200_CTRLREG_ADC_RES (TSC2200_CTRLREG_ADC_RES_12BIT) | TSC2200_CTRLREG_ADC_AVG (TSC2200_CTRLREG_ADC_16AVG) | TSC2200_CTRLREG_ADC_CL (TSC2200_CTRLREG_ADC_CL_4MHZ_10BIT) | TSC2200_CTRLREG_ADC_PV (TSC2200_CTRLREG_ADC_PV_1mS) ); req->senses[i].value = tsc2200_read(devdata->tsc2200_dev, pin_ctrl_cmd[pin_id]); } } /* Reset PD0&1, put chip to sleep */ tsc2200_stop(devdata->tsc2200_dev); up(&dev->sem); return 0;}EXPORT_SYMBOL(tsc2200_sense);static void tsc2200_ssp_init(int freq){}static struct adc_classdev acdevs[] = { { .name = "x", .pin_id = TSC2200_PIN_XPOS, .units = "coordinate", }, { .name = "y", .pin_id = TSC2200_PIN_YPOS, .units = "coordinate", }, { .name = "z1", .pin_id = TSC2200_PIN_Z1POS, .units = "coordinate", }, { .name = "z2", .pin_id = TSC2200_PIN_Z2POS, .units = "coordinate", }, { .name = "temp1", .pin_id = TSC2200_PIN_TEMP1, }, { .name = "temp2", .pin_id = TSC2200_PIN_TEMP2, }, { .name = "vbat1", .pin_id = TSC2200_PIN_BAT1, }, { .name = "vbat2", .pin_id = TSC2200_PIN_BAT2, }, { .name = "vaux1", .pin_id = TSC2200_PIN_AUX1, }, { .name = "vaux2", .pin_id = TSC2200_PIN_AUX2, },};static int __devinit tsc2200_ssp_probe(struct platform_device *dev){ struct tsc2200_ssp_platform_data *params = dev->dev.platform_data; int ret = 0; size_t i, j; platform_set_drvdata(dev, params); int port = params->port; printk(KERN_INFO "tsc2200_ssp: Using SSP%d\n", port); if (port < 1 || port > 3) { printk(KERN_ERR "tsc2200_ssp: Invalid port\n"); return -EINVAL; } //pxa_set_cken(CKEN_SSP(port), 1); /* ~1.8Mhz - max frequency for PXA25x */ tsc2200_ssp_init(params->freq ? params->freq : 1800000); for (i = 0; i < ARRAY_SIZE(acdevs); i++) { acdevs[i].sense = tsc2200_sense; ret = adc_classdev_register(&dev->dev, &acdevs[i]); if (ret) { printk("tsc2200_adc_ssp: failed to register " "class device %s", acdevs[i].name); goto adc_cdev_register_failed; } } for (j = 0; j < params->num_custom_adcs; j++) { params->custom_adcs[j].sense = tsc2200_sense; ret = adc_classdev_register(&dev->dev, ¶ms->custom_adcs[j]); if (ret) { printk("tsc2200_adc_ssp: failed to register " "class device %s", params->custom_adcs[j].name); goto plat_adc_cdev_register_failed; } } goto success;plat_adc_cdev_register_failed: while (--j >= 0) adc_classdev_unregister(¶ms->custom_adcs[j]);adc_cdev_register_failed: while (--i >= 0) adc_classdev_unregister(&acdevs[i]);success: return ret;}static int __devexit tsc2200_ssp_remove(struct platform_device *dev){ struct tsc2200_ssp_platform_data *params = dev->dev.platform_data; size_t i; for (i = 0; i < params->num_custom_adcs; i++) adc_classdev_unregister(¶ms->custom_adcs[i]); for (i = 0; i < ARRAY_SIZE(acdevs); i++) adc_classdev_unregister(&acdevs[i]); //SSCR0_P(port) &= ~SSCR0_SSE; //pxa_set_cken(CKEN_SSP(port), 0); return 0;}#ifdef CONFIG_PMstatic int tsc2200_ssp_suspend(struct platform_device *dev, pm_message_t state){ return 0;}static int tsc2200_ssp_resume(struct platform_device *dev){ struct tsc2200_ssp_platform_data *params = dev->dev.platform_data; tsc2200_ssp_init(params->freq ? params->freq : 1800000); return 0;}#endifstatic struct platform_driver tsc2200_ssp_driver = { .driver = { .name = "tsc2200-ssp", }, .probe = tsc2200_ssp_probe, .remove = __devexit_p(tsc2200_ssp_remove),#ifdef CONFIG_PM .suspend = tsc2200_ssp_suspend, .resume = tsc2200_ssp_resume,#endif};static int __init tsc2200_adc_init(void){ return platform_driver_register(&tsc2200_ssp_driver);}static void __exit tsc2200_adc_exit(void){ platform_driver_unregister(&tsc2200_ssp_driver);}#ifdef MODULEmodule_init(tsc2200_adc_init)#else /* start early for dependencies */subsys_initcall(tsc2200_adc_init);#endifmodule_exit(tsc2200_adc_exit)MODULE_AUTHOR("Pawel Kolodziejski, Paul Sokolovsky");MODULE_DESCRIPTION("TSC2200 SSP channel driver");MODULE_LICENSE("GPL");