Commit 1f1f584c authored by Ben Dooks's avatar Ben Dooks

ARM: SAMSUNG: Make ADC client SMP safe

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: default avatarBen Dooks <ben-linux@fluff.org>
parent 6a399547
...@@ -66,6 +66,7 @@ struct adc_device { ...@@ -66,6 +66,7 @@ struct adc_device {
struct s3c_adc_client *cur; struct s3c_adc_client *cur;
struct s3c_adc_client *ts_pend; struct s3c_adc_client *ts_pend;
void __iomem *regs; void __iomem *regs;
spinlock_t lock;
unsigned int prescale; unsigned int prescale;
...@@ -74,7 +75,7 @@ struct adc_device { ...@@ -74,7 +75,7 @@ struct adc_device {
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)
...@@ -145,7 +146,7 @@ int s3c_adc_start(struct s3c_adc_client *client, ...@@ -145,7 +146,7 @@ int s3c_adc_start(struct s3c_adc_client *client,
if (client->is_ts && adc->ts_pend) if (client->is_ts && adc->ts_pend)
return -EAGAIN; return -EAGAIN;
local_irq_save(flags); spin_lock_irqsave(&adc->lock, flags);
client->channel = channel; client->channel = channel;
client->nr_samples = nr_samples; client->nr_samples = nr_samples;
...@@ -157,7 +158,8 @@ int s3c_adc_start(struct s3c_adc_client *client, ...@@ -157,7 +158,8 @@ int s3c_adc_start(struct s3c_adc_client *client,
if (!adc->cur) if (!adc->cur)
s3c_adc_try(adc); s3c_adc_try(adc);
local_irq_restore(flags);
spin_unlock_irqrestore(&adc->lock, flags);
return 0; return 0;
} }
...@@ -237,6 +239,10 @@ EXPORT_SYMBOL_GPL(s3c_adc_register); ...@@ -237,6 +239,10 @@ EXPORT_SYMBOL_GPL(s3c_adc_register);
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. */ /* We should really check that nothing is in progress. */
if (adc_dev->cur == client) if (adc_dev->cur == client)
adc_dev->cur = NULL; adc_dev->cur = NULL;
...@@ -255,6 +261,8 @@ void s3c_adc_release(struct s3c_adc_client *client) ...@@ -255,6 +261,8 @@ void s3c_adc_release(struct s3c_adc_client *client)
if (adc_dev->cur == NULL) if (adc_dev->cur == NULL)
s3c_adc_try(adc_dev); s3c_adc_try(adc_dev);
spin_unlock_irqrestore(&adc_dev->lock, flags);
kfree(client); kfree(client);
} }
EXPORT_SYMBOL_GPL(s3c_adc_release); EXPORT_SYMBOL_GPL(s3c_adc_release);
...@@ -264,7 +272,6 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) ...@@ -264,7 +272,6 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)
struct adc_device *adc = pw; struct adc_device *adc = pw;
struct s3c_adc_client *client = adc->cur; struct s3c_adc_client *client = adc->cur;
enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data; enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
unsigned long flags;
unsigned data0, data1; unsigned data0, data1;
if (!client) { if (!client) {
...@@ -296,12 +303,12 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) ...@@ -296,12 +303,12 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)
client->select_cb(client, 1); client->select_cb(client, 1);
s3c_adc_convert(adc); s3c_adc_convert(adc);
} else { } else {
local_irq_save(flags); spin_lock(&adc->lock);
(client->select_cb)(client, 0); (client->select_cb)(client, 0);
adc->cur = NULL; adc->cur = NULL;
s3c_adc_try(adc); s3c_adc_try(adc);
local_irq_restore(flags); spin_unlock(&adc->lock);
} }
exit: exit:
...@@ -326,6 +333,8 @@ static int s3c_adc_probe(struct platform_device *pdev) ...@@ -326,6 +333,8 @@ static int s3c_adc_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
spin_lock_init(&adc->lock);
adc->pdev = pdev; adc->pdev = pdev;
adc->prescale = S3C2410_ADCCON_PRSCVL(49); adc->prescale = S3C2410_ADCCON_PRSCVL(49);
...@@ -407,13 +416,17 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev) ...@@ -407,13 +416,17 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev)
static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct adc_device *adc = platform_get_drvdata(pdev); struct adc_device *adc = platform_get_drvdata(pdev);
unsigned long flags;
u32 con; u32 con;
spin_lock_irqsave(&adc->lock, flags);
con = readl(adc->regs + S3C2410_ADCCON); con = readl(adc->regs + S3C2410_ADCCON);
con |= S3C2410_ADCCON_STDBM; con |= S3C2410_ADCCON_STDBM;
writel(con, adc->regs + S3C2410_ADCCON); writel(con, adc->regs + S3C2410_ADCCON);
disable_irq(adc->irq); disable_irq(adc->irq);
spin_unlock_irqrestore(&adc->lock, flags);
clk_disable(adc->clk); clk_disable(adc->clk);
return 0; return 0;
...@@ -422,6 +435,7 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -422,6 +435,7 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
static int s3c_adc_resume(struct platform_device *pdev) static int s3c_adc_resume(struct platform_device *pdev)
{ {
struct adc_device *adc = platform_get_drvdata(pdev); struct adc_device *adc = platform_get_drvdata(pdev);
unsigned long flags;
clk_enable(adc->clk); clk_enable(adc->clk);
enable_irq(adc->irq); enable_irq(adc->irq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment