/* * GSC3280 SoC adc Controller Driver * * Copyright (C) 2013 BLX IC Design Corp.,Ltd. * Author: Davied, apple_guet@126.com * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_GSC3280_ADC_DEBUG #define DBG(msg...) do { \ printk(KERN_INFO msg); \ } while (0) #else #define DBG(msg...) do { } while(0) #endif #define GSC_SYS_CTL_RST0_REG *(volatile unsigned int *)0xbc04a010 #define SYS_CTL_RST0_ADC_TS BIT(3) //adc cmd state #define GSC_ADC_CMD_TX_NOW 0x00 #define GSC_ADC_CMD_IDLE 0x08 #define GSC_ADC_CMD_SUSPEND 0x09 #define GSC_ADC_CMD_BUSY 0x0f //adc date state #define GSC_ADC_VAL_DATE 0x00 #define GSC_ADC_BUSY 0x0f //ret value #define SPI_BUSY -1 #define WRITE_DATE_ERR -2 #define READ_DATE_ERR -3 #define CMD_ERR -4 #define RESULT_ERR -5 #define MAX_WAIT_CNT 60000 #define GSC3280_ADC_NAME "gsc-adc" typedef enum { TX_FIFO_NO_FULL, TX_FIFO_EMPTY, RX_FIFO_NO_EMPTY, RX_FIFO_FULL, BUSY, FREE, SEND_OK, READ_OK } spi_state_e; struct gsc_adc_dev { char name[20]; int irq; struct clk *clk; spinlock_t lock; void __iomem *regs; int result; unsigned short cmd; struct list_head device_entry; struct platform_device *pdev; struct adc_core_dev *adc_dev; }; static LIST_HEAD(gsc3280_adc_list); static DEFINE_MUTEX(gsc3280_adc_list_lock); //ret: 1:busy, 0:free static int getSpiState(struct gsc_adc_dev *adc) { unsigned int time_cnt = 0; while (readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_BUSY) { if (time_cnt++ > MAX_WAIT_CNT) { DBG("spi busy, stat = %x\n", readl(adc->regs + GSC_SPI_SR)); return SPI_BUSY; } } return 0; } static int writeSpiDate(struct gsc_adc_dev *adc) { int cnt = 0, stat = 0; stat = getSpiState(adc); if (stat != 0) { DBG("in write spi date,spi is busy\n"); return stat; } //spi0 fifo can write, transmit fifo empty while (!(readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_TX_NO_FULL)) { if (cnt++ > MAX_WAIT_CNT) { DBG("write spi date error, stat = %x\n", readl(adc->regs + GSC_SPI_SR)); return WRITE_DATE_ERR; } } writel(adc->cmd, adc->regs + GSC_SPI_DA_S); return 0; } /* prepare to read data from adc */ static int readSpiDate(struct gsc_adc_dev *adc) { int cnt= 0, stat = 0; stat = getSpiState(adc); if (stat < 0) { DBG("in read spi date,spi is busy\n"); return stat; } //spi0 fifo receive not empty while (!(readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_RX_N_EMPTY)) { if (cnt++ > MAX_WAIT_CNT) { DBG("read spi date error, spi stat = %x\n", readl(adc->regs + GSC_SPI_SR)); return READ_DATE_ERR; } } adc->result = (unsigned short)readl(adc->regs + GSC_SPI_DA_S); return 0; } #if 0 static void resetAdcTs(void) { GSC_SYS_CTL_RST0_REG |= SYS_CTL_RST0_ADC_TS; mdelay(1); GSC_SYS_CTL_RST0_REG &= ~SYS_CTL_RST0_ADC_TS; } #endif //return 0:date valid, other:date error static int gsc3280AdcCon(unsigned short cmd) { struct gsc_adc_dev *adc; int ret = 0, status = 0/*, cnt = 0*/; //DBG("gscAdcCon\n"); mutex_lock(&gsc3280_adc_list_lock); list_for_each_entry(adc, &gsc3280_adc_list, device_entry) { if(strcmp(adc->name, GSC3280_ADC_NAME) == 0) { status = 0; break; } } mutex_unlock(&gsc3280_adc_list_lock); if (status != 0) { DBG("get gsc3280 adc struct error\n"); return -5; } adc->cmd = cmd; #if 0 resetAdcTs(); while (cnt++ < MAX_CMD_NUM) { ret = writeSpiDate(client, CMD_GSC_ADC_IDLE); if (ret != 0) return ret; ret = readSpiDate(client); if(ret != 0) return ret; status = client->result >> 12; switch (client->result) { case GSC_ADC_CMD_TX_NOW: status = 1; break; case GSC_ADC_CMD_IDLE: status = 0; //send ok break; case GSC_ADC_CMD_SUSPEND: status = 2; break; case GSC_ADC_CMD_BUSY: status = 3; break; default: status = 4; //soft reset break; } if (status != 0) mdelay(1); //delay and go on while else break; } if (status != 0) { DBG("adc enter idle error,status = %d\n", status); return 1; } #endif ret = writeSpiDate(adc); //send test cmd if (ret < 0) { //DBG("cmd = %x\n", adc->cmd); return ret; } ret = readSpiDate(adc); if (ret < 0) { //DBG("result = %x\n", adc->result); return ret; } if (adc->result != ((adc->cmd >> 12) | 0x8000)) { DBG("cmd error\n"); return CMD_ERR; } again: adc->cmd = CMD_GSC_ADC_NOP; ret = writeSpiDate(adc); //send nop cmd if (ret < 0) { DBG("send nop cmd error\n"); return ret; } ret = readSpiDate(adc); if (ret < 0) { DBG("in read result = %x\n", adc->result); return ret; } if ((adc->result & 0xf000) == 0xf000) goto again; if ((adc->result & 0xf000) == 0) { adc->result &= 0x0fff; DBG("get result success, result = %d\n", adc->result); return adc->result; } else { DBG("get adc result error, result = %d\n", adc->result); return RESULT_ERR; } } static const struct adc_class_ops gsc3280_adc_ops = { .convert = gsc3280AdcCon, }; static int adc_sysctl(struct gsc_adc_dev *adc) { int cnt = 0; writel(0x09, (volatile unsigned int *)0xbc04a034); //spi0 divid freq writel(0x01, adc->regs + GSC_SPI_SEABAUR); //spi_clik divid freq,4 divid freq writel(0x0f07, adc->regs + GSC_SPI_CTRL); msleep(300); writel(0x00, adc->regs + GSC_SPI_CS); //spi adc cs while(1) { writel(0x00, adc->regs + GSC_SPI_DA_S); while (readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_BUSY) { ; } if (readl(adc->regs + GSC_SPI_DA_S) == 0x8000) { DBG("spi0 stat OK!\n"); return 0; } if (cnt++ > 10000) { DBG("cnt over, spi0 stat error!\n"); return -1; } } } static int __devinit gsc3280_adc_probe(struct platform_device *pdev) { int ret = 0, size = 0; unsigned long rate = 0; struct gsc_adc_dev *adc; struct resource *mem, *ioarea; DBG("############\n"); printk(KERN_INFO "GSC3280 spi0 adc probe start\n"); adc = kzalloc(sizeof(struct gsc_adc_dev), GFP_KERNEL); if (adc == NULL) { DBG("failed to allocate adc_core_dev\n"); return -ENOMEM; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { DBG("no mem resource.\n"); ret = -EINVAL; goto err_alloc; } size = resource_size(mem); ioarea = request_mem_region(mem->start, size, pdev->name); if (!ioarea) { DBG("SPI region already claimed.\n"); ret = -EBUSY; goto err_alloc; } adc->regs = ioremap_nocache(mem->start, resource_size(mem)); if (!adc->regs) { DBG("SPI region already mapped.\n"); ret = -ENOMEM; goto err_mem; } DBG("probe: mapped spi0 base=%p.\n", adc->regs); adc->clk = clk_get(NULL, "spi0"); if (IS_ERR(adc->clk)) { DBG("failed to find watchdog clock source.\n"); ret = PTR_ERR(adc->clk); goto err_map; } rate = clk_get_rate(adc->clk); DBG("rate is %ld.\n", rate); clk_enable(adc->clk); ret = adc_sysctl(adc); if (ret != 0) goto err_map; spin_lock_init(&adc->lock); INIT_LIST_HEAD(&adc->device_entry); strlcpy(adc->name, GSC3280_ADC_NAME, sizeof(adc->name)); mutex_lock(&gsc3280_adc_list_lock); list_add(&adc->device_entry, &gsc3280_adc_list); mutex_unlock(&gsc3280_adc_list_lock); adc->adc_dev = adc_device_register(adc->name, &pdev->dev, &gsc3280_adc_ops, THIS_MODULE); if (IS_ERR(adc->adc_dev)) { ret = PTR_ERR(adc->adc_dev); DBG("unable to register the class device\n"); goto err_clk; } platform_set_drvdata(pdev, adc); printk(KERN_INFO "GSC3280 adc probe SUCCESS.\n"); DBG("############\n"); return 0; err_clk: clk_disable(adc->clk); clk_put(adc->clk); err_map: iounmap(adc->regs); err_mem: release_mem_region(mem->start, size); mem = NULL; err_alloc: kfree(adc); printk(KERN_INFO "!!!!!!GSC3280 adc probe error!!!!!!\n"); return ret; } static int __devexit gsc3280_adc_remove(struct platform_device *pdev) { struct gsc_adc_dev *adc = platform_get_drvdata(pdev); iounmap(adc->regs); clk_disable(adc->clk); clk_put(adc->clk); adc_device_unregister(adc->adc_dev); kfree(adc); return 0; } //#ifdef CONFIG_PM #if 0 static int gsc3280_adc_suspend(struct platform_device *pdev, pm_message_t state) { struct adc_core_dev *adc = platform_get_drvdata(pdev); unsigned long flags; u32 con; spin_lock_irqsave(&adc->lock, flags); con = readl(adc->regs + S3C2410_ADCCON); con |= S3C2410_ADCCON_STDBM; writel(con, adc->regs + S3C2410_ADCCON); disable_irq(adc->irq); spin_unlock_irqrestore(&adc->lock, flags); clk_disable(adc->clk); return 0; } static int gsc3280_adc_resume(struct platform_device *pdev) { struct adc_core_dev *adc = platform_get_drvdata(pdev); clk_enable(adc->clk); enable_irq(adc->irq); writel(adc->prescale | S3C2410_ADCCON_PRSCEN, adc->regs + S3C2410_ADCCON); return 0; } #else #define gsc3280_adc_suspend NULL #define gsc3280_adc_resume NULL #endif static struct platform_driver gsc3280adc_driver = { .driver = { .name = "adc-core", .owner = THIS_MODULE, }, .probe = gsc3280_adc_probe, .remove = __devexit_p(gsc3280_adc_remove), .suspend = gsc3280_adc_suspend, .resume = gsc3280_adc_resume, }; static int __init gsc3280_adc_init(void) { int ret = 0; ret = platform_driver_register(&gsc3280adc_driver); if (ret != 0) DBG("!!!!!!gsc adc core register error!!!!!!\n"); return ret; } static void __exit gsc3280_adc_exit(void) { platform_driver_unregister(&gsc3280adc_driver); } module_init(gsc3280_adc_init); module_exit(gsc3280_adc_exit); MODULE_AUTHOR("Davied"); MODULE_DESCRIPTION("gsc3280 spi0 adc Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:gsc3280-spi0 adc");