Merge tag 'mfd-next-5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones: "New Drivers: - Add support for ROHM BD71828 PMICs and GPIOs - Add support for Qualcomm Aqstic Audio Codecs WCD9340 and WCD9341 New Device Support: - Add support for BD71828 to BD70528 RTC driver - Add support for Intel's Jasper Lake to LPSS PCI New Functionality: - Add support for Power Key to ROHM BD71828 - Add support for Clocks to ROHM BD71828 - Add support for GPIOs to Dialog DA9062 - Add support for USB PD Notify to ChromiumOS EC - Allow callers to specify args when requesting regmap lookup; syscon Fix-ups: - Improve error handling and sanity checking; atmel-hlcdc, dln2 - Device Tree support/documentation; bd71828, da9062, xylon,logicvc, ab8500, max14577, atmel-usart - Match devices using platform IDs; bd7xxxx - Refactor BD718x7 regulator component; bd718x7-regulator - Use standard interfaces/helpers; syscon, sm501 - Trivial (whitespace, spelling, etc); ab8500-core, Kconfig - Remove unused code; db8500-prcmu, tqmx86 - Wait until boot has finished before accessing registers; madera-core - Provide missing register value defaults; cs47l15-tables - Allow more time for hardware to reset; madera-core Bug Fixes: - Fix erroneous register values; rohm-bd70528 - Fix register volatility; axp20x, rn5t618 - Fix Kconfig dependencies; MFD_MAX77650 - Fix incorrect compatible string; da9062-core - Fix syscon_regmap_lookup_by_phandle_args() stub; syscon" * tag 'mfd-next-5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (41 commits) mfd: syscon: Fix syscon_regmap_lookup_by_phandle_args() dummy mfd: wcd934x: Add support to wcd9340/wcd9341 codec mfd: syscon: Add arguments support for syscon reference mfd: rn5t618: Mark ADC control register volatile dt-bindings: atmel-usart: Add microchip,sam9x60-{usart, dbgu} dt-bindings: atmel-usart: Remove wildcard mfd: cros_ec: Add cros-usbpd-notify subdevice mfd: da9062: Fix watchdog compatible string mfd: madera: Allow more time for hardware reset mfd: cs47l15: Add missing register default mfd: madera: Wait for boot done before accessing any other registers mfd: Kconfig: Rename Samsung to lowercase mfd: tqmx86: remove set but not used variable 'i2c_ien' mfd: dbx500-prcmu: Drop DSI pll clock functions mfd: dbx500-prcmu: Drop set_display_clocks() mfd: max77650: Select REGMAP_IRQ in Kconfig mfd: axp20x: Mark AXP20X_VBUS_IPSOUT_MGMT as volatile mfd: ab8500: Fix ab8500-clk typo mfd: intel-lpss: Add Intel Jasper Lake PCI IDs dt-bindings: mfd: max14577: Add reference to max14040_battery.txt descriptions ...
This commit is contained in:
@@ -758,6 +758,7 @@ config MFD_MAX77650
|
||||
depends on OF || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Say Y here to add support for Maxim Semiconductor MAX77650 and
|
||||
MAX77651 Power Management ICs. This is the core multifunction
|
||||
@@ -1065,7 +1066,7 @@ config MFD_RN5T618
|
||||
functionality of the device.
|
||||
|
||||
config MFD_SEC_CORE
|
||||
tristate "SAMSUNG Electronics PMIC Series Support"
|
||||
tristate "Samsung Electronics PMIC Series Support"
|
||||
depends on I2C=y
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
@@ -1906,6 +1907,21 @@ config MFD_ROHM_BD70528
|
||||
10 bits SAR ADC for battery temperature monitor and 1S battery
|
||||
charger.
|
||||
|
||||
config MFD_ROHM_BD71828
|
||||
tristate "ROHM BD71828 Power Management IC"
|
||||
depends on I2C=y
|
||||
depends on OF
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to get support for the ROHM BD71828 Power
|
||||
Management IC. BD71828GW is a single-chip power management IC for
|
||||
battery-powered portable devices. The IC integrates 7 buck
|
||||
converters, 7 LDOs, and a 1500 mA single-cell linear charger.
|
||||
Also included is a Coulomb counter, a real-time clock (RTC), and
|
||||
a 32.768 kHz clock gate.
|
||||
|
||||
config MFD_STM32_LPTIMER
|
||||
tristate "Support for STM32 Low-Power Timer"
|
||||
depends on (ARCH_STM32 && OF) || COMPILE_TEST
|
||||
@@ -1960,6 +1976,18 @@ config MFD_STMFX
|
||||
additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
|
||||
config MFD_WCD934X
|
||||
tristate "Support for WCD9340/WCD9341 Codec"
|
||||
depends on SLIMBUS
|
||||
select REGMAP
|
||||
select REGMAP_SLIMBUS
|
||||
select REGMAP_IRQ
|
||||
select MFD_CORE
|
||||
help
|
||||
Support for the Qualcomm WCD9340/WCD9341 Codec.
|
||||
This driver provides common support WCD934x audio codec and its
|
||||
associated Pin Controller, Soundwire Controller and Audio codec.
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
depends on ARCH_SA1100
|
||||
|
||||
|
@@ -58,6 +58,7 @@ endif
|
||||
ifeq ($(CONFIG_MFD_CS47L24),y)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += cs47l24-tables.o
|
||||
endif
|
||||
obj-$(CONFIG_MFD_WCD934X) += wcd934x.o
|
||||
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
|
||||
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
|
||||
wm831x-objs += wm831x-auxadc.o
|
||||
@@ -252,6 +253,7 @@ obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o
|
||||
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
|
||||
obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o
|
||||
obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o
|
||||
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
|
||||
obj-$(CONFIG_MFD_STMFX) += stmfx.o
|
||||
|
||||
|
@@ -631,8 +631,8 @@ static const struct mfd_cell ab8500_devs[] = {
|
||||
NULL, NULL, 0, 0, "stericsson,ab8500-ext-regulator"),
|
||||
OF_MFD_CELL("ab8500-regulator",
|
||||
NULL, NULL, 0, 0, "stericsson,ab8500-regulator"),
|
||||
OF_MFD_CELL("abx500-clk",
|
||||
NULL, NULL, 0, 0, "stericsson,abx500-clk"),
|
||||
OF_MFD_CELL("ab8500-clk",
|
||||
NULL, NULL, 0, 0, "stericsson,ab8500-clk"),
|
||||
OF_MFD_CELL("ab8500-gpadc",
|
||||
NULL, NULL, 0, 0, "stericsson,ab8500-gpadc"),
|
||||
OF_MFD_CELL("ab8500-rtc",
|
||||
@@ -718,17 +718,20 @@ static const struct mfd_cell ab8505_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.of_compatible = "stericsson,ab8500-debug",
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
.of_compatible = "stericsson,ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
.of_compatible = "stericsson,ab8505-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
.of_compatible = "stericsson,ab8500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
@@ -736,25 +739,32 @@ static const struct mfd_cell ab8505_devs[] = {
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.of_compatible = "stericsson,ab8500-rtc",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.of_compatible = "stericsson,ab8500-acc-det",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.of_compatible = "stericsson,ab8500-poweron-key",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "pinctrl-ab8505",
|
||||
.of_compatible = "stericsson,ab8505-gpio",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-usb",
|
||||
.of_compatible = "stericsson,ab8500-usb",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-codec",
|
||||
.of_compatible = "stericsson,ab8500-codec",
|
||||
},
|
||||
{
|
||||
.name = "ab-iddet",
|
||||
@@ -1276,7 +1286,7 @@ static int ab8500_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct platform_device_id ab8500_id[] = {
|
||||
{ "ab8500-core", AB8500_VERSION_AB8500 },
|
||||
{ "ab8505-i2c", AB8500_VERSION_AB8505 },
|
||||
{ "ab8505-core", AB8500_VERSION_AB8505 },
|
||||
{ "ab9540-i2c", AB8500_VERSION_AB9540 },
|
||||
{ "ab8540-i2c", AB8500_VERSION_AB8540 },
|
||||
{ }
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
struct atmel_hlcdc_regmap {
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const struct mfd_cell atmel_hlcdc_cells[] = {
|
||||
@@ -39,10 +40,17 @@ static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
|
||||
|
||||
if (reg <= ATMEL_HLCDC_DIS) {
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
|
||||
status, !(status & ATMEL_HLCDC_SIP),
|
||||
1, 100);
|
||||
ret = readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
|
||||
status,
|
||||
!(status & ATMEL_HLCDC_SIP),
|
||||
1, 100);
|
||||
if (ret) {
|
||||
dev_err(hregmap->dev,
|
||||
"Timeout! Clock domain synchronization is in progress!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
writel(val, hregmap->regs + reg);
|
||||
@@ -90,6 +98,8 @@ static int atmel_hlcdc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(hregmap->regs))
|
||||
return PTR_ERR(hregmap->regs);
|
||||
|
||||
hregmap->dev = &pdev->dev;
|
||||
|
||||
hlcdc->irq = platform_get_irq(pdev, 0);
|
||||
if (hlcdc->irq < 0)
|
||||
return hlcdc->irq;
|
||||
|
@@ -126,7 +126,7 @@ static const struct regmap_range axp288_writeable_ranges[] = {
|
||||
static const struct regmap_range axp288_volatile_ranges[] = {
|
||||
regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON),
|
||||
regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL),
|
||||
regmap_reg_range(AXP288_BC_DET_STAT, AXP288_BC_DET_STAT),
|
||||
regmap_reg_range(AXP288_BC_DET_STAT, AXP20X_VBUS_IPSOUT_MGMT),
|
||||
regmap_reg_range(AXP20X_CHRG_BAK_CTRL, AXP20X_CHRG_BAK_CTRL),
|
||||
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L),
|
||||
regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL),
|
||||
|
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/module.h>
|
||||
@@ -87,6 +88,10 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = {
|
||||
{ .name = "cros-usbpd-logger", },
|
||||
};
|
||||
|
||||
static const struct mfd_cell cros_usbpd_notify_cells[] = {
|
||||
{ .name = "cros-usbpd-notify", },
|
||||
};
|
||||
|
||||
static const struct cros_feature_to_cells cros_subdevices[] = {
|
||||
{
|
||||
.id = EC_FEATURE_CEC,
|
||||
@@ -202,6 +207,23 @@ static int ec_device_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The PD notifier driver cell is separate since it only needs to be
|
||||
* explicitly added on platforms that don't have the PD notifier ACPI
|
||||
* device entry defined.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_OF)) {
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
|
||||
retval = mfd_add_hotplug_devices(ec->dev,
|
||||
cros_usbpd_notify_cells,
|
||||
ARRAY_SIZE(cros_usbpd_notify_cells));
|
||||
if (retval)
|
||||
dev_err(ec->dev,
|
||||
"failed to add PD notify devices: %d\n",
|
||||
retval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following subdevices cannot be detected by sending the
|
||||
* EC_FEATURE_GET_CMD to the Embedded Controller device.
|
||||
|
@@ -112,6 +112,7 @@ static const struct reg_default cs47l15_reg_default[] = {
|
||||
{ 0x000001dd, 0x0011 }, /* R477 (0x1DD) - FLL AO Control 11 */
|
||||
{ 0x00000218, 0x00e6 }, /* R536 (0x218) - Mic Bias Ctrl 1 */
|
||||
{ 0x0000021c, 0x0222 }, /* R540 (0x21C) - Mic Bias Ctrl 5 */
|
||||
{ 0x00000293, 0x0080 }, /* R659 (0x293) - Accessory Detect Mode 1 */
|
||||
{ 0x00000299, 0x0000 }, /* R665 (0x299) - Headphone Detect 0 */
|
||||
{ 0x0000029b, 0x0000 }, /* R667 (0x29B) - Headphone Detect 1 */
|
||||
{ 0x000002a2, 0x0010 }, /* R674 (0x2A2) - Mic Detect 1 Control 0 */
|
||||
|
@@ -233,6 +233,14 @@ static struct resource da9062_onkey_resources[] = {
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_ONKEY, 1, "ONKEY", IORESOURCE_IRQ),
|
||||
};
|
||||
|
||||
static struct resource da9062_gpio_resources[] = {
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_GPI0, 1, "GPI0", IORESOURCE_IRQ),
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_GPI1, 1, "GPI1", IORESOURCE_IRQ),
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_GPI2, 1, "GPI2", IORESOURCE_IRQ),
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_GPI3, 1, "GPI3", IORESOURCE_IRQ),
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_GPI4, 1, "GPI4", IORESOURCE_IRQ),
|
||||
};
|
||||
|
||||
static const struct mfd_cell da9062_devs[] = {
|
||||
{
|
||||
.name = "da9062-core",
|
||||
@@ -248,7 +256,7 @@ static const struct mfd_cell da9062_devs[] = {
|
||||
.name = "da9062-watchdog",
|
||||
.num_resources = ARRAY_SIZE(da9062_wdt_resources),
|
||||
.resources = da9062_wdt_resources,
|
||||
.of_compatible = "dlg,da9062-wdt",
|
||||
.of_compatible = "dlg,da9062-watchdog",
|
||||
},
|
||||
{
|
||||
.name = "da9062-thermal",
|
||||
@@ -266,7 +274,13 @@ static const struct mfd_cell da9062_devs[] = {
|
||||
.name = "da9062-onkey",
|
||||
.num_resources = ARRAY_SIZE(da9062_onkey_resources),
|
||||
.resources = da9062_onkey_resources,
|
||||
.of_compatible = "dlg,da9062-onkey",
|
||||
.of_compatible = "dlg,da9062-onkey",
|
||||
},
|
||||
{
|
||||
.name = "da9062-gpio",
|
||||
.num_resources = ARRAY_SIZE(da9062_gpio_resources),
|
||||
.resources = da9062_gpio_resources,
|
||||
.of_compatible = "dlg,da9062-gpio",
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -542,102 +542,6 @@ static struct dsiescclk dsiescclk[3] = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Used by MCDE to setup all necessary PRCMU registers
|
||||
*/
|
||||
#define PRCMU_RESET_DSIPLL 0x00004000
|
||||
#define PRCMU_UNCLAMP_DSIPLL 0x00400800
|
||||
|
||||
#define PRCMU_CLK_PLL_DIV_SHIFT 0
|
||||
#define PRCMU_CLK_PLL_SW_SHIFT 5
|
||||
#define PRCMU_CLK_38 (1 << 9)
|
||||
#define PRCMU_CLK_38_SRC (1 << 10)
|
||||
#define PRCMU_CLK_38_DIV (1 << 11)
|
||||
|
||||
/* PLLDIV=12, PLLSW=4 (PLLDDR) */
|
||||
#define PRCMU_DSI_CLOCK_SETTING 0x0000008C
|
||||
|
||||
/* DPI 50000000 Hz */
|
||||
#define PRCMU_DPI_CLOCK_SETTING ((1 << PRCMU_CLK_PLL_SW_SHIFT) | \
|
||||
(16 << PRCMU_CLK_PLL_DIV_SHIFT))
|
||||
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000E00
|
||||
|
||||
/* D=101, N=1, R=4, SELDIV2=0 */
|
||||
#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
|
||||
|
||||
#define PRCMU_ENABLE_PLLDSI 0x00000001
|
||||
#define PRCMU_DISABLE_PLLDSI 0x00000000
|
||||
#define PRCMU_RELEASE_RESET_DSS 0x0000400C
|
||||
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000202
|
||||
/* ESC clk, div0=1, div1=1, div2=3 */
|
||||
#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x07030101
|
||||
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00030101
|
||||
#define PRCMU_DSI_RESET_SW 0x00000007
|
||||
|
||||
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
|
||||
|
||||
int db8500_prcmu_enable_dsipll(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Clear DSIPLL_RESETN */
|
||||
writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
|
||||
/* Unclamp DSIPLL in/out */
|
||||
writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
|
||||
|
||||
/* Set DSI PLL FREQ */
|
||||
writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
|
||||
writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
|
||||
/* Enable Escape clocks */
|
||||
writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
|
||||
|
||||
/* Start DSI PLL */
|
||||
writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
|
||||
/* Reset DSI PLL */
|
||||
writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED)
|
||||
== PRCMU_PLLDSI_LOCKP_LOCKED)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
/* Set DSIPLL_RESETN */
|
||||
writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db8500_prcmu_disable_dsipll(void)
|
||||
{
|
||||
/* Disable dsi pll */
|
||||
writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
|
||||
/* Disable escapeclock */
|
||||
writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db8500_prcmu_set_display_clocks(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clk_mgt_lock, flags);
|
||||
|
||||
/* Grab the HW semaphore. */
|
||||
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
|
||||
cpu_relax();
|
||||
|
||||
writel(PRCMU_DSI_CLOCK_SETTING, prcmu_base + PRCM_HDMICLK_MGT);
|
||||
writel(PRCMU_DSI_LP_CLOCK_SETTING, prcmu_base + PRCM_TVCLK_MGT);
|
||||
writel(PRCMU_DPI_CLOCK_SETTING, prcmu_base + PRCM_LCDCLK_MGT);
|
||||
|
||||
/* Release the HW semaphore. */
|
||||
writel(0, PRCM_SEM);
|
||||
|
||||
spin_unlock_irqrestore(&clk_mgt_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 db8500_prcmu_read(unsigned int reg)
|
||||
{
|
||||
return readl(prcmu_base + reg);
|
||||
@@ -3060,30 +2964,44 @@ static const struct mfd_cell db8500_prcmu_devs[] = {
|
||||
static int db8500_prcmu_register_ab8500(struct device *parent)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct resource ab8500_resource;
|
||||
struct resource ab850x_resource;
|
||||
const struct mfd_cell ab8500_cell = {
|
||||
.name = "ab8500-core",
|
||||
.of_compatible = "stericsson,ab8500",
|
||||
.id = AB8500_VERSION_AB8500,
|
||||
.resources = &ab8500_resource,
|
||||
.resources = &ab850x_resource,
|
||||
.num_resources = 1,
|
||||
};
|
||||
const struct mfd_cell ab8505_cell = {
|
||||
.name = "ab8505-core",
|
||||
.of_compatible = "stericsson,ab8505",
|
||||
.id = AB8500_VERSION_AB8505,
|
||||
.resources = &ab850x_resource,
|
||||
.num_resources = 1,
|
||||
};
|
||||
const struct mfd_cell *ab850x_cell;
|
||||
|
||||
if (!parent->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
/* Look up the device node, sneak the IRQ out of it */
|
||||
for_each_child_of_node(parent->of_node, np) {
|
||||
if (of_device_is_compatible(np, ab8500_cell.of_compatible))
|
||||
if (of_device_is_compatible(np, ab8500_cell.of_compatible)) {
|
||||
ab850x_cell = &ab8500_cell;
|
||||
break;
|
||||
}
|
||||
if (of_device_is_compatible(np, ab8505_cell.of_compatible)) {
|
||||
ab850x_cell = &ab8505_cell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!np) {
|
||||
dev_info(parent, "could not find AB8500 node in the device tree\n");
|
||||
dev_info(parent, "could not find AB850X node in the device tree\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
of_irq_to_resource_table(np, &ab8500_resource, 1);
|
||||
of_irq_to_resource_table(np, &ab850x_resource, 1);
|
||||
|
||||
return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);
|
||||
return mfd_add_devices(parent, 0, ab850x_cell, 1, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -722,6 +722,8 @@ static int dln2_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *usb_id)
|
||||
{
|
||||
struct usb_host_interface *hostif = interface->cur_altsetting;
|
||||
struct usb_endpoint_descriptor *epin;
|
||||
struct usb_endpoint_descriptor *epout;
|
||||
struct device *dev = &interface->dev;
|
||||
struct dln2_dev *dln2;
|
||||
int ret;
|
||||
@@ -731,12 +733,19 @@ static int dln2_probe(struct usb_interface *interface,
|
||||
hostif->desc.bNumEndpoints < 2)
|
||||
return -ENODEV;
|
||||
|
||||
epin = &hostif->endpoint[0].desc;
|
||||
epout = &hostif->endpoint[1].desc;
|
||||
if (!usb_endpoint_is_bulk_out(epout))
|
||||
return -ENODEV;
|
||||
if (!usb_endpoint_is_bulk_in(epin))
|
||||
return -ENODEV;
|
||||
|
||||
dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL);
|
||||
if (!dln2)
|
||||
return -ENOMEM;
|
||||
|
||||
dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress;
|
||||
dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress;
|
||||
dln2->ep_out = epout->bEndpointAddress;
|
||||
dln2->ep_in = epin->bEndpointAddress;
|
||||
dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface));
|
||||
dln2->interface = interface;
|
||||
usb_set_intfdata(interface, dln2);
|
||||
|
@@ -240,6 +240,19 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x4b79), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7a), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7b), (kernel_ulong_t)&bxt_i2c_info },
|
||||
/* JSL */
|
||||
{ PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&spt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&spt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4daf), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4dc5), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4dc6), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4de8), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4de9), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&spt_info },
|
||||
/* APL */
|
||||
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
|
||||
|
@@ -35,6 +35,9 @@
|
||||
|
||||
#define MADERA_32KZ_MCLK2 1
|
||||
|
||||
#define MADERA_RESET_MIN_US 2000
|
||||
#define MADERA_RESET_MAX_US 3000
|
||||
|
||||
static const char * const madera_core_supplies[] = {
|
||||
"AVDD",
|
||||
"DBVDD1",
|
||||
@@ -199,7 +202,7 @@ EXPORT_SYMBOL_GPL(madera_name_from_type);
|
||||
#define MADERA_BOOT_POLL_INTERVAL_USEC 5000
|
||||
#define MADERA_BOOT_POLL_TIMEOUT_USEC 25000
|
||||
|
||||
static int madera_wait_for_boot(struct madera *madera)
|
||||
static int madera_wait_for_boot_noack(struct madera *madera)
|
||||
{
|
||||
ktime_t timeout;
|
||||
unsigned int val = 0;
|
||||
@@ -226,6 +229,13 @@ static int madera_wait_for_boot(struct madera *madera)
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int madera_wait_for_boot(struct madera *madera)
|
||||
{
|
||||
int ret = madera_wait_for_boot_noack(madera);
|
||||
|
||||
/*
|
||||
* BOOT_DONE defaults to unmasked on boot so we must ack it.
|
||||
* Do this even after a timeout to avoid interrupt storms.
|
||||
@@ -249,16 +259,13 @@ static int madera_soft_reset(struct madera *madera)
|
||||
}
|
||||
|
||||
/* Allow time for internal clocks to startup after reset */
|
||||
usleep_range(1000, 2000);
|
||||
usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void madera_enable_hard_reset(struct madera *madera)
|
||||
{
|
||||
if (!madera->pdata.reset)
|
||||
return;
|
||||
|
||||
/*
|
||||
* There are many existing out-of-tree users of these codecs that we
|
||||
* can't break so preserve the expected behaviour of setting the line
|
||||
@@ -269,11 +276,9 @@ static void madera_enable_hard_reset(struct madera *madera)
|
||||
|
||||
static void madera_disable_hard_reset(struct madera *madera)
|
||||
{
|
||||
if (!madera->pdata.reset)
|
||||
return;
|
||||
|
||||
gpiod_set_raw_value_cansleep(madera->pdata.reset, 1);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US);
|
||||
}
|
||||
|
||||
static int __maybe_unused madera_runtime_resume(struct device *dev)
|
||||
@@ -292,6 +297,8 @@ static int __maybe_unused madera_runtime_resume(struct device *dev)
|
||||
regcache_cache_only(madera->regmap, false);
|
||||
regcache_cache_only(madera->regmap_32bit, false);
|
||||
|
||||
usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US);
|
||||
|
||||
ret = madera_wait_for_boot(madera);
|
||||
if (ret)
|
||||
goto err;
|
||||
@@ -545,6 +552,12 @@ int madera_dev_init(struct madera *madera)
|
||||
regcache_cache_only(madera->regmap, false);
|
||||
regcache_cache_only(madera->regmap_32bit, false);
|
||||
|
||||
ret = madera_wait_for_boot_noack(madera);
|
||||
if (ret) {
|
||||
dev_err(madera->dev, "Device failed initial boot: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we can power up and verify that this is a chip we know about
|
||||
* before we start doing any writes to its registers.
|
||||
@@ -650,7 +663,7 @@ int madera_dev_init(struct madera *madera)
|
||||
|
||||
ret = madera_wait_for_boot(madera);
|
||||
if (ret) {
|
||||
dev_err(madera->dev, "Device failed initial boot: %d\n", ret);
|
||||
dev_err(madera->dev, "Failed to clear boot done: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,7 @@ static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
|
||||
case RN5T618_WATCHDOGCNT:
|
||||
case RN5T618_DCIRQ:
|
||||
case RN5T618_ILIMDATAH ... RN5T618_AIN0DATAL:
|
||||
case RN5T618_ADCCNT3:
|
||||
case RN5T618_IR_ADC1 ... RN5T618_IR_ADC3:
|
||||
case RN5T618_IR_GPR:
|
||||
case RN5T618_IR_GPF:
|
||||
|
@@ -48,7 +48,7 @@ static struct mfd_cell bd70528_mfd_cells[] = {
|
||||
* We use BD71837 driver to drive the clock block. Only differences to
|
||||
* BD70528 clock gate are the register address and mask.
|
||||
*/
|
||||
{ .name = "bd718xx-clk", },
|
||||
{ .name = "bd70528-clk", },
|
||||
{ .name = "bd70528-wdt", },
|
||||
{
|
||||
.name = "bd70528-power",
|
||||
@@ -236,7 +236,6 @@ static int bd70528_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
dev_set_drvdata(&i2c->dev, &bd70528->chip);
|
||||
|
||||
bd70528->chip.chip_type = ROHM_CHIP_TYPE_BD70528;
|
||||
bd70528->chip.regmap = devm_regmap_init_i2c(i2c, &bd70528_regmap);
|
||||
if (IS_ERR(bd70528->chip.regmap)) {
|
||||
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
|
||||
|
344
drivers/mfd/rohm-bd71828.c
Normal file
344
drivers/mfd/rohm-bd71828.c
Normal file
@@ -0,0 +1,344 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright (C) 2019 ROHM Semiconductors
|
||||
//
|
||||
// ROHM BD71828 PMIC driver
|
||||
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/rohm-bd71828.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static struct gpio_keys_button button = {
|
||||
.code = KEY_POWER,
|
||||
.gpio = -1,
|
||||
.type = EV_KEY,
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data bd71828_powerkey_data = {
|
||||
.buttons = &button,
|
||||
.nbuttons = 1,
|
||||
.name = "bd71828-pwrkey",
|
||||
};
|
||||
|
||||
static const struct resource rtc_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
|
||||
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
|
||||
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
|
||||
};
|
||||
|
||||
static struct mfd_cell bd71828_mfd_cells[] = {
|
||||
{ .name = "bd71828-pmic", },
|
||||
{ .name = "bd71828-gpio", },
|
||||
{ .name = "bd71828-led", .of_compatible = "rohm,bd71828-leds" },
|
||||
/*
|
||||
* We use BD71837 driver to drive the clock block. Only differences to
|
||||
* BD70528 clock gate are the register address and mask.
|
||||
*/
|
||||
{ .name = "bd71828-clk", },
|
||||
{ .name = "bd71827-power", },
|
||||
{
|
||||
.name = "bd71828-rtc",
|
||||
.resources = rtc_irqs,
|
||||
.num_resources = ARRAY_SIZE(rtc_irqs),
|
||||
}, {
|
||||
.name = "gpio-keys",
|
||||
.platform_data = &bd71828_powerkey_data,
|
||||
.pdata_size = sizeof(bd71828_powerkey_data),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range volatile_ranges[] = {
|
||||
{
|
||||
.range_min = BD71828_REG_PS_CTRL_1,
|
||||
.range_max = BD71828_REG_PS_CTRL_1,
|
||||
}, {
|
||||
.range_min = BD71828_REG_PS_CTRL_3,
|
||||
.range_max = BD71828_REG_PS_CTRL_3,
|
||||
}, {
|
||||
.range_min = BD71828_REG_RTC_SEC,
|
||||
.range_max = BD71828_REG_RTC_YEAR,
|
||||
}, {
|
||||
/*
|
||||
* For now make all charger registers volatile because many
|
||||
* needs to be and because the charger block is not that
|
||||
* performance critical.
|
||||
*/
|
||||
.range_min = BD71828_REG_CHG_STATE,
|
||||
.range_max = BD71828_REG_CHG_FULL,
|
||||
}, {
|
||||
.range_min = BD71828_REG_INT_MAIN,
|
||||
.range_max = BD71828_REG_IO_STAT,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_access_table volatile_regs = {
|
||||
.yes_ranges = &volatile_ranges[0],
|
||||
.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
|
||||
};
|
||||
|
||||
static struct regmap_config bd71828_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_table = &volatile_regs,
|
||||
.max_register = BD71828_MAX_REGISTER,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
|
||||
* access corect sub-IRQ registers based on bits that are set in main IRQ
|
||||
* register.
|
||||
*/
|
||||
|
||||
static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
|
||||
static unsigned int bit1_offsets[] = {10}; /* TEMP IRQ */
|
||||
static unsigned int bit2_offsets[] = {6, 7, 8, 9}; /* BAT MON IRQ */
|
||||
static unsigned int bit3_offsets[] = {5}; /* BAT IRQ */
|
||||
static unsigned int bit4_offsets[] = {4}; /* CHG IRQ */
|
||||
static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
|
||||
static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
|
||||
static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
|
||||
|
||||
static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
|
||||
};
|
||||
|
||||
static struct regmap_irq bd71828_irqs[] = {
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK3_OCP, 0, BD71828_INT_BUCK3_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK4_OCP, 0, BD71828_INT_BUCK4_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK5_OCP, 0, BD71828_INT_BUCK5_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK6_OCP, 0, BD71828_INT_BUCK6_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BUCK7_OCP, 0, BD71828_INT_BUCK7_OCP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_PGFAULT, 0, BD71828_INT_PGFAULT_MASK),
|
||||
/* DCIN1 interrupts */
|
||||
REGMAP_IRQ_REG(BD71828_INT_DCIN_DET, 1, BD71828_INT_DCIN_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_DCIN_RMV, 1, BD71828_INT_DCIN_RMV_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CLPS_OUT, 1, BD71828_INT_CLPS_OUT_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CLPS_IN, 1, BD71828_INT_CLPS_IN_MASK),
|
||||
/* DCIN2 interrupts */
|
||||
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2,
|
||||
BD71828_INT_DCIN_MON_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2,
|
||||
BD71828_INT_DCIN_MON_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_LONGPUSH, 2, BD71828_INT_LONGPUSH_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_MIDPUSH, 2, BD71828_INT_MIDPUSH_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_SHORTPUSH, 2, BD71828_INT_SHORTPUSH_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_PUSH, 2, BD71828_INT_PUSH_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_WDOG, 2, BD71828_INT_WDOG_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_SWRESET, 2, BD71828_INT_SWRESET_MASK),
|
||||
/* Vsys */
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3,
|
||||
BD71828_INT_VSYS_UV_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3,
|
||||
BD71828_INT_VSYS_UV_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3,
|
||||
BD71828_INT_VSYS_LOW_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3,
|
||||
BD71828_INT_VSYS_LOW_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3,
|
||||
BD71828_INT_VSYS_HALL_IN_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3,
|
||||
BD71828_INT_VSYS_HALL_TOGGLE_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3,
|
||||
BD71828_INT_VSYS_MON_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3,
|
||||
BD71828_INT_VSYS_MON_DET_MASK),
|
||||
/* Charger */
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4,
|
||||
BD71828_INT_CHG_DCIN_ILIM_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4,
|
||||
BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4,
|
||||
BD71828_INT_CHG_WDG_TEMP_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4,
|
||||
BD71828_INT_CHG_WDG_TIME_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4,
|
||||
BD71828_INT_CHG_RECHARGE_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4,
|
||||
BD71828_INT_CHG_RECHARGE_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_RANGED_TEMP_TRANSITION, 4,
|
||||
BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4,
|
||||
BD71828_INT_CHG_STATE_TRANSITION_MASK),
|
||||
/* Battery */
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5,
|
||||
BD71828_INT_BAT_TEMP_NORMAL_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5,
|
||||
BD71828_INT_BAT_TEMP_ERANGE_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5,
|
||||
BD71828_INT_BAT_TEMP_WARN_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5,
|
||||
BD71828_INT_BAT_REMOVED_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5,
|
||||
BD71828_INT_BAT_DETECTED_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5,
|
||||
BD71828_INT_THERM_REMOVED_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5,
|
||||
BD71828_INT_THERM_DETECTED_MASK),
|
||||
/* Battery Mon 1 */
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_DEAD, 6, BD71828_INT_BAT_DEAD_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6,
|
||||
BD71828_INT_BAT_SHORTC_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6,
|
||||
BD71828_INT_BAT_SHORTC_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6,
|
||||
BD71828_INT_BAT_LOW_VOLT_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6,
|
||||
BD71828_INT_BAT_LOW_VOLT_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6,
|
||||
BD71828_INT_BAT_OVER_VOLT_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6,
|
||||
BD71828_INT_BAT_OVER_VOLT_DET_MASK),
|
||||
/* Battery Mon 2 */
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7,
|
||||
BD71828_INT_BAT_MON_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7,
|
||||
BD71828_INT_BAT_MON_DET_MASK),
|
||||
/* Battery Mon 3 (Coulomb counter) */
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8,
|
||||
BD71828_INT_BAT_CC_MON1_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8,
|
||||
BD71828_INT_BAT_CC_MON2_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8,
|
||||
BD71828_INT_BAT_CC_MON3_MASK),
|
||||
/* Battery Mon 4 */
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9,
|
||||
BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
|
||||
/* Temperature */
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10,
|
||||
BD71828_INT_TEMP_BAT_LOW_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10,
|
||||
BD71828_INT_TEMP_BAT_LOW_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10,
|
||||
BD71828_INT_TEMP_BAT_HI_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10,
|
||||
BD71828_INT_TEMP_BAT_HI_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_RES, 10,
|
||||
BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_DET, 10,
|
||||
BD71828_INT_TEMP_CHIP_OVER_125_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_DET, 10,
|
||||
BD71828_INT_TEMP_CHIP_OVER_VF_DET_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_RES, 10,
|
||||
BD71828_INT_TEMP_CHIP_OVER_VF_RES_MASK),
|
||||
/* RTC Alarm */
|
||||
REGMAP_IRQ_REG(BD71828_INT_RTC0, 11, BD71828_INT_RTC0_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_RTC1, 11, BD71828_INT_RTC1_MASK),
|
||||
REGMAP_IRQ_REG(BD71828_INT_RTC2, 11, BD71828_INT_RTC2_MASK),
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip bd71828_irq_chip = {
|
||||
.name = "bd71828_irq",
|
||||
.main_status = BD71828_REG_INT_MAIN,
|
||||
.irqs = &bd71828_irqs[0],
|
||||
.num_irqs = ARRAY_SIZE(bd71828_irqs),
|
||||
.status_base = BD71828_REG_INT_BUCK,
|
||||
.mask_base = BD71828_REG_INT_MASK_BUCK,
|
||||
.ack_base = BD71828_REG_INT_BUCK,
|
||||
.mask_invert = true,
|
||||
.init_ack_masked = true,
|
||||
.num_regs = 12,
|
||||
.num_main_regs = 1,
|
||||
.sub_reg_offsets = &bd71828_sub_irq_offsets[0],
|
||||
.num_main_status_bits = 8,
|
||||
.irq_reg_stride = 1,
|
||||
};
|
||||
|
||||
static int bd71828_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct rohm_regmap_dev *chip;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
int ret;
|
||||
|
||||
if (!i2c->irq) {
|
||||
dev_err(&i2c->dev, "No IRQ configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&i2c->dev, chip);
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(i2c, &bd71828_regmap);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
|
||||
return PTR_ERR(chip->regmap);
|
||||
}
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&i2c->dev, chip->regmap,
|
||||
i2c->irq, IRQF_ONESHOT, 0,
|
||||
&bd71828_irq_chip, &irq_data);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to add IRQ chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
|
||||
bd71828_irq_chip.num_irqs);
|
||||
|
||||
ret = regmap_irq_get_virq(irq_data, BD71828_INT_SHORTPUSH);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
button.irq = ret;
|
||||
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
|
||||
bd71828_mfd_cells,
|
||||
ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
|
||||
regmap_irq_get_domain(irq_data));
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Failed to create subdevices\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id bd71828_of_match[] = {
|
||||
{ .compatible = "rohm,bd71828", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd71828_of_match);
|
||||
|
||||
static struct i2c_driver bd71828_drv = {
|
||||
.driver = {
|
||||
.name = "rohm-bd71828",
|
||||
.of_match_table = bd71828_of_match,
|
||||
},
|
||||
.probe_new = &bd71828_i2c_probe,
|
||||
};
|
||||
module_i2c_driver(bd71828_drv);
|
||||
|
||||
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
||||
MODULE_DESCRIPTION("ROHM BD71828 Power Management IC driver");
|
||||
MODULE_LICENSE("GPL");
|
@@ -30,14 +30,24 @@ static struct gpio_keys_platform_data bd718xx_powerkey_data = {
|
||||
.name = "bd718xx-pwrkey",
|
||||
};
|
||||
|
||||
static struct mfd_cell bd718xx_mfd_cells[] = {
|
||||
static struct mfd_cell bd71837_mfd_cells[] = {
|
||||
{
|
||||
.name = "gpio-keys",
|
||||
.platform_data = &bd718xx_powerkey_data,
|
||||
.pdata_size = sizeof(bd718xx_powerkey_data),
|
||||
},
|
||||
{ .name = "bd718xx-clk", },
|
||||
{ .name = "bd718xx-pmic", },
|
||||
{ .name = "bd71837-clk", },
|
||||
{ .name = "bd71837-pmic", },
|
||||
};
|
||||
|
||||
static struct mfd_cell bd71847_mfd_cells[] = {
|
||||
{
|
||||
.name = "gpio-keys",
|
||||
.platform_data = &bd718xx_powerkey_data,
|
||||
.pdata_size = sizeof(bd718xx_powerkey_data),
|
||||
},
|
||||
{ .name = "bd71847-clk", },
|
||||
{ .name = "bd71847-pmic", },
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd718xx_irqs[] = {
|
||||
@@ -124,6 +134,9 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
|
||||
{
|
||||
struct bd718xx *bd718xx;
|
||||
int ret;
|
||||
unsigned int chip_type;
|
||||
struct mfd_cell *mfd;
|
||||
int cells;
|
||||
|
||||
if (!i2c->irq) {
|
||||
dev_err(&i2c->dev, "No IRQ configured\n");
|
||||
@@ -136,8 +149,21 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
|
||||
return -ENOMEM;
|
||||
|
||||
bd718xx->chip_irq = i2c->irq;
|
||||
bd718xx->chip.chip_type = (unsigned int)(uintptr_t)
|
||||
of_device_get_match_data(&i2c->dev);
|
||||
chip_type = (unsigned int)(uintptr_t)
|
||||
of_device_get_match_data(&i2c->dev);
|
||||
switch (chip_type) {
|
||||
case ROHM_CHIP_TYPE_BD71837:
|
||||
mfd = bd71837_mfd_cells;
|
||||
cells = ARRAY_SIZE(bd71837_mfd_cells);
|
||||
break;
|
||||
case ROHM_CHIP_TYPE_BD71847:
|
||||
mfd = bd71847_mfd_cells;
|
||||
cells = ARRAY_SIZE(bd71847_mfd_cells);
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c->dev, "Unknown device type");
|
||||
return -EINVAL;
|
||||
}
|
||||
bd718xx->chip.dev = &i2c->dev;
|
||||
dev_set_drvdata(&i2c->dev, bd718xx);
|
||||
|
||||
@@ -170,8 +196,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
|
||||
button.irq = ret;
|
||||
|
||||
ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
|
||||
bd718xx_mfd_cells,
|
||||
ARRAY_SIZE(bd718xx_mfd_cells), NULL, 0,
|
||||
mfd, cells, NULL, 0,
|
||||
regmap_irq_get_domain(bd718xx->irq_data));
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Failed to create subdevices\n");
|
||||
@@ -188,6 +213,10 @@ static const struct of_device_id bd718xx_of_match[] = {
|
||||
.compatible = "rohm,bd71847",
|
||||
.data = (void *)ROHM_CHIP_TYPE_BD71847,
|
||||
},
|
||||
{
|
||||
.compatible = "rohm,bd71850",
|
||||
.data = (void *)ROHM_CHIP_TYPE_BD71847,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd718xx_of_match);
|
||||
|
@@ -1086,8 +1086,7 @@ static int sm501_register_gpio(struct sm501_devdata *sm)
|
||||
iounmap(gpio->regs);
|
||||
|
||||
err_claimed:
|
||||
release_resource(gpio->regs_res);
|
||||
kfree(gpio->regs_res);
|
||||
release_mem_region(iobase, 0x20);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1095,6 +1094,7 @@ static int sm501_register_gpio(struct sm501_devdata *sm)
|
||||
static void sm501_gpio_remove(struct sm501_devdata *sm)
|
||||
{
|
||||
struct sm501_gpio *gpio = &sm->gpio;
|
||||
resource_size_t iobase = sm->io_res->start + SM501_GPIO;
|
||||
|
||||
if (!sm->gpio.registered)
|
||||
return;
|
||||
@@ -1103,8 +1103,7 @@ static void sm501_gpio_remove(struct sm501_devdata *sm)
|
||||
gpiochip_remove(&gpio->high.gpio);
|
||||
|
||||
iounmap(gpio->regs);
|
||||
release_resource(gpio->regs_res);
|
||||
kfree(gpio->regs_res);
|
||||
release_mem_region(iobase, 0x20);
|
||||
}
|
||||
|
||||
static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
|
||||
@@ -1427,8 +1426,7 @@ static int sm501_plat_probe(struct platform_device *dev)
|
||||
return sm501_init_dev(sm);
|
||||
|
||||
err_claim:
|
||||
release_resource(sm->regs_claim);
|
||||
kfree(sm->regs_claim);
|
||||
release_mem_region(sm->io_res->start, 0x100);
|
||||
err_res:
|
||||
kfree(sm);
|
||||
err1:
|
||||
@@ -1637,8 +1635,7 @@ static int sm501_pci_probe(struct pci_dev *dev,
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
release_resource(sm->regs_claim);
|
||||
kfree(sm->regs_claim);
|
||||
release_mem_region(sm->io_res->start, 0x100);
|
||||
err3:
|
||||
pci_disable_device(dev);
|
||||
err2:
|
||||
@@ -1673,8 +1670,7 @@ static void sm501_pci_remove(struct pci_dev *dev)
|
||||
sm501_dev_remove(sm);
|
||||
iounmap(sm->regs);
|
||||
|
||||
release_resource(sm->regs_claim);
|
||||
kfree(sm->regs_claim);
|
||||
release_mem_region(sm->io_res->start, 0x100);
|
||||
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
@@ -1686,8 +1682,7 @@ static int sm501_plat_remove(struct platform_device *dev)
|
||||
sm501_dev_remove(sm);
|
||||
iounmap(sm->regs);
|
||||
|
||||
release_resource(sm->regs_claim);
|
||||
kfree(sm->regs_claim);
|
||||
release_mem_region(sm->io_res->start, 0x100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -224,6 +224,35 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
|
||||
|
||||
struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np,
|
||||
const char *property,
|
||||
int arg_count,
|
||||
unsigned int *out_args)
|
||||
{
|
||||
struct device_node *syscon_np;
|
||||
struct of_phandle_args args;
|
||||
struct regmap *regmap;
|
||||
unsigned int index;
|
||||
int rc;
|
||||
|
||||
rc = of_parse_phandle_with_fixed_args(np, property, arg_count,
|
||||
0, &args);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
syscon_np = args.np;
|
||||
if (!syscon_np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
regmap = syscon_node_to_regmap(syscon_np);
|
||||
for (index = 0; index < arg_count; index++)
|
||||
out_args[index] = args.args[index];
|
||||
of_node_put(syscon_np);
|
||||
|
||||
return regmap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_args);
|
||||
|
||||
static int syscon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -245,7 +274,7 @@ static int syscon_probe(struct platform_device *pdev)
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
syscon_config.max_register = res->end - res->start - 3;
|
||||
syscon_config.max_register = resource_size(res) - 4;
|
||||
if (pdata)
|
||||
syscon_config.name = pdata->label;
|
||||
syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);
|
||||
|
@@ -158,7 +158,7 @@ static int tqmx86_board_id_to_clk_rate(u8 board_id)
|
||||
|
||||
static int tqmx86_probe(struct platform_device *pdev)
|
||||
{
|
||||
u8 board_id, rev, i2c_det, i2c_ien, io_ext_int_val;
|
||||
u8 board_id, rev, i2c_det, io_ext_int_val;
|
||||
struct device *dev = &pdev->dev;
|
||||
u8 gpio_irq_cfg, readback;
|
||||
const char *board_name;
|
||||
@@ -196,7 +196,6 @@ static int tqmx86_probe(struct platform_device *pdev)
|
||||
board_name, board_id, rev >> 4, rev & 0xf);
|
||||
|
||||
i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT);
|
||||
i2c_ien = ioread8(io_base + TQMX86_REG_I2C_INT_EN);
|
||||
|
||||
if (gpio_irq_cfg) {
|
||||
io_ext_int_val =
|
||||
|
306
drivers/mfd/wcd934x.c
Normal file
306
drivers/mfd/wcd934x.c
Normal file
@@ -0,0 +1,306 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019, Linaro Limited
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/wcd934x/registers.h>
|
||||
#include <linux/mfd/wcd934x/wcd934x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slimbus.h>
|
||||
|
||||
static const struct mfd_cell wcd934x_devices[] = {
|
||||
{
|
||||
.name = "wcd934x-codec",
|
||||
}, {
|
||||
.name = "wcd934x-gpio",
|
||||
.of_compatible = "qcom,wcd9340-gpio",
|
||||
}, {
|
||||
.name = "wcd934x-soundwire",
|
||||
.of_compatible = "qcom,soundwire-v1.3.0",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_irq wcd934x_irqs[] = {
|
||||
[WCD934X_IRQ_SLIMBUS] = {
|
||||
.reg_offset = 0,
|
||||
.mask = BIT(0),
|
||||
.type = {
|
||||
.type_reg_offset = 0,
|
||||
.types_supported = IRQ_TYPE_EDGE_BOTH,
|
||||
.type_reg_mask = BIT(0),
|
||||
.type_level_low_val = BIT(0),
|
||||
.type_level_high_val = BIT(0),
|
||||
.type_falling_val = 0,
|
||||
.type_rising_val = 0,
|
||||
},
|
||||
},
|
||||
[WCD934X_IRQ_SOUNDWIRE] = {
|
||||
.reg_offset = 2,
|
||||
.mask = BIT(4),
|
||||
.type = {
|
||||
.type_reg_offset = 2,
|
||||
.types_supported = IRQ_TYPE_EDGE_BOTH,
|
||||
.type_reg_mask = BIT(4),
|
||||
.type_level_low_val = BIT(4),
|
||||
.type_level_high_val = BIT(4),
|
||||
.type_falling_val = 0,
|
||||
.type_rising_val = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip wcd934x_regmap_irq_chip = {
|
||||
.name = "wcd934x_irq",
|
||||
.status_base = WCD934X_INTR_PIN1_STATUS0,
|
||||
.mask_base = WCD934X_INTR_PIN1_MASK0,
|
||||
.ack_base = WCD934X_INTR_PIN1_CLEAR0,
|
||||
.type_base = WCD934X_INTR_LEVEL0,
|
||||
.num_type_reg = 4,
|
||||
.type_in_mask = false,
|
||||
.num_regs = 4,
|
||||
.irqs = wcd934x_irqs,
|
||||
.num_irqs = ARRAY_SIZE(wcd934x_irqs),
|
||||
};
|
||||
|
||||
static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case WCD934X_INTR_PIN1_STATUS0...WCD934X_INTR_PIN2_CLEAR3:
|
||||
case WCD934X_SWR_AHB_BRIDGE_RD_DATA_0:
|
||||
case WCD934X_SWR_AHB_BRIDGE_RD_DATA_1:
|
||||
case WCD934X_SWR_AHB_BRIDGE_RD_DATA_2:
|
||||
case WCD934X_SWR_AHB_BRIDGE_RD_DATA_3:
|
||||
case WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS:
|
||||
case WCD934X_ANA_MBHC_RESULT_3:
|
||||
case WCD934X_ANA_MBHC_RESULT_2:
|
||||
case WCD934X_ANA_MBHC_RESULT_1:
|
||||
case WCD934X_ANA_MBHC_MECH:
|
||||
case WCD934X_ANA_MBHC_ELECT:
|
||||
case WCD934X_ANA_MBHC_ZDET:
|
||||
case WCD934X_ANA_MICB2:
|
||||
case WCD934X_ANA_RCO:
|
||||
case WCD934X_ANA_BIAS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static const struct regmap_range_cfg wcd934x_ranges[] = {
|
||||
{ .name = "WCD934X",
|
||||
.range_min = 0x0,
|
||||
.range_max = WCD934X_MAX_REGISTER,
|
||||
.selector_reg = WCD934X_SEL_REGISTER,
|
||||
.selector_mask = WCD934X_SEL_MASK,
|
||||
.selector_shift = WCD934X_SEL_SHIFT,
|
||||
.window_start = WCD934X_WINDOW_START,
|
||||
.window_len = WCD934X_WINDOW_LENGTH,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_config wcd934x_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = 0xffff,
|
||||
.can_multi_write = true,
|
||||
.ranges = wcd934x_ranges,
|
||||
.num_ranges = ARRAY_SIZE(wcd934x_ranges),
|
||||
.volatile_reg = wcd934x_is_volatile_register,
|
||||
};
|
||||
|
||||
static int wcd934x_bring_up(struct wcd934x_ddata *ddata)
|
||||
{
|
||||
struct regmap *regmap = ddata->regmap;
|
||||
u16 id_minor, id_major;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
|
||||
(u8 *)&id_minor, sizeof(u16));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
|
||||
(u8 *)&id_major, sizeof(u16));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ddata->dev, "WCD934x chip id major 0x%x, minor 0x%x\n",
|
||||
id_major, id_minor);
|
||||
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);
|
||||
regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);
|
||||
regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);
|
||||
/* Add 1msec delay for VOUT to settle */
|
||||
usleep_range(1000, 1100);
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);
|
||||
regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcd934x_slim_status_up(struct slim_device *sdev)
|
||||
{
|
||||
struct device *dev = &sdev->dev;
|
||||
struct wcd934x_ddata *ddata;
|
||||
int ret;
|
||||
|
||||
ddata = dev_get_drvdata(dev);
|
||||
|
||||
ddata->regmap = regmap_init_slimbus(sdev, &wcd934x_regmap_config);
|
||||
if (IS_ERR(ddata->regmap)) {
|
||||
dev_err(dev, "Error allocating slim regmap\n");
|
||||
return PTR_ERR(ddata->regmap);
|
||||
}
|
||||
|
||||
ret = wcd934x_bring_up(ddata);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to bring up WCD934X: err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq,
|
||||
IRQF_TRIGGER_HIGH, 0,
|
||||
&wcd934x_regmap_irq_chip,
|
||||
&ddata->irq_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to add IRQ chip: err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, wcd934x_devices,
|
||||
ARRAY_SIZE(wcd934x_devices), NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to add child devices: err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd934x_slim_status(struct slim_device *sdev,
|
||||
enum slim_device_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case SLIM_DEVICE_STATUS_UP:
|
||||
return wcd934x_slim_status_up(sdev);
|
||||
case SLIM_DEVICE_STATUS_DOWN:
|
||||
mfd_remove_devices(&sdev->dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcd934x_slim_probe(struct slim_device *sdev)
|
||||
{
|
||||
struct device *dev = &sdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct wcd934x_ddata *ddata;
|
||||
int reset_gpio, ret;
|
||||
|
||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
ddata->irq = of_irq_get(np, 0);
|
||||
if (ddata->irq < 0) {
|
||||
if (ddata->irq != -EPROBE_DEFER)
|
||||
dev_err(ddata->dev, "Failed to get IRQ: err = %d\n",
|
||||
ddata->irq);
|
||||
return ddata->irq;
|
||||
}
|
||||
|
||||
reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
if (reset_gpio < 0) {
|
||||
dev_err(dev, "Failed to get reset gpio: err = %d\n",
|
||||
reset_gpio);
|
||||
return reset_gpio;
|
||||
}
|
||||
|
||||
ddata->extclk = devm_clk_get(dev, "extclk");
|
||||
if (IS_ERR(ddata->extclk)) {
|
||||
dev_err(dev, "Failed to get extclk");
|
||||
return PTR_ERR(ddata->extclk);
|
||||
}
|
||||
|
||||
ddata->supplies[0].supply = "vdd-buck";
|
||||
ddata->supplies[1].supply = "vdd-buck-sido";
|
||||
ddata->supplies[2].supply = "vdd-tx";
|
||||
ddata->supplies[3].supply = "vdd-rx";
|
||||
ddata->supplies[4].supply = "vdd-io";
|
||||
|
||||
ret = regulator_bulk_get(dev, WCD934X_MAX_SUPPLY, ddata->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get supplies: err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(WCD934X_MAX_SUPPLY, ddata->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable supplies: err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For WCD934X, it takes about 600us for the Vout_A and
|
||||
* Vout_D to be ready after BUCK_SIDO is powered up.
|
||||
* SYS_RST_N shouldn't be pulled high during this time
|
||||
*/
|
||||
usleep_range(600, 650);
|
||||
gpio_direction_output(reset_gpio, 0);
|
||||
msleep(20);
|
||||
gpio_set_value(reset_gpio, 1);
|
||||
msleep(20);
|
||||
|
||||
ddata->dev = dev;
|
||||
dev_set_drvdata(dev, ddata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcd934x_slim_remove(struct slim_device *sdev)
|
||||
{
|
||||
struct wcd934x_ddata *ddata = dev_get_drvdata(&sdev->dev);
|
||||
|
||||
regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies);
|
||||
mfd_remove_devices(&sdev->dev);
|
||||
kfree(ddata);
|
||||
}
|
||||
|
||||
static const struct slim_device_id wcd934x_slim_id[] = {
|
||||
{ SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9340,
|
||||
SLIM_DEV_IDX_WCD9340, SLIM_DEV_INSTANCE_ID_WCD9340 },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct slim_driver wcd934x_slim_driver = {
|
||||
.driver = {
|
||||
.name = "wcd934x-slim",
|
||||
},
|
||||
.probe = wcd934x_slim_probe,
|
||||
.remove = wcd934x_slim_remove,
|
||||
.device_status = wcd934x_slim_status,
|
||||
.id_table = wcd934x_slim_id,
|
||||
};
|
||||
|
||||
module_slim_driver(wcd934x_slim_driver);
|
||||
MODULE_DESCRIPTION("WCD934X slim driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("slim:217:250:*");
|
||||
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
|
Reference in New Issue
Block a user