|
@@ -0,0 +1,563 @@
|
|
|
+/*
|
|
|
+ * Base driver for Dialog Semiconductor DA9030/DA9034
|
|
|
+ *
|
|
|
+ * Copyright (C) 2008 Compulab, Ltd.
|
|
|
+ * Mike Rapoport <mike@compulab.co.il>
|
|
|
+ *
|
|
|
+ * Copyright (C) 2006-2008 Marvell International Ltd.
|
|
|
+ * Eric Miao <eric.miao@marvell.com>
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
|
+ * published by the Free Software Foundation.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/i2c.h>
|
|
|
+#include <linux/mfd/da903x.h>
|
|
|
+
|
|
|
+#define DA9030_CHIP_ID 0x00
|
|
|
+#define DA9030_EVENT_A 0x01
|
|
|
+#define DA9030_EVENT_B 0x02
|
|
|
+#define DA9030_EVENT_C 0x03
|
|
|
+#define DA9030_STATUS 0x04
|
|
|
+#define DA9030_IRQ_MASK_A 0x05
|
|
|
+#define DA9030_IRQ_MASK_B 0x06
|
|
|
+#define DA9030_IRQ_MASK_C 0x07
|
|
|
+#define DA9030_SYS_CTRL_A 0x08
|
|
|
+#define DA9030_SYS_CTRL_B 0x09
|
|
|
+#define DA9030_FAULT_LOG 0x0a
|
|
|
+
|
|
|
+#define DA9034_CHIP_ID 0x00
|
|
|
+#define DA9034_EVENT_A 0x01
|
|
|
+#define DA9034_EVENT_B 0x02
|
|
|
+#define DA9034_EVENT_C 0x03
|
|
|
+#define DA9034_EVENT_D 0x04
|
|
|
+#define DA9034_STATUS_A 0x05
|
|
|
+#define DA9034_STATUS_B 0x06
|
|
|
+#define DA9034_IRQ_MASK_A 0x07
|
|
|
+#define DA9034_IRQ_MASK_B 0x08
|
|
|
+#define DA9034_IRQ_MASK_C 0x09
|
|
|
+#define DA9034_IRQ_MASK_D 0x0a
|
|
|
+#define DA9034_SYS_CTRL_A 0x0b
|
|
|
+#define DA9034_SYS_CTRL_B 0x0c
|
|
|
+#define DA9034_FAULT_LOG 0x0d
|
|
|
+
|
|
|
+struct da903x_chip;
|
|
|
+
|
|
|
+struct da903x_chip_ops {
|
|
|
+ int (*init_chip)(struct da903x_chip *);
|
|
|
+ int (*unmask_events)(struct da903x_chip *, unsigned int events);
|
|
|
+ int (*mask_events)(struct da903x_chip *, unsigned int events);
|
|
|
+ int (*read_events)(struct da903x_chip *, unsigned int *events);
|
|
|
+ int (*read_status)(struct da903x_chip *, unsigned int *status);
|
|
|
+};
|
|
|
+
|
|
|
+struct da903x_chip {
|
|
|
+ struct i2c_client *client;
|
|
|
+ struct device *dev;
|
|
|
+ struct da903x_chip_ops *ops;
|
|
|
+
|
|
|
+ int type;
|
|
|
+ uint32_t events_mask;
|
|
|
+
|
|
|
+ struct mutex lock;
|
|
|
+ struct work_struct irq_work;
|
|
|
+
|
|
|
+ struct blocking_notifier_head notifier_list;
|
|
|
+};
|
|
|
+
|
|
|
+static inline int __da903x_read(struct i2c_client *client,
|
|
|
+ int reg, uint8_t *val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = i2c_smbus_read_byte_data(client, reg);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ *val = (uint8_t)ret;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int __da903x_reads(struct i2c_client *client, int reg,
|
|
|
+ int len, uint8_t *val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int __da903x_write(struct i2c_client *client,
|
|
|
+ int reg, uint8_t val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = i2c_smbus_write_byte_data(client, reg, val);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
|
|
|
+ val, reg);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int __da903x_writes(struct i2c_client *client, int reg,
|
|
|
+ int len, uint8_t *val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int da903x_register_notifier(struct device *dev, struct notifier_block *nb,
|
|
|
+ unsigned int events)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ chip->ops->unmask_events(chip, events);
|
|
|
+ return blocking_notifier_chain_register(&chip->notifier_list, nb);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_register_notifier);
|
|
|
+
|
|
|
+int da903x_unregister_notifier(struct device *dev, struct notifier_block *nb,
|
|
|
+ unsigned int events)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ chip->ops->mask_events(chip, events);
|
|
|
+ return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_unregister_notifier);
|
|
|
+
|
|
|
+int da903x_write(struct device *dev, int reg, uint8_t val)
|
|
|
+{
|
|
|
+ return __da903x_write(to_i2c_client(dev), reg, val);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_write);
|
|
|
+
|
|
|
+int da903x_read(struct device *dev, int reg, uint8_t *val)
|
|
|
+{
|
|
|
+ return __da903x_read(to_i2c_client(dev), reg, val);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_read);
|
|
|
+
|
|
|
+int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+ uint8_t reg_val;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&chip->lock);
|
|
|
+
|
|
|
+ ret = __da903x_read(chip->client, reg, ®_val);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if ((reg_val & bit_mask) == 0) {
|
|
|
+ reg_val |= bit_mask;
|
|
|
+ ret = __da903x_write(chip->client, reg, reg_val);
|
|
|
+ }
|
|
|
+out:
|
|
|
+ mutex_unlock(&chip->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_set_bits);
|
|
|
+
|
|
|
+int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+ uint8_t reg_val;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&chip->lock);
|
|
|
+
|
|
|
+ ret = __da903x_read(chip->client, reg, ®_val);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (reg_val & bit_mask) {
|
|
|
+ reg_val &= ~bit_mask;
|
|
|
+ ret = __da903x_write(chip->client, reg, reg_val);
|
|
|
+ }
|
|
|
+out:
|
|
|
+ mutex_unlock(&chip->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_clr_bits);
|
|
|
+
|
|
|
+int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+ uint8_t reg_val;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&chip->lock);
|
|
|
+
|
|
|
+ ret = __da903x_read(chip->client, reg, ®_val);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if ((reg_val & mask) != val) {
|
|
|
+ reg_val = (reg_val & ~mask) | val;
|
|
|
+ ret = __da903x_write(chip->client, reg, reg_val);
|
|
|
+ }
|
|
|
+out:
|
|
|
+ mutex_unlock(&chip->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(da903x_update);
|
|
|
+
|
|
|
+int da903x_query_status(struct device *dev, unsigned int sbits)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = dev_get_drvdata(dev);
|
|
|
+ unsigned int status = 0;
|
|
|
+
|
|
|
+ chip->ops->read_status(chip, &status);
|
|
|
+ return ((status & sbits) == sbits);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(da903x_query_status);
|
|
|
+
|
|
|
+static int __devinit da9030_init_chip(struct da903x_chip *chip)
|
|
|
+{
|
|
|
+ uint8_t chip_id;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = __da903x_read(chip->client, DA9030_CHIP_ID, &chip_id);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = __da903x_write(chip->client, DA9030_SYS_CTRL_A, 0xE8);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ dev_info(chip->dev, "DA9030 (CHIP ID: 0x%02x) detected\n", chip_id);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int da9030_unmask_events(struct da903x_chip *chip, unsigned int events)
|
|
|
+{
|
|
|
+ uint8_t v[3];
|
|
|
+
|
|
|
+ chip->events_mask &= ~events;
|
|
|
+
|
|
|
+ v[0] = (chip->events_mask & 0xff);
|
|
|
+ v[1] = (chip->events_mask >> 8) & 0xff;
|
|
|
+ v[2] = (chip->events_mask >> 16) & 0xff;
|
|
|
+
|
|
|
+ return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
|
|
|
+}
|
|
|
+
|
|
|
+static int da9030_mask_events(struct da903x_chip *chip, unsigned int events)
|
|
|
+{
|
|
|
+ uint8_t v[3];
|
|
|
+
|
|
|
+ chip->events_mask &= ~events;
|
|
|
+
|
|
|
+ v[0] = (chip->events_mask & 0xff);
|
|
|
+ v[1] = (chip->events_mask >> 8) & 0xff;
|
|
|
+ v[2] = (chip->events_mask >> 16) & 0xff;
|
|
|
+
|
|
|
+ return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
|
|
|
+}
|
|
|
+
|
|
|
+static int da9030_read_events(struct da903x_chip *chip, unsigned int *events)
|
|
|
+{
|
|
|
+ uint8_t v[3] = {0, 0, 0};
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = __da903x_reads(chip->client, DA9030_EVENT_A, 3, v);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *events = (v[2] << 16) | (v[1] << 8) | v[0];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int da9030_read_status(struct da903x_chip *chip, unsigned int *status)
|
|
|
+{
|
|
|
+ return __da903x_read(chip->client, DA9030_STATUS, (uint8_t *)status);
|
|
|
+}
|
|
|
+
|
|
|
+static int da9034_init_chip(struct da903x_chip *chip)
|
|
|
+{
|
|
|
+ uint8_t chip_id;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = __da903x_read(chip->client, DA9034_CHIP_ID, &chip_id);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = __da903x_write(chip->client, DA9034_SYS_CTRL_A, 0xE8);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /* avoid SRAM power off during sleep*/
|
|
|
+ __da903x_write(chip->client, 0x10, 0x07);
|
|
|
+ __da903x_write(chip->client, 0x11, 0xff);
|
|
|
+ __da903x_write(chip->client, 0x12, 0xff);
|
|
|
+
|
|
|
+ /* Enable the ONKEY power down functionality */
|
|
|
+ __da903x_write(chip->client, DA9034_SYS_CTRL_B, 0x20);
|
|
|
+ __da903x_write(chip->client, DA9034_SYS_CTRL_A, 0x60);
|
|
|
+
|
|
|
+ /* workaround to make LEDs work */
|
|
|
+ __da903x_write(chip->client, 0x90, 0x01);
|
|
|
+ __da903x_write(chip->client, 0xB0, 0x08);
|
|
|
+
|
|
|
+ /* make ADTV1 and SDTV1 effective */
|
|
|
+ __da903x_write(chip->client, 0x20, 0x00);
|
|
|
+
|
|
|
+ dev_info(chip->dev, "DA9034 (CHIP ID: 0x%02x) detected\n", chip_id);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int da9034_unmask_events(struct da903x_chip *chip, unsigned int events)
|
|
|
+{
|
|
|
+ uint8_t v[4];
|
|
|
+
|
|
|
+ chip->events_mask &= ~events;
|
|
|
+
|
|
|
+ v[0] = (chip->events_mask & 0xff);
|
|
|
+ v[1] = (chip->events_mask >> 8) & 0xff;
|
|
|
+ v[2] = (chip->events_mask >> 16) & 0xff;
|
|
|
+ v[3] = (chip->events_mask >> 24) & 0xff;
|
|
|
+
|
|
|
+ return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
|
|
|
+}
|
|
|
+
|
|
|
+static int da9034_mask_events(struct da903x_chip *chip, unsigned int events)
|
|
|
+{
|
|
|
+ uint8_t v[4];
|
|
|
+
|
|
|
+ chip->events_mask |= events;
|
|
|
+
|
|
|
+ v[0] = (chip->events_mask & 0xff);
|
|
|
+ v[1] = (chip->events_mask >> 8) & 0xff;
|
|
|
+ v[2] = (chip->events_mask >> 16) & 0xff;
|
|
|
+ v[3] = (chip->events_mask >> 24) & 0xff;
|
|
|
+
|
|
|
+ return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
|
|
|
+}
|
|
|
+
|
|
|
+static int da9034_read_events(struct da903x_chip *chip, unsigned int *events)
|
|
|
+{
|
|
|
+ uint8_t v[4] = {0, 0, 0, 0};
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = __da903x_reads(chip->client, DA9034_EVENT_A, 4, v);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *events = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int da9034_read_status(struct da903x_chip *chip, unsigned int *status)
|
|
|
+{
|
|
|
+ uint8_t v[2] = {0, 0};
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ ret = __da903x_reads(chip->client, DA9034_STATUS_A, 2, v);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *status = (v[1] << 8) | v[0];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void da903x_irq_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip =
|
|
|
+ container_of(work, struct da903x_chip, irq_work);
|
|
|
+ unsigned int events = 0;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ if (chip->ops->read_events(chip, &events))
|
|
|
+ break;
|
|
|
+
|
|
|
+ events &= ~chip->events_mask;
|
|
|
+ if (events == 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ blocking_notifier_call_chain(
|
|
|
+ &chip->notifier_list, events, NULL);
|
|
|
+ }
|
|
|
+ enable_irq(chip->client->irq);
|
|
|
+}
|
|
|
+
|
|
|
+static int da903x_irq_handler(int irq, void *data)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = data;
|
|
|
+
|
|
|
+ disable_irq_nosync(irq);
|
|
|
+ (void)schedule_work(&chip->irq_work);
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+static struct da903x_chip_ops da903x_ops[] = {
|
|
|
+ [0] = {
|
|
|
+ .init_chip = da9030_init_chip,
|
|
|
+ .unmask_events = da9030_unmask_events,
|
|
|
+ .mask_events = da9030_mask_events,
|
|
|
+ .read_events = da9030_read_events,
|
|
|
+ .read_status = da9030_read_status,
|
|
|
+ },
|
|
|
+ [1] = {
|
|
|
+ .init_chip = da9034_init_chip,
|
|
|
+ .unmask_events = da9034_unmask_events,
|
|
|
+ .mask_events = da9034_mask_events,
|
|
|
+ .read_events = da9034_read_events,
|
|
|
+ .read_status = da9034_read_status,
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+static const struct i2c_device_id da903x_id_table[] = {
|
|
|
+ { "da9030", 0 },
|
|
|
+ { "da9034", 1 },
|
|
|
+ { },
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(i2c, da903x_id_table);
|
|
|
+
|
|
|
+static int __devexit __remove_subdev(struct device *dev, void *unused)
|
|
|
+{
|
|
|
+ platform_device_unregister(to_platform_device(dev));
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int __devexit da903x_remove_subdevs(struct da903x_chip *chip)
|
|
|
+{
|
|
|
+ return device_for_each_child(chip->dev, NULL, __remove_subdev);
|
|
|
+}
|
|
|
+
|
|
|
+static int __devinit da903x_add_subdevs(struct da903x_chip *chip,
|
|
|
+ struct da903x_platform_data *pdata)
|
|
|
+{
|
|
|
+ struct da903x_subdev_info *subdev;
|
|
|
+ struct platform_device *pdev;
|
|
|
+ int i, ret = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < pdata->num_subdevs; i++) {
|
|
|
+ subdev = &pdata->subdevs[i];
|
|
|
+
|
|
|
+ pdev = platform_device_alloc(subdev->name, subdev->id);
|
|
|
+
|
|
|
+ pdev->dev.parent = chip->dev;
|
|
|
+ pdev->dev.platform_data = subdev->platform_data;
|
|
|
+
|
|
|
+ ret = platform_device_add(pdev);
|
|
|
+ if (ret)
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+failed:
|
|
|
+ da903x_remove_subdevs(chip);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int __devinit da903x_probe(struct i2c_client *client,
|
|
|
+ const struct i2c_device_id *id)
|
|
|
+{
|
|
|
+ struct da903x_platform_data *pdata = client->dev.platform_data;
|
|
|
+ struct da903x_chip *chip;
|
|
|
+ unsigned int tmp;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ chip->client = client;
|
|
|
+ chip->dev = &client->dev;
|
|
|
+ chip->ops = &da903x_ops[id->driver_data];
|
|
|
+
|
|
|
+ mutex_init(&chip->lock);
|
|
|
+ INIT_WORK(&chip->irq_work, da903x_irq_work);
|
|
|
+ BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
|
|
|
+
|
|
|
+ i2c_set_clientdata(client, chip);
|
|
|
+
|
|
|
+ ret = chip->ops->init_chip(chip);
|
|
|
+ if (ret)
|
|
|
+ goto out_free_chip;
|
|
|
+
|
|
|
+ /* mask and clear all IRQs */
|
|
|
+ chip->events_mask = 0xffffffff;
|
|
|
+ chip->ops->mask_events(chip, chip->events_mask);
|
|
|
+ chip->ops->read_events(chip, &tmp);
|
|
|
+
|
|
|
+ ret = request_irq(client->irq, da903x_irq_handler,
|
|
|
+ IRQF_DISABLED | IRQF_TRIGGER_FALLING,
|
|
|
+ "da903x", chip);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(&client->dev, "failed to request irq %d\n",
|
|
|
+ client->irq);
|
|
|
+ goto out_free_chip;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = da903x_add_subdevs(chip, pdata);
|
|
|
+ if (ret)
|
|
|
+ goto out_free_irq;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_free_irq:
|
|
|
+ free_irq(client->irq, chip);
|
|
|
+out_free_chip:
|
|
|
+ i2c_set_clientdata(client, NULL);
|
|
|
+ kfree(chip);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int __devexit da903x_remove(struct i2c_client *client)
|
|
|
+{
|
|
|
+ struct da903x_chip *chip = i2c_get_clientdata(client);
|
|
|
+
|
|
|
+ da903x_remove_subdevs(chip);
|
|
|
+ kfree(chip);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct i2c_driver da903x_driver = {
|
|
|
+ .driver = {
|
|
|
+ .name = "da903x",
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ },
|
|
|
+ .probe = da903x_probe,
|
|
|
+ .remove = __devexit_p(da903x_remove),
|
|
|
+ .id_table = da903x_id_table,
|
|
|
+};
|
|
|
+
|
|
|
+static int __init da903x_init(void)
|
|
|
+{
|
|
|
+ return i2c_add_driver(&da903x_driver);
|
|
|
+}
|
|
|
+module_init(da903x_init);
|
|
|
+
|
|
|
+static void __exit da903x_exit(void)
|
|
|
+{
|
|
|
+ i2c_del_driver(&da903x_driver);
|
|
|
+}
|
|
|
+module_exit(da903x_exit);
|
|
|
+
|
|
|
+MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034");
|
|
|
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
|
|
|
+ "Mike Rapoport <mike@compulab.co.il>");
|
|
|
+MODULE_LICENSE("GPL");
|