summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mfd/ac100.txt54
-rw-r--r--Documentation/devicetree/bindings/mfd/axp20x.txt28
-rw-r--r--Documentation/devicetree/bindings/mfd/rk808.txt37
-rw-r--r--Documentation/devicetree/bindings/mfd/stmpe.txt2
-rw-r--r--drivers/gpio/Kconfig10
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-lp873x.c193
-rw-r--r--drivers/gpio/gpio-stmpe.c167
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c135
-rw-r--r--drivers/mfd/Kconfig28
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/ac100.c137
-rw-r--r--drivers/mfd/arizona-core.c30
-rw-r--r--drivers/mfd/axp20x-rsb.c1
-rw-r--r--drivers/mfd/axp20x.c72
-rw-r--r--drivers/mfd/cros_ec.c58
-rw-r--r--drivers/mfd/lp873x.c99
-rw-r--r--drivers/mfd/rk808.c226
-rw-r--r--drivers/mfd/stmpe-i2c.c2
-rw-r--r--drivers/mfd/stmpe.c161
-rw-r--r--drivers/mfd/stmpe.h85
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c92
-rw-r--r--drivers/regulator/Kconfig6
-rw-r--r--drivers/regulator/axp20x-regulator.c118
-rw-r--r--drivers/regulator/rk808-regulator.c143
-rw-r--r--drivers/rtc/Kconfig14
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-ac100.c627
-rw-r--r--include/linux/mfd/ac100.h178
-rw-r--r--include/linux/mfd/arizona/core.h9
-rw-r--r--include/linux/mfd/axp20x.h60
-rw-r--r--include/linux/mfd/cros_ec.h18
-rw-r--r--include/linux/mfd/cros_ec_commands.h34
-rw-r--r--include/linux/mfd/lp873x.h269
-rw-r--r--include/linux/mfd/rk808.h154
-rw-r--r--include/linux/mfd/stmpe.h21
36 files changed, 3013 insertions, 261 deletions
diff --git a/Documentation/devicetree/bindings/mfd/ac100.txt b/Documentation/devicetree/bindings/mfd/ac100.txt
new file mode 100644
index 000000000000..b8ef00667599
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ac100.txt
@@ -0,0 +1,54 @@
+X-Powers AC100 Codec/RTC IC Device Tree bindings
+
+AC100 is a audio codec and RTC subsystem combo IC. The 2 parts are
+separated, including power supplies and interrupt lines, but share
+a common register address space and host interface.
+
+Required properties:
+- compatible: "x-powers,ac100"
+- reg: The I2C slave address or RSB hardware address for the chip
+- sub-nodes:
+ - codec
+ - compatible: "x-powers,ac100-codec"
+ - interrupt-parent: The parent interrupt controller
+ - interrupts: SoC NMI / GPIO interrupt connected to the
+ IRQ_AUDIO pin
+ - #clock-cells: Shall be 0
+ - clock-output-names: "4M_adda"
+
+ - see clock/clock-bindings.txt for common clock bindings
+
+ - rtc
+ - compatible: "x-powers,ac100-rtc"
+ - interrupt-parent: The parent interrupt controller
+ - interrupts: SoC NMI / GPIO interrupt connected to the
+ IRQ_RTC pin
+ - clocks: A phandle to the codec's "4M_adda" clock
+ - #clock-cells: Shall be 1
+ - clock-output-names: "cko1_rtc", "cko2_rtc", "cko3_rtc"
+
+ - see clock/clock-bindings.txt for common clock bindings
+
+Example:
+
+ac100: codec@e89 {
+ compatible = "x-powers,ac100";
+ reg = <0xe89>;
+
+ ac100_codec: codec {
+ compatible = "x-powers,ac100-codec";
+ interrupt-parent = <&r_pio>;
+ interrupts = <0 9 IRQ_TYPE_LEVEL_LOW>; /* PL9 */
+ #clock-cells = <0>;
+ clock-output-names = "4M_adda";
+ };
+
+ ac100_rtc: rtc {
+ compatible = "x-powers,ac100-rtc";
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&ac100_codec>;
+ #clock-cells = <1>;
+ clock-output-names = "cko1_rtc", "cko2_rtc", "cko3_rtc";
+ };
+};
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
index 585a95546288..8f3ad9ab4637 100644
--- a/Documentation/devicetree/bindings/mfd/axp20x.txt
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -10,7 +10,8 @@ axp809 (X-Powers)
Required properties:
- compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209",
- "x-powers,axp221", "x-powers,axp223", "x-powers,axp809"
+ "x-powers,axp221", "x-powers,axp223", "x-powers,axp806",
+ "x-powers,axp809"
- reg: The I2C slave address or RSB hardware address for the AXP chip
- interrupt-parent: The parent interrupt controller
- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
@@ -47,7 +48,6 @@ Optional properties for DCDC regulators:
probably makes sense for HiFi audio related
applications that aren't battery constrained.
-
AXP202/AXP209 regulators, type, and corresponding input supply names:
Regulator Type Supply Name Notes
@@ -86,6 +86,30 @@ LDO_IO1 : LDO : ips-supply : GPIO 1
RTC_LDO : LDO : ips-supply : always on
DRIVEVBUS : Enable output : drivevbus-supply : external regulator
+AXP806 regulators, type, and corresponding input supply names:
+
+Regulator Type Supply Name Notes
+--------- ---- ----------- -----
+DCDCA : DC-DC buck : vina-supply : poly-phase capable
+DCDCB : DC-DC buck : vinb-supply : poly-phase capable
+DCDCC : DC-DC buck : vinc-supply : poly-phase capable
+DCDCD : DC-DC buck : vind-supply : poly-phase capable
+DCDCE : DC-DC buck : vine-supply : poly-phase capable
+ALDO1 : LDO : aldoin-supply : shared supply
+ALDO2 : LDO : aldoin-supply : shared supply
+ALDO3 : LDO : aldoin-supply : shared supply
+BLDO1 : LDO : bldoin-supply : shared supply
+BLDO2 : LDO : bldoin-supply : shared supply
+BLDO3 : LDO : bldoin-supply : shared supply
+BLDO4 : LDO : bldoin-supply : shared supply
+CLDO1 : LDO : cldoin-supply : shared supply
+CLDO2 : LDO : cldoin-supply : shared supply
+CLDO3 : LDO : cldoin-supply : shared supply
+SW : On/Off Switch : swin-supply
+
+Additionally, the AXP806 DC-DC regulators support poly-phase arrangements
+for higher output current. The possible groupings are: A+B, A+B+C, D+E.
+
AXP809 regulators, type, and corresponding input supply names:
Regulator Type Supply Name Notes
diff --git a/Documentation/devicetree/bindings/mfd/rk808.txt b/Documentation/devicetree/bindings/mfd/rk808.txt
index 4ca6aab4273a..9636ae8d8d41 100644
--- a/Documentation/devicetree/bindings/mfd/rk808.txt
+++ b/Documentation/devicetree/bindings/mfd/rk808.txt
@@ -1,7 +1,11 @@
-RK808 Power Management Integrated Circuit
+RK8XX Power Management Integrated Circuit
+
+The rk8xx family current members:
+rk808
+rk818
Required properties:
-- compatible: "rockchip,rk808"
+- compatible: "rockchip,rk808", "rockchip,rk818"
- reg: I2C slave address
- interrupt-parent: The parent interrupt controller.
- interrupts: the interrupt outputs of the controller.
@@ -13,6 +17,8 @@ Optional properties:
default output clock name
- rockchip,system-power-controller: Telling whether or not this pmic is controlling
the system power.
+
+Optional RK808 properties:
- vcc1-supply: The input supply for DCDC_REG1
- vcc2-supply: The input supply for DCDC_REG2
- vcc3-supply: The input supply for DCDC_REG3
@@ -29,7 +35,20 @@ Optional properties:
the gpio controller. If DVS GPIOs aren't present, voltage changes will happen
very quickly with no slow ramp time.
-Regulators: All the regulators of RK808 to be instantiated shall be
+Optional RK818 properties:
+- vcc1-supply: The input supply for DCDC_REG1
+- vcc2-supply: The input supply for DCDC_REG2
+- vcc3-supply: The input supply for DCDC_REG3
+- vcc4-supply: The input supply for DCDC_REG4
+- boost-supply: The input supply for DCDC_BOOST
+- vcc6-supply: The input supply for LDO_REG1 and LDO_REG2
+- vcc7-supply: The input supply for LDO_REG3, LDO_REG5 and LDO_REG7
+- vcc8-supply: The input supply for LDO_REG4, LDO_REG6 and LDO_REG8
+- vcc9-supply: The input supply for LDO_REG9 and SWITCH_REG
+- h_5v-supply: The input supply for HDMI_SWITCH
+- usb-supply: The input supply for OTG_SWITCH
+
+Regulators: All the regulators of RK8XX to be instantiated shall be
listed in a child node named 'regulators'. Each regulator is represented
by a child node of the 'regulators' node.
@@ -48,6 +67,18 @@ number as described in RK808 datasheet.
- SWITCH_REGn
- valid values for n are 1 to 2
+Following regulators of the RK818 PMIC block are supported. Note that
+the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO
+number as described in RK818 datasheet.
+
+ - DCDC_REGn
+ - valid values for n are 1 to 4.
+ - LDO_REGn
+ - valid values for n are 1 to 9.
+ - SWITCH_REG
+ - HDMI_SWITCH
+ - OTG_SWITCH
+
Standard regulator bindings are used inside regulator subnodes. Check
Documentation/devicetree/bindings/regulator/regulator.txt
for more details
diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt
index 3fb68bfefc8b..f9065a5781a2 100644
--- a/Documentation/devicetree/bindings/mfd/stmpe.txt
+++ b/Documentation/devicetree/bindings/mfd/stmpe.txt
@@ -4,7 +4,7 @@ STMPE is an MFD device which may expose the following inbuilt devices: gpio,
keypad, touchscreen, adc, pwm, rotator.
Required properties:
- - compatible : "st,stmpe[610|801|811|1601|2401|2403]"
+ - compatible : "st,stmpe[610|801|811|1600|1601|2401|2403]"
- reg : I2C/SPI address of the device
Optional properties:
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 98dd47a30fc7..346e9a9b4397 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -874,6 +874,16 @@ config GPIO_LP3943
LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
Open drain outputs are required for this usage.
+config GPIO_LP873X
+ tristate "TI LP873X GPO"
+ depends on MFD_TI_LP873X
+ help
+ This driver supports the GPO on TI Lp873x PMICs. 2 GPOs are present
+ on LP873X PMICs.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-lp873x.
+
config GPIO_MAX77620
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 2a035ed8f168..d60432f2a126 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
+obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c
new file mode 100644
index 000000000000..f10d49da1554
--- /dev/null
+++ b/drivers/gpio/gpio-lp873x.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
+ * Keerthy <j-keerthy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65218 driver
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lp873x.h>
+
+#define BITS_PER_GPO 0x4
+#define LP873X_GPO_CTRL_OD 0x2
+
+struct lp873x_gpio {
+ struct gpio_chip chip;
+ struct lp873x *lp873;
+};
+
+static int lp873x_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ /* This device is output only */
+ return 0;
+}
+
+static int lp873x_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ /* This device is output only */
+ return -EINVAL;
+}
+
+static int lp873x_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct lp873x_gpio *gpio = gpiochip_get_data(chip);
+
+ /* Set the initial value */
+ return regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL,
+ BIT(offset * BITS_PER_GPO),
+ value ? BIT(offset * BITS_PER_GPO) : 0);
+}
+
+static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct lp873x_gpio *gpio = gpiochip_get_data(chip);
+ int ret, val;
+
+ ret = regmap_read(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, &val);
+ if (ret < 0)
+ return ret;
+
+ return val & BIT(offset * BITS_PER_GPO);
+}
+
+static void lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct lp873x_gpio *gpio = gpiochip_get_data(chip);
+
+ regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL,
+ BIT(offset * BITS_PER_GPO),
+ value ? BIT(offset * BITS_PER_GPO) : 0);
+}
+
+static int lp873x_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct lp873x_gpio *gpio = gpiochip_get_data(gc);
+ int ret;
+
+ switch (offset) {
+ case 0:
+ /* No MUX Set up Needed for GPO */
+ break;
+ case 1:
+ /* Setup the CLKIN_PIN_SEL MUX to GPO2 */
+ ret = regmap_update_bits(gpio->lp873->regmap, LP873X_REG_CONFIG,
+ LP873X_CONFIG_CLKIN_PIN_SEL, 0);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lp873x_gpio_set_single_ended(struct gpio_chip *gc,
+ unsigned int offset,
+ enum single_ended_mode mode)
+{
+ struct lp873x_gpio *gpio = gpiochip_get_data(gc);
+
+ switch (mode) {
+ case LINE_MODE_OPEN_DRAIN:
+ return regmap_update_bits(gpio->lp873->regmap,
+ LP873X_REG_GPO_CTRL,
+ BIT(offset * BITS_PER_GPO +
+ LP873X_GPO_CTRL_OD),
+ BIT(offset * BITS_PER_GPO +
+ LP873X_GPO_CTRL_OD));
+ case LINE_MODE_PUSH_PULL:
+ return regmap_update_bits(gpio->lp873->regmap,
+ LP873X_REG_GPO_CTRL,
+ BIT(offset * BITS_PER_GPO +
+ LP873X_GPO_CTRL_OD), 0);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static struct gpio_chip template_chip = {
+ .label = "lp873x-gpio",
+ .owner = THIS_MODULE,
+ .request = lp873x_gpio_request,
+ .get_direction = lp873x_gpio_get_direction,
+ .direction_input = lp873x_gpio_direction_input,
+ .direction_output = lp873x_gpio_direction_output,
+ .get = lp873x_gpio_get,
+ .set = lp873x_gpio_set,
+ .set_single_ended = lp873x_gpio_set_single_ended,
+ .base = -1,
+ .ngpio = 2,
+ .can_sleep = true,
+};
+
+static int lp873x_gpio_probe(struct platform_device *pdev)
+{
+ struct lp873x_gpio *gpio;
+ int ret;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, gpio);
+
+ gpio->lp873 = dev_get_drvdata(pdev->dev.parent);
+ gpio->chip = template_chip;
+ gpio->chip.parent = gpio->lp873->dev;
+
+ ret = gpiochip_add_data(&gpio->chip, gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lp873x_gpio_remove(struct platform_device *pdev)
+{
+ struct lp873x_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->chip);
+
+ return 0;
+}
+
+static const struct platform_device_id lp873x_gpio_id_table[] = {
+ { "lp873x-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table);
+
+static struct platform_driver lp873x_gpio_driver = {
+ .driver = {
+ .name = "lp873x-gpio",
+ },
+ .probe = lp873x_gpio_probe,
+ .remove = lp873x_gpio_remove,
+ .id_table = lp873x_gpio_id_table,
+};
+module_platform_driver(lp873x_gpio_driver);
+
+MODULE_AUTHOR("Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("LP873X GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index f675132de10e..5c0d8189a304 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -20,6 +20,8 @@
*/
enum { REG_RE, REG_FE, REG_IE };
+enum { LSB, CSB, MSB };
+
#define CACHE_NR_REGS 3
/* No variant has more than 24 GPIOs */
#define CACHE_NR_BANKS (24 / 8)
@@ -39,7 +41,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
struct stmpe *stmpe = stmpe_gpio->stmpe;
- u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
+ u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB + (offset / 8)];
u8 mask = 1 << (offset % 8);
int ret;
@@ -55,7 +57,7 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
struct stmpe *stmpe = stmpe_gpio->stmpe;
int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
- u8 reg = stmpe->regs[which] - (offset / 8);
+ u8 reg = stmpe->regs[which + (offset / 8)];
u8 mask = 1 << (offset % 8);
/*
@@ -89,7 +91,7 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip,
{
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
struct stmpe *stmpe = stmpe_gpio->stmpe;
- u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)];
u8 mask = 1 << (offset % 8);
stmpe_gpio_set(chip, offset, val);
@@ -102,7 +104,7 @@ static int stmpe_gpio_direction_input(struct gpio_chip *chip,
{
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
struct stmpe *stmpe = stmpe_gpio->stmpe;
- u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)];
u8 mask = 1 << (offset % 8);
return stmpe_set_bits(stmpe, reg, mask, 0);
@@ -142,8 +144,9 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
return -EINVAL;
- /* STMPE801 doesn't have RE and FE registers */
- if (stmpe_gpio->stmpe->partnum == STMPE801)
+ /* STMPE801 and STMPE 1600 don't have RE and FE registers */
+ if (stmpe_gpio->stmpe->partnum == STMPE801 ||
+ stmpe_gpio->stmpe->partnum == STMPE1600)
return 0;
if (type & IRQ_TYPE_EDGE_RISING)
@@ -173,17 +176,24 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
struct stmpe *stmpe = stmpe_gpio->stmpe;
int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
- static const u8 regmap[] = {
- [REG_RE] = STMPE_IDX_GPRER_LSB,
- [REG_FE] = STMPE_IDX_GPFER_LSB,
- [REG_IE] = STMPE_IDX_IEGPIOR_LSB,
+ static const u8 regmap[CACHE_NR_REGS][CACHE_NR_BANKS] = {
+ [REG_RE][LSB] = STMPE_IDX_GPRER_LSB,
+ [REG_RE][CSB] = STMPE_IDX_GPRER_CSB,
+ [REG_RE][MSB] = STMPE_IDX_GPRER_MSB,
+ [REG_FE][LSB] = STMPE_IDX_GPFER_LSB,
+ [REG_FE][CSB] = STMPE_IDX_GPFER_CSB,
+ [REG_FE][MSB] = STMPE_IDX_GPFER_MSB,
+ [REG_IE][LSB] = STMPE_IDX_IEGPIOR_LSB,
+ [REG_IE][CSB] = STMPE_IDX_IEGPIOR_CSB,
+ [REG_IE][MSB] = STMPE_IDX_IEGPIOR_MSB,
};
int i, j;
for (i = 0; i < CACHE_NR_REGS; i++) {
- /* STMPE801 doesn't have RE and FE registers */
- if ((stmpe->partnum == STMPE801) &&
- (i != REG_IE))
+ /* STMPE801 and STMPE1600 don't have RE and FE registers */
+ if ((stmpe->partnum == STMPE801 ||
+ stmpe->partnum == STMPE1600) &&
+ (i != REG_IE))
continue;
for (j = 0; j < num_banks; j++) {
@@ -194,7 +204,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
continue;
stmpe_gpio->oldregs[i][j] = new;
- stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
+ stmpe_reg_write(stmpe, stmpe->regs[regmap[i][j]], new);
}
}
@@ -216,11 +226,21 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
stmpe_gpio->regs[REG_IE][regoffset] |= mask;
+
+ /*
+ * STMPE1600 workaround: to be able to get IRQ from pins,
+ * a read must be done on GPMR register, or a write in
+ * GPSR or GPCR registers
+ */
+ if (stmpe->partnum == STMPE1600)
+ stmpe_reg_read(stmpe,
+ stmpe->regs[STMPE_IDX_GPMR_LSB + regoffset]);
}
static void stmpe_dbg_show_one(struct seq_file *s,
@@ -230,9 +250,9 @@ static void stmpe_dbg_show_one(struct seq_file *s,
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
struct stmpe *stmpe = stmpe_gpio->stmpe;
const char *label = gpiochip_is_requested(gc, offset);
- int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
bool val = !!stmpe_gpio_get(gc, offset);
- u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 bank = offset / 8;
+ u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB + bank];
u8 mask = 1 << (offset % 8);
int ret;
u8 dir;
@@ -247,39 +267,72 @@ static void stmpe_dbg_show_one(struct seq_file *s,
gpio, label ?: "(none)",
val ? "hi" : "lo");
} else {
- u8 edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + num_banks - 1 - (offset / 8);
- u8 rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - (offset / 8);
- u8 fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - (offset / 8);
- u8 irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - (offset / 8);
- bool edge_det;
- bool rise;
- bool fall;
+ u8 edge_det_reg;
+ u8 rise_reg;
+ u8 fall_reg;
+ u8 irqen_reg;
+
+ char *edge_det_values[] = {"edge-inactive",
+ "edge-asserted",
+ "not-supported"};
+ char *rise_values[] = {"no-rising-edge-detection",
+ "rising-edge-detection",
+ "not-supported"};
+ char *fall_values[] = {"no-falling-edge-detection",
+ "falling-edge-detection",
+ "not-supported"};
+ #define NOT_SUPPORTED_IDX 2
+ u8 edge_det = NOT_SUPPORTED_IDX;
+ u8 rise = NOT_SUPPORTED_IDX;
+ u8 fall = NOT_SUPPORTED_IDX;
bool irqen;
- ret = stmpe_reg_read(stmpe, edge_det_reg);
- if (ret < 0)
- return;
- edge_det = !!(ret & mask);
- ret = stmpe_reg_read(stmpe, rise_reg);
- if (ret < 0)
+ switch (stmpe->partnum) {
+ case STMPE610:
+ case STMPE811:
+ case STMPE1601:
+ case STMPE2401:
+ case STMPE2403:
+ edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_LSB + bank];
+ ret = stmpe_reg_read(stmpe, edge_det_reg);
+ if (ret < 0)
+ return;
+ edge_det = !!(ret & mask);
+
+ case STMPE1801:
+ rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB + bank];
+ fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB + bank];
+
+ ret = stmpe_reg_read(stmpe, rise_reg);
+ if (ret < 0)
+ return;
+ rise = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, fall_reg);
+ if (ret < 0)
+ return;
+ fall = !!(ret & mask);
+
+ case STMPE801:
+ case STMPE1600:
+ irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB + bank];
+ break;
+
+ default:
return;
- rise = !!(ret & mask);
- ret = stmpe_reg_read(stmpe, fall_reg);
- if (ret < 0)
- return;
- fall = !!(ret & mask);
+ }
+
ret = stmpe_reg_read(stmpe, irqen_reg);
if (ret < 0)
return;
irqen = !!(ret & mask);
- seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s %s%s%s",
+ seq_printf(s, " gpio-%-3d (%-20.20s) in %s %13s %13s %25s %25s",
gpio, label ?: "(none)",
val ? "hi" : "lo",
- edge_det ? "edge-asserted" : "edge-inactive",
- irqen ? "IRQ-enabled" : "",
- rise ? " rising-edge-detection" : "",
- fall ? " falling-edge-detection" : "");
+ edge_det_values[edge_det],
+ irqen ? "IRQ-enabled" : "IRQ-disabled",
+ rise_values[rise],
+ fall_values[fall]);
}
}
@@ -307,18 +360,32 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
{
struct stmpe_gpio *stmpe_gpio = dev;
struct stmpe *stmpe = stmpe_gpio->stmpe;
- u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+ u8 statmsbreg;
int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
u8 status[num_banks];
int ret;
int i;
+ /*
+ * the stmpe_block_read() call below, imposes to set statmsbreg
+ * with the register located at the lowest address. As STMPE1600
+ * variant is the only one which respect registers address's order
+ * (LSB regs located at lowest address than MSB ones) whereas all
+ * the others have a registers layout with MSB located before the
+ * LSB regs.
+ */
+ if (stmpe->partnum == STMPE1600)
+ statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_LSB];
+ else
+ statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+
ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
if (ret < 0)
return IRQ_NONE;
for (i = 0; i < num_banks; i++) {
- int bank = num_banks - i - 1;
+ int bank = (stmpe_gpio->stmpe->partnum == STMPE1600) ? i :
+ num_banks - i - 1;
unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
unsigned int stat = status[i];
@@ -336,12 +403,18 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
stat &= ~(1 << bit);
}
- stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
-
- /* Edge detect register is not present on 801 */
- if (stmpe->partnum != STMPE801)
- stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB]
- + i, status[i]);
+ /*
+ * interrupt status register write has no effect on
+ * 801/1801/1600, bits are cleared when read.
+ * Edge detect register is not present on 801/1600/1801
+ */
+ if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1600 ||
+ stmpe->partnum != STMPE1801) {
+ stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
+ stmpe_reg_write(stmpe,
+ stmpe->regs[STMPE_IDX_GPEDR_LSB + i],
+ status[i]);
+ }
}
return IRQ_HANDLED;
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 4b0878f35471..25943e9bc8bf 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -27,6 +27,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/input/matrix_keypad.h>
@@ -44,6 +45,7 @@
* @dev: Device pointer
* @idev: Input device
* @ec: Top level ChromeOS device to use to talk to EC
+ * @notifier: interrupt event notifier for transport devices
*/
struct cros_ec_keyb {
unsigned int rows;
@@ -57,6 +59,7 @@ struct cros_ec_keyb {
struct device *dev;
struct input_dev *idev;
struct cros_ec_device *ec;
+ struct notifier_block notifier;
};
@@ -146,67 +149,44 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
input_sync(ckdev->idev);
}
-static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
-{
- int ret = 0;
- struct cros_ec_command *msg;
-
- msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- msg->version = 0;
- msg->command = EC_CMD_MKBP_STATE;
- msg->insize = ckdev->cols;
- msg->outsize = 0;
-
- ret = cros_ec_cmd_xfer(ckdev->ec, msg);
- if (ret < 0) {
- dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
- goto exit;
- }
-
- memcpy(kb_state, msg->data, ckdev->cols);
-exit:
- kfree(msg);
- return ret;
-}
-
-static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
+static int cros_ec_keyb_open(struct input_dev *dev)
{
- struct cros_ec_keyb *ckdev = data;
- struct cros_ec_device *ec = ckdev->ec;
- int ret;
- uint8_t kb_state[ckdev->cols];
-
- if (device_may_wakeup(ec->dev))
- pm_wakeup_event(ec->dev, 0);
-
- ret = cros_ec_keyb_get_state(ckdev, kb_state);
- if (ret >= 0)
- cros_ec_keyb_process(ckdev, kb_state, ret);
- else
- dev_err(ckdev->dev, "failed to get keyboard state: %d\n", ret);
+ struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
- return IRQ_HANDLED;
+ return blocking_notifier_chain_register(&ckdev->ec->event_notifier,
+ &ckdev->notifier);
}
-static int cros_ec_keyb_open(struct input_dev *dev)
+static void cros_ec_keyb_close(struct input_dev *dev)
{
struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
- struct cros_ec_device *ec = ckdev->ec;
- return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "cros_ec_keyb", ckdev);
+ blocking_notifier_chain_unregister(&ckdev->ec->event_notifier,
+ &ckdev->notifier);
}
-static void cros_ec_keyb_close(struct input_dev *dev)
+static int cros_ec_keyb_work(struct notifier_block *nb,
+ unsigned long queued_during_suspend, void *_notify)
{
- struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
- struct cros_ec_device *ec = ckdev->ec;
+ struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
+ notifier);
- free_irq(ec->irq, ckdev);
+ if (ckdev->ec->event_data.event_type != EC_MKBP_EVENT_KEY_MATRIX)
+ return NOTIFY_DONE;
+ /*
+ * If EC is not the wake source, discard key state changes during
+ * suspend.
+ */
+ if (queued_during_suspend)
+ return NOTIFY_OK;
+ if (ckdev->ec->event_size != ckdev->cols) {
+ dev_err(ckdev->dev,
+ "Discarded incomplete key matrix event.\n");
+ return NOTIFY_OK;
+ }
+ cros_ec_keyb_process(ckdev, ckdev->ec->event_data.data.key_matrix,
+ ckdev->ec->event_size);
+ return NOTIFY_OK;
}
/*
@@ -265,12 +245,8 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
if (!idev)
return -ENOMEM;
- if (!ec->irq) {
- dev_err(dev, "no EC IRQ specified\n");
- return -EINVAL;
- }
-
ckdev->ec = ec;
+ ckdev->notifier.notifier_call = cros_ec_keyb_work;
ckdev->dev = dev;
dev_set_drvdata(dev, ckdev);
@@ -311,54 +287,6 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-/* Clear any keys in the buffer */
-static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev)
-{
- uint8_t old_state[ckdev->cols];
- uint8_t new_state[ckdev->cols];
- unsigned long duration;
- int i, ret;
-
- /*
- * Keep reading until we see that the scan state does not change.
- * That indicates that we are done.
- *
- * Assume that the EC keyscan buffer is at most 32 deep.
- */
- duration = jiffies;
- ret = cros_ec_keyb_get_state(ckdev, new_state);
- for (i = 1; !ret && i < 32; i++) {
- memcpy(old_state, new_state, sizeof(old_state));
- ret = cros_ec_keyb_get_state(ckdev, new_state);
- if (0 == memcmp(old_state, new_state, sizeof(old_state)))
- break;
- }
- duration = jiffies - duration;
- dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i,
- jiffies_to_usecs(duration));
-}
-
-static int cros_ec_keyb_resume(struct device *dev)
-{
- struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
-
- /*
- * When the EC is not a wake source, then it could not have caused the
- * resume, so we clear the EC's key scan buffer. If the EC was a
- * wake source (e.g. the lid is open and the user might press a key to
- * wake) then the key scan buffer should be preserved.
- */
- if (!ckdev->ec->was_wake_device)
- cros_ec_keyb_clear_keyboard(ckdev);
-
- return 0;
-}
-
-#endif
-
-static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume);
-
#ifdef CONFIG_OF
static const struct of_device_id cros_ec_keyb_of_match[] = {
{ .compatible = "google,cros-ec-keyb" },
@@ -372,7 +300,6 @@ static struct platform_driver cros_ec_keyb_driver = {
.driver = {
.name = "cros-ec-keyb",
.of_match_table = of_match_ptr(cros_ec_keyb_of_match),
- .pm = &cros_ec_keyb_pm_ops,
},
};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2d1fb6420592..69b6fe2b982e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -112,6 +112,16 @@ config MFD_BCM590XX
help
Support for the BCM590xx PMUs from Broadcom
+config MFD_AC100
+ tristate "X-Powers AC100"
+ select MFD_CORE
+ depends on SUNXI_RSB
+ help
+ If you say Y here you get support for the X-Powers AC100 audio codec
+ IC.
+ This driver include only the core APIs. You have to select individual
+ components like codecs or RTC under the corresponding menus.
+
config MFD_AXP20X
tristate
select MFD_CORE
@@ -852,13 +862,13 @@ config MFD_RC5T583
different functionality of the device.
config MFD_RK808
- tristate "Rockchip RK808 Power Management chip"
+ tristate "Rockchip RK808/RK818 Power Management Chip"
depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
- If you say yes here you get support for the RK808
+ If you say yes here you get support for the RK808 and RK818
Power Management chips.
This driver provides common support for accessing the device
through I2C interface. The device supports multiple sub-devices
@@ -1224,6 +1234,20 @@ config MFD_TPS65217
This driver can also be built as a module. If so, the module
will be called tps65217.
+config MFD_TI_LP873X
+ tristate "TI LP873X Power Management IC"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here then you get support for the LP873X series of
+ Power Management Integrated Circuits (PMIC).
+ These include voltage regulators, thermal protection, configurable
+ General Purpose Outputs (GPO) that are used in portable devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called lp873x.
+
config MFD_TPS65218
tristate "TI TPS65218 Power Management chips"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2ba3ba35f745..5bf35c6a528b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -22,6 +22,8 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
+obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o
+
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
@@ -113,6 +115,8 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
+
+obj-$(CONFIG_MFD_AC100) += ac100.o
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c
new file mode 100644
index 000000000000..9bc69cd7807d
--- /dev/null
+++ b/drivers/mfd/ac100.c
@@ -0,0 +1,137 @@
+/*
+ * MFD core driver for X-Powers' AC100 Audio Codec IC
+ *
+ * The AC100 is a highly integrated audio codec and RTC subsystem designed
+ * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC,
+ * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has
+ * 3 clock outputs.
+ *
+ * The audio codec and RTC parts are completely separate, sharing only the
+ * host interface for access to its registers.
+ *
+ * Copyright (2016) Chen-Yu Tsai
+ *
+ * Author: Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ac100.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/sunxi-rsb.h>
+
+static const struct regmap_range ac100_writeable_ranges[] = {
+ regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL),
+ regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN),
+ regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN),
+ regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL),
+ regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL),
+ regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS),
+ regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN),
+ regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL),
+ regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT),
+ regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT),
+ regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA),
+ regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2),
+ regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2),
+ regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLKOUT_CTRL3),
+ regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD),
+ regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA),
+ regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)),
+};
+
+static const struct regmap_range ac100_volatile_ranges[] = {
+ regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2),
+ regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS),
+ regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA),
+ regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1),
+ regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1),
+ regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4),
+ regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST),
+ regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA),
+ regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD),
+};
+
+static const struct regmap_access_table ac100_writeable_table = {
+ .yes_ranges = ac100_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ac100_writeable_ranges),
+};
+
+static const struct regmap_access_table ac100_volatile_table = {
+ .yes_ranges = ac100_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ac100_volatile_ranges),
+};
+
+static const struct regmap_config ac100_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .wr_table = &ac100_writeable_table,
+ .volatile_table = &ac100_volatile_table,
+ .max_register = AC100_RTC_GP(15),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static struct mfd_cell ac100_cells[] = {
+ {
+ .name = "ac100-codec",
+ .of_compatible = "x-powers,ac100-codec",
+ }, {
+ .name = "ac100-rtc",
+ .of_compatible = "x-powers,ac100-rtc",
+ },
+};
+
+static int ac100_rsb_probe(struct sunxi_rsb_device *rdev)
+{
+ struct ac100_dev *ac100;
+ int ret;
+
+ ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL);
+ if (!ac100)
+ return -ENOMEM;
+
+ ac100->dev = &rdev->dev;
+ sunxi_rsb_device_set_drvdata(rdev, ac100);
+
+ ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config);
+ if (IS_ERR(ac100->regmap)) {
+ ret = PTR_ERR(ac100->regmap);
+ dev_err(ac100->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells,
+ ARRAY_SIZE(ac100_cells), NULL, 0, NULL);
+ if (ret) {
+ dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ac100_of_match[] = {
+ { .compatible = "x-powers,ac100" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ac100_of_match);
+
+static struct sunxi_rsb_driver ac100_rsb_driver = {
+ .driver = {
+ .name = "ac100",
+ .of_match_table = of_match_ptr(ac100_of_match),
+ },
+ .probe = ac100_rsb_probe,
+};
+module_sunxi_rsb_driver(ac100_rsb_driver);
+
+MODULE_DESCRIPTION("Audio codec MFD core driver for AC100");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index e4f97b3c824b..4fa0fae51006 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -10,6 +10,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
@@ -49,7 +50,15 @@ int arizona_clk32k_enable(struct arizona *arizona)
case ARIZONA_32KZ_MCLK1:
ret = pm_runtime_get_sync(arizona->dev);
if (ret != 0)
- goto out;
+ goto err_ref;
+ ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]);
+ if (ret != 0)
+ goto err_pm;
+ break;
+ case ARIZONA_32KZ_MCLK2:
+ ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]);
+ if (ret != 0)
+ goto err_ref;
break;
}
@@ -58,7 +67,9 @@ int arizona_clk32k_enable(struct arizona *arizona)
ARIZONA_CLK_32K_ENA);
}
-out:
+err_pm:
+ pm_runtime_put_sync(arizona->dev);
+err_ref:
if (ret != 0)
arizona->clk32k_ref--;
@@ -83,6 +94,10 @@ int arizona_clk32k_disable(struct arizona *arizona)
switch (arizona->pdata.clk32k_src) {
case ARIZONA_32KZ_MCLK1:
pm_runtime_put_sync(arizona->dev);
+ clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK1]);
+ break;
+ case ARIZONA_32KZ_MCLK2:
+ clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK2]);
break;
}
}
@@ -1000,6 +1015,7 @@ static const struct mfd_cell wm8998_devs[] = {
int arizona_dev_init(struct arizona *arizona)
{
+ const char * const mclk_name[] = { "mclk1", "mclk2" };
struct device *dev = arizona->dev;
const char *type_name = NULL;
unsigned int reg, val, mask;
@@ -1016,6 +1032,16 @@ int arizona_dev_init(struct arizona *arizona)
else
arizona_of_get_core_pdata(arizona);
+ BUILD_BUG_ON(ARRAY_SIZE(arizona->mclk) != ARRAY_SIZE(mclk_name));
+ for (i = 0; i < ARRAY_SIZE(arizona->mclk); i++) {
+ arizona->mclk[i] = devm_clk_get(arizona->dev, mclk_name[i]);
+ if (IS_ERR(arizona->mclk[i])) {
+ dev_info(arizona->dev, "Failed to get %s: %ld\n",
+ mclk_name[i], PTR_ERR(arizona->mclk[i]));
+ arizona->mclk[i] = NULL;
+ }
+ }
+
regcache_cache_only(arizona->regmap, true);
switch (arizona->type) {
diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c
index a407527bcd09..a732cb50bcff 100644
--- a/drivers/mfd/axp20x-rsb.c
+++ b/drivers/mfd/axp20x-rsb.c
@@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
static const struct of_device_id axp20x_rsb_of_match[] = {
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
+ { .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ .compatible = "x-powers,axp809", .data = (void *)AXP809_ID },
{ },
};
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index fd80b0981f0f..96102753847f 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -38,6 +38,7 @@ static const char * const axp20x_model_names[] = {
"AXP221",
"AXP223",
"AXP288",
+ "AXP806",
"AXP809",
};
@@ -129,6 +130,27 @@ static const struct regmap_access_table axp288_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges),
};
+static const struct regmap_range axp806_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_DATACACHE(3)),
+ regmap_reg_range(AXP806_PWR_OUT_CTRL1, AXP806_CLDO3_V_CTRL),
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ2_EN),
+ regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
+};
+
+static const struct regmap_range axp806_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
+};
+
+static const struct regmap_access_table axp806_writeable_table = {
+ .yes_ranges = axp806_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp806_writeable_ranges),
+};
+
+static const struct regmap_access_table axp806_volatile_table = {
+ .yes_ranges = axp806_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges),
+};
+
static struct resource axp152_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
@@ -278,6 +300,15 @@ static const struct regmap_config axp288_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct regmap_config axp806_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp806_writeable_table,
+ .volatile_table = &axp806_volatile_table,
+ .max_register = AXP806_VREF_TEMP_WARN_L,
+ .cache_type = REGCACHE_RBTREE,
+};
+
#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
[_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
@@ -409,6 +440,21 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1),
};
+static const struct regmap_irq axp806_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0),
+ INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1),
+ INIT_REGMAP_IRQ(AXP806, DCDCA_V_LOW, 0, 3),
+ INIT_REGMAP_IRQ(AXP806, DCDCB_V_LOW, 0, 4),
+ INIT_REGMAP_IRQ(AXP806, DCDCC_V_LOW, 0, 5),
+ INIT_REGMAP_IRQ(AXP806, DCDCD_V_LOW, 0, 6),
+ INIT_REGMAP_IRQ(AXP806, DCDCE_V_LOW, 0, 7),
+ INIT_REGMAP_IRQ(AXP806, PWROK_LONG, 1, 0),
+ INIT_REGMAP_IRQ(AXP806, PWROK_SHORT, 1, 1),
+ INIT_REGMAP_IRQ(AXP806, WAKEUP, 1, 4),
+ INIT_REGMAP_IRQ(AXP806, PWROK_FALL, 1, 5),
+ INIT_REGMAP_IRQ(AXP806, PWROK_RISE, 1, 6),
+};
+
static const struct regmap_irq axp809_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP809, ACIN_OVER_V, 0, 7),
INIT_REGMAP_IRQ(AXP809, ACIN_PLUGIN, 0, 6),
@@ -494,6 +540,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = {
};
+static const struct regmap_irq_chip axp806_regmap_irq_chip = {
+ .name = "axp806",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .irqs = axp806_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp806_regmap_irqs),
+ .num_regs = 2,
+};
+
static const struct regmap_irq_chip axp809_regmap_irq_chip = {
.name = "axp809",
.status_base = AXP20X_IRQ1_STATE,
@@ -660,12 +718,20 @@ static struct mfd_cell axp288_cells[] = {
},
};
+static struct mfd_cell axp806_cells[] = {
+ {
+ .id = 2,
+ .name = "axp20x-regulator",
+ },
+};
+
static struct mfd_cell axp809_cells[] = {
{
.name = "axp20x-pek",
.num_resources = ARRAY_SIZE(axp809_pek_resources),
.resources = axp809_pek_resources,
}, {
+ .id = 1,
.name = "axp20x-regulator",
},
};
@@ -732,6 +798,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_cfg = &axp288_regmap_config;
axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
break;
+ case AXP806_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
+ axp20x->cells = axp806_cells;
+ axp20x->regmap_cfg = &axp806_regmap_config;
+ axp20x->regmap_irq_chip = &axp806_regmap_irq_chip;
+ break;
case AXP809_ID:
axp20x->nr_cells = ARRAY_SIZE(axp809_cells);
axp20x->cells = axp809_cells;
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index 0eee63542038..abd83424b498 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>
+#include <asm/unaligned.h>
#define CROS_EC_DEV_EC_INDEX 0
#define CROS_EC_DEV_PD_INDEX 1
@@ -49,11 +50,28 @@ static const struct mfd_cell ec_pd_cell = {
.pdata_size = sizeof(pd_p),
};
+static irqreturn_t ec_irq_thread(int irq, void *data)
+{
+ struct cros_ec_device *ec_dev = data;
+ int ret;
+
+ if (device_may_wakeup(ec_dev->dev))
+ pm_wakeup_event(ec_dev->dev, 0);
+
+ ret = cros_ec_get_next_event(ec_dev);
+ if (ret > 0)
+ blocking_notifier_call_chain(&ec_dev->event_notifier,
+ 0, ec_dev);
+ return IRQ_HANDLED;
+}
+
int cros_ec_register(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
int err = 0;
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
+
ec_dev->max_request = sizeof(struct ec_params_hello);
ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
ec_dev->max_passthru = 0;
@@ -70,13 +88,24 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
cros_ec_query_all(ec_dev);
+ if (ec_dev->irq) {
+ err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "chromeos-ec", ec_dev);
+ if (err) {
+ dev_err(dev, "Failed to request IRQ %d: %d",
+ ec_dev->irq, err);
+ return err;
+ }
+ }
+
err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev,
"Failed to register Embedded Controller subdevice %d\n",
err);
- return err;
+ goto fail_mfd;
}
if (ec_dev->max_passthru) {
@@ -94,7 +123,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
dev_err(dev,
"Failed to register Power Delivery subdevice %d\n",
err);
- return err;
+ goto fail_mfd;
}
}
@@ -103,13 +132,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
if (err) {
mfd_remove_devices(dev);
dev_err(dev, "Failed to register sub-devices\n");
- return err;
+ goto fail_mfd;
}
}
dev_info(dev, "Chrome EC device registered\n");
return 0;
+
+fail_mfd:
+ if (ec_dev->irq)
+ free_irq(ec_dev->irq, ec_dev);
+ return err;
}
EXPORT_SYMBOL(cros_ec_register);
@@ -136,13 +170,31 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev)
}
EXPORT_SYMBOL(cros_ec_suspend);
+static void cros_ec_drain_events(struct cros_ec_device *ec_dev)
+{
+ while (cros_ec_get_next_event(ec_dev) > 0)
+ blocking_notifier_call_chain(&ec_dev->event_notifier,
+ 1, ec_dev);
+}
+
int cros_ec_resume(struct cros_ec_device *ec_dev)
{
enable_irq(ec_dev->irq);
+ /*
+ * In some cases, we need to distinguish between events that occur
+ * during suspend if the EC is not a wake source. For example,
+ * keypresses during suspend should be discarded if it does not wake
+ * the system.
+ *
+ * If the EC is not a wake source, drain the event queue and mark them
+ * as "queued during suspend".
+ */
if (ec_dev->wake_enabled) {
disable_irq_wake(ec_dev->irq);
ec_dev->wake_enabled = 0;
+ } else {
+ cros_ec_drain_events(ec_dev);
}
return 0;
diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c
new file mode 100644
index 000000000000..9af064c940ee
--- /dev/null
+++ b/drivers/mfd/lp873x.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Author: Keerthy <j-keerthy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lp873x.h>
+
+static const struct regmap_config lp873x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LP873X_REG_MAX,
+};
+
+static const struct mfd_cell lp873x_cells[] = {
+ { .name = "lp873x-regulator", },
+ { .name = "lp873x-gpio", },
+};
+
+static int lp873x_probe(struct i2c_client *client,
+ const struct i2c_device_id *ids)
+{
+ struct lp873x *lp873;
+ int ret;
+ unsigned int otpid;
+
+ lp873 = devm_kzalloc(&client->dev, sizeof(*lp873), GFP_KERNEL);
+ if (!lp873)
+ return -ENOMEM;
+
+ lp873->dev = &client->dev;
+
+ lp873->regmap = devm_regmap_init_i2c(client, &lp873x_regmap_config);
+ if (IS_ERR(lp873->regmap)) {
+ ret = PTR_ERR(lp873->regmap);
+ dev_err(lp873->dev,
+ "Failed to initialize register map: %d\n", ret);
+ return ret;
+ }
+
+ mutex_init(&lp873->lock);
+
+ ret = regmap_read(lp873->regmap, LP873X_REG_OTP_REV, &otpid);
+ if (ret) {
+ dev_err(lp873->dev, "Failed to read OTP ID\n");
+ return ret;
+ }
+
+ lp873->rev = otpid & LP873X_OTP_REV_OTP_ID;
+
+ i2c_set_clientdata(client, lp873);
+
+ ret = mfd_add_devices(lp873->dev, PLATFORM_DEVID_AUTO, lp873x_cells,
+ ARRAY_SIZE(lp873x_cells), NULL, 0, NULL);
+
+ return ret;
+}
+
+static const struct of_device_id of_lp873x_match_table[] = {
+ { .compatible = "ti,lp8733", },
+ { .compatible = "ti,lp8732", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_lp873x_match_table);
+
+static const struct i2c_device_id lp873x_id_table[] = {
+ { "lp873x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, lp873x_id_table);
+
+static struct i2c_driver lp873x_driver = {
+ .driver = {
+ .name = "lp873x",
+ .of_match_table = of_lp873x_match_table,
+ },
+ .probe = lp873x_probe,
+ .id_table = lp873x_id_table,
+};
+module_i2c_driver(lp873x_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("LP873X chip family Multi-Function Device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index 49d7f624fc94..0f8acc5882a4 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -1,11 +1,15 @@
/*
- * MFD core driver for Rockchip RK808
+ * MFD core driver for Rockchip RK808/RK818
*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Chris Zhong <zyw@rock-chips.com>
* Author: Zhang Qing <zhangqing@rock-chips.com>
*
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -21,6 +25,7 @@
#include <linux/mfd/rk808.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/regmap.h>
struct rk808_reg_data {
@@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static const struct regmap_config rk818_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RK818_USB_CTRL_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = rk808_is_volatile_reg,
+};
+
static const struct regmap_config rk808_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = {
{
.name = "rk808-rtc",
.num_resources = ARRAY_SIZE(rtc_resources),
- .resources = &rtc_resources[0],
+ .resources = rtc_resources,
},
};
-static const struct rk808_reg_data pre_init_reg[] = {
+static const struct mfd_cell rk818s[] = {
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
+ {
+ .name = "rk808-rtc",
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resources = rtc_resources,
+ },
+};
+
+static const struct rk808_reg_data rk808_pre_init_reg[] = {
{ RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA },
{ RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA },
{ RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
@@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = {
VB_LO_SEL_3500MV },
};
+static const struct rk808_reg_data rk818_pre_init_reg[] = {
+ /* improve efficiency */
+ { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA },
+ { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA },
+ { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
+ { RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK,
+ RK818_USB_ILMIN_2000MA },
+ /* close charger when usb lower then 3.4V */
+ { RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK,
+ (0x7 << 4) },
+ /* no action when vref */
+ { RK818_H5V_EN_REG, BIT(1), RK818_REF_RDY_CTRL },
+ /* enable HDMI 5V */
+ { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN },
+ { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT |
+ VB_LO_SEL_3500MV },
+};
+
static const struct regmap_irq rk808_irqs[] = {
/* INT_STS */
[RK808_IRQ_VOUT_LO] = {
@@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = {
},
};
+static const struct regmap_irq rk818_irqs[] = {
+ /* INT_STS */
+ [RK818_IRQ_VOUT_LO] = {
+ .mask = RK818_IRQ_VOUT_LO_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_VB_LO] = {
+ .mask = RK818_IRQ_VB_LO_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_PWRON] = {
+ .mask = RK818_IRQ_PWRON_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_PWRON_LP] = {
+ .mask = RK818_IRQ_PWRON_LP_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_HOTDIE] = {
+ .mask = RK818_IRQ_HOTDIE_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_RTC_ALARM] = {
+ .mask = RK818_IRQ_RTC_ALARM_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_RTC_PERIOD] = {
+ .mask = RK818_IRQ_RTC_PERIOD_MSK,
+ .reg_offset = 0,
+ },
+ [RK818_IRQ_USB_OV] = {
+ .mask = RK818_IRQ_USB_OV_MSK,
+ .reg_offset = 0,
+ },
+
+ /* INT_STS2 */
+ [RK818_IRQ_PLUG_IN] = {
+ .mask = RK818_IRQ_PLUG_IN_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_PLUG_OUT] = {
+ .mask = RK818_IRQ_PLUG_OUT_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_CHG_OK] = {
+ .mask = RK818_IRQ_CHG_OK_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_CHG_TE] = {
+ .mask = RK818_IRQ_CHG_TE_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_CHG_TS1] = {
+ .mask = RK818_IRQ_CHG_TS1_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_TS2] = {
+ .mask = RK818_IRQ_TS2_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_CHG_CVTLIM] = {
+ .mask = RK818_IRQ_CHG_CVTLIM_MSK,
+ .reg_offset = 1,
+ },
+ [RK818_IRQ_DISCHG_ILIM] = {
+ .mask = RK818_IRQ_DISCHG_ILIM_MSK,
+ .reg_offset = 1,
+ },
+};
+
static struct regmap_irq_chip rk808_irq_chip = {
.name = "rk808",
.irqs = rk808_irqs,
@@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
.init_ack_masked = true,
};
+static struct regmap_irq_chip rk818_irq_chip = {
+ .name = "rk818",
+ .irqs = rk818_irqs,
+ .num_irqs = ARRAY_SIZE(rk818_irqs),
+ .num_regs = 2,
+ .irq_reg_stride = 2,
+ .status_base = RK818_INT_STS_REG1,
+ .mask_base = RK818_INT_STS_MSK_REG1,
+ .ack_base = RK818_INT_STS_REG1,
+ .init_ack_masked = true,
+};
+
static struct i2c_client *rk808_i2c_client;
static void rk808_device_shutdown(void)
{
@@ -167,55 +290,100 @@ static void rk808_device_shutdown(void)
dev_err(&rk808_i2c_client->dev, "power off error!\n");
}
+static const struct of_device_id rk808_of_match[] = {
+ { .compatible = "rockchip,rk808" },
+ { .compatible = "rockchip,rk818" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rk808_of_match);
+
static int rk808_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device_node *np = client->dev.of_node;
struct rk808 *rk808;
+ const struct rk808_reg_data *pre_init_reg;
+ const struct mfd_cell *cells;
+ int nr_pre_init_regs;
+ int nr_cells;
int pm_off = 0;
int ret;
int i;
- if (!client->irq) {
- dev_err(&client->dev, "No interrupt support, no core IRQ\n");
- return -EINVAL;
- }
-
rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
if (!rk808)
return -ENOMEM;
- rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config);
+ rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB);
+ if (rk808->variant < 0) {
+ dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n",
+ RK808_ID_MSB);
+ return rk808->variant;
+ }
+
+ dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant);
+
+ switch (rk808->variant) {
+ case RK808_ID:
+ rk808->regmap_cfg = &rk808_regmap_config;
+ rk808->regmap_irq_chip = &rk808_irq_chip;
+ pre_init_reg = rk808_pre_init_reg;
+ nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
+ cells = rk808s;
+ nr_cells = ARRAY_SIZE(rk808s);
+ break;
+ case RK818_ID:
+ rk808->regmap_cfg = &rk818_regmap_config;
+ rk808->regmap_irq_chip = &rk818_irq_chip;
+ pre_init_reg = rk818_pre_init_reg;
+ nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
+ cells = rk818s;
+ nr_cells = ARRAY_SIZE(rk818s);
+ break;
+ default:
+ dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
+ rk808->variant);
+ return -EINVAL;
+ }
+
+ rk808->i2c = client;
+ i2c_set_clientdata(client, rk808);
+
+ rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg);
if (IS_ERR(rk808->regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
return PTR_ERR(rk808->regmap);
}
- for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) {
- ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr,
- pre_init_reg[i].mask,
- pre_init_reg[i].value);
- if (ret) {
- dev_err(&client->dev,
- "0x%x write err\n", pre_init_reg[i].addr);
- return ret;
- }
+ if (!client->irq) {
+ dev_err(&client->dev, "No interrupt support, no core IRQ\n");
+ return -EINVAL;
}
ret = regmap_add_irq_chip(rk808->regmap, client->irq,
IRQF_ONESHOT, -1,
- &rk808_irq_chip, &rk808->irq_data);
+ rk808->regmap_irq_chip, &rk808->irq_data);
if (ret) {
dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
return ret;
}
- rk808->i2c = client;
- i2c_set_clientdata(client, rk808);
+ for (i = 0; i < nr_pre_init_regs; i++) {
+ ret = regmap_update_bits(rk808->regmap,
+ pre_init_reg[i].addr,
+ pre_init_reg[i].mask,
+ pre_init_reg[i].value);
+ if (ret) {
+ dev_err(&client->dev,
+ "0x%x write err\n",
+ pre_init_reg[i].addr);
+ return ret;
+ }
+ }
- ret = devm_mfd_add_devices(&client->dev, -1,
- rk808s, ARRAY_SIZE(rk808s), NULL, 0,
- regmap_irq_get_domain(rk808->irq_data));
+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+ cells, nr_cells, NULL, 0,
+ regmap_irq_get_domain(rk808->irq_data));
if (ret) {
dev_err(&client->dev, "failed to add MFD devices %d\n", ret);
goto err_irq;
@@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client)
return 0;
}
-static const struct of_device_id rk808_of_match[] = {
- { .compatible = "rockchip,rk808" },
- { },
-};
-MODULE_DEVICE_TABLE(of, rk808_of_match);
-
static const struct i2c_device_id rk808_ids[] = {
{ "rk808" },
+ { "rk818" },
{ },
};
MODULE_DEVICE_TABLE(i2c, rk808_ids);
@@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
-MODULE_DESCRIPTION("RK808 PMIC driver");
+MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
+MODULE_DESCRIPTION("RK808/RK818 PMIC driver");
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
index c3f4aab53b07..863c39a3353c 100644
--- a/drivers/mfd/stmpe-i2c.c
+++ b/drivers/mfd/stmpe-i2c.c
@@ -57,6 +57,7 @@ static const struct of_device_id stmpe_of_match[] = {
{ .compatible = "st,stmpe610", .data = (void *)STMPE610, },
{ .compatible = "st,stmpe801", .data = (void *)STMPE801, },
{ .compatible = "st,stmpe811", .data = (void *)STMPE811, },
+ { .compatible = "st,stmpe1600", .data = (void *)STMPE1600, },
{ .compatible = "st,stmpe1601", .data = (void *)STMPE1601, },
{ .compatible = "st,stmpe1801", .data = (void *)STMPE1801, },
{ .compatible = "st,stmpe2401", .data = (void *)STMPE2401, },
@@ -101,6 +102,7 @@ static const struct i2c_device_id stmpe_i2c_id[] = {
{ "stmpe610", STMPE610 },
{ "stmpe801", STMPE801 },
{ "stmpe811", STMPE811 },
+ { "stmpe1600", STMPE1600 },
{ "stmpe1601", STMPE1601 },
{ "stmpe1801", STMPE1801 },
{ "stmpe2401", STMPE2401 },
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 94c7cc02fdab..cfdae8a3d779 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -469,6 +469,8 @@ static const struct mfd_cell stmpe_ts_cell = {
static const u8 stmpe811_regs[] = {
[STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID,
+ [STMPE_IDX_SYS_CTRL] = STMPE811_REG_SYS_CTRL,
+ [STMPE_IDX_SYS_CTRL2] = STMPE811_REG_SYS_CTRL2,
[STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL,
[STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN,
[STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA,
@@ -481,7 +483,7 @@ static const u8 stmpe811_regs[] = {
[STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF,
[STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN,
[STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA,
- [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED,
+ [STMPE_IDX_GPEDR_LSB] = STMPE811_REG_GPIO_ED,
};
static struct stmpe_variant_block stmpe811_blocks[] = {
@@ -511,7 +513,7 @@ static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
if (blocks & STMPE_BLOCK_TOUCHSCREEN)
mask |= STMPE811_SYS_CTRL2_TSC_OFF;
- return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
+ return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2], mask,
enable ? 0 : mask);
}
@@ -551,25 +553,89 @@ static struct stmpe_variant_info stmpe610 = {
};
/*
+ * STMPE1600
+ * Compared to all others STMPE variant, LSB and MSB regs are located in this
+ * order : LSB addr
+ * MSB addr + 1
+ * As there is only 2 * 8bits registers for GPMR/GPSR/IEGPIOPR, CSB index is MSB registers
+ */
+
+static const u8 stmpe1600_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE1600_REG_CHIP_ID,
+ [STMPE_IDX_SYS_CTRL] = STMPE1600_REG_SYS_CTRL,
+ [STMPE_IDX_ICR_LSB] = STMPE1600_REG_SYS_CTRL,
+ [STMPE_IDX_GPMR_LSB] = STMPE1600_REG_GPMR_LSB,
+ [STMPE_IDX_GPMR_CSB] = STMPE1600_REG_GPMR_MSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE1600_REG_GPSR_LSB,
+ [STMPE_IDX_GPSR_CSB] = STMPE1600_REG_GPSR_MSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE1600_REG_GPDR_LSB,
+ [STMPE_IDX_GPDR_CSB] = STMPE1600_REG_GPDR_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE1600_REG_IEGPIOR_LSB,
+ [STMPE_IDX_IEGPIOR_CSB] = STMPE1600_REG_IEGPIOR_MSB,
+ [STMPE_IDX_ISGPIOR_LSB] = STMPE1600_REG_ISGPIOR_LSB,
+};
+
+static struct stmpe_variant_block stmpe1600_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = 0,
+ .block = STMPE_BLOCK_GPIO,
+ },
+};
+
+static int stmpe1600_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ if (blocks & STMPE_BLOCK_GPIO)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static struct stmpe_variant_info stmpe1600 = {
+ .name = "stmpe1600",
+ .id_val = STMPE1600_ID,
+ .id_mask = 0xffff,
+ .num_gpios = 16,
+ .af_bits = 0,
+ .regs = stmpe1600_regs,
+ .blocks = stmpe1600_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe1600_blocks),
+ .num_irqs = STMPE1600_NR_INTERNAL_IRQS,
+ .enable = stmpe1600_enable,
+};
+
+/*
* STMPE1601
*/
static const u8 stmpe1601_regs[] = {
[STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID,
+ [STMPE_IDX_SYS_CTRL] = STMPE1601_REG_SYS_CTRL,
+ [STMPE_IDX_SYS_CTRL2] = STMPE1601_REG_SYS_CTRL2,
[STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB,
+ [STMPE_IDX_IER_MSB] = STMPE1601_REG_IER_MSB,
[STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB,
[STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB,
[STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB,
+ [STMPE_IDX_GPMR_CSB] = STMPE1601_REG_GPIO_MP_MSB,
[STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB,
+ [STMPE_IDX_GPSR_CSB] = STMPE1601_REG_GPIO_SET_MSB,
[STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB,
+ [STMPE_IDX_GPCR_CSB] = STMPE1601_REG_GPIO_CLR_MSB,
[STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB,
+ [STMPE_IDX_GPDR_CSB] = STMPE1601_REG_GPIO_SET_DIR_MSB,
+ [STMPE_IDX_GPEDR_LSB] = STMPE1601_REG_GPIO_ED_LSB,
+ [STMPE_IDX_GPEDR_CSB] = STMPE1601_REG_GPIO_ED_MSB,
[STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB,
+ [STMPE_IDX_GPRER_CSB] = STMPE1601_REG_GPIO_RE_MSB,
[STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB,
+ [STMPE_IDX_GPFER_CSB] = STMPE1601_REG_GPIO_FE_MSB,
[STMPE_IDX_GPPUR_LSB] = STMPE1601_REG_GPIO_PU_LSB,
[STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB,
[STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
+ [STMPE_IDX_IEGPIOR_CSB] = STMPE1601_REG_INT_EN_GPIO_MASK_MSB,
[STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB,
- [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB,
};
static struct stmpe_variant_block stmpe1601_blocks[] = {
@@ -640,13 +706,13 @@ static int stmpe1601_autosleep(struct stmpe *stmpe,
return timeout;
}
- ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ ret = __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2],
STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
timeout);
if (ret < 0)
return ret;
- return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2],
STPME1601_AUTOSLEEP_ENABLE,
STPME1601_AUTOSLEEP_ENABLE);
}
@@ -671,7 +737,7 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
else
mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM;
- return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
+ return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], mask,
enable ? mask : 0);
}
@@ -710,18 +776,33 @@ static struct stmpe_variant_info stmpe1601 = {
*/
static const u8 stmpe1801_regs[] = {
[STMPE_IDX_CHIP_ID] = STMPE1801_REG_CHIP_ID,
+ [STMPE_IDX_SYS_CTRL] = STMPE1801_REG_SYS_CTRL,
[STMPE_IDX_ICR_LSB] = STMPE1801_REG_INT_CTRL_LOW,
[STMPE_IDX_IER_LSB] = STMPE1801_REG_INT_EN_MASK_LOW,
[STMPE_IDX_ISR_LSB] = STMPE1801_REG_INT_STA_LOW,
[STMPE_IDX_GPMR_LSB] = STMPE1801_REG_GPIO_MP_LOW,
+ [STMPE_IDX_GPMR_CSB] = STMPE1801_REG_GPIO_MP_MID,
+ [STMPE_IDX_GPMR_MSB] = STMPE1801_REG_GPIO_MP_HIGH,
[STMPE_IDX_GPSR_LSB] = STMPE1801_REG_GPIO_SET_LOW,
+ [STMPE_IDX_GPSR_CSB] = STMPE1801_REG_GPIO_SET_MID,
+ [STMPE_IDX_GPSR_MSB] = STMPE1801_REG_GPIO_SET_HIGH,
[STMPE_IDX_GPCR_LSB] = STMPE1801_REG_GPIO_CLR_LOW,
+ [STMPE_IDX_GPCR_CSB] = STMPE1801_REG_GPIO_CLR_MID,
+ [STMPE_IDX_GPCR_MSB] = STMPE1801_REG_GPIO_CLR_HIGH,
[STMPE_IDX_GPDR_LSB] = STMPE1801_REG_GPIO_SET_DIR_LOW,
+ [STMPE_IDX_GPDR_CSB] = STMPE1801_REG_GPIO_SET_DIR_MID,
+ [STMPE_IDX_GPDR_MSB] = STMPE1801_REG_GPIO_SET_DIR_HIGH,
[STMPE_IDX_GPRER_LSB] = STMPE1801_REG_GPIO_RE_LOW,
+ [STMPE_IDX_GPRER_CSB] = STMPE1801_REG_GPIO_RE_MID,
+ [STMPE_IDX_GPRER_MSB] = STMPE1801_REG_GPIO_RE_HIGH,
[STMPE_IDX_GPFER_LSB] = STMPE1801_REG_GPIO_FE_LOW,
+ [STMPE_IDX_GPFER_CSB] = STMPE1801_REG_GPIO_FE_MID,
+ [STMPE_IDX_GPFER_MSB] = STMPE1801_REG_GPIO_FE_HIGH,
[STMPE_IDX_GPPUR_LSB] = STMPE1801_REG_GPIO_PULL_UP_LOW,
[STMPE_IDX_IEGPIOR_LSB] = STMPE1801_REG_INT_EN_GPIO_MASK_LOW,
- [STMPE_IDX_ISGPIOR_LSB] = STMPE1801_REG_INT_STA_GPIO_LOW,
+ [STMPE_IDX_IEGPIOR_CSB] = STMPE1801_REG_INT_EN_GPIO_MASK_MID,
+ [STMPE_IDX_IEGPIOR_MSB] = STMPE1801_REG_INT_EN_GPIO_MASK_HIGH,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE1801_REG_INT_STA_GPIO_HIGH,
};
static struct stmpe_variant_block stmpe1801_blocks[] = {
@@ -751,22 +832,31 @@ static int stmpe1801_enable(struct stmpe *stmpe, unsigned int blocks,
enable ? mask : 0);
}
-static int stmpe1801_reset(struct stmpe *stmpe)
+static int stmpe_reset(struct stmpe *stmpe)
{
+ u16 id_val = stmpe->variant->id_val;
unsigned long timeout;
int ret = 0;
+ u8 reset_bit;
+
+ if (id_val == STMPE811_ID)
+ /* STMPE801 and STMPE610 use bit 1 of SYS_CTRL register */
+ reset_bit = STMPE811_SYS_CTRL_RESET;
+ else
+ /* all other STMPE variant use bit 7 of SYS_CTRL register */
+ reset_bit = STMPE_SYS_CTRL_RESET;
- ret = __stmpe_set_bits(stmpe, STMPE1801_REG_SYS_CTRL,
- STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET);
+ ret = __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL],
+ reset_bit, reset_bit);
if (ret < 0)
return ret;
timeout = jiffies + msecs_to_jiffies(100);
while (time_before(jiffies, timeout)) {
- ret = __stmpe_reg_read(stmpe, STMPE1801_REG_SYS_CTRL);
+ ret = __stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL]);
if (ret < 0)
return ret;
- if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET))
+ if (!(ret & reset_bit))
return 0;
usleep_range(100, 200);
}
@@ -794,20 +884,39 @@ static struct stmpe_variant_info stmpe1801 = {
static const u8 stmpe24xx_regs[] = {
[STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID,
+ [STMPE_IDX_SYS_CTRL] = STMPE24XX_REG_SYS_CTRL,
+ [STMPE_IDX_SYS_CTRL2] = STMPE24XX_REG_SYS_CTRL2,
[STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB,
+ [STMPE_IDX_IER_MSB] = STMPE24XX_REG_IER_MSB,
[STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB,
[STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB,
[STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB,
+ [STMPE_IDX_GPMR_CSB] = STMPE24XX_REG_GPMR_CSB,
+ [STMPE_IDX_GPMR_MSB] = STMPE24XX_REG_GPMR_MSB,
[STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB,
+ [STMPE_IDX_GPSR_CSB] = STMPE24XX_REG_GPSR_CSB,
+ [STMPE_IDX_GPSR_MSB] = STMPE24XX_REG_GPSR_MSB,
[STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB,
+ [STMPE_IDX_GPCR_CSB] = STMPE24XX_REG_GPCR_CSB,
+ [STMPE_IDX_GPCR_MSB] = STMPE24XX_REG_GPCR_MSB,
[STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB,
+ [STMPE_IDX_GPDR_CSB] = STMPE24XX_REG_GPDR_CSB,
+ [STMPE_IDX_GPDR_MSB] = STMPE24XX_REG_GPDR_MSB,
[STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB,
+ [STMPE_IDX_GPRER_CSB] = STMPE24XX_REG_GPRER_CSB,
+ [STMPE_IDX_GPRER_MSB] = STMPE24XX_REG_GPRER_MSB,
[STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB,
+ [STMPE_IDX_GPFER_CSB] = STMPE24XX_REG_GPFER_CSB,
+ [STMPE_IDX_GPFER_MSB] = STMPE24XX_REG_GPFER_MSB,
[STMPE_IDX_GPPUR_LSB] = STMPE24XX_REG_GPPUR_LSB,
[STMPE_IDX_GPPDR_LSB] = STMPE24XX_REG_GPPDR_LSB,
[STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB,
[STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB,
+ [STMPE_IDX_IEGPIOR_CSB] = STMPE24XX_REG_IEGPIOR_CSB,
+ [STMPE_IDX_IEGPIOR_MSB] = STMPE24XX_REG_IEGPIOR_MSB,
[STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB,
+ [STMPE_IDX_GPEDR_LSB] = STMPE24XX_REG_GPEDR_LSB,
+ [STMPE_IDX_GPEDR_CSB] = STMPE24XX_REG_GPEDR_CSB,
[STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB,
};
@@ -840,7 +949,7 @@ static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
if (blocks & STMPE_BLOCK_KEYPAD)
mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
- return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
+ return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], mask,
enable ? mask : 0);
}
@@ -893,6 +1002,7 @@ static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {
[STMPE610] = &stmpe610,
[STMPE801] = &stmpe801,
[STMPE811] = &stmpe811,
+ [STMPE1600] = &stmpe1600,
[STMPE1601] = &stmpe1601,
[STMPE1801] = &stmpe1801,
[STMPE2401] = &stmpe2401,
@@ -919,7 +1029,8 @@ static irqreturn_t stmpe_irq(int irq, void *data)
int ret;
int i;
- if (variant->id_val == STMPE801_ID) {
+ if (variant->id_val == STMPE801_ID ||
+ variant->id_val == STMPE1600_ID) {
int base = irq_create_mapping(stmpe->domain, 0);
handle_nested_irq(base);
@@ -982,7 +1093,7 @@ static void stmpe_irq_sync_unlock(struct irq_data *data)
continue;
stmpe->oldier[i] = new;
- stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB + i], new);
}
mutex_unlock(&stmpe->irq_lock);
@@ -1088,20 +1199,18 @@ static int stmpe_chip_init(struct stmpe *stmpe)
if (ret)
return ret;
- if (id == STMPE1801_ID) {
- ret = stmpe1801_reset(stmpe);
- if (ret < 0)
- return ret;
- }
+ ret = stmpe_reset(stmpe);
+ if (ret < 0)
+ return ret;
if (stmpe->irq >= 0) {
- if (id == STMPE801_ID)
- icr = STMPE801_REG_SYS_CTRL_INT_EN;
+ if (id == STMPE801_ID || id == STMPE1600_ID)
+ icr = STMPE_SYS_CTRL_INT_EN;
else
icr = STMPE_ICR_LSB_GIM;
- /* STMPE801 doesn't support Edge interrupts */
- if (id != STMPE801_ID) {
+ /* STMPE801 and STMPE1600 don't support Edge interrupts */
+ if (id != STMPE801_ID && id != STMPE1600_ID) {
if (irq_trigger == IRQF_TRIGGER_FALLING ||
irq_trigger == IRQF_TRIGGER_RISING)
icr |= STMPE_ICR_LSB_EDGE;
@@ -1109,8 +1218,8 @@ static int stmpe_chip_init(struct stmpe *stmpe)
if (irq_trigger == IRQF_TRIGGER_RISING ||
irq_trigger == IRQF_TRIGGER_HIGH) {
- if (id == STMPE801_ID)
- icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+ if (id == STMPE801_ID || id == STMPE1600_ID)
+ icr |= STMPE_SYS_CTRL_INT_HI;
else
icr |= STMPE_ICR_LSB_HIGH;
}
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
index 84adb46b3e2f..f7efdd8a5fc6 100644
--- a/drivers/mfd/stmpe.h
+++ b/drivers/mfd/stmpe.h
@@ -104,6 +104,10 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE_ICR_LSB_EDGE (1 << 1)
#define STMPE_ICR_LSB_GIM (1 << 0)
+#define STMPE_SYS_CTRL_RESET (1 << 7)
+#define STMPE_SYS_CTRL_INT_EN (1 << 2)
+#define STMPE_SYS_CTRL_INT_HI (1 << 0)
+
/*
* STMPE801
*/
@@ -119,13 +123,10 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE801_REG_GPIO_SET_PIN 0x11
#define STMPE801_REG_GPIO_DIR 0x12
-#define STMPE801_REG_SYS_CTRL_RESET (1 << 7)
-#define STMPE801_REG_SYS_CTRL_INT_EN (1 << 2)
-#define STMPE801_REG_SYS_CTRL_INT_HI (1 << 0)
-
/*
* STMPE811
*/
+#define STMPE811_ID 0x0811
#define STMPE811_IRQ_TOUCH_DET 0
#define STMPE811_IRQ_FIFO_TH 1
@@ -138,6 +139,7 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE811_NR_INTERNAL_IRQS 8
#define STMPE811_REG_CHIP_ID 0x00
+#define STMPE811_REG_SYS_CTRL 0x03
#define STMPE811_REG_SYS_CTRL2 0x04
#define STMPE811_REG_SPI_CFG 0x08
#define STMPE811_REG_INT_CTRL 0x09
@@ -154,12 +156,35 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE811_REG_GPIO_FE 0x16
#define STMPE811_REG_GPIO_AF 0x17
+#define STMPE811_SYS_CTRL_RESET (1 << 1)
+
#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0)
#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1)
#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2)
#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3)
/*
+ * STMPE1600
+ */
+#define STMPE1600_ID 0x0016
+#define STMPE1600_NR_INTERNAL_IRQS 16
+
+#define STMPE1600_REG_CHIP_ID 0x00
+#define STMPE1600_REG_SYS_CTRL 0x03
+#define STMPE1600_REG_IEGPIOR_LSB 0x08
+#define STMPE1600_REG_IEGPIOR_MSB 0x09
+#define STMPE1600_REG_ISGPIOR_LSB 0x0A
+#define STMPE1600_REG_ISGPIOR_MSB 0x0B
+#define STMPE1600_REG_GPMR_LSB 0x10
+#define STMPE1600_REG_GPMR_MSB 0x11
+#define STMPE1600_REG_GPSR_LSB 0x12
+#define STMPE1600_REG_GPSR_MSB 0x13
+#define STMPE1600_REG_GPDR_LSB 0x14
+#define STMPE1600_REG_GPDR_MSB 0x15
+#define STMPE1600_REG_GPPIR_LSB 0x16
+#define STMPE1600_REG_GPPIR_MSB 0x17
+
+/*
* STMPE1601
*/
@@ -175,19 +200,32 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE1601_REG_SYS_CTRL 0x02
#define STMPE1601_REG_SYS_CTRL2 0x03
+#define STMPE1601_REG_ICR_MSB 0x10
#define STMPE1601_REG_ICR_LSB 0x11
+#define STMPE1601_REG_IER_MSB 0x12
#define STMPE1601_REG_IER_LSB 0x13
#define STMPE1601_REG_ISR_MSB 0x14
-#define STMPE1601_REG_CHIP_ID 0x80
+#define STMPE1601_REG_ISR_LSB 0x15
+#define STMPE1601_REG_INT_EN_GPIO_MASK_MSB 0x16
#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17
#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18
-#define STMPE1601_REG_GPIO_MP_LSB 0x87
+#define STMPE1601_REG_INT_STA_GPIO_LSB 0x19
+#define STMPE1601_REG_CHIP_ID 0x80
+#define STMPE1601_REG_GPIO_SET_MSB 0x82
#define STMPE1601_REG_GPIO_SET_LSB 0x83
+#define STMPE1601_REG_GPIO_CLR_MSB 0x84
#define STMPE1601_REG_GPIO_CLR_LSB 0x85
+#define STMPE1601_REG_GPIO_MP_MSB 0x86
+#define STMPE1601_REG_GPIO_MP_LSB 0x87
+#define STMPE1601_REG_GPIO_SET_DIR_MSB 0x88
#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89
#define STMPE1601_REG_GPIO_ED_MSB 0x8A
+#define STMPE1601_REG_GPIO_ED_LSB 0x8B
+#define STMPE1601_REG_GPIO_RE_MSB 0x8C
#define STMPE1601_REG_GPIO_RE_LSB 0x8D
+#define STMPE1601_REG_GPIO_FE_MSB 0x8E
#define STMPE1601_REG_GPIO_FE_LSB 0x8F
+#define STMPE1601_REG_GPIO_PU_MSB 0x90
#define STMPE1601_REG_GPIO_PU_LSB 0x91
#define STMPE1601_REG_GPIO_AF_U_MSB 0x92
@@ -243,8 +281,6 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE1801_REG_GPIO_PULL_UP_MID 0x23
#define STMPE1801_REG_GPIO_PULL_UP_HIGH 0x24
-#define STMPE1801_MSK_SYS_CTRL_RESET (1 << 7)
-
#define STMPE1801_MSK_INT_EN_KPC (1 << 1)
#define STMPE1801_MSK_INT_EN_GPIO (1 << 3)
@@ -264,23 +300,48 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE24XX_NR_INTERNAL_IRQS 9
#define STMPE24XX_REG_SYS_CTRL 0x02
+#define STMPE24XX_REG_SYS_CTRL2 0x03
+#define STMPE24XX_REG_ICR_MSB 0x10
#define STMPE24XX_REG_ICR_LSB 0x11
+#define STMPE24XX_REG_IER_MSB 0x12
#define STMPE24XX_REG_IER_LSB 0x13
#define STMPE24XX_REG_ISR_MSB 0x14
-#define STMPE24XX_REG_CHIP_ID 0x80
+#define STMPE24XX_REG_ISR_LSB 0x15
+#define STMPE24XX_REG_IEGPIOR_MSB 0x16
+#define STMPE24XX_REG_IEGPIOR_CSB 0x17
#define STMPE24XX_REG_IEGPIOR_LSB 0x18
#define STMPE24XX_REG_ISGPIOR_MSB 0x19
-#define STMPE24XX_REG_GPMR_LSB 0xA4
+#define STMPE24XX_REG_ISGPIOR_CSB 0x1A
+#define STMPE24XX_REG_ISGPIOR_LSB 0x1B
+#define STMPE24XX_REG_CHIP_ID 0x80
+#define STMPE24XX_REG_GPSR_MSB 0x83
+#define STMPE24XX_REG_GPSR_CSB 0x84
#define STMPE24XX_REG_GPSR_LSB 0x85
+#define STMPE24XX_REG_GPCR_MSB 0x86
+#define STMPE24XX_REG_GPCR_CSB 0x87
#define STMPE24XX_REG_GPCR_LSB 0x88
+#define STMPE24XX_REG_GPDR_MSB 0x89
+#define STMPE24XX_REG_GPDR_CSB 0x8A
#define STMPE24XX_REG_GPDR_LSB 0x8B
#define STMPE24XX_REG_GPEDR_MSB 0x8C
+#define STMPE24XX_REG_GPEDR_CSB 0x8D
+#define STMPE24XX_REG_GPEDR_LSB 0x8E
+#define STMPE24XX_REG_GPRER_MSB 0x8F
+#define STMPE24XX_REG_GPRER_CSB 0x90
#define STMPE24XX_REG_GPRER_LSB 0x91
+#define STMPE24XX_REG_GPFER_MSB 0x92
+#define STMPE24XX_REG_GPFER_CSB 0x93
#define STMPE24XX_REG_GPFER_LSB 0x94
+#define STMPE24XX_REG_GPPUR_MSB 0x95
+#define STMPE24XX_REG_GPPUR_CSB 0x96
#define STMPE24XX_REG_GPPUR_LSB 0x97
-#define STMPE24XX_REG_GPPDR_LSB 0x9a
+#define STMPE24XX_REG_GPPDR_MSB 0x98
+#define STMPE24XX_REG_GPPDR_CSB 0x99
+#define STMPE24XX_REG_GPPDR_LSB 0x9A
#define STMPE24XX_REG_GPAFR_U_MSB 0x9B
-
+#define STMPE24XX_REG_GPMR_MSB 0xA2
+#define STMPE24XX_REG_GPMR_CSB 0xA3
+#define STMPE24XX_REG_GPMR_LSB 0xA4
#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3)
#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2)
#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1)
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 6c084b266651..04053fe1e980 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -19,6 +19,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
#define EC_COMMAND_RETRIES 50
@@ -234,11 +235,44 @@ static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
return ret;
}
+static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev,
+ u16 cmd, u32 *mask)
+{
+ struct ec_params_get_cmd_versions *pver;
+ struct ec_response_get_cmd_versions *rver;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)),
+ GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_CMD_VERSIONS;
+ msg->insize = sizeof(*rver);
+ msg->outsize = sizeof(*pver);
+
+ pver = (struct ec_params_get_cmd_versions *)msg->data;
+ pver->cmd = cmd;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret > 0) {
+ rver = (struct ec_response_get_cmd_versions *)msg->data;
+ *mask = rver->version_mask;
+ }
+
+ kfree(msg);
+
+ return ret;
+}
+
int cros_ec_query_all(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
struct cros_ec_command *proto_msg;
struct ec_response_get_protocol_info *proto_info;
+ u32 ver_mask = 0;
int ret;
proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
@@ -328,6 +362,15 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
goto exit;
}
+ /* Probe if MKBP event is supported */
+ ret = cros_ec_get_host_command_version_mask(ec_dev,
+ EC_CMD_GET_NEXT_EVENT,
+ &ver_mask);
+ if (ret < 0 || ver_mask == 0)
+ ec_dev->mkbp_event_supported = 0;
+ else
+ ec_dev->mkbp_event_supported = 1;
+
exit:
kfree(proto_msg);
return ret;
@@ -397,3 +440,52 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
return ret;
}
EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
+
+static int get_next_event(struct cros_ec_device *ec_dev)
+{
+ u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)];
+ struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
+ int ret;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_NEXT_EVENT;
+ msg->insize = sizeof(ec_dev->event_data);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret > 0) {
+ ec_dev->event_size = ret - 1;
+ memcpy(&ec_dev->event_data, msg->data,
+ sizeof(ec_dev->event_data));
+ }
+
+ return ret;
+}
+
+static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
+{
+ u8 buffer[sizeof(struct cros_ec_command) +
+ sizeof(ec_dev->event_data.data)];
+ struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
+
+ msg->version = 0;
+ msg->command = EC_CMD_MKBP_STATE;
+ msg->insize = sizeof(ec_dev->event_data.data);
+ msg->outsize = 0;
+
+ ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg);
+ ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX;
+ memcpy(&ec_dev->event_data.data, msg->data,
+ sizeof(ec_dev->event_data.data));
+
+ return ec_dev->event_size;
+}
+
+int cros_ec_get_next_event(struct cros_ec_device *ec_dev)
+{
+ if (ec_dev->mkbp_event_supported)
+ return get_next_event(ec_dev);
+ else
+ return get_keyboard_state_event(ec_dev);
+}
+EXPORT_SYMBOL(cros_ec_get_next_event);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6c88e31c01f7..620412966beb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -323,7 +323,7 @@ config REGULATOR_LP872X
config REGULATOR_LP873X
tristate "TI LP873X Power regulators"
- depends on MFD_LP873X && OF
+ depends on MFD_TI_LP873X && OF
help
This driver supports LP873X voltage regulator chips. LP873X
provides two step-down converters and two general-purpose LDO
@@ -635,11 +635,11 @@ config REGULATOR_RC5T583
outputs which can be controlled by i2c communication.
config REGULATOR_RK808
- tristate "Rockchip RK808 Power regulators"
+ tristate "Rockchip RK808/RK818 Power regulators"
depends on MFD_RK808
help
Select this option to enable the power regulator of ROCKCHIP
- PMIC RK808.
+ PMIC RK808 and RK818.
This driver supports the control of different power rails of device
through regulator interface. The device supports multiple DCDC/LDO
outputs which can be controlled by i2c communication.
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index 6d9ac76a772f..54382ef902c6 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -244,16 +244,64 @@ static const struct regulator_desc axp22x_drivevbus_regulator = {
.ops = &axp20x_ops_sw,
};
-static const struct regulator_linear_range axp809_dcdc4_ranges[] = {
- REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000),
- REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000),
+static const struct regulator_linear_range axp806_dcdca_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000),
+ REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000),
};
-static const struct regulator_linear_range axp809_dldo1_ranges[] = {
+static const struct regulator_linear_range axp806_dcdcd_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000),
+};
+
+static const struct regulator_linear_range axp806_cldo2_ranges[] = {
REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000),
REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000),
};
+static const struct regulator_desc axp806_regulators[] = {
+ AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges,
+ 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
+ BIT(0)),
+ AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50,
+ AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges,
+ 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
+ BIT(2)),
+ AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges,
+ 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1,
+ BIT(3)),
+ AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100,
+ AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)),
+ AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)),
+ AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100,
+ AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100,
+ AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100,
+ AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100,
+ AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100,
+ AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100,
+ AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp806_cldo2_ranges,
+ 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2,
+ BIT(5)),
+ AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100,
+ AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)),
+};
+
+static const struct regulator_linear_range axp809_dcdc4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000),
+};
+
static const struct regulator_desc axp809_regulators[] = {
AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
@@ -278,7 +326,7 @@ static const struct regulator_desc axp809_regulators[] = {
AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
- AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp809_dldo1_ranges,
+ AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp806_cldo2_ranges,
32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
BIT(3)),
AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
@@ -302,6 +350,7 @@ static const struct regulator_desc axp809_regulators[] = {
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ unsigned int reg = AXP20X_DCDC_FREQ;
u32 min, max, def, step;
switch (axp20x->variant) {
@@ -312,6 +361,14 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
def = 1500;
step = 75;
break;
+ case AXP806_ID:
+ /*
+ * AXP806 DCDC work frequency setting has the same range and
+ * step as AXP22X, but at a different register.
+ * Fall through to the check below.
+ * (See include/linux/mfd/axp20x.h)
+ */
+ reg = AXP806_DCDC_FREQ_CTRL;
case AXP221_ID:
case AXP223_ID:
case AXP809_ID:
@@ -343,7 +400,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
dcdcfreq = (dcdcfreq - min) / step;
- return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
+ return regmap_update_bits(axp20x->regmap, reg,
AXP20X_FREQ_DCDC_MASK, dcdcfreq);
}
@@ -377,6 +434,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
{
struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+ unsigned int reg = AXP20X_DCDC_MODE;
unsigned int mask;
switch (axp20x->variant) {
@@ -392,6 +450,13 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work
workmode <<= ffs(mask) - 1;
break;
+ case AXP806_ID:
+ reg = AXP806_DCDC_MODE_CTRL2;
+ /*
+ * AXP806 DCDC regulator IDs have the same range as AXP22X.
+ * Fall through to the check below.
+ * (See include/linux/mfd/axp20x.h)
+ */
case AXP221_ID:
case AXP223_ID:
case AXP809_ID:
@@ -408,7 +473,34 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work
return -EINVAL;
}
- return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
+ return regmap_update_bits(rdev->regmap, reg, mask, workmode);
+}
+
+/*
+ * This function checks whether a regulator is part of a poly-phase
+ * output setup based on the registers settings. Returns true if it is.
+ */
+static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id)
+{
+ u32 reg = 0;
+
+ /* Only AXP806 has poly-phase outputs */
+ if (axp20x->variant != AXP806_ID)
+ return false;
+
+ regmap_read(axp20x->regmap, AXP806_DCDC_MODE_CTRL2, &reg);
+
+ switch (id) {
+ case AXP806_DCDCB:
+ return (((reg & GENMASK(7, 6)) == BIT(6)) ||
+ ((reg & GENMASK(7, 6)) == BIT(7)));
+ case AXP806_DCDCC:
+ return ((reg & GENMASK(7, 6)) == BIT(7));
+ case AXP806_DCDCE:
+ return !!(reg & BIT(5));
+ }
+
+ return false;
}
static int axp20x_regulator_probe(struct platform_device *pdev)
@@ -440,6 +532,10 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
drivevbus = of_property_read_bool(pdev->dev.parent->of_node,
"x-powers,drive-vbus-en");
break;
+ case AXP806_ID:
+ regulators = axp806_regulators;
+ nregulators = AXP806_REG_ID_MAX;
+ break;
case AXP809_ID:
regulators = axp809_regulators;
nregulators = AXP809_REG_ID_MAX;
@@ -458,6 +554,14 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
struct regulator_desc *new_desc;
/*
+ * If this regulator is a slave in a poly-phase setup,
+ * skip it, as its controls are bound to the master
+ * regulator and won't work.
+ */
+ if (axp20x_is_polyphase_slave(axp20x, i))
+ continue;
+
+ /*
* Regulators DC1SW and DC5LDO are connected internally,
* so we have to handle their supply names separately.
*
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index 40d07ba036e7..5f412a5e35e3 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -1,11 +1,15 @@
/*
- * Regulator driver for Rockchip RK808
+ * Regulator driver for Rockchip RK808/RK818
*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Chris Zhong <zyw@rock-chips.com>
* Author: Zhang Qing <zhangqing@rock-chips.com>
*
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -32,6 +36,12 @@
#define RK808_BUCK4_VSEL_MASK 0xf
#define RK808_LDO_VSEL_MASK 0x1f
+#define RK818_BUCK_VSEL_MASK 0x3f
+#define RK818_BUCK4_VSEL_MASK 0x1f
+#define RK818_LDO_VSEL_MASK 0x1f
+#define RK818_LDO3_ON_VSEL_MASK 0xf
+#define RK818_BOOST_ON_VSEL_MASK 0xe0
+
/* Ramp rate definitions for buck1 / buck2 only */
#define RK808_RAMP_RATE_OFFSET 3
#define RK808_RAMP_RATE_MASK (3 << RK808_RAMP_RATE_OFFSET)
@@ -454,6 +464,108 @@ static const struct regulator_desc rk808_reg[] = {
RK808_DCDC_EN_REG, BIT(6)),
};
+static const struct regulator_desc rk818_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC1,
+ .ops = &rk808_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK818_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(0),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC2,
+ .ops = &rk808_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK818_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(1),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC3,
+ .ops = &rk808_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(2),
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK818_ID_DCDC4, "DCDC_REG4", "vcc4", 1800, 3600, 100,
+ RK818_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(3), 0),
+ RK8XX_DESC(RK818_ID_BOOST, "DCDC_BOOST", "boost", 4700, 5400, 100,
+ RK818_BOOST_LDO9_ON_VSEL_REG, RK818_BOOST_ON_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(4), 0),
+ RK8XX_DESC(RK818_ID_LDO1, "LDO_REG1", "vcc6", 1800, 3400, 100,
+ RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(0), 400),
+ RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
+ RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(1), 400),
+ {
+ .name = "LDO_REG3",
+ .supply_name = "vcc7",
+ .of_match = of_match_ptr("LDO_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_LDO3,
+ .ops = &rk808_reg_ops_ranges,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 16,
+ .linear_ranges = rk808_ldo3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges),
+ .vsel_reg = RK818_LDO3_ON_VSEL_REG,
+ .vsel_mask = RK818_LDO3_ON_VSEL_MASK,
+ .enable_reg = RK818_LDO_EN_REG,
+ .enable_mask = BIT(2),
+ .enable_time = 400,
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK818_ID_LDO4, "LDO_REG4", "vcc8", 1800, 3400, 100,
+ RK818_LDO4_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(3), 400),
+ RK8XX_DESC(RK818_ID_LDO5, "LDO_REG5", "vcc7", 1800, 3400, 100,
+ RK818_LDO5_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(4), 400),
+ RK8XX_DESC(RK818_ID_LDO6, "LDO_REG6", "vcc8", 800, 2500, 100,
+ RK818_LDO6_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(5), 400),
+ RK8XX_DESC(RK818_ID_LDO7, "LDO_REG7", "vcc7", 800, 2500, 100,
+ RK818_LDO7_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(6), 400),
+ RK8XX_DESC(RK818_ID_LDO8, "LDO_REG8", "vcc8", 1800, 3400, 100,
+ RK818_LDO8_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(7), 400),
+ RK8XX_DESC(RK818_ID_LDO9, "LDO_REG9", "vcc9", 1800, 3400, 100,
+ RK818_BOOST_LDO9_ON_VSEL_REG, RK818_LDO_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(5), 400),
+ RK8XX_DESC_SWITCH(RK818_ID_SWITCH, "SWITCH_REG", "vcc9",
+ RK818_DCDC_EN_REG, BIT(6)),
+ RK8XX_DESC_SWITCH(RK818_ID_HDMI_SWITCH, "HDMI_SWITCH", "h_5v",
+ RK818_H5V_EN_REG, BIT(0)),
+ RK8XX_DESC_SWITCH(RK818_ID_OTG_SWITCH, "OTG_SWITCH", "usb",
+ RK818_DCDC_EN_REG, BIT(7)),
+};
+
static int rk808_regulator_dt_parse_pdata(struct device *dev,
struct device *client_dev,
struct regmap *map,
@@ -499,7 +611,8 @@ static int rk808_regulator_probe(struct platform_device *pdev)
struct regulator_config config = {};
struct regulator_dev *rk808_rdev;
struct rk808_regulator_data *pdata;
- int ret, i;
+ const struct regulator_desc *regulators;
+ int ret, i, nregulators;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -512,14 +625,29 @@ static int rk808_regulator_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pdata);
+ switch (rk808->variant) {
+ case RK808_ID:
+ regulators = rk808_reg;
+ nregulators = RK808_NUM_REGULATORS;
+ break;
+ case RK818_ID:
+ regulators = rk818_reg;
+ nregulators = RK818_NUM_REGULATORS;
+ break;
+ default:
+ dev_err(&client->dev, "unsupported RK8XX ID %lu\n",
+ rk808->variant);
+ return -EINVAL;
+ }
+
config.dev = &client->dev;
config.driver_data = pdata;
config.regmap = rk808->regmap;
/* Instantiate the regulators */
- for (i = 0; i < RK808_NUM_REGULATORS; i++) {
+ for (i = 0; i < nregulators; i++) {
rk808_rdev = devm_regulator_register(&pdev->dev,
- &rk808_reg[i], &config);
+ &regulators[i], &config);
if (IS_ERR(rk808_rdev)) {
dev_err(&client->dev,
"failed to register %d regulator\n", i);
@@ -540,8 +668,9 @@ static struct platform_driver rk808_regulator_driver = {
module_platform_driver(rk808_regulator_driver);
-MODULE_DESCRIPTION("regulator driver for the rk808 series PMICs");
-MODULE_AUTHOR("Chris Zhong<zyw@rock-chips.com>");
-MODULE_AUTHOR("Zhang Qing<zhangqing@rock-chips.com>");
+MODULE_DESCRIPTION("regulator driver for the RK808/RK818 series PMICs");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rk808-regulator");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e215f50794b6..d1e080701264 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -187,6 +187,16 @@ config RTC_DRV_ABX80X
This driver can also be built as a module. If so, the module
will be called rtc-abx80x.
+config RTC_DRV_AC100
+ tristate "X-Powers AC100"
+ depends on MFD_AC100
+ help
+ If you say yes here you get support for the real-time clock found
+ in X-Powers AC100 family peripheral ICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ac100.
+
config RTC_DRV_AS3722
tristate "ams AS3722 RTC driver"
depends on MFD_AS3722
@@ -328,11 +338,11 @@ config RTC_DRV_MAX77686
will be called rtc-max77686.
config RTC_DRV_RK808
- tristate "Rockchip RK808 RTC"
+ tristate "Rockchip RK808/RK818 RTC"
depends on MFD_RK808
help
If you say yes here you will get support for the
- RTC of RK808 PMIC.
+ RTC of RK808 and RK818 PMIC.
This driver can also be built as a module. If so, the module
will be called rk808-rtc.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7cf7ad559c79..8fb994bacdf7 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
+obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c
new file mode 100644
index 000000000000..70b4fd0f6122
--- /dev/null
+++ b/drivers/rtc/rtc-ac100.c
@@ -0,0 +1,627 @@
+/*
+ * RTC Driver for X-Powers AC100
+ *
+ * Copyright (c) 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/bcd.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ac100.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+
+/* Control register */
+#define AC100_RTC_CTRL_24HOUR BIT(0)
+
+/* Clock output register bits */
+#define AC100_CLKOUT_PRE_DIV_SHIFT 5
+#define AC100_CLKOUT_PRE_DIV_WIDTH 3
+#define AC100_CLKOUT_MUX_SHIFT 4
+#define AC100_CLKOUT_MUX_WIDTH 1
+#define AC100_CLKOUT_DIV_SHIFT 1
+#define AC100_CLKOUT_DIV_WIDTH 3
+#define AC100_CLKOUT_EN BIT(0)
+
+/* RTC */
+#define AC100_RTC_SEC_MASK GENMASK(6, 0)
+#define AC100_RTC_MIN_MASK GENMASK(6, 0)
+#define AC100_RTC_HOU_MASK GENMASK(5, 0)
+#define AC100_RTC_WEE_MASK GENMASK(2, 0)
+#define AC100_RTC_DAY_MASK GENMASK(5, 0)
+#define AC100_RTC_MON_MASK GENMASK(4, 0)
+#define AC100_RTC_YEA_MASK GENMASK(7, 0)
+#define AC100_RTC_YEA_LEAP BIT(15)
+#define AC100_RTC_UPD_TRIGGER BIT(15)
+
+/* Alarm (wall clock) */
+#define AC100_ALM_INT_ENABLE BIT(0)
+
+#define AC100_ALM_SEC_MASK GENMASK(6, 0)
+#define AC100_ALM_MIN_MASK GENMASK(6, 0)
+#define AC100_ALM_HOU_MASK GENMASK(5, 0)
+#define AC100_ALM_WEE_MASK GENMASK(2, 0)
+#define AC100_ALM_DAY_MASK GENMASK(5, 0)
+#define AC100_ALM_MON_MASK GENMASK(4, 0)
+#define AC100_ALM_YEA_MASK GENMASK(7, 0)
+#define AC100_ALM_ENABLE_FLAG BIT(15)
+#define AC100_ALM_UPD_TRIGGER BIT(15)
+
+/*
+ * The year parameter passed to the driver is usually an offset relative to
+ * the year 1900. This macro is used to convert this offset to another one
+ * relative to the minimum year allowed by the hardware.
+ *
+ * The year range is 1970 - 2069. This range is selected to match Allwinner's
+ * driver.
+ */
+#define AC100_YEAR_MIN 1970
+#define AC100_YEAR_MAX 2069
+#define AC100_YEAR_OFF (AC100_YEAR_MIN - 1900)
+
+struct ac100_clkout {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ u8 offset;
+};
+
+#define to_ac100_clkout(_hw) container_of(_hw, struct ac100_clkout, hw)
+
+#define AC100_RTC_32K_NAME "ac100-rtc-32k"
+#define AC100_RTC_32K_RATE 32768
+#define AC100_CLKOUT_NUM 3
+
+static const char * const ac100_clkout_names[AC100_CLKOUT_NUM] = {
+ "ac100-cko1-rtc",
+ "ac100-cko2-rtc",
+ "ac100-cko3-rtc",
+};
+
+struct ac100_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ struct regmap *regmap;
+ int irq;
+ unsigned long alarm;
+
+ struct clk_hw *rtc_32k_clk;
+ struct ac100_clkout clks[AC100_CLKOUT_NUM];
+ struct clk_hw_onecell_data *clk_data;
+};
+
+/**
+ * Clock controls for 3 clock output pins
+ */
+
+static const struct clk_div_table ac100_clkout_prediv[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { .val = 4, .div = 16 },
+ { .val = 5, .div = 32 },
+ { .val = 6, .div = 64 },
+ { .val = 7, .div = 122 },
+ { },
+};
+
+/* Abuse the fact that one parent is 32768 Hz, and the other is 4 MHz */
+static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+ unsigned int reg, div;
+
+ regmap_read(clk->regmap, clk->offset, &reg);
+
+ /* Handle pre-divider first */
+ if (prate != AC100_RTC_32K_RATE) {
+ div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) &
+ ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1);
+ prate = divider_recalc_rate(hw, prate, div,
+ ac100_clkout_prediv, 0);
+ }
+
+ div = (reg >> AC100_CLKOUT_DIV_SHIFT) &
+ (BIT(AC100_CLKOUT_DIV_WIDTH) - 1);
+ return divider_recalc_rate(hw, prate, div, NULL,
+ CLK_DIVIDER_POWER_OF_TWO);
+}
+
+static long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ unsigned long best_rate = 0, tmp_rate, tmp_prate;
+ int i;
+
+ if (prate == AC100_RTC_32K_RATE)
+ return divider_round_rate(hw, rate, &prate, NULL,
+ AC100_CLKOUT_DIV_WIDTH,
+ CLK_DIVIDER_POWER_OF_TWO);
+
+ for (i = 0; ac100_clkout_prediv[i].div; i++) {
+ tmp_prate = DIV_ROUND_UP(prate, ac100_clkout_prediv[i].val);
+ tmp_rate = divider_round_rate(hw, rate, &tmp_prate, NULL,
+ AC100_CLKOUT_DIV_WIDTH,
+ CLK_DIVIDER_POWER_OF_TWO);
+
+ if (tmp_rate > rate)
+ continue;
+ if (rate - tmp_rate < best_rate - tmp_rate)
+ best_rate = tmp_rate;
+ }
+
+ return best_rate;
+}
+
+static int ac100_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_hw *best_parent;
+ unsigned long best = 0;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+
+ for (i = 0; i < num_parents; i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+ unsigned long tmp, prate = clk_hw_get_rate(parent);
+
+ tmp = ac100_clkout_round_rate(hw, req->rate, prate);
+
+ if (tmp > req->rate)
+ continue;
+ if (req->rate - tmp < req->rate - best) {
+ best = tmp;
+ best_parent = parent;
+ }
+ }
+
+ if (!best)
+ return -EINVAL;
+
+ req->best_parent_hw = best_parent;
+ req->best_parent_rate = best;
+ req->rate = best;
+
+ return 0;
+}
+
+static int ac100_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+ int div = 0, pre_div = 0;
+
+ do {
+ div = divider_get_val(rate * ac100_clkout_prediv[pre_div].div,
+ prate, NULL, AC100_CLKOUT_DIV_WIDTH,
+ CLK_DIVIDER_POWER_OF_TWO);
+ if (div >= 0)
+ break;
+ } while (prate != AC100_RTC_32K_RATE &&
+ ac100_clkout_prediv[++pre_div].div);
+
+ if (div < 0)
+ return div;
+
+ pre_div = ac100_clkout_prediv[pre_div].val;
+
+ regmap_update_bits(clk->regmap, clk->offset,
+ ((1 << AC100_CLKOUT_DIV_WIDTH) - 1) << AC100_CLKOUT_DIV_SHIFT |
+ ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1) << AC100_CLKOUT_PRE_DIV_SHIFT,
+ (div - 1) << AC100_CLKOUT_DIV_SHIFT |
+ (pre_div - 1) << AC100_CLKOUT_PRE_DIV_SHIFT);
+
+ return 0;
+}
+
+static int ac100_clkout_prepare(struct clk_hw *hw)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+
+ return regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN,
+ AC100_CLKOUT_EN);
+}
+
+static void ac100_clkout_unprepare(struct clk_hw *hw)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+
+ regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, 0);
+}
+
+static int ac100_clkout_is_prepared(struct clk_hw *hw)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+ unsigned int reg;
+
+ regmap_read(clk->regmap, clk->offset, &reg);
+
+ return reg & AC100_CLKOUT_EN;
+}
+
+static u8 ac100_clkout_get_parent(struct clk_hw *hw)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+ unsigned int reg;
+
+ regmap_read(clk->regmap, clk->offset, &reg);
+
+ return (reg >> AC100_CLKOUT_MUX_SHIFT) & 0x1;
+}
+
+static int ac100_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ac100_clkout *clk = to_ac100_clkout(hw);
+
+ return regmap_update_bits(clk->regmap, clk->offset,
+ BIT(AC100_CLKOUT_MUX_SHIFT),
+ index ? BIT(AC100_CLKOUT_MUX_SHIFT) : 0);
+}
+
+static const struct clk_ops ac100_clkout_ops = {
+ .prepare = ac100_clkout_prepare,
+ .unprepare = ac100_clkout_unprepare,
+ .is_prepared = ac100_clkout_is_prepared,
+ .recalc_rate = ac100_clkout_recalc_rate,
+ .determine_rate = ac100_clkout_determine_rate,
+ .get_parent = ac100_clkout_get_parent,
+ .set_parent = ac100_clkout_set_parent,
+ .set_rate = ac100_clkout_set_rate,
+};
+
+static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
+{
+ struct device_node *np = chip->dev->of_node;
+ const char *parents[2] = {AC100_RTC_32K_NAME};
+ int i, ret;
+
+ chip->clk_data = devm_kzalloc(chip->dev, sizeof(*chip->clk_data) +
+ sizeof(*chip->clk_data->hws) *
+ AC100_CLKOUT_NUM,
+ GFP_KERNEL);
+ if (!chip->clk_data)
+ return -ENOMEM;
+
+ chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev,
+ AC100_RTC_32K_NAME,
+ NULL, 0,
+ AC100_RTC_32K_RATE);
+ if (IS_ERR(chip->rtc_32k_clk)) {
+ ret = PTR_ERR(chip->rtc_32k_clk);
+ dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ parents[1] = of_clk_get_parent_name(np, 0);
+ if (!parents[1]) {
+ dev_err(chip->dev, "Failed to get ADDA 4M clock\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < AC100_CLKOUT_NUM; i++) {
+ struct ac100_clkout *clk = &chip->clks[i];
+ struct clk_init_data init = {
+ .name = ac100_clkout_names[i],
+ .ops = &ac100_clkout_ops,
+ .parent_names = parents,
+ .num_parents = ARRAY_SIZE(parents),
+ .flags = 0,
+ };
+
+ clk->regmap = chip->regmap;
+ clk->offset = AC100_CLKOUT_CTRL1 + i;
+ clk->hw.init = &init;
+
+ ret = devm_clk_hw_register(chip->dev, &clk->hw);
+ if (ret) {
+ dev_err(chip->dev, "Failed to register clk '%s': %d\n",
+ init.name, ret);
+ goto err_unregister_rtc_32k;
+ }
+
+ chip->clk_data->hws[i] = &clk->hw;
+ }
+
+ chip->clk_data->num = i;
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data);
+ if (ret)
+ goto err_unregister_rtc_32k;
+
+ return 0;
+
+err_unregister_rtc_32k:
+ clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
+
+ return ret;
+}
+
+static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip)
+{
+ of_clk_del_provider(chip->dev->of_node);
+ clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
+}
+
+/**
+ * RTC related bits
+ */
+static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+ struct regmap *regmap = chip->regmap;
+ u16 reg[7];
+ int ret;
+
+ ret = regmap_bulk_read(regmap, AC100_RTC_SEC, reg, 7);
+ if (ret)
+ return ret;
+
+ rtc_tm->tm_sec = bcd2bin(reg[0] & AC100_RTC_SEC_MASK);
+ rtc_tm->tm_min = bcd2bin(reg[1] & AC100_RTC_MIN_MASK);
+ rtc_tm->tm_hour = bcd2bin(reg[2] & AC100_RTC_HOU_MASK);
+ rtc_tm->tm_wday = bcd2bin(reg[3] & AC100_RTC_WEE_MASK);
+ rtc_tm->tm_mday = bcd2bin(reg[4] & AC100_RTC_DAY_MASK);
+ rtc_tm->tm_mon = bcd2bin(reg[5] & AC100_RTC_MON_MASK) - 1;
+ rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) +
+ AC100_YEAR_OFF;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+ struct regmap *regmap = chip->regmap;
+ int year;
+ u16 reg[8];
+
+ /* our RTC has a limited year range... */
+ year = rtc_tm->tm_year - AC100_YEAR_OFF;
+ if (year < 0 || year > (AC100_YEAR_MAX - 1900)) {
+ dev_err(dev, "rtc only supports year in range %d - %d\n",
+ AC100_YEAR_MIN, AC100_YEAR_MAX);
+ return -EINVAL;
+ }
+
+ /* convert to BCD */
+ reg[0] = bin2bcd(rtc_tm->tm_sec) & AC100_RTC_SEC_MASK;
+ reg[1] = bin2bcd(rtc_tm->tm_min) & AC100_RTC_MIN_MASK;
+ reg[2] = bin2bcd(rtc_tm->tm_hour) & AC100_RTC_HOU_MASK;
+ reg[3] = bin2bcd(rtc_tm->tm_wday) & AC100_RTC_WEE_MASK;
+ reg[4] = bin2bcd(rtc_tm->tm_mday) & AC100_RTC_DAY_MASK;
+ reg[5] = bin2bcd(rtc_tm->tm_mon + 1) & AC100_RTC_MON_MASK;
+ reg[6] = bin2bcd(year) & AC100_RTC_YEA_MASK;
+ /* trigger write */
+ reg[7] = AC100_RTC_UPD_TRIGGER;
+
+ /* Is it a leap year? */
+ if (is_leap_year(year + AC100_YEAR_OFF + 1900))
+ reg[6] |= AC100_RTC_YEA_LEAP;
+
+ return regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8);
+}
+
+static int ac100_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
+{
+ struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+ struct regmap *regmap = chip->regmap;
+ unsigned int val;
+
+ val = en ? AC100_ALM_INT_ENABLE : 0;
+
+ return regmap_write(regmap, AC100_ALM_INT_ENA, val);
+}
+
+static int ac100_rtc_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+ struct regmap *regmap = chip->regmap;
+ struct rtc_time *alrm_tm = &alrm->time;
+ u16 reg[7];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, AC100_ALM_INT_ENA, &val);
+ if (ret)
+ return ret;
+
+ alrm->enabled = !!(val & AC100_ALM_INT_ENABLE);
+
+ ret = regmap_bulk_read(regmap, AC100_ALM_SEC, reg, 7);
+ if (ret)
+ return ret;
+
+ alrm_tm->tm_sec = bcd2bin(reg[0] & AC100_ALM_SEC_MASK);
+ alrm_tm->tm_min = bcd2bin(reg[1] & AC100_ALM_MIN_MASK);
+ alrm_tm->tm_hour = bcd2bin(reg[2] & AC100_ALM_HOU_MASK);
+ alrm_tm->tm_wday = bcd2bin(reg[3] & AC100_ALM_WEE_MASK);
+ alrm_tm->tm_mday = bcd2bin(reg[4] & AC100_ALM_DAY_MASK);
+ alrm_tm->tm_mon = bcd2bin(reg[5] & AC100_ALM_MON_MASK) - 1;
+ alrm_tm->tm_year = bcd2bin(reg[6] & AC100_ALM_YEA_MASK) +
+ AC100_YEAR_OFF;
+
+ return 0;
+}
+
+static int ac100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+ struct regmap *regmap = chip->regmap;
+ struct rtc_time *alrm_tm = &alrm->time;
+ u16 reg[8];
+ int year;
+ int ret;
+
+ /* our alarm has a limited year range... */
+ year = alrm_tm->tm_year - AC100_YEAR_OFF;
+ if (year < 0 || year > (AC100_YEAR_MAX - 1900)) {
+ dev_err(dev, "alarm only supports year in range %d - %d\n",
+ AC100_YEAR_MIN, AC100_YEAR_MAX);
+ return -EINVAL;
+ }
+
+ /* convert to BCD */
+ reg[0] = (bin2bcd(alrm_tm->tm_sec) & AC100_ALM_SEC_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ reg[1] = (bin2bcd(alrm_tm->tm_min) & AC100_ALM_MIN_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ reg[2] = (bin2bcd(alrm_tm->tm_hour) & AC100_ALM_HOU_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ /* Do not enable weekday alarm */
+ reg[3] = bin2bcd(alrm_tm->tm_wday) & AC100_ALM_WEE_MASK;
+ reg[4] = (bin2bcd(alrm_tm->tm_mday) & AC100_ALM_DAY_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ reg[5] = (bin2bcd(alrm_tm->tm_mon + 1) & AC100_ALM_MON_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ reg[6] = (bin2bcd(year) & AC100_ALM_YEA_MASK) |
+ AC100_ALM_ENABLE_FLAG;
+ /* trigger write */
+ reg[7] = AC100_ALM_UPD_TRIGGER;
+
+ ret = regmap_bulk_write(regmap, AC100_ALM_SEC, reg, 8);
+ if (ret)
+ return ret;
+
+ return ac100_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static irqreturn_t ac100_rtc_irq(int irq, void *data)
+{
+ struct ac100_rtc_dev *chip = data;
+ struct regmap *regmap = chip->regmap;
+ unsigned int val = 0;
+ int ret;
+
+ mutex_lock(&chip->rtc->ops_lock);
+
+ /* read status */
+ ret = regmap_read(regmap, AC100_ALM_INT_STA, &val);
+ if (ret)
+ goto out;
+
+ if (val & AC100_ALM_INT_ENABLE) {
+ /* signal rtc framework */
+ rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+ /* clear status */
+ ret = regmap_write(regmap, AC100_ALM_INT_STA, val);
+ if (ret)
+ goto out;
+
+ /* disable interrupt */
+ ret = ac100_rtc_alarm_irq_enable(chip->dev, 0);
+ if (ret)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&chip->rtc->ops_lock);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops ac100_rtc_ops = {
+ .read_time = ac100_rtc_get_time,
+ .set_time = ac100_rtc_set_time,
+ .read_alarm = ac100_rtc_get_alarm,
+ .set_alarm = ac100_rtc_set_alarm,
+ .alarm_irq_enable = ac100_rtc_alarm_irq_enable,
+};
+
+static int ac100_rtc_probe(struct platform_device *pdev)
+{
+ struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent);
+ struct ac100_rtc_dev *chip;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ platform_set_drvdata(pdev, chip);
+ chip->dev = &pdev->dev;
+ chip->regmap = ac100->regmap;
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return chip->irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL,
+ ac100_rtc_irq,
+ IRQF_SHARED | IRQF_ONESHOT,
+ dev_name(&pdev->dev), chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ /* always use 24 hour mode */
+ regmap_write_bits(chip->regmap, AC100_RTC_CTRL, AC100_RTC_CTRL_24HOUR,
+ AC100_RTC_CTRL_24HOUR);
+
+ /* disable counter alarm interrupt */
+ regmap_write(chip->regmap, AC100_ALM_INT_ENA, 0);
+
+ /* clear counter alarm pending interrupts */
+ regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE);
+
+ chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ac100",
+ &ac100_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ return PTR_ERR(chip->rtc);
+ }
+
+ ret = ac100_rtc_register_clks(chip);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "RTC enabled\n");
+
+ return 0;
+}
+
+static int ac100_rtc_remove(struct platform_device *pdev)
+{
+ struct ac100_rtc_dev *chip = platform_get_drvdata(pdev);
+
+ ac100_rtc_unregister_clks(chip);
+
+ return 0;
+}
+
+static const struct of_device_id ac100_rtc_match[] = {
+ { .compatible = "x-powers,ac100-rtc" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ac100_rtc_match);
+
+static struct platform_driver ac100_rtc_driver = {
+ .probe = ac100_rtc_probe,
+ .remove = ac100_rtc_remove,
+ .driver = {
+ .name = "ac100-rtc",
+ .of_match_table = of_match_ptr(ac100_rtc_match),
+ },
+};
+module_platform_driver(ac100_rtc_driver);
+
+MODULE_DESCRIPTION("X-Powers AC100 RTC driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h
new file mode 100644
index 000000000000..3c148f196b9f
--- /dev/null
+++ b/include/linux/mfd/ac100.h
@@ -0,0 +1,178 @@
+/*
+ * Functions and registers to access AC100 codec / RTC combo IC.
+ *
+ * Copyright (C) 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_AC100_H
+#define __LINUX_MFD_AC100_H
+
+#include <linux/regmap.h>
+
+struct ac100_dev {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+/* Audio codec related registers */
+#define AC100_CHIP_AUDIO_RST 0x00
+#define AC100_PLL_CTRL1 0x01
+#define AC100_PLL_CTRL2 0x02
+#define AC100_SYSCLK_CTRL 0x03
+#define AC100_MOD_CLK_ENA 0x04
+#define AC100_MOD_RST_CTRL 0x05
+#define AC100_I2S_SR_CTRL 0x06
+
+/* I2S1 interface */
+#define AC100_I2S1_CLK_CTRL 0x10
+#define AC100_I2S1_SND_OUT_CTRL 0x11
+#define AC100_I2S1_SND_IN_CTRL 0x12
+#define AC100_I2S1_MXR_SRC 0x13
+#define AC100_I2S1_VOL_CTRL1 0x14
+#define AC100_I2S1_VOL_CTRL2 0x15
+#define AC100_I2S1_VOL_CTRL3 0x16
+#define AC100_I2S1_VOL_CTRL4 0x17
+#define AC100_I2S1_MXR_GAIN 0x18
+
+/* I2S2 interface */
+#define AC100_I2S2_CLK_CTRL 0x20
+#define AC100_I2S2_SND_OUT_CTRL 0x21
+#define AC100_I2S2_SND_IN_CTRL 0x22
+#define AC100_I2S2_MXR_SRC 0x23
+#define AC100_I2S2_VOL_CTRL1 0x24
+#define AC100_I2S2_VOL_CTRL2 0x25
+#define AC100_I2S2_VOL_CTRL3 0x26
+#define AC100_I2S2_VOL_CTRL4 0x27
+#define AC100_I2S2_MXR_GAIN 0x28
+
+/* I2S3 interface */
+#define AC100_I2S3_CLK_CTRL 0x30
+#define AC100_I2S3_SND_OUT_CTRL 0x31
+#define AC100_I2S3_SND_IN_CTRL 0x32
+#define AC100_I2S3_SIG_PATH_CTRL 0x33
+
+/* ADC digital controls */
+#define AC100_ADC_DIG_CTRL 0x40
+#define AC100_ADC_VOL_CTRL 0x41
+
+/* HMIC plug sensing / key detection */
+#define AC100_HMIC_CTRL1 0x44
+#define AC100_HMIC_CTRL2 0x45
+#define AC100_HMIC_STATUS 0x46
+
+/* DAC digital controls */
+#define AC100_DAC_DIG_CTRL 0x48
+#define AC100_DAC_VOL_CTRL 0x49
+#define AC100_DAC_MXR_SRC 0x4c
+#define AC100_DAC_MXR_GAIN 0x4d
+
+/* Analog controls */
+#define AC100_ADC_APC_CTRL 0x50
+#define AC100_ADC_SRC 0x51
+#define AC100_ADC_SRC_BST_CTRL 0x52
+#define AC100_OUT_MXR_DAC_A_CTRL 0x53
+#define AC100_OUT_MXR_SRC 0x54
+#define AC100_OUT_MXR_SRC_BST 0x55
+#define AC100_HPOUT_CTRL 0x56
+#define AC100_ERPOUT_CTRL 0x57
+#define AC100_SPKOUT_CTRL 0x58
+#define AC100_LINEOUT_CTRL 0x59
+
+/* ADC digital audio processing (high pass filter & auto gain control */
+#define AC100_ADC_DAP_L_STA 0x80
+#define AC100_ADC_DAP_R_STA 0x81
+#define AC100_ADC_DAP_L_CTRL 0x82
+#define AC100_ADC_DAP_R_CTRL 0x83
+#define AC100_ADC_DAP_L_T_L 0x84 /* Left Target Level */
+#define AC100_ADC_DAP_R_T_L 0x85 /* Right Target Level */
+#define AC100_ADC_DAP_L_H_A_C 0x86 /* Left High Avg. Coef */
+#define AC100_ADC_DAP_L_L_A_C 0x87 /* Left Low Avg. Coef */
+#define AC100_ADC_DAP_R_H_A_C 0x88 /* Right High Avg. Coef */
+#define AC100_ADC_DAP_R_L_A_C 0x89 /* Right Low Avg. Coef */
+#define AC100_ADC_DAP_L_D_T 0x8a /* Left Decay Time */
+#define AC100_ADC_DAP_L_A_T 0x8b /* Left Attack Time */
+#define AC100_ADC_DAP_R_D_T 0x8c /* Right Decay Time */
+#define AC100_ADC_DAP_R_A_T 0x8d /* Right Attack Time */
+#define AC100_ADC_DAP_N_TH 0x8e /* Noise Threshold */
+#define AC100_ADC_DAP_L_H_N_A_C 0x8f /* Left High Noise Avg. Coef */
+#define AC100_ADC_DAP_L_L_N_A_C 0x90 /* Left Low Noise Avg. Coef */
+#define AC100_ADC_DAP_R_H_N_A_C 0x91 /* Right High Noise Avg. Coef */
+#define AC100_ADC_DAP_R_L_N_A_C 0x92 /* Right Low Noise Avg. Coef */
+#define AC100_ADC_DAP_H_HPF_C 0x93 /* High High-Pass-Filter Coef */
+#define AC100_ADC_DAP_L_HPF_C 0x94 /* Low High-Pass-Filter Coef */
+#define AC100_ADC_DAP_OPT 0x95 /* AGC Optimum */
+
+/* DAC digital audio processing (high pass filter & dynamic range control) */
+#define AC100_DAC_DAP_CTRL 0xa0
+#define AC100_DAC_DAP_H_HPF_C 0xa1 /* High High-Pass-Filter Coef */
+#define AC100_DAC_DAP_L_HPF_C 0xa2 /* Low High-Pass-Filter Coef */
+#define AC100_DAC_DAP_L_H_E_A_C 0xa3 /* Left High Energy Avg Coef */
+#define AC100_DAC_DAP_L_L_E_A_C 0xa4 /* Left Low Energy Avg Coef */
+#define AC100_DAC_DAP_R_H_E_A_C 0xa5 /* Right High Energy Avg Coef */
+#define AC100_DAC_DAP_R_L_E_A_C 0xa6 /* Right Low Energy Avg Coef */
+#define AC100_DAC_DAP_H_G_D_T_C 0xa7 /* High Gain Delay Time Coef */
+#define AC100_DAC_DAP_L_G_D_T_C 0xa8 /* Low Gain Delay Time Coef */
+#define AC100_DAC_DAP_H_G_A_T_C 0xa9 /* High Gain Attack Time Coef */
+#define AC100_DAC_DAP_L_G_A_T_C 0xaa /* Low Gain Attack Time Coef */
+#define AC100_DAC_DAP_H_E_TH 0xab /* High Energy Threshold */
+#define AC100_DAC_DAP_L_E_TH 0xac /* Low Energy Threshold */
+#define AC100_DAC_DAP_H_G_K 0xad /* High Gain K parameter */
+#define AC100_DAC_DAP_L_G_K 0xae /* Low Gain K parameter */
+#define AC100_DAC_DAP_H_G_OFF 0xaf /* High Gain offset */
+#define AC100_DAC_DAP_L_G_OFF 0xb0 /* Low Gain offset */
+#define AC100_DAC_DAP_OPT 0xb1 /* DRC optimum */
+
+/* Digital audio processing enable */
+#define AC100_ADC_DAP_ENA 0xb4
+#define AC100_DAC_DAP_ENA 0xb5
+
+/* SRC control */
+#define AC100_SRC1_CTRL1 0xb8
+#define AC100_SRC1_CTRL2 0xb9
+#define AC100_SRC1_CTRL3 0xba
+#define AC100_SRC1_CTRL4 0xbb
+#define AC100_SRC2_CTRL1 0xbc
+#define AC100_SRC2_CTRL2 0xbd
+#define AC100_SRC2_CTRL3 0xbe
+#define AC100_SRC2_CTRL4 0xbf
+
+/* RTC clk control */
+#define AC100_CLK32K_ANALOG_CTRL 0xc0
+#define AC100_CLKOUT_CTRL1 0xc1
+#define AC100_CLKOUT_CTRL2 0xc2
+#define AC100_CLKOUT_CTRL3 0xc3
+
+/* RTC module */
+#define AC100_RTC_RST 0xc6
+#define AC100_RTC_CTRL 0xc7
+#define AC100_RTC_SEC 0xc8 /* second */
+#define AC100_RTC_MIN 0xc9 /* minute */
+#define AC100_RTC_HOU 0xca /* hour */
+#define AC100_RTC_WEE 0xcb /* weekday */
+#define AC100_RTC_DAY 0xcc /* day */
+#define AC100_RTC_MON 0xcd /* month */
+#define AC100_RTC_YEA 0xce /* year */
+#define AC100_RTC_UPD 0xcf /* update trigger */
+
+/* RTC alarm */
+#define AC100_ALM_INT_ENA 0xd0
+#define AC100_ALM_INT_STA 0xd1
+#define AC100_ALM_SEC 0xd8
+#define AC100_ALM_MIN 0xd9
+#define AC100_ALM_HOU 0xda
+#define AC100_ALM_WEE 0xdb
+#define AC100_ALM_DAY 0xdc
+#define AC100_ALM_MON 0xdd
+#define AC100_ALM_YEA 0xde
+#define AC100_ALM_UPD 0xdf
+
+/* RTC general purpose register 0 ~ 15 */
+#define AC100_RTC_GP(x) (0xe0 + (x))
+
+#endif /* __LINUX_MFD_AC100_H */
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
index 58ab4c0fe761..b9909bb0642b 100644
--- a/include/linux/mfd/arizona/core.h
+++ b/include/linux/mfd/arizona/core.h
@@ -13,6 +13,7 @@
#ifndef _WM_ARIZONA_CORE_H
#define _WM_ARIZONA_CORE_H
+#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/regmap.h>
@@ -21,6 +22,12 @@
#define ARIZONA_MAX_CORE_SUPPLIES 2
+enum {
+ ARIZONA_MCLK1,
+ ARIZONA_MCLK2,
+ ARIZONA_NUM_MCLK
+};
+
enum arizona_type {
WM5102 = 1,
WM5110 = 2,
@@ -139,6 +146,8 @@ struct arizona {
struct mutex clk_lock;
int clk32k_ref;
+ struct clk *mclk[ARIZONA_NUM_MCLK];
+
bool ctrlif_error;
struct snd_soc_dapm_context *dapm;
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 0be4982f08fe..fec597fb34cb 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -20,6 +20,7 @@ enum {
AXP221_ID,
AXP223_ID,
AXP288_ID,
+ AXP806_ID,
AXP809_ID,
NR_AXP20X_VARIANTS,
};
@@ -91,6 +92,30 @@ enum {
#define AXP22X_ALDO3_V_OUT 0x2a
#define AXP22X_CHRG_CTRL3 0x35
+#define AXP806_STARTUP_SRC 0x00
+#define AXP806_CHIP_ID 0x03
+#define AXP806_PWR_OUT_CTRL1 0x10
+#define AXP806_PWR_OUT_CTRL2 0x11
+#define AXP806_DCDCA_V_CTRL 0x12
+#define AXP806_DCDCB_V_CTRL 0x13
+#define AXP806_DCDCC_V_CTRL 0x14
+#define AXP806_DCDCD_V_CTRL 0x15
+#define AXP806_DCDCE_V_CTRL 0x16
+#define AXP806_ALDO1_V_CTRL 0x17
+#define AXP806_ALDO2_V_CTRL 0x18
+#define AXP806_ALDO3_V_CTRL 0x19
+#define AXP806_DCDC_MODE_CTRL1 0x1a
+#define AXP806_DCDC_MODE_CTRL2 0x1b
+#define AXP806_DCDC_FREQ_CTRL 0x1c
+#define AXP806_BLDO1_V_CTRL 0x20
+#define AXP806_BLDO2_V_CTRL 0x21
+#define AXP806_BLDO3_V_CTRL 0x22
+#define AXP806_BLDO4_V_CTRL 0x23
+#define AXP806_CLDO1_V_CTRL 0x24
+#define AXP806_CLDO2_V_CTRL 0x25
+#define AXP806_CLDO3_V_CTRL 0x26
+#define AXP806_VREF_TEMP_WARN_L 0xf3
+
/* Interrupt */
#define AXP152_IRQ1_EN 0x40
#define AXP152_IRQ2_EN 0x41
@@ -266,6 +291,26 @@ enum {
};
enum {
+ AXP806_DCDCA = 0,
+ AXP806_DCDCB,
+ AXP806_DCDCC,
+ AXP806_DCDCD,
+ AXP806_DCDCE,
+ AXP806_ALDO1,
+ AXP806_ALDO2,
+ AXP806_ALDO3,
+ AXP806_BLDO1,
+ AXP806_BLDO2,
+ AXP806_BLDO3,
+ AXP806_BLDO4,
+ AXP806_CLDO1,
+ AXP806_CLDO2,
+ AXP806_CLDO3,
+ AXP806_SW,
+ AXP806_REG_ID_MAX,
+};
+
+enum {
AXP809_DCDC1 = 0,
AXP809_DCDC2,
AXP809_DCDC3,
@@ -414,6 +459,21 @@ enum axp288_irqs {
AXP288_IRQ_BC_USB_CHNG,
};
+enum axp806_irqs {
+ AXP806_IRQ_DIE_TEMP_HIGH_LV1,
+ AXP806_IRQ_DIE_TEMP_HIGH_LV2,
+ AXP806_IRQ_DCDCA_V_LOW,
+ AXP806_IRQ_DCDCB_V_LOW,
+ AXP806_IRQ_DCDCC_V_LOW,
+ AXP806_IRQ_DCDCD_V_LOW,
+ AXP806_IRQ_DCDCE_V_LOW,
+ AXP806_IRQ_PWROK_LONG,
+ AXP806_IRQ_PWROK_SHORT,
+ AXP806_IRQ_WAKEUP,
+ AXP806_IRQ_PWROK_FALL,
+ AXP806_IRQ_PWROK_RISE,
+};
+
enum axp809_irqs {
AXP809_IRQ_ACIN_OVER_V = 1,
AXP809_IRQ_ACIN_PLUGIN,
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index d641a18abacb..76f7ef4d3a0d 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -109,6 +109,10 @@ struct cros_ec_command {
* should check msg.result for the EC's result code.
* @pkt_xfer: send packet to EC and get response
* @lock: one transaction at a time
+ * @mkbp_event_supported: true if this EC supports the MKBP event protocol.
+ * @event_notifier: interrupt event notifier for transport devices.
+ * @event_data: raw payload transferred with the MKBP event.
+ * @event_size: size in bytes of the event data.
*/
struct cros_ec_device {
@@ -137,6 +141,11 @@ struct cros_ec_device {
int (*pkt_xfer)(struct cros_ec_device *ec,
struct cros_ec_command *msg);
struct mutex lock;
+ bool mkbp_event_supported;
+ struct blocking_notifier_head event_notifier;
+
+ struct ec_response_get_next_event event_data;
+ int event_size;
};
/* struct cros_ec_platform - ChromeOS EC platform information
@@ -269,6 +278,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev);
*/
int cros_ec_query_all(struct cros_ec_device *ec_dev);
+/**
+ * cros_ec_get_next_event - Fetch next event from the ChromeOS EC
+ *
+ * @ec_dev: Device to fetch event from
+ *
+ * Returns: 0 on success, Linux error number on failure
+ */
+int cros_ec_get_next_event(struct cros_ec_device *ec_dev);
+
/* sysfs stuff */
extern struct attribute_group cros_ec_attr_group;
extern struct attribute_group cros_ec_lightbar_attr_group;
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 7e7a8d4b4551..76728ff37d01 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -1793,6 +1793,40 @@ struct ec_result_keyscan_seq_ctrl {
};
} __packed;
+/*
+ * Command for retrieving the next pending MKBP event from the EC device
+ *
+ * The device replies with UNAVAILABLE if there aren't any pending events.
+ */
+#define EC_CMD_GET_NEXT_EVENT 0x67
+
+enum ec_mkbp_event {
+ /* Keyboard matrix changed. The event data is the new matrix state. */
+ EC_MKBP_EVENT_KEY_MATRIX = 0,
+
+ /* New host event. The event data is 4 bytes of host event flags. */
+ EC_MKBP_EVENT_HOST_EVENT = 1,
+
+ /* New Sensor FIFO data. The event data is fifo_info structure. */
+ EC_MKBP_EVENT_SENSOR_FIFO = 2,
+
+ /* Number of MKBP events */
+ EC_MKBP_EVENT_COUNT,
+};
+
+union ec_response_get_next_data {
+ uint8_t key_matrix[13];
+
+ /* Unaligned */
+ uint32_t host_event;
+} __packed;
+
+struct ec_response_get_next_event {
+ uint8_t event_type;
+ /* Followed by event data if any */
+ union ec_response_get_next_data data;
+} __packed;
+
/*****************************************************************************/
/* Temperature sensor commands */
diff --git a/include/linux/mfd/lp873x.h b/include/linux/mfd/lp873x.h
new file mode 100644
index 000000000000..83b1bd7588be
--- /dev/null
+++ b/include/linux/mfd/lp873x.h
@@ -0,0 +1,269 @@
+/*
+ * Functions to access LP873X power management chip.
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_MFD_LP873X_H
+#define __LINUX_MFD_LP873X_H
+
+#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/* LP873x chip id list */
+#define LP873X 0x00
+
+/* All register addresses */
+#define LP873X_REG_DEV_REV 0X00
+#define LP873X_REG_OTP_REV 0X01
+#define LP873X_REG_BUCK0_CTRL_1 0X02
+#define LP873X_REG_BUCK0_CTRL_2 0X03
+#define LP873X_REG_BUCK1_CTRL_1 0X04
+#define LP873X_REG_BUCK1_CTRL_2 0X05
+#define LP873X_REG_BUCK0_VOUT 0X06
+#define LP873X_REG_BUCK1_VOUT 0X07
+#define LP873X_REG_LDO0_CTRL 0X08
+#define LP873X_REG_LDO1_CTRL 0X09
+#define LP873X_REG_LDO0_VOUT 0X0A
+#define LP873X_REG_LDO1_VOUT 0X0B
+#define LP873X_REG_BUCK0_DELAY 0X0C
+#define LP873X_REG_BUCK1_DELAY 0X0D
+#define LP873X_REG_LDO0_DELAY 0X0E
+#define LP873X_REG_LDO1_DELAY 0X0F
+#define LP873X_REG_GPO_DELAY 0X10
+#define LP873X_REG_GPO2_DELAY 0X11
+#define LP873X_REG_GPO_CTRL 0X12
+#define LP873X_REG_CONFIG 0X13
+#define LP873X_REG_PLL_CTRL 0X14
+#define LP873X_REG_PGOOD_CTRL1 0X15
+#define LP873X_REG_PGOOD_CTRL2 0X16
+#define LP873X_REG_PG_FAULT 0X17
+#define LP873X_REG_RESET 0X18
+#define LP873X_REG_INT_TOP_1 0X19
+#define LP873X_REG_INT_TOP_2 0X1A
+#define LP873X_REG_INT_BUCK 0X1B
+#define LP873X_REG_INT_LDO 0X1C
+#define LP873X_REG_TOP_STAT 0X1D
+#define LP873X_REG_BUCK_STAT 0X1E
+#define LP873X_REG_LDO_STAT 0x1F
+#define LP873X_REG_TOP_MASK_1 0x20
+#define LP873X_REG_TOP_MASK_2 0x21
+#define LP873X_REG_BUCK_MASK 0x22
+#define LP873X_REG_LDO_MASK 0x23
+#define LP873X_REG_SEL_I_LOAD 0x24
+#define LP873X_REG_I_LOAD_2 0x25
+#define LP873X_REG_I_LOAD_1 0x26
+
+#define LP873X_REG_MAX LP873X_REG_I_LOAD_1
+
+/* Register field definitions */
+#define LP873X_DEV_REV_DEV_ID 0xC0
+#define LP873X_DEV_REV_ALL_LAYER 0x30
+#define LP873X_DEV_REV_METAL_LAYER 0x0F
+
+#define LP873X_OTP_REV_OTP_ID 0xFF
+
+#define LP873X_BUCK0_CTRL_1_BUCK0_FPWM BIT(3)
+#define LP873X_BUCK0_CTRL_1_BUCK0_RDIS_EN BIT(2)
+#define LP873X_BUCK0_CTRL_1_BUCK0_EN_PIN_CTRL BIT(1)
+#define LP873X_BUCK0_CTRL_1_BUCK0_EN BIT(0)
+
+#define LP873X_BUCK0_CTRL_2_BUCK0_ILIM 0x38
+#define LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE 0x07
+
+#define LP873X_BUCK1_CTRL_1_BUCK1_FPWM BIT(3)
+#define LP873X_BUCK1_CTRL_1_BUCK1_RDIS_EN BIT(2)
+#define LP873X_BUCK1_CTRL_1_BUCK1_EN_PIN_CTRL BIT(1)
+#define LP873X_BUCK1_CTRL_1_BUCK1_EN BIT(0)
+
+#define LP873X_BUCK1_CTRL_2_BUCK1_ILIM 0x38
+#define LP873X_BUCK1_CTRL_2_BUCK1_SLEW_RATE 0x07
+
+#define LP873X_BUCK0_VOUT_BUCK0_VSET 0xFF
+
+#define LP873X_BUCK1_VOUT_BUCK1_VSET 0xFF
+
+#define LP873X_LDO0_CTRL_LDO0_RDIS_EN BIT(2)
+#define LP873X_LDO0_CTRL_LDO0_EN_PIN_CTRL BIT(1)
+#define LP873X_LDO0_CTRL_LDO0_EN BIT(0)
+
+#define LP873X_LDO1_CTRL_LDO1_RDIS_EN BIT(2)
+#define LP873X_LDO1_CTRL_LDO1_EN_PIN_CTRL BIT(1)
+#define LP873X_LDO1_CTRL_LDO1_EN BIT(0)
+
+#define LP873X_LDO0_VOUT_LDO0_VSET 0x1F
+
+#define LP873X_LDO1_VOUT_LDO1_VSET 0x1F
+
+#define LP873X_BUCK0_DELAY_BUCK0_SD_DELAY 0xF0
+#define LP873X_BUCK0_DELAY_BUCK0_SU_DELAY 0x0F
+
+#define LP873X_BUCK1_DELAY_BUCK1_SD_DELAY 0xF0
+#define LP873X_BUCK1_DELAY_BUCK1_SU_DELAY 0x0F
+
+#define LP873X_LDO0_DELAY_LDO0_SD_DELAY 0xF0
+#define LP873X_LDO0_DELAY_LDO0_SU_DELAY 0x0F
+
+#define LP873X_LDO1_DELAY_LDO1_SD_DELAY 0xF0
+#define LP873X_LDO1_DELAY_LDO1_SU_DELAY 0x0F
+
+#define LP873X_GPO_DELAY_GPO_SD_DELAY 0xF0
+#define LP873X_GPO_DELAY_GPO_SU_DELAY 0x0F
+
+#define LP873X_GPO2_DELAY_GPO2_SD_DELAY 0xF0
+#define LP873X_GPO2_DELAY_GPO2_SU_DELAY 0x0F
+
+#define LP873X_GPO_CTRL_GPO2_OD BIT(6)
+#define LP873X_GPO_CTRL_GPO2_EN_PIN_CTRL BIT(5)
+#define LP873X_GPO_CTRL_GPO2_EN BIT(4)
+#define LP873X_GPO_CTRL_GPO_OD BIT(2)
+#define LP873X_GPO_CTRL_GPO_EN_PIN_CTRL BIT(1)
+#define LP873X_GPO_CTRL_GPO_EN BIT(0)
+
+#define LP873X_CONFIG_SU_DELAY_SEL BIT(6)
+#define LP873X_CONFIG_SD_DELAY_SEL BIT(5)
+#define LP873X_CONFIG_CLKIN_PIN_SEL BIT(4)
+#define LP873X_CONFIG_CLKIN_PD BIT(3)
+#define LP873X_CONFIG_EN_PD BIT(2)
+#define LP873X_CONFIG_TDIE_WARN_LEVEL BIT(1)
+#define LP873X_EN_SPREAD_SPEC BIT(0)
+
+#define LP873X_PLL_CTRL_EN_PLL BIT(6)
+#define LP873X_EXT_CLK_FREQ 0x1F
+
+#define LP873X_PGOOD_CTRL1_PGOOD_POL BIT(7)
+#define LP873X_PGOOD_CTRL1_PGOOD_OD BIT(6)
+#define LP873X_PGOOD_CTRL1_PGOOD_WINDOW_LDO BIT(5)
+#define LP873X_PGOOD_CTRL1_PGOOD_WINDOWN_BUCK BIT(4)
+#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_LDO1 BIT(3)
+#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_LDO0 BIT(2)
+#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_BUCK1 BIT(1)
+#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_BUCK0 BIT(0)
+
+#define LP873X_PGOOD_CTRL2_EN_PGOOD_TWARN BIT(2)
+#define LP873X_PGOOD_CTRL2_EN_PG_FAULT_GATE BIT(1)
+#define LP873X_PGOOD_CTRL2_PGOOD_MODE BIT(0)
+
+#define LP873X_PG_FAULT_PG_FAULT_LDO1 BIT(3)
+#define LP873X_PG_FAULT_PG_FAULT_LDO0 BIT(2)
+#define LP873X_PG_FAULT_PG_FAULT_BUCK1 BIT(1)
+#define LP873X_PG_FAULT_PG_FAULT_BUCK0 BIT(0)
+
+#define LP873X_RESET_SW_RESET BIT(0)
+
+#define LP873X_INT_TOP_1_PGOOD_INT BIT(7)
+#define LP873X_INT_TOP_1_LDO_INT BIT(6)
+#define LP873X_INT_TOP_1_BUCK_INT BIT(5)
+#define LP873X_INT_TOP_1_SYNC_CLK_INT BIT(4)
+#define LP873X_INT_TOP_1_TDIE_SD_INT BIT(3)
+#define LP873X_INT_TOP_1_TDIE_WARN_INT BIT(2)
+#define LP873X_INT_TOP_1_OVP_INT BIT(1)
+#define LP873X_INT_TOP_1_I_MEAS_INT BIT(0)
+
+#define LP873X_INT_TOP_2_RESET_REG_INT BIT(0)
+
+#define LP873X_INT_BUCK_BUCK1_PG_INT BIT(6)
+#define LP873X_INT_BUCK_BUCK1_SC_INT BIT(5)
+#define LP873X_INT_BUCK_BUCK1_ILIM_INT BIT(4)
+#define LP873X_INT_BUCK_BUCK0_PG_INT BIT(2)
+#define LP873X_INT_BUCK_BUCK0_SC_INT BIT(1)
+#define LP873X_INT_BUCK_BUCK0_ILIM_INT BIT(0)
+
+#define LP873X_INT_LDO_LDO1_PG_INT BIT(6)
+#define LP873X_INT_LDO_LDO1_SC_INT BIT(5)
+#define LP873X_INT_LDO_LDO1_ILIM_INT BIT(4)
+#define LP873X_INT_LDO_LDO0_PG_INT BIT(2)
+#define LP873X_INT_LDO_LDO0_SC_INT BIT(1)
+#define LP873X_INT_LDO_LDO0_ILIM_INT BIT(0)
+
+#define LP873X_TOP_STAT_PGOOD_STAT BIT(7)
+#define LP873X_TOP_STAT_SYNC_CLK_STAT BIT(4)
+#define LP873X_TOP_STAT_TDIE_SD_STAT BIT(3)
+#define LP873X_TOP_STAT_TDIE_WARN_STAT BIT(2)
+#define LP873X_TOP_STAT_OVP_STAT BIT(1)
+
+#define LP873X_BUCK_STAT_BUCK1_STAT BIT(7)
+#define LP873X_BUCK_STAT_BUCK1_PG_STAT BIT(6)
+#define LP873X_BUCK_STAT_BUCK1_ILIM_STAT BIT(4)
+#define LP873X_BUCK_STAT_BUCK0_STAT BIT(3)
+#define LP873X_BUCK_STAT_BUCK0_PG_STAT BIT(2)
+#define LP873X_BUCK_STAT_BUCK0_ILIM_STAT BIT(0)
+
+#define LP873X_LDO_STAT_LDO1_STAT BIT(7)
+#define LP873X_LDO_STAT_LDO1_PG_STAT BIT(6)
+#define LP873X_LDO_STAT_LDO1_ILIM_STAT BIT(4)
+#define LP873X_LDO_STAT_LDO0_STAT BIT(3)
+#define LP873X_LDO_STAT_LDO0_PG_STAT BIT(2)
+#define LP873X_LDO_STAT_LDO0_ILIM_STAT BIT(0)
+
+#define LP873X_TOP_MASK_1_PGOOD_INT_MASK BIT(7)
+#define LP873X_TOP_MASK_1_SYNC_CLK_MASK BIT(4)
+#define LP873X_TOP_MASK_1_TDIE_WARN_MASK BIT(2)
+#define LP873X_TOP_MASK_1_I_MEAS_MASK BIT(0)
+
+#define LP873X_TOP_MASK_2_RESET_REG_MASK BIT(0)
+
+#define LP873X_BUCK_MASK_BUCK1_PGF_MASK BIT(7)
+#define LP873X_BUCK_MASK_BUCK1_PGR_MASK BIT(6)
+#define LP873X_BUCK_MASK_BUCK1_ILIM_MASK BIT(4)
+#define LP873X_BUCK_MASK_BUCK0_PGF_MASK BIT(3)
+#define LP873X_BUCK_MASK_BUCK0_PGR_MASK BIT(2)
+#define LP873X_BUCK_MASK_BUCK0_ILIM_MASK BIT(0)
+
+#define LP873X_LDO_MASK_LDO1_PGF_MASK BIT(7)
+#define LP873X_LDO_MASK_LDO1_PGR_MASK BIT(6)
+#define LP873X_LDO_MASK_LDO1_ILIM_MASK BIT(4)
+#define LP873X_LDO_MASK_LDO0_PGF_MASK BIT(3)
+#define LP873X_LDO_MASK_LDO0_PGR_MASK BIT(2)
+#define LP873X_LDO_MASK_LDO0_ILIM_MASK BIT(0)
+
+#define LP873X_SEL_I_LOAD_CURRENT_BUCK_SELECT BIT(0)
+
+#define LP873X_I_LOAD_2_BUCK_LOAD_CURRENT BIT(0)
+
+#define LP873X_I_LOAD_1_BUCK_LOAD_CURRENT 0xFF
+
+#define LP873X_MAX_REG_ID LP873X_LDO_1
+
+/* Number of step-down converters available */
+#define LP873X_NUM_BUCK 2
+/* Number of LDO voltage regulators available */
+#define LP873X_NUM_LDO 2
+/* Number of total regulators available */
+#define LP873X_NUM_REGULATOR (LP873X_NUM_BUCK + LP873X_NUM_LDO)
+
+enum lp873x_regulator_id {
+ /* BUCK's */
+ LP873X_BUCK_0,
+ LP873X_BUCK_1,
+ /* LDOs */
+ LP873X_LDO_0,
+ LP873X_LDO_1,
+};
+
+/**
+ * struct lp873x - state holder for the lp873x driver
+ * @dev: struct device pointer for MFD device
+ * @rev: revision of the lp873x
+ * @lock: lock guarding the data structure
+ * @regmap: register map of the lp873x PMIC
+ *
+ * Device data may be used to access the LP873X chip
+ */
+struct lp873x {
+ struct device *dev;
+ u8 rev;
+ struct mutex lock; /* lock guarding the data structure */
+ struct regmap *regmap;
+};
+#endif /* __LINUX_MFD_LP873X_H */
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index 441b6ee72691..fc5db6fcb57d 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -1,11 +1,15 @@
/*
- * rk808.h for Rockchip RK808
+ * Register definitions for Rockchip's RK808/RK818 PMIC
*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Chris Zhong <zyw@rock-chips.com>
* Author: Zhang Qing <zhangqing@rock-chips.com>
*
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -16,8 +20,8 @@
* more details.
*/
-#ifndef __LINUX_REGULATOR_rk808_H
-#define __LINUX_REGULATOR_rk808_H
+#ifndef __LINUX_REGULATOR_RK808_H
+#define __LINUX_REGULATOR_RK808_H
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
@@ -28,7 +32,7 @@
#define RK808_DCDC1 0 /* (0+RK808_START) */
#define RK808_LDO1 4 /* (4+RK808_START) */
-#define RK808_NUM_REGULATORS 14
+#define RK808_NUM_REGULATORS 14
enum rk808_reg {
RK808_ID_DCDC1,
@@ -65,6 +69,8 @@ enum rk808_reg {
#define RK808_RTC_INT_REG 0x12
#define RK808_RTC_COMP_LSB_REG 0x13
#define RK808_RTC_COMP_MSB_REG 0x14
+#define RK808_ID_MSB 0x17
+#define RK808_ID_LSB 0x18
#define RK808_CLK32OUT_REG 0x20
#define RK808_VB_MON_REG 0x21
#define RK808_THERMAL_REG 0x22
@@ -115,7 +121,92 @@ enum rk808_reg {
#define RK808_INT_STS_MSK_REG2 0x4f
#define RK808_IO_POL_REG 0x50
-/* IRQ Definitions */
+/* RK818 */
+#define RK818_DCDC1 0
+#define RK818_LDO1 4
+#define RK818_NUM_REGULATORS 17
+
+enum rk818_reg {
+ RK818_ID_DCDC1,
+ RK818_ID_DCDC2,
+ RK818_ID_DCDC3,
+ RK818_ID_DCDC4,
+ RK818_ID_BOOST,
+ RK818_ID_LDO1,
+ RK818_ID_LDO2,
+ RK818_ID_LDO3,
+ RK818_ID_LDO4,
+ RK818_ID_LDO5,
+ RK818_ID_LDO6,
+ RK818_ID_LDO7,
+ RK818_ID_LDO8,
+ RK818_ID_LDO9,
+ RK818_ID_SWITCH,
+ RK818_ID_HDMI_SWITCH,
+ RK818_ID_OTG_SWITCH,
+};
+
+#define RK818_DCDC_EN_REG 0x23
+#define RK818_LDO_EN_REG 0x24
+#define RK818_SLEEP_SET_OFF_REG1 0x25
+#define RK818_SLEEP_SET_OFF_REG2 0x26
+#define RK818_DCDC_UV_STS_REG 0x27
+#define RK818_DCDC_UV_ACT_REG 0x28
+#define RK818_LDO_UV_STS_REG 0x29
+#define RK818_LDO_UV_ACT_REG 0x2a
+#define RK818_DCDC_PG_REG 0x2b
+#define RK818_LDO_PG_REG 0x2c
+#define RK818_VOUT_MON_TDB_REG 0x2d
+#define RK818_BUCK1_CONFIG_REG 0x2e
+#define RK818_BUCK1_ON_VSEL_REG 0x2f
+#define RK818_BUCK1_SLP_VSEL_REG 0x30
+#define RK818_BUCK2_CONFIG_REG 0x32
+#define RK818_BUCK2_ON_VSEL_REG 0x33
+#define RK818_BUCK2_SLP_VSEL_REG 0x34
+#define RK818_BUCK3_CONFIG_REG 0x36
+#define RK818_BUCK4_CONFIG_REG 0x37
+#define RK818_BUCK4_ON_VSEL_REG 0x38
+#define RK818_BUCK4_SLP_VSEL_REG 0x39
+#define RK818_BOOST_CONFIG_REG 0x3a
+#define RK818_LDO1_ON_VSEL_REG 0x3b
+#define RK818_LDO1_SLP_VSEL_REG 0x3c
+#define RK818_LDO2_ON_VSEL_REG 0x3d
+#define RK818_LDO2_SLP_VSEL_REG 0x3e
+#define RK818_LDO3_ON_VSEL_REG 0x3f
+#define RK818_LDO3_SLP_VSEL_REG 0x40
+#define RK818_LDO4_ON_VSEL_REG 0x41
+#define RK818_LDO4_SLP_VSEL_REG 0x42
+#define RK818_LDO5_ON_VSEL_REG 0x43
+#define RK818_LDO5_SLP_VSEL_REG 0x44
+#define RK818_LDO6_ON_VSEL_REG 0x45
+#define RK818_LDO6_SLP_VSEL_REG 0x46
+#define RK818_LDO7_ON_VSEL_REG 0x47
+#define RK818_LDO7_SLP_VSEL_REG 0x48
+#define RK818_LDO8_ON_VSEL_REG 0x49
+#define RK818_LDO8_SLP_VSEL_REG 0x4a
+#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54
+#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55
+#define RK818_DEVCTRL_REG 0x4b
+#define RK818_INT_STS_REG1 0X4c
+#define RK818_INT_STS_MSK_REG1 0x4d
+#define RK818_INT_STS_REG2 0x4e
+#define RK818_INT_STS_MSK_REG2 0x4f
+#define RK818_IO_POL_REG 0x50
+#define RK818_H5V_EN_REG 0x52
+#define RK818_SLEEP_SET_OFF_REG3 0x53
+#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54
+#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55
+#define RK818_BOOST_CTRL_REG 0x56
+#define RK818_DCDC_ILMAX 0x90
+#define RK818_USB_CTRL_REG 0xa1
+
+#define RK818_H5V_EN BIT(0)
+#define RK818_REF_RDY_CTRL BIT(1)
+#define RK818_USB_ILIM_SEL_MASK 0xf
+#define RK818_USB_ILMIN_2000MA 0x7
+#define RK818_USB_CHG_SD_VSEL_MASK 0x70
+
+/* RK808 IRQ Definitions */
#define RK808_IRQ_VOUT_LO 0
#define RK808_IRQ_VB_LO 1
#define RK808_IRQ_PWRON 2
@@ -137,6 +228,43 @@ enum rk808_reg {
#define RK808_IRQ_PLUG_IN_INT_MSK BIT(0)
#define RK808_IRQ_PLUG_OUT_INT_MSK BIT(1)
+/* RK818 IRQ Definitions */
+#define RK818_IRQ_VOUT_LO 0
+#define RK818_IRQ_VB_LO 1
+#define RK818_IRQ_PWRON 2
+#define RK818_IRQ_PWRON_LP 3
+#define RK818_IRQ_HOTDIE 4
+#define RK818_IRQ_RTC_ALARM 5
+#define RK818_IRQ_RTC_PERIOD 6
+#define RK818_IRQ_USB_OV 7
+#define RK818_IRQ_PLUG_IN 8
+#define RK818_IRQ_PLUG_OUT 9
+#define RK818_IRQ_CHG_OK 10
+#define RK818_IRQ_CHG_TE 11
+#define RK818_IRQ_CHG_TS1 12
+#define RK818_IRQ_TS2 13
+#define RK818_IRQ_CHG_CVTLIM 14
+#define RK818_IRQ_DISCHG_ILIM 7
+
+#define RK818_IRQ_VOUT_LO_MSK BIT(0)
+#define RK818_IRQ_VB_LO_MSK BIT(1)
+#define RK818_IRQ_PWRON_MSK BIT(2)
+#define RK818_IRQ_PWRON_LP_MSK BIT(3)
+#define RK818_IRQ_HOTDIE_MSK BIT(4)
+#define RK818_IRQ_RTC_ALARM_MSK BIT(5)
+#define RK818_IRQ_RTC_PERIOD_MSK BIT(6)
+#define RK818_IRQ_USB_OV_MSK BIT(7)
+#define RK818_IRQ_PLUG_IN_MSK BIT(0)
+#define RK818_IRQ_PLUG_OUT_MSK BIT(1)
+#define RK818_IRQ_CHG_OK_MSK BIT(2)
+#define RK818_IRQ_CHG_TE_MSK BIT(3)
+#define RK818_IRQ_CHG_TS1_MSK BIT(4)
+#define RK818_IRQ_TS2_MSK BIT(5)
+#define RK818_IRQ_CHG_CVTLIM_MSK BIT(6)
+#define RK818_IRQ_DISCHG_ILIM_MSK BIT(7)
+
+#define RK818_NUM_IRQ 16
+
#define RK808_VBAT_LOW_2V8 0x00
#define RK808_VBAT_LOW_2V9 0x01
#define RK808_VBAT_LOW_3V0 0x02
@@ -191,9 +319,17 @@ enum {
BOOST_ILMIN_250MA,
};
+enum {
+ RK808_ID = 0x0000,
+ RK818_ID = 0x8181,
+};
+
struct rk808 {
- struct i2c_client *i2c;
- struct regmap_irq_chip_data *irq_data;
- struct regmap *regmap;
+ struct i2c_client *i2c;
+ struct regmap_irq_chip_data *irq_data;
+ struct regmap *regmap;
+ long variant;
+ const struct regmap_config *regmap_cfg;
+ const struct regmap_irq_chip *regmap_irq_chip;
};
-#endif /* __LINUX_REGULATOR_rk808_H */
+#endif /* __LINUX_REGULATOR_RK808_H */
diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h
index de748bc7525e..4a827af17e59 100644
--- a/include/linux/mfd/stmpe.h
+++ b/include/linux/mfd/stmpe.h
@@ -26,6 +26,7 @@ enum stmpe_partnum {
STMPE610,
STMPE801,
STMPE811,
+ STMPE1600,
STMPE1601,
STMPE1801,
STMPE2401,
@@ -39,22 +40,42 @@ enum stmpe_partnum {
*/
enum {
STMPE_IDX_CHIP_ID,
+ STMPE_IDX_SYS_CTRL,
+ STMPE_IDX_SYS_CTRL2,
STMPE_IDX_ICR_LSB,
STMPE_IDX_IER_LSB,
+ STMPE_IDX_IER_MSB,
STMPE_IDX_ISR_LSB,
STMPE_IDX_ISR_MSB,
STMPE_IDX_GPMR_LSB,
+ STMPE_IDX_GPMR_CSB,
+ STMPE_IDX_GPMR_MSB,
STMPE_IDX_GPSR_LSB,
+ STMPE_IDX_GPSR_CSB,
+ STMPE_IDX_GPSR_MSB,
STMPE_IDX_GPCR_LSB,
+ STMPE_IDX_GPCR_CSB,
+ STMPE_IDX_GPCR_MSB,
STMPE_IDX_GPDR_LSB,
+ STMPE_IDX_GPDR_CSB,
+ STMPE_IDX_GPDR_MSB,
+ STMPE_IDX_GPEDR_LSB,
+ STMPE_IDX_GPEDR_CSB,
STMPE_IDX_GPEDR_MSB,
STMPE_IDX_GPRER_LSB,
+ STMPE_IDX_GPRER_CSB,
+ STMPE_IDX_GPRER_MSB,
STMPE_IDX_GPFER_LSB,
+ STMPE_IDX_GPFER_CSB,
+ STMPE_IDX_GPFER_MSB,
STMPE_IDX_GPPUR_LSB,
STMPE_IDX_GPPDR_LSB,
STMPE_IDX_GPAFR_U_MSB,
STMPE_IDX_IEGPIOR_LSB,
+ STMPE_IDX_IEGPIOR_CSB,
+ STMPE_IDX_IEGPIOR_MSB,
STMPE_IDX_ISGPIOR_LSB,
+ STMPE_IDX_ISGPIOR_CSB,
STMPE_IDX_ISGPIOR_MSB,
STMPE_IDX_MAX,
};