diff --git a/Documentation/devicetree/bindings/power/supply/cps5601x_charger.txt b/Documentation/devicetree/bindings/power/supply/cps5601x_charger.txt new file mode 100644 index 000000000000..8ce8411fc6d8 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/cps5601x_charger.txt @@ -0,0 +1,32 @@ +Binding for cps5601x battery charger + +Required properties: +- compatible: "cps,cps5601x" for cps5601x Charger Power Supply +- monitored-battery: phandle of battery characteristics devicetree node +- input-voltage-limit-microvolt: integer, input voltage level in uV, used to + decrease voltage level when the over current + of the input power source occurs. +- input-current-limit-microamp: integer, input current value in uA drained by the + charger from the power source.Default: 500000 uA (500mA) + +child nodes: +- vbus5v0_typec: + Usage: optional + Description: Regulator that is used to control the VBUS voltage direction for + either USB host mode or for charging on the OTG port. + +Example: + +cps5601x: cps5601x@60 { + compatible = "cps,cps5601x"; + reg = <0x60>; + input-voltage-limit-microvolt = <4500000>; + input-current-limit-microamp = <3000000>; + monitored-battery = <&bat>; + regulators { + vbus5v0_typec: vbus5v0-typec { + regulator-compatible = "otg-vbus"; + regulator-name = "vbus5v0_typec"; + }; + }; + }; diff --git a/drivers/input/touchscreen/gt1x/gt1x.c b/drivers/input/touchscreen/gt1x/gt1x.c index 7d54cd651ea2..32e5d1f5851c 100644 --- a/drivers/input/touchscreen/gt1x/gt1x.c +++ b/drivers/input/touchscreen/gt1x/gt1x.c @@ -31,8 +31,8 @@ static const struct dev_pm_ops gt1x_ts_pm_ops; #endif #ifdef GTP_CONFIG_OF bool gt1x_gt5688; -int gt1x_rst_gpio; -int gt1x_int_gpio; +struct gpio_desc *gt1x_rst_gpio; +struct gpio_desc *gt1x_int_gpio; static bool power_invert; #endif @@ -321,19 +321,16 @@ static int gt1x_parse_dt(struct device *dev) gt1x_gt5688 = true; } - gt1x_int_gpio = of_get_named_gpio(np, "goodix,irq-gpio", 0); - gt1x_rst_gpio = of_get_named_gpio(np, "goodix,rst-gpio", 0); - - if (!gpio_is_valid(gt1x_int_gpio) && !gpio_is_valid(gt1x_rst_gpio)) { - GTP_ERROR("Invalid GPIO, irq-gpio:%d, rst-gpio:%d", - gt1x_int_gpio, gt1x_rst_gpio); - return -EINVAL; + gt1x_int_gpio = devm_gpiod_get(dev, "goodix,irq", GPIOD_IN); + if (IS_ERR(gt1x_int_gpio)) { + GTP_ERROR("Failed to get irq GPIO"); + return PTR_ERR(gt1x_int_gpio); } - if (!gpio_is_valid(gt1x_int_gpio)) { - GTP_ERROR("Invalid GPIO, irq-gpio:%d", - gt1x_int_gpio); - return -EINVAL; + gt1x_rst_gpio = devm_gpiod_get_optional(dev, "goodix,rst", GPIOD_OUT_LOW); + if (IS_ERR(gt1x_rst_gpio)) { + GTP_ERROR("Failed to get reset GPIO"); + return PTR_ERR(gt1x_rst_gpio); } vdd_ana = devm_regulator_get_optional(dev, "vdd_ana"); @@ -403,12 +400,6 @@ int gt1x_power_switch(int on) static void gt1x_remove_gpio_and_power(void) { - if (gpio_is_valid(gt1x_int_gpio)) - gpio_free(gt1x_int_gpio); - - if (gpio_is_valid(gt1x_rst_gpio)) - gpio_free(gt1x_rst_gpio); - if (gt1x_i2c_client && gt1x_i2c_client->irq) free_irq(gt1x_i2c_client->irq, gt1x_i2c_client); } @@ -419,27 +410,15 @@ static void gt1x_remove_gpio_and_power(void) */ static s32 gt1x_request_io_port(void) { - s32 ret = 0; - GTP_DEBUG_FUNC(); - ret = gpio_request(GTP_INT_PORT, "GTP_INT_IRQ"); - if (ret < 0) { - GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32) GTP_INT_PORT, ret); - return ret; - } + if (IS_ERR_OR_NULL(gt1x_int_gpio)) + return -1; GTP_GPIO_AS_INT(GTP_INT_PORT); gt1x_i2c_client->irq = GTP_INT_IRQ; - - if (gpio_is_valid(gt1x_rst_gpio)) { - ret = gpio_request(GTP_RST_PORT, "GTP_RST_PORT"); - if (ret < 0) { - GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32) GTP_RST_PORT, ret); - gpio_free(GTP_INT_PORT); - return ret; - } - - GTP_GPIO_AS_INPUT(GTP_RST_PORT); + if (gt1x_i2c_client->irq < 0) { + GTP_ERROR("Failed to get IRQ from GPIO"); + return gt1x_i2c_client->irq; } return 0; @@ -458,11 +437,12 @@ static s32 gt1x_request_irq(void) GTP_DEBUG_FUNC(); GTP_DEBUG("INT trigger type:%x", gt1x_int_type); + if (IS_ERR_OR_NULL(gt1x_int_gpio)) + return -1; ret = request_irq(gt1x_i2c_client->irq, gt1x_ts_irq_handler, irq_table[gt1x_int_type], gt1x_i2c_client->name, gt1x_i2c_client); if (ret) { GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret); GTP_GPIO_AS_INPUT(GTP_INT_PORT); - gpio_free(GTP_INT_PORT); return -1; } else { diff --git a/drivers/input/touchscreen/gt1x/gt1x.h b/drivers/input/touchscreen/gt1x/gt1x.h index b9339f10b0ad..9f4488446116 100644 --- a/drivers/input/touchscreen/gt1x/gt1x.h +++ b/drivers/input/touchscreen/gt1x/gt1x.h @@ -20,9 +20,8 @@ #ifndef _GOODIX_GT1X_H_ #define _GOODIX_GT1X_H_ #include "gt1x_generic.h" -#include +#include #ifdef GTP_CONFIG_OF -#include #include #endif #ifdef CONFIG_FB @@ -38,8 +37,8 @@ /* Customize your I/O ports & I/O operations */ #ifdef GTP_CONFIG_OF extern bool gt1x_gt5688; -extern int gt1x_rst_gpio; -extern int gt1x_int_gpio; +extern struct gpio_desc *gt1x_int_gpio; +extern struct gpio_desc *gt1x_rst_gpio; extern struct regulator *gt1x_supply; #define GTP_RST_PORT gt1x_rst_gpio #define GTP_INT_PORT gt1x_int_gpio @@ -48,17 +47,17 @@ extern struct regulator *gt1x_supply; #define GTP_INT_PORT 52 #endif -#define GTP_INT_IRQ gpio_to_irq(GTP_INT_PORT) +#define GTP_INT_IRQ gpiod_to_irq(GTP_INT_PORT) /*#define GTP_INT_CFG S3C_GPIO_SFN(0xF)*/ #define GTP_GPIO_AS_INPUT(pin) do {\ - gpio_direction_input(pin);\ + gpiod_direction_input(pin);\ } while (0) #define GTP_GPIO_AS_INT(pin) do {\ GTP_GPIO_AS_INPUT(pin);\ } while (0) -#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin) -#define GTP_GPIO_OUTPUT(pin, level) gpio_direction_output(pin, level) +#define GTP_GPIO_GET_VALUE(pin) gpiod_get_raw_value(pin) +#define GTP_GPIO_OUTPUT(pin, level) gpiod_direction_output_raw(pin, level) #define GTP_IRQ_TAB {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH} #endif /* _GOODIX_GT1X_H_ */ diff --git a/drivers/input/touchscreen/gt1x/gt1x_generic.c b/drivers/input/touchscreen/gt1x/gt1x_generic.c index 5238aa0ae2b9..3840444c0131 100644 --- a/drivers/input/touchscreen/gt1x/gt1x_generic.c +++ b/drivers/input/touchscreen/gt1x/gt1x_generic.c @@ -663,11 +663,11 @@ s32 gt1x_init_panel(void) void gt1x_select_addr(void) { - if (gpio_is_valid(gt1x_rst_gpio)) + if (!IS_ERR_OR_NULL(gt1x_rst_gpio)) GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); GTP_GPIO_OUTPUT(GTP_INT_PORT, gt1x_i2c_client->addr == 0x14); usleep_range(2000, 3000); - if (gpio_is_valid(gt1x_rst_gpio)) + if (!IS_ERR_OR_NULL(gt1x_rst_gpio)) GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); usleep_range(2000, 3000); } diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c index e644a1a0367e..9ea9e8265f22 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1178,7 +1179,6 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev) struct csi2_dphy_hw *dphy_hw; struct regmap *grf; struct resource *res; - const struct of_device_id *of_id; const struct dphy_hw_drv_data *drv_data; dphy_hw = devm_kzalloc(dev, sizeof(*dphy_hw), GFP_KERNEL); @@ -1186,12 +1186,10 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev) return -ENOMEM; dphy_hw->dev = dev; - of_id = of_match_device(rockchip_csi2_dphy_hw_match_id, dev); - if (!of_id) + drv_data = device_get_match_data(dev); + if (!drv_data) return -EINVAL; - drv_data = of_id->data; - grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(grf)) { diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c index 489afe88abd2..ad589467654b 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1171,7 +1172,6 @@ static int rockchip_csi2_dphy_get_hw(struct csi2_dphy *dphy) static int rockchip_csi2_dphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; struct csi2_dphy *csi2dphy; struct v4l2_subdev *sd; const struct dphy_drv_data *drv_data; @@ -1182,10 +1182,9 @@ static int rockchip_csi2_dphy_probe(struct platform_device *pdev) return -ENOMEM; csi2dphy->dev = dev; - of_id = of_match_device(rockchip_csi2_dphy_match_id, dev); - if (!of_id) + drv_data = device_get_match_data(dev); + if (!drv_data) return -EINVAL; - drv_data = of_id->data; csi2dphy->drv_data = drv_data; csi2dphy->phy_index = of_alias_get_id(dev->of_node, drv_data->dev_name); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c index f345f23afbd1..9fdf3005e2d0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -827,12 +828,11 @@ static int rockchip_u3phy_probe(struct platform_device *pdev) struct phy_provider *provider; struct rockchip_u3phy *u3phy; const struct rockchip_u3phy_cfg *phy_cfgs; - const struct of_device_id *match; unsigned int reg[2]; int index, ret; - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { + phy_cfgs = device_get_match_data(dev); + if (!phy_cfgs) { dev_err(dev, "phy-cfgs are not assigned!\n"); return -EINVAL; } @@ -861,7 +861,6 @@ static int rockchip_u3phy_probe(struct platform_device *pdev) u3phy->dev = dev; u3phy->vbus_enabled = false; - phy_cfgs = match->data; platform_set_drvdata(pdev, u3phy); /* find out a proper config which can be matched with dt. */ diff --git a/drivers/phy/rockchip/phy-rockchip-mipi-rx.c b/drivers/phy/rockchip/phy-rockchip-mipi-rx.c index 2b9ef776bbf7..57f86ea96fed 100644 --- a/drivers/phy/rockchip/phy-rockchip-mipi-rx.c +++ b/drivers/phy/rockchip/phy-rockchip-mipi-rx.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1801,7 +1802,6 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev) struct mipidphy_priv *priv; struct regmap *grf; struct resource *res; - const struct of_device_id *of_id; const struct dphy_drv_data *drv_data; int i, ret; @@ -1810,10 +1810,6 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev) return -ENOMEM; priv->dev = dev; - of_id = of_match_device(rockchip_mipidphy_match_id, dev); - if (!of_id) - return -EINVAL; - grf = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(grf)) { grf = syscon_regmap_lookup_by_phandle(dev->of_node, @@ -1829,9 +1825,11 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev) if (priv->phy_index < 0) priv->phy_index = 0; - drv_data = of_id->data; + drv_data = device_get_match_data(dev); if (soc_is_px30s()) drv_data = &rk3326s_mipidphy_drv_data; + if (!drv_data) + return -EINVAL; for (i = 0; i < drv_data->num_clks; i++) { priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]); diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 53adee16a3e3..9fb0a449011c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -490,6 +490,14 @@ config CHARGER_CPCAP Say Y to enable support for CPCAP PMIC charger driver for Motorola mobile devices such as Droid 4. +config CHARGER_CPS5601X + tristate "CPS5601X charger driver" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + select REGMAP_I2C + help + Say Y to enable support for the CPS5601X battery charger. + config CHARGER_ISP1704 tristate "ISP1704 USB Charger Detection" depends on USB_PHY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index da0cf3f8519d..5084beae256b 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o +obj-$(CONFIG_CHARGER_CPS5601X) += cps5601x_charger.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o diff --git a/drivers/power/supply/cps5601x_charger.c b/drivers/power/supply/cps5601x_charger.c new file mode 100644 index 000000000000..11fd82b8fc8c --- /dev/null +++ b/drivers/power/supply/cps5601x_charger.c @@ -0,0 +1,1256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Chrager driver for cps5601x + * + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * + * Author: Chen Shunqing + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int dbg_enable; + +module_param_named(dbg_level, dbg_enable, int, 0644); + +#define DBG(args...) \ + do { \ + if (dbg_enable) { \ + pr_info(args); \ + } \ + } while (0) + +#define CPS5601X_MANUFACTURER "ConvenientPower" +#define CPS5601X_MODEL_NAME "cps5601x" + +#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) + +/* Register 00h */ +#define CPS5601X_REG_00 0x00 +#define CPS5601X_PRODUCT_ID_MASK GENMASK(7, 0) +/* default 0xA9=CPS5601 */ + +/* Register 01h */ +#define CPS5601X_REG_01 0x01 + +/* Register 02h */ +#define CPS5601X_REG_02 0x02 + +/* Register 03h */ +#define CPS5601X_REG_03 0x03 +#define VREG_MASK GENMASK(6, 0) +#define VREG_BASE 3600000 +#define VREG_LSB 10000 +#define VREG_MAXVAL 0x6e + +/* Register 04h */ +#define CPS5601X_REG_04 0x04 +#define ICHG_MASK GENMASK(6, 0) +#define ICHG_BASE 0 +#define ICHG_LSB 25000 +#define ICHG_MINVAL 0x4 +#define ICHG_MAXVAL 0x78 + +/* Register 05h */ +#define CPS5601X_REG_05 0x05 +#define EN_TERM_MASK BIT(6) +#define EN_TERM_ENABLE(x) UPDATE(x, 6, 6) +#define IPRECHG_MASK GENMASK(5, 0) +#define IPRECHG_BASE 0 +#define IPRECHG_LSB 12500 +#define IPRECHG_MINVAL 0x1 +#define IPRECHG_MAXVAL 0x3c + +/* Register 06h */ +#define CPS5601X_REG_06 0x06 +#define ITERM_MASK GENMASK(5, 0) +#define ITERM_BASE 0 +#define ITERM_LSB 10000 +#define ITERM_MINVAL 0x5 +#define ITERM_MAXVAL 0x3c + +/* Register 07h */ +#define CPS5601X_REG_07 0x07 +#define VINDPM_MASK GENMASK(5, 0) +#define VINDPM_BASE 3400000 +#define VINDPM_LSB 100000 +#define VINDPM_MINVAL 0x4 +#define VINDPM_MAXVAL 0x3e + +/* Register 08h */ +#define CPS5601X_REG_08 0x08 +#define IINDPM_MASK GENMASK(5, 0) +#define IINDPM_BASE 50000 +#define IINDPM_LSB 50000 +#define IINDPM_MINVAL 0x1 + +/* Register 09h */ +#define CPS5601X_REG_09 0x09 +#define VOTG_MASK GENMASK(5, 0) +#define VOTG_BASE 3400000 +#define VOTG_LSB 100000 +#define VOTG_MAXVAL 0x3e + +/* Register 0Ah */ +#define CPS5601X_REG_0A 0x0A +#define IOTG_MASK GENMASK(5, 0) +#define IOTG_BASE 50000 +#define IOTG_LSB 50000 +#define IOTG_MINVAL 0x1 + +/* Register 0Bh */ +#define CPS5601X_REG_0B 0x0B +#define WATCHDOG_MASK GENMASK(7, 6) +#define WATCHDOG_TIME(x) UPDATE(x, 7, 6) +#define WATCHDOG_BASE 0 +#define WATCHDOG_LSB 40 +#define WD_RST_MASK BIT(5) +#define WD_RST(x) UPDATE(x, 5, 5) +#define EN_CHG_MASK BIT(3) +#define EN_CHG(x) UPDATE(x, 3, 3) + +/* Register 0Ch */ +#define CPS5601X_REG_0C 0x0C +#define EN_OTG_MASK BIT(3) +#define EN_OTG(x) UPDATE(x, 3, 3) + +/* Register 0Dh */ +#define CPS5601X_REG_0D 0x0D + +/* Register 0Eh */ +#define CPS5601X_REG_0E 0x0E +#define TS_IGNORE_MASK BIT(0) +#define EN_TS_IGNORE(x) UPDATE(x, 0, 0) + +/* Register 0Fh */ +#define CPS5601X_REG_0F 0x0F +#define PG_STAT_MASK BIT(3) + +/* Register 10h */ +#define CPS5601X_REG_10 0x10 +#define CHG_STAT_MASK GENMASK(7, 5) +#define CHG_STAT_SHIFT 5 +#define CHG_STAT_NOTCHG 0 +#define CHG_STAT_TRICKLECHG 1 +#define CHG_STAT_PRECHG 2 +#define CHG_STAT_FASTCHG 3 +#define CHG_STAT_TAPERCHG 4 +#define CHG_STAT_RESERVED 5 +#define CHG_STAT_TOTACHG 6 +#define CHG_STAT_CHGTERM 7 +#define VBUS_STAT_MASK GENMASK(4, 1) +#define VBUS_STAT_SHIFT 1 +#define VBUS_STAT_NOT 0 +#define VBUS_STAT_USBSDP 1 +#define VBUS_STAT_USBCDP 2 +#define VBUS_STAT_USBDCP 3 +#define VBUS_STAT_HVDCP 4 +#define VBUS_STAT_UNKNOWN 5 +#define VBUS_STAT_NONSTANDARD 6 +#define VBUS_STAT_OTGMODE 7 +#define VBUS_STAT_NOTQUALIFIED 8 + +/* Register 11h */ +#define CPS5601X_REG_11 0x11 + +/* Register 12h */ +#define CPS5601X_REG_12 0x12 + +/* Register 13h */ +#define CPS5601X_REG_13 0x13 + +/* Register 14h */ +#define CPS5601X_REG_14 0x14 + +/* Register 15h */ +#define CPS5601X_REG_15 0x15 + +/* Register 16h */ +#define CPS5601X_REG_16 0x16 + +/* Register 17h */ +#define CPS5601X_REG_17 0x17 + +/* Register 18h */ +#define CPS5601X_REG_18 0x18 + +/* Register 19h */ +#define CPS5601X_REG_19 0x19 +#define TREG_MK_MASK BIT(7) + +/* Register 1Ah */ +#define CPS5601X_REG_1A 0x1A + +/* Register 1Bh */ +#define CPS5601X_REG_1B 0x1B + +#define CPS5601X_ICHRG_I_DEF_uA 2040000 +#define CPS5601X_VREG_V_DEF_uV 4208000 +#define CPS5601X_PRECHRG_I_DEF_uA 180000 +#define CPS5601X_TERMCHRG_I_DEF_uA 180000 +#define CPS5601X_ICHRG_I_MIN_uA 100000 +#define CPS5601X_ICHRG_I_MAX_uA 3000000 +#define CPS5601X_VINDPM_DEF_uV 4500000 +#define CPS5601X_VINDPM_V_MIN_uV 3800000 +#define CPS5601X_VINDPM_V_MAX_uV 9600000 +#define CPS5601X_IINDPM_DEF_uA 2400000 +#define CPS5601X_IINDPM_I_MIN_uA 100000 +#define CPS5601X_IINDPM_I_MAX_uA 3200000 +#define DEFAULT_INPUT_CURRENT (500 * 1000) + +static char *charge_state_str[] = { + "No Charge", + "Trickle Charge", + "Pre-Charge", + "Fast charge", + "Taper charge", + "Unknown", + "Top-off timer active charging", + "Charge terminated", +}; + +static char *charge_type_str[] = { + "No Input", + "USB SDP", + "USB CDP", + "USB DCP", + "HVDCP", + "Unknown adaptor", + "Non-standard adapter", + "OTG", + "Not qualified adaptor", +}; + +struct cps5601x_init_data { + int ichg; /* charge current */ + int ilim; /* input current */ + int vreg; /* regulation voltage */ + int iterm; /* termination current */ + int iprechg; /* precharge current */ + int vlim; /* minimum system voltage limit */ + int max_ichg; + int max_ilim; + int max_vreg; +}; + +struct cps5601x_state { + bool vsys_stat; + bool therm_stat; + bool online; + bool term_en; + u8 chrg_stat; + u8 chrg_type; +}; + +struct cps5601x { + struct device *dev; + struct i2c_client *client; + struct mutex lock; + + struct regmap *regmap; + struct cps5601x_state state; + struct cps5601x_init_data init_data; + struct regulator_dev *otg_rdev; + + struct power_supply *charger; + struct power_supply *psy; + + struct workqueue_struct *cps_monitor_wq; + struct delayed_work cps_delay_work; + bool watchdog_enable; + int part_no; + int irq; +}; + +static int cps5601x_read(struct cps5601x *cps, int reg, int *val) +{ + int ret; + + ret = regmap_read(cps->regmap, reg, val); + if (ret) + dev_err(cps->dev, "%s: read 0x%x error!\n", __func__, reg); + + return ret; +} + +static int cps5601x_update_bits(struct cps5601x *cps, int reg, int mask, int val) +{ + int ret; + + ret = regmap_update_bits(cps->regmap, reg, mask, val); + if (ret) + dev_err(cps->dev, "update reg: 0x%x mask:0x%x val: 0x%x error!\n", + reg, mask, val); + + return ret; +} + +static int cps5601x_detect_device(struct cps5601x *cps) +{ + int ret; + int data; + + ret = cps5601x_read(cps, CPS5601X_REG_00, &data); + if (!ret) + cps->part_no = data & CPS5601X_PRODUCT_ID_MASK; + if (cps->part_no != 0xa9) { + dev_err(cps->dev, "cps5601x not detect device %x !\n", cps->part_no); + return -1; + } + + return ret; +} + +static bool cps5601x_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CPS5601X_REG_00 ... CPS5601X_REG_1B: + return true; + default: + return false; + } +} + +static const struct regmap_config cps5601x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CPS5601X_REG_1B, + + .cache_type = REGCACHE_RBTREE, + .volatile_reg = cps5601x_is_volatile_reg, +}; + +static int cps5601x_enable_charger(struct cps5601x *cps) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0B, EN_CHG_MASK, EN_CHG(1)); +} + +static int cps5601x_disable_charger(struct cps5601x *cps) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0B, EN_CHG_MASK, EN_CHG(0)); +} + +static int cps5601x_set_chargecurrent(struct cps5601x *cps, int curr) +{ + int ichg; + + if (curr < (ICHG_BASE + (ICHG_MINVAL * ICHG_LSB))) + curr = ICHG_BASE + (ICHG_MINVAL * ICHG_LSB); + else if (curr > (ICHG_BASE + (ICHG_MAXVAL * ICHG_LSB))) + curr = ICHG_BASE + (ICHG_MAXVAL * ICHG_LSB); + + ichg = (curr - ICHG_BASE) / ICHG_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_04, ICHG_MASK, ichg); +} + +static int cps5601x_get_chargecurrent(struct cps5601x *cps, int *curr) +{ + int ret, val, icurr; + + + ret = cps5601x_read(cps, CPS5601X_REG_04, &val); + if (!ret) { + icurr = val & ICHG_MASK; + *curr = icurr * ICHG_LSB + ICHG_BASE; + } + + return ret; +} + +static int cps5601x_set_chargevolt(struct cps5601x *cps, int volt) +{ + int val; + + if (volt < VREG_BASE) + volt = VREG_BASE; + else if (volt > (VREG_BASE + (VREG_MAXVAL * VREG_LSB))) + volt = VREG_BASE + (VREG_MAXVAL * VREG_LSB); + + val = (volt - VREG_BASE) / VREG_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_03, VREG_MASK, val); +} + +static int cps5601x_get_chargevol(struct cps5601x *cps, int *volt) +{ + int reg_val; + int vchg; + int ret; + + ret = cps5601x_read(cps, CPS5601X_REG_03, ®_val); + if (!ret) { + vchg = reg_val & VREG_MASK; + *volt = vchg * VREG_LSB + VREG_BASE; + } + + return ret; +} + +static int cps5601x_set_input_volt_limit(struct cps5601x *cps, int volt) +{ + int val; + + if (volt < (VINDPM_BASE + (VINDPM_MINVAL * VINDPM_LSB))) + volt = VINDPM_BASE + (VINDPM_MINVAL * VINDPM_LSB); + else if (volt > (VINDPM_BASE + (VINDPM_MAXVAL * VINDPM_LSB))) + volt = VINDPM_BASE + (VINDPM_MAXVAL * VINDPM_LSB); + + val = (volt - VINDPM_BASE) / VINDPM_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_07, VINDPM_MASK, val); +} + +static int cps5601x_set_input_current_limit(struct cps5601x *cps, int curr) +{ + int val; + + if (curr < IINDPM_BASE + (IINDPM_MINVAL * IINDPM_LSB)) + curr = IINDPM_BASE + (IINDPM_MINVAL * IINDPM_LSB); + + val = (curr - IINDPM_BASE) / IINDPM_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_08, IINDPM_MASK, val); +} + +static int cps5601x_get_input_volt_limit(struct cps5601x *cps, u32 *volt) +{ + int reg_val; + int vchg; + int ret; + + ret = cps5601x_read(cps, CPS5601X_REG_07, ®_val); + if (!ret) { + vchg = reg_val & VINDPM_MASK; + *volt = vchg * VINDPM_LSB + VINDPM_BASE; + } + + return ret; +} + +static int cps5601x_get_input_current_limit(struct cps5601x *cps) +{ + int reg_val; + int icl, curr = 0; + int ret; + + ret = cps5601x_read(cps, CPS5601X_REG_08, ®_val); + if (!ret) { + icl = reg_val & IINDPM_MASK; + curr = icl * IINDPM_LSB + IINDPM_BASE; + } + + return curr; +} + +static int cps5601x_set_iprechg(struct cps5601x *cps, int curr) +{ + int iprechg; + + if (curr < (IPRECHG_BASE + (IPRECHG_MINVAL * IPRECHG_LSB))) + curr = IPRECHG_BASE + (IPRECHG_MINVAL * IPRECHG_LSB); + else if (curr > (IPRECHG_BASE + (IPRECHG_MAXVAL * IPRECHG_LSB))) + curr = IPRECHG_BASE + (IPRECHG_MAXVAL * IPRECHG_LSB); + + iprechg = (curr - IPRECHG_BASE) / IPRECHG_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_05, IPRECHG_MASK, iprechg); +} + +static int cps5601x_enable_term(struct cps5601x *cps, bool enable) +{ + int val; + int ret; + + val = EN_TERM_ENABLE(enable); + ret = cps5601x_update_bits(cps, CPS5601X_REG_05, EN_TERM_MASK, val); + + return ret; +} + +static int cps5601x_set_term_current(struct cps5601x *cps, int curr) +{ + int iterm; + + if (curr < (ITERM_BASE + (ITERM_MINVAL * ITERM_LSB))) + curr = ITERM_BASE + (ITERM_MINVAL * ITERM_LSB); + else if (curr > (ITERM_BASE + (ITERM_MAXVAL * ITERM_LSB))) + curr = ITERM_BASE + (ITERM_MAXVAL * ITERM_LSB); + + iterm = (curr - ITERM_BASE) / ITERM_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_06, ITERM_MASK, iterm); + +} + +static int cps5601x_get_term_current(struct cps5601x *cps, int *curr) +{ + int reg_val; + int iterm; + int ret; + + ret = cps5601x_read(cps, CPS5601X_REG_06, ®_val); + if (!ret) { + iterm = reg_val & ITERM_MASK; + *curr = iterm * ITERM_LSB + ITERM_BASE; + } + + return ret; +} + +static int cps5601x_reset_watchdog_timer(struct cps5601x *cps) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0B, WD_RST_MASK, WD_RST(1)); +} + +static int cps5601x_set_watchdog_timer(struct cps5601x *cps, int timeout) +{ + int val, ret; + + val = (timeout - WATCHDOG_BASE) / WATCHDOG_LSB; + ret = cps5601x_update_bits(cps, CPS5601X_REG_0B, WATCHDOG_MASK, WATCHDOG_TIME(val)); + if (ret) { + dev_err(cps->dev, "cps5601x set watchdog fail\n"); + return ret; + } + + if (timeout) { + DBG("cps5601x: enable watchdog\n"); + if (!cps->watchdog_enable) + queue_delayed_work(cps->cps_monitor_wq, + &cps->cps_delay_work, + msecs_to_jiffies(1000 * 5)); + cps->watchdog_enable = true; + } else { + DBG("cps5601x: disable watchdog\n"); + cps->watchdog_enable = false; + cps5601x_reset_watchdog_timer(cps); + } + + return ret; +} + +static int cps5601x_ts_ignore(struct cps5601x *cps, bool en) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0E, TS_IGNORE_MASK, EN_TS_IGNORE(en)); +} + +static int cps5601x_enable_otg(struct cps5601x *cps) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0C, EN_OTG_MASK, EN_OTG(1)); +} + +static int cps5601x_disable_otg(struct cps5601x *cps) +{ + return cps5601x_update_bits(cps, CPS5601X_REG_0C, EN_OTG_MASK, EN_OTG(0)); +} + +static int cps5601x_set_boost_current(struct cps5601x *cps, int curr) +{ + int val; + + if (curr < IOTG_BASE + (IOTG_MINVAL * IOTG_LSB)) + val = IOTG_BASE + (IOTG_MINVAL * IOTG_LSB); + else + val = ((curr - IOTG_BASE) / IOTG_LSB); + + return cps5601x_update_bits(cps, CPS5601X_REG_0A, IOTG_MASK, val); +} + +static int cps5601x_set_boost_voltage(struct cps5601x *cps, int volt) +{ + int val = 0; + + if (volt < VOTG_BASE) + volt = VOTG_BASE; + + if (volt > VOTG_BASE + (VOTG_MAXVAL * VOTG_LSB)) + volt = VOTG_BASE + (VOTG_MAXVAL * VOTG_LSB); + + val = (volt - VOTG_BASE) / VOTG_LSB; + + return cps5601x_update_bits(cps, CPS5601X_REG_09, VOTG_MASK, val); +} + +static int cps5601x_get_state(struct cps5601x *cps, + struct cps5601x_state *state) +{ + int val, ret; + + ret = cps5601x_read(cps, CPS5601X_REG_10, &val); + if (ret) { + dev_err(cps->dev, "read CPS5601X_CHRG_STAT fail\n"); + return ret; + } + + DBG("CPS5601X_CHRG_STAT[0x%x]: 0x%x\n", CPS5601X_REG_10, val); + state->chrg_type = (val & VBUS_STAT_MASK) >> VBUS_STAT_SHIFT; + state->chrg_stat = (val & CHG_STAT_MASK) >> CHG_STAT_SHIFT; + + ret = cps5601x_read(cps, CPS5601X_REG_0F, &val); + if (ret) { + dev_err(cps->dev, "read CPS5601X_PG fail\n"); + return ret; + } + state->online = !!(val & PG_STAT_MASK); + + ret = cps5601x_read(cps, CPS5601X_REG_19, &val); + if (ret) { + dev_err(cps->dev, "read CPS5601X_THERMAL fail\n"); + return ret; + } + state->therm_stat = !!(val & TREG_MK_MASK); + + ret = cps5601x_read(cps, CPS5601X_REG_05, &val); + if (ret) { + dev_err(cps->dev, "read CPS5601X_EN_TERM fail\n"); + return ret; + } + state->term_en = !!(val & EN_TERM_MASK); + + DBG("chrg_type: 0x%x\n", state->chrg_type); + DBG("chrg_stat: 0x%x\n", state->chrg_stat); + DBG("online: 0x%x\n", state->online); + DBG("term_en: 0x%x\n", state->term_en); + + return ret; +} + +static int cps5601x_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_ONLINE: + return true; + default: + return false; + } +} + +static int cps5601x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct cps5601x *cps = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: + if (val->intval) { + ret = cps5601x_enable_charger(cps); + cps5601x_set_watchdog_timer(cps, 40); + } else { + cps5601x_set_watchdog_timer(cps, 0); + ret = cps5601x_disable_charger(cps); + } + + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = cps5601x_set_input_current_limit(cps, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = cps5601x_set_chargecurrent(cps, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = cps5601x_set_chargevolt(cps, val->intval); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int cps5601x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct cps5601x *cps = power_supply_get_drvdata(psy); + struct cps5601x_state state; + int ret = 0; + + mutex_lock(&cps->lock); + ret = cps5601x_get_state(cps, &state); + if (ret) { + dev_err(cps->dev, "get state error!\n"); + mutex_unlock(&cps->lock); + return ret; + } + cps->state = state; + mutex_unlock(&cps->lock); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (!state.chrg_type || (state.chrg_type == VBUS_STAT_OTGMODE)) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (!state.chrg_stat) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (state.chrg_stat == CHG_STAT_CHGTERM) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + switch (state.chrg_stat) { + case CHG_STAT_TRICKLECHG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case CHG_STAT_FASTCHG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case CHG_STAT_CHGTERM: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case CHG_STAT_NOTCHG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = CPS5601X_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = CPS5601X_MODEL_NAME; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = true; + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = cps->init_data.max_vreg; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = CPS5601X_ICHRG_I_MAX_uA; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + val->intval = CPS5601X_VINDPM_V_MAX_uV; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + val->intval = cps5601x_get_input_current_limit(cps); + if (val->intval < 0) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return ret; +} + +static enum power_supply_property cps5601x_power_supply_props[] = { + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_PRESENT +}; + +static char *cps5601x_charger_supplied_to[] = { + "usb", +}; + +static struct power_supply_desc cps5601x_power_supply_desc = { + .name = "cps5601x-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = cps5601x_power_supply_props, + .num_properties = ARRAY_SIZE(cps5601x_power_supply_props), + .get_property = cps5601x_charger_get_property, + .set_property = cps5601x_charger_set_property, + .property_is_writeable = cps5601x_property_is_writeable, +}; + +static irqreturn_t cps5601x_irq_handler_thread(int irq, void *private) +{ + struct cps5601x *cps = private; + struct cps5601x_state state; + int ret; + u8 addr; + int val; + + for (addr = 0x0; addr <= CPS5601X_REG_1B; addr++) { + ret = cps5601x_read(cps, addr, &val); + if (ret) + dev_err(cps->dev, "read addr[0x%x] error!\n", addr); + DBG("[0x%x]: 0x%x\n", addr, val); + } + ret = cps5601x_get_state(cps, &state); + if (ret) { + dev_err(cps->dev, "get state error!\n"); + return IRQ_NONE; + } + cps->state = state; + power_supply_changed(cps->charger); + + return IRQ_HANDLED; +} + +static int cps5601x_power_supply_init(struct cps5601x *cps, struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = cps, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = cps5601x_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(cps5601x_charger_supplied_to); + psy_cfg.of_node = dev->of_node; + cps->charger = devm_power_supply_register(cps->dev, + &cps5601x_power_supply_desc, + &psy_cfg); + if (IS_ERR(cps->charger)) + return -EINVAL; + + return 0; +} + +static int cps5601x_hw_init(struct cps5601x *cps) +{ + struct power_supply_battery_info *bat_info; + struct cps5601x_state state; + int ret = 0; + + ret = power_supply_get_battery_info(cps->charger, &bat_info); + if (ret) { + /* Allocate an empty battery */ + bat_info = devm_kzalloc(cps->dev, sizeof(*bat_info), GFP_KERNEL); + if (!bat_info) + return -ENOMEM; + dev_info(cps->dev, "cps5601x: no battery information is supplied\n"); + /* + * If no battery information is supplied, we should set + * default charge termination current to 120 mA, and default + * charge termination voltage to 4.35V. + */ + bat_info->constant_charge_current_max_ua = + CPS5601X_ICHRG_I_DEF_uA; + bat_info->constant_charge_voltage_max_uv = + CPS5601X_VREG_V_DEF_uV; + bat_info->precharge_current_ua = + CPS5601X_PRECHRG_I_DEF_uA; + bat_info->charge_term_current_ua = + CPS5601X_TERMCHRG_I_DEF_uA; + cps->init_data.max_vreg = CPS5601X_VREG_V_DEF_uV; + } + if (!bat_info->constant_charge_current_max_ua) + bat_info->constant_charge_current_max_ua = CPS5601X_ICHRG_I_MAX_uA; + if (!bat_info->constant_charge_voltage_max_uv) + bat_info->constant_charge_voltage_max_uv = CPS5601X_VREG_V_DEF_uV; + if (!bat_info->precharge_current_ua) + bat_info->precharge_current_ua = CPS5601X_PRECHRG_I_DEF_uA; + if (!bat_info->charge_term_current_ua) + bat_info->charge_term_current_ua = CPS5601X_TERMCHRG_I_DEF_uA; + if (!cps->init_data.max_ichg) + cps->init_data.max_ichg = CPS5601X_ICHRG_I_MAX_uA; + + if (bat_info->constant_charge_voltage_max_uv) + cps->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv; + + ret = cps5601x_set_watchdog_timer(cps, 0); + if (ret) + return ret; + + ret = cps5601x_set_iprechg(cps, bat_info->precharge_current_ua); + if (ret) + return ret; + + ret = cps5601x_set_chargevolt(cps, cps->init_data.max_vreg); + if (ret) + return ret; + + cps5601x_set_term_current(cps, bat_info->charge_term_current_ua); + cps5601x_enable_term(cps, true); + + ret = cps5601x_set_input_volt_limit(cps, cps->init_data.vlim); + if (ret) + return ret; + + ret = cps5601x_get_state(cps, &state); + if (ret || !state.online) { + ret = cps5601x_set_input_current_limit(cps, DEFAULT_INPUT_CURRENT); + if (ret) + return ret; + ret = cps5601x_set_chargecurrent(cps, + bat_info->constant_charge_current_max_ua); + if (ret) + return ret; + + ret = cps5601x_disable_charger(cps); + if (ret) + return ret; + } + cps5601x_ts_ignore(cps, true); + + DBG("ichrg_curr:%d\n" + "prechrg_curr:%d\n" + "chrg_vol:%d\n" + "term_curr:%d\n" + "input_curr_lim:%d\n", + bat_info->constant_charge_current_max_ua, + bat_info->precharge_current_ua, + bat_info->constant_charge_voltage_max_uv, + bat_info->charge_term_current_ua, + cps->init_data.ilim); + + return ret; +} + +static int cps5601x_parse_dt(struct cps5601x *cps) +{ + int ret; + + ret = device_property_read_u32(cps->dev, + "input-voltage-limit-microvolt", + &cps->init_data.vlim); + if (ret) + cps->init_data.vlim = CPS5601X_VINDPM_DEF_uV; + + if (cps->init_data.vlim > CPS5601X_VINDPM_V_MIN_uV || + cps->init_data.vlim < CPS5601X_VINDPM_V_MAX_uV) + return -EINVAL; + + ret = device_property_read_u32(cps->dev, + "input-current-limit-microamp", + &cps->init_data.ilim); + if (ret) + cps->init_data.ilim = CPS5601X_IINDPM_DEF_uA; + + if (cps->init_data.ilim > CPS5601X_IINDPM_I_MIN_uA || + cps->init_data.ilim < CPS5601X_IINDPM_I_MAX_uA) + return -EINVAL; + + return 0; +} + +static void cps_charger_work(struct work_struct *work) +{ + struct cps5601x *cps = container_of(work, struct cps5601x, + cps_delay_work.work); + + cps5601x_reset_watchdog_timer(cps); + if (cps->watchdog_enable) + queue_delayed_work(cps->cps_monitor_wq, + &cps->cps_delay_work, + msecs_to_jiffies(1000 * 5)); +} + +static int cps5601x_enable_vbus(struct regulator_dev *rdev) +{ + struct cps5601x *cps = rdev_get_drvdata(rdev); + int ret = 0; + + ret = cps5601x_enable_otg(cps); + if (ret) { + dev_err(cps->dev, "set OTG enable error!\n"); + return ret; + } + + return ret; +} + +static int cps5601x_disable_vbus(struct regulator_dev *rdev) +{ + struct cps5601x *cps = rdev_get_drvdata(rdev); + int ret = 0; + + ret = cps5601x_disable_otg(cps); + if (ret) { + dev_err(cps->dev, "set OTG disable error!\n"); + return ret; + } + + return ret; +} + +static int cps5601x_is_enabled_vbus(struct regulator_dev *rdev) +{ + struct cps5601x *cps = rdev_get_drvdata(rdev); + int val, ret = 0; + + ret = cps5601x_read(cps, CPS5601X_REG_0C, &val); + if (ret) { + dev_err(cps->dev, "get vbus status error!\n"); + return ret; + } + + return (val & EN_OTG_MASK) ? 1 : 0; +} + +static const struct regulator_ops cps5601x_vbus_ops = { + .enable = cps5601x_enable_vbus, + .disable = cps5601x_disable_vbus, + .is_enabled = cps5601x_is_enabled_vbus, +}; + +static struct regulator_desc cps5601x_otg_rdesc = { + .of_match = "otg-vbus", + .name = "otg-vbus", + .regulators_node = of_match_ptr("regulators"), + .ops = &cps5601x_vbus_ops, + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static int cps5601x_vbus_regulator_register(struct cps5601x *cps) +{ + struct device_node *np; + struct regulator_config config = {}; + int ret = 0; + + np = of_get_child_by_name(cps->dev->of_node, "regulators"); + if (!np) { + dev_warn(cps->dev, "cannot find regulators node\n"); + return -ENXIO; + } + + /* otg regulator */ + config.dev = cps->dev; + config.driver_data = cps; + cps->otg_rdev = devm_regulator_register(cps->dev, + &cps5601x_otg_rdesc, + &config); + if (IS_ERR(cps->otg_rdev)) + ret = PTR_ERR(cps->otg_rdev); + + return ret; +} + +static ssize_t registers_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cps5601x *cps = dev_get_drvdata(dev); + u8 tmpbuf[30]; + int idx = 0; + u8 addr; + int val; + int len; + int ret; + + for (addr = 0x0; addr <= CPS5601X_REG_1B; addr++) { + ret = regmap_read(cps->regmap, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, 30, + "Reg[%.2X] = 0x%.2x\n", + addr, + val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t registers_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cps5601x *cps = dev_get_drvdata(dev); + unsigned int reg; + int ret; + int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg <= CPS5601X_REG_1B) + regmap_write(cps->regmap, (unsigned char)reg, val); + + return count; +} +static DEVICE_ATTR_RW(registers); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct cps5601x *cps = dev_get_drvdata(dev); + int chrg_curr = 0, chrg_volt = 0, icurr = 0, ivolt = 0, term_curr = 0; + int chrg_state, chrg_type; + struct cps5601x_state state; + + cps5601x_get_state(cps, &state); + chrg_state = state.chrg_stat <= 7 ? state.chrg_stat : 0; + chrg_type = state.chrg_type <= 8 ? state.chrg_type : 0; + cps5601x_get_chargecurrent(cps, &chrg_curr); + cps5601x_get_chargevol(cps, &chrg_volt); + icurr = cps5601x_get_input_current_limit(cps); + cps5601x_get_input_volt_limit(cps, &ivolt); + cps5601x_get_term_current(cps, &term_curr); + + return snprintf(buf, PAGE_SIZE, + "online: %d\n" + "charge state: %s\n" + "charge type: %s\n" + "charge current: %d uA\n" + "charge voltage: %d uV\n" + "input current: %d uA\n" + "input voltage: %d uV\n" + "term current: %d uA\n", + state.online, + charge_state_str[chrg_state], + charge_type_str[chrg_type], + chrg_curr, chrg_volt, + icurr, + ivolt, + term_curr); +} +static DEVICE_ATTR_RO(status); + +static void cps5601x_create_device_node(struct device *dev) +{ + device_create_file(dev, &dev_attr_registers); + device_create_file(dev, &dev_attr_status); +} + +static int cps5601x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct cps5601x *cps; + int ret; + + cps = devm_kzalloc(dev, sizeof(*cps), GFP_KERNEL); + if (!cps) + return -ENOMEM; + + cps->client = client; + cps->dev = dev; + + mutex_init(&cps->lock); + + cps->regmap = devm_regmap_init_i2c(client, &cps5601x_regmap_config); + if (IS_ERR(cps->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + return PTR_ERR(cps->regmap); + } + + i2c_set_clientdata(client, cps); + + ret = cps5601x_detect_device(cps); + if (ret) { + dev_err(cps->dev, "No cps5601x device found!\n"); + return -ENODEV; + } + + cps5601x_parse_dt(cps); + + device_init_wakeup(dev, 1); + + ret = cps5601x_power_supply_init(cps, dev); + if (ret) { + dev_err(dev, "Failed to register power supply\n"); + return ret; + } + + cps5601x_hw_init(cps); + + /* OTG setting 5V/1.2A */ + ret = cps5601x_set_boost_voltage(cps, 5000000); + if (ret) { + dev_err(cps->dev, "set OTG voltage error!\n"); + return ret; + } + ret = cps5601x_set_boost_current(cps, 1200000); + if (ret) { + dev_err(cps->dev, "set OTG current error!\n"); + return ret; + } + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + cps5601x_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "cps5601x-irq", cps); + if (ret) + return ret; + enable_irq_wake(client->irq); + } + + cps->cps_monitor_wq = alloc_ordered_workqueue("%s", + WQ_MEM_RECLAIM | WQ_FREEZABLE, "cps-monitor-wq"); + if (!cps->cps_monitor_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&cps->cps_delay_work, cps_charger_work); + + cps5601x_vbus_regulator_register(cps); + cps5601x_create_device_node(cps->dev); + + return 0; +} + +static void cps5601x_charger_remove(struct i2c_client *client) +{ + struct cps5601x *cps = i2c_get_clientdata(client); + + device_remove_file(cps->dev, &dev_attr_registers); + device_remove_file(cps->dev, &dev_attr_status); + destroy_workqueue(cps->cps_monitor_wq); + mutex_destroy(&cps->lock); +} + +static void cps5601x_charger_shutdown(struct i2c_client *client) +{ + struct cps5601x *cps = i2c_get_clientdata(client); + int ret = 0; + + cps5601x_set_iprechg(cps, CPS5601X_PRECHRG_I_DEF_uA); + ret = cps5601x_disable_charger(cps); + if (ret) + dev_err(cps->dev, "Failed to disable charger, ret = %d\n", ret); +} + +static const struct i2c_device_id cps5601x_i2c_ids[] = { + { "cps5601x", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, cps5601x_i2c_ids); + +static const struct of_device_id cps5601x_of_match[] = { + { .compatible = "cps,cps5601x", }, + { } +}; +MODULE_DEVICE_TABLE(of, cps5601x_of_match); + +static struct i2c_driver cps5601x_driver = { + .driver = { + .name = "cps5601x-charger", + .of_match_table = cps5601x_of_match, + }, + .probe = cps5601x_probe, + .remove = cps5601x_charger_remove, + .shutdown = cps5601x_charger_shutdown, + .id_table = cps5601x_i2c_ids, +}; +module_i2c_driver(cps5601x_driver); + +MODULE_AUTHOR("Chen Shunqing "); +MODULE_DESCRIPTION("cps5601x charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/rockchip/rockchip_decompress.c b/drivers/soc/rockchip/rockchip_decompress.c index 7494ea2ceef7..eca36c64cd69 100644 --- a/drivers/soc/rockchip/rockchip_decompress.c +++ b/drivers/soc/rockchip/rockchip_decompress.c @@ -15,6 +15,7 @@ #include #include #include +#include #define DECOM_CTRL 0x0 #define DECOM_ENR 0x4 @@ -314,8 +315,138 @@ static ssize_t start_decom_store(struct device *dev, } static DEVICE_ATTR_WO(start_decom); + +static void *uncomp_virt; +static dma_addr_t uncomp_phys; + +static void *comp_virt; +static dma_addr_t comp_phys; + +static void *decomp_virt; +static dma_addr_t decomp_phys; + +static size_t uncomp_size; +static size_t comp_size; + +#define RK_DECOME_TIMEOUT 3 /* 3 seconds */ + +#define FILE_UNCOMPRESSED "/data/data/asyoulik.txt" +#define FILE_COMPRESSED "/data/data/asyoulik.tar.gz" + +static int decompress_and_compare(struct rk_decom *rk_decom, int mode) +{ + struct file *file1 = NULL, *file2 = NULL; + int ret = -EINVAL; + u64 decom_len; + loff_t pos; + + pr_info("Starting decompress and compare operation\n"); + + file1 = filp_open(FILE_UNCOMPRESSED, O_RDONLY, 0); + if (IS_ERR(file1)) + goto err_file1; + + file2 = filp_open(FILE_COMPRESSED, O_RDONLY, 0); + if (IS_ERR(file2)) + goto err_file2; + + uncomp_size = file1->f_inode->i_size; + comp_size = file2->f_inode->i_size; + + pr_info("Uncompressed file size: %zu bytes\n", uncomp_size); + + uncomp_virt = dma_alloc_coherent(rk_decom->dev, uncomp_size, + &uncomp_phys, GFP_KERNEL); + if (!uncomp_virt) + goto out_free; + + comp_virt = dma_alloc_coherent(rk_decom->dev, comp_size, &comp_phys, + GFP_KERNEL); + if (!comp_virt) + goto out_free1; + + decomp_virt = dma_alloc_coherent(rk_decom->dev, uncomp_size * 2, + &decomp_phys, GFP_KERNEL); + if (!decomp_virt) + goto out_free2; + + pos = 0; + if (kernel_read(file1, uncomp_virt, uncomp_size, &pos) < 0) { + pr_err("Failed to read uncompressed file\n"); + goto out_free3; + } + filp_close(file1, NULL); + file1 = NULL; + + pos = 0; + if (kernel_read(file2, comp_virt, comp_size, &pos) < 0) { + pr_err("Failed to read compressed file\n"); + goto out_free3; + } + filp_close(file2, NULL); + file2 = NULL; + + ret = rk_decom_start(mode, comp_phys, decomp_phys, 0x80000000); + if (ret) { + pr_err("rk_decom_start failed[%d].", ret); + goto out_free3; + } + + ret = rk_decom_wait_done(RK_DECOME_TIMEOUT, &decom_len); + pr_info("Decompression %lld completed\n", decom_len); + + pr_info("Comparing files...\n"); + if (memcmp(uncomp_virt, decomp_virt, uncomp_size) == 0) { + pr_info("Files match exactly!\n"); + ret = 0; + } else { + pr_info("Files differ\n"); + ret = -EINVAL; + } + +out_free3: + dma_free_coherent(rk_decom->dev, uncomp_size * 2, decomp_virt, + decomp_phys); +out_free2: + dma_free_coherent(rk_decom->dev, comp_size, comp_virt, comp_phys); +out_free1: + dma_free_coherent(rk_decom->dev, uncomp_size, uncomp_virt, uncomp_phys); +out_free: + if (file2) + filp_close(file2, NULL); +err_file2: + if (file1) + filp_close(file1, NULL); +err_file1: + return ret; +} + +static ssize_t dynamic_buf_decom_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u32 mode; + int ret; + struct rk_decom *rk_dec = dev_get_drvdata(dev); + + ret = kstrtou32(buf, 10, &mode); + if (ret) + return ret; + + if (mode != LZ4_MOD && mode != GZIP_MOD && mode != ZLIB_MOD) + return -EINVAL; + + ret = decompress_and_compare(rk_dec, mode); + if (ret) + pr_info("%s, user decompress error\n", __func__); + + return size; +} +static DEVICE_ATTR_WO(dynamic_buf_decom); + static struct attribute *decom_attrs[] = { &dev_attr_start_decom.attr, + &dev_attr_dynamic_buf_decom.attr, NULL }; @@ -348,14 +479,18 @@ static int __init rockchip_decom_probe(struct platform_device *pdev) mem = of_parse_phandle(np, "memory-region", 0); if (!mem) { dev_err(dev, "missing \"memory-region\" property\n"); +#ifndef CONFIG_ROCKCHIP_HW_DECOMPRESS_TEST return -ENODEV; +#endif } ret = of_address_to_resource(mem, 0, ®); of_node_put(mem); if (ret) { dev_err(dev, "missing \"reg\" property\n"); +#ifndef CONFIG_ROCKCHIP_HW_DECOMPRESS_TEST return -ENODEV; +#endif } rk_dec->mem_start = reg.start; diff --git a/drivers/soc/rockchip/rockchip_pvtm.c b/drivers/soc/rockchip/rockchip_pvtm.c index e8d5ddcf4192..846cc69c8ed5 100644 --- a/drivers/soc/rockchip/rockchip_pvtm.c +++ b/drivers/soc/rockchip/rockchip_pvtm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -983,13 +984,13 @@ static int rockchip_pvtm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *node; - const struct of_device_id *match; + const struct rockchip_pvtm_data *data; struct rockchip_pvtm *pvtm; struct regmap *grf = NULL; void __iomem *base = NULL; - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { + data = device_get_match_data(dev); + if (!data) { dev_err(dev, "missing pvtm data\n"); return -EINVAL; } @@ -1005,7 +1006,7 @@ static int rockchip_pvtm_probe(struct platform_device *pdev) } for_each_available_child_of_node(np, node) { - pvtm = rockchip_pvtm_init(dev, node, match->data, grf, base); + pvtm = rockchip_pvtm_init(dev, node, data, grf, base); if (!pvtm) { dev_err(dev, "failed to handle node %s\n", node->full_name); diff --git a/drivers/soc/rockchip/rockchip_system_monitor.c b/drivers/soc/rockchip/rockchip_system_monitor.c index dd20883979b5..a4d8266178f3 100644 --- a/drivers/soc/rockchip/rockchip_system_monitor.c +++ b/drivers/soc/rockchip/rockchip_system_monitor.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1909,7 +1910,6 @@ MODULE_DEVICE_TABLE(of, rockchip_system_monitor_of_match); static int rockchip_system_monitor_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *match; int (*init)(struct platform_device *pdev); system_monitor = devm_kzalloc(dev, sizeof(struct system_monitor), @@ -1933,11 +1933,9 @@ static int rockchip_system_monitor_probe(struct platform_device *pdev) rockchip_system_monitor_parse_dt(system_monitor); - match = of_match_device(rockchip_system_monitor_of_match, &pdev->dev); - if (match && match->data) { - init = match->data; + init = device_get_match_data(dev); + if (init) init(pdev); - } if (system_monitor->tz) { system_monitor->last_temp = INT_MAX; diff --git a/sound/soc/codecs/rv1106_codec.c b/sound/soc/codecs/rv1106_codec.c index a835d8c1592a..c05f4bc4a227 100644 --- a/sound/soc/codecs/rv1106_codec.c +++ b/sound/soc/codecs/rv1106_codec.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -2131,7 +2132,6 @@ MODULE_DEVICE_TABLE(of, rv1106_codec_of_match); static int rv1106_platform_probe(struct platform_device *pdev) { - const struct of_device_id *of_id; struct device_node *np = pdev->dev.of_node; struct rv1106_codec_priv *rv1106; struct resource *res; @@ -2142,9 +2142,7 @@ static int rv1106_platform_probe(struct platform_device *pdev) if (!rv1106) return -ENOMEM; - of_id = of_match_device(rv1106_codec_of_match, &pdev->dev); - if (of_id) - rv1106->soc_id = (enum soc_id_e)of_id->data; + rv1106->soc_id = (enum soc_id_e)device_get_match_data(&pdev->dev); dev_info(&pdev->dev, "current soc_id: rv%x\n", rv1106->soc_id); rv1106->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");