Merge branches 'ib-mfd-gpio-4.9', 'ib-mfd-gpio-regulator-4.9', 'ib-mfd-input-4.9', 'ib-mfd-regulator-4.9', 'ib-mfd-regulator-4.9.1', 'ib-mfd-regulator-rtc-4.9', 'ib-mfd-regulator-rtc-4.9-1' and 'ib-mfd-rtc-4.9' into ibs-for-mfd-merged
This commit is contained in:

@@ -112,6 +112,16 @@ config MFD_BCM590XX
|
||||
help
|
||||
Support for the BCM590xx PMUs from Broadcom
|
||||
|
||||
config MFD_AC100
|
||||
tristate "X-Powers AC100"
|
||||
select MFD_CORE
|
||||
depends on SUNXI_RSB
|
||||
help
|
||||
If you say Y here you get support for the X-Powers AC100 audio codec
|
||||
IC.
|
||||
This driver include only the core APIs. You have to select individual
|
||||
components like codecs or RTC under the corresponding menus.
|
||||
|
||||
config MFD_AXP20X
|
||||
tristate
|
||||
select MFD_CORE
|
||||
@@ -852,13 +862,13 @@ config MFD_RC5T583
|
||||
different functionality of the device.
|
||||
|
||||
config MFD_RK808
|
||||
tristate "Rockchip RK808 Power Management chip"
|
||||
tristate "Rockchip RK808/RK818 Power Management Chip"
|
||||
depends on I2C && OF
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
If you say yes here you get support for the RK808
|
||||
If you say yes here you get support for the RK808 and RK818
|
||||
Power Management chips.
|
||||
This driver provides common support for accessing the device
|
||||
through I2C interface. The device supports multiple sub-devices
|
||||
@@ -1224,6 +1234,20 @@ config MFD_TPS65217
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps65217.
|
||||
|
||||
config MFD_TI_LP873X
|
||||
tristate "TI LP873X Power Management IC"
|
||||
depends on I2C
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here then you get support for the LP873X series of
|
||||
Power Management Integrated Circuits (PMIC).
|
||||
These include voltage regulators, thermal protection, configurable
|
||||
General Purpose Outputs (GPO) that are used in portable devices.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lp873x.
|
||||
|
||||
config MFD_TPS65218
|
||||
tristate "TI TPS65218 Power Management chips"
|
||||
depends on I2C
|
||||
|
@@ -22,6 +22,8 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
|
||||
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
|
||||
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
|
||||
|
||||
obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o
|
||||
|
||||
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
|
||||
@@ -113,6 +115,8 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
|
||||
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
|
||||
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
|
||||
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
|
||||
|
||||
obj-$(CONFIG_MFD_AC100) += ac100.o
|
||||
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
|
||||
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
|
||||
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
|
||||
|
137
drivers/mfd/ac100.c
Normal file
137
drivers/mfd/ac100.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* MFD core driver for X-Powers' AC100 Audio Codec IC
|
||||
*
|
||||
* The AC100 is a highly integrated audio codec and RTC subsystem designed
|
||||
* for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC,
|
||||
* a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has
|
||||
* 3 clock outputs.
|
||||
*
|
||||
* The audio codec and RTC parts are completely separate, sharing only the
|
||||
* host interface for access to its registers.
|
||||
*
|
||||
* Copyright (2016) Chen-Yu Tsai
|
||||
*
|
||||
* Author: Chen-Yu Tsai <wens@csie.org>
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/ac100.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sunxi-rsb.h>
|
||||
|
||||
static const struct regmap_range ac100_writeable_ranges[] = {
|
||||
regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL),
|
||||
regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN),
|
||||
regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN),
|
||||
regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL),
|
||||
regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL),
|
||||
regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS),
|
||||
regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN),
|
||||
regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL),
|
||||
regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT),
|
||||
regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT),
|
||||
regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA),
|
||||
regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2),
|
||||
regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2),
|
||||
regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLKOUT_CTRL3),
|
||||
regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD),
|
||||
regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA),
|
||||
regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)),
|
||||
};
|
||||
|
||||
static const struct regmap_range ac100_volatile_ranges[] = {
|
||||
regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2),
|
||||
regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS),
|
||||
regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA),
|
||||
regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1),
|
||||
regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1),
|
||||
regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4),
|
||||
regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST),
|
||||
regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA),
|
||||
regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table ac100_writeable_table = {
|
||||
.yes_ranges = ac100_writeable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(ac100_writeable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table ac100_volatile_table = {
|
||||
.yes_ranges = ac100_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(ac100_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config ac100_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.wr_table = &ac100_writeable_table,
|
||||
.volatile_table = &ac100_volatile_table,
|
||||
.max_register = AC100_RTC_GP(15),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static struct mfd_cell ac100_cells[] = {
|
||||
{
|
||||
.name = "ac100-codec",
|
||||
.of_compatible = "x-powers,ac100-codec",
|
||||
}, {
|
||||
.name = "ac100-rtc",
|
||||
.of_compatible = "x-powers,ac100-rtc",
|
||||
},
|
||||
};
|
||||
|
||||
static int ac100_rsb_probe(struct sunxi_rsb_device *rdev)
|
||||
{
|
||||
struct ac100_dev *ac100;
|
||||
int ret;
|
||||
|
||||
ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL);
|
||||
if (!ac100)
|
||||
return -ENOMEM;
|
||||
|
||||
ac100->dev = &rdev->dev;
|
||||
sunxi_rsb_device_set_drvdata(rdev, ac100);
|
||||
|
||||
ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config);
|
||||
if (IS_ERR(ac100->regmap)) {
|
||||
ret = PTR_ERR(ac100->regmap);
|
||||
dev_err(ac100->dev, "regmap init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells,
|
||||
ARRAY_SIZE(ac100_cells), NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ac100_of_match[] = {
|
||||
{ .compatible = "x-powers,ac100" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ac100_of_match);
|
||||
|
||||
static struct sunxi_rsb_driver ac100_rsb_driver = {
|
||||
.driver = {
|
||||
.name = "ac100",
|
||||
.of_match_table = of_match_ptr(ac100_of_match),
|
||||
},
|
||||
.probe = ac100_rsb_probe,
|
||||
};
|
||||
module_sunxi_rsb_driver(ac100_rsb_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Audio codec MFD core driver for AC100");
|
||||
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -10,6 +10,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
@@ -49,7 +50,15 @@ int arizona_clk32k_enable(struct arizona *arizona)
|
||||
case ARIZONA_32KZ_MCLK1:
|
||||
ret = pm_runtime_get_sync(arizona->dev);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
goto err_ref;
|
||||
ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]);
|
||||
if (ret != 0)
|
||||
goto err_pm;
|
||||
break;
|
||||
case ARIZONA_32KZ_MCLK2:
|
||||
ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]);
|
||||
if (ret != 0)
|
||||
goto err_ref;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -58,7 +67,9 @@ int arizona_clk32k_enable(struct arizona *arizona)
|
||||
ARIZONA_CLK_32K_ENA);
|
||||
}
|
||||
|
||||
out:
|
||||
err_pm:
|
||||
pm_runtime_put_sync(arizona->dev);
|
||||
err_ref:
|
||||
if (ret != 0)
|
||||
arizona->clk32k_ref--;
|
||||
|
||||
@@ -83,6 +94,10 @@ int arizona_clk32k_disable(struct arizona *arizona)
|
||||
switch (arizona->pdata.clk32k_src) {
|
||||
case ARIZONA_32KZ_MCLK1:
|
||||
pm_runtime_put_sync(arizona->dev);
|
||||
clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK1]);
|
||||
break;
|
||||
case ARIZONA_32KZ_MCLK2:
|
||||
clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1000,6 +1015,7 @@ static const struct mfd_cell wm8998_devs[] = {
|
||||
|
||||
int arizona_dev_init(struct arizona *arizona)
|
||||
{
|
||||
const char * const mclk_name[] = { "mclk1", "mclk2" };
|
||||
struct device *dev = arizona->dev;
|
||||
const char *type_name = NULL;
|
||||
unsigned int reg, val, mask;
|
||||
@@ -1016,6 +1032,16 @@ int arizona_dev_init(struct arizona *arizona)
|
||||
else
|
||||
arizona_of_get_core_pdata(arizona);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(arizona->mclk) != ARRAY_SIZE(mclk_name));
|
||||
for (i = 0; i < ARRAY_SIZE(arizona->mclk); i++) {
|
||||
arizona->mclk[i] = devm_clk_get(arizona->dev, mclk_name[i]);
|
||||
if (IS_ERR(arizona->mclk[i])) {
|
||||
dev_info(arizona->dev, "Failed to get %s: %ld\n",
|
||||
mclk_name[i], PTR_ERR(arizona->mclk[i]));
|
||||
arizona->mclk[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
regcache_cache_only(arizona->regmap, true);
|
||||
|
||||
switch (arizona->type) {
|
||||
|
@@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
|
||||
|
||||
static const struct of_device_id axp20x_rsb_of_match[] = {
|
||||
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
|
||||
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
|
||||
{ .compatible = "x-powers,axp809", .data = (void *)AXP809_ID },
|
||||
{ },
|
||||
};
|
||||
|
@@ -38,6 +38,7 @@ static const char * const axp20x_model_names[] = {
|
||||
"AXP221",
|
||||
"AXP223",
|
||||
"AXP288",
|
||||
"AXP806",
|
||||
"AXP809",
|
||||
};
|
||||
|
||||
@@ -129,6 +130,27 @@ static const struct regmap_access_table axp288_volatile_table = {
|
||||
.n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range axp806_writeable_ranges[] = {
|
||||
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_DATACACHE(3)),
|
||||
regmap_reg_range(AXP806_PWR_OUT_CTRL1, AXP806_CLDO3_V_CTRL),
|
||||
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ2_EN),
|
||||
regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
|
||||
};
|
||||
|
||||
static const struct regmap_range axp806_volatile_ranges[] = {
|
||||
regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table axp806_writeable_table = {
|
||||
.yes_ranges = axp806_writeable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(axp806_writeable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table axp806_volatile_table = {
|
||||
.yes_ranges = axp806_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges),
|
||||
};
|
||||
|
||||
static struct resource axp152_pek_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
|
||||
DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
|
||||
@@ -278,6 +300,15 @@ static const struct regmap_config axp288_regmap_config = {
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_config axp806_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.wr_table = &axp806_writeable_table,
|
||||
.volatile_table = &axp806_volatile_table,
|
||||
.max_register = AXP806_VREF_TEMP_WARN_L,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
|
||||
[_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
|
||||
|
||||
@@ -409,6 +440,21 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1),
|
||||
};
|
||||
|
||||
static const struct regmap_irq axp806_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0),
|
||||
INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1),
|
||||
INIT_REGMAP_IRQ(AXP806, DCDCA_V_LOW, 0, 3),
|
||||
INIT_REGMAP_IRQ(AXP806, DCDCB_V_LOW, 0, 4),
|
||||
INIT_REGMAP_IRQ(AXP806, DCDCC_V_LOW, 0, 5),
|
||||
INIT_REGMAP_IRQ(AXP806, DCDCD_V_LOW, 0, 6),
|
||||
INIT_REGMAP_IRQ(AXP806, DCDCE_V_LOW, 0, 7),
|
||||
INIT_REGMAP_IRQ(AXP806, PWROK_LONG, 1, 0),
|
||||
INIT_REGMAP_IRQ(AXP806, PWROK_SHORT, 1, 1),
|
||||
INIT_REGMAP_IRQ(AXP806, WAKEUP, 1, 4),
|
||||
INIT_REGMAP_IRQ(AXP806, PWROK_FALL, 1, 5),
|
||||
INIT_REGMAP_IRQ(AXP806, PWROK_RISE, 1, 6),
|
||||
};
|
||||
|
||||
static const struct regmap_irq axp809_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP809, ACIN_OVER_V, 0, 7),
|
||||
INIT_REGMAP_IRQ(AXP809, ACIN_PLUGIN, 0, 6),
|
||||
@@ -494,6 +540,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = {
|
||||
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp806_regmap_irq_chip = {
|
||||
.name = "axp806",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
.ack_base = AXP20X_IRQ1_STATE,
|
||||
.mask_base = AXP20X_IRQ1_EN,
|
||||
.mask_invert = true,
|
||||
.init_ack_masked = true,
|
||||
.irqs = axp806_regmap_irqs,
|
||||
.num_irqs = ARRAY_SIZE(axp806_regmap_irqs),
|
||||
.num_regs = 2,
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp809_regmap_irq_chip = {
|
||||
.name = "axp809",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
@@ -660,12 +718,20 @@ static struct mfd_cell axp288_cells[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp806_cells[] = {
|
||||
{
|
||||
.id = 2,
|
||||
.name = "axp20x-regulator",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp809_cells[] = {
|
||||
{
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp809_pek_resources),
|
||||
.resources = axp809_pek_resources,
|
||||
}, {
|
||||
.id = 1,
|
||||
.name = "axp20x-regulator",
|
||||
},
|
||||
};
|
||||
@@ -732,6 +798,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
|
||||
axp20x->regmap_cfg = &axp288_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
|
||||
break;
|
||||
case AXP806_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
|
||||
axp20x->cells = axp806_cells;
|
||||
axp20x->regmap_cfg = &axp806_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp806_regmap_irq_chip;
|
||||
break;
|
||||
case AXP809_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp809_cells);
|
||||
axp20x->cells = axp809_cells;
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define CROS_EC_DEV_EC_INDEX 0
|
||||
#define CROS_EC_DEV_PD_INDEX 1
|
||||
@@ -49,11 +50,28 @@ static const struct mfd_cell ec_pd_cell = {
|
||||
.pdata_size = sizeof(pd_p),
|
||||
};
|
||||
|
||||
static irqreturn_t ec_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = data;
|
||||
int ret;
|
||||
|
||||
if (device_may_wakeup(ec_dev->dev))
|
||||
pm_wakeup_event(ec_dev->dev, 0);
|
||||
|
||||
ret = cros_ec_get_next_event(ec_dev);
|
||||
if (ret > 0)
|
||||
blocking_notifier_call_chain(&ec_dev->event_notifier,
|
||||
0, ec_dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct device *dev = ec_dev->dev;
|
||||
int err = 0;
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
|
||||
|
||||
ec_dev->max_request = sizeof(struct ec_params_hello);
|
||||
ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->max_passthru = 0;
|
||||
@@ -70,13 +88,24 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
|
||||
cros_ec_query_all(ec_dev);
|
||||
|
||||
if (ec_dev->irq) {
|
||||
err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"chromeos-ec", ec_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to request IRQ %d: %d",
|
||||
ec_dev->irq, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
|
||||
NULL, ec_dev->irq, NULL);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Failed to register Embedded Controller subdevice %d\n",
|
||||
err);
|
||||
return err;
|
||||
goto fail_mfd;
|
||||
}
|
||||
|
||||
if (ec_dev->max_passthru) {
|
||||
@@ -94,7 +123,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
dev_err(dev,
|
||||
"Failed to register Power Delivery subdevice %d\n",
|
||||
err);
|
||||
return err;
|
||||
goto fail_mfd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,13 +132,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
if (err) {
|
||||
mfd_remove_devices(dev);
|
||||
dev_err(dev, "Failed to register sub-devices\n");
|
||||
return err;
|
||||
goto fail_mfd;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "Chrome EC device registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_mfd:
|
||||
if (ec_dev->irq)
|
||||
free_irq(ec_dev->irq, ec_dev);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_register);
|
||||
|
||||
@@ -136,13 +170,31 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev)
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_suspend);
|
||||
|
||||
static void cros_ec_drain_events(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
while (cros_ec_get_next_event(ec_dev) > 0)
|
||||
blocking_notifier_call_chain(&ec_dev->event_notifier,
|
||||
1, ec_dev);
|
||||
}
|
||||
|
||||
int cros_ec_resume(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
enable_irq(ec_dev->irq);
|
||||
|
||||
/*
|
||||
* In some cases, we need to distinguish between events that occur
|
||||
* during suspend if the EC is not a wake source. For example,
|
||||
* keypresses during suspend should be discarded if it does not wake
|
||||
* the system.
|
||||
*
|
||||
* If the EC is not a wake source, drain the event queue and mark them
|
||||
* as "queued during suspend".
|
||||
*/
|
||||
if (ec_dev->wake_enabled) {
|
||||
disable_irq_wake(ec_dev->irq);
|
||||
ec_dev->wake_enabled = 0;
|
||||
} else {
|
||||
cros_ec_drain_events(ec_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
99
drivers/mfd/lp873x.c
Normal file
99
drivers/mfd/lp873x.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* Author: Keerthy <j-keerthy@ti.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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/lp873x.h>
|
||||
|
||||
static const struct regmap_config lp873x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = LP873X_REG_MAX,
|
||||
};
|
||||
|
||||
static const struct mfd_cell lp873x_cells[] = {
|
||||
{ .name = "lp873x-regulator", },
|
||||
{ .name = "lp873x-gpio", },
|
||||
};
|
||||
|
||||
static int lp873x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *ids)
|
||||
{
|
||||
struct lp873x *lp873;
|
||||
int ret;
|
||||
unsigned int otpid;
|
||||
|
||||
lp873 = devm_kzalloc(&client->dev, sizeof(*lp873), GFP_KERNEL);
|
||||
if (!lp873)
|
||||
return -ENOMEM;
|
||||
|
||||
lp873->dev = &client->dev;
|
||||
|
||||
lp873->regmap = devm_regmap_init_i2c(client, &lp873x_regmap_config);
|
||||
if (IS_ERR(lp873->regmap)) {
|
||||
ret = PTR_ERR(lp873->regmap);
|
||||
dev_err(lp873->dev,
|
||||
"Failed to initialize register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&lp873->lock);
|
||||
|
||||
ret = regmap_read(lp873->regmap, LP873X_REG_OTP_REV, &otpid);
|
||||
if (ret) {
|
||||
dev_err(lp873->dev, "Failed to read OTP ID\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
lp873->rev = otpid & LP873X_OTP_REV_OTP_ID;
|
||||
|
||||
i2c_set_clientdata(client, lp873);
|
||||
|
||||
ret = mfd_add_devices(lp873->dev, PLATFORM_DEVID_AUTO, lp873x_cells,
|
||||
ARRAY_SIZE(lp873x_cells), NULL, 0, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_lp873x_match_table[] = {
|
||||
{ .compatible = "ti,lp8733", },
|
||||
{ .compatible = "ti,lp8732", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_lp873x_match_table);
|
||||
|
||||
static const struct i2c_device_id lp873x_id_table[] = {
|
||||
{ "lp873x", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp873x_id_table);
|
||||
|
||||
static struct i2c_driver lp873x_driver = {
|
||||
.driver = {
|
||||
.name = "lp873x",
|
||||
.of_match_table = of_lp873x_match_table,
|
||||
},
|
||||
.probe = lp873x_probe,
|
||||
.id_table = lp873x_id_table,
|
||||
};
|
||||
module_i2c_driver(lp873x_driver);
|
||||
|
||||
MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
|
||||
MODULE_DESCRIPTION("LP873X chip family Multi-Function Device driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -309,6 +309,7 @@ static const struct regmap_config ssbi_regmap_config = {
|
||||
};
|
||||
|
||||
static const struct of_device_id pm8921_id_table[] = {
|
||||
{ .compatible = "qcom,pm8018", },
|
||||
{ .compatible = "qcom,pm8058", },
|
||||
{ .compatible = "qcom,pm8921", },
|
||||
{ }
|
||||
|
@@ -388,11 +388,62 @@ static const struct qcom_rpm_data ipq806x_template = {
|
||||
.ack_sel_size = 7,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_resource mdm9615_rpm_resource_table[] = {
|
||||
[QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
|
||||
[QCOM_RPM_SYS_FABRIC_CLK] = { 26, 10, 9, 1 },
|
||||
[QCOM_RPM_DAYTONA_FABRIC_CLK] = { 27, 11, 11, 1 },
|
||||
[QCOM_RPM_SFPB_CLK] = { 28, 12, 12, 1 },
|
||||
[QCOM_RPM_CFPB_CLK] = { 29, 13, 13, 1 },
|
||||
[QCOM_RPM_EBI1_CLK] = { 30, 14, 16, 1 },
|
||||
[QCOM_RPM_APPS_FABRIC_HALT] = { 31, 15, 22, 2 },
|
||||
[QCOM_RPM_APPS_FABRIC_MODE] = { 33, 16, 23, 3 },
|
||||
[QCOM_RPM_APPS_FABRIC_IOCTL] = { 36, 17, 24, 1 },
|
||||
[QCOM_RPM_APPS_FABRIC_ARB] = { 37, 18, 25, 27 },
|
||||
[QCOM_RPM_PM8018_SMPS1] = { 64, 19, 30, 2 },
|
||||
[QCOM_RPM_PM8018_SMPS2] = { 66, 21, 31, 2 },
|
||||
[QCOM_RPM_PM8018_SMPS3] = { 68, 23, 32, 2 },
|
||||
[QCOM_RPM_PM8018_SMPS4] = { 70, 25, 33, 2 },
|
||||
[QCOM_RPM_PM8018_SMPS5] = { 72, 27, 34, 2 },
|
||||
[QCOM_RPM_PM8018_LDO1] = { 74, 29, 35, 2 },
|
||||
[QCOM_RPM_PM8018_LDO2] = { 76, 31, 36, 2 },
|
||||
[QCOM_RPM_PM8018_LDO3] = { 78, 33, 37, 2 },
|
||||
[QCOM_RPM_PM8018_LDO4] = { 80, 35, 38, 2 },
|
||||
[QCOM_RPM_PM8018_LDO5] = { 82, 37, 39, 2 },
|
||||
[QCOM_RPM_PM8018_LDO6] = { 84, 39, 40, 2 },
|
||||
[QCOM_RPM_PM8018_LDO7] = { 86, 41, 41, 2 },
|
||||
[QCOM_RPM_PM8018_LDO8] = { 88, 43, 42, 2 },
|
||||
[QCOM_RPM_PM8018_LDO9] = { 90, 45, 43, 2 },
|
||||
[QCOM_RPM_PM8018_LDO10] = { 92, 47, 44, 2 },
|
||||
[QCOM_RPM_PM8018_LDO11] = { 94, 49, 45, 2 },
|
||||
[QCOM_RPM_PM8018_LDO12] = { 96, 51, 46, 2 },
|
||||
[QCOM_RPM_PM8018_LDO13] = { 98, 53, 47, 2 },
|
||||
[QCOM_RPM_PM8018_LDO14] = { 100, 55, 48, 2 },
|
||||
[QCOM_RPM_PM8018_LVS1] = { 102, 57, 49, 1 },
|
||||
[QCOM_RPM_PM8018_NCP] = { 103, 58, 80, 2 },
|
||||
[QCOM_RPM_CXO_BUFFERS] = { 105, 60, 81, 1 },
|
||||
[QCOM_RPM_USB_OTG_SWITCH] = { 106, 61, 82, 1 },
|
||||
[QCOM_RPM_HDMI_SWITCH] = { 107, 62, 83, 1 },
|
||||
[QCOM_RPM_VOLTAGE_CORNER] = { 109, 64, 87, 1 },
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_data mdm9615_template = {
|
||||
.version = 3,
|
||||
.resource_table = mdm9615_rpm_resource_table,
|
||||
.n_resources = ARRAY_SIZE(mdm9615_rpm_resource_table),
|
||||
.req_ctx_off = 3,
|
||||
.req_sel_off = 11,
|
||||
.ack_ctx_off = 15,
|
||||
.ack_sel_off = 23,
|
||||
.req_sel_size = 4,
|
||||
.ack_sel_size = 7,
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_rpm_of_match[] = {
|
||||
{ .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
|
||||
{ .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
|
||||
{ .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
|
||||
{ .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
|
||||
{ .compatible = "qcom,rpm-mdm9615", .data = &mdm9615_template },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
|
||||
|
@@ -1,11 +1,15 @@
|
||||
/*
|
||||
* MFD core driver for Rockchip RK808
|
||||
* MFD core driver for Rockchip RK808/RK818
|
||||
*
|
||||
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* Author: Chris Zhong <zyw@rock-chips.com>
|
||||
* Author: Zhang Qing <zhangqing@rock-chips.com>
|
||||
*
|
||||
* Copyright (C) 2016 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* Author: Wadim Egorov <w.egorov@phytec.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
@@ -21,6 +25,7 @@
|
||||
#include <linux/mfd/rk808.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct rk808_reg_data {
|
||||
@@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config rk818_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = RK818_USB_CTRL_REG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = rk808_is_volatile_reg,
|
||||
};
|
||||
|
||||
static const struct regmap_config rk808_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
@@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = {
|
||||
{
|
||||
.name = "rk808-rtc",
|
||||
.num_resources = ARRAY_SIZE(rtc_resources),
|
||||
.resources = &rtc_resources[0],
|
||||
.resources = rtc_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rk808_reg_data pre_init_reg[] = {
|
||||
static const struct mfd_cell rk818s[] = {
|
||||
{ .name = "rk808-clkout", },
|
||||
{ .name = "rk808-regulator", },
|
||||
{
|
||||
.name = "rk808-rtc",
|
||||
.num_resources = ARRAY_SIZE(rtc_resources),
|
||||
.resources = rtc_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rk808_reg_data rk808_pre_init_reg[] = {
|
||||
{ RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA },
|
||||
{ RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA },
|
||||
{ RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
|
||||
@@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = {
|
||||
VB_LO_SEL_3500MV },
|
||||
};
|
||||
|
||||
static const struct rk808_reg_data rk818_pre_init_reg[] = {
|
||||
/* improve efficiency */
|
||||
{ RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA },
|
||||
{ RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA },
|
||||
{ RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
|
||||
{ RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK,
|
||||
RK818_USB_ILMIN_2000MA },
|
||||
/* close charger when usb lower then 3.4V */
|
||||
{ RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK,
|
||||
(0x7 << 4) },
|
||||
/* no action when vref */
|
||||
{ RK818_H5V_EN_REG, BIT(1), RK818_REF_RDY_CTRL },
|
||||
/* enable HDMI 5V */
|
||||
{ RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN },
|
||||
{ RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT |
|
||||
VB_LO_SEL_3500MV },
|
||||
};
|
||||
|
||||
static const struct regmap_irq rk808_irqs[] = {
|
||||
/* INT_STS */
|
||||
[RK808_IRQ_VOUT_LO] = {
|
||||
@@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_irq rk818_irqs[] = {
|
||||
/* INT_STS */
|
||||
[RK818_IRQ_VOUT_LO] = {
|
||||
.mask = RK818_IRQ_VOUT_LO_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_VB_LO] = {
|
||||
.mask = RK818_IRQ_VB_LO_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_PWRON] = {
|
||||
.mask = RK818_IRQ_PWRON_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_PWRON_LP] = {
|
||||
.mask = RK818_IRQ_PWRON_LP_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_HOTDIE] = {
|
||||
.mask = RK818_IRQ_HOTDIE_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_RTC_ALARM] = {
|
||||
.mask = RK818_IRQ_RTC_ALARM_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_RTC_PERIOD] = {
|
||||
.mask = RK818_IRQ_RTC_PERIOD_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
[RK818_IRQ_USB_OV] = {
|
||||
.mask = RK818_IRQ_USB_OV_MSK,
|
||||
.reg_offset = 0,
|
||||
},
|
||||
|
||||
/* INT_STS2 */
|
||||
[RK818_IRQ_PLUG_IN] = {
|
||||
.mask = RK818_IRQ_PLUG_IN_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_PLUG_OUT] = {
|
||||
.mask = RK818_IRQ_PLUG_OUT_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_CHG_OK] = {
|
||||
.mask = RK818_IRQ_CHG_OK_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_CHG_TE] = {
|
||||
.mask = RK818_IRQ_CHG_TE_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_CHG_TS1] = {
|
||||
.mask = RK818_IRQ_CHG_TS1_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_TS2] = {
|
||||
.mask = RK818_IRQ_TS2_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_CHG_CVTLIM] = {
|
||||
.mask = RK818_IRQ_CHG_CVTLIM_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[RK818_IRQ_DISCHG_ILIM] = {
|
||||
.mask = RK818_IRQ_DISCHG_ILIM_MSK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip rk808_irq_chip = {
|
||||
.name = "rk808",
|
||||
.irqs = rk808_irqs,
|
||||
@@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
|
||||
.init_ack_masked = true,
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip rk818_irq_chip = {
|
||||
.name = "rk818",
|
||||
.irqs = rk818_irqs,
|
||||
.num_irqs = ARRAY_SIZE(rk818_irqs),
|
||||
.num_regs = 2,
|
||||
.irq_reg_stride = 2,
|
||||
.status_base = RK818_INT_STS_REG1,
|
||||
.mask_base = RK818_INT_STS_MSK_REG1,
|
||||
.ack_base = RK818_INT_STS_REG1,
|
||||
.init_ack_masked = true,
|
||||
};
|
||||
|
||||
static struct i2c_client *rk808_i2c_client;
|
||||
static void rk808_device_shutdown(void)
|
||||
{
|
||||
@@ -167,55 +290,100 @@ static void rk808_device_shutdown(void)
|
||||
dev_err(&rk808_i2c_client->dev, "power off error!\n");
|
||||
}
|
||||
|
||||
static const struct of_device_id rk808_of_match[] = {
|
||||
{ .compatible = "rockchip,rk808" },
|
||||
{ .compatible = "rockchip,rk818" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk808_of_match);
|
||||
|
||||
static int rk808_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct rk808 *rk808;
|
||||
const struct rk808_reg_data *pre_init_reg;
|
||||
const struct mfd_cell *cells;
|
||||
int nr_pre_init_regs;
|
||||
int nr_cells;
|
||||
int pm_off = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
|
||||
if (!rk808)
|
||||
return -ENOMEM;
|
||||
|
||||
rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB);
|
||||
if (rk808->variant < 0) {
|
||||
dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n",
|
||||
RK808_ID_MSB);
|
||||
return rk808->variant;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant);
|
||||
|
||||
switch (rk808->variant) {
|
||||
case RK808_ID:
|
||||
rk808->regmap_cfg = &rk808_regmap_config;
|
||||
rk808->regmap_irq_chip = &rk808_irq_chip;
|
||||
pre_init_reg = rk808_pre_init_reg;
|
||||
nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
|
||||
cells = rk808s;
|
||||
nr_cells = ARRAY_SIZE(rk808s);
|
||||
break;
|
||||
case RK818_ID:
|
||||
rk808->regmap_cfg = &rk818_regmap_config;
|
||||
rk808->regmap_irq_chip = &rk818_irq_chip;
|
||||
pre_init_reg = rk818_pre_init_reg;
|
||||
nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
|
||||
cells = rk818s;
|
||||
nr_cells = ARRAY_SIZE(rk818s);
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
|
||||
rk808->variant);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rk808->i2c = client;
|
||||
i2c_set_clientdata(client, rk808);
|
||||
|
||||
rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg);
|
||||
if (IS_ERR(rk808->regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(rk808->regmap);
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "No interrupt support, no core IRQ\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
|
||||
if (!rk808)
|
||||
return -ENOMEM;
|
||||
|
||||
rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config);
|
||||
if (IS_ERR(rk808->regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(rk808->regmap);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) {
|
||||
ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr,
|
||||
pre_init_reg[i].mask,
|
||||
pre_init_reg[i].value);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"0x%x write err\n", pre_init_reg[i].addr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(rk808->regmap, client->irq,
|
||||
IRQF_ONESHOT, -1,
|
||||
&rk808_irq_chip, &rk808->irq_data);
|
||||
rk808->regmap_irq_chip, &rk808->irq_data);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rk808->i2c = client;
|
||||
i2c_set_clientdata(client, rk808);
|
||||
for (i = 0; i < nr_pre_init_regs; i++) {
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
pre_init_reg[i].addr,
|
||||
pre_init_reg[i].mask,
|
||||
pre_init_reg[i].value);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"0x%x write err\n",
|
||||
pre_init_reg[i].addr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(&client->dev, -1,
|
||||
rk808s, ARRAY_SIZE(rk808s), NULL, 0,
|
||||
regmap_irq_get_domain(rk808->irq_data));
|
||||
ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
|
||||
cells, nr_cells, NULL, 0,
|
||||
regmap_irq_get_domain(rk808->irq_data));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to add MFD devices %d\n", ret);
|
||||
goto err_irq;
|
||||
@@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rk808_of_match[] = {
|
||||
{ .compatible = "rockchip,rk808" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk808_of_match);
|
||||
|
||||
static const struct i2c_device_id rk808_ids[] = {
|
||||
{ "rk808" },
|
||||
{ "rk818" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rk808_ids);
|
||||
@@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
|
||||
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("RK808 PMIC driver");
|
||||
MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
|
||||
MODULE_DESCRIPTION("RK808/RK818 PMIC driver");
|
||||
|
Reference in New Issue
Block a user