|
@@ -250,3 +250,154 @@ struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
|
|
|
client->convert_cb = conv;
|
|
|
|
|
|
return client;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(s3c_adc_register);
|
|
|
+
|
|
|
+void s3c_adc_release(struct s3c_adc_client *client)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&adc_dev->lock, flags);
|
|
|
+
|
|
|
+ /* We should really check that nothing is in progress. */
|
|
|
+ if (adc_dev->cur == client)
|
|
|
+ adc_dev->cur = NULL;
|
|
|
+ if (adc_dev->ts_pend == client)
|
|
|
+ adc_dev->ts_pend = NULL;
|
|
|
+ else {
|
|
|
+ struct list_head *p, *n;
|
|
|
+ struct s3c_adc_client *tmp;
|
|
|
+
|
|
|
+ list_for_each_safe(p, n, &adc_pending) {
|
|
|
+ tmp = list_entry(p, struct s3c_adc_client, pend);
|
|
|
+ if (tmp == client)
|
|
|
+ list_del(&tmp->pend);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (adc_dev->cur == NULL)
|
|
|
+ s3c_adc_try(adc_dev);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&adc_dev->lock, flags);
|
|
|
+ kfree(client);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(s3c_adc_release);
|
|
|
+
|
|
|
+static irqreturn_t s3c_adc_irq(int irq, void *pw)
|
|
|
+{
|
|
|
+ struct adc_device *adc = pw;
|
|
|
+ struct s3c_adc_client *client = adc->cur;
|
|
|
+ enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
|
|
|
+ unsigned data0, data1;
|
|
|
+
|
|
|
+ if (!client) {
|
|
|
+ dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ data0 = readl(adc->regs + S3C2410_ADCDAT0);
|
|
|
+ data1 = readl(adc->regs + S3C2410_ADCDAT1);
|
|
|
+ adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
|
|
|
+
|
|
|
+ client->nr_samples--;
|
|
|
+
|
|
|
+ if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV11) {
|
|
|
+ data0 &= 0x3ff;
|
|
|
+ data1 &= 0x3ff;
|
|
|
+ } else {
|
|
|
+ /* S3C2416/S3C64XX/S5P ADC resolution is 12-bit */
|
|
|
+ data0 &= 0xfff;
|
|
|
+ data1 &= 0xfff;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (client->convert_cb)
|
|
|
+ (client->convert_cb)(client, data0, data1, &client->nr_samples);
|
|
|
+
|
|
|
+ if (client->nr_samples > 0) {
|
|
|
+ /* fire another conversion for this */
|
|
|
+
|
|
|
+ client->select_cb(client, 1);
|
|
|
+ s3c_adc_convert(adc);
|
|
|
+ } else {
|
|
|
+ spin_lock(&adc->lock);
|
|
|
+ (client->select_cb)(client, 0);
|
|
|
+ adc->cur = NULL;
|
|
|
+
|
|
|
+ s3c_adc_try(adc);
|
|
|
+ spin_unlock(&adc->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+exit:
|
|
|
+ if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) {
|
|
|
+ /* Clear ADC interrupt */
|
|
|
+ writel(0, adc->regs + S3C64XX_ADCCLRINT);
|
|
|
+ }
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c_adc_probe(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct device *dev = &pdev->dev;
|
|
|
+ struct adc_device *adc;
|
|
|
+ struct resource *regs;
|
|
|
+ enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
|
|
|
+ int ret;
|
|
|
+ unsigned tmp;
|
|
|
+
|
|
|
+ adc = devm_kzalloc(dev, sizeof(struct adc_device), GFP_KERNEL);
|
|
|
+ if (adc == NULL) {
|
|
|
+ dev_err(dev, "failed to allocate adc_device\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_init(&adc->lock);
|
|
|
+
|
|
|
+ adc->pdev = pdev;
|
|
|
+ adc->prescale = S3C2410_ADCCON_PRSCVL(49);
|
|
|
+
|
|
|
+ adc->vdd = devm_regulator_get(dev, "vdd");
|
|
|
+ if (IS_ERR(adc->vdd)) {
|
|
|
+ dev_err(dev, "operating without regulator \"vdd\" .\n");
|
|
|
+ return PTR_ERR(adc->vdd);
|
|
|
+ }
|
|
|
+
|
|
|
+ adc->irq = platform_get_irq(pdev, 1);
|
|
|
+ if (adc->irq <= 0) {
|
|
|
+ dev_err(dev, "failed to get adc irq\n");
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),
|
|
|
+ adc);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(dev, "failed to attach adc irq\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ adc->clk = devm_clk_get(dev, "adc");
|
|
|
+ if (IS_ERR(adc->clk)) {
|
|
|
+ dev_err(dev, "failed to get adc clock\n");
|
|
|
+ return PTR_ERR(adc->clk);
|
|
|
+ }
|
|
|
+
|
|
|
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
+ if (!regs) {
|
|
|
+ dev_err(dev, "failed to find registers\n");
|
|
|
+ return -ENXIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ adc->regs = devm_request_and_ioremap(dev, regs);
|
|
|
+ if (!adc->regs) {
|
|
|
+ dev_err(dev, "failed to map registers\n");
|
|
|
+ return -ENXIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = regulator_enable(adc->vdd);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ clk_enable(adc->clk);
|
|
|
+
|
|
|
+ tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
|
|
|
+
|
|
|
+ /* Enable 12-bit ADC resolution */
|