summaryrefslogtreecommitdiff
path: root/drivers/mfd/rt5033_core.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/mfd/rt5033_core.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/mfd/rt5033_core.c')
-rw-r--r--drivers/mfd/rt5033_core.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/drivers/mfd/rt5033_core.c b/drivers/mfd/rt5033_core.c
new file mode 100644
index 00000000..b89b21e2
--- /dev/null
+++ b/drivers/mfd/rt5033_core.c
@@ -0,0 +1,592 @@
+/* drivers/mfd/rt5033_core.c
+ * RT5033 Multifunction Device Driver
+ * Charger / Buck / LDOs / FlashLED
+ *
+ * Copyright (C) 2013 Richtek Technology Corp.
+ * Author: Patrick Chang <patrick_chang@richtek.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; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/rt5033.h>
+#include <linux/mfd/rt5033_irq.h>
+#include <linux/battery/charger/rt5033_charger.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#ifdef CONFIG_OF
+#if defined(CONFIG_MFD_RT5033_USE_DT) || (LINUX_VERSION_CODE>=KERNEL_VERSION(3,10,0))
+#define RT5033_USE_NEW_MFD_DT_API
+#endif
+#endif
+
+#define RT5033_DECLARE_IRQ(irq) { \
+ irq, irq, \
+ irq##_NAME, IORESOURCE_IRQ }
+
+
+#ifdef CONFIG_CHARGER_RT5033
+const static struct resource rt5033_charger_res[] = {
+ RT5033_DECLARE_IRQ(RT5033_ADPBAD_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_PPBATLV_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHTERMI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_VINOVPI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_TSDI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHMIVRI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHTREGI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHTMRFI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHRCHGI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_IEOC_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHBATOVI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_CHRVPI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_BSTLOWVI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_BSTOLI_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_BSTVMIDOVP_IRQ),
+};
+
+static struct mfd_cell rt5033_charger_devs[] = {
+ {
+ .name = "rt5033-charger",
+ .num_resources = ARRAY_SIZE(rt5033_charger_res),
+ .id = -1,
+ .resources = rt5033_charger_res,
+#ifdef RT5033_USE_NEW_MFD_DT_API
+ .of_compatible = "richtek,rt5033-charger"
+#endif
+ },
+};
+#endif /*CONFIG_CHARGER_RT5033*/
+
+#ifdef CONFIG_FLED_RT5033
+const static struct resource rt5033_fled_res[] = {
+ RT5033_DECLARE_IRQ(RT5033_VF_L_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_LEDCS2_SHORT_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_LEDCS1_SHORT_IRQ),
+};
+static struct mfd_cell rt5033_fled_devs[] = {
+ {
+ .name = "rt5033-fled",
+ .num_resources = ARRAY_SIZE(rt5033_fled_res),
+ .id = -1,
+ .resources = rt5033_fled_res,
+#ifdef RT5033_USE_NEW_MFD_DT_API
+ .of_compatible = "richtek,rt5033-fled"
+#endif
+ },
+};
+#endif /*CONFIG_FLED_RT5033*/
+
+#ifdef CONFIG_REGULATOR_RT5033
+const static struct resource rt5033_regulator_res_LDO_SAFE[] = {
+ RT5033_DECLARE_IRQ(RT5033_SAFE_LDO_LV_IRQ),
+};
+
+const static struct resource rt5033_regulator_res_LDO1[] = {
+ RT5033_DECLARE_IRQ(RT5033_LDO_LV_IRQ),
+};
+
+const static struct resource rt5033_regulator_res_DCDC1[] = {
+ RT5033_DECLARE_IRQ(RT5033_BUCK_OCP_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_BUCK_LV_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_OT_IRQ),
+ RT5033_DECLARE_IRQ(RT5033_VDDA_UV_IRQ),
+};
+
+#define RT5033_OF_COMPATIBLE_LDO_SAFE "richtek,rt5033-safeldo"
+#define RT5033_OF_COMPATIBLE_LDO1 "richtek,rt5033-ldo1"
+#define RT5033_OF_COMPATIBLE_DCDC1 "richtek,rt5033-dcdc1"
+
+#ifdef RT5033_USE_NEW_MFD_DT_API
+#define REG_OF_COMP(_id) .of_compatible = RT5033_OF_COMPATIBLE_##_id,
+#else
+#define REG_OF_COMP(_id)
+#endif /* RT5033_USE_NEW_MFD_DT_API */
+
+#define RT5033_VR_DEVS(_id) \
+{ \
+ .name = "rt5033-regulator", \
+ .num_resources = ARRAY_SIZE(rt5033_regulator_res_##_id), \
+ .id = RT5033_ID_##_id, \
+ .resources = rt5033_regulator_res_##_id, \
+ REG_OF_COMP(_id) \
+}
+
+static struct mfd_cell rt5033_regulator_devs[] = {
+ RT5033_VR_DEVS(LDO_SAFE),
+ RT5033_VR_DEVS(LDO1),
+ RT5033_VR_DEVS(DCDC1),
+};
+#endif
+
+inline static int rt5033_read_device(struct i2c_client *i2c,
+ int reg, int bytes, void *dest)
+{
+ int ret;
+ if (bytes > 1) {
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest);
+ } else {
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ if (ret < 0)
+ return ret;
+ *(unsigned char *)dest = (unsigned char)ret;
+ }
+ return ret;
+}
+
+inline static int rt5033_write_device(struct i2c_client *i2c,
+ int reg, int bytes, const void *src)
+{
+ int ret;
+ const uint8_t *data;
+ if (bytes > 1)
+ ret = i2c_smbus_write_i2c_block_data(i2c, reg, bytes, src);
+ else {
+ data = src;
+ ret = i2c_smbus_write_byte_data(i2c, reg, *data);
+ }
+ return ret;
+}
+
+int rt5033_block_read_device(struct i2c_client *i2c,
+ int reg, int bytes, void *dest)
+{
+ struct rt5033_mfd_chip *chip = i2c_get_clientdata(i2c);
+ int ret;
+ mutex_lock(&chip->io_lock);
+ ret = rt5033_read_device(i2c, reg, bytes, dest);
+ mutex_unlock(&chip->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL(rt5033_block_read_device);
+
+int rt5033_block_write_device(struct i2c_client *i2c,
+ int reg, int bytes, const void *src)
+{
+ struct rt5033_mfd_chip *chip = i2c_get_clientdata(i2c);
+ int ret;
+ mutex_lock(&chip->io_lock);
+ ret = rt5033_write_device(i2c, reg, bytes, src);
+ mutex_unlock(&chip->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL(rt5033_block_write_device);
+
+int rt5033_reg_read(struct i2c_client *i2c, int reg)
+{
+ struct rt5033_mfd_chip *chip = i2c_get_clientdata(i2c);
+ unsigned char data = 0;
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = rt5033_read_device(i2c, reg, 1, &data);
+ mutex_unlock(&chip->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return (int)data;
+}
+EXPORT_SYMBOL(rt5033_reg_read);
+
+int rt5033_reg_write(struct i2c_client *i2c, int reg,
+ unsigned char data)
+{
+ struct rt5033_mfd_chip *chip = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = i2c_smbus_write_byte_data(i2c, reg, data);
+ mutex_unlock(&chip->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(rt5033_reg_write);
+
+int rt5033_assign_bits(struct i2c_client *i2c, int reg,
+ unsigned char mask, unsigned char data)
+{
+ struct rt5033_mfd_chip *chip = i2c_get_clientdata(i2c);
+ unsigned char value;
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = rt5033_read_device(i2c, reg, 1, &value);
+
+ if (ret < 0)
+ goto out;
+
+ value &= ~mask;
+ value |= data;
+ ret = i2c_smbus_write_byte_data(i2c, reg, value);
+
+out:
+ mutex_unlock(&chip->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL(rt5033_assign_bits);
+
+int rt5033_set_bits(struct i2c_client *i2c, int reg,
+ unsigned char mask)
+{
+ return rt5033_assign_bits(i2c, reg, mask, mask);
+}
+EXPORT_SYMBOL(rt5033_set_bits);
+
+int rt5033_clr_bits(struct i2c_client *i2c, int reg,
+ unsigned char mask)
+{
+ return rt5033_assign_bits(i2c, reg, mask, 0);
+}
+EXPORT_SYMBOL(rt5033_clr_bits);
+
+extern int rt5033_init_irq(rt5033_mfd_chip_t *chip);
+extern int rt5033_exit_irq(rt5033_mfd_chip_t *chip);
+
+static int rt5033mfd_parse_dt(struct device *dev,
+ rt5033_mfd_platform_data_t *pdata)
+{
+ int ret;
+ struct device_node *np = dev->of_node;
+ enum of_gpio_flags irq_gpio_flags;
+
+ ret = pdata->irq_gpio = of_get_named_gpio_flags(np, "rt5033,irq-gpio",
+ 0, &irq_gpio_flags);
+ if (ret < 0) {
+ dev_err(dev, "%s : can't get irq-gpio\r\n", __FUNCTION__);
+ return ret;
+ }
+
+ pdata->irq_base = -1;
+ ret = of_property_read_u32(np, "rt5033,irq-base", (u32 *)&pdata->irq_base);
+ if (ret < 0 || pdata->irq_base == -1) {
+ dev_info(dev, "%s : no assignment of irq_base, use irq_alloc_descs()\r\n",
+ __FUNCTION__);
+#if CONFIG_MFD_RT5033_EN_MRSTB
+ ret = pdata->mrstb_gpio = of_get_named_gpio_flags(np,
+ "rt5033,mrstb-gpio", 0, NULL);
+ if (ret < 0) {
+ dev_err(dev, "%s : can't get mrstb-gpio\r\n", __FUNCTION__);
+ return ret;
+ }
+#endif /* CONFIG_MFD_RT5033_EN_MRSTB */
+
+ }
+ return 0;
+}
+
+static int rt5033_mfd_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ u8 data = 0;
+ struct device_node *of_node = i2c->dev.of_node;
+ rt5033_mfd_chip_t *chip;
+ rt5033_mfd_platform_data_t *pdata = i2c->dev.platform_data;
+
+ pr_info("%s : RT5033 MFD Driver start probe\n", __func__);
+ if (of_node) {
+ pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&i2c->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_dt_nomem;
+ }
+ ret = rt5033mfd_parse_dt(&i2c->dev, pdata);
+ if (ret < 0)
+ goto err_parse_dt;
+ } else {
+ pdata = i2c->dev.platform_data;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ dev_err(&i2c->dev, "Memory is not enough.\n");
+ ret = -ENOMEM;
+ goto err_mfd_nomem;
+ }
+ chip->dev = &i2c->dev;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK);
+ if (!ret) {
+ ret = i2c_get_functionality(i2c->adapter);
+ dev_err(chip->dev, "I2C functionality is not supported.\n");
+ ret = -ENOSYS;
+ goto err_i2cfunc_not_support;
+ }
+
+ chip->i2c_client = i2c;
+ chip->pdata = pdata;
+
+ pr_info("%s:%s pdata->irq_base = %d\n",
+ "rt5033-mfd", __func__, pdata->irq_base);
+ /* if board-init had already assigned irq_base (>=0) ,
+ no need to allocate it;
+ assign -1 to let this driver allocate resource by itself*/
+ if (pdata->irq_base < 0)
+ pdata->irq_base = irq_alloc_descs(-1, 0, RT5033_IRQS_NR, 0);
+ if (pdata->irq_base < 0) {
+ pr_err("%s:%s irq_alloc_descs Fail! ret(%d)\n",
+ "rt5033-mfd", __func__, pdata->irq_base);
+ ret = -EINVAL;
+ goto irq_base_err;
+ } else {
+ chip->irq_base = pdata->irq_base;
+ pr_info("%s:%s irq_base = %d\n",
+ "rt5033-mfd", __func__, chip->irq_base);
+#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,4,0))
+ irq_domain_add_legacy(of_node, RT5033_IRQS_NR, chip->irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+#endif /*(LINUX_VERSION_CODE>=KERNEL_VERSION(3,4,0))*/
+ }
+
+ i2c_set_clientdata(i2c, chip);
+ mutex_init(&chip->io_lock);
+ mutex_init(&chip->suspend_flag_lock);
+
+#ifdef CONFIG_MFD_RT5033_EN_MRSTB
+ /* Set MRSTB GPIO pin to high level to indicate that
+ * * system is alive (do NOT do reset) */
+ ret = gpio_request(pdata->mrstb_gpio, "rt5033_mrstb");
+ if (ret == 0) {
+ ret = gpio_direction_output(pdata->mrstb_gpio, 0);
+ if (ret < 0)
+ pr_err("%s : cannot set GPIO%d output direction(%d)\n",
+ __func__, pdata->mrstb_gpio, ret);
+
+ } else
+ pr_info("%s : Request GPIO %d failed\n",
+ __func__, (int)pdata->mrstb_gpio);
+#endif /* CONFIG_MFD_RT5033_EN_MRSTB */
+
+ wake_lock_init(&(chip->irq_wake_lock), WAKE_LOCK_SUSPEND,
+ "rt5033mfd_wakelock");
+
+ /* To disable MRST function should be
+ finished before set any reg init-value*/
+ data = rt5033_reg_read(i2c, 0x47);
+ pr_info("%s : Manual Reset Data = 0x%x", __func__, data);
+ /* Disable Manual Reset and set debounce time = 3 sec*/
+ rt5033_assign_bits(i2c, 0x47, 0x0f, 0);
+
+ ret = rt5033_init_irq(chip);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error : can't initialize RT5033 MFD irq\n");
+ goto err_init_irq;
+ }
+
+#ifdef CONFIG_REGULATOR_RT5033
+#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0))
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_regulator_devs[0],
+ ARRAY_SIZE(rt5033_regulator_devs),
+ NULL, chip->irq_base, NULL);
+#else
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_regulator_devs[0],
+ ARRAY_SIZE(rt5033_regulator_devs),
+ NULL, chip->irq_base);
+#endif
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error : can't add regulator\n");
+ goto err_add_regulator_devs;
+ }
+#endif /*CONFIG_REGULATOR_RT5033*/
+
+#ifdef CONFIG_FLED_RT5033
+#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0))
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_fled_devs[0],
+ ARRAY_SIZE(rt5033_fled_devs),
+ NULL, chip->irq_base, NULL);
+#else
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_fled_devs[0],
+ ARRAY_SIZE(rt5033_fled_devs),
+ NULL, chip->irq_base);
+#endif
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed : add FlashLED devices");
+ goto err_add_fled_devs;
+ }
+#endif /*CONFIG_FLED_RT5033*/
+
+
+#ifdef CONFIG_CHARGER_RT5033
+#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0))
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_charger_devs[0],
+ ARRAY_SIZE(rt5033_charger_devs),
+ NULL, chip->irq_base, NULL);
+#else
+ ret = mfd_add_devices(chip->dev, 0, &rt5033_charger_devs[0],
+ ARRAY_SIZE(rt5033_charger_devs),
+ NULL, chip->irq_base);
+#endif
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed : add charger devices\n");
+ goto err_add_chg_devs;
+ }
+#endif /*CONFIG_CHARGER_RT5033*/
+
+ pr_info("%s : RT5033 MFD Driver Fin probe\n", __func__);
+ return ret;
+
+#ifdef CONFIG_CHARGER_RT5033
+err_add_chg_devs:
+#endif /*CONFIG_CHARGER_RT5033*/
+
+#ifdef CONFIG_FLED_RT5033
+err_add_fled_devs:
+#endif /*CONFIG_FLED_RT5033*/
+ mfd_remove_devices(chip->dev);
+#ifdef CONFIG_REGULATOR_RT5033
+err_add_regulator_devs:
+#endif /*CONFIG_REGULATOR_RT5033*/
+err_init_irq:
+ wake_lock_destroy(&(chip->irq_wake_lock));
+ mutex_destroy(&chip->io_lock);
+ kfree(chip);
+irq_base_err:
+err_mfd_nomem:
+err_i2cfunc_not_support:
+err_parse_dt:
+err_dt_nomem:
+ return ret;
+}
+
+static int rt5033_mfd_remove(struct i2c_client *i2c)
+{
+ rt5033_mfd_chip_t *chip = i2c_get_clientdata(i2c);
+
+ pr_info("%s : RT5033 MFD Driver remove\n", __func__);
+#ifdef CONFIG_MFD_RT5033_EN_MRSTB
+ gpio_free(chip->pdata->mrstb_gpio);
+#endif /* CONFIG_MFD_RT5033_EN_MRSTB */
+
+ mfd_remove_devices(chip->dev);
+ wake_lock_destroy(&(chip->irq_wake_lock));
+ mutex_destroy(&chip->suspend_flag_lock);
+ mutex_destroy(&chip->io_lock);
+ kfree(chip);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+extern int rt5033_irq_suspend(rt5033_mfd_chip_t *chip);
+extern int rt5033_irq_resume(rt5033_mfd_chip_t *chip);
+
+int rt5033_mfd_suspend(struct device *dev)
+{
+
+ struct i2c_client *i2c =
+ container_of(dev, struct i2c_client, dev);
+
+ rt5033_mfd_chip_t *chip = i2c_get_clientdata(i2c);
+ BUG_ON(chip == NULL);
+ return rt5033_irq_suspend(chip);
+}
+
+int rt5033_mfd_resume(struct device *dev)
+{
+ struct i2c_client *i2c =
+ container_of(dev, struct i2c_client, dev);
+ rt5033_mfd_chip_t *chip = i2c_get_clientdata(i2c);
+ BUG_ON(chip == NULL);
+ return rt5033_irq_resume(chip);
+}
+#endif /* CONFIG_PM */
+
+
+const static uint8_t rt5033_chg_group1_default[] = { 0x40, 0x58 };
+const static uint8_t rt5033_chg_group2_default[] = {0x41, 0xAB, 0x35};
+
+static void rt5033_mfd_shutdown(struct i2c_client *client)
+{
+ struct i2c_client *i2c = client;
+ /* Force to enable charger & reset charger before shutdown */
+ rt5033_clr_bits(i2c, RT5033_CHG_STAT_CTRL, RT5033_CHGENB_MASK);
+ /* Set all charger settings to default values */
+ rt5033_block_write_device(i2c, RT5033_CHG_CTRL1,
+ sizeof(rt5033_chg_group1_default),
+ rt5033_chg_group1_default);
+ rt5033_block_write_device(i2c, RT5033_CHG_CTRL3,
+ sizeof(rt5033_chg_group2_default),
+ rt5033_chg_group2_default);
+}
+
+static const struct i2c_device_id rt5033_mfd_id_table[] = {
+ { "rt5033-mfd", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, rt5033_id_table);
+
+
+#ifdef CONFIG_PM
+const struct dev_pm_ops rt5033_pm = {
+ .suspend = rt5033_mfd_suspend,
+ .resume = rt5033_mfd_resume,
+
+};
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id rt5033_match_table[] = {
+ { .compatible = "richtek,rt5033mfd",},
+ {},
+};
+#else
+#define rt5033_match_table NULL
+#endif
+
+static struct i2c_driver rt5033_mfd_driver = {
+ .driver = {
+ .name = "rt5033-mfd",
+ .owner = THIS_MODULE,
+ .of_match_table = rt5033_match_table,
+#ifdef CONFIG_PM
+ .pm = &rt5033_pm,
+#endif
+ },
+ .shutdown = rt5033_mfd_shutdown,
+ .probe = rt5033_mfd_probe,
+ .remove = rt5033_mfd_remove,
+ .id_table = rt5033_mfd_id_table,
+};
+
+static int __init rt5033_mfd_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&rt5033_mfd_driver);
+ if (ret != 0)
+ pr_info("%s : Failed to register RT5033 MFD I2C driver\n",
+ __func__);
+
+ return ret;
+}
+subsys_initcall(rt5033_mfd_i2c_init);
+
+static void __exit rt5033_mfd_i2c_exit(void)
+{
+ i2c_del_driver(&rt5033_mfd_driver);
+}
+module_exit(rt5033_mfd_i2c_exit);
+
+MODULE_DESCRIPTION("Richtek RT5033 MFD I2C Driver");
+MODULE_AUTHOR("Patrick Chang <patrick_chang@richtek.com>");
+MODULE_VERSION(RT5033_DRV_VER);
+MODULE_LICENSE("GPL");