Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions arch/arm64/boot/dts/qcom/sdm450-samsung-a6plte-r4.dts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
i2c2 = &i2c_2;
i2c3 = &i2c_3;
i2c5 = &i2c_5;
i2c8 = &i2c_8;
i2c11 = &i2c_gpio_1;
i2c12 = &i2c_gpio_2;
i2c13 = &i2c_gpio_3;
i2c14 = &i2c_gpio_4;
i2c15 = &i2c_gpio_5;
i2c16 = &i2c_gpio_6;
i2c20 = &cci_i2c0;
i2c21 = &cci_i2c1;
serial0 = &uart_0;
Expand Down Expand Up @@ -116,6 +116,15 @@
#size-cells = <0>;
};

i2c_gpio_6: i2c-gpio-6 {
compatible = "i2c-gpio";
sda-gpios = <&tlmm 98 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
scl-gpios = <&tlmm 99 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;

#address-cells = <1>;
#size-cells = <0>;
};

reserved-memory {
tzapp_mem@82800000 {
reg = <0x0 0x82800000 0x0 0x2800000>;
Expand Down Expand Up @@ -482,7 +491,7 @@
};
};

&i2c_8 {
&i2c_gpio_6 {
status = "okay";

usb_extcon: extcon@25 {
Expand All @@ -496,6 +505,21 @@
pmic@49 {
compatible = "siliconmitus,sm5708";
reg = <0x49>;
interrupt-extended = <&tlmm 60 IRQ_TYPE_EDGE_FALLING>;
sm5708,irq-gpio = <&tlmm 60 0x00>;
pinctrl-0 = <&charger_int_default>;
pinctrl-names = "default";

charger: sm5708-charger {
compatible = "siliconmitus,sm5708-charger";
float-voltage = <4350>;
input-current = <1500>;
charge-current = <1800>;
extcon = <&usb_extcon>;
pinctrl-0 = <&charger_en_default>;
pinctrl-names = "default";
enable-gpios = <&tlmm 34 GPIO_ACTIVE_HIGH>;
};
};
};

Expand All @@ -520,6 +544,15 @@
};
};

&i2c_gpio_3 {
fuelgauge@71 {
compatible = "sm5708-battery,a6plte";
reg = <0x71>;
interrupts-extended = <&tlmm 62 IRQ_TYPE_LEVEL_HIGH>;
power-supplies = <&charger>;
};
};

&i2c_gpio_4 {
accelerometer@6b {
compatible = "st,lsm6dsl";
Expand Down Expand Up @@ -762,6 +795,13 @@
bias-disable;
};

charger_en_default: chg-en-pins {
pins = "gpio34";
function = "gpio";
bias-pull-down;
output-high;
};

fg_int_default: fg-int-default-state {
pins = "gpio62";
function = "gpio";
Expand Down
11 changes: 11 additions & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,17 @@ config MFD_SKY81452
This driver can also be built as a module. If so, the module
will be called sky81452.

config MFD_SM5708
bool "Siliconmitus SM5708 IFPM Support"
depends on I2C=y
select MFD_CORE
help
Say yes here to support for Siliconmitus SM5708.
This is a Power Management IC with USBLDO, RGB(SVC led), Dual Flash, Charger
controls on chip.
This driver provides common support for accessing the device;
additional drivers

config MFD_SC27XX_PMIC
tristate "Spreadtrum SC27xx PMICs"
depends on ARCH_SPRD || COMPILE_TEST
Expand Down
1 change: 1 addition & 0 deletions drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_88PM886_PMIC) += 88pm886.o
obj-$(CONFIG_MFD_ACT8945A) += act8945a.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_SM5708) += sm5708.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
Expand Down
261 changes: 261 additions & 0 deletions drivers/mfd/sm5708.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/*
* sm5708.c - mfd core driver for the SM5708
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/regulator/machine.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>

#include <linux/mfd/sm5708.h>

#define MFD_DEV_NAME "sm5708-mfd"

#define SM5708_IRQ(name, regnum) \
REGMAP_IRQ_REG(name##_IRQ, regnum - 1, name##_STATUS##regnum)

static const struct regmap_irq sm5708_irqs[] = {
SM5708_IRQ(SM5708_VBUSPOK, 1),
SM5708_IRQ(SM5708_VBUSUVLO, 1),
SM5708_IRQ(SM5708_VBUSOVP, 1),
SM5708_IRQ(SM5708_VBUSLIMIT, 1),

SM5708_IRQ(SM5708_AICL, 2),
SM5708_IRQ(SM5708_BATOVP, 2),
SM5708_IRQ(SM5708_NOBAT, 2),
SM5708_IRQ(SM5708_CHGON, 2),
SM5708_IRQ(SM5708_Q4FULLON, 2),
SM5708_IRQ(SM5708_TOPOFF, 2),
SM5708_IRQ(SM5708_DONE, 2),
SM5708_IRQ(SM5708_WDTMROFF, 2),

SM5708_IRQ(SM5708_THEMREG, 3),
SM5708_IRQ(SM5708_THEMSHDN, 3),
SM5708_IRQ(SM5708_OTGFAIL, 3),
SM5708_IRQ(SM5708_DISLIMIT, 3),
SM5708_IRQ(SM5708_PRETMROFF, 3),
SM5708_IRQ(SM5708_FASTTMROFF, 3),
SM5708_IRQ(SM5708_LOWBATT, 3),
SM5708_IRQ(SM5708_nENQ4, 3),

SM5708_IRQ(SM5708_FLED1SHORT, 4),
SM5708_IRQ(SM5708_FLED1OPEN, 4),
SM5708_IRQ(SM5708_FLED2SHORT, 4),
SM5708_IRQ(SM5708_FLED2OPEN, 4),
SM5708_IRQ(SM5708_BOOSTPOK_NG, 4),
SM5708_IRQ(SM5708_BOOSTPOK, 4),
SM5708_IRQ(SM5708_ABSTMR1OFF, 4),
SM5708_IRQ(SM5708_SBPS, 4),
};

static bool sm5708_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SM5708_REG_INT1 ... SM5708_REG_INT4:
case SM5708_REG_STATUS1 ... SM5708_REG_STATUS4:
return true;
default:
return false;
}
}

static const struct regmap_config sm5708_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = sm5708_volatile_reg,
.max_register = SM5708_REG_MAX,
};

void sm5708_request_mode(struct device *dev, u8 mode_mask, bool enable) {
struct sm5708_chip *chip = (struct sm5708_chip*) dev_get_drvdata(dev);
u8 new_mode;
int boost_output = SM5708_BST_OUT_4v5;
int otg_current = SM5708_OTG_CURRENT_500mA;
int op_mode = SM5708_MODE_CHG_ON;

mutex_lock(&chip->mode_mutex);

if (enable)
new_mode = chip->requested_mode | mode_mask;
else
new_mode = chip->requested_mode & ~mode_mask;

if (new_mode == chip->requested_mode)
goto unlock;

switch(new_mode & ~SM5708_TORCH) {
case SM5708_FLASH:
case SM5708_FLASH | SM5708_CHARGE:
op_mode = SM5708_MODE_FLASH_BOOST;
break;
case SM5708_FLASH | SM5708_OTG:
op_mode = SM5708_MODE_FLASH_BOOST;
otg_current = SM5708_OTG_CURRENT_900mA;
break;
case SM5708_OTG:
otg_current = SM5708_OTG_CURRENT_900mA;
boost_output = SM5708_BST_OUT_5v1;
op_mode = SM5708_MODE_USB_OTG;
break;
default:
if (new_mode == SM5708_TORCH)
op_mode = SM5708_MODE_FLASH_BOOST;
}

if (chip->op_mode == SM5708_MODE_USB_OTG &&
op_mode == SM5708_MODE_CHG_ON) {
regmap_update_bits(chip->regmap, SM5708_REG_CNTL, 0x7,
SM5708_MODE_SUSPEND);
msleep(80);
}

regmap_update_bits(chip->regmap, SM5708_REG_FLEDCNTL6, 0xf, boost_output);
regmap_update_bits(chip->regmap, SM5708_REG_CHGCNTL5, 0x3 << 2, otg_current << 2);
regmap_update_bits(chip->regmap, SM5708_REG_CNTL, 0x7, op_mode);

chip->op_mode = op_mode;
chip->requested_mode = new_mode;
unlock:
mutex_unlock(&chip->mode_mutex);
}
EXPORT_SYMBOL(sm5708_request_mode);

static const struct regmap_irq_chip sm5708_irq_chip = {
.name = MFD_DEV_NAME,
.status_base = SM5708_REG_INT1,
.mask_base = SM5708_REG_INTMSK1,
.num_regs = 4,
.irqs = sm5708_irqs,
.num_irqs = ARRAY_SIZE(sm5708_irqs),
};

static struct mfd_cell sm5708_devs[] = {
{ .name = "sm5708-charger", .of_compatible = "siliconmitus,sm5708-charger" },
{ .name = "sm5708-rgb-leds", .of_compatible = "siliconmitus,sm5708-leds" },
{ .name = "sm5708-fled", .of_compatible = "siliconmitus,sm5708-fled" },
};

static int sm5708_i2c_probe(struct i2c_client *i2c)
{
struct device_node *np = i2c->dev.of_node;
struct sm5708_chip *chip;
int ret = 0;

if (!np)
return -EINVAL;

chip = devm_kzalloc(&i2c->dev, sizeof(struct sm5708_chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;

/* get irq and related info from device tree */
int irq_gpio = of_get_named_gpio(np, "sm5708,irq-gpio", 0);
chip->irq = gpio_to_irq(irq_gpio);

i2c_set_clientdata(i2c, chip);
chip->dev = &i2c->dev;
chip->i2c = i2c;
/* commented out, i2c->irq is 0 */
//chip->irq = i2c->irq;
mutex_init(&chip->mode_mutex);

chip->regmap = devm_regmap_init_i2c(i2c,
&sm5708_regmap_config);

ret = regmap_add_irq_chip(chip->regmap, chip->irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND,
-1, &sm5708_irq_chip, &chip->irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add IRQ chip: %d\n", ret);
return ret;
}

ret = devm_mfd_add_devices(chip->dev, -1, sm5708_devs,
ARRAY_SIZE(sm5708_devs), NULL, 0, NULL);
if (ret < 0)
return ret;

device_init_wakeup(chip->dev, 0);

return 0;
}

static int sm5708_suspend(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct sm5708_chip *chip = i2c_get_clientdata(i2c);

if (device_may_wakeup(dev))
enable_irq_wake(chip->irq);

disable_irq(chip->irq);

return 0;
}

static int sm5708_resume(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct sm5708_chip *chip = i2c_get_clientdata(i2c);

if (device_may_wakeup(dev))
disable_irq_wake(chip->irq);

enable_irq(chip->irq);

return 0;
}

static void sm5708_shutdown(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct sm5708_chip *chip = i2c_get_clientdata(i2c);

regmap_update_bits(chip->regmap, SM5708_REG_CNTL, 0x7,
SM5708_MODE_CHG_ON);
}

static SIMPLE_DEV_PM_OPS(sm5708_pm, sm5708_suspend, sm5708_resume);

static const struct of_device_id sm5708_i2c_dt_ids[] = {
{ .compatible = "siliconmitus,sm5708" },
{ },
};
MODULE_DEVICE_TABLE(of, sm5708_i2c_dt_ids);

static struct i2c_driver sm5708_i2c_driver = {
.driver = {
.name = MFD_DEV_NAME,
.owner = THIS_MODULE,
.pm = &sm5708_pm,
.of_match_table = sm5708_i2c_dt_ids,
.shutdown = sm5708_shutdown,
},
.probe = sm5708_i2c_probe,
};

module_i2c_driver(sm5708_i2c_driver);

MODULE_DESCRIPTION("SM5708 multi-function core driver");
MODULE_LICENSE("GPL");
Loading