diff --git a/drivers/power/mmi_discrete_charger/Kbuild b/drivers/power/mmi_discrete_charger/Kbuild index 0dfc60aee070..588c8a218dff 100644 --- a/drivers/power/mmi_discrete_charger/Kbuild +++ b/drivers/power/mmi_discrete_charger/Kbuild @@ -6,6 +6,7 @@ EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ obj-m += mmi_discrete_charger.o mmi_discrete_charger-objs += mmi_discrete_charger_core.o +mmi_discrete_charger-objs += mmi_discrete_charger_iio.o mmi_discrete_charger-objs += mmi_discrete_voter.o mmi_discrete_charger-objs += mmi_discrete_factory_tcmd.o diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c index 68ad92199dc9..27c420dd8624 100644 --- a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c @@ -29,6 +29,7 @@ #include "mmi_discrete_charger_core.h" #include "mmi_discrete_voter.h" +#include "mmi_discrete_charger_iio.h" #include "mmi_discrete_factory_tcmd.h" #include @@ -706,6 +707,22 @@ static int mmi_discrete_disable_hw_jeita(struct mmi_discrete_charger *chip) return rc; } +int mmi_discrete_otg_enable(struct mmi_discrete_charger *chip, bool en) +{ + int rc = 0; + + if (en == !chip->vbus_enabled) { + rc = charger_dev_enable_otg(chip->master_chg_dev, en); + if (rc) { + mmi_err(chip, "Unable to %s otg (%d)\n", en?"enable":"disable", rc); + return rc; + } + chip->vbus_enabled = en; + } + + return rc; +} + static void mmi_discrete_check_otg_power(struct mmi_discrete_charger *chip) { int rc = 0; @@ -2293,12 +2310,22 @@ static int mmi_discrete_probe(struct platform_device *pdev) { int rc = 0; struct mmi_discrete_charger *chip; + struct iio_dev *indio_dev; struct power_supply_config psy_cfg = {}; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) return -ENOMEM; + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_discrete_iio\n"); + return -ENOMEM; + } + + chip->indio_dev = indio_dev; + chip->dev = &pdev->dev; chip->name = "mmi_discrete"; psy_cfg.drv_data = chip; @@ -2336,6 +2363,12 @@ static int mmi_discrete_probe(struct platform_device *pdev) chip->wls_psy = power_supply_get_by_name("wireless"); + rc = mmi_discrete_init_iio_psy(chip, pdev); + if (rc < 0) { + mmi_err(chip, "Failed to init iio psy\n"); + return rc; + } + rc = mmi_discrete_parse_dts(chip); if (rc < 0) { mmi_err(chip, "Failed to parse dts\n"); diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h index eecbc60a6e73..ac09c6549806 100644 --- a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h @@ -13,6 +13,8 @@ #ifndef __MMI_DISCRETE_CHARGER_CORE_H__ #define __MMI_DISCRETE_CHARGER_CORE_H__ +#include +#include #include #include "mmi_charger.h" @@ -102,6 +104,11 @@ struct mmi_discrete_charger { /* extcon for VBUS / ID notification to USB for type-c only */ struct extcon_dev *extcon; + /*IIO*/ + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + /*PD*/ /* struct usbpd *pd_handle; int pd_current_pdo; @@ -128,4 +135,6 @@ struct mmi_discrete_charger { bool use_extcon; }; +int mmi_discrete_otg_enable(struct mmi_discrete_charger *chip, bool en); + #endif \ No newline at end of file diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c new file mode 100644 index 000000000000..47f005451582 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include "mmi_discrete_charger_iio.h" + +static int mmi_discrete_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_MMI_OTG_ENABLE: + rc = mmi_discrete_otg_enable(chip, !!val1); + break; + default: + pr_err("Unsupported mmi_discrete IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_discrete_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_USB_REAL_TYPE: + *val1 = chip->real_charger_type; + break; + default: + pr_err("Unsupported mmi_discrete IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_discrete_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_discrete_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_discrete_iio_info = { + .read_raw = mmi_discrete_iio_read_raw, + .write_raw = mmi_discrete_iio_write_raw, + .of_xlate = mmi_discrete_iio_of_xlate, +}; + +int mmi_discrete_init_iio_psy(struct mmi_discrete_charger *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_discrete_num_iio_channels = ARRAY_SIZE(mmi_discrete_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_discrete_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_discrete_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_discrete_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-discrete-iio"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_discrete_num_iio_channels; + + for (i = 0; i < mmi_discrete_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_discrete_iio_psy_channels[i].channel_num; + chan->type = mmi_discrete_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_discrete_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_discrete_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_discrete_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register mmi-discrete-iio IIO device, rc=%d\n", rc); + return rc; + } + + return rc; +} diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h new file mode 100644 index 000000000000..3854e2017cbe --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef __MMI_DISCRETE_CHARGER_IIO_H__ +#define __MMI_DISCRETE_CHARGER_IIO_H__ + +#include +#include +#include +#include +#include "mmi_discrete_charger_core.h" + +struct mmi_discrete_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define MMI_DISCRETE_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_DISCRETE_CHAN_INDEX(_name, _num) \ + MMI_DISCRETE_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_discrete_iio_channels mmi_discrete_iio_psy_channels[] = { + MMI_DISCRETE_CHAN_INDEX("usb_real_type", PSY_IIO_USB_REAL_TYPE) + MMI_DISCRETE_CHAN_INDEX("otg_enable", PSY_IIO_MMI_OTG_ENABLE) +}; + +int mmi_discrete_init_iio_psy(struct mmi_discrete_charger *chip, + struct platform_device *pdev); + +#endif \ No newline at end of file