mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'mfd-for-linus-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"Core Frameworks:
- Add new !TOUCHSCREEN_SUN4I dependency for SUN4I_GPADC
- List include/dt-bindings/mfd/* to files supported in MAINTAINERS
New Drivers:
- Intel Apollo Lake SPI NOR
- ST STM32 Timers (Advanced, Basic and PWM)
- Motorola 6556002 CPCAP (PMIC)
New Device Support:
- Add support for AXP221 to axp20x
- Add support for Intel Gemini Lake to intel-lpss-pci
- Add support for MT6323 LED to mt6397-core
- Add support for COMe-bBD#, COMe-bSL6, COMe-bKL6, COMe-cAL6 and
COMe-cKL6 to kempld-core
New Functionality:
- Add support for Analog CODAC to sun6i-prcm
- Add support for Watchdog to lpc_ich
Fix-ups:
- Error handling improvements; axp288_charger, axp20x, ab8500-sysctrl
- Adapt platform data handling; axp20x
- IRQ handling improvements; arizona, axp20x
- Remove superfluous code; arizona, axp20x, lpc_ich
- Trivial coding style/spelling fixes; axp20x, abx500, mfd.txt
- Regmap fix-ups; axp20x
- DT changes; mfd.txt, aspeed-lpc, aspeed-gfx, ab8500-core, tps65912,
mt6397
- Use new I2C probing mechanism; max77686
- Constification; rk808
Bug Fixes:
- Stop data transfer whilst suspended; cros_ec"
* tag 'mfd-for-linus-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (43 commits)
mfd: lpc_ich: Enable watchdog on Intel Apollo Lake PCH
mfd: lpc_ich: Remove useless comments in core part
mfd: Add support for several boards to Kontron PLD driver
mfd: constify regmap_irq_chip structures
MAINTAINERS: Add include/dt-bindings/mfd to MFD entry
mfd: cpcap: Add minimal support
mfd: mt6397: Add MT6323 LED support into MT6397 driver
Documentation: devicetree: Add LED subnode binding for MT6323 PMIC
mfd: tps65912: Export OF device ID table as module aliases
mfd: ab8500-core: Rename clock device and compatible
mfd: cros_ec: Send correct suspend/resume event to EC
mfd: max77686: Remove I2C device ID table
mfd: max77686: Use the struct i2c_driver .probe_new instead of .probe
mfd: max77686: Use of_device_get_match_data() helper
mfd: max77686: Don't attempt to get i2c_device_id .data
mfd: ab8500-sysctrl: Handle probe deferral
mfd: intel-lpss: Add Intel Gemini Lake PCI IDs
mfd: axp20x: Fix AXP806 access errors on cold boot
mfd: cros_ec: Send suspend state notification to EC
mfd: cros_ec: Prevent data transfer while device is suspended
...
This commit is contained in:
17
Documentation/devicetree/bindings/mfd/aspeed-gfx.txt
Normal file
17
Documentation/devicetree/bindings/mfd/aspeed-gfx.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
* Device tree bindings for Aspeed SoC Display Controller (GFX)
|
||||
|
||||
The Aspeed SoC Display Controller primarily does as its name suggests, but also
|
||||
participates in pinmux requests on the g5 SoCs. It is therefore considered a
|
||||
syscon device.
|
||||
|
||||
Required properties:
|
||||
- compatible: "aspeed,ast2500-gfx", "syscon"
|
||||
- reg: contains offset/length value of the GFX memory
|
||||
region.
|
||||
|
||||
Example:
|
||||
|
||||
gfx: display@1e6e6000 {
|
||||
compatible = "aspeed,ast2500-gfx", "syscon";
|
||||
reg = <0x1e6e6000 0x1000>;
|
||||
};
|
||||
137
Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
Normal file
137
Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
Normal file
@@ -0,0 +1,137 @@
|
||||
======================================================================
|
||||
Device tree bindings for the Aspeed Low Pin Count (LPC) Bus Controller
|
||||
======================================================================
|
||||
|
||||
The LPC bus is a means to bridge a host CPU to a number of low-bandwidth
|
||||
peripheral devices, replacing the use of the ISA bus in the age of PCI[0]. The
|
||||
primary use case of the Aspeed LPC controller is as a slave on the bus
|
||||
(typically in a Baseboard Management Controller SoC), but under certain
|
||||
conditions it can also take the role of bus master.
|
||||
|
||||
The LPC controller is represented as a multi-function device to account for the
|
||||
mix of functionality it provides. The principle split is between the register
|
||||
layout at the start of the I/O space which is, to quote the Aspeed datasheet,
|
||||
"basically compatible with the [LPC registers from the] popular BMC controller
|
||||
H8S/2168[1]", and everything else, where everything else is an eclectic
|
||||
collection of functions with a esoteric register layout. "Everything else",
|
||||
here labeled the "host" portion of the controller, includes, but is not limited
|
||||
to:
|
||||
|
||||
* An IPMI Block Transfer[2] Controller
|
||||
|
||||
* An LPC Host Controller: Manages LPC functions such as host vs slave mode, the
|
||||
physical properties of some LPC pins, configuration of serial IRQs, and
|
||||
APB-to-LPC bridging amonst other functions.
|
||||
|
||||
* An LPC Host Interface Controller: Manages functions exposed to the host such
|
||||
as LPC firmware hub cycles, configuration of the LPC-to-AHB mapping, UART
|
||||
management and bus snoop configuration.
|
||||
|
||||
* A set of SuperIO[3] scratch registers: Enables implementation of e.g. custom
|
||||
hardware management protocols for handover between the host and baseboard
|
||||
management controller.
|
||||
|
||||
Additionally the state of the LPC controller influences the pinmux
|
||||
configuration, therefore the host portion of the controller is exposed as a
|
||||
syscon as a means to arbitrate access.
|
||||
|
||||
[0] http://www.intel.com/design/chipsets/industry/25128901.pdf
|
||||
[1] https://www.renesas.com/en-sg/doc/products/mpumcu/001/rej09b0078_h8s2168.pdf?key=7c88837454702128622bee53acbda8f4
|
||||
[2] http://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf
|
||||
[3] https://en.wikipedia.org/wiki/Super_I/O
|
||||
|
||||
Required properties
|
||||
===================
|
||||
|
||||
- compatible: One of:
|
||||
"aspeed,ast2400-lpc", "simple-mfd"
|
||||
"aspeed,ast2500-lpc", "simple-mfd"
|
||||
|
||||
- reg: contains the physical address and length values of the Aspeed
|
||||
LPC memory region.
|
||||
|
||||
- #address-cells: <1>
|
||||
- #size-cells: <1>
|
||||
- ranges: Maps 0 to the physical address and length of the LPC memory
|
||||
region
|
||||
|
||||
Required LPC Child nodes
|
||||
========================
|
||||
|
||||
BMC Node
|
||||
--------
|
||||
|
||||
- compatible: One of:
|
||||
"aspeed,ast2400-lpc-bmc"
|
||||
"aspeed,ast2500-lpc-bmc"
|
||||
|
||||
- reg: contains the physical address and length values of the
|
||||
H8S/2168-compatible LPC controller memory region
|
||||
|
||||
Host Node
|
||||
---------
|
||||
|
||||
- compatible: One of:
|
||||
"aspeed,ast2400-lpc-host", "simple-mfd", "syscon"
|
||||
"aspeed,ast2500-lpc-host", "simple-mfd", "syscon"
|
||||
|
||||
- reg: contains the address and length values of the host-related
|
||||
register space for the Aspeed LPC controller
|
||||
|
||||
- #address-cells: <1>
|
||||
- #size-cells: <1>
|
||||
- ranges: Maps 0 to the address and length of the host-related LPC memory
|
||||
region
|
||||
|
||||
Example:
|
||||
|
||||
lpc: lpc@1e789000 {
|
||||
compatible = "aspeed,ast2500-lpc", "simple-mfd";
|
||||
reg = <0x1e789000 0x1000>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x1e789000 0x1000>;
|
||||
|
||||
lpc_bmc: lpc-bmc@0 {
|
||||
compatible = "aspeed,ast2500-lpc-bmc";
|
||||
reg = <0x0 0x80>;
|
||||
};
|
||||
|
||||
lpc_host: lpc-host@80 {
|
||||
compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
|
||||
reg = <0x80 0x1e0>;
|
||||
reg-io-width = <4>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x80 0x1e0>;
|
||||
};
|
||||
};
|
||||
|
||||
Host Node Children
|
||||
==================
|
||||
|
||||
LPC Host Controller
|
||||
-------------------
|
||||
|
||||
The Aspeed LPC Host Controller configures the Low Pin Count (LPC) bus behaviour
|
||||
between the host and the baseboard management controller. The registers exist
|
||||
in the "host" portion of the Aspeed LPC controller, which must be the parent of
|
||||
the LPC host controller node.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: One of:
|
||||
"aspeed,ast2400-lhc";
|
||||
"aspeed,ast2500-lhc";
|
||||
|
||||
- reg: contains offset/length values of the LHC memory regions. In the
|
||||
AST2400 and AST2500 there are two regions.
|
||||
|
||||
Example:
|
||||
|
||||
lhc: lhc@20 {
|
||||
compatible = "aspeed,ast2500-lhc";
|
||||
reg = <0x20 0x24 0x48 0x8>;
|
||||
};
|
||||
@@ -19,12 +19,22 @@ Optional properties:
|
||||
|
||||
- compatible : "simple-mfd" - this signifies that the operating system should
|
||||
consider all subnodes of the MFD device as separate devices akin to how
|
||||
"simple-bus" inidicates when to see subnodes as children for a simple
|
||||
"simple-bus" indicates when to see subnodes as children for a simple
|
||||
memory-mapped bus. For more complex devices, when the nexus driver has to
|
||||
probe registers to figure out what child devices exist etc, this should not
|
||||
be used. In the latter case the child devices will be determined by the
|
||||
operating system.
|
||||
|
||||
- ranges: Describes the address mapping relationship to the parent. Should set
|
||||
the child's base address to 0, the physical address within parent's address
|
||||
space, and the length of the address map.
|
||||
|
||||
- #address-cells: Specifies the number of cells used to represent physical base
|
||||
addresses. Must be present if ranges is used.
|
||||
|
||||
- #size-cells: Specifies the number of cells used to represent the size of an
|
||||
address. Must be present if ranges is used.
|
||||
|
||||
Example:
|
||||
|
||||
foo@1000 {
|
||||
|
||||
31
Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
Normal file
31
Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Motorola CPCAP PMIC device tree binding
|
||||
|
||||
Required properties:
|
||||
- compatible : One or both of "motorola,cpcap" or "ste,6556002"
|
||||
- reg : SPI chip select
|
||||
- interrupt-parent : The parent interrupt controller
|
||||
- interrupts : The interrupt line the device is connected to
|
||||
- interrupt-controller : Marks the device node as an interrupt controller
|
||||
- #interrupt-cells : The number of cells to describe an IRQ, should be 2
|
||||
- #address-cells : Child device offset number of cells, should be 1
|
||||
- #size-cells : Child device size number of cells, should be 0
|
||||
- spi-max-frequency : Typically set to 3000000
|
||||
- spi-cs-high : SPI chip select direction
|
||||
|
||||
Example:
|
||||
|
||||
&mcspi1 {
|
||||
cpcap: pmic@0 {
|
||||
compatible = "motorola,cpcap", "ste,6556002";
|
||||
reg = <0>; /* cs0 */
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cs-high;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -34,6 +34,10 @@ Optional subnodes:
|
||||
- clk
|
||||
Required properties:
|
||||
- compatible: "mediatek,mt6397-clk"
|
||||
- led
|
||||
Required properties:
|
||||
- compatible: "mediatek,mt6323-led"
|
||||
see Documentation/devicetree/bindings/leds/leds-mt6323.txt
|
||||
|
||||
Example:
|
||||
pwrap: pwrap@1000f000 {
|
||||
|
||||
@@ -8476,6 +8476,7 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/mfd/
|
||||
F: drivers/mfd/
|
||||
F: include/linux/mfd/
|
||||
F: include/dt-bindings/mfd/
|
||||
|
||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
||||
M: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,7 @@ config MFD_SUN4I_GPADC
|
||||
select REGMAP_MMIO
|
||||
select REGMAP_IRQ
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
depends on !TOUCHSCREEN_SUN4I
|
||||
help
|
||||
Select this to get support for Allwinner SoCs (A10, A13 and A31) ADC.
|
||||
This driver will only map the hardware interrupt and registers, you
|
||||
@@ -506,17 +507,22 @@ config MFD_KEMPLD
|
||||
device may provide functions like watchdog, GPIO, UART and I2C bus.
|
||||
|
||||
The following modules are supported:
|
||||
* COMe-bBD#
|
||||
* COMe-bBL6
|
||||
* COMe-bHL6
|
||||
* COMe-bSL6
|
||||
* COMe-bIP#
|
||||
* COMe-bKL6
|
||||
* COMe-bPC2 (ETXexpress-PC)
|
||||
* COMe-bSC# (ETXexpress-SC T#)
|
||||
* COMe-cAL6
|
||||
* COMe-cBL6
|
||||
* COMe-cBT6
|
||||
* COMe-cBW6
|
||||
* COMe-cCT6
|
||||
* COMe-cDC2 (microETXexpress-DC)
|
||||
* COMe-cHL6
|
||||
* COMe-cKL6
|
||||
* COMe-cPC2 (microETXexpress-PC)
|
||||
* COMe-cSL6
|
||||
* COMe-mAL10
|
||||
@@ -714,6 +720,17 @@ config EZX_PCAP
|
||||
This enables the PCAP ASIC present on EZX Phones. This is
|
||||
needed for MMC, TouchScreen, Sound, USB, etc..
|
||||
|
||||
config MFD_CPCAP
|
||||
tristate "Support for Motorola CPCAP"
|
||||
depends on SPI
|
||||
depends on OF || COMPILE_TEST
|
||||
select REGMAP_SPI
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Say yes here if you want to include driver for CPCAP.
|
||||
It is used on many Motorola phones and tablets as a PMIC.
|
||||
At least Motorola Droid 4 is known to use CPCAP.
|
||||
|
||||
config MFD_VIPERBOARD
|
||||
tristate "Nano River Technologies Viperboard"
|
||||
select MFD_CORE
|
||||
|
||||
@@ -97,6 +97,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
|
||||
obj-$(CONFIG_MFD_CORE) += mfd-core.o
|
||||
|
||||
obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
|
||||
obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o
|
||||
|
||||
obj-$(CONFIG_MCP) += mcp-core.o
|
||||
obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
|
||||
|
||||
@@ -656,8 +656,8 @@ static const struct mfd_cell ab8500_devs[] = {
|
||||
.of_compatible = "stericsson,ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
.name = "ab8500-clk",
|
||||
.of_compatible = "stericsson,ab8500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
|
||||
@@ -101,7 +101,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
|
||||
u8 bank;
|
||||
|
||||
if (sysctrl_dev == NULL)
|
||||
return -EINVAL;
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
bank = (reg >> 8);
|
||||
if (!valid_bank(bank))
|
||||
@@ -117,11 +117,13 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
|
||||
u8 bank;
|
||||
|
||||
if (sysctrl_dev == NULL)
|
||||
return -EINVAL;
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
bank = (reg >> 8);
|
||||
if (!valid_bank(bank))
|
||||
if (!valid_bank(bank)) {
|
||||
pr_err("invalid bank\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
|
||||
(u8)(reg & 0xFF), mask, value);
|
||||
@@ -148,9 +150,15 @@ static int ab8500_sysctrl_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_sysctrl_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-sysctrl", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_sysctrl_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-sysctrl",
|
||||
.of_match_table = ab8500_sysctrl_match,
|
||||
},
|
||||
.probe = ab8500_sysctrl_probe,
|
||||
.remove = ab8500_sysctrl_remove,
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
#define ARIZONA_AOD_IRQ_INDEX 0
|
||||
#define ARIZONA_MAIN_IRQ_INDEX 1
|
||||
|
||||
static int arizona_map_irq(struct arizona *arizona, int irq)
|
||||
{
|
||||
int ret;
|
||||
@@ -204,9 +207,10 @@ static const struct irq_domain_ops arizona_domain_ops = {
|
||||
int arizona_irq_init(struct arizona *arizona)
|
||||
{
|
||||
int flags = IRQF_ONESHOT;
|
||||
int ret, i;
|
||||
int ret;
|
||||
const struct regmap_irq_chip *aod, *irq;
|
||||
struct irq_data *irq_data;
|
||||
unsigned int virq;
|
||||
|
||||
arizona->ctrlif_error = true;
|
||||
|
||||
@@ -318,24 +322,34 @@ int arizona_irq_init(struct arizona *arizona)
|
||||
}
|
||||
|
||||
if (aod) {
|
||||
ret = regmap_add_irq_chip(arizona->regmap,
|
||||
irq_create_mapping(arizona->virq, 0),
|
||||
IRQF_ONESHOT, 0, aod,
|
||||
&arizona->aod_irq_chip);
|
||||
virq = irq_create_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
|
||||
if (!virq) {
|
||||
dev_err(arizona->dev, "Failed to map AOD IRQs\n");
|
||||
ret = -EINVAL;
|
||||
goto err_domain;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
|
||||
0, aod, &arizona->aod_irq_chip);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to add AOD IRQs: %d\n", ret);
|
||||
goto err;
|
||||
goto err_map_aod;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(arizona->regmap,
|
||||
irq_create_mapping(arizona->virq, 1),
|
||||
IRQF_ONESHOT, 0, irq,
|
||||
&arizona->irq_chip);
|
||||
virq = irq_create_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
|
||||
if (!virq) {
|
||||
dev_err(arizona->dev, "Failed to map main IRQs\n");
|
||||
ret = -EINVAL;
|
||||
goto err_aod;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
|
||||
0, irq, &arizona->irq_chip);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);
|
||||
goto err_aod;
|
||||
goto err_map_main_irq;
|
||||
}
|
||||
|
||||
/* Used to emulate edge trigger and to work around broken pinmux */
|
||||
@@ -368,9 +382,8 @@ int arizona_irq_init(struct arizona *arizona)
|
||||
}
|
||||
|
||||
/* Make sure the boot done IRQ is unmasked for resumes */
|
||||
i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
|
||||
ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
|
||||
"Boot done", arizona);
|
||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_BOOT_DONE, "Boot done",
|
||||
arizona_boot_done, arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
|
||||
arizona->irq, ret);
|
||||
@@ -379,10 +392,9 @@ int arizona_irq_init(struct arizona *arizona)
|
||||
|
||||
/* Handle control interface errors in the core */
|
||||
if (arizona->ctrlif_error) {
|
||||
i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
|
||||
ret = request_threaded_irq(i, NULL, arizona_ctrlif_err,
|
||||
IRQF_ONESHOT,
|
||||
"Control interface error", arizona);
|
||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR,
|
||||
"Control interface error",
|
||||
arizona_ctrlif_err, arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to request CTRLIF_ERR %d: %d\n",
|
||||
@@ -394,29 +406,47 @@ int arizona_irq_init(struct arizona *arizona)
|
||||
return 0;
|
||||
|
||||
err_ctrlif:
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
|
||||
err_boot_done:
|
||||
free_irq(arizona->irq, arizona);
|
||||
err_main_irq:
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1),
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq,
|
||||
ARIZONA_MAIN_IRQ_INDEX),
|
||||
arizona->irq_chip);
|
||||
err_map_main_irq:
|
||||
irq_dispose_mapping(irq_find_mapping(arizona->virq,
|
||||
ARIZONA_MAIN_IRQ_INDEX));
|
||||
err_aod:
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0),
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq,
|
||||
ARIZONA_AOD_IRQ_INDEX),
|
||||
arizona->aod_irq_chip);
|
||||
err_map_aod:
|
||||
irq_dispose_mapping(irq_find_mapping(arizona->virq,
|
||||
ARIZONA_AOD_IRQ_INDEX));
|
||||
err_domain:
|
||||
irq_domain_remove(arizona->virq);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int arizona_irq_exit(struct arizona *arizona)
|
||||
{
|
||||
unsigned int virq;
|
||||
|
||||
if (arizona->ctrlif_error)
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR),
|
||||
arizona);
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1),
|
||||
arizona->irq_chip);
|
||||
regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0),
|
||||
arizona->aod_irq_chip);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
|
||||
|
||||
virq = irq_find_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
|
||||
regmap_del_irq_chip(virq, arizona->irq_chip);
|
||||
irq_dispose_mapping(virq);
|
||||
|
||||
virq = irq_find_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
|
||||
regmap_del_irq_chip(virq, arizona->aod_irq_chip);
|
||||
irq_dispose_mapping(virq);
|
||||
|
||||
irq_domain_remove(arizona->virq);
|
||||
|
||||
free_irq(arizona->irq, arizona);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
struct wm_arizona;
|
||||
|
||||
extern const struct regmap_config wm5102_i2c_regmap;
|
||||
extern const struct regmap_config wm5102_spi_regmap;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
#define AXP20X_OFF 0x80
|
||||
|
||||
#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
|
||||
|
||||
static const char * const axp20x_model_names[] = {
|
||||
"AXP152",
|
||||
"AXP202",
|
||||
@@ -118,7 +120,14 @@ 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(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L),
|
||||
regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL),
|
||||
regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE),
|
||||
regmap_reg_range(AXP288_RT_BATT_V_H, AXP288_RT_BATT_V_L),
|
||||
regmap_reg_range(AXP20X_FG_RES, AXP288_FG_CC_CAP_REG),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table axp288_writeable_table = {
|
||||
@@ -207,14 +216,14 @@ static struct resource axp22x_pek_resources[] = {
|
||||
static struct resource axp288_power_button_resources[] = {
|
||||
{
|
||||
.name = "PEK_DBR",
|
||||
.start = AXP288_IRQ_POKN,
|
||||
.end = AXP288_IRQ_POKN,
|
||||
.start = AXP288_IRQ_POKP,
|
||||
.end = AXP288_IRQ_POKP,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "PEK_DBF",
|
||||
.start = AXP288_IRQ_POKP,
|
||||
.end = AXP288_IRQ_POKP,
|
||||
.start = AXP288_IRQ_POKN,
|
||||
.end = AXP288_IRQ_POKN,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
@@ -407,6 +416,9 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
|
||||
INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3),
|
||||
INIT_REGMAP_IRQ(AXP288, OV, 0, 4),
|
||||
INIT_REGMAP_IRQ(AXP288, FALLING_ALT, 0, 5),
|
||||
INIT_REGMAP_IRQ(AXP288, RISING_ALT, 0, 6),
|
||||
INIT_REGMAP_IRQ(AXP288, OV_ALT, 0, 7),
|
||||
|
||||
INIT_REGMAP_IRQ(AXP288, DONE, 1, 2),
|
||||
INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3),
|
||||
@@ -589,7 +601,22 @@ static struct mfd_cell axp20x_cells[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp22x_cells[] = {
|
||||
static struct mfd_cell axp221_cells[] = {
|
||||
{
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
|
||||
.resources = axp22x_pek_resources,
|
||||
}, {
|
||||
.name = "axp20x-regulator",
|
||||
}, {
|
||||
.name = "axp20x-usb-power-supply",
|
||||
.of_compatible = "x-powers,axp221-usb-power-supply",
|
||||
.num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
|
||||
.resources = axp22x_usb_power_supply_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp223_cells[] = {
|
||||
{
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
|
||||
@@ -598,7 +625,7 @@ static struct mfd_cell axp22x_cells[] = {
|
||||
.name = "axp20x-regulator",
|
||||
}, {
|
||||
.name = "axp20x-usb-power-supply",
|
||||
.of_compatible = "x-powers,axp221-usb-power-supply",
|
||||
.of_compatible = "x-powers,axp223-usb-power-supply",
|
||||
.num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
|
||||
.resources = axp22x_usb_power_supply_resources,
|
||||
},
|
||||
@@ -791,9 +818,14 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
|
||||
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
|
||||
break;
|
||||
case AXP221_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp221_cells);
|
||||
axp20x->cells = axp221_cells;
|
||||
axp20x->regmap_cfg = &axp22x_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
|
||||
break;
|
||||
case AXP223_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
|
||||
axp20x->cells = axp22x_cells;
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp223_cells);
|
||||
axp20x->cells = axp223_cells;
|
||||
axp20x->regmap_cfg = &axp22x_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
|
||||
break;
|
||||
@@ -802,6 +834,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
|
||||
axp20x->regmap_cfg = &axp288_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
|
||||
axp20x->irq_flags = IRQF_TRIGGER_LOW;
|
||||
break;
|
||||
case AXP806_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
|
||||
@@ -830,10 +863,33 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The AXP806 supports either master/standalone or slave mode.
|
||||
* Slave mode allows sharing the serial bus, even with multiple
|
||||
* AXP806 which all have the same hardware address.
|
||||
*
|
||||
* This is done with extra "serial interface address extension",
|
||||
* or AXP806_BUS_ADDR_EXT, and "register address extension", or
|
||||
* AXP806_REG_ADDR_EXT, registers. The former is read-only, with
|
||||
* 1 bit customizable at the factory, and 1 bit depending on the
|
||||
* state of an external pin. The latter is writable. The device
|
||||
* will only respond to operations to its other registers when
|
||||
* the these device addressing bits (in the upper 4 bits of the
|
||||
* registers) match.
|
||||
*
|
||||
* Since we only support an AXP806 chained to an AXP809 in slave
|
||||
* mode, and there isn't any existing hardware which uses AXP806
|
||||
* in master mode, or has 2 AXP806s in the same system, we can
|
||||
* just program the register address extension to the slave mode
|
||||
* address.
|
||||
*/
|
||||
if (axp20x->variant == AXP806_ID)
|
||||
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
|
||||
AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
|
||||
|
||||
ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED, -1,
|
||||
axp20x->regmap_irq_chip,
|
||||
&axp20x->regmap_irqc);
|
||||
IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags,
|
||||
-1, axp20x->regmap_irq_chip, &axp20x->regmap_irqc);
|
||||
if (ret) {
|
||||
dev_err(axp20x->dev, "failed to add irq chip: %d\n", ret);
|
||||
return ret;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define CROS_EC_DEV_EC_INDEX 0
|
||||
@@ -65,6 +66,24 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
|
||||
{
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
struct ec_params_host_sleep_event req;
|
||||
} __packed buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
|
||||
buf.req.sleep_event = sleep_event;
|
||||
|
||||
buf.msg.command = EC_CMD_HOST_SLEEP_EVENT;
|
||||
buf.msg.version = 0;
|
||||
buf.msg.outsize = sizeof(buf.req);
|
||||
|
||||
return cros_ec_cmd_xfer(ec_dev, &buf.msg);
|
||||
}
|
||||
|
||||
int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct device *dev = ec_dev->dev;
|
||||
@@ -136,6 +155,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear sleep event - this will fail harmlessly on platforms that
|
||||
* don't implement the sleep event host command.
|
||||
*/
|
||||
err = cros_ec_sleep_event(ec_dev, 0);
|
||||
if (err < 0)
|
||||
dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
|
||||
err);
|
||||
|
||||
dev_info(dev, "Chrome EC device registered\n");
|
||||
|
||||
return 0;
|
||||
@@ -159,12 +187,24 @@ EXPORT_SYMBOL(cros_ec_remove);
|
||||
int cros_ec_suspend(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct device *dev = ec_dev->dev;
|
||||
int ret;
|
||||
u8 sleep_event;
|
||||
|
||||
sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
|
||||
HOST_SLEEP_EVENT_S3_RESUME :
|
||||
HOST_SLEEP_EVENT_S0IX_RESUME;
|
||||
|
||||
ret = cros_ec_sleep_event(ec_dev, sleep_event);
|
||||
if (ret < 0)
|
||||
dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec",
|
||||
ret);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
|
||||
|
||||
disable_irq(ec_dev->irq);
|
||||
ec_dev->was_wake_device = ec_dev->wake_enabled;
|
||||
ec_dev->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -179,8 +219,21 @@ static void cros_ec_drain_events(struct cros_ec_device *ec_dev)
|
||||
|
||||
int cros_ec_resume(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
int ret;
|
||||
u8 sleep_event;
|
||||
|
||||
ec_dev->suspended = false;
|
||||
enable_irq(ec_dev->irq);
|
||||
|
||||
sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
|
||||
HOST_SLEEP_EVENT_S3_RESUME :
|
||||
HOST_SLEEP_EVENT_S0IX_RESUME;
|
||||
|
||||
ret = cros_ec_sleep_event(ec_dev, sleep_event);
|
||||
if (ret < 0)
|
||||
dev_dbg(ec_dev->dev, "Error %d sending resume event to ec",
|
||||
ret);
|
||||
|
||||
/*
|
||||
* In some cases, we need to distinguish between events that occur
|
||||
* during suspend if the EC is not a wake source. For example,
|
||||
|
||||
@@ -157,7 +157,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info },
|
||||
|
||||
/* GLK */
|
||||
{ PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info },
|
||||
/* APL */
|
||||
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
|
||||
|
||||
@@ -496,6 +496,14 @@ static struct platform_driver kempld_driver = {
|
||||
|
||||
static struct dmi_system_id kempld_dmi_table[] __initdata = {
|
||||
{
|
||||
.ident = "BBD6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"),
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "BBL6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
@@ -511,6 +519,30 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = {
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "BKL6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"),
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "BSL6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"),
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "CAL6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"),
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "CBL6",
|
||||
.matches = {
|
||||
@@ -599,6 +631,14 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = {
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "CKL6",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"),
|
||||
},
|
||||
.driver_data = (void *)&kempld_platform_data_generic,
|
||||
.callback = kempld_create_platform_device,
|
||||
}, {
|
||||
.ident = "CNTG",
|
||||
.matches = {
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This driver supports the following I/O Controller hubs:
|
||||
* (See the intel documentation on http://developer.intel.com.)
|
||||
* document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
|
||||
@@ -45,18 +41,6 @@
|
||||
* document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
|
||||
* document number 320066-003, 320257-008: EP80597 (IICH)
|
||||
* document number 324645-001, 324646-001: Cougar Point (CPT)
|
||||
* document number TBD : Patsburg (PBG)
|
||||
* document number TBD : DH89xxCC
|
||||
* document number TBD : Panther Point
|
||||
* document number TBD : Lynx Point
|
||||
* document number TBD : Lynx Point-LP
|
||||
* document number TBD : Wellsburg
|
||||
* document number TBD : Avoton SoC
|
||||
* document number TBD : Coleto Creek
|
||||
* document number TBD : Wildcat Point-LP
|
||||
* document number TBD : 9 Series
|
||||
* document number TBD : Lewisburg
|
||||
* document number TBD : Apollo Lake SoC
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@@ -567,6 +551,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
||||
},
|
||||
[LPC_APL] = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.iTCO_version = 5,
|
||||
.spi_type = INTEL_SPI_BXT,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <linux/mfd/max77686-private.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
static const struct mfd_cell max77686_devs[] = {
|
||||
{ .name = "max77686-pmic", },
|
||||
@@ -171,11 +172,9 @@ static const struct of_device_id max77686_pmic_dt_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max77686_pmic_dt_match);
|
||||
|
||||
static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
static int max77686_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct max77686_dev *max77686 = NULL;
|
||||
const struct of_device_id *match;
|
||||
unsigned int data;
|
||||
int ret = 0;
|
||||
const struct regmap_config *config;
|
||||
@@ -188,16 +187,8 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
if (!max77686)
|
||||
return -ENOMEM;
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
max77686->type = (unsigned long)match->data;
|
||||
} else
|
||||
max77686->type = id->driver_data;
|
||||
|
||||
i2c_set_clientdata(i2c, max77686);
|
||||
max77686->type = (unsigned long)of_device_get_match_data(&i2c->dev);
|
||||
max77686->dev = &i2c->dev;
|
||||
max77686->i2c = i2c;
|
||||
|
||||
@@ -250,13 +241,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max77686_i2c_id[] = {
|
||||
{ "max77686", TYPE_MAX77686 },
|
||||
{ "max77802", TYPE_MAX77802 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max77686_suspend(struct device *dev)
|
||||
{
|
||||
@@ -302,8 +286,7 @@ static struct i2c_driver max77686_i2c_driver = {
|
||||
.pm = &max77686_pm,
|
||||
.of_match_table = of_match_ptr(max77686_pmic_dt_match),
|
||||
},
|
||||
.probe = max77686_i2c_probe,
|
||||
.id_table = max77686_i2c_id,
|
||||
.probe_new = max77686_i2c_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(max77686_i2c_driver);
|
||||
|
||||
259
drivers/mfd/motorola-cpcap.c
Normal file
259
drivers/mfd/motorola-cpcap.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Motorola CPCAP PMIC core driver
|
||||
*
|
||||
* Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mfd/motorola-cpcap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define CPCAP_NR_IRQ_REG_BANKS 6
|
||||
#define CPCAP_NR_IRQ_CHIPS 3
|
||||
|
||||
struct cpcap_ddata {
|
||||
struct spi_device *spi;
|
||||
struct regmap_irq *irqs;
|
||||
struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
|
||||
const struct regmap_config *regmap_conf;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int cpcap_check_revision(struct cpcap_ddata *cpcap)
|
||||
{
|
||||
u16 vendor, rev;
|
||||
int ret;
|
||||
|
||||
ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
|
||||
vendor == CPCAP_VENDOR_ST ? "ST" : "TI",
|
||||
CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
|
||||
rev);
|
||||
|
||||
if (rev < CPCAP_REVISION_2_1) {
|
||||
dev_info(&cpcap->spi->dev,
|
||||
"Please add old CPCAP revision support as needed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* First two irq chips are the two private macro interrupt chips, the third
|
||||
* irq chip is for register banks 1 - 4 and is available for drivers to use.
|
||||
*/
|
||||
static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
|
||||
{
|
||||
.name = "cpcap-m2",
|
||||
.num_regs = 1,
|
||||
.status_base = CPCAP_REG_MI1,
|
||||
.ack_base = CPCAP_REG_MI1,
|
||||
.mask_base = CPCAP_REG_MIM1,
|
||||
.use_ack = true,
|
||||
},
|
||||
{
|
||||
.name = "cpcap-m2",
|
||||
.num_regs = 1,
|
||||
.status_base = CPCAP_REG_MI2,
|
||||
.ack_base = CPCAP_REG_MI2,
|
||||
.mask_base = CPCAP_REG_MIM2,
|
||||
.use_ack = true,
|
||||
},
|
||||
{
|
||||
.name = "cpcap1-4",
|
||||
.num_regs = 4,
|
||||
.status_base = CPCAP_REG_INT1,
|
||||
.ack_base = CPCAP_REG_INT1,
|
||||
.mask_base = CPCAP_REG_INTM1,
|
||||
.type_base = CPCAP_REG_INTS1,
|
||||
.use_ack = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap,
|
||||
struct regmap_irq *rirq,
|
||||
int irq_base, int irq)
|
||||
{
|
||||
unsigned int reg_offset;
|
||||
unsigned int bit, mask;
|
||||
|
||||
reg_offset = irq - irq_base;
|
||||
reg_offset /= cpcap->regmap_conf->val_bits;
|
||||
reg_offset *= cpcap->regmap_conf->reg_stride;
|
||||
|
||||
bit = irq % cpcap->regmap_conf->val_bits;
|
||||
mask = (1 << bit);
|
||||
|
||||
rirq->reg_offset = reg_offset;
|
||||
rirq->mask = mask;
|
||||
}
|
||||
|
||||
static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
|
||||
int irq_start, int nr_irqs)
|
||||
{
|
||||
struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip];
|
||||
int i, ret;
|
||||
|
||||
for (i = irq_start; i < irq_start + nr_irqs; i++) {
|
||||
struct regmap_irq *rirq = &cpcap->irqs[i];
|
||||
|
||||
cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i);
|
||||
}
|
||||
chip->irqs = &cpcap->irqs[irq_start];
|
||||
chip->num_irqs = nr_irqs;
|
||||
chip->irq_drv_data = cpcap;
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
|
||||
cpcap->spi->irq,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_SHARED, -1,
|
||||
chip, &cpcap->irqdata[irq_chip]);
|
||||
if (ret) {
|
||||
dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n",
|
||||
irq_chip, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpcap_init_irq(struct cpcap_ddata *cpcap)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cpcap->irqs = devm_kzalloc(&cpcap->spi->dev,
|
||||
sizeof(*cpcap->irqs) *
|
||||
CPCAP_NR_IRQ_REG_BANKS *
|
||||
cpcap->regmap_conf->val_bits,
|
||||
GFP_KERNEL);
|
||||
if (!cpcap->irqs)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cpcap_init_irq_chip(cpcap, 0, 0, 16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cpcap_init_irq_chip(cpcap, 1, 16, 16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cpcap_init_irq_chip(cpcap, 2, 32, 64);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
enable_irq_wake(cpcap->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cpcap_of_match[] = {
|
||||
{ .compatible = "motorola,cpcap", },
|
||||
{ .compatible = "st,6556002", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cpcap_of_match);
|
||||
|
||||
static const struct regmap_config cpcap_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.reg_stride = 4,
|
||||
.pad_bits = 0,
|
||||
.val_bits = 16,
|
||||
.write_flag_mask = 0x8000,
|
||||
.max_register = CPCAP_REG_ST_TEST2,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static int cpcap_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct cpcap_ddata *cpcap;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
|
||||
if (!cpcap)
|
||||
return -ENOMEM;
|
||||
|
||||
cpcap->spi = spi;
|
||||
spi_set_drvdata(spi, cpcap);
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
|
||||
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cpcap->regmap_conf = &cpcap_regmap_config;
|
||||
cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
|
||||
if (IS_ERR(cpcap->regmap)) {
|
||||
ret = PTR_ERR(cpcap->regmap);
|
||||
dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpcap_check_revision(cpcap);
|
||||
if (ret) {
|
||||
dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpcap_init_irq(cpcap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return of_platform_populate(spi->dev.of_node, NULL, NULL,
|
||||
&cpcap->spi->dev);
|
||||
}
|
||||
|
||||
static int cpcap_remove(struct spi_device *pdev)
|
||||
{
|
||||
struct cpcap_ddata *cpcap = spi_get_drvdata(pdev);
|
||||
|
||||
of_platform_depopulate(&cpcap->spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver cpcap_driver = {
|
||||
.driver = {
|
||||
.name = "cpcap-core",
|
||||
.of_match_table = cpcap_of_match,
|
||||
},
|
||||
.probe = cpcap_probe,
|
||||
.remove = cpcap_remove,
|
||||
};
|
||||
module_spi_driver(cpcap_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cpcap");
|
||||
MODULE_DESCRIPTION("CPCAP driver");
|
||||
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user