Merge branches 'ib-mfd-gpio-4.9', 'ib-mfd-gpio-regulator-4.9', 'ib-mfd-input-4.9...
authorLee Jones <lee.jones@linaro.org>
Tue, 4 Oct 2016 14:47:01 +0000 (15:47 +0100)
committerLee Jones <lee.jones@linaro.org>
Tue, 4 Oct 2016 14:47:01 +0000 (15:47 +0100)
39 files changed:
Documentation/devicetree/bindings/mfd/ac100.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/axp20x.txt
Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
Documentation/devicetree/bindings/mfd/qcom-rpm.txt
Documentation/devicetree/bindings/mfd/stmpe.txt
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-lp873x.c [new file with mode: 0644]
drivers/gpio/gpio-stmpe.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ac100.c [new file with mode: 0644]
drivers/mfd/arizona-core.c
drivers/mfd/axp20x-rsb.c
drivers/mfd/axp20x.c
drivers/mfd/cros_ec.c
drivers/mfd/lp873x.c [new file with mode: 0644]
drivers/mfd/pm8921-core.c
drivers/mfd/qcom_rpm.c
drivers/mfd/stmpe-i2c.c
drivers/mfd/stmpe.c
drivers/mfd/stmpe.h
drivers/platform/chrome/cros_ec_proto.c
drivers/regulator/Kconfig
drivers/regulator/axp20x-regulator.c
drivers/regulator/qcom_rpm-regulator.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ac100.c [new file with mode: 0644]
drivers/rtc/rtc-pm8xxx.c
include/dt-bindings/mfd/qcom-rpm.h
include/linux/mfd/ac100.h [new file with mode: 0644]
include/linux/mfd/arizona/core.h
include/linux/mfd/axp20x.h
include/linux/mfd/cros_ec.h
include/linux/mfd/cros_ec_commands.h
include/linux/mfd/lp873x.h [new file with mode: 0644]
include/linux/mfd/stmpe.h

diff --git a/Documentation/devicetree/bindings/mfd/ac100.txt b/Documentation/devicetree/bindings/mfd/ac100.txt
new file mode 100644 (file)
index 0000000..b8ef006
--- /dev/null
@@ -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";
+       };
+};
index 585a955..8f3ad9a 100644 (file)
@@ -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
index f24f334..37a088f 100644 (file)
@@ -62,6 +62,7 @@ The below bindings specify the set of valid subnodes.
                    "qcom,pm8058-rtc"
                    "qcom,pm8921-rtc"
                    "qcom,pm8941-rtc"
+                   "qcom,pm8018-rtc"
 
 - reg:
        Usage: required
index b98b291..485bc59 100644 (file)
@@ -13,6 +13,7 @@ frequencies.
                    "qcom,rpm-msm8660"
                    "qcom,rpm-msm8960"
                    "qcom,rpm-ipq8064"
+                   "qcom,rpm-mdm9615"
 
 - reg:
        Usage: required
@@ -59,6 +60,7 @@ Regulator nodes are identified by their compatible:
                    "qcom,rpm-pm8058-regulators"
                    "qcom,rpm-pm8901-regulators"
                    "qcom,rpm-pm8921-regulators"
+                   "qcom,rpm-pm8018-regulators"
 
 - vdd_l0_l1_lvs-supply:
 - vdd_l2_l11_l12-supply:
@@ -137,6 +139,15 @@ Regulator nodes are identified by their compatible:
        Definition: reference to regulator supplying the input pin, as
                    described in the data sheet
 
+- vin_lvs1-supply:
+- vdd_l7-supply:
+- vdd_l8-supply:
+- vdd_l9_l10_l11_l12-supply:
+       Usage: optional (pm8018 only)
+       Value type: <phandle>
+       Definition: reference to regulator supplying the input pin, as
+                   described in the data sheet
+
 The regulator node houses sub-nodes for each regulator within the device. Each
 sub-node is identified using the node's name, with valid values listed for each
 of the pmics below.
@@ -156,6 +167,10 @@ pm8921:
        l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch,
        ncp
 
+pm8018:
+       s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
+       l12, l14, lvs1
+
 The content of each sub-node is defined by the standard binding for regulators -
 see regulator.txt - with additional custom properties described below:
 
index 3fb68bf..f9065a5 100644 (file)
@@ -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:
index 98dd47a..346e9a9 100644 (file)
@@ -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
index 2a035ed..d60432f 100644 (file)
@@ -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 (file)
index 0000000..f10d49d
--- /dev/null
@@ -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");
index f675132..5c0d818 100644 (file)
@@ -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;
index 4b0878f..25943e9 100644 (file)
@@ -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,
        },
 };
 
index a55be95..69b6fe2 100644 (file)
@@ -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
@@ -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
index 2ba3ba3..5bf35c6 100644 (file)
@@ -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 (file)
index 0000000..9bc69cd
--- /dev/null
@@ -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");
index e4f97b3..4fa0fae 100644 (file)
@@ -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) {
index a407527..a732cb5 100644 (file)
@@ -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 },
        { },
 };
index fd80b09..9610275 100644 (file)
@@ -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;
index 0eee635..abd8342 100644 (file)
@@ -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 (file)
index 0000000..9af064c
--- /dev/null
@@ -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");
index 1b7ec08..0e3a2ea 100644 (file)
@@ -309,6 +309,7 @@ static const struct regmap_config ssbi_regmap_config = {
 };
 
 static const struct of_device_id pm8921_id_table[] = {
+       { .compatible = "qcom,pm8018", },
        { .compatible = "qcom,pm8058", },
        { .compatible = "qcom,pm8921", },
        { }
index 2e44323..a74210d 100644 (file)
@@ -388,11 +388,62 @@ static const struct qcom_rpm_data ipq806x_template = {
        .ack_sel_size = 7,
 };
 
+static const struct qcom_rpm_resource mdm9615_rpm_resource_table[] = {
+       [QCOM_RPM_CXO_CLK] =                    { 25, 9, 5, 1 },
+       [QCOM_RPM_SYS_FABRIC_CLK] =             { 26, 10, 9, 1 },
+       [QCOM_RPM_DAYTONA_FABRIC_CLK] =         { 27, 11, 11, 1 },
+       [QCOM_RPM_SFPB_CLK] =                   { 28, 12, 12, 1 },
+       [QCOM_RPM_CFPB_CLK] =                   { 29, 13, 13, 1 },
+       [QCOM_RPM_EBI1_CLK] =                   { 30, 14, 16, 1 },
+       [QCOM_RPM_APPS_FABRIC_HALT] =           { 31, 15, 22, 2 },
+       [QCOM_RPM_APPS_FABRIC_MODE] =           { 33, 16, 23, 3 },
+       [QCOM_RPM_APPS_FABRIC_IOCTL] =          { 36, 17, 24, 1 },
+       [QCOM_RPM_APPS_FABRIC_ARB] =            { 37, 18, 25, 27 },
+       [QCOM_RPM_PM8018_SMPS1] =               { 64, 19, 30, 2 },
+       [QCOM_RPM_PM8018_SMPS2] =               { 66, 21, 31, 2 },
+       [QCOM_RPM_PM8018_SMPS3] =               { 68, 23, 32, 2 },
+       [QCOM_RPM_PM8018_SMPS4] =               { 70, 25, 33, 2 },
+       [QCOM_RPM_PM8018_SMPS5] =               { 72, 27, 34, 2 },
+       [QCOM_RPM_PM8018_LDO1] =                { 74, 29, 35, 2 },
+       [QCOM_RPM_PM8018_LDO2] =                { 76, 31, 36, 2 },
+       [QCOM_RPM_PM8018_LDO3] =                { 78, 33, 37, 2 },
+       [QCOM_RPM_PM8018_LDO4] =                { 80, 35, 38, 2 },
+       [QCOM_RPM_PM8018_LDO5] =                { 82, 37, 39, 2 },
+       [QCOM_RPM_PM8018_LDO6] =                { 84, 39, 40, 2 },
+       [QCOM_RPM_PM8018_LDO7] =                { 86, 41, 41, 2 },
+       [QCOM_RPM_PM8018_LDO8] =                { 88, 43, 42, 2 },
+       [QCOM_RPM_PM8018_LDO9] =                { 90, 45, 43, 2 },
+       [QCOM_RPM_PM8018_LDO10] =               { 92, 47, 44, 2 },
+       [QCOM_RPM_PM8018_LDO11] =               { 94, 49, 45, 2 },
+       [QCOM_RPM_PM8018_LDO12] =               { 96, 51, 46, 2 },
+       [QCOM_RPM_PM8018_LDO13] =               { 98, 53, 47, 2 },
+       [QCOM_RPM_PM8018_LDO14] =               { 100, 55, 48, 2 },
+       [QCOM_RPM_PM8018_LVS1] =                { 102, 57, 49, 1 },
+       [QCOM_RPM_PM8018_NCP] =                 { 103, 58, 80, 2 },
+       [QCOM_RPM_CXO_BUFFERS] =                { 105, 60, 81, 1 },
+       [QCOM_RPM_USB_OTG_SWITCH] =             { 106, 61, 82, 1 },
+       [QCOM_RPM_HDMI_SWITCH] =                { 107, 62, 83, 1 },
+       [QCOM_RPM_VOLTAGE_CORNER] =             { 109, 64, 87, 1 },
+};
+
+static const struct qcom_rpm_data mdm9615_template = {
+       .version = 3,
+       .resource_table = mdm9615_rpm_resource_table,
+       .n_resources = ARRAY_SIZE(mdm9615_rpm_resource_table),
+       .req_ctx_off = 3,
+       .req_sel_off = 11,
+       .ack_ctx_off = 15,
+       .ack_sel_off = 23,
+       .req_sel_size = 4,
+       .ack_sel_size = 7,
+};
+
 static const struct of_device_id qcom_rpm_of_match[] = {
        { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
        { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
        { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
        { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
+       { .compatible = "qcom,rpm-mdm9615", .data = &mdm9615_template },
        { }
 };
 MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
index c3f4aab..863c39a 100644 (file)
@@ -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 },
index 94c7cc0..cfdae8a 100644 (file)
@@ -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);
 }
 
@@ -550,26 +552,90 @@ static struct stmpe_variant_info stmpe610 = {
        .get_altfunc    = stmpe811_get_altfunc,
 };
 
+/*
+ * 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;
                }
index 84adb46..f7efdd8 100644 (file)
@@ -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,11 +156,34 @@ 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)
index 6c084b2..04053fe 100644 (file)
@@ -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);
index d3a86a8..6204129 100644 (file)
@@ -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
index 6d9ac76..54382ef 100644 (file)
@@ -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;
@@ -457,6 +553,14 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
                const struct regulator_desc *desc = &regulators[i];
                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.
index e254272..1b2acc4 100644 (file)
@@ -447,6 +447,44 @@ static struct regulator_ops switch_ops = {
        .is_enabled = rpm_reg_is_enabled,
 };
 
+/*
+ * PM8018 regulators
+ */
+static const struct qcom_rpm_reg pm8018_pldo = {
+       .desc.linear_ranges = pldo_ranges,
+       .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+       .desc.n_voltages = 161,
+       .desc.ops = &uV_ops,
+       .parts = &rpm8960_ldo_parts,
+       .supports_force_mode_auto = false,
+       .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_nldo = {
+       .desc.linear_ranges = nldo_ranges,
+       .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+       .desc.n_voltages = 64,
+       .desc.ops = &uV_ops,
+       .parts = &rpm8960_ldo_parts,
+       .supports_force_mode_auto = false,
+       .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_smps = {
+       .desc.linear_ranges = smps_ranges,
+       .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+       .desc.n_voltages = 154,
+       .desc.ops = &uV_ops,
+       .parts = &rpm8960_smps_parts,
+       .supports_force_mode_auto = false,
+       .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_switch = {
+       .desc.ops = &switch_ops,
+       .parts = &rpm8960_switch_parts,
+};
+
 /*
  * PM8058 regulators
  */
@@ -755,6 +793,32 @@ struct rpm_regulator_data {
        const char *supply;
 };
 
+static const struct rpm_regulator_data rpm_pm8018_regulators[] = {
+       { "s1",  QCOM_RPM_PM8018_SMPS1, &pm8018_smps, "vdd_s1" },
+       { "s2",  QCOM_RPM_PM8018_SMPS2, &pm8018_smps, "vdd_s2" },
+       { "s3",  QCOM_RPM_PM8018_SMPS3, &pm8018_smps, "vdd_s3" },
+       { "s4",  QCOM_RPM_PM8018_SMPS4, &pm8018_smps, "vdd_s4" },
+       { "s5",  QCOM_RPM_PM8018_SMPS5, &pm8018_smps, "vdd_s5" },
+
+       { "l2",  QCOM_RPM_PM8018_LDO2,  &pm8018_pldo, "vdd_l2" },
+       { "l3",  QCOM_RPM_PM8018_LDO3,  &pm8018_pldo, "vdd_l3" },
+       { "l4",  QCOM_RPM_PM8018_LDO4,  &pm8018_pldo, "vdd_l4" },
+       { "l5",  QCOM_RPM_PM8018_LDO5,  &pm8018_pldo, "vdd_l5" },
+       { "l6",  QCOM_RPM_PM8018_LDO6,  &pm8018_pldo, "vdd_l7" },
+       { "l7",  QCOM_RPM_PM8018_LDO7,  &pm8018_pldo, "vdd_l7" },
+       { "l8",  QCOM_RPM_PM8018_LDO8,  &pm8018_nldo, "vdd_l8" },
+       { "l9",  QCOM_RPM_PM8018_LDO9,  &pm8921_nldo1200,
+                                                     "vdd_l9_l10_l11_l12" },
+       { "l10", QCOM_RPM_PM8018_LDO10, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+       { "l11", QCOM_RPM_PM8018_LDO11, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+       { "l12", QCOM_RPM_PM8018_LDO12, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+       { "l14", QCOM_RPM_PM8018_LDO14, &pm8018_pldo, "vdd_l14" },
+
+       { "lvs1", QCOM_RPM_PM8018_LVS1, &pm8018_switch, "lvs1_in" },
+
+       { }
+};
+
 static const struct rpm_regulator_data rpm_pm8058_regulators[] = {
        { "l0",   QCOM_RPM_PM8058_LDO0,   &pm8058_nldo, "vdd_l0_l1_lvs" },
        { "l1",   QCOM_RPM_PM8058_LDO1,   &pm8058_nldo, "vdd_l0_l1_lvs" },
@@ -870,6 +934,8 @@ static const struct rpm_regulator_data rpm_pm8921_regulators[] = {
 };
 
 static const struct of_device_id rpm_of_match[] = {
+       { .compatible = "qcom,rpm-pm8018-regulators",
+               .data = &rpm_pm8018_regulators },
        { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
        { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
        { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
index 1102e8f..d1e0807 100644 (file)
@@ -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
index 7cf7ad5..8fb994b 100644 (file)
@@ -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 (file)
index 0000000..70b4fd0
--- /dev/null
@@ -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");
index 795fcbd..fac8355 100644 (file)
@@ -428,6 +428,7 @@ static const struct pm8xxx_rtc_regs pm8941_regs = {
  */
 static const struct of_device_id pm8xxx_id_table[] = {
        { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
+       { .compatible = "qcom,pm8018-rtc", .data = &pm8921_regs },
        { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
        { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
        { },
index 13a9d4b..54aef5e 100644 (file)
 #define QCOM_RPM_SMB208_S1b                    137
 #define QCOM_RPM_SMB208_S2a                    138
 #define QCOM_RPM_SMB208_S2b                    139
+#define QCOM_RPM_PM8018_SMPS1                  140
+#define QCOM_RPM_PM8018_SMPS2                  141
+#define QCOM_RPM_PM8018_SMPS3                  142
+#define QCOM_RPM_PM8018_SMPS4                  143
+#define QCOM_RPM_PM8018_SMPS5                  144
+#define QCOM_RPM_PM8018_LDO1                   145
+#define QCOM_RPM_PM8018_LDO2                   146
+#define QCOM_RPM_PM8018_LDO3                   147
+#define QCOM_RPM_PM8018_LDO4                   148
+#define QCOM_RPM_PM8018_LDO5                   149
+#define QCOM_RPM_PM8018_LDO6                   150
+#define QCOM_RPM_PM8018_LDO7                   151
+#define QCOM_RPM_PM8018_LDO8                   152
+#define QCOM_RPM_PM8018_LDO9                   153
+#define QCOM_RPM_PM8018_LDO10                  154
+#define QCOM_RPM_PM8018_LDO11                  155
+#define QCOM_RPM_PM8018_LDO12                  156
+#define QCOM_RPM_PM8018_LDO13                  157
+#define QCOM_RPM_PM8018_LDO14                  158
+#define QCOM_RPM_PM8018_LVS1                   159
+#define QCOM_RPM_PM8018_NCP                    160
+#define QCOM_RPM_VOLTAGE_CORNER                        161
 
 /*
  * Constants used to select force mode for regulators.
diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h
new file mode 100644 (file)
index 0000000..3c148f1
--- /dev/null
@@ -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 */
index 58ab4c0..b9909bb 100644 (file)
@@ -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>
 
 #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;
index 0be4982..fec597f 100644 (file)
@@ -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
@@ -265,6 +290,26 @@ enum {
        AXP22X_REG_ID_MAX,
 };
 
+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,
@@ -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,
index d641a18..76f7ef4 100644 (file)
@@ -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;
index 7e7a8d4..76728ff 100644 (file)
@@ -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 (file)
index 0000000..83b1bd7
--- /dev/null
@@ -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 */
index de748bc..4a827af 100644 (file)
@@ -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,
 };