blob: 35f3a770805e988ae78d01e8cbcbac97b58917ed [file] [log] [blame]
/*
* muic_task.c
*
* Copyright (C) 2014 Samsung Electronics
* Thomas Ryu <smilesr.ryu@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/host_notify.h>
#include <linux/muic/muic.h>
#include <linux/mfd/muic_mfd.h>
#if defined(CONFIG_SEC_DEBUG)
#include <linux/mfd/max77865.h>
#include <linux/mfd/max77865-private.h>
#endif
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic_notifier.h>
#endif /* CONFIG_MUIC_NOTIFIER */
#if defined(CONFIG_VBUS_NOTIFIER)
#include <linux/vbus_notifier.h>
#endif /* CONFIG_VBUS_NOTIFIER */
#if defined (CONFIG_OF)
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif /* CONFIG_OF */
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
#include <linux/ccic/ccic_notifier.h>
#endif
#include "muic-internal.h"
#include "muic_apis.h"
#include "muic_state.h"
#include "muic_vps.h"
#include "muic_i2c.h"
#include "muic_sysfs.h"
#include "muic_debug.h"
#include "muic_dt.h"
#include "muic_regmap.h"
#include "muic_regmap_max77865.h"
#include "muic_coagent.h"
#include "../../battery_v2/include/sec_charging_common.h"
#if defined(CONFIG_MUIC_HV_MAX77865)
#include "muic_hv.h"
#include "muic_hv_max77865.h"
#endif
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
#include "muic_ccic.h"
#endif
#if defined(CONFIG_USB_EXTERNAL_NOTIFY)
#include "muic_usb.h"
#endif
#if defined(CONFIG_SEC_FACTORY)
int f_opmode;
#endif
extern struct muic_platform_data muic_pdata;
extern void muic_send_dock_intent(int type);
static bool muic_online = false;
/*
* 0 : normal, 1: abnormal
* This flag is set by USB for an abnormal HMT and
* cleared by MUIC on detachment.
*/
static int muic_hmt_status;
bool muic_is_online(void)
{
return muic_online;
}
void muic_set_hmt_status(int status)
{
pr_info("%s:%s hmt_status=[%s]\n", MUIC_DEV_NAME, __func__,
status ? "abnormal" : "normal");
muic_hmt_status = status;
if (status)
muic_send_dock_intent(MUIC_DOCK_ABNORMAL);
}
#if 0
static int muic_get_hmt_status(void)
{
return muic_hmt_status;
}
#endif
#if defined(CONFIG_VBUS_NOTIFIER)
static void muic_handle_vbus(muic_data_t *pmuic)
{
vbus_status_t status = pmuic->vps.t.vbvolt ?
STATUS_VBUS_HIGH: STATUS_VBUS_LOW;
pr_info("%s:%s <%d>\n", MUIC_DEV_NAME, __func__, pmuic->vps.t.vbvolt);
#if 0
if (pmuic->attached_dev == ATTACHED_DEV_HMT_MUIC) {
if (muic_get_hmt_status()) {
pr_info("%s:%s Abnormal HMT -> VBUS_UNKNOWN Noti.\n",
MUIC_DEV_NAME, __func__);
status = STATUS_VBUS_UNKNOWN;
}
}
#endif
vbus_notifier_handle(status);
return;
}
#else
static void muic_handle_vbus(muic_data_t *pmuic)
{
pr_info("%s:%s <%d> Not implemented.\n", MUIC_DEV_NAME,
__func__, pmuic->vps.t.vbvolt);
}
#endif
static void max77865_restore(muic_data_t *pmuic)
{
struct i2c_client *i2c = pmuic->i2c;
int ccdet;
muic_i2c_write_byte(i2c, MAX77865_REG_INTMASK_MAIN, INTMASK_MAIN_RESTORE);
muic_i2c_write_byte(i2c, MAX77865_REG_INTMASK_BC, INTMASK_BC_RESTORE);
muic_i2c_write_byte(i2c, MAX77865_REG_INTMASK_FC, INTMASK_FC_RESTORE);
muic_i2c_write_byte(i2c, MAX77865_REG_INTMASK_GP, INTMASK_GP_RESTORE);
muic_i2c_write_byte(i2c, MAX77865_REG_CCDET, CCDET_RESTORE);
ccdet = muic_i2c_read_byte(i2c, MAX77865_REG_CCDET);
pr_info("%s:%s \n", MUIC_DEV_NAME, __func__);
muic_print_reg_dump(pmuic);
pr_info("CCDET[0x%02x]\n", ccdet);
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
}
static irqreturn_t max77865_muic_irq_handler(muic_data_t *pmuic, int irq)
{
struct i2c_client *i2c = pmuic->i2c;
int irq_main, irq_bc, irq_fc, irq_gp, irq_vbus, irq_ovp, irq_dcdtmr, irq_reset;
union power_supply_propval val;
#if defined(CONFIG_MUIC_HV_MAX77865)
int irq_hv, irq_hv1, irq_hv2, irq_hv3, irq_hv4;
#endif
pr_info("%s:%s irq(%d)\n", pmuic->chip_name, __func__, irq);
/* read and clear interrupt status bits */
irq_main = muic_i2c_read_byte(i2c, MAX77865_REG_INT_MAIN);
irq_bc = muic_i2c_read_byte(i2c, MAX77865_REG_INT_BC);
irq_fc = muic_i2c_read_byte(i2c, MAX77865_REG_INT_FC);
irq_gp = muic_i2c_read_byte(i2c, MAX77865_REG_INT_GP);
muic_i2c_write_byte(i2c, MAX77865_REG_INT_MAIN, INT_MAIN_CLEAR);
muic_i2c_write_byte(i2c, MAX77865_REG_INT_BC, INT_BC_CLEAR);
muic_i2c_write_byte(i2c, MAX77865_REG_INT_FC, INT_FC_CLEAR);
muic_i2c_write_byte(i2c, MAX77865_REG_INT_GP, INT_GP_CLEAR);
if ((irq_main < 0) || (irq_bc < 0) || (irq_fc < 0) || (irq_gp < 0)) {
pr_err("%s: err read interrupt status [1:0x%x, 2:0x%x, 3:0x%x, 4:0x%x]\n",
__func__, irq_main, irq_bc, irq_fc, irq_gp);
return INT_REQ_DISCARD;
}
pr_info("%s:%s intr[1:0x%x, 2:0x%x, 3:0x%x, 4:0x%x]\n",
pmuic->chip_name, __func__, irq_main, irq_bc, irq_fc, irq_gp);
if (irq < 0)
return INT_REQ_DONE;
/* W/A for irq value none when irq occured [Except when driver init] */
if ((irq_main == 0) && (irq_bc == 0) && (irq_fc == 0) && (irq_gp == 0)) {
pr_info("%s: irq occured. but nothing irq information.\n", __func__);
return INT_REQ_NONE;
}
irq_vbus = irq_bc & MAX77865_VBUS_MASK;
irq_ovp = irq_bc & MAX77865_OVP_MASK;
irq_dcdtmr = irq_bc & MAX77865_DCDTMR_MASK;
irq_reset = irq_gp & MAX77865_RESET_MASK;
if (irq_reset) {
pr_info("%s RESET interrupt occured\n",__func__);
max77865_restore(pmuic);
val.intval = true;
psy_do_property("max77865-charger", set, POWER_SUPPLY_EXT_PROP_SURGE, val);
return INT_REQ_DONE;
}
/* VBUS Noti */
if (irq_vbus) {
pr_info("%s VBUS interrupt occured\n",__func__);
return INT_REQ_DONE_VB;
}
if (irq_ovp) {
pr_info("%s OVP interrupt occured\n",__func__);
return INT_REQ_OVP;
}
if (irq_dcdtmr) {
pr_info("%s DCDTMR interrupt occured. RID[%d]\n", __func__, pmuic->rid);
if (pmuic->rid != RID_619K)
pmuic->is_dcdtmr_intr = true;
}
#if defined(CONFIG_MUIC_HV_MAX77865)
irq_hv1 = irq_bc & MAX77865_VDNMON_MASK;
irq_hv2 = irq_fc & MAX77865_MRXRDY_MASK;
irq_hv3 = irq_fc & MAX77865_MPNACK_MASK;
irq_hv4 = irq_gp & MAX77865_VBADC_MASK;
irq_hv = irq_hv1 + irq_hv4;
if ((irq_hv > 0) || (irq_fc > 0)) {
pr_info("%s:%s HV intr: vdnmon(%d), mrxrdy(%d), mpnack(%d), vbadc(%d)\n",
pmuic->chip_name, __func__, irq_hv1, irq_hv2, irq_hv3, irq_hv4);
if (irq_hv1)
pmuic->phv->irq = MUIC_AFC_IRQ_VDNMON;
else if (irq_hv2)
pmuic->phv->irq = MUIC_AFC_IRQ_MRXRDY;
else if (irq_hv3)
pmuic->phv->irq = MUIC_AFC_IRQ_MPNACK;
else if (irq_hv4)
pmuic->phv->irq = MUIC_AFC_IRQ_VBADC;
return INT_REQ_DONE_HV;
}
#endif
return INT_REQ_DONE;
}
static irqreturn_t muic_irq_thread(int irq, void *data)
{
muic_data_t *pmuic = data;
int irq_ret;
mutex_lock(&pmuic->muic_mutex);
irq_ret = max77865_muic_irq_handler(pmuic, irq);
switch (irq_ret) {
case INT_REQ_DONE_VB:
case INT_REQ_DONE:
muic_detect_dev(pmuic, irq);
muic_handle_vbus(pmuic);
break;
#if defined(CONFIG_MUIC_HV_MAX77865)
case INT_REQ_DONE_HV:
if (pmuic->phv->is_muic_ready == false)
pr_info("%s:%s MUIC is not ready, just return\n", MUIC_HV_DEV_NAME, __func__);
else if (pmuic->phv->is_afc_muic_ready == false)
pr_info("%s:%s not ready yet(afc_muic_ready[%c])\n",
MUIC_HV_DEV_NAME, __func__, (pmuic->phv->is_afc_muic_ready ? 'T' : 'F'));
else if (pmuic->phv->is_charger_ready == false && irq != pmuic->phv->irq_vdnmon)
pr_info("%s:%s not ready yet(charger_ready[%c])\n",
MUIC_HV_DEV_NAME, __func__, (pmuic->phv->is_charger_ready ? 'T' : 'F'));
else if (pmuic->pdata->afc_disable)
pr_info("%s:%s AFC disable by USER (afc_disable[%c]\n",
MUIC_HV_DEV_NAME, __func__, (pmuic->pdata->afc_disable ? 'T' : 'F'));
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
else if (pmuic->afc_water_disable)
pr_info("%s:%s AFC disable by WATER (afc_water_disable[%c]\n",
MUIC_HV_DEV_NAME, __func__, (pmuic->afc_water_disable ? 'T' : 'F'));
#endif
else
max77865_hv_muic_detect_dev(pmuic->phv, pmuic->phv->irq);
break;
#endif
case INT_REQ_NONE:
case INT_REQ_OVP:
default:
break;
}
mutex_unlock(&pmuic->muic_mutex);
return IRQ_HANDLED;
}
static void muic_init_detect(muic_data_t *pmuic)
{
pr_info("%s:%s\n", pmuic->chip_name, __func__);
pmuic->is_muic_ready = true;
muic_irq_thread(-1, pmuic);
}
static int muic_irq_init(muic_data_t *pmuic)
{
struct i2c_client *i2c = pmuic->i2c;
struct muic_platform_data *pdata = pmuic->pdata;
int ret = 0;
pr_info("%s:%s\n", pmuic->chip_name, __func__);
if (!pdata->irq_gpio) {
pr_warn("%s:%s No interrupt specified\n", pmuic->chip_name,
__func__);
return -ENXIO;
}
i2c->irq = gpio_to_irq(pdata->irq_gpio);
if (i2c->irq) {
ret = request_threaded_irq(i2c->irq, NULL,
muic_irq_thread,
(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
"muic-irq", pmuic);
if (ret < 0) {
pr_err("%s:%s failed to reqeust IRQ(%d)\n",
pmuic->chip_name, __func__, i2c->irq);
return ret;
}
ret = enable_irq_wake(i2c->irq);
if (ret < 0)
pr_err("%s:%s failed to enable wakeup src\n",
pmuic->chip_name, __func__);
}
pmuic->irqs.irq_val = pdata->irq_gpio;
#if defined(CONFIG_MUIC_HV_MAX77865)
pmuic->phv->irq_vdnmon = MUIC_AFC_IRQ_VDNMON;
pmuic->phv->irq_mrxrdy = MUIC_AFC_IRQ_MRXRDY;
pmuic->phv->irq_mpnack = MUIC_AFC_IRQ_MPNACK;
pmuic->phv->irq_vbadc = MUIC_AFC_IRQ_VBADC;
#endif
return ret;
}
static int muic_probe(struct platform_device *pdev)
{
struct muic_mfd_dev *pmfd = dev_get_drvdata(pdev->dev.parent);
// struct muic_mfd_platform_data *mfd_pdata = dev_get_platdata(pmfd->dev);
struct muic_platform_data *pdata = &muic_pdata;
muic_data_t *pmuic;
struct regmap_desc *pdesc = NULL;
struct regmap_ops *pops = NULL;
int ret = 0;
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
pmuic = kzalloc(sizeof(muic_data_t), GFP_KERNEL);
if (!pmuic) {
pr_err("%s: failed to allocate driver data\n", __func__);
ret = -ENOMEM;
goto err_return;
}
#if defined(CONFIG_OF)
ret = of_muic_dt(pmfd->i2c, pdata, pmuic);
if (ret < 0) {
pr_err("%s:%s Failed to get device of_node \n",
MUIC_DEV_NAME, __func__);
goto err_io;
}
of_update_supported_list(pmfd->i2c, pdata);
vps_show_table();
#endif /* CONFIG_OF */
if (!pdata) {
pr_err("%s: failed to get i2c platform data\n", __func__);
ret = -ENODEV;
goto err_io;
}
mutex_init(&pmuic->muic_mutex);
pmuic->pdata = pdata;
pr_info("%s: muic irq_gpio = %d\n", __func__, pdata->irq_gpio);
pr_info("%s: muic i2c client %s at 0x%02x\n", __func__, pmfd->muic->name, pmfd->muic->addr);
pr_info("%s: mfd i2c client %s at 0x%02x\n", __func__, pmfd->i2c->name, pmfd->i2c->addr);
pr_info("%s: fuel i2c client %s at 0x%02x\n", __func__, pmfd->fuel->name, pmfd->fuel->addr);
pr_info("%s: chg i2c client %s at 0x%02x\n", __func__, pmfd->chg->name, pmfd->chg->addr);
pmuic->i2c = pmfd->muic;
pmuic->is_factory_start = false;
pmuic->is_otg_test = false;
pmuic->attached_dev = ATTACHED_DEV_UNKNOWN_MUIC;
pmuic->is_gamepad = false;
#if defined(CONFIG_MUIC_TEST_FUNC)
pmuic->usb_to_ta_state = false;
#endif
pmuic->is_dcdtmr_intr = false;
if(!strcmp(pmuic->chip_name,"max,max77865"))
pmuic->vps_table = VPS_TYPE_TABLE;
else
pmuic->vps_table = VPS_TYPE_SCATTERED;
pr_info("%s: VPS_TYPE=%d\n", __func__, pmuic->vps_table);
if (!pdata->set_gpio_uart_sel) {
if (pmuic->gpio_uart_sel) {
pr_info("%s: gpio_uart_sel registered.\n", __func__);
pdata->set_gpio_uart_sel = muic_set_gpio_uart_sel;
} else
pr_info("%s: gpio_uart_sel is not supported.\n", __func__);
}
if (pmuic->pdata->init_gpio_cb) {
ret = pmuic->pdata->init_gpio_cb(pdata, get_switch_sel());
if (ret) {
pr_err("%s: failed to init gpio(%d)\n", __func__, ret);
goto fail_init_gpio;
}
}
if (pmuic->pdata->init_switch_dev_cb)
pmuic->pdata->init_switch_dev_cb();
if (pmuic->pdata->init_cable_data_collect_cb)
pmuic->pdata->init_cable_data_collect_cb();
pr_info(" switch_sel : 0x%04x\n", get_switch_sel());
if (!(get_switch_sel() & SWITCH_SEL_RUSTPROOF_MASK)) {
pr_info(" Enable rustproof mode\n");
pmuic->is_rustproof = true;
} else {
pr_info(" Disable rustproof mode\n");
pmuic->is_rustproof = false;
}
if (get_afc_mode() == CH_MODE_AFC_DISABLE_VAL) {
pr_info(" AFC mode disabled\n");
pmuic->pdata->afc_disable = true;
} else {
pr_info(" AFC mode enabled\n");
pmuic->pdata->afc_disable = false;
}
/* Register chipset register map. */
muic_register_regmap(&pdesc, pmuic);
pdesc->muic = pmuic;
pops = pdesc->regmapops;
pmuic->regmapdesc = pdesc;
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
pmuic->opmode = get_ccic_info() & 0x0F;
pmuic->afc_water_disable = false;
pmuic->afc_tsub_disable = false;
pmuic->is_ccic_attach = false;
pmuic->is_ccic_afc_enable = 0;
pmuic->is_ccic_rp56_enable = false;
#if defined(CONFIG_SEC_FACTORY)
f_opmode = pmuic->opmode;
#endif
#endif
/* set switch device's driver_data */
dev_set_drvdata(switch_device, pmuic);
dev_set_drvdata(&pdev->dev, pmuic);
/* create sysfs group */
ret = sysfs_create_group(&switch_device->kobj, &muic_sysfs_group);
if (ret) {
pr_err("%s: failed to create max77865 muic attribute group\n",
__func__);
goto fail;
}
ret = pops->revision(pdesc);
if (ret) {
pr_err("%s: failed to init muic rev register(%d)\n", __func__,
ret);
goto fail;
}
ret = muic_reg_init(pmuic);
if (ret) {
pr_err("%s: failed to init muic register(%d)\n", __func__, ret);
goto fail;
}
pops->update(pdesc);
pops->show(pdesc);
#if defined(CONFIG_MUIC_HV_MAX77865)
hv_initialize(pmuic, &pmuic->phv);
of_muic_hv_dt(pmuic);
hv_configure_AFC(pmuic->phv);
#endif
coagent_update_ctx(pmuic);
muic_irq_init(pmuic);
/* initial cable detection */
muic_init_detect(pmuic);
#if defined(CONFIG_MUIC_HV_MAX77865)
pmuic->phv->is_muic_ready = true;
printk("%s: is_afc_muic_ready=%d\n", __func__, pmuic->phv->is_afc_muic_ready);
if (pmuic->phv->is_afc_muic_ready)
max77865_hv_muic_init_detect(pmuic->phv);
#endif
#ifdef DEBUG_MUIC
INIT_DELAYED_WORK(&pmuic->usb_work, muic_show_debug_info);
schedule_delayed_work(&pmuic->usb_work, msecs_to_jiffies(10000));
#endif
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
muic_is_ccic_supported_jig(pmuic, pmuic->attached_dev);
if (pmuic->opmode & OPMODE_CCIC)
muic_register_ccic_notifier(pmuic);
else
pr_info("%s: OPMODE_MUIC. CCIC is not used.\n", __func__);
#endif
#if defined(CONFIG_USB_EXTERNAL_NOTIFY)
muic_register_usb_notifier(pmuic);
#endif
muic_online = true;
return 0;
fail:
if (pmuic->pdata->cleanup_switch_dev_cb)
pmuic->pdata->cleanup_switch_dev_cb();
sysfs_remove_group(&switch_device->kobj, &muic_sysfs_group);
mutex_unlock(&pmuic->muic_mutex);
mutex_destroy(&pmuic->muic_mutex);
fail_init_gpio:
err_io:
kfree(pmuic);
err_return:
return ret;
}
static int muic_remove(struct platform_device *pdev)
{
muic_data_t *pmuic = platform_get_drvdata(pdev);
sysfs_remove_group(&switch_device->kobj, &muic_sysfs_group);
if (pmuic) {
pr_info("%s:%s\n", pmuic->chip_name, __func__);
#if defined(CONFIG_MUIC_HV_MAX77865)
max77865_hv_muic_remove(pmuic->phv);
#endif
cancel_delayed_work(&pmuic->usb_work);
disable_irq_wake(pmuic->i2c->irq);
free_irq(pmuic->i2c->irq, pmuic);
if (pmuic->pdata->cleanup_switch_dev_cb)
pmuic->pdata->cleanup_switch_dev_cb();
#if defined(CONFIG_USB_EXTERNAL_NOTIFY)
muic_unregister_usb_notifier(pmuic);
#endif
mutex_destroy(&pmuic->muic_mutex);
i2c_set_clientdata(pmuic->i2c, NULL);
kfree(pmuic);
}
muic_online = false;
return 0;
}
#if defined(CONFIG_OF)
static struct of_device_id muic_i2c_dt_ids[] = {
{ .compatible = "muic-universal" },
{ },
};
MODULE_DEVICE_TABLE(of, muic_i2c_dt_ids);
#endif /* CONFIG_OF */
static void muic_shutdown(struct platform_device *pdev)
{
muic_data_t *pmuic = platform_get_drvdata(pdev);
int ret;
if(pmuic == NULL)
{
printk("sabin pmuic is NULL\n");
return;
}
printk("sabin muic_shutdown start\n");
pr_info("%s:%s\n", pmuic->chip_name, __func__);
if (!pmuic->i2c) {
pr_err("%s:%s no muic i2c client\n", pmuic->chip_name, __func__);
return;
}
free_irq(pmuic->i2c->irq, pmuic);
#if defined(CONFIG_MUIC_HV_MAX77865)
max77865_hv_muic_remove(pmuic->phv);
#endif
if (cancel_delayed_work(&pmuic->usb_work))
pr_info("%s: usb_work canceled.\n", __func__);
else
pr_info("%s: usb_work is not pending.\n", __func__);
pr_info("%s:%s open D+,D-\n", pmuic->chip_name, __func__);
pmuic->is_hiccup_mode = false;
pmuic->afc_water_disable = false;
ret = com_to_open_with_vbus(pmuic);
if (ret < 0)
pr_err("%s:%s fail to open mansw1 reg\n", pmuic->chip_name,
__func__);
/* set auto sw mode before shutdown to make sure device goes into */
/* LPM charging when TA or USB is connected during off state */
pr_info("%s:%s muic auto detection enable\n", pmuic->chip_name, __func__);
set_switch_mode(pmuic,SWMODE_AUTO);
if (pmuic->pdata && pmuic->pdata->cleanup_switch_dev_cb)
pmuic->pdata->cleanup_switch_dev_cb();
muic_online = false;
pr_info("%s:%s -\n", MUIC_DEV_NAME, __func__);
}
#if defined(CONFIG_PM)
static int muic_suspend(struct device *dev)
{
muic_data_t *pmuic = dev_get_drvdata(dev);
pr_info("%s:%s \n", MUIC_DEV_NAME, __func__);
cancel_delayed_work(&pmuic->usb_work);
if (device_may_wakeup(dev))
enable_irq_wake(pmuic->i2c->irq);
disable_irq(pmuic->i2c->irq);
return 0;
}
static int muic_resume(struct device *dev)
{
muic_data_t *pmuic = dev_get_drvdata(dev);
pr_info("%s:%s \n", MUIC_DEV_NAME, __func__);
schedule_delayed_work(&pmuic->usb_work, msecs_to_jiffies(1000));
if (device_may_wakeup(dev))
disable_irq_wake(pmuic->i2c->irq);
enable_irq(pmuic->i2c->irq);
return 0;
}
const struct dev_pm_ops muic_pm = {
.suspend = muic_suspend,
.resume = muic_resume,
};
#endif /* CONFIG_PM */
static struct platform_driver muic_driver = {
.driver = {
.name = MUIC_DEV_NAME,
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = muic_i2c_dt_ids,
#endif /* CONFIG_OF */
#if defined(CONFIG_PM)
.pm = &muic_pm,
#endif /* CONFIG_PM */
},
.probe = muic_probe,
.remove = muic_remove,
.shutdown = muic_shutdown,
};
static int __init muic_init(void)
{
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
return platform_driver_register(&muic_driver);
}
module_init(muic_init);
static void muic_exit(void)
{
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
platform_driver_unregister(&muic_driver);
}
module_exit(muic_exit);
MODULE_DESCRIPTION("MUIC driver");
MODULE_AUTHOR("<smilesr.ryu@samsung.com>");
MODULE_LICENSE("GPL");