adc.c revision 2ee8e6f0e375c68d420c47ddab70485d8e587d95
13929e1e76d9116856a4c7a00fcce0539dd8507a0Maurus Cuelenaere/* arch/arm/plat-samsung/adc.c
228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *
328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * Copyright (c) 2008 Simtec Electronics
428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *	http://armlinux.simtec.co.uk/
528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *	Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *
73929e1e76d9116856a4c7a00fcce0539dd8507a0Maurus Cuelenaere * Samsung ADC device core
828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *
928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * This program is free software; you can redistribute it and/or modify
1028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * it under the terms of the GNU General Public License as published by
1128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * the Free Software Foundation; either version 2 of the License.
1228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks*/
1328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
1428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/module.h>
1528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/kernel.h>
1628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/platform_device.h>
17d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h>
1828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/list.h>
195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/err.h>
2128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/clk.h>
2228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/interrupt.h>
2328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <linux/io.h>
24f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham#include <linux/regulator/consumer.h>
2528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
2628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <plat/regs-adc.h>
2728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#include <plat/adc.h>
2828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
2928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks/* This driver is designed to control the usage of the ADC block between
3028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * the touchscreen and any other drivers that may need to use it, such as
3128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * the hwmon driver.
3228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *
3328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * Priority will be given to the touchscreen driver, but as this itself is
3428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * rate limited it should not starve other requests which are processed in
3528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * order that they are received.
3628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks *
3728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * Each user registers to get a client block which uniquely identifies it
3828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * and stores information such as the necessary functions to callback when
3928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks * action is required.
4028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks */
4128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
42bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaereenum s3c_cpu_type {
4364df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	TYPE_ADCV1, /* S3C24XX */
446247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner	TYPE_ADCV11, /* S3C2443 */
4535cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	TYPE_ADCV12, /* S3C2416, S3C2450 */
4664df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	TYPE_ADCV2, /* S3C64XX, S5P64X0, S5PC100 */
4764df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	TYPE_ADCV3, /* S5PV210, S5PC110, EXYNOS4210 */
48bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere};
49bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere
5028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstruct s3c_adc_client {
5128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct platform_device	*pdev;
5228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct list_head	 pend;
53e170adcb406504b8acd35554c69830c11916be1fBen Dooks	wait_queue_head_t	*wait;
5428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
5528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned int		 nr_samples;
56e170adcb406504b8acd35554c69830c11916be1fBen Dooks	int			 result;
5728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned char		 is_ts;
5828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned char		 channel;
5928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
60e170adcb406504b8acd35554c69830c11916be1fBen Dooks	void	(*select_cb)(struct s3c_adc_client *c, unsigned selected);
61e170adcb406504b8acd35554c69830c11916be1fBen Dooks	void	(*convert_cb)(struct s3c_adc_client *c,
62e170adcb406504b8acd35554c69830c11916be1fBen Dooks			      unsigned val1, unsigned val2,
633f7ea467be1bad860c0f71ba7373dd3cf76b485aNelson Castillo			      unsigned *samples_left);
6428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks};
6528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
6628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstruct adc_device {
6728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct platform_device	*pdev;
6828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct platform_device	*owner;
6928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct clk		*clk;
7028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct s3c_adc_client	*cur;
7128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct s3c_adc_client	*ts_pend;
7228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	void __iomem		*regs;
731f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spinlock_t		 lock;
7428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
7528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned int		 prescale;
7628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
7728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	int			 irq;
78f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	struct regulator	*vdd;
7928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks};
8028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
8128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic struct adc_device *adc_dev;
8228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
831f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooksstatic LIST_HEAD(adc_pending);	/* protected by adc_device.lock */
8428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
8528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
8628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
8728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic inline void s3c_adc_convert(struct adc_device *adc)
8828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
8928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned con = readl(adc->regs + S3C2410_ADCCON);
9028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
9128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	con |= S3C2410_ADCCON_ENABLE_START;
9228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	writel(con, adc->regs + S3C2410_ADCCON);
9328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
9428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
9528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic inline void s3c_adc_select(struct adc_device *adc,
9628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks				  struct s3c_adc_client *client)
9728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
9828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned con = readl(adc->regs + S3C2410_ADCCON);
9964df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
10028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
101e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->select_cb(client, 1);
10228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
103df303e0236417ca006e04116f18641dbe4bd704aHeiko Stuebner	if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV2)
104df303e0236417ca006e04116f18641dbe4bd704aHeiko Stuebner		con &= ~S3C2410_ADCCON_MUXMASK;
10528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	con &= ~S3C2410_ADCCON_STDBM;
10628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	con &= ~S3C2410_ADCCON_STARTMASK;
10728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
10864df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	if (!client->is_ts) {
10964df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		if (cpu == TYPE_ADCV3)
11064df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham			writel(client->channel & 0xf, adc->regs + S5P_ADCMUX);
11135cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		else if (cpu == TYPE_ADCV11 || cpu == TYPE_ADCV12)
1126247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner			writel(client->channel & 0xf,
1136247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner						adc->regs + S3C2443_ADCMUX);
11464df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		else
11564df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham			con |= S3C2410_ADCCON_SELMUX(client->channel);
11664df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	}
11728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
11828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	writel(con, adc->regs + S3C2410_ADCCON);
11928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
12028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
12128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic void s3c_adc_dbgshow(struct adc_device *adc)
12228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
12328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",
12428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		readl(adc->regs + S3C2410_ADCCON),
12528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		readl(adc->regs + S3C2410_ADCTSC),
12628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		readl(adc->regs + S3C2410_ADCDLY));
12728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
12828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
129f8c8ac8109ecdd3583b0ac9fd3adf058678a802eBen Dooksstatic void s3c_adc_try(struct adc_device *adc)
13028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
13128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct s3c_adc_client *next = adc->ts_pend;
13228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
13328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!next && !list_empty(&adc_pending)) {
13428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		next = list_first_entry(&adc_pending,
13528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks					struct s3c_adc_client, pend);
13628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		list_del(&next->pend);
13728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	} else
13828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		adc->ts_pend = NULL;
13928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
14028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (next) {
14128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		adc_dbg(adc, "new client is %p\n", next);
14228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		adc->cur = next;
14328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_select(adc, next);
14428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_convert(adc);
14528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_dbgshow(adc);
14628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
14728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
14828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
14928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksint s3c_adc_start(struct s3c_adc_client *client,
15028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		  unsigned int channel, unsigned int nr_samples)
15128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
15228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc = adc_dev;
15328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned long flags;
15428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
15528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!adc) {
15628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		printk(KERN_ERR "%s: failed to find adc\n", __func__);
15728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		return -EINVAL;
15828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
15928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
1601f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_lock_irqsave(&adc->lock, flags);
16128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
1628265981bb439f3ecc5356fb877a6c2a6636ac88aTodd Poynor	if (client->is_ts && adc->ts_pend) {
1638265981bb439f3ecc5356fb877a6c2a6636ac88aTodd Poynor		spin_unlock_irqrestore(&adc->lock, flags);
1648265981bb439f3ecc5356fb877a6c2a6636ac88aTodd Poynor		return -EAGAIN;
1658265981bb439f3ecc5356fb877a6c2a6636ac88aTodd Poynor	}
1668265981bb439f3ecc5356fb877a6c2a6636ac88aTodd Poynor
16728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->channel = channel;
16828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->nr_samples = nr_samples;
16928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
17028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (client->is_ts)
17128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		adc->ts_pend = client;
17228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	else
17328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		list_add_tail(&client->pend, &adc_pending);
17428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
17528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!adc->cur)
17628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_try(adc);
1771f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
1781f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_unlock_irqrestore(&adc->lock, flags);
17928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
18028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return 0;
18128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
18228ab44c5be60a9b91021a7cc7b4e202775c04764Ben DooksEXPORT_SYMBOL_GPL(s3c_adc_start);
18328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
184e170adcb406504b8acd35554c69830c11916be1fBen Dooksstatic void s3c_convert_done(struct s3c_adc_client *client,
185e170adcb406504b8acd35554c69830c11916be1fBen Dooks			     unsigned v, unsigned u, unsigned *left)
186e170adcb406504b8acd35554c69830c11916be1fBen Dooks{
187e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->result = v;
188e170adcb406504b8acd35554c69830c11916be1fBen Dooks	wake_up(client->wait);
189e170adcb406504b8acd35554c69830c11916be1fBen Dooks}
190e170adcb406504b8acd35554c69830c11916be1fBen Dooks
191e170adcb406504b8acd35554c69830c11916be1fBen Dooksint s3c_adc_read(struct s3c_adc_client *client, unsigned int ch)
192e170adcb406504b8acd35554c69830c11916be1fBen Dooks{
193e170adcb406504b8acd35554c69830c11916be1fBen Dooks	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
194e170adcb406504b8acd35554c69830c11916be1fBen Dooks	int ret;
195e170adcb406504b8acd35554c69830c11916be1fBen Dooks
196e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->convert_cb = s3c_convert_done;
197e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->wait = &wake;
198e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->result = -1;
199e170adcb406504b8acd35554c69830c11916be1fBen Dooks
200e170adcb406504b8acd35554c69830c11916be1fBen Dooks	ret = s3c_adc_start(client, ch, 1);
201e170adcb406504b8acd35554c69830c11916be1fBen Dooks	if (ret < 0)
202e170adcb406504b8acd35554c69830c11916be1fBen Dooks		goto err;
203e170adcb406504b8acd35554c69830c11916be1fBen Dooks
204e170adcb406504b8acd35554c69830c11916be1fBen Dooks	ret = wait_event_timeout(wake, client->result >= 0, HZ / 2);
205e170adcb406504b8acd35554c69830c11916be1fBen Dooks	if (client->result < 0) {
206e170adcb406504b8acd35554c69830c11916be1fBen Dooks		ret = -ETIMEDOUT;
207e170adcb406504b8acd35554c69830c11916be1fBen Dooks		goto err;
208e170adcb406504b8acd35554c69830c11916be1fBen Dooks	}
209e170adcb406504b8acd35554c69830c11916be1fBen Dooks
210e170adcb406504b8acd35554c69830c11916be1fBen Dooks	client->convert_cb = NULL;
211e170adcb406504b8acd35554c69830c11916be1fBen Dooks	return client->result;
212e170adcb406504b8acd35554c69830c11916be1fBen Dooks
213e170adcb406504b8acd35554c69830c11916be1fBen Dookserr:
214e170adcb406504b8acd35554c69830c11916be1fBen Dooks	return ret;
215e170adcb406504b8acd35554c69830c11916be1fBen Dooks}
216d3bf3956c75b38def079fb6db40b5cf3f1466a93Ryan MallonEXPORT_SYMBOL_GPL(s3c_adc_read);
217e170adcb406504b8acd35554c69830c11916be1fBen Dooks
218e170adcb406504b8acd35554c69830c11916be1fBen Dooksstatic void s3c_adc_default_select(struct s3c_adc_client *client,
219e170adcb406504b8acd35554c69830c11916be1fBen Dooks				   unsigned select)
22028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
22128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
22228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
22328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstruct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
224e170adcb406504b8acd35554c69830c11916be1fBen Dooks					void (*select)(struct s3c_adc_client *client,
225e170adcb406504b8acd35554c69830c11916be1fBen Dooks						       unsigned int selected),
226e170adcb406504b8acd35554c69830c11916be1fBen Dooks					void (*conv)(struct s3c_adc_client *client,
227e170adcb406504b8acd35554c69830c11916be1fBen Dooks						     unsigned d0, unsigned d1,
2283f7ea467be1bad860c0f71ba7373dd3cf76b485aNelson Castillo						     unsigned *samples_left),
22928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks					unsigned int is_ts)
23028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
23128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct s3c_adc_client *client;
23228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
23328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	WARN_ON(!pdev);
23428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
23528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!select)
23628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		select = s3c_adc_default_select;
23728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
238e170adcb406504b8acd35554c69830c11916be1fBen Dooks	if (!pdev)
23928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		return ERR_PTR(-EINVAL);
24028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
24128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
24228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!client) {
24328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(&pdev->dev, "no memory for adc client\n");
24428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		return ERR_PTR(-ENOMEM);
24528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
24628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
24728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->pdev = pdev;
24828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->is_ts = is_ts;
24928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->select_cb = select;
25028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	client->convert_cb = conv;
25128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
25228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return client;
25328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
25428ab44c5be60a9b91021a7cc7b4e202775c04764Ben DooksEXPORT_SYMBOL_GPL(s3c_adc_register);
25528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
25628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksvoid s3c_adc_release(struct s3c_adc_client *client)
25728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
2581f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	unsigned long flags;
2591f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
2601f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_lock_irqsave(&adc_dev->lock, flags);
2611f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
26228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	/* We should really check that nothing is in progress. */
2630c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo	if (adc_dev->cur == client)
2640c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		adc_dev->cur = NULL;
2650c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo	if (adc_dev->ts_pend == client)
2660c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		adc_dev->ts_pend = NULL;
2670c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo	else {
2680c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		struct list_head *p, *n;
2690c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		struct s3c_adc_client *tmp;
2700c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo
2710c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		list_for_each_safe(p, n, &adc_pending) {
2720c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo			tmp = list_entry(p, struct s3c_adc_client, pend);
2730c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo			if (tmp == client)
2740c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo				list_del(&tmp->pend);
2750c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		}
2760c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo	}
2770c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo
2780c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo	if (adc_dev->cur == NULL)
2790c3ee078251b85812e585b5cf5d1635afd73f4e2Ramax Lo		s3c_adc_try(adc_dev);
2801f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
2811f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_unlock_irqrestore(&adc_dev->lock, flags);
28228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	kfree(client);
28328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
28428ab44c5be60a9b91021a7cc7b4e202775c04764Ben DooksEXPORT_SYMBOL_GPL(s3c_adc_release);
28528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
28628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic irqreturn_t s3c_adc_irq(int irq, void *pw)
28728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
28828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc = pw;
28928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct s3c_adc_client *client = adc->cur;
29091492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere	enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
29128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	unsigned data0, data1;
29228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
29328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!client) {
29428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
295bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		goto exit;
29628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
29728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
29828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	data0 = readl(adc->regs + S3C2410_ADCDAT0);
29928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	data1 = readl(adc->regs + S3C2410_ADCDAT1);
30028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
30128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3023f7ea467be1bad860c0f71ba7373dd3cf76b485aNelson Castillo	client->nr_samples--;
303e170adcb406504b8acd35554c69830c11916be1fBen Dooks
3046247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner	if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV11) {
3056247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner		data0 &= 0x3ff;
3066247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner		data1 &= 0x3ff;
3076247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner	} else {
30835cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		/* S3C2416/S3C64XX/S5P ADC resolution is 12-bit */
30991492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere		data0 &= 0xfff;
31091492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere		data1 &= 0xfff;
31191492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere	}
31291492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere
313e170adcb406504b8acd35554c69830c11916be1fBen Dooks	if (client->convert_cb)
31491492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere		(client->convert_cb)(client, data0, data1, &client->nr_samples);
31528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3163f7ea467be1bad860c0f71ba7373dd3cf76b485aNelson Castillo	if (client->nr_samples > 0) {
31728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		/* fire another conversion for this */
31828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
319e170adcb406504b8acd35554c69830c11916be1fBen Dooks		client->select_cb(client, 1);
32028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_convert(adc);
32128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	} else {
3221f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks		spin_lock(&adc->lock);
323e170adcb406504b8acd35554c69830c11916be1fBen Dooks		(client->select_cb)(client, 0);
32428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		adc->cur = NULL;
32528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
32628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		s3c_adc_try(adc);
3271f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks		spin_unlock(&adc->lock);
32828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
32928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
330bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaereexit:
3316247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) {
332bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		/* Clear ADC interrupt */
333bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		writel(0, adc->regs + S3C64XX_ADCCLRINT);
334bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	}
33528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return IRQ_HANDLED;
33628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
33728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
33828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic int s3c_adc_probe(struct platform_device *pdev)
33928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
34028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct device *dev = &pdev->dev;
34128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc;
34228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct resource *regs;
34335cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
34428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	int ret;
34591492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere	unsigned tmp;
34628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3472ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim	adc = devm_kzalloc(dev, sizeof(struct adc_device), GFP_KERNEL);
34828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (adc == NULL) {
34928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to allocate adc_device\n");
35028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		return -ENOMEM;
35128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
35228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3531f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_lock_init(&adc->lock);
3541f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
35528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc->pdev = pdev;
35628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc->prescale = S3C2410_ADCCON_PRSCVL(49);
35728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3582ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim	adc->vdd = devm_regulator_get(dev, "vdd");
359f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	if (IS_ERR(adc->vdd)) {
360f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham		dev_err(dev, "operating without regulator \"vdd\" .\n");
3612ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return PTR_ERR(adc->vdd);
362f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	}
363f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham
36428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc->irq = platform_get_irq(pdev, 1);
36528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (adc->irq <= 0) {
36628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to get adc irq\n");
3672ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return -ENOENT;
36828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
36928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3702ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim	ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),
3712ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim				adc);
37228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (ret < 0) {
37328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to attach adc irq\n");
3742ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return ret;
37528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
37628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3772ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim	adc->clk = devm_clk_get(dev, "adc");
37828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (IS_ERR(adc->clk)) {
37928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to get adc clock\n");
3802ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return PTR_ERR(adc->clk);
38128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
38228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
38328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
38428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!regs) {
38528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to find registers\n");
3862ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return -ENXIO;
38728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
38828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
3892ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim	adc->regs = devm_request_and_ioremap(dev, regs);
39028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (!adc->regs) {
39128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		dev_err(dev, "failed to map registers\n");
3922ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return -ENXIO;
39328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	}
39428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
395f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	ret = regulator_enable(adc->vdd);
396f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	if (ret)
3972ee8e6f0e375c68d420c47ddab70485d8e587d95Eunki Kim		return ret;
398f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham
39928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	clk_enable(adc->clk);
40028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
40191492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere	tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
40235cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner
40335cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	/* Enable 12-bit ADC resolution */
40435cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	if (cpu == TYPE_ADCV12)
40535cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		tmp |= S3C2416_ADCCON_RESSEL;
40635cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
40791492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere		tmp |= S3C64XX_ADCCON_RESSEL;
40835cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner
40991492b4a04586e7cb191c72de9d1b22545a3ce16Maurus Cuelenaere	writel(tmp, adc->regs + S3C2410_ADCCON);
41028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
41128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	dev_info(dev, "attached adc driver\n");
41228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
41328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	platform_set_drvdata(pdev, adc);
41428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	adc_dev = adc;
41528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
41628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return 0;
41728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
41828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
419ad4e22fa4762516eef0e3f19a13a86e13fba71e8Uwe Kleine-Königstatic int __devexit s3c_adc_remove(struct platform_device *pdev)
42028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
42128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc = platform_get_drvdata(pdev);
42228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
42328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	clk_disable(adc->clk);
424f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	regulator_disable(adc->vdd);
42528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
42628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return 0;
42728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
42828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
42928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#ifdef CONFIG_PM
43067dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Hamstatic int s3c_adc_suspend(struct device *dev)
43128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
43267dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	struct platform_device *pdev = container_of(dev,
43367dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham			struct platform_device, dev);
43428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc = platform_get_drvdata(pdev);
4351f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	unsigned long flags;
43628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	u32 con;
43728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
4381f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_lock_irqsave(&adc->lock, flags);
4391f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks
44028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	con = readl(adc->regs + S3C2410_ADCCON);
44128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	con |= S3C2410_ADCCON_STDBM;
44228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	writel(con, adc->regs + S3C2410_ADCCON);
44328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
444a0af8b3c701d254b55fc291150d5320317c0a338Vasily Khoruzhick	disable_irq(adc->irq);
4451f1f584c9a1dd234041573d2d1c42620b3966607Ben Dooks	spin_unlock_irqrestore(&adc->lock, flags);
44628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	clk_disable(adc->clk);
447f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	regulator_disable(adc->vdd);
44828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
44928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return 0;
45028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
45128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
45267dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Hamstatic int s3c_adc_resume(struct device *dev)
45328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
45467dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	struct platform_device *pdev = container_of(dev,
45567dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham			struct platform_device, dev);
45628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	struct adc_device *adc = platform_get_drvdata(pdev);
45735cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
458f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	int ret;
45967dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	unsigned long tmp;
46028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
461f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	ret = regulator_enable(adc->vdd);
462f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham	if (ret)
463f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Ham		return ret;
46428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	clk_enable(adc->clk);
465a0af8b3c701d254b55fc291150d5320317c0a338Vasily Khoruzhick	enable_irq(adc->irq);
46628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
46767dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
46835cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner
46967dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	/* Enable 12-bit ADC resolution */
47035cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	if (cpu == TYPE_ADCV12)
47135cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		tmp |= S3C2416_ADCCON_RESSEL;
47235cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
47367dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham		tmp |= S3C64XX_ADCCON_RESSEL;
47435cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner
47567dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	writel(tmp, adc->regs + S3C2410_ADCCON);
47628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
47728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return 0;
47828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
47928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
48028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#else
48128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#define s3c_adc_suspend NULL
48228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#define s3c_adc_resume NULL
48328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks#endif
48428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
485bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaerestatic struct platform_device_id s3c_adc_driver_ids[] = {
486bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	{
487bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		.name           = "s3c24xx-adc",
48864df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		.driver_data    = TYPE_ADCV1,
489bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	}, {
4906247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner		.name		= "s3c2443-adc",
4916247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner		.driver_data	= TYPE_ADCV11,
4926247cea2b9c193a845a01381c36e18f11676fdfbHeiko Stuebner	}, {
49335cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		.name		= "s3c2416-adc",
49435cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner		.driver_data	= TYPE_ADCV12,
49535cc3cea2c2adb825dbe987000165005d28acaecHeiko Stuebner	}, {
496bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		.name           = "s3c64xx-adc",
49764df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		.driver_data    = TYPE_ADCV2,
49864df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham	}, {
49964df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		.name		= "samsung-adc-v3",
50064df92ea7893d1cfd714c2f6acfd2eb15fbe3279MyungJoo Ham		.driver_data	= TYPE_ADCV3,
501bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	},
502bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	{ }
503bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere};
504bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus CuelenaereMODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);
505bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere
50667dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Hamstatic const struct dev_pm_ops adc_pm_ops = {
50767dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	.suspend	= s3c_adc_suspend,
50867dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham	.resume		= s3c_adc_resume,
50967dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham};
51067dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham
51128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic struct platform_driver s3c_adc_driver = {
512bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere	.id_table	= s3c_adc_driver_ids,
51328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	.driver		= {
514bcedfa98d97e02e95bb023ab93675dbe63c508efMaurus Cuelenaere		.name	= "s3c-adc",
51528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		.owner	= THIS_MODULE,
51667dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5MyungJoo Ham		.pm	= &adc_pm_ops,
51728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	},
51828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	.probe		= s3c_adc_probe,
51928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	.remove		= __devexit_p(s3c_adc_remove),
52028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks};
52128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
52228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooksstatic int __init adc_init(void)
52328ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks{
52428ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	int ret;
52528ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
52628ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	ret = platform_driver_register(&s3c_adc_driver);
52728ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	if (ret)
52828ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks		printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
52928ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
53028ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks	return ret;
53128ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks}
53228ab44c5be60a9b91021a7cc7b4e202775c04764Ben Dooks
533f462904ef1508c0a2cc22c65478a7be0cd89f47aMyungJoo Hammodule_init(adc_init);
534