Merge branches 'ib-mfd-watchdog-rtc-4.2', 'ib-mfd-regulator-4.2' and 'ib-mfd-i2c-input-chrome-4.2' into ibs-for-mfd-merged
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
index 98685f2..753f14f 100644
--- a/Documentation/devicetree/bindings/mfd/axp20x.txt
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -1,15 +1,16 @@
-AXP202/AXP209 device tree bindings
+AXP family PMIC device tree bindings
The axp20x family current members :
axp202 (X-Powers)
axp209 (X-Powers)
+axp221 (X-Powers)
Required properties:
-- compatible: "x-powers,axp202" or "x-powers,axp209"
+- compatible: "x-powers,axp202", "x-powers,axp209", "x-powers,axp221"
- reg: The I2C slave address for the AXP chip
- interrupt-parent: The parent interrupt controller
- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
-- interrupt-controller: axp20x has its own internal IRQs
+- interrupt-controller: The PMIC has its own internal IRQs
- #interrupt-cells: Should be set to 1
Optional properties:
@@ -48,6 +49,31 @@
LDO4 : LDO : ldo24in-supply : shared supply
LDO5 : LDO : ldo5in-supply
+AXP221 regulators, type, and corresponding input supply names:
+
+Regulator Type Supply Name Notes
+--------- ---- ----------- -----
+DCDC1 : DC-DC buck : vin1-supply
+DCDC2 : DC-DC buck : vin2-supply
+DCDC3 : DC-DC buck : vin3-supply
+DCDC4 : DC-DC buck : vin4-supply
+DCDC5 : DC-DC buck : vin5-supply
+DC1SW : On/Off Switch : dcdc1-supply : DCDC1 secondary output
+DC5LDO : LDO : dcdc5-supply : input from DCDC5
+ALDO1 : LDO : aldoin-supply : shared supply
+ALDO2 : LDO : aldoin-supply : shared supply
+ALDO3 : LDO : aldoin-supply : shared supply
+DLDO1 : LDO : dldoin-supply : shared supply
+DLDO2 : LDO : dldoin-supply : shared supply
+DLDO3 : LDO : dldoin-supply : shared supply
+DLDO4 : LDO : dldoin-supply : shared supply
+ELDO1 : LDO : eldoin-supply : shared supply
+ELDO2 : LDO : eldoin-supply : shared supply
+ELDO3 : LDO : eldoin-supply : shared supply
+LDO_IO0 : LDO : ips-supply : GPIO 0
+LDO_IO1 : LDO : ips-supply : GPIO 1
+RTC_LDO : LDO : ips-supply : always on
+
Example:
axp209: pmic@34 {
diff --git a/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt
new file mode 100644
index 0000000..73407f50
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt
@@ -0,0 +1,25 @@
+STMicroelectronics Low Power Controller (LPC) - RTC
+===================================================
+
+LPC currently supports Watchdog OR Real Time Clock functionality.
+
+[See: ../watchdog/st_lpc_wdt.txt for Watchdog options]
+
+Required properties
+
+- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+ "st,stih415-lpc" "st,stid127-lpc"
+- reg : LPC registers base address + size
+- interrupts : LPC interrupt line number and associated flags
+- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
+- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+ ST_LPC_MODE_WDT [1]. One (and only one) mode must be
+ selected.
+
+Example:
+ lpc@fde05000 {
+ compatible = "st,stih407-lpc";
+ reg = <0xfde05000 0x1000>;
+ clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
+ st,lpc-mode = <ST_LPC_MODE_RTC>;
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt
new file mode 100644
index 0000000..388c88a
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt
@@ -0,0 +1,38 @@
+STMicroelectronics Low Power Controller (LPC) - Watchdog
+========================================================
+
+LPC currently supports Watchdog OR Real Time Clock functionality.
+
+[See: ../rtc/rtc-st-lpc.txt for RTC options]
+
+Required properties
+
+- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+ "st,stih415-lpc" "st,stid127-lpc"
+- reg : LPC registers base address + size
+- interrupts : LPC interrupt line number and associated flags
+- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
+- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+ ST_LPC_MODE_WDT [1]. One (and only one) mode must be
+ selected.
+
+Required properties [watchdog mode]
+
+- st,syscfg : Phandle to syscfg node used to enable watchdog and configure
+ CPU reset type.
+- timeout-sec : Watchdog timeout in seconds
+
+Optional properties [watchdog mode]
+
+- st,warm-reset : If present reset type will be 'warm' - if not it will be cold
+
+Example:
+ lpc@fde05000 {
+ compatible = "st,stih407-lpc";
+ reg = <0xfde05000 0x1000>;
+ clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
+ st,syscfg = <&syscfg_core>;
+ timeout-sec = <120>;
+ st,lpc-mode = <ST_LPC_MODE_WDT>;
+ st,warm-reset;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 781e099..e476ff7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1473,10 +1473,12 @@
F: drivers/phy/phy-stih41x-usb.c
F: drivers/pinctrl/pinctrl-st.c
F: drivers/reset/sti/
+F: drivers/rtc/rtc-st-lpc.c
F: drivers/tty/serial/st-asc.c
F: drivers/usb/dwc3/dwc3-st.c
F: drivers/usb/host/ehci-st.c
F: drivers/usb/host/ohci-st.c
+F: drivers/watchdog/st_lpc_wdt.c
F: drivers/ata/ahci_st.c
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index d18029b..6df9155 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -32,6 +32,7 @@
static const char * const axp20x_model_names[] = {
"AXP202",
"AXP209",
+ "AXP221",
"AXP288",
};
@@ -54,6 +55,25 @@
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
+static const struct regmap_range axp22x_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
+};
+
+static const struct regmap_range axp22x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp22x_writeable_table = {
+ .yes_ranges = axp22x_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp22x_volatile_table = {
+ .yes_ranges = axp22x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges),
+};
+
static const struct regmap_range axp288_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
@@ -87,6 +107,20 @@
},
};
+static struct resource axp22x_pek_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP22X_IRQ_PEK_RIS_EDGE,
+ .end = AXP22X_IRQ_PEK_RIS_EDGE,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .name = "PEK_DBF",
+ .start = AXP22X_IRQ_PEK_FAL_EDGE,
+ .end = AXP22X_IRQ_PEK_FAL_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource axp288_fuel_gauge_resources[] = {
{
.start = AXP288_IRQ_QWBTU,
@@ -129,6 +163,15 @@
.cache_type = REGCACHE_RBTREE,
};
+static const struct regmap_config axp22x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp22x_writeable_table,
+ .volatile_table = &axp22x_volatile_table,
+ .max_register = AXP22X_BATLOW_THRES1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static const struct regmap_config axp288_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -181,6 +224,34 @@
INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0),
};
+static const struct regmap_irq axp22x_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V, 0, 7),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN, 0, 6),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V, 0, 4),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN, 0, 3),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW, 0, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE, 1, 5),
+ INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE, 1, 4),
+ INIT_REGMAP_IRQ(AXP22X, CHARG, 1, 3),
+ INIT_REGMAP_IRQ(AXP22X, CHARG_DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH, 1, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW, 1, 0),
+ INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH, 2, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_SHORT, 2, 1),
+ INIT_REGMAP_IRQ(AXP22X, PEK_LONG, 2, 0),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1, 3, 1),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2, 3, 0),
+ INIT_REGMAP_IRQ(AXP22X, TIMER, 4, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE, 4, 6),
+ INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE, 4, 5),
+ INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT, 4, 1),
+ INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT, 4, 0),
+};
+
/* some IRQs are compatible with axp20x models */
static const struct regmap_irq axp288_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
@@ -224,6 +295,7 @@
static const struct of_device_id axp20x_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+ { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
{ },
};
MODULE_DEVICE_TABLE(of, axp20x_of_match);
@@ -258,6 +330,18 @@
};
+static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
+ .name = "axp22x_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .irqs = axp22x_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp22x_regmap_irqs),
+ .num_regs = 5,
+};
+
static const struct regmap_irq_chip axp288_regmap_irq_chip = {
.name = "axp288_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
@@ -281,6 +365,16 @@
},
};
+static struct mfd_cell axp22x_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp22x_pek_resources),
+ .resources = axp22x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
+ },
+};
+
static struct resource axp288_adc_resources[] = {
{
.name = "GPADC",
@@ -426,6 +520,12 @@
axp20x->regmap_cfg = &axp20x_regmap_config;
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
break;
+ case AXP221_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
+ axp20x->cells = axp22x_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
+ break;
case AXP288_ID:
axp20x->cells = axp288_cells;
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index e4331f5..6468291 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -27,20 +27,24 @@
#define AXP20X_IO_ENABLED 0x03
#define AXP20X_IO_DISABLED 0x07
+#define AXP22X_IO_ENABLED 0x04
+#define AXP22X_IO_DISABLED 0x03
+
#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
+#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x)
#define AXP20X_FREQ_DCDC_MASK 0x0f
-#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask, _enable_val, _disable_val) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _enable_val, _disable_val) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -54,15 +58,15 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -74,29 +78,49 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp20x_ops_sw, \
+ }
+
+#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
+ [_family##_##_id] = { \
+ .name = #_id, \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
.n_voltages = 1, \
.owner = THIS_MODULE, \
.min_uV = (_volt) * 1000, \
.ops = &axp20x_ops_fixed \
}
-#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \
- _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask, \
+ _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = ARRAY_SIZE(_table), \
.owner = THIS_MODULE, \
.vsel_reg = (_vreg), \
@@ -135,38 +159,118 @@
.is_enabled = regulator_is_enabled_regmap,
};
+static struct regulator_ops axp20x_ops_sw = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
static const struct regulator_desc axp20x_regulators[] = {
- AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT,
- 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
- AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
- AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300),
- AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
- AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
- AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
- AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
- AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
- AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
- AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
- AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+ AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+ AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+ AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+ AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+ AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
+ AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
+ AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+ AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
+ AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
+ AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
+ AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
+ AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
+ AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+ AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+};
+
+static const struct regulator_desc axp22x_regulators[] = {
+ AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+ AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+ AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+ AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
+ AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
+ /* LDO regulator internally chained to DCDC5 */
+ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", "dcdc5", 700, 1400, 100,
+ AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
};
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ u32 min, max, def, step;
- if (dcdcfreq < 750) {
- dcdcfreq = 750;
- dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n");
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ min = 750;
+ max = 1875;
+ def = 1500;
+ step = 75;
+ break;
+ case AXP221_ID:
+ min = 1800;
+ max = 4050;
+ def = 3000;
+ step = 150;
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "Setting DCDC frequency for unsupported AXP variant\n");
+ return -EINVAL;
}
- if (dcdcfreq > 1875) {
- dcdcfreq = 1875;
- dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n");
+ if (dcdcfreq == 0)
+ dcdcfreq = def;
+
+ if (dcdcfreq < min) {
+ dcdcfreq = min;
+ dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
+ min);
}
- dcdcfreq = (dcdcfreq - 750) / 75;
+ if (dcdcfreq > max) {
+ dcdcfreq = max;
+ dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
+ max);
+ }
+
+ dcdcfreq = (dcdcfreq - min) / step;
return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
AXP20X_FREQ_DCDC_MASK, dcdcfreq);
@@ -176,7 +280,7 @@
{
struct device_node *np, *regulators;
int ret;
- u32 dcdcfreq;
+ u32 dcdcfreq = 0;
np = of_node_get(pdev->dev.parent->of_node);
if (!np)
@@ -186,7 +290,6 @@
if (!regulators) {
dev_warn(&pdev->dev, "regulators node not found\n");
} else {
- dcdcfreq = 1500;
of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
if (ret < 0) {
@@ -202,15 +305,35 @@
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
{
- unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
+ struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+ unsigned int mask;
- if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ return -EINVAL;
+
+ mask = AXP20X_WORKMODE_DCDC2_MASK;
+ if (id == AXP20X_DCDC3)
+ mask = AXP20X_WORKMODE_DCDC3_MASK;
+
+ workmode <<= ffs(mask) - 1;
+ break;
+
+ case AXP221_ID:
+ if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
+ return -EINVAL;
+
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
+ workmode <<= id - AXP22X_DCDC1;
+ break;
+
+ default:
+ /* should not happen */
+ WARN_ON(1);
return -EINVAL;
-
- if (id == AXP20X_DCDC3)
- mask = AXP20X_WORKMODE_DCDC3_MASK;
-
- workmode <<= ffs(mask) - 1;
+ }
return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
}
@@ -219,22 +342,40 @@
{
struct regulator_dev *rdev;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *regulators;
struct regulator_config config = {
.dev = pdev->dev.parent,
.regmap = axp20x->regmap,
+ .driver_data = axp20x,
};
- int ret, i;
+ int ret, i, nregulators;
u32 workmode;
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ regulators = axp20x_regulators;
+ nregulators = AXP20X_REG_ID_MAX;
+ break;
+ case AXP221_ID:
+ regulators = axp22x_regulators;
+ nregulators = AXP22X_REG_ID_MAX;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+ axp20x->variant);
+ return -EINVAL;
+ }
+
/* This only sets the dcdc freq. Ignore any errors */
axp20x_regulator_parse_dt(pdev);
- for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
- rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i],
+ for (i = 0; i < nregulators; i++) {
+ rdev = devm_regulator_register(&pdev->dev, ®ulators[i],
&config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "Failed to register %s\n",
- axp20x_regulators[i].name);
+ regulators[i].name);
return PTR_ERR(rdev);
}
@@ -245,7 +386,7 @@
if (!ret) {
if (axp20x_set_dcdc_workmode(rdev, i, workmode))
dev_err(&pdev->dev, "Failed to set workmode on %s\n",
- axp20x_regulators[i].name);
+ rdev->desc->name);
}
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 6149ae0..8b8b332 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1510,6 +1510,17 @@
Say "yes" here to support the real time clock on SiRF SOC chips.
This driver can also be built as a module called rtc-sirfsoc.
+config RTC_DRV_ST_LPC
+ tristate "STMicroelectronics LPC RTC"
+ depends on ARCH_STI
+ depends on OF
+ help
+ Say Y here to include STMicroelectronics Low Power Controller
+ (LPC) based RTC support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-st-lpc.
+
config RTC_DRV_MOXART
tristate "MOXA ART RTC"
depends on ARCH_MOXART || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index c31731c..411e630 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -153,4 +153,5 @@
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644
index 0000000..3f9d0ac
--- /dev/null
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -0,0 +1,354 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ * Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF 0x400
+#define LPC_LPT_MSB_OFF 0x404
+#define LPC_LPT_START_OFF 0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF 0x410
+#define LPC_LPA_MSB_OFF 0x414
+#define LPC_LPA_START_OFF 0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF 0x510
+#define LPC_WDT_FLAG_OFF 0x514
+
+struct st_rtc {
+ struct rtc_device *rtc_dev;
+ struct rtc_wkalrm alarm;
+ struct resource *res;
+ struct clk *clk;
+ unsigned long clkrate;
+ void __iomem *ioaddr;
+ bool irq_enabled:1;
+ spinlock_t lock;
+ short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+ unsigned long msb, unsigned long lsb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+
+ writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+ writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+ struct st_rtc *rtc = (struct st_rtc *)data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long lpt_lsb, lpt_msb;
+ unsigned long long lpt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ do {
+ lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF);
+ lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF);
+ } while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+ do_div(lpt, rtc->clkrate);
+ rtc_time_to_tm(lpt, tm);
+
+ return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long long lpt;
+ unsigned long secs, flags;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &secs);
+ if (ret)
+ return ret;
+
+ lpt = (unsigned long long)secs * rtc->clkrate;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+ writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled && !rtc->irq_enabled) {
+ enable_irq(rtc->irq);
+ rtc->irq_enabled = true;
+ } else if (!enabled && rtc->irq_enabled) {
+ disable_irq(rtc->irq);
+ rtc->irq_enabled = false;
+ }
+
+ return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ struct rtc_time now;
+ unsigned long now_secs;
+ unsigned long alarm_secs;
+ unsigned long long lpa;
+
+ st_rtc_read_time(dev, &now);
+ rtc_tm_to_time(&now, &now_secs);
+ rtc_tm_to_time(&t->time, &alarm_secs);
+
+ /* Invalid alarm time */
+ if (now_secs > alarm_secs)
+ return -EINVAL;
+
+ memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+ /* Now many secs to fire */
+ alarm_secs -= now_secs;
+ lpa = (unsigned long long)alarm_secs * rtc->clkrate;
+
+ st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+ st_rtc_alarm_irq_enable(dev, t->enabled);
+
+ return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+ .read_time = st_rtc_read_time,
+ .set_time = st_rtc_set_time,
+ .read_alarm = st_rtc_read_alarm,
+ .set_alarm = st_rtc_set_alarm,
+ .alarm_irq_enable = st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct st_rtc *rtc;
+ struct resource *res;
+ struct rtc_time tm_check;
+ uint32_t mode;
+ int ret = 0;
+
+ ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+ if (ret) {
+ dev_err(&pdev->dev, "An LPC mode must be provided\n");
+ return -EINVAL;
+ }
+
+ /* LPC can either run in RTC or WDT mode */
+ if (mode != ST_LPC_MODE_RTC)
+ return -ENODEV;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->ioaddr))
+ return PTR_ERR(rtc->ioaddr);
+
+ rtc->irq = irq_of_parse_and_map(np, 0);
+ if (!rtc->irq) {
+ dev_err(&pdev->dev, "IRQ missing or invalid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+ pdev->name, rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+ return ret;
+ }
+
+ enable_irq_wake(rtc->irq);
+ disable_irq(rtc->irq);
+
+ rtc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rtc->clk)) {
+ dev_err(&pdev->dev, "Unable to request clock\n");
+ return PTR_ERR(rtc->clk);
+ }
+
+ clk_prepare_enable(rtc->clk);
+
+ rtc->clkrate = clk_get_rate(rtc->clk);
+ if (!rtc->clkrate) {
+ dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+ return -EINVAL;
+ }
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+
+ /*
+ * The RTC-LPC is able to manage date.year > 2038
+ * but currently the kernel can not manage this date!
+ * If the RTC-LPC has a date.year > 2038 then
+ * it's set to the epoch "Jan 1st 2000"
+ */
+ st_rtc_read_time(&pdev->dev, &tm_check);
+
+ if (tm_check.tm_year >= (2038 - 1900)) {
+ memset(&tm_check, 0, sizeof(tm_check));
+ tm_check.tm_year = 100;
+ tm_check.tm_mday = 1;
+ st_rtc_set_time(&pdev->dev, &tm_check);
+ }
+
+ rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+ &st_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ clk_disable_unprepare(rtc->clk);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+ struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (likely(rtc->rtc_dev))
+ rtc_device_unregister(rtc->rtc_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_rtc_suspend(struct device *dev)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ return 0;
+
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+ /*
+ * clean 'rtc->alarm' to allow a new
+ * .set_alarm to the upper RTC layer
+ */
+ memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static const struct of_device_id st_rtc_match[] = {
+ { .compatible = "st,stih407-lpc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, st_rtc_match);
+
+static struct platform_driver st_rtc_platform_driver = {
+ .driver = {
+ .name = "st-lpc-rtc",
+ .pm = &st_rtc_pm_ops,
+ .of_match_table = st_rtc_match,
+ },
+ .probe = st_rtc_probe,
+ .remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index e5e7c55..262647b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -470,6 +470,18 @@
Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
the watchdog triggers the system will be reset.
+config ST_LPC_WATCHDOG
+ tristate "STMicroelectronics LPC Watchdog"
+ depends on ARCH_STI
+ depends on OF
+ select WATCHDOG_CORE
+ help
+ Say Y here to include STMicroelectronics Low Power Controller
+ (LPC) based Watchdog timer support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st_lpc_wdt.
+
config TEGRA_WATCHDOG
tristate "Tegra watchdog"
depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294..d98768c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -59,6 +59,7 @@
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
new file mode 100644
index 0000000..f32be15
--- /dev/null
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -0,0 +1,344 @@
+/*
+ * ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ * Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF 0x410
+#define LPC_LPA_START_OFF 0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF 0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+ unsigned int reset_type_reg;
+ unsigned int reset_type_mask;
+ unsigned int enable_reg;
+ unsigned int enable_mask;
+};
+
+struct st_wdog {
+ void __iomem *base;
+ struct device *dev;
+ struct regmap *regmap;
+ struct st_wdog_syscfg *syscfg;
+ struct clk *clk;
+ unsigned long clkrate;
+ bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+ .reset_type_reg = 0x004,
+ .reset_type_mask = BIT(2),
+ .enable_reg = 0x000,
+ .enable_mask = BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+ .reset_type_reg = 0x0B8,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x0B4,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+ .reset_type_reg = 0x88C,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x888,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+ .enable_reg = 0x204,
+ .enable_mask = BIT(19),
+};
+
+static const struct of_device_id st_wdog_match[] = {
+ {
+ .compatible = "st,stih407-lpc",
+ .data = &stih407_syscfg,
+ },
+ {
+ .compatible = "st,stih416-lpc",
+ .data = &stih416_syscfg,
+ },
+ {
+ .compatible = "st,stih415-lpc",
+ .data = &stih415_syscfg,
+ },
+ {
+ .compatible = "st,stid127-lpc",
+ .data = &stid127_syscfg,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+ /* Type of watchdog reset - 0: Cold 1: Warm */
+ if (st_wdog->syscfg->reset_type_reg)
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->reset_type_reg,
+ st_wdog->syscfg->reset_type_mask,
+ st_wdog->warm_reset);
+
+ /* Mask/unmask watchdog reset */
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->enable_reg,
+ st_wdog->syscfg->enable_mask,
+ enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+ unsigned long clkrate = st_wdog->clkrate;
+
+ writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ wdd->timeout = timeout;
+ st_wdog_load_timer(st_wdog, timeout);
+
+ return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ st_wdog_load_timer(st_wdog, wdd->timeout);
+
+ return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+ .owner = THIS_MODULE,
+ .start = st_wdog_start,
+ .stop = st_wdog_stop,
+ .ping = st_wdog_keepalive,
+ .set_timeout = st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+ .info = &st_wdog_info,
+ .ops = &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct st_wdog *st_wdog;
+ struct regmap *regmap;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ uint32_t mode;
+ int ret;
+
+ ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+ if (ret) {
+ dev_err(&pdev->dev, "An LPC mode must be provided\n");
+ return -EINVAL;
+ }
+
+ /* LPC can either run in RTC or WDT mode */
+ if (mode != ST_LPC_MODE_WDT)
+ return -ENODEV;
+
+ st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+ if (!st_wdog)
+ return -ENOMEM;
+
+ match = of_match_device(st_wdog_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Couldn't match device\n");
+ return -ENODEV;
+ }
+ st_wdog->syscfg = (struct st_wdog_syscfg *)match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(regmap);
+ }
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Unable to request clock\n");
+ return PTR_ERR(clk);
+ }
+
+ st_wdog->dev = &pdev->dev;
+ st_wdog->base = base;
+ st_wdog->clk = clk;
+ st_wdog->regmap = regmap;
+ st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset");
+ st_wdog->clkrate = clk_get_rate(st_wdog->clk);
+
+ if (!st_wdog->clkrate) {
+ dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+ return -EINVAL;
+ }
+ st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable clock\n");
+ return ret;
+ }
+
+ watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+ watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+ /* Init Watchdog timeout with value in DT */
+ ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ ret = watchdog_register_device(&st_wdog_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register watchdog\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+ st_wdog->warm_reset ? "warm" : "cold");
+
+ return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_disable_unprepare(st_wdog->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_wdog_suspend(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ if (watchdog_active(&st_wdog_dev))
+ st_wdog_stop(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+
+ clk_disable(st_wdog->clk);
+
+ return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+ int ret;
+
+ ret = clk_enable(st_wdog->clk);
+ if (ret) {
+ dev_err(dev, "Unable to re-enable clock\n");
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_unprepare(st_wdog->clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ if (watchdog_active(&st_wdog_dev)) {
+ st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+ st_wdog_start(&st_wdog_dev);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+ st_wdog_suspend,
+ st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+ .driver = {
+ .name = "st-lpc-wdt",
+ .pm = &st_wdog_pm_ops,
+ .of_match_table = st_wdog_match,
+ },
+ .probe = st_wdog_probe,
+ .remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h
new file mode 100644
index 0000000..e3e6c75
--- /dev/null
+++ b/include/dt-bindings/mfd/st-lpc.h
@@ -0,0 +1,15 @@
+/*
+ * This header provides shared DT/Driver defines for ST's LPC device
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ */
+
+#ifndef __DT_BINDINGS_ST_LPC_H__
+#define __DT_BINDINGS_ST_LPC_H__
+
+#define ST_LPC_MODE_RTC 0
+#define ST_LPC_MODE_WDT 1
+
+#endif /* __DT_BINDINGS_ST_LPC_H__ */
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index dfabd6d..95568eb 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -14,6 +14,7 @@
enum {
AXP202_ID = 0,
AXP209_ID,
+ AXP221_ID,
AXP288_ID,
NR_AXP20X_VARIANTS,
};
@@ -45,6 +46,28 @@
#define AXP20X_V_LTF_DISCHRG 0x3c
#define AXP20X_V_HTF_DISCHRG 0x3d
+#define AXP22X_PWR_OUT_CTRL1 0x10
+#define AXP22X_PWR_OUT_CTRL2 0x12
+#define AXP22X_PWR_OUT_CTRL3 0x13
+#define AXP22X_DLDO1_V_OUT 0x15
+#define AXP22X_DLDO2_V_OUT 0x16
+#define AXP22X_DLDO3_V_OUT 0x17
+#define AXP22X_DLDO4_V_OUT 0x18
+#define AXP22X_ELDO1_V_OUT 0x19
+#define AXP22X_ELDO2_V_OUT 0x1a
+#define AXP22X_ELDO3_V_OUT 0x1b
+#define AXP22X_DC5LDO_V_OUT 0x1c
+#define AXP22X_DCDC1_V_OUT 0x21
+#define AXP22X_DCDC2_V_OUT 0x22
+#define AXP22X_DCDC3_V_OUT 0x23
+#define AXP22X_DCDC4_V_OUT 0x24
+#define AXP22X_DCDC5_V_OUT 0x25
+#define AXP22X_DCDC23_V_RAMP_CTRL 0x27
+#define AXP22X_ALDO1_V_OUT 0x28
+#define AXP22X_ALDO2_V_OUT 0x29
+#define AXP22X_ALDO3_V_OUT 0x2a
+#define AXP22X_CHRG_CTRL3 0x35
+
/* Interrupt */
#define AXP20X_IRQ1_EN 0x40
#define AXP20X_IRQ2_EN 0x41
@@ -100,6 +123,9 @@
#define AXP20X_VBUS_MON 0x8b
#define AXP20X_OVER_TMP 0x8f
+#define AXP22X_PWREN_CTRL1 0x8c
+#define AXP22X_PWREN_CTRL2 0x8d
+
/* GPIO */
#define AXP20X_GPIO0_CTRL 0x90
#define AXP20X_LDO5_V_OUT 0x91
@@ -108,6 +134,11 @@
#define AXP20X_GPIO20_SS 0x94
#define AXP20X_GPIO3_CTRL 0x95
+#define AXP22X_LDO_IO0_V_OUT 0x91
+#define AXP22X_LDO_IO1_V_OUT 0x93
+#define AXP22X_GPIO_STATE 0x94
+#define AXP22X_GPIO_PULL_DOWN 0x95
+
/* Battery */
#define AXP20X_CHRG_CC_31_24 0xb0
#define AXP20X_CHRG_CC_23_16 0xb1
@@ -120,6 +151,9 @@
#define AXP20X_CC_CTRL 0xb8
#define AXP20X_FG_RES 0xb9
+/* AXP22X specific registers */
+#define AXP22X_BATLOW_THRES1 0xe6
+
/* AXP288 specific registers */
#define AXP288_PMIC_ADC_H 0x56
#define AXP288_PMIC_ADC_L 0x57
@@ -158,6 +192,30 @@
AXP20X_REG_ID_MAX,
};
+enum {
+ AXP22X_DCDC1 = 0,
+ AXP22X_DCDC2,
+ AXP22X_DCDC3,
+ AXP22X_DCDC4,
+ AXP22X_DCDC5,
+ AXP22X_DC1SW,
+ AXP22X_DC5LDO,
+ AXP22X_ALDO1,
+ AXP22X_ALDO2,
+ AXP22X_ALDO3,
+ AXP22X_ELDO1,
+ AXP22X_ELDO2,
+ AXP22X_ELDO3,
+ AXP22X_DLDO1,
+ AXP22X_DLDO2,
+ AXP22X_DLDO3,
+ AXP22X_DLDO4,
+ AXP22X_RTC_LDO,
+ AXP22X_LDO_IO0,
+ AXP22X_LDO_IO1,
+ AXP22X_REG_ID_MAX,
+};
+
/* IRQs */
enum {
AXP20X_IRQ_ACIN_OVER_V = 1,
@@ -199,6 +257,34 @@
AXP20X_IRQ_GPIO0_INPUT,
};
+enum axp22x_irqs {
+ AXP22X_IRQ_ACIN_OVER_V = 1,
+ AXP22X_IRQ_ACIN_PLUGIN,
+ AXP22X_IRQ_ACIN_REMOVAL,
+ AXP22X_IRQ_VBUS_OVER_V,
+ AXP22X_IRQ_VBUS_PLUGIN,
+ AXP22X_IRQ_VBUS_REMOVAL,
+ AXP22X_IRQ_VBUS_V_LOW,
+ AXP22X_IRQ_BATT_PLUGIN,
+ AXP22X_IRQ_BATT_REMOVAL,
+ AXP22X_IRQ_BATT_ENT_ACT_MODE,
+ AXP22X_IRQ_BATT_EXIT_ACT_MODE,
+ AXP22X_IRQ_CHARG,
+ AXP22X_IRQ_CHARG_DONE,
+ AXP22X_IRQ_BATT_TEMP_HIGH,
+ AXP22X_IRQ_BATT_TEMP_LOW,
+ AXP22X_IRQ_DIE_TEMP_HIGH,
+ AXP22X_IRQ_PEK_SHORT,
+ AXP22X_IRQ_PEK_LONG,
+ AXP22X_IRQ_LOW_PWR_LVL1,
+ AXP22X_IRQ_LOW_PWR_LVL2,
+ AXP22X_IRQ_TIMER,
+ AXP22X_IRQ_PEK_RIS_EDGE,
+ AXP22X_IRQ_PEK_FAL_EDGE,
+ AXP22X_IRQ_GPIO1_INPUT,
+ AXP22X_IRQ_GPIO0_INPUT,
+};
+
enum axp288_irqs {
AXP288_IRQ_VBUS_FALL = 2,
AXP288_IRQ_VBUS_RISE,