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)
36 files changed:
Documentation/devicetree/bindings/mfd/ac100.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/axp20x.txt
Documentation/devicetree/bindings/mfd/rk808.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/rk808.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/rk808-regulator.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ac100.c [new file with mode: 0644]
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/rk808.h
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 4ca6aab..9636ae8 100644 (file)
@@ -1,7 +1,11 @@
-RK808 Power Management Integrated Circuit
+RK8XX Power Management Integrated Circuit
+
+The rk8xx family current members:
+rk808
+rk818
 
 Required properties:
-- compatible: "rockchip,rk808"
+- compatible: "rockchip,rk808", "rockchip,rk818"
 - reg: I2C slave address
 - interrupt-parent: The parent interrupt controller.
 - interrupts: the interrupt outputs of the controller.
@@ -13,6 +17,8 @@ Optional properties:
   default output clock name
 - rockchip,system-power-controller: Telling whether or not this pmic is controlling
   the system power.
+
+Optional RK808 properties:
 - vcc1-supply:  The input supply for DCDC_REG1
 - vcc2-supply:  The input supply for DCDC_REG2
 - vcc3-supply:  The input supply for DCDC_REG3
@@ -29,7 +35,20 @@ Optional properties:
   the gpio controller. If DVS GPIOs aren't present, voltage changes will happen
   very quickly with no slow ramp time.
 
-Regulators: All the regulators of RK808 to be instantiated shall be
+Optional RK818 properties:
+- vcc1-supply:  The input supply for DCDC_REG1
+- vcc2-supply:  The input supply for DCDC_REG2
+- vcc3-supply:  The input supply for DCDC_REG3
+- vcc4-supply:  The input supply for DCDC_REG4
+- boost-supply: The input supply for DCDC_BOOST
+- vcc6-supply:  The input supply for LDO_REG1 and LDO_REG2
+- vcc7-supply:  The input supply for LDO_REG3, LDO_REG5 and LDO_REG7
+- vcc8-supply:  The input supply for LDO_REG4, LDO_REG6 and LDO_REG8
+- vcc9-supply:  The input supply for LDO_REG9 and SWITCH_REG
+- h_5v-supply:  The input supply for HDMI_SWITCH
+- usb-supply:   The input supply for OTG_SWITCH
+
+Regulators: All the regulators of RK8XX to be instantiated shall be
 listed in a child node named 'regulators'. Each regulator is represented
 by a child node of the 'regulators' node.
 
@@ -48,6 +67,18 @@ number as described in RK808 datasheet.
        - SWITCH_REGn
                - valid values for n are 1 to 2
 
+Following regulators of the RK818 PMIC block are supported. Note that
+the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO
+number as described in RK818 datasheet.
+
+       - DCDC_REGn
+               - valid values for n are 1 to 4.
+       - LDO_REGn
+               - valid values for n are 1 to 9.
+       - SWITCH_REG
+       - HDMI_SWITCH
+       - OTG_SWITCH
+
 Standard regulator bindings are used inside regulator subnodes. Check
   Documentation/devicetree/bindings/regulator/regulator.txt
 for more details
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 2d1fb64..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
@@ -852,13 +862,13 @@ config MFD_RC5T583
          different functionality of the device.
 
 config MFD_RK808
-       tristate "Rockchip RK808 Power Management chip"
+       tristate "Rockchip RK808/RK818 Power Management Chip"
        depends on I2C && OF
        select MFD_CORE
        select REGMAP_I2C
        select REGMAP_IRQ
        help
-         If you say yes here you get support for the RK808
+         If you say yes here you get support for the RK808 and RK818
          Power Management chips.
          This driver provides common support for accessing the device
          through I2C interface. The device supports multiple sub-devices
@@ -1224,6 +1234,20 @@ config MFD_TPS65217
          This driver can also be built as a module.  If so, the module
          will be called tps65217.
 
+config MFD_TI_LP873X
+       tristate "TI LP873X Power Management IC"
+       depends on I2C
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         If you say yes here then you get support for the LP873X series of
+         Power Management Integrated Circuits (PMIC).
+         These include voltage regulators, thermal protection, configurable
+         General Purpose Outputs (GPO) that are used in portable devices.
+
+         This driver can also be built as a module. If so, the module
+         will be called lp873x.
+
 config MFD_TPS65218
        tristate "TI TPS65218 Power Management chips"
        depends on I2C
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 49d7f62..0f8acc5 100644 (file)
@@ -1,11 +1,15 @@
 /*
- * MFD core driver for Rockchip RK808
+ * MFD core driver for Rockchip RK808/RK818
  *
  * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
  *
  * Author: Chris Zhong <zyw@rock-chips.com>
  * Author: Zhang Qing <zhangqing@rock-chips.com>
  *
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * version 2, as published by the Free Software Foundation.
@@ -21,6 +25,7 @@
 #include <linux/mfd/rk808.h>
 #include <linux/mfd/core.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 
 struct rk808_reg_data {
@@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
        return false;
 }
 
+static const struct regmap_config rk818_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = RK818_USB_CTRL_REG,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = rk808_is_volatile_reg,
+};
+
 static const struct regmap_config rk808_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
@@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = {
        {
                .name = "rk808-rtc",
                .num_resources = ARRAY_SIZE(rtc_resources),
-               .resources = &rtc_resources[0],
+               .resources = rtc_resources,
        },
 };
 
-static const struct rk808_reg_data pre_init_reg[] = {
+static const struct mfd_cell rk818s[] = {
+       { .name = "rk808-clkout", },
+       { .name = "rk808-regulator", },
+       {
+               .name = "rk808-rtc",
+               .num_resources = ARRAY_SIZE(rtc_resources),
+               .resources = rtc_resources,
+       },
+};
+
+static const struct rk808_reg_data rk808_pre_init_reg[] = {
        { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_150MA },
        { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_200MA },
        { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
@@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = {
                                                    VB_LO_SEL_3500MV },
 };
 
+static const struct rk808_reg_data rk818_pre_init_reg[] = {
+       /* improve efficiency */
+       { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK,  BUCK_ILMIN_250MA },
+       { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_250MA },
+       { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
+       { RK818_USB_CTRL_REG,     RK818_USB_ILIM_SEL_MASK,
+                                                   RK818_USB_ILMIN_2000MA },
+       /* close charger when usb lower then 3.4V */
+       { RK818_USB_CTRL_REG,     RK818_USB_CHG_SD_VSEL_MASK,
+                                                   (0x7 << 4) },
+       /* no action when vref */
+       { RK818_H5V_EN_REG,       BIT(1),           RK818_REF_RDY_CTRL },
+       /* enable HDMI 5V */
+       { RK818_H5V_EN_REG,       BIT(0),           RK818_H5V_EN },
+       { RK808_VB_MON_REG,       MASK_ALL,         VB_LO_ACT |
+                                                   VB_LO_SEL_3500MV },
+};
+
 static const struct regmap_irq rk808_irqs[] = {
        /* INT_STS */
        [RK808_IRQ_VOUT_LO] = {
@@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = {
        },
 };
 
+static const struct regmap_irq rk818_irqs[] = {
+       /* INT_STS */
+       [RK818_IRQ_VOUT_LO] = {
+               .mask = RK818_IRQ_VOUT_LO_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_VB_LO] = {
+               .mask = RK818_IRQ_VB_LO_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_PWRON] = {
+               .mask = RK818_IRQ_PWRON_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_PWRON_LP] = {
+               .mask = RK818_IRQ_PWRON_LP_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_HOTDIE] = {
+               .mask = RK818_IRQ_HOTDIE_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_RTC_ALARM] = {
+               .mask = RK818_IRQ_RTC_ALARM_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_RTC_PERIOD] = {
+               .mask = RK818_IRQ_RTC_PERIOD_MSK,
+               .reg_offset = 0,
+       },
+       [RK818_IRQ_USB_OV] = {
+               .mask = RK818_IRQ_USB_OV_MSK,
+               .reg_offset = 0,
+       },
+
+       /* INT_STS2 */
+       [RK818_IRQ_PLUG_IN] = {
+               .mask = RK818_IRQ_PLUG_IN_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_PLUG_OUT] = {
+               .mask = RK818_IRQ_PLUG_OUT_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_CHG_OK] = {
+               .mask = RK818_IRQ_CHG_OK_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_CHG_TE] = {
+               .mask = RK818_IRQ_CHG_TE_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_CHG_TS1] = {
+               .mask = RK818_IRQ_CHG_TS1_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_TS2] = {
+               .mask = RK818_IRQ_TS2_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_CHG_CVTLIM] = {
+               .mask = RK818_IRQ_CHG_CVTLIM_MSK,
+               .reg_offset = 1,
+       },
+       [RK818_IRQ_DISCHG_ILIM] = {
+               .mask = RK818_IRQ_DISCHG_ILIM_MSK,
+               .reg_offset = 1,
+       },
+};
+
 static struct regmap_irq_chip rk808_irq_chip = {
        .name = "rk808",
        .irqs = rk808_irqs,
@@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
        .init_ack_masked = true,
 };
 
+static struct regmap_irq_chip rk818_irq_chip = {
+       .name = "rk818",
+       .irqs = rk818_irqs,
+       .num_irqs = ARRAY_SIZE(rk818_irqs),
+       .num_regs = 2,
+       .irq_reg_stride = 2,
+       .status_base = RK818_INT_STS_REG1,
+       .mask_base = RK818_INT_STS_MSK_REG1,
+       .ack_base = RK818_INT_STS_REG1,
+       .init_ack_masked = true,
+};
+
 static struct i2c_client *rk808_i2c_client;
 static void rk808_device_shutdown(void)
 {
@@ -167,55 +290,100 @@ static void rk808_device_shutdown(void)
                dev_err(&rk808_i2c_client->dev, "power off error!\n");
 }
 
+static const struct of_device_id rk808_of_match[] = {
+       { .compatible = "rockchip,rk808" },
+       { .compatible = "rockchip,rk818" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rk808_of_match);
+
 static int rk808_probe(struct i2c_client *client,
                       const struct i2c_device_id *id)
 {
        struct device_node *np = client->dev.of_node;
        struct rk808 *rk808;
+       const struct rk808_reg_data *pre_init_reg;
+       const struct mfd_cell *cells;
+       int nr_pre_init_regs;
+       int nr_cells;
        int pm_off = 0;
        int ret;
        int i;
 
-       if (!client->irq) {
-               dev_err(&client->dev, "No interrupt support, no core IRQ\n");
-               return -EINVAL;
-       }
-
        rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
        if (!rk808)
                return -ENOMEM;
 
-       rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config);
+       rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB);
+       if (rk808->variant < 0) {
+               dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n",
+                       RK808_ID_MSB);
+               return rk808->variant;
+       }
+
+       dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant);
+
+       switch (rk808->variant) {
+       case RK808_ID:
+               rk808->regmap_cfg = &rk808_regmap_config;
+               rk808->regmap_irq_chip = &rk808_irq_chip;
+               pre_init_reg = rk808_pre_init_reg;
+               nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
+               cells = rk808s;
+               nr_cells = ARRAY_SIZE(rk808s);
+               break;
+       case RK818_ID:
+               rk808->regmap_cfg = &rk818_regmap_config;
+               rk808->regmap_irq_chip = &rk818_irq_chip;
+               pre_init_reg = rk818_pre_init_reg;
+               nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
+               cells = rk818s;
+               nr_cells = ARRAY_SIZE(rk818s);
+               break;
+       default:
+               dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
+                       rk808->variant);
+               return -EINVAL;
+       }
+
+       rk808->i2c = client;
+       i2c_set_clientdata(client, rk808);
+
+       rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg);
        if (IS_ERR(rk808->regmap)) {
                dev_err(&client->dev, "regmap initialization failed\n");
                return PTR_ERR(rk808->regmap);
        }
 
-       for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) {
-               ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr,
-                                        pre_init_reg[i].mask,
-                                        pre_init_reg[i].value);
-               if (ret) {
-                       dev_err(&client->dev,
-                               "0x%x write err\n", pre_init_reg[i].addr);
-                       return ret;
-               }
+       if (!client->irq) {
+               dev_err(&client->dev, "No interrupt support, no core IRQ\n");
+               return -EINVAL;
        }
 
        ret = regmap_add_irq_chip(rk808->regmap, client->irq,
                                  IRQF_ONESHOT, -1,
-                                 &rk808_irq_chip, &rk808->irq_data);
+                                 rk808->regmap_irq_chip, &rk808->irq_data);
        if (ret) {
                dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
                return ret;
        }
 
-       rk808->i2c = client;
-       i2c_set_clientdata(client, rk808);
+       for (i = 0; i < nr_pre_init_regs; i++) {
+               ret = regmap_update_bits(rk808->regmap,
+                                       pre_init_reg[i].addr,
+                                       pre_init_reg[i].mask,
+                                       pre_init_reg[i].value);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "0x%x write err\n",
+                               pre_init_reg[i].addr);
+                       return ret;
+               }
+       }
 
-       ret = devm_mfd_add_devices(&client->dev, -1,
-                                  rk808s, ARRAY_SIZE(rk808s), NULL, 0,
-                                  regmap_irq_get_domain(rk808->irq_data));
+       ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+                             cells, nr_cells, NULL, 0,
+                             regmap_irq_get_domain(rk808->irq_data));
        if (ret) {
                dev_err(&client->dev, "failed to add MFD devices %d\n", ret);
                goto err_irq;
@@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client)
        return 0;
 }
 
-static const struct of_device_id rk808_of_match[] = {
-       { .compatible = "rockchip,rk808" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, rk808_of_match);
-
 static const struct i2c_device_id rk808_ids[] = {
        { "rk808" },
+       { "rk818" },
        { },
 };
 MODULE_DEVICE_TABLE(i2c, rk808_ids);
@@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
-MODULE_DESCRIPTION("RK808 PMIC driver");
+MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
+MODULE_DESCRIPTION("RK808/RK818 PMIC driver");
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 6c88e31..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
@@ -635,11 +635,11 @@ config REGULATOR_RC5T583
          outputs which can be controlled by i2c communication.
 
 config REGULATOR_RK808
-       tristate "Rockchip RK808 Power regulators"
+       tristate "Rockchip RK808/RK818 Power regulators"
        depends on MFD_RK808
        help
          Select this option to enable the power regulator of ROCKCHIP
-         PMIC RK808.
+         PMIC RK808 and RK818.
          This driver supports the control of different power rails of device
          through regulator interface. The device supports multiple DCDC/LDO
          outputs which can be controlled by i2c communication.
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 40d07ba..5f412a5 100644 (file)
@@ -1,11 +1,15 @@
 /*
- * Regulator driver for Rockchip RK808
+ * Regulator driver for Rockchip RK808/RK818
  *
  * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
  *
  * Author: Chris Zhong <zyw@rock-chips.com>
  * Author: Zhang Qing <zhangqing@rock-chips.com>
  *
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * version 2, as published by the Free Software Foundation.
 #define RK808_BUCK4_VSEL_MASK  0xf
 #define RK808_LDO_VSEL_MASK    0x1f
 
+#define RK818_BUCK_VSEL_MASK           0x3f
+#define RK818_BUCK4_VSEL_MASK          0x1f
+#define RK818_LDO_VSEL_MASK            0x1f
+#define RK818_LDO3_ON_VSEL_MASK                0xf
+#define RK818_BOOST_ON_VSEL_MASK       0xe0
+
 /* Ramp rate definitions for buck1 / buck2 only */
 #define RK808_RAMP_RATE_OFFSET         3
 #define RK808_RAMP_RATE_MASK           (3 << RK808_RAMP_RATE_OFFSET)
@@ -454,6 +464,108 @@ static const struct regulator_desc rk808_reg[] = {
                RK808_DCDC_EN_REG, BIT(6)),
 };
 
+static const struct regulator_desc rk818_reg[] = {
+       {
+               .name = "DCDC_REG1",
+               .supply_name = "vcc1",
+               .of_match = of_match_ptr("DCDC_REG1"),
+               .regulators_node = of_match_ptr("regulators"),
+               .id = RK818_ID_DCDC1,
+               .ops = &rk808_reg_ops,
+               .type = REGULATOR_VOLTAGE,
+               .min_uV = 712500,
+               .uV_step = 12500,
+               .n_voltages = 64,
+               .vsel_reg = RK818_BUCK1_ON_VSEL_REG,
+               .vsel_mask = RK818_BUCK_VSEL_MASK,
+               .enable_reg = RK818_DCDC_EN_REG,
+               .enable_mask = BIT(0),
+               .owner = THIS_MODULE,
+       }, {
+               .name = "DCDC_REG2",
+               .supply_name = "vcc2",
+               .of_match = of_match_ptr("DCDC_REG2"),
+               .regulators_node = of_match_ptr("regulators"),
+               .id = RK818_ID_DCDC2,
+               .ops = &rk808_reg_ops,
+               .type = REGULATOR_VOLTAGE,
+               .min_uV = 712500,
+               .uV_step = 12500,
+               .n_voltages = 64,
+               .vsel_reg = RK818_BUCK2_ON_VSEL_REG,
+               .vsel_mask = RK818_BUCK_VSEL_MASK,
+               .enable_reg = RK818_DCDC_EN_REG,
+               .enable_mask = BIT(1),
+               .owner = THIS_MODULE,
+       }, {
+               .name = "DCDC_REG3",
+               .supply_name = "vcc3",
+               .of_match = of_match_ptr("DCDC_REG3"),
+               .regulators_node = of_match_ptr("regulators"),
+               .id = RK818_ID_DCDC3,
+               .ops = &rk808_switch_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = 1,
+               .enable_reg = RK818_DCDC_EN_REG,
+               .enable_mask = BIT(2),
+               .owner = THIS_MODULE,
+       },
+       RK8XX_DESC(RK818_ID_DCDC4, "DCDC_REG4", "vcc4", 1800, 3600, 100,
+               RK818_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK,
+               RK818_DCDC_EN_REG, BIT(3), 0),
+       RK8XX_DESC(RK818_ID_BOOST, "DCDC_BOOST", "boost", 4700, 5400, 100,
+               RK818_BOOST_LDO9_ON_VSEL_REG, RK818_BOOST_ON_VSEL_MASK,
+               RK818_DCDC_EN_REG, BIT(4), 0),
+       RK8XX_DESC(RK818_ID_LDO1, "LDO_REG1", "vcc6", 1800, 3400, 100,
+               RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(0), 400),
+       RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
+               RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(1), 400),
+       {
+               .name = "LDO_REG3",
+               .supply_name = "vcc7",
+               .of_match = of_match_ptr("LDO_REG3"),
+               .regulators_node = of_match_ptr("regulators"),
+               .id = RK818_ID_LDO3,
+               .ops = &rk808_reg_ops_ranges,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = 16,
+               .linear_ranges = rk808_ldo3_voltage_ranges,
+               .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges),
+               .vsel_reg = RK818_LDO3_ON_VSEL_REG,
+               .vsel_mask = RK818_LDO3_ON_VSEL_MASK,
+               .enable_reg = RK818_LDO_EN_REG,
+               .enable_mask = BIT(2),
+               .enable_time = 400,
+               .owner = THIS_MODULE,
+       },
+       RK8XX_DESC(RK818_ID_LDO4, "LDO_REG4", "vcc8", 1800, 3400, 100,
+               RK818_LDO4_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(3), 400),
+       RK8XX_DESC(RK818_ID_LDO5, "LDO_REG5", "vcc7", 1800, 3400, 100,
+               RK818_LDO5_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(4), 400),
+       RK8XX_DESC(RK818_ID_LDO6, "LDO_REG6", "vcc8", 800, 2500, 100,
+               RK818_LDO6_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(5), 400),
+       RK8XX_DESC(RK818_ID_LDO7, "LDO_REG7", "vcc7", 800, 2500, 100,
+               RK818_LDO7_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(6), 400),
+       RK8XX_DESC(RK818_ID_LDO8, "LDO_REG8", "vcc8", 1800, 3400, 100,
+               RK818_LDO8_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+               BIT(7), 400),
+       RK8XX_DESC(RK818_ID_LDO9, "LDO_REG9", "vcc9", 1800, 3400, 100,
+               RK818_BOOST_LDO9_ON_VSEL_REG, RK818_LDO_VSEL_MASK,
+               RK818_DCDC_EN_REG, BIT(5), 400),
+       RK8XX_DESC_SWITCH(RK818_ID_SWITCH, "SWITCH_REG", "vcc9",
+               RK818_DCDC_EN_REG, BIT(6)),
+       RK8XX_DESC_SWITCH(RK818_ID_HDMI_SWITCH, "HDMI_SWITCH", "h_5v",
+               RK818_H5V_EN_REG, BIT(0)),
+       RK8XX_DESC_SWITCH(RK818_ID_OTG_SWITCH, "OTG_SWITCH", "usb",
+               RK818_DCDC_EN_REG, BIT(7)),
+};
+
 static int rk808_regulator_dt_parse_pdata(struct device *dev,
                                   struct device *client_dev,
                                   struct regmap *map,
@@ -499,7 +611,8 @@ static int rk808_regulator_probe(struct platform_device *pdev)
        struct regulator_config config = {};
        struct regulator_dev *rk808_rdev;
        struct rk808_regulator_data *pdata;
-       int ret, i;
+       const struct regulator_desc *regulators;
+       int ret, i, nregulators;
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
@@ -512,14 +625,29 @@ static int rk808_regulator_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pdata);
 
+       switch (rk808->variant) {
+       case RK808_ID:
+               regulators = rk808_reg;
+               nregulators = RK808_NUM_REGULATORS;
+               break;
+       case RK818_ID:
+               regulators = rk818_reg;
+               nregulators = RK818_NUM_REGULATORS;
+               break;
+       default:
+               dev_err(&client->dev, "unsupported RK8XX ID %lu\n",
+                       rk808->variant);
+               return -EINVAL;
+       }
+
        config.dev = &client->dev;
        config.driver_data = pdata;
        config.regmap = rk808->regmap;
 
        /* Instantiate the regulators */
-       for (i = 0; i < RK808_NUM_REGULATORS; i++) {
+       for (i = 0; i < nregulators; i++) {
                rk808_rdev = devm_regulator_register(&pdev->dev,
-                                                    &rk808_reg[i], &config);
+                                                    &regulators[i], &config);
                if (IS_ERR(rk808_rdev)) {
                        dev_err(&client->dev,
                                "failed to register %d regulator\n", i);
@@ -540,8 +668,9 @@ static struct platform_driver rk808_regulator_driver = {
 
 module_platform_driver(rk808_regulator_driver);
 
-MODULE_DESCRIPTION("regulator driver for the rk808 series PMICs");
-MODULE_AUTHOR("Chris Zhong<zyw@rock-chips.com>");
-MODULE_AUTHOR("Zhang Qing<zhangqing@rock-chips.com>");
+MODULE_DESCRIPTION("regulator driver for the RK808/RK818 series PMICs");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:rk808-regulator");
index e215f50..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
@@ -328,11 +338,11 @@ config RTC_DRV_MAX77686
          will be called rtc-max77686.
 
 config RTC_DRV_RK808
-       tristate "Rockchip RK808 RTC"
+       tristate "Rockchip RK808/RK818 RTC"
        depends on MFD_RK808
        help
          If you say yes here you will get support for the
-         RTC of RK808 PMIC.
+         RTC of RK808 and RK818 PMIC.
 
          This driver can also be built as a module. If so, the module
          will be called rk808-rtc.
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");
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 441b6ee..fc5db6f 100644 (file)
@@ -1,11 +1,15 @@
 /*
- * rk808.h for Rockchip RK808
+ * Register definitions for Rockchip's RK808/RK818 PMIC
  *
  * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
  *
  * Author: Chris Zhong <zyw@rock-chips.com>
  * Author: Zhang Qing <zhangqing@rock-chips.com>
  *
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * version 2, as published by the Free Software Foundation.
@@ -16,8 +20,8 @@
  * more details.
  */
 
-#ifndef __LINUX_REGULATOR_rk808_H
-#define __LINUX_REGULATOR_rk808_H
+#ifndef __LINUX_REGULATOR_RK808_H
+#define __LINUX_REGULATOR_RK808_H
 
 #include <linux/regulator/machine.h>
 #include <linux/regmap.h>
@@ -28,7 +32,7 @@
 
 #define RK808_DCDC1    0 /* (0+RK808_START) */
 #define RK808_LDO1     4 /* (4+RK808_START) */
-#define RK808_NUM_REGULATORS   14
+#define RK808_NUM_REGULATORS   14
 
 enum rk808_reg {
        RK808_ID_DCDC1,
@@ -65,6 +69,8 @@ enum rk808_reg {
 #define RK808_RTC_INT_REG      0x12
 #define RK808_RTC_COMP_LSB_REG 0x13
 #define RK808_RTC_COMP_MSB_REG 0x14
+#define RK808_ID_MSB           0x17
+#define RK808_ID_LSB           0x18
 #define RK808_CLK32OUT_REG     0x20
 #define RK808_VB_MON_REG       0x21
 #define RK808_THERMAL_REG      0x22
@@ -115,7 +121,92 @@ enum rk808_reg {
 #define RK808_INT_STS_MSK_REG2 0x4f
 #define RK808_IO_POL_REG       0x50
 
-/* IRQ Definitions */
+/* RK818 */
+#define RK818_DCDC1                    0
+#define RK818_LDO1                     4
+#define RK818_NUM_REGULATORS           17
+
+enum rk818_reg {
+       RK818_ID_DCDC1,
+       RK818_ID_DCDC2,
+       RK818_ID_DCDC3,
+       RK818_ID_DCDC4,
+       RK818_ID_BOOST,
+       RK818_ID_LDO1,
+       RK818_ID_LDO2,
+       RK818_ID_LDO3,
+       RK818_ID_LDO4,
+       RK818_ID_LDO5,
+       RK818_ID_LDO6,
+       RK818_ID_LDO7,
+       RK818_ID_LDO8,
+       RK818_ID_LDO9,
+       RK818_ID_SWITCH,
+       RK818_ID_HDMI_SWITCH,
+       RK818_ID_OTG_SWITCH,
+};
+
+#define RK818_DCDC_EN_REG              0x23
+#define RK818_LDO_EN_REG               0x24
+#define RK818_SLEEP_SET_OFF_REG1       0x25
+#define RK818_SLEEP_SET_OFF_REG2       0x26
+#define RK818_DCDC_UV_STS_REG          0x27
+#define RK818_DCDC_UV_ACT_REG          0x28
+#define RK818_LDO_UV_STS_REG           0x29
+#define RK818_LDO_UV_ACT_REG           0x2a
+#define RK818_DCDC_PG_REG              0x2b
+#define RK818_LDO_PG_REG               0x2c
+#define RK818_VOUT_MON_TDB_REG         0x2d
+#define RK818_BUCK1_CONFIG_REG         0x2e
+#define RK818_BUCK1_ON_VSEL_REG                0x2f
+#define RK818_BUCK1_SLP_VSEL_REG       0x30
+#define RK818_BUCK2_CONFIG_REG         0x32
+#define RK818_BUCK2_ON_VSEL_REG                0x33
+#define RK818_BUCK2_SLP_VSEL_REG       0x34
+#define RK818_BUCK3_CONFIG_REG         0x36
+#define RK818_BUCK4_CONFIG_REG         0x37
+#define RK818_BUCK4_ON_VSEL_REG                0x38
+#define RK818_BUCK4_SLP_VSEL_REG       0x39
+#define RK818_BOOST_CONFIG_REG         0x3a
+#define RK818_LDO1_ON_VSEL_REG         0x3b
+#define RK818_LDO1_SLP_VSEL_REG                0x3c
+#define RK818_LDO2_ON_VSEL_REG         0x3d
+#define RK818_LDO2_SLP_VSEL_REG                0x3e
+#define RK818_LDO3_ON_VSEL_REG         0x3f
+#define RK818_LDO3_SLP_VSEL_REG                0x40
+#define RK818_LDO4_ON_VSEL_REG         0x41
+#define RK818_LDO4_SLP_VSEL_REG                0x42
+#define RK818_LDO5_ON_VSEL_REG         0x43
+#define RK818_LDO5_SLP_VSEL_REG                0x44
+#define RK818_LDO6_ON_VSEL_REG         0x45
+#define RK818_LDO6_SLP_VSEL_REG                0x46
+#define RK818_LDO7_ON_VSEL_REG         0x47
+#define RK818_LDO7_SLP_VSEL_REG                0x48
+#define RK818_LDO8_ON_VSEL_REG         0x49
+#define RK818_LDO8_SLP_VSEL_REG                0x4a
+#define RK818_BOOST_LDO9_ON_VSEL_REG   0x54
+#define RK818_BOOST_LDO9_SLP_VSEL_REG  0x55
+#define RK818_DEVCTRL_REG              0x4b
+#define RK818_INT_STS_REG1             0X4c
+#define RK818_INT_STS_MSK_REG1         0x4d
+#define RK818_INT_STS_REG2             0x4e
+#define RK818_INT_STS_MSK_REG2         0x4f
+#define RK818_IO_POL_REG               0x50
+#define RK818_H5V_EN_REG               0x52
+#define RK818_SLEEP_SET_OFF_REG3       0x53
+#define RK818_BOOST_LDO9_ON_VSEL_REG   0x54
+#define RK818_BOOST_LDO9_SLP_VSEL_REG  0x55
+#define RK818_BOOST_CTRL_REG           0x56
+#define RK818_DCDC_ILMAX               0x90
+#define RK818_USB_CTRL_REG             0xa1
+
+#define RK818_H5V_EN                   BIT(0)
+#define RK818_REF_RDY_CTRL             BIT(1)
+#define RK818_USB_ILIM_SEL_MASK                0xf
+#define RK818_USB_ILMIN_2000MA         0x7
+#define RK818_USB_CHG_SD_VSEL_MASK     0x70
+
+/* RK808 IRQ Definitions */
 #define RK808_IRQ_VOUT_LO      0
 #define RK808_IRQ_VB_LO                1
 #define RK808_IRQ_PWRON                2
@@ -137,6 +228,43 @@ enum rk808_reg {
 #define RK808_IRQ_PLUG_IN_INT_MSK      BIT(0)
 #define RK808_IRQ_PLUG_OUT_INT_MSK     BIT(1)
 
+/* RK818 IRQ Definitions */
+#define RK818_IRQ_VOUT_LO      0
+#define RK818_IRQ_VB_LO                1
+#define RK818_IRQ_PWRON                2
+#define RK818_IRQ_PWRON_LP     3
+#define RK818_IRQ_HOTDIE       4
+#define RK818_IRQ_RTC_ALARM    5
+#define RK818_IRQ_RTC_PERIOD   6
+#define RK818_IRQ_USB_OV       7
+#define RK818_IRQ_PLUG_IN      8
+#define RK818_IRQ_PLUG_OUT     9
+#define RK818_IRQ_CHG_OK       10
+#define RK818_IRQ_CHG_TE       11
+#define RK818_IRQ_CHG_TS1      12
+#define RK818_IRQ_TS2          13
+#define RK818_IRQ_CHG_CVTLIM   14
+#define RK818_IRQ_DISCHG_ILIM  7
+
+#define RK818_IRQ_VOUT_LO_MSK          BIT(0)
+#define RK818_IRQ_VB_LO_MSK            BIT(1)
+#define RK818_IRQ_PWRON_MSK            BIT(2)
+#define RK818_IRQ_PWRON_LP_MSK         BIT(3)
+#define RK818_IRQ_HOTDIE_MSK           BIT(4)
+#define RK818_IRQ_RTC_ALARM_MSK                BIT(5)
+#define RK818_IRQ_RTC_PERIOD_MSK       BIT(6)
+#define RK818_IRQ_USB_OV_MSK           BIT(7)
+#define RK818_IRQ_PLUG_IN_MSK          BIT(0)
+#define RK818_IRQ_PLUG_OUT_MSK         BIT(1)
+#define RK818_IRQ_CHG_OK_MSK           BIT(2)
+#define RK818_IRQ_CHG_TE_MSK           BIT(3)
+#define RK818_IRQ_CHG_TS1_MSK          BIT(4)
+#define RK818_IRQ_TS2_MSK              BIT(5)
+#define RK818_IRQ_CHG_CVTLIM_MSK       BIT(6)
+#define RK818_IRQ_DISCHG_ILIM_MSK      BIT(7)
+
+#define RK818_NUM_IRQ          16
+
 #define RK808_VBAT_LOW_2V8     0x00
 #define RK808_VBAT_LOW_2V9     0x01
 #define RK808_VBAT_LOW_3V0     0x02
@@ -191,9 +319,17 @@ enum {
        BOOST_ILMIN_250MA,
 };
 
+enum {
+       RK808_ID = 0x0000,
+       RK818_ID = 0x8181,
+};
+
 struct rk808 {
-       struct i2c_client *i2c;
-       struct regmap_irq_chip_data *irq_data;
-       struct regmap *regmap;
+       struct i2c_client               *i2c;
+       struct regmap_irq_chip_data     *irq_data;
+       struct regmap                   *regmap;
+       long                            variant;
+       const struct regmap_config      *regmap_cfg;
+       const struct regmap_irq_chip    *regmap_irq_chip;
 };
-#endif /* __LINUX_REGULATOR_rk808_H */
+#endif /* __LINUX_REGULATOR_RK808_H */
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,
 };