Change local_irq disable calls to use spinlocks to ensure that the
ADC driver data is protected against multiple access..
Note, this does not protect the client's data, and the client should
ensure it does not make multiple calls to the ADC driver.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
struct s3c_adc_client *cur;
struct s3c_adc_client *ts_pend;
void __iomem *regs;
struct s3c_adc_client *cur;
struct s3c_adc_client *ts_pend;
void __iomem *regs;
static struct adc_device *adc_dev;
static struct adc_device *adc_dev;
-static LIST_HEAD(adc_pending);
+static LIST_HEAD(adc_pending); /* protected by adc_device.lock */
#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
if (client->is_ts && adc->ts_pend)
return -EAGAIN;
if (client->is_ts && adc->ts_pend)
return -EAGAIN;
+ spin_lock_irqsave(&adc->lock, flags);
client->channel = channel;
client->nr_samples = nr_samples;
client->channel = channel;
client->nr_samples = nr_samples;
if (!adc->cur)
s3c_adc_try(adc);
if (!adc->cur)
s3c_adc_try(adc);
- local_irq_restore(flags);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
void s3c_adc_release(struct s3c_adc_client *client)
{
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;
/* We should really check that nothing is in progress. */
if (adc_dev->cur == client)
adc_dev->cur = NULL;
if (adc_dev->cur == NULL)
s3c_adc_try(adc_dev);
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);
kfree(client);
}
EXPORT_SYMBOL_GPL(s3c_adc_release);
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;
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) {
unsigned data0, data1;
if (!client) {
client->select_cb(client, 1);
s3c_adc_convert(adc);
} else {
client->select_cb(client, 1);
s3c_adc_convert(adc);
} else {
(client->select_cb)(client, 0);
adc->cur = NULL;
s3c_adc_try(adc);
(client->select_cb)(client, 0);
adc->cur = NULL;
s3c_adc_try(adc);
- local_irq_restore(flags);
+ spin_unlock(&adc->lock);
+ spin_lock_init(&adc->lock);
+
adc->pdev = pdev;
adc->prescale = S3C2410_ADCCON_PRSCVL(49);
adc->pdev = pdev;
adc->prescale = S3C2410_ADCCON_PRSCVL(49);
static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct adc_device *adc = platform_get_drvdata(pdev);
static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct adc_device *adc = platform_get_drvdata(pdev);
+ 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);
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;
clk_disable(adc->clk);
return 0;
static int s3c_adc_resume(struct platform_device *pdev)
{
struct adc_device *adc = platform_get_drvdata(pdev);
static int s3c_adc_resume(struct platform_device *pdev)
{
struct adc_device *adc = platform_get_drvdata(pdev);
clk_enable(adc->clk);
enable_irq(adc->irq);
clk_enable(adc->clk);
enable_irq(adc->irq);