#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
+#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl4030-madc.h>
#define TWL4030_MADC_PFX "twl4030-madc: "
-static struct twl4030_madc_data {
+struct twl4030_madc_data {
+ struct device *dev;
struct mutex lock;
struct work_struct ws;
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
-} twl4030_madc;
+ int imr;
+ int isr;
+};
-static const char irq_pin = 1; /* XXX Read from platfrom data */
+static struct twl4030_madc_data *the_madc;
static
const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
},
};
-static void twl4030_madc_read(u8 reg, u8 *val)
+static int twl4030_madc_read(struct twl4030_madc_data *madc, u8 reg)
{
- int ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, val, reg);
- if (ret)
- printk(KERN_ERR TWL4030_MADC_PFX
- "unable to read register 0x%X\n", reg);
+ int ret;
+ u8 val;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &val, reg);
+ if (ret) {
+ dev_dbg(madc->dev, "unable to read register 0x%X\n", reg);
+ return ret;
+ }
+
+ return val;
}
-static void twl4030_madc_write(u8 reg, u8 val)
+static void twl4030_madc_write(struct twl4030_madc_data *madc, u8 reg, u8 val)
{
- int ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
+ int ret;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
if (ret)
- printk(KERN_ERR TWL4030_MADC_PFX
- "unable to write register 0x%X\n", reg);
+ dev_err(madc->dev, "unable to write register 0x%X\n", reg);
}
-static int twl4030_madc_channel_raw_read(u8 reg)
+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
{
u8 msb, lsb;
/* For each ADC channel, we have MSB and LSB register pair. MSB address
* is always LSB address+1. reg parameter is the addr of LSB register */
- twl4030_madc_read(reg+1, &msb);
- twl4030_madc_read(reg, &lsb);
+ msb = twl4030_madc_read(madc, reg + 1);
+ lsb = twl4030_madc_read(madc, reg);
return (int)(((msb << 8) | lsb) >> 6);
}
-static int twl4030_madc_read_channels(u8 reg_base, u16 channels, int *buf)
+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
+ u8 reg_base, u16 channels, int *buf)
{
int count = 0;
u8 reg, i;
for (i = 0; i < TWL4030_MADC_MAX_CHANNELS; i++) {
if (channels & (1<<i)) {
reg = reg_base + 2*i;
- buf[i] = twl4030_madc_channel_raw_read(reg);
+ buf[i] = twl4030_madc_channel_raw_read(madc, reg);
count++;
}
}
return count;
}
-static void twl4030_madc_enable_irq(int id)
+static void twl4030_madc_enable_irq(struct twl4030_madc_data *madc, int id)
{
u8 val;
- static u8 imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
-
- twl4030_madc_read(imr, &val);
+ val = twl4030_madc_read(madc, madc->imr);
val &= ~(1 << id);
- twl4030_madc_write(imr, val);
+ twl4030_madc_write(madc, madc->imr, val);
}
-static void twl4030_madc_disable_irq(int id)
+static void twl4030_madc_disable_irq(struct twl4030_madc_data *madc, int id)
{
u8 val;
- static u8 imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
-
- twl4030_madc_read(imr, &val);
+ val = twl4030_madc_read(madc, madc->imr);
val |= (1 << id);
- twl4030_madc_write(imr, val);
+ twl4030_madc_write(madc, madc->imr, val);
}
-static irqreturn_t twl4030_madc_irq_handler(int irq, void *madc_dev)
+static irqreturn_t twl4030_madc_irq_handler(int irq, void *_madc)
{
+ struct twl4030_madc_data *madc = _madc;
u8 isr_val, imr_val;
- static u8 isr, imr;
int i;
- imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
- isr = (irq_pin == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
-
/* Use COR to ack interrupts since we have no shared IRQs in ISRx */
- twl4030_madc_read(isr, &isr_val);
- twl4030_madc_read(imr, &imr_val);
+ isr_val = twl4030_madc_read(madc, madc->isr);
+ imr_val = twl4030_madc_read(madc, madc->imr);
isr_val &= ~imr_val;
if (!(isr_val & (1<<i)))
continue;
- twl4030_madc_disable_irq(i);
- twl4030_madc.requests[i].result_pending = 1;
+ twl4030_madc_disable_irq(madc, i);
+ madc->requests[i].result_pending = 1;
}
- schedule_work(&twl4030_madc.ws);
+ schedule_work(&madc->ws);
return IRQ_HANDLED;
}
static void twl4030_madc_work(struct work_struct *ws)
{
const struct twl4030_madc_conversion_method *method;
+ struct twl4030_madc_data *madc;
struct twl4030_madc_request *r;
int len, i;
- mutex_lock(&twl4030_madc.lock);
+ madc = container_of(ws, struct twl4030_madc_data, ws);
+ mutex_lock(&madc->lock);
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
- r = &twl4030_madc.requests[i];
+ r = &madc->requests[i];
/* No pending results for this method, move to next one */
if (!r->result_pending)
method = &twl4030_conversion_methods[r->method];
/* Read results */
- len = twl4030_madc_read_channels(method->rbase,
+ len = twl4030_madc_read_channels(madc, method->rbase,
r->channels, r->rbuf);
/* Return results to caller */
r->active = 0;
}
- mutex_unlock(&twl4030_madc.lock);
+ mutex_unlock(&madc->lock);
}
-static int twl4030_madc_set_irq(struct twl4030_madc_request *req)
+static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
+ struct twl4030_madc_request *req)
{
struct twl4030_madc_request *p;
- p = &twl4030_madc.requests[req->method];
+ p = &madc->requests[req->method];
memcpy(p, req, sizeof *req);
- twl4030_madc_enable_irq(req->method);
+ twl4030_madc_enable_irq(madc, req->method);
return 0;
}
-static inline void twl4030_madc_start_conversion(int conv_method)
+static inline void twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
+ int conv_method)
{
const struct twl4030_madc_conversion_method *method;
switch (conv_method) {
case TWL4030_MADC_SW1:
case TWL4030_MADC_SW2:
- twl4030_madc_write(method->ctrl, TWL4030_MADC_SW_START);
+ twl4030_madc_write(madc, method->ctrl, TWL4030_MADC_SW_START);
break;
case TWL4030_MADC_RT:
default:
}
}
-static void twl4030_madc_wait_conversion_ready_ms(u8 *time, u8 status_reg)
+static void twl4030_madc_wait_conversion_ready_ms(
+ struct twl4030_madc_data *madc,
+ u8 *time, u8 status_reg)
{
u8 reg = 0;
do {
msleep(1);
(*time)--;
- twl4030_madc_read(status_reg, ®);
+ reg = twl4030_madc_read(madc, status_reg);
} while (((reg & TWL4030_MADC_BUSY) && !(reg & TWL4030_MADC_EOC_SW)) &&
(*time != 0));
}
return -EINVAL;
/* Do we have a conversion request ongoing */
- if (twl4030_madc.requests[req->method].active)
+ if (the_madc->requests[req->method].active)
return -EBUSY;
ch_msb = (req->channels >> 8) & 0xff;
method = &twl4030_conversion_methods[req->method];
- mutex_lock(&twl4030_madc.lock);
+ mutex_lock(&the_madc->lock);
/* Select channels to be converted */
- twl4030_madc_write(method->sel + 1, ch_msb);
- twl4030_madc_write(method->sel, ch_lsb);
+ twl4030_madc_write(the_madc, method->sel + 1, ch_msb);
+ twl4030_madc_write(the_madc, method->sel, ch_lsb);
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
- twl4030_madc_write(method->avg + 1, ch_msb);
- twl4030_madc_write(method->avg, ch_lsb);
+ twl4030_madc_write(the_madc, method->avg + 1, ch_msb);
+ twl4030_madc_write(the_madc, method->avg, ch_lsb);
}
if ((req->type == TWL4030_MADC_IRQ_ONESHOT) && (req->func_cb != NULL)) {
- twl4030_madc_set_irq(req);
- twl4030_madc_start_conversion(req->method);
- twl4030_madc.requests[req->method].active = 1;
+ twl4030_madc_set_irq(the_madc, req);
+ twl4030_madc_start_conversion(the_madc, req->method);
+ the_madc->requests[req->method].active = 1;
ret = 0;
goto out;
}
goto out;
}
- twl4030_madc_start_conversion(req->method);
- twl4030_madc.requests[req->method].active = 1;
+ twl4030_madc_start_conversion(the_madc, req->method);
+ the_madc->requests[req->method].active = 1;
/* Wait until conversion is ready (ctrl register returns EOC) */
wait_time = 50;
- twl4030_madc_wait_conversion_ready_ms(&wait_time, method->ctrl);
+ twl4030_madc_wait_conversion_ready_ms(the_madc,
+ &wait_time, method->ctrl);
if (wait_time == 0) {
- printk(KERN_ERR TWL4030_MADC_PFX "conversion timeout!\n");
+ dev_dbg(the_madc->dev, "conversion timeout!\n");
ret = -EAGAIN;
goto out;
}
- ret = twl4030_madc_read_channels(method->rbase, req->channels,
+ ret = twl4030_madc_read_channels(the_madc, method->rbase, req->channels,
req->rbuf);
- twl4030_madc.requests[req->method].active = 0;
+ the_madc->requests[req->method].active = 0;
out:
- mutex_unlock(&twl4030_madc.lock);
+ mutex_unlock(&the_madc->lock);
return ret;
}
-
EXPORT_SYMBOL(twl4030_madc_conversion);
-static int twl4030_madc_set_current_generator(int chan, int on)
+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
+ int chan, int on)
{
int ret;
u8 regval;
return ret;
}
-static int twl4030_madc_set_power(int on)
+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
{
u8 regval;
- twl4030_madc_read(TWL4030_MADC_CTRL1, ®val);
+ regval = twl4030_madc_read(madc, TWL4030_MADC_CTRL1);
if (on)
regval |= TWL4030_MADC_MADCON;
else
regval &= ~TWL4030_MADC_MADCON;
- twl4030_madc_write(TWL4030_MADC_CTRL1, regval);
+ twl4030_madc_write(madc, TWL4030_MADC_CTRL1, regval);
return 0;
}
ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
if (ret) {
- printk(KERN_ERR TWL4030_MADC_PFX "copy_from_user: %d\n", ret);
+ dev_dbg(the_madc->dev, "copy_from_user: %d\n", ret);
return -EACCES;
}
if (par.channel >= TWL4030_MADC_MAX_CHANNELS)
return -EINVAL;
- req.channels = (1<<par.channel);
+ req.channels = (1 << par.channel);
req.do_avg = par.average;
req.method = TWL4030_MADC_SW1;
req.func_cb = NULL;
ret = copy_to_user((void __user *) arg, &par, sizeof(par));
if (ret) {
- printk(KERN_ERR TWL4030_MADC_PFX "copy_to_user: %d\n", ret);
+ dev_dbg(the_madc->dev, "copy_to_user: %d\n", ret);
return -EACCES;
}
static struct miscdevice twl4030_madc_device = {
.minor = MISC_DYNAMIC_MINOR,
- .name = "twl4030-adc",
+ .name = "twl4030-madc",
.fops = &twl4030_madc_fileops
};
-static int __init twl4030_madc_init(void)
+static int __init twl4030_madc_probe(struct platform_device *pdev)
{
+ struct twl4030_madc_data *madc;
+ struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
int ret;
u8 regval;
+ madc = kzalloc(sizeof *madc, GFP_KERNEL);
+ if (!madc)
+ return -ENOMEM;
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "platform_data not available\n");
+ ret = -EINVAL;
+ goto err_pdata;
+ }
+
+ madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
+ madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+
ret = misc_register(&twl4030_madc_device);
- if (ret == -1) {
- printk(KERN_ERR TWL4030_MADC_PFX "misc_register() failed!\n");
- return ret;
+ if (ret) {
+ dev_dbg(&pdev->dev, "could not register misc_device\n");
+ goto err_misc;
}
- twl4030_madc_set_power(1);
- twl4030_madc_set_current_generator(0, 1);
+ twl4030_madc_set_power(madc, 1);
+ twl4030_madc_set_current_generator(madc, 0, 1);
ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
®val, TWL4030_BCI_BCICTL1);
regval, TWL4030_BCI_BCICTL1);
ret = request_irq(TWL4030_MODIRQ_MADC, twl4030_madc_irq_handler,
- IRQF_DISABLED, "twl4030_madc", &twl4030_madc);
- if (ret)
- printk(KERN_ERR TWL4030_MADC_PFX "request_irq: %d\n", ret);
+ IRQF_DISABLED, "twl4030_madc", madc);
+ if (ret) {
+ dev_dbg(&pdev->dev, "could not request irq\n");
+ goto err_irq;
+ }
- mutex_init(&twl4030_madc.lock);
+ platform_set_drvdata(pdev, madc);
+ mutex_init(&madc->lock);
+ INIT_WORK(&madc->ws, twl4030_madc_work);
- INIT_WORK(&twl4030_madc.ws, twl4030_madc_work);
+ the_madc = madc;
- printk(KERN_INFO TWL4030_MADC_PFX "initialised\n");
+ return 0;
+
+err_irq:
+ misc_deregister(&twl4030_madc_device);
+
+err_misc:
+err_pdata:
+ kfree(madc);
return ret;
}
-static void __exit twl4030_madc_exit(void)
+static int __exit twl4030_madc_remove(struct platform_device *pdev)
{
- twl4030_madc_set_power(0);
- twl4030_madc_set_current_generator(0, 0);
- free_irq(TWL4030_MODIRQ_MADC, &twl4030_madc);
- cancel_work_sync(&twl4030_madc.ws);
+ struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+
+ twl4030_madc_set_power(madc, 0);
+ twl4030_madc_set_current_generator(madc, 0, 0);
+ free_irq(TWL4030_MODIRQ_MADC, madc);
+ cancel_work_sync(&madc->ws);
misc_deregister(&twl4030_madc_device);
+
+ return 0;
}
+static struct platform_driver twl4030_madc_driver = {
+ .probe = twl4030_madc_probe,
+ .remove = __exit_p(twl4030_madc_remove),
+ .driver = {
+ .name = "twl4030_madc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init twl4030_madc_init(void)
+{
+ return platform_driver_register(&twl4030_madc_driver);
+}
module_init(twl4030_madc_init);
+
+static void __exit twl4030_madc_exit(void)
+{
+ platform_driver_unregister(&twl4030_madc_driver);
+}
module_exit(twl4030_madc_exit);
-MODULE_ALIAS("i2c:twl4030-adc");
+MODULE_ALIAS("platform:twl4030-madc");
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("twl4030 ADC driver");
MODULE_LICENSE("GPL");
+