Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] dt-bindings: sound: tlv320adcx140: Add GPI config property
From: Rob Herring @ 2020-05-28 14:05 UTC (permalink / raw)
  To: Dan Murphy
  Cc: lgirdwood, broonie, perex, tiwai, devicetree, alsa-devel,
	linux-kernel
In-Reply-To: <20200526200917.10385-1-dmurphy@ti.com>

On Tue, May 26, 2020 at 03:09:16PM -0500, Dan Murphy wrote:
> Add an array property that configures the General Purpose Input (GPI)
> register.  The device has 4 GPI pins and each pin can be configured in 1
> of 7 different ways.

Dan seems to have trouble running get_maintainers.pl and Cc'ing the DT 
list. Running 'make dt_binding_check' also seems to be a problem. Now 
linux-next has these warnings:

/builds/robherring/linux-dt-bindings/Documentation/devicetree/bindings/sound/tlv320adcx140.example.dt.yaml: codec@4c: ti,gpi-config:0:0: 4 is greater than the maximum of 1
/builds/robherring/linux-dt-bindings/Documentation/devicetree/bindings/sound/tlv320adcx140.example.dt.yaml: codec@4c: ti,gpi-config:0:1: 5 is greater than the maximum of 1
/builds/robherring/linux-dt-bindings/Documentation/devicetree/bindings/sound/tlv320adcx140.example.dt.yaml: codec@4c: ti,gpi-config:0:2: 6 is greater than the maximum of 1
/builds/robherring/linux-dt-bindings/Documentation/devicetree/bindings/sound/tlv320adcx140.example.dt.yaml: codec@4c: ti,gpi-config:0:3: 7 is greater than the maximum of 1

Please send a fix.

> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
>  .../bindings/sound/tlv320adcx140.yaml         | 27 +++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
> index daa6cc0e031b..e8a69b1c7ca9 100644
> --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
> +++ b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
> @@ -86,6 +86,32 @@ properties:
>            maximum: 1
>          default: [0, 0, 0, 0]
>  
> +  ti,gpi-config:
> +    description: |
> +       Defines the configuration for the general purpose input pins (GPI).
> +       The array is defined as <GPI1 GPI2 GPI3 GPI4>.
> +
> +       0 - (default) disabled
> +       1 - GPIX is configured as a general-purpose input (GPI)
> +       2 - GPIX is configured as a master clock input (MCLK)
> +       3 - GPIX is configured as an ASI input for daisy-chain (SDIN)
> +       4 - GPIX is configured as a PDM data input for channel 1 and channel
> +            (PDMDIN1)
> +       5 - GPIX is configured as a PDM data input for channel 3 and channel
> +            (PDMDIN2)
> +       6 - GPIX is configured as a PDM data input for channel 5 and channel
> +            (PDMDIN3)
> +       7 - GPIX is configured as a PDM data input for channel 7 and channel
> +            (PDMDIN4)
> +
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32-array
> +      - minItems: 1
> +        maxItems: 4
> +        items:
> +          maximum: 1

I believe you want '7' here.

> +        default: [0, 0, 0, 0]
> +
>  required:
>    - compatible
>    - reg
> @@ -101,6 +127,7 @@ examples:
>          reg = <0x4c>;
>          ti,mic-bias-source = <6>;
>          ti,pdm-edge-select = <0 1 0 1>;
> +        ti,gpi-config = <4 5 6 7>;
>          reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
>        };
>      };
> -- 
> 2.26.2
> 

^ permalink raw reply

* Re: [PATCH v9 1/2] dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
From: Rob Herring @ 2020-05-28 14:06 UTC (permalink / raw)
  To: Ramuthevar,Vadivel MuruganX
  Cc: vigneshr, linux-kernel, arnd, hauke.mehrtens, linux-mips, richard,
	qi-ming.wu, tglx, brendanhiggins, linux-mtd, boris.brezillon,
	anders.roxell, cheol.yong.kim, devicetree, miquel.raynal,
	andriy.shevchenko, robh+dt, masonccyang
In-Reply-To: <20200528051211.3063-2-vadivel.muruganx.ramuthevar@linux.intel.com>

On Thu, 28 May 2020 13:12:10 +0800, Ramuthevar,Vadivel MuruganX wrote:
> From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
> 
> Add YAML file for dt-bindings to support NAND Flash Controller
> on Intel's Lightning Mountain SoC.
> 
> Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
> ---
>  .../devicetree/bindings/mtd/intel,lgm-nand.yaml    | 93 ++++++++++++++++++++++
>  1 file changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
> 


My bot found errors running 'make dt_binding_check' on your patch:

/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mtd/intel,lgm-nand.example.dt.yaml: nand-controller@e0f00000: '#address-cells', '#size-cells' do not match any of the regexes: '^nand@[a-f0-9]+$', 'pinctrl-[0-9]+'
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mtd/intel,lgm-nand.example.dt.yaml: nand-controller@e0f00000: nand@0: '#address-cells', '#size-cells', 'nand-on-flash-bbt' do not match any of the regexes: 'pinctrl-[0-9]+'

See https://patchwork.ozlabs.org/patch/1299399

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.


^ permalink raw reply

* [PATCH v11 3/4] dt-bindings: power: Add the bindings for the bq2515x family of chargers.
From: Ricardo Rivera-Matos @ 2020-05-28 14:05 UTC (permalink / raw)
  To: sre, pali, robh
  Cc: afd, r-rivera-matos, dmurphy, linux-pm, linux-kernel, devicetree,
	sspatil
In-Reply-To: <20200528140546.25260-1-r-rivera-matos@ti.com>

The BQ2515X family of devices are highly integrated battery management
ICs that integrate the most common functions for wearable devices
namely a charger, an output voltage rail, ADC for battery and system
monitoring, and a push-button controller.

Datasheets:
http://www.ti.com/lit/ds/symlink/bq25150.pdf
http://www.ti.com/lit/ds/symlink/bq25155.pdf

Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
---
 .../bindings/power/supply/bq2515x.yaml        | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml

diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml
new file mode 100644
index 000000000000..19cb336d581e
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: TI bq2515x 500-mA Linear charger family
+
+maintainers:
+  - Dan Murphy <dmurphy@ti.com>
+  - Ricardo Rivera-Matos <r-rivera-matos@ti.com>
+
+description: |
+  The BQ2515x family is a highly integrated battery charge management IC that
+  integrates the most common functions for wearable devices, namely a charger,
+  an output voltage rail, ADC for battery and system monitoring, and
+  push-button controller.
+
+  Specifications about the charger can be found at:
+    http://www.ti.com/lit/ds/symlink/bq25150.pdf
+    http://www.ti.com/lit/ds/symlink/bq25155.pdf
+
+properties:
+  compatible:
+    enum:
+      - ti,bq25150
+      - ti,bq25155
+
+  reg:
+    maxItems: 1
+    description: I2C address of the charger.
+
+  ac-detect-gpios:
+    description: |
+       GPIO used for connecting the bq2515x device PG (AC Detect)
+       pin.
+    maxItems: 1
+
+  reset-gpios:
+    description: GPIO used for hardware reset.
+    maxItems: 1
+
+  powerdown-gpios:
+    description: GPIO used for low power mode of IC.
+    maxItems: 1
+
+  charge-enable-gpios:
+    description: GPIO used to turn on and off charging.
+    maxItems: 1
+
+  input-current-limit-microamp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Maximum input current in micro Amps.
+    minimum: 50000
+    maximum: 500000
+
+  monitored-battery:
+    $ref: battery.yaml#
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    bat: battery {
+      compatible = "simple-battery";
+      constant-charge-current-max-microamp = <50000>;
+      precharge-current-microamp = <2500>;
+      constant-charge-voltage-max-microvolt = <4000000>;
+    };
+    #include <dt-bindings/gpio/gpio.h>
+    i2c0 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      bq25150: charger@6b {
+        compatible = "ti,bq25150";
+        reg = <0x6b>;
+        monitored-battery = <&bat>;
+        input-current-limit-microamp = <100000>;
+
+        ac-detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+        powerdown-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
+        charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+      };
+    };
-- 
2.26.2


^ permalink raw reply related

* [PATCH v11 4/4] power: supply: bq25150 introduce the bq25150
From: Ricardo Rivera-Matos @ 2020-05-28 14:05 UTC (permalink / raw)
  To: sre, pali, robh
  Cc: afd, r-rivera-matos, dmurphy, linux-pm, linux-kernel, devicetree,
	sspatil
In-Reply-To: <20200528140546.25260-1-r-rivera-matos@ti.com>

Introduce the bq2515x family of chargers.

The BQ2515X family of devices are highly integrated battery management
ICs that integrate the most common functions for wearable devices
namely a charger, an output voltage rail, ADC for battery and system
monitoring, and a push-button controller.

Datasheets:
	bq25150 - http://www.ti.com/lit/ds/symlink/bq25150.pdf
	bq25155 - http://www.ti.com/lit/ds/symlink/bq25155.pdf

Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
---
 drivers/power/supply/Kconfig           |   13 +
 drivers/power/supply/Makefile          |    1 +
 drivers/power/supply/bq2515x_charger.c | 1159 ++++++++++++++++++++++++
 3 files changed, 1173 insertions(+)
 create mode 100644 drivers/power/supply/bq2515x_charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index f3424fdce341..266193301e2d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -589,6 +589,19 @@ config CHARGER_BQ24735
 	help
 	  Say Y to enable support for the TI BQ24735 battery charger.
 
+config CHARGER_BQ2515X
+	tristate "TI BQ2515X battery charger family"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the TI BQ2515X family of battery
+	  charging integrated circuits. The BQ2515X are highly integrated
+	  battery charge management ICs that integrate the most common
+	  functions for wearable devices, namely a charger, an output voltage
+	  rail, ADC for battery and system monitoring, and push-button
+	  controller.
+
 config CHARGER_BQ25890
 	tristate "TI BQ25890 battery charger driver"
 	depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 6c7da920ea83..8fcc175a7e22 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_BQ2415X)	+= bq2415x_charger.o
 obj-$(CONFIG_CHARGER_BQ24190)	+= bq24190_charger.o
 obj-$(CONFIG_CHARGER_BQ24257)	+= bq24257_charger.o
 obj-$(CONFIG_CHARGER_BQ24735)	+= bq24735-charger.o
+obj-$(CONFIG_CHARGER_BQ2515X)	+= bq2515x_charger.o
 obj-$(CONFIG_CHARGER_BQ25890)	+= bq25890_charger.o
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c
new file mode 100644
index 000000000000..3e9d221e23bb
--- /dev/null
+++ b/drivers/power/supply/bq2515x_charger.c
@@ -0,0 +1,1159 @@
+// SPDX-License-Identifier: GPL-2.0
+// BQ2515X Battery Charger Driver
+// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define BQ2515X_MANUFACTURER "Texas Instruments"
+
+#define BQ2515X_STAT0		0x00
+#define BQ2515X_STAT1		0x01
+#define BQ2515X_STAT2		0x02
+#define BQ2515X_FLAG0		0x03
+#define BQ2515X_FLAG1		0x04
+#define BQ2515X_FLAG2		0x05
+#define BQ2515X_FLAG3		0x06
+#define BQ2515X_MASK0		0x07
+#define BQ2515X_MASK1		0x08
+#define BQ2515X_MASK2		0x09
+#define BQ2515X_MASK3		0x0a
+#define BQ2515X_VBAT_CTRL	0x12
+#define BQ2515X_ICHG_CTRL	0x13
+#define BQ2515X_PCHRGCTRL	0x14
+#define BQ2515X_TERMCTRL	0x15
+#define BQ2515X_BUVLO		0x16
+#define BQ2515X_CHARGERCTRL0	0x17
+#define BQ2515X_CHARGERCTRL1	0x18
+#define BQ2515X_ILIMCTRL	0x19
+#define BQ2515X_LDOCTRL		0x1d
+#define BQ2515X_MRCTRL		0x30
+#define BQ2515X_ICCTRL0		0x35
+#define BQ2515X_ICCTRL1		0x36
+#define BQ2515X_ICCTRL2		0x37
+#define BQ2515X_ADCCTRL0	0x40
+#define BQ2515X_ADCCTRL1	0x41
+#define BQ2515X_ADC_VBAT_M	0x42
+#define BQ2515X_ADC_VBAT_L	0x43
+#define BQ2515X_ADC_TS_M	0x44
+#define BQ2515X_ADC_TS_L	0x45
+#define BQ2515X_ADC_ICHG_M	0x46
+#define BQ2515X_ADC_ICHG_L	0x47
+#define BQ2515X_ADC_ADCIN_M	0x48
+#define BQ2515X_ADC_ADCIN_L	0x49
+#define BQ2515X_ADC_VIN_M	0x4a
+#define BQ2515X_ADC_VIN_L	0x4b
+#define BQ2515X_ADC_PMID_M	0x4c
+#define BQ2515X_ADC_PMID_L	0x4d
+#define BQ2515X_ADC_IIN_M	0x4e
+#define BQ2515X_ADC_IIN_L	0x4f
+#define BQ2515X_ADC_COMP1_M	0x52
+#define BQ2515X_ADC_COMP1_L	0X53
+#define BQ2515X_ADC_COMP2_M	0X54
+#define BQ2515X_ADC_COMP2_L	0x55
+#define BQ2515X_ADC_COMP3_M	0x56
+#define BQ2515X_ADC_COMP3_L	0x57
+#define BQ2515X_ADC_READ_EN	0x58
+#define BQ2515X_TS_FASTCHGCTRL	0x61
+#define BQ2515X_TS_COLD		0x62
+#define BQ2515X_TS_COOL		0x63
+#define BQ2515X_TS_WARM		0x64
+#define BQ2515X_TS_HOT		0x65
+#define BQ2515X_DEVICE_ID	0x6f
+
+#define BQ2515X_DEFAULT_ICHG_UA		10000
+#define BQ25150_DEFAULT_ILIM_UA		100000
+#define BQ25155_DEFAULT_ILIM_UA		500000
+#define BQ2515X_DEFAULT_VBAT_REG_UV	4200000
+#define BQ2515X_DEFAULT_IPRECHARGE_UA	2500
+
+#define BQ2515X_DIVISOR				65536
+#define BQ2515X_VBAT_BASE_VOLT			3600000
+#define BQ2515X_VBAT_REG_MAX			4600000
+#define BQ2515X_VBAT_REG_MIN			3600000
+#define BQ2515X_VBAT_STEP_UV			10000
+#define BQ2515X_UV_FACTOR			1000000
+#define BQ2515X_VBAT_MULTIPLIER			6
+#define BQ2515X_ICHG_DIVISOR			52429
+#define BQ2515X_ICHG_CURR_STEP_THRESH_UA	318750
+#define BQ2515X_ICHG_MIN_UA			0
+#define BQ2515X_ICHG_MAX_UA			500000
+#define BQ2515X_ICHG_RNG_1B0_UA			1250
+#define BQ2515X_ICHG_RNG_1B1_UA			2500
+#define BQ2515X_VLOWV_SEL_1B0_UV		3000000
+#define BQ2515X_VLOWV_SEL_1B1_UV		2800000
+#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA	18750
+#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA	37500
+#define BQ2515X_TWAKE2_MIN_US			1700000
+#define BQ2515X_TWAKE2_MAX_US			2300000
+
+#define BQ2515X_ILIM_150MA	0x2
+#define BQ2515X_ILIM_MASK	0x7
+#define BQ2515X_ILIM_MIN	50000
+#define BQ2515X_ILIM_MAX	600000
+#define BQ2515X_HEALTH_MASK	0xf
+#define BQ2515X_ICHGRNG_MASK	0x80
+#define BQ2515X_STAT0_MASK	0x0f
+#define BQ2515X_STAT1_MASK	0x1f
+#define BQ2515X_PRECHARGE_MASK	0x1f
+
+#define BQ2515X_TS_HOT_STAT		BIT(0)
+#define BQ2515X_TS_WARM_STAT		BIT(1)
+#define BQ2515X_TS_COOL_STAT		BIT(2)
+#define BQ2515X_TS_COLD_STAT		BIT(3)
+#define BQ2515X_SAFETY_TIMER_EXP	BIT(5)
+
+#define BQ2515X_EN_VBAT_READ		BIT(3)
+#define BQ2515X_EN_ICHG_READ		BIT(5)
+
+#define BQ2515X_VIN_GOOD		BIT(0)
+#define BQ2515X_CHRG_DONE		BIT(5)
+#define BQ2515X_CV_CHRG_MODE		BIT(6)
+
+#define BQ2515X_VIN_OVP_FAULT_STAT	BIT(7)
+
+#define BQ2515X_WATCHDOG_DISABLE	BIT(4)
+
+#define BQ2515X_ICHARGE_RANGE		BIT(7)
+
+#define BQ2515X_VLOWV_SEL		BIT(5)
+
+#define BQ2515X_CHARGER_DISABLE		BIT(0)
+
+#define BQ2515X_HWRESET_14S_WD		BIT(1)
+
+static const int bq2515x_ilim_lvl_values[] = {
+	50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000
+};
+
+/**
+ * struct bq2515x_init_data -
+ * @ilim: input current limit
+ */
+struct bq2515x_init_data {
+	int ilim;
+};
+
+enum bq2515x_id {
+	BQ25150,
+	BQ25155,
+};
+
+/**
+ * struct bq2515x_device -
+ * @mains: mains properties
+ * @battery: battery properties
+ * @regmap: register map structure
+ * @dev: device structure
+ *
+ * @reset_gpio: manual reset (MR) pin
+ * @powerdown_gpio: low power mode pin
+ * @ac_detect_gpio: power good (PG) pin
+ * @ce_gpio: charge enable (CE) pin
+ *
+ * @model_name: string value describing device model
+ * @device_id: value of device_id
+ * @mains_online: boolean value indicating power supply online
+ *
+ * @bq2515x_init_data init_data: charger initialization data structure
+ */
+struct bq2515x_device {
+	struct power_supply *mains;
+	struct power_supply *battery;
+	struct regmap *regmap;
+	struct device *dev;
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *powerdown_gpio;
+	struct gpio_desc *ac_detect_gpio;
+	struct gpio_desc *ce_gpio;
+
+	char model_name[I2C_NAME_SIZE];
+	int device_id;
+	bool mains_online;
+
+	struct bq2515x_init_data init_data;
+};
+
+static struct reg_default bq25150_reg_defaults[] = {
+	{BQ2515X_FLAG0, 0x0},
+	{BQ2515X_FLAG1, 0x0},
+	{BQ2515X_FLAG2, 0x0},
+	{BQ2515X_FLAG3, 0x0},
+	{BQ2515X_MASK0, 0x0},
+	{BQ2515X_MASK1, 0x0},
+	{BQ2515X_MASK2, 0x71},
+	{BQ2515X_MASK3, 0x0},
+	{BQ2515X_VBAT_CTRL, 0x3C},
+	{BQ2515X_ICHG_CTRL, 0x8},
+	{BQ2515X_PCHRGCTRL, 0x2},
+	{BQ2515X_TERMCTRL, 0x14},
+	{BQ2515X_BUVLO, 0x0},
+	{BQ2515X_CHARGERCTRL0, 0x82},
+	{BQ2515X_CHARGERCTRL1, 0x42},
+	{BQ2515X_ILIMCTRL, 0x1},
+	{BQ2515X_LDOCTRL, 0xB0},
+	{BQ2515X_MRCTRL, 0x2A},
+	{BQ2515X_ICCTRL0, 0x10},
+	{BQ2515X_ICCTRL1, 0x0},
+	{BQ2515X_ICCTRL2, 0x0},
+	{BQ2515X_ADCCTRL0, 0x2},
+	{BQ2515X_ADCCTRL1, 0x40},
+	{BQ2515X_ADC_COMP1_M, 0x23},
+	{BQ2515X_ADC_COMP1_L, 0x20},
+	{BQ2515X_ADC_COMP2_M, 0x38},
+	{BQ2515X_ADC_COMP2_L, 0x90},
+	{BQ2515X_ADC_COMP3_M, 0x0},
+	{BQ2515X_ADC_COMP3_L, 0x0},
+	{BQ2515X_ADC_READ_EN, 0x0},
+	{BQ2515X_TS_FASTCHGCTRL, 0x34},
+	{BQ2515X_TS_COLD, 0x7C},
+	{BQ2515X_TS_COOL, 0x6D},
+	{BQ2515X_TS_WARM, 0x38},
+	{BQ2515X_TS_HOT, 0x27},
+	{BQ2515X_DEVICE_ID, 0x20},
+};
+
+static struct reg_default bq25155_reg_defaults[] = {
+	{BQ2515X_FLAG0, 0x0},
+	{BQ2515X_FLAG1, 0x0},
+	{BQ2515X_FLAG2, 0x0},
+	{BQ2515X_FLAG3, 0x0},
+	{BQ2515X_MASK0, 0x0},
+	{BQ2515X_MASK1, 0x0},
+	{BQ2515X_MASK2, 0x71},
+	{BQ2515X_MASK3, 0x0},
+	{BQ2515X_VBAT_CTRL, 0x3C},
+	{BQ2515X_ICHG_CTRL, 0x8},
+	{BQ2515X_PCHRGCTRL, 0x2},
+	{BQ2515X_TERMCTRL, 0x14},
+	{BQ2515X_BUVLO, 0x0},
+	{BQ2515X_CHARGERCTRL0, 0x82},
+	{BQ2515X_CHARGERCTRL1, 0xC2},
+	{BQ2515X_ILIMCTRL, 0x6},
+	{BQ2515X_LDOCTRL, 0xB0},
+	{BQ2515X_MRCTRL, 0x2A},
+	{BQ2515X_ICCTRL0, 0x10},
+	{BQ2515X_ICCTRL1, 0x0},
+	{BQ2515X_ICCTRL2, 0x40},
+	{BQ2515X_ADCCTRL0, 0x2},
+	{BQ2515X_ADCCTRL1, 0x40},
+	{BQ2515X_ADC_COMP1_M, 0x23},
+	{BQ2515X_ADC_COMP1_L, 0x20},
+	{BQ2515X_ADC_COMP2_M, 0x38},
+	{BQ2515X_ADC_COMP2_L, 0x90},
+	{BQ2515X_ADC_COMP3_M, 0x0},
+	{BQ2515X_ADC_COMP3_L, 0x0},
+	{BQ2515X_ADC_READ_EN, 0x0},
+	{BQ2515X_TS_FASTCHGCTRL, 0x34},
+	{BQ2515X_TS_COLD, 0x7C},
+	{BQ2515X_TS_COOL, 0x6D},
+	{BQ2515X_TS_WARM, 0x38},
+	{BQ2515X_TS_HOT, 0x27},
+	{BQ2515X_DEVICE_ID, 0x35},
+};
+
+static int bq2515x_wake_up(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	int val;
+
+	/* Read the STAT register if we can read it then the device is out
+	 * of ship mode.  If the register cannot be read then attempt to wake
+	 * it up and enable the ADC.
+	 */
+	ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
+	if (ret)
+		return ret;
+
+	/* Need to toggle LP and bring device out of ship mode. The device
+	 * will exit the ship mode when the MR pin is held low for at least
+	 * t_WAKE2 as shown in section 8.3.7.1 of the datasheet.
+	 */
+	gpiod_set_value_cansleep(bq2515x->powerdown_gpio, 0);
+
+	gpiod_set_value_cansleep(bq2515x->reset_gpio, 0);
+	usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US);
+	gpiod_set_value_cansleep(bq2515x->reset_gpio, 1);
+
+	return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN,
+				(BQ2515X_EN_VBAT_READ | BQ2515X_EN_ICHG_READ));
+}
+
+static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x)
+{
+	bool dc = false;
+	unsigned int val;
+	int ret;
+
+	if (bq2515x->ac_detect_gpio)
+		val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio);
+	else {
+		ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
+		if (ret)
+			return ret;
+	}
+
+	dc = val & BQ2515X_VIN_GOOD;
+
+	ret = bq2515x->mains_online != dc;
+
+	bq2515x->mains_online = dc;
+
+	return ret;
+}
+
+static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x)
+{
+	int ret;
+
+	ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0,
+			BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
+						BQ2515X_HWRESET_14S_WD, 0);
+}
+
+static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	int vbat_msb;
+	int vbat_lsb;
+	uint32_t vbat_measurement;
+
+	if (!bq2515x->mains_online)
+		bq2515x_wake_up(bq2515x);
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb);
+	if (ret)
+		return ret;
+
+	vbat_measurement = (vbat_msb << 8) | vbat_lsb;
+
+	return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) *
+						BQ2515X_VBAT_MULTIPLIER;
+}
+
+static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	int ichg_msb;
+	int ichg_lsb;
+	uint32_t ichg_measurement;
+	u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+	unsigned int ichg_reg_code, reg_code;
+	unsigned int icharge_range = 0, pchrgctrl;
+	unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV;
+
+	if (!bq2515x->mains_online)
+		return -ENODATA;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb);
+	if (ret)
+		return ret;
+
+	ichg_measurement = (ichg_msb << 8) | ichg_lsb;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo);
+	if (ret)
+		return ret;
+
+	vlowv_sel = buvlo & BQ2515X_VLOWV_SEL;
+
+	if (vlowv_sel)
+		vlowv = BQ2515X_VLOWV_SEL_1B1_UV;
+
+	if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) {
+		ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+								&pchrgctrl);
+		if (ret)
+			return ret;
+
+		reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
+	} else {
+		ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL,
+							&ichg_reg_code);
+		if (ret)
+			return ret;
+
+		reg_code = ichg_reg_code;
+	}
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+	if (ret)
+		return ret;
+
+	icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+	if (icharge_range)
+		ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+	return reg_code * (ichg_multiplier * ichg_measurement /
+							BQ2515X_ICHG_DIVISOR);
+}
+
+static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	int ce_pin;
+	int icctrl2;
+	int charger_disable;
+
+	ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio);
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2);
+	if (ret)
+		return ret;
+
+	charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE;
+
+	if (charger_disable || ce_pin)
+		return true;
+
+	return false;
+}
+
+static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val)
+{
+	gpiod_set_value_cansleep(bq2515x->ce_gpio, val);
+
+	return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
+					BQ2515X_CHARGER_DISABLE, val);
+}
+
+static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+	unsigned int ichg_reg_code;
+	unsigned int pchrgctrl;
+	unsigned int icharge_range;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+	if (ret)
+		return ret;
+
+	icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+	if (icharge_range)
+		ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+	return ichg_reg_code * ichg_multiplier;
+}
+
+static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x,
+								int val)
+{
+	int ret;
+	unsigned int ichg_reg_code;
+	u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+	unsigned int icharge_range = 0;
+
+	if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA)
+		return -EINVAL;
+
+	if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) {
+		ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+		icharge_range = BQ2515X_ICHARGE_RANGE;
+	}
+
+	bq2515x_set_charge_disable(bq2515x, 1);
+
+	ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+					BQ2515X_ICHARGE_RANGE, icharge_range);
+	if (ret)
+		return ret;
+
+	ichg_reg_code = val / ichg_multiplier;
+
+	ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code);
+	if (ret)
+		return ret;
+
+	return bq2515x_set_charge_disable(bq2515x, 0);
+}
+
+static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	unsigned int pchrgctrl;
+	unsigned int icharge_range;
+	u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+	unsigned int precharge_reg_code;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+	if (ret)
+		return ret;
+
+	icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+	if (icharge_range)
+		precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+	precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
+
+	return precharge_reg_code * precharge_multiplier;
+}
+
+static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x,
+					int val)
+{
+	int ret;
+	unsigned int pchrgctrl;
+	unsigned int icharge_range;
+	unsigned int precharge_reg_code;
+	u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+	u16 precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+	if (ret)
+		return ret;
+
+	icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+	if (icharge_range) {
+		precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA;
+		precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+	}
+	if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA)
+		return -EINVAL;
+
+	precharge_reg_code = val / precharge_multiplier;
+
+	ret = bq2515x_set_charge_disable(bq2515x, 1);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+				BQ2515X_PRECHARGE_MASK, precharge_reg_code);
+	if (ret)
+		return ret;
+
+	return bq2515x_set_charge_disable(bq2515x, 0);
+}
+
+static int bq2515x_charging_status(struct bq2515x_device *bq2515x,
+				   union power_supply_propval *val)
+{
+	bool status0_no_fault;
+	bool status1_no_fault;
+	bool ce_status;
+	bool charge_done;
+	unsigned int status;
+	int ret;
+
+	if (!bq2515x->mains_online) {
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		return 0;
+	}
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status);
+	if (ret)
+		return ret;
+
+	/*
+	 * The code block below is used to determine if any faults from the
+	 * STAT0 register are disbaling charging or if the charge has completed
+	 * according to the CHARGE_DONE_STAT bit.
+	 */
+	if (((status & BQ2515X_STAT0_MASK) == true) &
+			((status & BQ2515X_CHRG_DONE) == false)) {
+		status0_no_fault = true;
+		charge_done = false;
+	} else if (status & BQ2515X_CHRG_DONE) {
+		charge_done = true;
+		status0_no_fault = false;
+	} else {
+		status0_no_fault = false;
+		charge_done = false;
+	}
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status);
+	if (ret)
+		return ret;
+	/*
+	 * The code block below is used to determine if any faults from the
+	 * STAT1 register are disbaling charging
+	 */
+	if ((status & BQ2515X_STAT1_MASK) == false)
+		status1_no_fault = true;
+	else
+		status1_no_fault = false;
+
+	ce_status = (!bq2515x_get_charge_disable(bq2515x));
+
+	/*
+	 * If there are no faults and charging is enabled, then status is
+	 * charging. Otherwise, if charging is complete, then status is full.
+	 * Otherwise, if a fault exists or charging is disabled, then status is
+	 * not charging
+	 */
+	if (status0_no_fault & status1_no_fault & ce_status)
+		val->intval = POWER_SUPPLY_STATUS_CHARGING;
+	else if (charge_done)
+		val->intval = POWER_SUPPLY_STATUS_FULL;
+	else if (!(status0_no_fault & status1_no_fault & ce_status))
+		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+	return 0;
+}
+
+static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x)
+{
+	int vbat_reg_code;
+	int ret;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code);
+	if (ret)
+		return ret;
+
+	return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV;
+}
+
+static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val)
+{
+	int vbat_reg_code;
+
+	if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN)
+		return -EINVAL;
+
+	vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV;
+
+	return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code);
+}
+
+static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	int ilimctrl;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl);
+	if (ret)
+		return ret;
+
+	return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK];
+}
+
+static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val)
+{
+	int i = 0;
+	unsigned int array_size = ARRAY_SIZE(bq2515x_ilim_lvl_values);
+
+	if (val >= bq2515x_ilim_lvl_values[array_size - 1]) {
+		i = array_size - 1;
+	} else {
+		for (i = array_size - 1; i > 0; i--) {
+			if (val >= bq2515x_ilim_lvl_values[i])
+				break;
+		}
+	}
+	return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i);
+}
+
+static int bq2515x_power_supply_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_PRECHARGE_CURRENT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x,
+				      union power_supply_propval *val)
+{
+	int health = POWER_SUPPLY_HEALTH_GOOD;
+	int ret;
+	unsigned int stat1;
+	unsigned int flag3;
+
+	if (!bq2515x->mains_online)
+		bq2515x_wake_up(bq2515x);
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1);
+	if (ret)
+		return ret;
+
+	if (stat1 & BQ2515X_HEALTH_MASK) {
+		switch (stat1 & BQ2515X_HEALTH_MASK) {
+		case BQ2515X_TS_HOT_STAT:
+			health = POWER_SUPPLY_HEALTH_HOT;
+			break;
+		case BQ2515X_TS_WARM_STAT:
+			health = POWER_SUPPLY_HEALTH_WARM;
+			break;
+		case BQ2515X_TS_COOL_STAT:
+			health = POWER_SUPPLY_HEALTH_COOL;
+			break;
+		case BQ2515X_TS_COLD_STAT:
+			health = POWER_SUPPLY_HEALTH_COLD;
+			break;
+		default:
+			health = POWER_SUPPLY_HEALTH_UNKNOWN;
+			break;
+		}
+	}
+
+	if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT)
+		health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+
+	if (flag3 & BQ2515X_SAFETY_TIMER_EXP)
+		health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+
+	val->intval = health;
+	return 0;
+}
+
+static int bq2515x_mains_set_property(struct power_supply *psy,
+		enum power_supply_property prop,
+		const union power_supply_propval *val)
+{
+	struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+	int ret;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		ret = bq2515x_set_ilim_lvl(bq2515x, val->intval);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int bq2515x_battery_set_property(struct power_supply *psy,
+		enum power_supply_property prop,
+		const union power_supply_propval *val)
+{
+	struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+	int ret;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2515x_set_batt_reg(bq2515x, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq2515x_set_const_charge_current(bq2515x, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+		ret = bq2515x_set_precharge_current(bq2515x, val->intval);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int bq2515x_mains_get_property(struct power_supply *psy,
+				     enum power_supply_property prop,
+				     union power_supply_propval *val)
+{
+	struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+	int ret = 0;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = bq2515x->mains_online;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = bq2515x_charger_get_health(bq2515x, val);
+		if (ret)
+			val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		ret = bq2515x_get_ilim_lvl(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = bq2515x->model_name;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = BQ2515X_MANUFACTURER;
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = bq2515x_charging_status(bq2515x, val);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int bq2515x_battery_get_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       union power_supply_propval *val)
+{
+	struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+	int ret;
+
+	ret = bq2515x_update_ps_status(bq2515x);
+	if (ret)
+		return ret;
+
+	switch (prop) {
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = bq2515x_get_battery_voltage_now(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = bq2515x_get_battery_current_now(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq2515x_get_const_charge_current(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+		ret = bq2515x_get_precharge_current(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2515x_get_batt_reg(bq2515x);
+		if (ret < 0)
+			return ret;
+
+		val->intval = ret;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property bq2515x_battery_properties[] = {
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+};
+
+static enum power_supply_property bq2515x_mains_properties[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static struct power_supply_desc bq2515x_mains_desc = {
+	.name			= "bq2515x-mains",
+	.type			= POWER_SUPPLY_TYPE_MAINS,
+	.get_property		= bq2515x_mains_get_property,
+	.set_property		= bq2515x_mains_set_property,
+	.properties		= bq2515x_mains_properties,
+	.num_properties		= ARRAY_SIZE(bq2515x_mains_properties),
+	.property_is_writeable	= bq2515x_power_supply_property_is_writeable,
+};
+
+static struct power_supply_desc bq2515x_battery_desc = {
+	.name			= "bq2515x-battery",
+	.type			= POWER_SUPPLY_TYPE_BATTERY,
+	.get_property		= bq2515x_battery_get_property,
+	.set_property		= bq2515x_battery_set_property,
+	.properties		= bq2515x_battery_properties,
+	.num_properties		= ARRAY_SIZE(bq2515x_battery_properties),
+	.property_is_writeable	= bq2515x_power_supply_property_is_writeable,
+};
+
+static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x,
+		struct device *dev, struct power_supply_config psy_cfg)
+{
+	bq2515x->mains = devm_power_supply_register(bq2515x->dev,
+						    &bq2515x_mains_desc,
+						    &psy_cfg);
+	if (IS_ERR(bq2515x->mains))
+		return -EINVAL;
+
+	bq2515x->battery = devm_power_supply_register(bq2515x->dev,
+						      &bq2515x_battery_desc,
+						      &psy_cfg);
+	if (IS_ERR(bq2515x->battery))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int bq2515x_hw_init(struct bq2515x_device *bq2515x)
+{
+	int ret;
+	struct power_supply_battery_info bat_info = { };
+
+	ret = bq2515x_disable_watchdog_timers(bq2515x);
+	if (ret)
+		return ret;
+
+	if (bq2515x->init_data.ilim) {
+		ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim);
+		if (ret)
+			return ret;
+	}
+
+	ret = power_supply_get_battery_info(bq2515x->mains, &bat_info);
+	if (ret) {
+		dev_warn(bq2515x->dev, "battery info missing, default values will be applied\n");
+
+		bat_info.constant_charge_current_max_ua =
+						BQ2515X_DEFAULT_ICHG_UA;
+
+		bat_info.constant_charge_voltage_max_uv =
+						BQ2515X_DEFAULT_VBAT_REG_UV;
+
+		bat_info.precharge_current_ua =
+						BQ2515X_DEFAULT_IPRECHARGE_UA;
+	}
+
+	ret = bq2515x_set_const_charge_current(bq2515x,
+			bat_info.constant_charge_current_max_ua);
+	if (ret)
+		return ret;
+
+	ret = bq2515x_set_batt_reg(bq2515x,
+			bat_info.constant_charge_voltage_max_uv);
+	if (ret)
+		return ret;
+
+	return bq2515x_set_precharge_current(bq2515x,
+			bat_info.precharge_current_ua);
+}
+
+static int bq2515x_read_properties(struct bq2515x_device *bq2515x)
+{
+	int ret;
+
+	ret = device_property_read_u32(bq2515x->dev,
+				      "input-current-limit-microamp",
+				      &bq2515x->init_data.ilim);
+	if (ret)
+		switch (bq2515x->device_id) {
+		case BQ25150:
+			bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA;
+			break;
+		case BQ25155:
+			bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA;
+			break;
+		}
+
+	bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev,
+						   "ac-detect", GPIOD_IN);
+	if (IS_ERR(bq2515x->ac_detect_gpio)) {
+		ret = PTR_ERR(bq2515x->ac_detect_gpio);
+		if (ret != -ENODEV) {
+			dev_err(bq2515x->dev, "Failed to get ac detect");
+			return ret;
+		}
+		bq2515x->ac_detect_gpio = NULL;
+	}
+
+	bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev,
+						   "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(bq2515x->reset_gpio)) {
+		ret = PTR_ERR(bq2515x->reset_gpio);
+		if (ret != -ENODEV) {
+			dev_err(bq2515x->dev, "Failed to get reset");
+			return ret;
+		}
+		bq2515x->reset_gpio = NULL;
+	}
+
+	bq2515x->powerdown_gpio = devm_gpiod_get_optional(bq2515x->dev,
+						"powerdown", GPIOD_OUT_LOW);
+	if (IS_ERR(bq2515x->powerdown_gpio)) {
+		ret = PTR_ERR(bq2515x->powerdown_gpio);
+		if (ret != -ENODEV) {
+			dev_err(bq2515x->dev, "Failed to get powerdown");
+			return ret;
+		}
+		bq2515x->powerdown_gpio = NULL;
+	}
+
+	bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev,
+						   "charge-enable",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(bq2515x->ce_gpio)) {
+		ret = PTR_ERR(bq2515x->ce_gpio);
+		if (ret != -ENODEV) {
+			dev_err(bq2515x->dev, "Failed to get ce");
+			return ret;
+		}
+		bq2515x->ce_gpio = NULL;
+	}
+
+	return 0;
+}
+
+static bool bq2515x_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case BQ2515X_STAT0 ... BQ2515X_FLAG3:
+	case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config bq25150_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register		= BQ2515X_DEVICE_ID,
+	.reg_defaults		= bq25150_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(bq25150_reg_defaults),
+	.cache_type		= REGCACHE_RBTREE,
+	.volatile_reg		= bq2515x_volatile_register,
+};
+
+static const struct regmap_config bq25155_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register		= BQ2515X_DEVICE_ID,
+	.reg_defaults		= bq25155_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(bq25155_reg_defaults),
+	.cache_type		= REGCACHE_RBTREE,
+	.volatile_reg		= bq2515x_volatile_register,
+};
+
+static int bq2515x_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct bq2515x_device *bq2515x;
+	struct power_supply_config charger_cfg = {};
+	int ret;
+
+	bq2515x = devm_kzalloc(dev, sizeof(*bq2515x), GFP_KERNEL);
+	if (!bq2515x)
+		return -ENOMEM;
+
+	bq2515x->dev = dev;
+
+	strncpy(bq2515x->model_name, id->name, I2C_NAME_SIZE);
+
+	bq2515x->device_id = id->driver_data;
+
+	switch (bq2515x->device_id) {
+	case BQ25150:
+		bq2515x->regmap = devm_regmap_init_i2c(client,
+						&bq25150_regmap_config);
+		break;
+	case BQ25155:
+		bq2515x->regmap = devm_regmap_init_i2c(client,
+						&bq25155_regmap_config);
+		break;
+	}
+
+	if (IS_ERR(bq2515x->regmap)) {
+		dev_err(dev, "failed to allocate register map\n");
+		return PTR_ERR(bq2515x->regmap);
+	}
+
+	i2c_set_clientdata(client, bq2515x);
+
+	charger_cfg.drv_data = bq2515x;
+	charger_cfg.of_node = dev->of_node;
+
+	ret = bq2515x_read_properties(bq2515x);
+	if (ret) {
+		dev_err(dev, "Failed to read device tree properties %d\n",
+									ret);
+		return ret;
+	}
+
+	ret = bq2515x_power_supply_register(bq2515x, dev, charger_cfg);
+	if (ret) {
+		dev_err(dev, "failed to register power supply\n");
+		return ret;
+	}
+
+	ret = bq2515x_hw_init(bq2515x);
+	if (ret) {
+		dev_err(dev, "Cannot initialize the chip\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id bq2515x_i2c_ids[] = {
+	{ "bq25150", BQ25150, },
+	{ "bq25155", BQ25155, },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids);
+
+static const struct of_device_id bq2515x_of_match[] = {
+	{ .compatible = "ti,bq25150", },
+	{ .compatible = "ti,bq25155", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bq2515x_of_match);
+
+static struct i2c_driver bq2515x_driver = {
+	.driver = {
+		.name = "bq2515x-charger",
+		.of_match_table = bq2515x_of_match,
+	},
+	.probe = bq2515x_probe,
+	.id_table = bq2515x_i2c_ids,
+};
+module_i2c_driver(bq2515x_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
+MODULE_DESCRIPTION("BQ2515X charger driver");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2


^ permalink raw reply related

* [PATCH v11 1/4] power_supply: Add additional health properties to the header
From: Ricardo Rivera-Matos @ 2020-05-28 14:05 UTC (permalink / raw)
  To: sre, pali, robh
  Cc: afd, r-rivera-matos, dmurphy, linux-pm, linux-kernel, devicetree,
	sspatil, Guru Das Srinagesh
In-Reply-To: <20200528140546.25260-1-r-rivera-matos@ti.com>

From: Dan Murphy <dmurphy@ti.com>

Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum.

HEALTH_WARM, HEALTH_COOL, and HEALTH_HOT properties are taken from the JEITA spec.

Tested-by: Guru Das Srinagesh <gurus@codeaurora.org>
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 Documentation/ABI/testing/sysfs-class-power | 2 +-
 drivers/power/supply/power_supply_sysfs.c   | 2 +-
 include/linux/power_supply.h                | 3 +++
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
index bf3b48f022dc..9f3fd01a9373 100644
--- a/Documentation/ABI/testing/sysfs-class-power
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -190,7 +190,7 @@ Description:
 		Valid values: "Unknown", "Good", "Overheat", "Dead",
 			      "Over voltage", "Unspecified failure", "Cold",
 			      "Watchdog timer expire", "Safety timer expire",
-			      "Over current"
+			      "Over current", "Warm", "Cool", "Hot"
 
 What:		/sys/class/power_supply/<supply_name>/precharge_current
 Date:		June 2017
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index f37ad4eae60b..d0d549611794 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -61,7 +61,7 @@ static const char * const power_supply_charge_type_text[] = {
 static const char * const power_supply_health_text[] = {
 	"Unknown", "Good", "Overheat", "Dead", "Over voltage",
 	"Unspecified failure", "Cold", "Watchdog timer expire",
-	"Safety timer expire", "Over current"
+	"Safety timer expire", "Over current", "Warm", "Cool", "Hot"
 };
 
 static const char * const power_supply_technology_text[] = {
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index dcd5a71e6c67..8670e90c1d51 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -61,6 +61,9 @@ enum {
 	POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
 	POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
 	POWER_SUPPLY_HEALTH_OVERCURRENT,
+	POWER_SUPPLY_HEALTH_WARM,
+	POWER_SUPPLY_HEALTH_COOL,
+	POWER_SUPPLY_HEALTH_HOT,
 };
 
 enum {
-- 
2.26.2


^ permalink raw reply related

* [PATCH v11 2/4] dt-bindings: power: Convert battery.txt to battery.yaml
From: Ricardo Rivera-Matos @ 2020-05-28 14:05 UTC (permalink / raw)
  To: sre, pali, robh
  Cc: afd, r-rivera-matos, dmurphy, linux-pm, linux-kernel, devicetree,
	sspatil
In-Reply-To: <20200528140546.25260-1-r-rivera-matos@ti.com>

From: Dan Murphy <dmurphy@ti.com>

Convert the battery.txt file to yaml and fix up the examples.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../bindings/power/supply/battery.txt         |  82 +---------
 .../bindings/power/supply/battery.yaml        | 143 ++++++++++++++++++
 2 files changed, 144 insertions(+), 81 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.yaml

diff --git a/Documentation/devicetree/bindings/power/supply/battery.txt b/Documentation/devicetree/bindings/power/supply/battery.txt
index 3049cf88bdcf..b9a81621ce59 100644
--- a/Documentation/devicetree/bindings/power/supply/battery.txt
+++ b/Documentation/devicetree/bindings/power/supply/battery.txt
@@ -1,81 +1 @@
-Battery Characteristics
-
-The devicetree battery node provides static battery characteristics.
-In smart batteries, these are typically stored in non-volatile memory
-on a fuel gauge chip. The battery node should be used where there is
-no appropriate non-volatile memory, or it is unprogrammed/incorrect.
-
-Upstream dts files should not include battery nodes, unless the battery
-represented cannot easily be replaced in the system by one of a
-different type. This prevents unpredictable, potentially harmful,
-behavior should a replacement that changes the battery type occur
-without a corresponding update to the dtb.
-
-Required Properties:
- - compatible: Must be "simple-battery"
-
-Optional Properties:
- - voltage-min-design-microvolt: drained battery voltage
- - voltage-max-design-microvolt: fully charged battery voltage
- - energy-full-design-microwatt-hours: battery design energy
- - charge-full-design-microamp-hours: battery design capacity
- - precharge-current-microamp: current for pre-charge phase
- - charge-term-current-microamp: current for charge termination phase
- - constant-charge-current-max-microamp: maximum constant input current
- - constant-charge-voltage-max-microvolt: maximum constant input voltage
- - factory-internal-resistance-micro-ohms: battery factory internal resistance
- - ocv-capacity-table-0: An array providing the open circuit voltage (OCV)
-   of the battery and corresponding battery capacity percent, which is used
-   to look up battery capacity according to current OCV value. And the open
-   circuit voltage unit is microvolt.
- - ocv-capacity-table-1: Same as ocv-capacity-table-0
- ......
- - ocv-capacity-table-n: Same as ocv-capacity-table-0
- - ocv-capacity-celsius: An array containing the temperature in degree Celsius,
-   for each of the battery capacity lookup table. The first temperature value
-   specifies the OCV table 0, and the second temperature value specifies the
-   OCV table 1, and so on.
- - resistance-temp-table: An array providing the temperature in degree Celsius
-   and corresponding battery internal resistance percent, which is used to look
-   up the resistance percent according to current temperature to get a accurate
-   batterty internal resistance in different temperatures.
-
-Battery properties are named, where possible, for the corresponding
-elements in enum power_supply_property, defined in
-https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/power_supply.h
-
-Batteries must be referenced by chargers and/or fuel-gauges
-using a phandle. The phandle's property should be named
-"monitored-battery".
-
-Example:
-
-	bat: battery {
-		compatible = "simple-battery";
-		voltage-min-design-microvolt = <3200000>;
-		voltage-max-design-microvolt = <4200000>;
-		energy-full-design-microwatt-hours = <5290000>;
-		charge-full-design-microamp-hours = <1430000>;
-		precharge-current-microamp = <256000>;
-		charge-term-current-microamp = <128000>;
-		constant-charge-current-max-microamp = <900000>;
-		constant-charge-voltage-max-microvolt = <4200000>;
-		factory-internal-resistance-micro-ohms = <250000>;
-		ocv-capacity-celsius = <(-10) 0 10>;
-		ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...;
-		ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...;
-		ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...;
-		resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>;
-	};
-
-	charger: charger@11 {
-		....
-		monitored-battery = <&bat>;
-		...
-	};
-
-	fuel_gauge: fuel-gauge@22 {
-		....
-		monitored-battery = <&bat>;
-		...
-	};
+The contents of this file has been moved to battery.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/battery.yaml b/Documentation/devicetree/bindings/power/supply/battery.yaml
new file mode 100644
index 000000000000..f0b544a22219
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/battery.yaml
@@ -0,0 +1,143 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/battery.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Battery Characteristics
+
+maintainers:
+  - Sebastian Reichel <sre@kernel.org> 
+
+description: |
+  The devicetree battery node provides static battery characteristics.
+  In smart batteries, these are typically stored in non-volatile memory
+  on a fuel gauge chip. The battery node should be used where there is
+  no appropriate non-volatile memory, or it is unprogrammed/incorrect.
+
+  Upstream dts files should not include battery nodes, unless the battery
+  represented cannot easily be replaced in the system by one of a
+  different type. This prevents unpredictable, potentially harmful,
+  behavior should a replacement that changes the battery type occur
+  without a corresponding update to the dtb.
+
+  Battery properties are named, where possible, for the corresponding elements
+  in enum power_supply_property, defined in include/linux/power_supply.h
+
+  Batteries must be referenced by chargers and/or fuel-gauges using a phandle.
+  The phandle's property should be named "monitored-battery".
+
+properties:
+  compatible:
+    const: simple-battery
+
+  voltage-min-design-microvolt: 
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: drained battery voltage
+
+  voltage-max-design-microvolt:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: fully charged battery voltage
+
+  energy-full-design-microwatt-hours:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: battery design energy
+
+  charge-full-design-microamp-hours:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: battery design capacity
+
+  precharge-current-microamp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: current for pre-charge phase
+
+  charge-term-current-microamp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: current for charge termination phase
+
+  constant-charge-current-max-microamp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: maximum constant input current
+
+  constant-charge-voltage-max-microvolt:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: maximum constant input voltage
+
+  factory-internal-resistance-micro-ohms:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: battery factory internal resistance
+
+  ocv-capacity-table-0:
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    description: |
+      An array providing the open circuit voltage (OCV)
+      of the battery and corresponding battery capacity percent, which is used
+      to look up battery capacity according to current OCV value. And the open
+      circuit voltage unit is microvolt.
+
+  ocv-capacity-table-1:
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    description: Same as ocv-capacity-table-0
+
+  ocv-capacity-table-n:
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    description: Same as ocv-capacity-table-0
+
+  ocv-capacity-celsius:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      An array containing the temperature in degree Celsius,
+      for each of the battery capacity lookup table. The first temperature value
+      specifies the OCV table 0, and the second temperature value specifies the
+      OCV table 1, and so on.
+
+  resistance-temp-table:
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    description: |
+      An array providing the temperature in degree Celsius
+      and corresponding battery internal resistance percent, which is used to
+      look up the resistance percent according to current temperature to get an
+      accurate batterty internal resistance in different temperatures.
+
+  monitored-battery:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle to the battery node being monitored
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    power {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      battery:battery {
+        compatible = "simple-battery";
+        voltage-min-design-microvolt = <3200000>;
+        voltage-max-design-microvolt = <4200000>;
+        energy-full-design-microwatt-hours = <5290000>;
+        charge-full-design-microamp-hours = <1430000>;
+        precharge-current-microamp = <256000>;
+        charge-term-current-microamp = <128000>;
+        constant-charge-current-max-microamp = <900000>;
+        constant-charge-voltage-max-microvolt = <4200000>;
+        factory-internal-resistance-micro-ohms = <250000>;
+        ocv-capacity-celsius = <(-10) 0 10>;
+        ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>;
+        ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>;
+        resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>;
+      };
+
+      charger:charger@11 {
+        reg = <0x11>;
+        monitored-battery = <&battery>;
+      };
+
+      fuel_gauge:fuel-gauge@22 {
+        reg = <0x22>;
+        monitored-battery = <&battery>;
+      };
+    };
-- 
2.26.2


^ permalink raw reply related

* [PATCH v11 0/4] Add JEITA properties and introduce the bq2515x charger
From: Ricardo Rivera-Matos @ 2020-05-28 14:05 UTC (permalink / raw)
  To: sre, pali, robh
  Cc: afd, r-rivera-matos, dmurphy, linux-pm, linux-kernel, devicetree,
	sspatil

Hello,

This patchset adds additional health properties to the power_supply header.
These additional properties are taken from the JEITA specification. This
patchset also introduces the bq2515x family of charging ICs.

Dan Murphy (2):
  power_supply: Add additional health properties to the header
  dt-bindings: power: Convert battery.txt to battery.yaml

Ricardo Rivera-Matos (2):
  dt-bindings: power: Add the bindings for the bq2515x family of
    chargers.
  power: supply: bq25150 introduce the bq25150

 Documentation/ABI/testing/sysfs-class-power   |    2 +-
 .../bindings/power/supply/battery.txt         |   82 +-
 .../bindings/power/supply/battery.yaml        |  143 ++
 .../bindings/power/supply/bq2515x.yaml        |   91 ++
 drivers/power/supply/Kconfig                  |   13 +
 drivers/power/supply/Makefile                 |    1 +
 drivers/power/supply/bq2515x_charger.c        | 1159 +++++++++++++++++
 drivers/power/supply/power_supply_sysfs.c     |    2 +-
 include/linux/power_supply.h                  |    3 +
 9 files changed, 1413 insertions(+), 83 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.yaml
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml
 create mode 100644 drivers/power/supply/bq2515x_charger.c

-- 
2.26.2


^ permalink raw reply

* Re: [PATCH v6 02/18] mtd: rawnand: Create a new enumeration to describe OOB placement
From: Miquel Raynal @ 2020-05-28 14:10 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
	Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
	Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
	Mason Yang, Julien Su
In-Reply-To: <20200528140852.51f19794@collabora.com>


Boris Brezillon <boris.brezillon@collabora.com> wrote on Thu, 28 May
2020 14:08:52 +0200:

> On Thu, 28 May 2020 13:30:57 +0200
> Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> 
> In the subject s/OOB placement/ECC placement/

That's a leftover. Fixed in patch 2 and 3.

^ permalink raw reply

* Re: [PATCH v4 4/4] dmaengine: mediatek-cqdma: add dma mask for capability
From: Matthias Brugger @ 2020-05-28 14:10 UTC (permalink / raw)
  To: EastL, Sean Wang
  Cc: vkoul, robh+dt, mark.rutland, dmaengine, linux-kernel,
	linux-arm-kernel, linux-mediatek, devicetree, wsd_upstream
In-Reply-To: <1590659832-31476-5-git-send-email-EastL.Lee@mediatek.com>



On 28/05/2020 11:57, EastL wrote:
> This patch add dma mask for capability.
> 
> Change-Id: I31f4622f9541d769702029532e5f5f185815dda2

No Change-Id in the commit message please.

> Signed-off-by: EastL <EastL.Lee@mediatek.com>
> ---
>  drivers/dma/mediatek/mtk-cqdma.c | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c
> index bca7118..1805a76 100644
> --- a/drivers/dma/mediatek/mtk-cqdma.c
> +++ b/drivers/dma/mediatek/mtk-cqdma.c
> @@ -117,6 +117,7 @@ struct mtk_cqdma_vchan {
>   * @clk:                    The clock that device internal is using
>   * @dma_requests:           The number of VCs the device supports to
>   * @dma_channels:           The number of PCs the device supports to
> + * @dma_mask:               A mask for DMA capability
>   * @vc:                     The pointer to all available VCs
>   * @pc:                     The pointer to all the underlying PCs
>   */
> @@ -126,6 +127,7 @@ struct mtk_cqdma_device {
>  
>  	u32 dma_requests;
>  	u32 dma_channels;
> +	u32 dma_mask;
>  	struct mtk_cqdma_vchan *vc;
>  	struct mtk_cqdma_pchan **pc;
>  };
> @@ -549,6 +551,7 @@ static void mtk_cqdma_hw_deinit(struct mtk_cqdma_device *cqdma)
>  };
>  MODULE_DEVICE_TABLE(of, mtk_cqdma_match);
>  
> +static u64 cqdma_dmamask;
>  static int mtk_cqdma_probe(struct platform_device *pdev)
>  {
>  	struct mtk_cqdma_device *cqdma;
> @@ -607,6 +610,16 @@ static int mtk_cqdma_probe(struct platform_device *pdev)
>  		cqdma->dma_channels = MTK_CQDMA_NR_PCHANS;
>  	}
>  
> +	if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
> +						      "dma-channel-mask",
> +						      &cqdma->dma_mask)) {

I'd prefer:

if (pdev->dev.of_node)
    ret = of_property_read_u32(pdev->dev.of_node,
                               "dma-channel-mask",
                               &cqdma->dma_mask))
if (ret) {
    dev_warn(&pdev->dev,
             "Using 0 as missing dma-channel-mask
              property\n");
    cqdma->dma_mask = 0;
}

> +		dev_info(&pdev->dev,
> +			 "Using 0 as missing dma-channel-mask property\n");

dev_warn should be OK.

> +	} else {
> +		cqdma_dmamask = DMA_BIT_MASK(cqdma->dma_mask);
> +		pdev->dev.dma_mask = &cqdma_dmamask;

if (dma_set_mask(&pdev->dev,
    DMA_BIT_MASK(cqdma->dma_mask)) {
         /* error out */
}

> +	}
> +
>  	cqdma->pc = devm_kcalloc(&pdev->dev, cqdma->dma_channels,
>  				 sizeof(*cqdma->pc), GFP_KERNEL);
>  	if (!cqdma->pc)
> 

^ permalink raw reply

* Re: [PATCH v6 03/18] mtd: rawnand: Separate the ECC engine type and the OOB placement
From: Boris Brezillon @ 2020-05-28 14:14 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
	Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
	Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
	Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-4-miquel.raynal@bootlin.com>

On Thu, 28 May 2020 13:30:58 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> The use of "syndrome" placement should not be encoded in the ECC
> engine mode/type.
> 
> Create a "placement" field in NAND chip and change all occurrences of
> the NAND_ECC_HW_SYNDROME enumeration to be just NAND_ECC_HW and
> possibly a placement entry like NAND_ECC_PLACEMENT_INTERLEAVED.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

I'm not a big fan of the extra indentation level you add to the davinci
driver, but I can live with it.

> ---
>  arch/arm/mach-davinci/board-dm355-leopard.c |   3 +-
>  drivers/mtd/nand/raw/cafe_nand.c            |   3 +-
>  drivers/mtd/nand/raw/davinci_nand.c         |   5 +-
>  drivers/mtd/nand/raw/denali.c               |   3 +-
>  drivers/mtd/nand/raw/diskonchip.c           |   3 +-
>  drivers/mtd/nand/raw/lpc32xx_slc.c          |   3 +-
>  drivers/mtd/nand/raw/nand_base.c            | 109 +++++++++++---------
>  drivers/mtd/nand/raw/r852.c                 |   3 +-
>  include/linux/mtd/rawnand.h                 |   6 +-
>  include/linux/platform_data/mtd-davinci.h   |   1 +
>  10 files changed, 81 insertions(+), 58 deletions(-)
> 
> diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c
> index b9e9950dd300..4c8a592754ac 100644
> --- a/arch/arm/mach-davinci/board-dm355-leopard.c
> +++ b/arch/arm/mach-davinci/board-dm355-leopard.c
> @@ -76,7 +76,8 @@ static struct davinci_nand_pdata davinci_nand_data = {
>  	.mask_chipsel		= BIT(14),
>  	.parts			= davinci_nand_partitions,
>  	.nr_parts		= ARRAY_SIZE(davinci_nand_partitions),
> -	.ecc_mode		= NAND_ECC_HW_SYNDROME,
> +	.ecc_mode		= NAND_HW_ECC_ENGINE,
> +	.ecc_placement		= NAND_ECC_PLACEMENT_INTERLEAVED,
>  	.ecc_bits		= 4,
>  	.bbt_options		= NAND_BBT_USE_FLASH,
>  };
> diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c
> index 92173790f20b..2bf8ab542e38 100644
> --- a/drivers/mtd/nand/raw/cafe_nand.c
> +++ b/drivers/mtd/nand/raw/cafe_nand.c
> @@ -629,7 +629,8 @@ static int cafe_nand_attach_chip(struct nand_chip *chip)
>  		goto out_free_dma;
>  	}
>  
> -	cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
> +	cafe->nand.ecc.mode = NAND_ECC_HW;
> +	cafe->nand.ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED;
>  	cafe->nand.ecc.size = mtd->writesize;
>  	cafe->nand.ecc.bytes = 14;
>  	cafe->nand.ecc.strength = 4;
> diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
> index d975a62caaa5..2e5d6c113b56 100644
> --- a/drivers/mtd/nand/raw/davinci_nand.c
> +++ b/drivers/mtd/nand/raw/davinci_nand.c
> @@ -168,7 +168,7 @@ static int nand_davinci_correct_1bit(struct nand_chip *chip, u_char *dat,
>  /*
>   * 4-bit hardware ECC ... context maintained over entire AEMIF
>   *
> - * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
> + * This is a syndrome engine, but we avoid NAND_ECC_PLACEMENT_INTERLEAVED
>   * since that forces use of a problematic "infix OOB" layout.
>   * Among other things, it trashes manufacturer bad block markers.
>   * Also, and specific to this hardware, it ECC-protects the "prepad"
> @@ -851,6 +851,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
>  
>  	/* Use board-specific ECC config */
>  	info->chip.ecc.mode	= pdata->ecc_mode;
> +	info->chip.ecc.placement = pdata->ecc_placement;
>  
>  	spin_lock_irq(&davinci_nand_lock);
>  
> @@ -897,7 +898,7 @@ static int nand_davinci_remove(struct platform_device *pdev)
>  	int ret;
>  
>  	spin_lock_irq(&davinci_nand_lock);
> -	if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
> +	if (info->chip.ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED)
>  		ecc4_busy = false;
>  	spin_unlock_irq(&davinci_nand_lock);
>  
> diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
> index 4e6e1578aa2d..514a97ea4450 100644
> --- a/drivers/mtd/nand/raw/denali.c
> +++ b/drivers/mtd/nand/raw/denali.c
> @@ -1237,7 +1237,8 @@ int denali_chip_init(struct denali_controller *denali,
>  	chip->bbt_options |= NAND_BBT_USE_FLASH;
>  	chip->bbt_options |= NAND_BBT_NO_OOB;
>  	chip->options |= NAND_NO_SUBPAGE_WRITE;
> -	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED;
>  	chip->ecc.read_page = denali_read_page;
>  	chip->ecc.write_page = denali_write_page;
>  	chip->ecc.read_page_raw = denali_read_page_raw;
> diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c
> index 43721863a0d8..40360352136b 100644
> --- a/drivers/mtd/nand/raw/diskonchip.c
> +++ b/drivers/mtd/nand/raw/diskonchip.c
> @@ -1456,7 +1456,8 @@ static int __init doc_probe(unsigned long physadr)
>  	nand->ecc.calculate	= doc200x_calculate_ecc;
>  	nand->ecc.correct	= doc200x_correct_data;
>  
> -	nand->ecc.mode		= NAND_ECC_HW_SYNDROME;
> +	nand->ecc.mode		= NAND_ECC_HW;
> +	nand->ecc.placement	= NAND_ECC_PLACEMENT_INTERLEAVED;
>  	nand->ecc.size		= 512;
>  	nand->ecc.bytes		= 6;
>  	nand->ecc.strength	= 2;
> diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
> index b151fd000815..ccb189c8e343 100644
> --- a/drivers/mtd/nand/raw/lpc32xx_slc.c
> +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
> @@ -881,7 +881,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
>  	platform_set_drvdata(pdev, host);
>  
>  	/* NAND callbacks for LPC32xx SLC hardware */
> -	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED;
>  	chip->legacy.read_byte = lpc32xx_nand_read_byte;
>  	chip->legacy.read_buf = lpc32xx_nand_read_buf;
>  	chip->legacy.write_buf = lpc32xx_nand_write_buf;
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 4d2d444f9db9..9fbd2a474b62 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -5772,61 +5772,74 @@ static int nand_scan_tail(struct nand_chip *chip)
>  
>  	switch (ecc->mode) {
>  	case NAND_ECC_HW:
> -		/* Use standard hwecc read page function? */
> -		if (!ecc->read_page)
> -			ecc->read_page = nand_read_page_hwecc;
> -		if (!ecc->write_page)
> -			ecc->write_page = nand_write_page_hwecc;
> -		if (!ecc->read_page_raw)
> -			ecc->read_page_raw = nand_read_page_raw;
> -		if (!ecc->write_page_raw)
> -			ecc->write_page_raw = nand_write_page_raw;
> -		if (!ecc->read_oob)
> -			ecc->read_oob = nand_read_oob_std;
> -		if (!ecc->write_oob)
> -			ecc->write_oob = nand_write_oob_std;
> -		if (!ecc->read_subpage)
> -			ecc->read_subpage = nand_read_subpage;
> -		if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
> -			ecc->write_subpage = nand_write_subpage_hwecc;
> -		fallthrough;
> -	case NAND_ECC_HW_SYNDROME:
> -		if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
> -		    (!ecc->read_page ||
> -		     ecc->read_page == nand_read_page_hwecc ||
> -		     !ecc->write_page ||
> -		     ecc->write_page == nand_write_page_hwecc)) {
> -			WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
> -			ret = -EINVAL;
> -			goto err_nand_manuf_cleanup;
> -		}
> -		/* Use standard syndrome read/write page function? */
> -		if (!ecc->read_page)
> -			ecc->read_page = nand_read_page_syndrome;
> -		if (!ecc->write_page)
> -			ecc->write_page = nand_write_page_syndrome;
> -		if (!ecc->read_page_raw)
> -			ecc->read_page_raw = nand_read_page_raw_syndrome;
> -		if (!ecc->write_page_raw)
> -			ecc->write_page_raw = nand_write_page_raw_syndrome;
> -		if (!ecc->read_oob)
> -			ecc->read_oob = nand_read_oob_syndrome;
> -		if (!ecc->write_oob)
> -			ecc->write_oob = nand_write_oob_syndrome;
> +		switch (ecc->placement) {
> +		case NAND_ECC_PLACEMENT_UNKNOWN:
> +		case NAND_ECC_PLACEMENT_OOB:
> +			/* Use standard hwecc read page function? */
> +			if (!ecc->read_page)
> +				ecc->read_page = nand_read_page_hwecc;
> +			if (!ecc->write_page)
> +				ecc->write_page = nand_write_page_hwecc;
> +			if (!ecc->read_page_raw)
> +				ecc->read_page_raw = nand_read_page_raw;
> +			if (!ecc->write_page_raw)
> +				ecc->write_page_raw = nand_write_page_raw;
> +			if (!ecc->read_oob)
> +				ecc->read_oob = nand_read_oob_std;
> +			if (!ecc->write_oob)
> +				ecc->write_oob = nand_write_oob_std;
> +			if (!ecc->read_subpage)
> +				ecc->read_subpage = nand_read_subpage;
> +			if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
> +				ecc->write_subpage = nand_write_subpage_hwecc;
> +			fallthrough;
>  
> -		if (mtd->writesize >= ecc->size) {
> -			if (!ecc->strength) {
> -				WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
> +		case NAND_ECC_PLACEMENT_INTERLEAVED:
> +			if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
> +			    (!ecc->read_page ||
> +			     ecc->read_page == nand_read_page_hwecc ||
> +			     !ecc->write_page ||
> +			     ecc->write_page == nand_write_page_hwecc)) {
> +				WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
>  				ret = -EINVAL;
>  				goto err_nand_manuf_cleanup;
>  			}
> +			/* Use standard syndrome read/write page function? */
> +			if (!ecc->read_page)
> +				ecc->read_page = nand_read_page_syndrome;
> +			if (!ecc->write_page)
> +				ecc->write_page = nand_write_page_syndrome;
> +			if (!ecc->read_page_raw)
> +				ecc->read_page_raw = nand_read_page_raw_syndrome;
> +			if (!ecc->write_page_raw)
> +				ecc->write_page_raw = nand_write_page_raw_syndrome;
> +			if (!ecc->read_oob)
> +				ecc->read_oob = nand_read_oob_syndrome;
> +			if (!ecc->write_oob)
> +				ecc->write_oob = nand_write_oob_syndrome;
> +
> +			if (mtd->writesize >= ecc->size) {
> +				if (!ecc->strength) {
> +					WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
> +					ret = -EINVAL;
> +					goto err_nand_manuf_cleanup;
> +				}
> +				break;
> +			}
> +			pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
> +				ecc->size, mtd->writesize);
> +			ecc->mode = NAND_ECC_SOFT;
> +			ecc->algo = NAND_ECC_HAMMING;
>  			break;
> +
> +		default:
> +			pr_warn("Invalid NAND_ECC_PLACEMENT %d\n",
> +				ecc->placement);
> +			ret = -EINVAL;
> +			goto err_nand_manuf_cleanup;
>  		}
> -		pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
> -			ecc->size, mtd->writesize);
> -		ecc->mode = NAND_ECC_SOFT;
> -		ecc->algo = NAND_ECC_HAMMING;
>  		fallthrough;
> +
>  	case NAND_ECC_SOFT:
>  		ret = nand_set_ecc_soft_ops(chip);
>  		if (ret) {
> diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
> index f865e3a47b01..f0988cda4479 100644
> --- a/drivers/mtd/nand/raw/r852.c
> +++ b/drivers/mtd/nand/raw/r852.c
> @@ -859,7 +859,8 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
>  	chip->legacy.write_buf = r852_write_buf;
>  
>  	/* ecc */
> -	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED;
>  	chip->ecc.size = R852_DMA_LEN;
>  	chip->ecc.bytes = SM_OOB_SIZE;
>  	chip->ecc.strength = 2;
> diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> index 5e014807e050..f6ffd174abb7 100644
> --- a/include/linux/mtd/rawnand.h
> +++ b/include/linux/mtd/rawnand.h
> @@ -325,6 +325,7 @@ static const struct nand_ecc_caps __name = {			\
>  /**
>   * struct nand_ecc_ctrl - Control structure for ECC
>   * @mode:	ECC mode
> + * @placement:	OOB bytes placement
>   * @algo:	ECC algorithm
>   * @steps:	number of ECC steps per page
>   * @size:	data bytes per ECC step
> @@ -352,7 +353,7 @@ static const struct nand_ecc_caps __name = {			\
>   *			controller and always return contiguous in-band and
>   *			out-of-band data even if they're not stored
>   *			contiguously on the NAND chip (e.g.
> - *			NAND_ECC_HW_SYNDROME interleaves in-band and
> + *			NAND_ECC_PLACEMENT_INTERLEAVED interleaves in-band and
>   *			out-of-band data).
>   * @write_page_raw:	function to write a raw page without ECC. This function
>   *			should hide the specific layout used by the ECC
> @@ -360,7 +361,7 @@ static const struct nand_ecc_caps __name = {			\
>   *			in-band and out-of-band data. ECC controller is
>   *			responsible for doing the appropriate transformations
>   *			to adapt to its specific layout (e.g.
> - *			NAND_ECC_HW_SYNDROME interleaves in-band and
> + *			NAND_ECC_PLACEMENT_INTERLEAVED interleaves in-band and
>   *			out-of-band data).
>   * @read_page:	function to read a page according to the ECC generator
>   *		requirements; returns maximum number of bitflips corrected in
> @@ -377,6 +378,7 @@ static const struct nand_ecc_caps __name = {			\
>   */
>  struct nand_ecc_ctrl {
>  	enum nand_ecc_mode mode;
> +	enum nand_ecc_placement placement;
>  	enum nand_ecc_algo algo;
>  	int steps;
>  	int size;
> diff --git a/include/linux/platform_data/mtd-davinci.h b/include/linux/platform_data/mtd-davinci.h
> index 03e92c71b3fa..3383101c233b 100644
> --- a/include/linux/platform_data/mtd-davinci.h
> +++ b/include/linux/platform_data/mtd-davinci.h
> @@ -69,6 +69,7 @@ struct davinci_nand_pdata {		/* platform_data */
>  	 * using it with large page chips.
>  	 */
>  	enum nand_ecc_mode	ecc_mode;
> +	enum nand_ecc_placement	ecc_placement;
>  	u8			ecc_bits;
>  
>  	/* e.g. NAND_BUSWIDTH_16 */


^ permalink raw reply

* Re: [V6 PATCH 1/2] dt-bindings: Added device tree binding for max98390
From: Rob Herring @ 2020-05-28 14:15 UTC (permalink / raw)
  To: Steve Lee
  Cc: robh+dt, ryans.lee, broonie, alsa-devel, ryan.lee.maxim,
	devicetree, lgirdwood, steves.lee.maxim, linux-kernel
In-Reply-To: <20200528103755.17381-1-steves.lee@maximintegrated.com>

On Thu, 28 May 2020 19:37:55 +0900, Steve Lee wrote:
> Add DT binding of max98390 amplifier driver.
> 
> Signed-off-by: Steve Lee <steves.lee@maximintegrated.com>
> ---
> Changed since V5:
> 	* Change txt to yaml and fix up the examples.
> Changed since V4:
> 	* No changes.
> Changed since V3:
> 	* No changes.
> Changed since V2:
> 	* No changes.
> Changed since V1:
> 	* Modified sample text in example
> 
>  .../bindings/sound/maxim,max98390.yaml        | 39 +++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/maxim,max98390.yaml
> 


My bot found errors running 'make dt_binding_check' on your patch:

Documentation/devicetree/bindings/sound/maxim,max98390.yaml:  mapping values are not allowed in this context
  in "<unicode string>", line 22, column 97
Documentation/devicetree/bindings/Makefile:12: recipe for target 'Documentation/devicetree/bindings/sound/maxim,max98390.example.dts' failed
make[1]: *** [Documentation/devicetree/bindings/sound/maxim,max98390.example.dts] Error 1
make[1]: *** Waiting for unfinished jobs....
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/sound/maxim,max98390.yaml: ignoring, error parsing file
warning: no schema found in file: ./Documentation/devicetree/bindings/sound/maxim,max98390.yaml
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/sound/maxim,max98390.yaml: ignoring, error parsing file
warning: no schema found in file: ./Documentation/devicetree/bindings/sound/maxim,max98390.yaml
Makefile:1300: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1299651

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.


^ permalink raw reply

* Re: [PATCH v11 1/4] power_supply: Add additional health properties to the header
From: Andrew F. Davis @ 2020-05-28 14:16 UTC (permalink / raw)
  To: Ricardo Rivera-Matos, sre, pali, robh
  Cc: dmurphy, linux-pm, linux-kernel, devicetree, sspatil,
	Guru Das Srinagesh
In-Reply-To: <20200528140546.25260-2-r-rivera-matos@ti.com>

On 5/28/20 10:05 AM, Ricardo Rivera-Matos wrote:
> From: Dan Murphy <dmurphy@ti.com>
> 
> Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum.
> 
> HEALTH_WARM, HEALTH_COOL, and HEALTH_HOT properties are taken from the JEITA spec.


Wouldn't hurt to list the specific version of the spec these are from,
but not super important,

Acked-by: Andrew F. Davis <afd@ti.com>


> 
> Tested-by: Guru Das Srinagesh <gurus@codeaurora.org>
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
>  Documentation/ABI/testing/sysfs-class-power | 2 +-
>  drivers/power/supply/power_supply_sysfs.c   | 2 +-
>  include/linux/power_supply.h                | 3 +++
>  3 files changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
> index bf3b48f022dc..9f3fd01a9373 100644
> --- a/Documentation/ABI/testing/sysfs-class-power
> +++ b/Documentation/ABI/testing/sysfs-class-power
> @@ -190,7 +190,7 @@ Description:
>  		Valid values: "Unknown", "Good", "Overheat", "Dead",
>  			      "Over voltage", "Unspecified failure", "Cold",
>  			      "Watchdog timer expire", "Safety timer expire",
> -			      "Over current"
> +			      "Over current", "Warm", "Cool", "Hot"
>  
>  What:		/sys/class/power_supply/<supply_name>/precharge_current
>  Date:		June 2017
> diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
> index f37ad4eae60b..d0d549611794 100644
> --- a/drivers/power/supply/power_supply_sysfs.c
> +++ b/drivers/power/supply/power_supply_sysfs.c
> @@ -61,7 +61,7 @@ static const char * const power_supply_charge_type_text[] = {
>  static const char * const power_supply_health_text[] = {
>  	"Unknown", "Good", "Overheat", "Dead", "Over voltage",
>  	"Unspecified failure", "Cold", "Watchdog timer expire",
> -	"Safety timer expire", "Over current"
> +	"Safety timer expire", "Over current", "Warm", "Cool", "Hot"
>  };
>  
>  static const char * const power_supply_technology_text[] = {
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index dcd5a71e6c67..8670e90c1d51 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -61,6 +61,9 @@ enum {
>  	POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
>  	POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
>  	POWER_SUPPLY_HEALTH_OVERCURRENT,
> +	POWER_SUPPLY_HEALTH_WARM,
> +	POWER_SUPPLY_HEALTH_COOL,
> +	POWER_SUPPLY_HEALTH_HOT,
>  };
>  
>  enum {
> 

^ permalink raw reply

* Re: [V6 PATCH 1/2] dt-bindings: Added device tree binding for max98390
From: Rob Herring @ 2020-05-28 14:17 UTC (permalink / raw)
  To: Steve Lee
  Cc: lgirdwood, broonie, alsa-devel, devicetree, linux-kernel,
	ryan.lee.maxim, ryans.lee, steves.lee.maxim
In-Reply-To: <20200528103755.17381-1-steves.lee@maximintegrated.com>

On Thu, May 28, 2020 at 07:37:55PM +0900, Steve Lee wrote:
> Add DT binding of max98390 amplifier driver.
> 
> Signed-off-by: Steve Lee <steves.lee@maximintegrated.com>
> ---
> Changed since V5:
> 	* Change txt to yaml and fix up the examples.
> Changed since V4:
> 	* No changes.
> Changed since V3:
> 	* No changes.
> Changed since V2:
> 	* No changes.
> Changed since V1:
> 	* Modified sample text in example
> 
>  .../bindings/sound/maxim,max98390.yaml        | 39 +++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/maxim,max98390.yaml
> 
> diff --git a/Documentation/devicetree/bindings/sound/maxim,max98390.yaml b/Documentation/devicetree/bindings/sound/maxim,max98390.yaml
> new file mode 100644
> index 000000000000..1ed4ab9e1c37
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/maxim,max98390.yaml
> @@ -0,0 +1,39 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/maxim,max98390.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim Integrated MAX98390 Speaker Amplifier with Integrated Dynamic Speaker Management
> +
> +maintainers:
> +  - Steve Lee <steves.lee@maximintegrated.com>
> +
> +properties:
> +  compatible:
> +      const: maxim,max98390
> +
> +  reg:
> +    maxItems: 1
> +    description: I2C address of the device.
> +
> +  temperature_calib:

s/_/-/

And missing 'maxim' prefix.

> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    description: The calculated temperature data was measured while doing the calibration. Data : Temp / 100 * 2^12

Any constraints? 0-2^32 are valid values?

> +
> +  r0_calib:

Same here.

> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    description: This is r0 calibration data which was measured in factory mode.
> +
> +required:
> +  - compatible
> +  - reg

Add:

additionalProperties: false

> +
> +examples:
> +  - |
> +    max98390: amplifier@38 {
> +            compatible = "maxim,max98390";
> +            reg = <0x38>;
> +            maxim,temperature_calib = <1024>;
> +            maxim,r0_calib = <100232>;
> +    };
> -- 
> 2.17.1
> 

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: sound: tlv320adcx140: Add GPI config property
From: Dan Murphy @ 2020-05-28 14:20 UTC (permalink / raw)
  To: Rob Herring
  Cc: lgirdwood, broonie, perex, tiwai, devicetree, alsa-devel,
	linux-kernel
In-Reply-To: <20200528140525.GA4166160@bogus>

Rob

On 5/28/20 9:05 AM, Rob Herring wrote:
> On Tue, May 26, 2020 at 03:09:16PM -0500, Dan Murphy wrote:
>> Add an array property that configures the General Purpose Input (GPI)
>> register.  The device has 4 GPI pins and each pin can be configured in 1
>> of 7 different ways.
> Dan seems to have trouble running get_maintainers.pl and Cc'ing the DT
> list. Running 'make dt_binding_check' also seems to be a problem. Now
> linux-next has these warnings:

I don't have an issue with doing get_maintainers.  All the maintainers 
listed were added to the patch.

And devicetree@vger.kernel.org was cc'd.

I will fix this warning.

Dan



^ permalink raw reply

* Re: [PATCH v3 03/10] dmaengine: Introduce min burst length capability
From: Andy Shevchenko @ 2020-05-28 14:21 UTC (permalink / raw)
  To: Serge Semin
  Cc: Vinod Koul, Viresh Kumar, Dan Williams, Serge Semin,
	Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, Rob Herring,
	linux-mips, devicetree, dmaengine, linux-kernel
In-Reply-To: <20200526225022.20405-4-Sergey.Semin@baikalelectronics.ru>

On Wed, May 27, 2020 at 01:50:14AM +0300, Serge Semin wrote:
> Some hardware aside from default 0/1 may have greater minimum burst
> transactions length constraints. Here we introduce the DMA device
> and slave capability, which if required can be initialized by the DMA
> engine driver with the device-specific value.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: linux-mips@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> 
> ---
> 
> Changelog v3:
> - This is a new patch created as a result of the discussion with Vinud and
>   Andy in the framework of DW DMA burst and LLP capabilities.
> ---
>  drivers/dma/dmaengine.c   | 1 +
>  include/linux/dmaengine.h | 4 ++++
>  2 files changed, 5 insertions(+)
> 
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index d31076d9ef25..b332ffe52780 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -590,6 +590,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
>  	caps->src_addr_widths = device->src_addr_widths;
>  	caps->dst_addr_widths = device->dst_addr_widths;
>  	caps->directions = device->directions;
> +	caps->min_burst = device->min_burst;
>  	caps->max_burst = device->max_burst;
>  	caps->residue_granularity = device->residue_granularity;
>  	caps->descriptor_reuse = device->descriptor_reuse;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index e1c03339918f..0c7403b27133 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -465,6 +465,7 @@ enum dma_residue_granularity {
>   *	Since the enum dma_transfer_direction is not defined as bit flag for
>   *	each type, the dma controller should set BIT(<TYPE>) and same
>   *	should be checked by controller as well
> + * @min_burst: min burst capability per-transfer
>   * @max_burst: max burst capability per-transfer
>   * @cmd_pause: true, if pause is supported (i.e. for reading residue or
>   *	       for resume later)
> @@ -478,6 +479,7 @@ struct dma_slave_caps {
>  	u32 src_addr_widths;
>  	u32 dst_addr_widths;
>  	u32 directions;
> +	u32 min_burst;
>  	u32 max_burst;
>  	bool cmd_pause;
>  	bool cmd_resume;
> @@ -769,6 +771,7 @@ struct dma_filter {
>   *	Since the enum dma_transfer_direction is not defined as bit flag for
>   *	each type, the dma controller should set BIT(<TYPE>) and same
>   *	should be checked by controller as well
> + * @min_burst: min burst capability per-transfer
>   * @max_burst: max burst capability per-transfer
>   * @residue_granularity: granularity of the transfer residue reported
>   *	by tx_status
> @@ -839,6 +842,7 @@ struct dma_device {
>  	u32 src_addr_widths;
>  	u32 dst_addr_widths;
>  	u32 directions;
> +	u32 min_burst;
>  	u32 max_burst;
>  	bool descriptor_reuse;
>  	enum dma_residue_granularity residue_granularity;
> -- 
> 2.26.2
> 

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 0/8] R8A7742 add support for HSUSB and USB2.0/3.0
From: Lorenzo Pieralisi @ 2020-05-28 14:21 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Geert Uytterhoeven, Vinod Koul, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Greg Kroah-Hartman, Magnus Damm,
	dmaengine, linux-pci, linux-usb, linux-renesas-soc, devicetree,
	linux-kernel, Prabhakar
In-Reply-To: <1590356277-19993-1-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com>

On Sun, May 24, 2020 at 10:37:49PM +0100, Lad Prabhakar wrote:
> Hi All,
> 
> This patch series adds support for HSUSB, USB2.0 and USB3.0 to
> R8A7742 SoC DT.
> 
> This patch series applies on-top of [1].
> 
> [1] https://patchwork.kernel.org/project/linux-renesas-soc/list/?series=288491

I think Geert will pull this series, so I'd drop it from the PCI
patchwork unless there is a reason I should not, please let me know.

Thanks,
Lorenzo

> Cheers,
> Prabhakar
> 
> Lad Prabhakar (8):
>   dt-bindings: phy: rcar-gen2: Add r8a7742 support
>   dt-bindings: PCI: pci-rcar-gen2: Add device tree support for r8a7742
>   dt-bindings: usb: renesas,usbhs: Add support for r8a7742
>   dt-bindings: dmaengine: renesas,usb-dmac: Add binding for r8a7742
>   dt-bindings: usb: usb-xhci: Document r8a7742 support
>   ARM: dts: r8a7742: Add USB 2.0 host support
>   ARM: dts: r8a7742: Add USB-DMAC and HSUSB device nodes
>   ARM: dts: r8a7742: Add xhci support
> 
>  .../devicetree/bindings/dma/renesas,usb-dmac.yaml  |   1 +
>  .../devicetree/bindings/pci/pci-rcar-gen2.txt      |   3 +-
>  .../devicetree/bindings/phy/rcar-gen2-phy.txt      |   3 +-
>  .../devicetree/bindings/usb/renesas,usbhs.yaml     |   1 +
>  Documentation/devicetree/bindings/usb/usb-xhci.txt |   1 +
>  arch/arm/boot/dts/r8a7742.dtsi                     | 173 +++++++++++++++++++++
>  6 files changed, 180 insertions(+), 2 deletions(-)
> 
> -- 
> 2.7.4
> 

^ permalink raw reply

* [PATCH] clk: qcom: Add missing msm8998 ufs_unipro_core_clk_src
From: Jeffrey Hugo @ 2020-05-28 14:22 UTC (permalink / raw)
  To: sboyd, robh+dt, mturquette
  Cc: agross, bjorn.andersson, linux-arm-msm, linux-clk, linux-kernel,
	devicetree, Jeffrey Hugo

ufs_unipro_core_clk_src is required to allow UFS to clock scale for power
savings.

Fixes: b5f5f525c547 ("clk: qcom: Add MSM8998 Global Clock Control (GCC) driver")
Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 drivers/clk/qcom/gcc-msm8998.c               | 27 ++++++++++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8998.h |  1 +
 2 files changed, 28 insertions(+)

diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c
index df1d7056436c..9d7016bcd680 100644
--- a/drivers/clk/qcom/gcc-msm8998.c
+++ b/drivers/clk/qcom/gcc-msm8998.c
@@ -1110,6 +1110,27 @@ static struct clk_rcg2 ufs_axi_clk_src = {
 	},
 };
 
+static const struct freq_tbl ftbl_ufs_unipro_core_clk_src[] = {
+	F(37500000, P_GPLL0_OUT_MAIN, 16, 0, 0),
+	F(75000000, P_GPLL0_OUT_MAIN, 8, 0, 0),
+	F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 ufs_unipro_core_clk_src = {
+	.cmd_rcgr = 0x76028,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_ufs_unipro_core_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "ufs_unipro_core_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.ops = &clk_rcg2_ops,
+	},
+};
+
 static const struct freq_tbl ftbl_usb30_master_clk_src[] = {
 	F(19200000, P_XO, 1, 0, 0),
 	F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
@@ -2549,6 +2570,11 @@ static struct clk_branch gcc_ufs_unipro_core_clk = {
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_ufs_unipro_core_clk",
+			.parent_names = (const char *[]){
+				"ufs_unipro_core_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -2904,6 +2930,7 @@ static struct clk_regmap *gcc_msm8998_clocks[] = {
 	[SDCC4_APPS_CLK_SRC] = &sdcc4_apps_clk_src.clkr,
 	[TSIF_REF_CLK_SRC] = &tsif_ref_clk_src.clkr,
 	[UFS_AXI_CLK_SRC] = &ufs_axi_clk_src.clkr,
+	[UFS_UNIPRO_CORE_CLK_SRC] = &ufs_unipro_core_clk_src.clkr,
 	[USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr,
 	[USB30_MOCK_UTMI_CLK_SRC] = &usb30_mock_utmi_clk_src.clkr,
 	[USB3_PHY_AUX_CLK_SRC] = &usb3_phy_aux_clk_src.clkr,
diff --git a/include/dt-bindings/clock/qcom,gcc-msm8998.h b/include/dt-bindings/clock/qcom,gcc-msm8998.h
index 63e02dc32a0b..6a73a174f049 100644
--- a/include/dt-bindings/clock/qcom,gcc-msm8998.h
+++ b/include/dt-bindings/clock/qcom,gcc-msm8998.h
@@ -183,6 +183,7 @@
 #define GCC_MSS_SNOC_AXI_CLK					174
 #define GCC_MSS_MNOC_BIMC_AXI_CLK				175
 #define GCC_BIMC_GFX_CLK					176
+#define UFS_UNIPRO_CORE_CLK_SRC					177
 
 #define PCIE_0_GDSC						0
 #define UFS_GDSC						1
-- 
2.17.1


^ permalink raw reply related

* Re: [PATCH v6 04/18] mtd: rawnand: Create a helper to retrieve the ECC placement
From: Boris Brezillon @ 2020-05-28 14:22 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
	Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
	Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
	Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-5-miquel.raynal@bootlin.com>

On Thu, 28 May 2020 13:30:59 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Use it from nand_dt_init() to initialize the ECC structure.
> 
> This allows the deprecation of the hw_syndrome ECC mode.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/mtd/nand/raw/nand_base.c | 29 +++++++++++++++++++++++++++++
>  1 file changed, 29 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 9fbd2a474b62..fd0bfe9bf7ae 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -5047,6 +5047,34 @@ static int of_get_nand_ecc_mode(struct device_node *np)
>  	return -ENODEV;
>  }
>  
> +enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np)
> +{
> +	enum nand_ecc_placement placement;
> +	const char *pm;
> +	int err;
> +
> +	err = of_property_read_string(np, "nand-ecc-placement", &pm);
> +	if (!err) {
> +		for (placement = NAND_ECC_PLACEMENT_INTERLEAVED;
> +		     placement < ARRAY_SIZE(nand_ecc_placement); placement++) {
> +			if (!strcasecmp(pm, nand_ecc_placement[placement]))
> +				return placement;
> +		}
> +	}
> +
> +	/*
> +	 * For backward compatibility we support few obsoleted values that don't
> +	 * have their mappings into the nand_ecc_placement enum anymore.
> +	 */
> +	err = of_property_read_string(np, "nand-ecc-mode", &pm);
> +	if (!err) {
> +		if (!strcasecmp(pm, "hw_syndrome"))
> +			return NAND_ECC_PLACEMENT_INTERLEAVED;
> +	}
> +
> +	return NAND_ECC_PLACEMENT_UNKNOWN;
> +}
> +
>  static const char * const nand_ecc_algos[] = {
>  	[NAND_ECC_HAMMING]	= "hamming",
>  	[NAND_ECC_BCH]		= "bch",
> @@ -5143,6 +5171,7 @@ static int nand_dt_init(struct nand_chip *chip)
>  
>  	ecc_mode = of_get_nand_ecc_mode(dn);
>  	ecc_algo = of_get_nand_ecc_algo(dn);
> +	chip->ecc.placement = of_get_nand_ecc_placement(dn);
>  	ecc_strength = of_get_nand_ecc_strength(dn);
>  	ecc_step = of_get_nand_ecc_step_size(dn);
>  


^ permalink raw reply

* Re: [PATCH v3 04/10] dmaengine: Introduce max SG list entries capability
From: Andy Shevchenko @ 2020-05-28 14:22 UTC (permalink / raw)
  To: Serge Semin
  Cc: Vinod Koul, Viresh Kumar, Dan Williams, Serge Semin,
	Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, Rob Herring,
	linux-mips, devicetree, dmaengine, linux-kernel
In-Reply-To: <20200526225022.20405-5-Sergey.Semin@baikalelectronics.ru>

On Wed, May 27, 2020 at 01:50:15AM +0300, Serge Semin wrote:
> Some devices may lack the support of the hardware accelerated SG list
> entries automatic walking through and execution. In this case a burden of
> the SG list traversal and DMA engine re-initialization lies on the
> DMA engine driver (normally implemented by using a DMA transfer completion
> IRQ to recharge the DMA device with a next SG list entry). But such
> solution may not be suitable for some DMA consumers. In particular SPI
> devices need both Tx and Rx DMA channels work synchronously in order
> to avoid the Rx FIFO overflow. In case if Rx DMA channel is paused for
> some time while the Tx DMA channel works implicitly pulling data into the
> Rx FIFO, the later will be eventually overflown, which will cause the data
> loss. So if SG list entries aren't automatically fetched by the DMA
> engine, but are one-by-one manually selected for execution in the
> ISRs/deferred work/etc., such problem will eventually happen due to the
> non-deterministic latencies of the service execution.
> 
> In order to let the DMA consumer know about the DMA device capabilities
> regarding the hardware accelerated SG list traversal we introduce the
> max_sg_list capability. It is supposed to be initialized by the DMA engine
> driver with 0 if there is no limitation for the number of SG entries
> atomically executed and with non-zero value if there is such constraints,
> so the upper limit is determined by the number set to the property.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

But see below.

> Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: linux-mips@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> 
> ---
> 
> Changelog v3:
> - This is a new patch created as a result of the discussion with Vinud and
>   Andy in the framework of DW DMA burst and LLP capabilities.
> ---
>  drivers/dma/dmaengine.c   | 1 +
>  include/linux/dmaengine.h | 8 ++++++++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index b332ffe52780..ad56ad58932c 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -592,6 +592,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
>  	caps->directions = device->directions;
>  	caps->min_burst = device->min_burst;
>  	caps->max_burst = device->max_burst;
> +	caps->max_sg_nents = device->max_sg_nents;
>  	caps->residue_granularity = device->residue_granularity;
>  	caps->descriptor_reuse = device->descriptor_reuse;
>  	caps->cmd_pause = !!device->device_pause;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 0c7403b27133..6801200c76b6 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -467,6 +467,9 @@ enum dma_residue_granularity {
>   *	should be checked by controller as well
>   * @min_burst: min burst capability per-transfer
>   * @max_burst: max burst capability per-transfer
> + * @max_sg_nents: max number of SG list entries executed in a single atomic
> + *	DMA tansaction with no intermediate IRQ for reinitialization. Zero
> + *	value means unlimited number if entries.

if -> of ?

>   * @cmd_pause: true, if pause is supported (i.e. for reading residue or
>   *	       for resume later)
>   * @cmd_resume: true, if resume is supported
> @@ -481,6 +484,7 @@ struct dma_slave_caps {
>  	u32 directions;
>  	u32 min_burst;
>  	u32 max_burst;
> +	u32 max_sg_nents;
>  	bool cmd_pause;
>  	bool cmd_resume;
>  	bool cmd_terminate;
> @@ -773,6 +777,9 @@ struct dma_filter {
>   *	should be checked by controller as well
>   * @min_burst: min burst capability per-transfer
>   * @max_burst: max burst capability per-transfer
> + * @max_sg_nents: max number of SG list entries executed in a single atomic
> + *	DMA tansaction with no intermediate IRQ for reinitialization. Zero
> + *	value means unlimited number if entries.

Ditto.

>   * @residue_granularity: granularity of the transfer residue reported
>   *	by tx_status
>   * @device_alloc_chan_resources: allocate resources and return the
> @@ -844,6 +851,7 @@ struct dma_device {
>  	u32 directions;
>  	u32 min_burst;
>  	u32 max_burst;
> +	u32 max_sg_nents;
>  	bool descriptor_reuse;
>  	enum dma_residue_granularity residue_granularity;
>  
> -- 
> 2.26.2
> 

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v6 05/18] mtd: rawnand: Add a kernel doc to the ECC algorithm enumeration
From: Boris Brezillon @ 2020-05-28 14:26 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
	Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
	Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
	Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-6-miquel.raynal@bootlin.com>

On Thu, 28 May 2020 13:31:00 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Before moving it to the generic raw NAND core, ensure the enumeration
> is properly described.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  include/linux/mtd/rawnand.h | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> index f6ffd174abb7..6699ec7f4d40 100644
> --- a/include/linux/mtd/rawnand.h
> +++ b/include/linux/mtd/rawnand.h
> @@ -106,6 +106,13 @@ enum nand_ecc_placement {
>  	NAND_ECC_PLACEMENT_INTERLEAVED,
>  };
>  
> +/**
> + * enum nand_ecc_algo - NAND ECC algorithm
> + * @NAND_ECC_UNKNOWN: Unknown algorithm
> + * @NAND_ECC_HAMMING: Hamming algorithm
> + * @NAND_ECC_BCH: Bose-Chaudhuri-Hocquenghem algorithm
> + * @NAND_ECC_RS: Reed-Solomon algorithm
> + */
>  enum nand_ecc_algo {
>  	NAND_ECC_UNKNOWN,
>  	NAND_ECC_HAMMING,


^ permalink raw reply

* Re: [PATCH v6 06/18] mtd: rawnand: Rename the ECC algorithm enumeration items
From: Boris Brezillon @ 2020-05-28 14:26 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
	Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
	Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
	Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-7-miquel.raynal@bootlin.com>

On Thu, 28 May 2020 13:31:01 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> NAND_ECC_ is not a meaningful prefix, use NAND_ECC_ALGO_ instead.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/mtd/nand/raw/ams-delta.c              |  2 +-
>  drivers/mtd/nand/raw/arasan-nand-controller.c |  2 +-
>  drivers/mtd/nand/raw/atmel/nand-controller.c  |  2 +-
>  drivers/mtd/nand/raw/au1550nd.c               |  2 +-
>  drivers/mtd/nand/raw/brcmnand/brcmnand.c      | 12 ++++-----
>  drivers/mtd/nand/raw/davinci_nand.c           |  8 +++---
>  drivers/mtd/nand/raw/fsl_elbc_nand.c          |  2 +-
>  drivers/mtd/nand/raw/fsl_ifc_nand.c           |  2 +-
>  drivers/mtd/nand/raw/fsl_upm.c                |  2 +-
>  drivers/mtd/nand/raw/fsmc_nand.c              |  2 +-
>  drivers/mtd/nand/raw/gpio.c                   |  2 +-
>  drivers/mtd/nand/raw/marvell_nand.c           | 10 +++----
>  drivers/mtd/nand/raw/mpc5121_nfc.c            |  2 +-
>  drivers/mtd/nand/raw/mxc_nand.c               |  2 +-
>  drivers/mtd/nand/raw/nand_base.c              | 26 +++++++++----------
>  drivers/mtd/nand/raw/nand_micron.c            |  2 +-
>  drivers/mtd/nand/raw/nandsim.c                |  4 +--
>  drivers/mtd/nand/raw/omap2.c                  |  2 +-
>  drivers/mtd/nand/raw/orion_nand.c             |  2 +-
>  drivers/mtd/nand/raw/pasemi_nand.c            |  2 +-
>  drivers/mtd/nand/raw/plat_nand.c              |  2 +-
>  drivers/mtd/nand/raw/s3c2410.c                |  4 +--
>  drivers/mtd/nand/raw/sh_flctl.c               |  2 +-
>  drivers/mtd/nand/raw/socrates_nand.c          |  2 +-
>  drivers/mtd/nand/raw/tango_nand.c             |  2 +-
>  drivers/mtd/nand/raw/tegra_nand.c             | 20 +++++++-------
>  drivers/mtd/nand/raw/xway_nand.c              |  2 +-
>  include/linux/mtd/rawnand.h                   | 16 ++++++------
>  28 files changed, 70 insertions(+), 70 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 3711e7a0436c..72a44b2411c1 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -261,7 +261,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
>  	}
>  
>  	this->ecc.mode = NAND_ECC_SOFT;
> -	this->ecc.algo = NAND_ECC_HAMMING;
> +	this->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	platform_set_drvdata(pdev, priv);
>  
> diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
> index 7141dcccba3c..076736351bc6 100644
> --- a/drivers/mtd/nand/raw/arasan-nand-controller.c
> +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
> @@ -983,7 +983,7 @@ static int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc,
>  	mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
>  
>  	ecc->steps = mtd->writesize / ecc->size;
> -	ecc->algo = NAND_ECC_BCH;
> +	ecc->algo = NAND_ECC_ALGO_BCH;
>  	anand->ecc_bits = bch_gf_mag * ecc->strength;
>  	ecc->bytes = DIV_ROUND_UP(anand->ecc_bits, 8);
>  	anand->ecc_total = DIV_ROUND_UP(anand->ecc_bits * ecc->steps, 8);
> diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
> index 46a3724a788e..d9839461e460 100644
> --- a/drivers/mtd/nand/raw/atmel/nand-controller.c
> +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
> @@ -1099,7 +1099,7 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip)
>  	if (IS_ERR(nand->pmecc))
>  		return PTR_ERR(nand->pmecc);
>  
> -	chip->ecc.algo = NAND_ECC_BCH;
> +	chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  	chip->ecc.size = req.ecc.sectorsize;
>  	chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
>  	chip->ecc.strength = req.ecc.strength;
> diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c
> index f7b4f421b2b0..e9140bf5cbac 100644
> --- a/drivers/mtd/nand/raw/au1550nd.c
> +++ b/drivers/mtd/nand/raw/au1550nd.c
> @@ -295,7 +295,7 @@ static int au1550nd_probe(struct platform_device *pdev)
>  	ctx->controller.ops = &au1550nd_ops;
>  	this->controller = &ctx->controller;
>  	this->ecc.mode = NAND_ECC_SOFT;
> -	this->ecc.algo = NAND_ECC_HAMMING;
> +	this->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	if (pd->devwidth)
>  		this->options |= NAND_BUSWIDTH_16;
> diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
> index 4a0a7053fb7a..2a9f2ff89fe7 100644
> --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
> +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
> @@ -2545,17 +2545,17 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
>  		return -EINVAL;
>  	}
>  
> -	if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
> +	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) {
>  		if (chip->ecc.strength == 1 && chip->ecc.size == 512)
>  			/* Default to Hamming for 1-bit ECC, if unspecified */
> -			chip->ecc.algo = NAND_ECC_HAMMING;
> +			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		else
>  			/* Otherwise, BCH */
> -			chip->ecc.algo = NAND_ECC_BCH;
> +			chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  	}
>  
> -	if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
> -						   chip->ecc.size != 512)) {
> +	if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING &&
> +	    (chip->ecc.strength != 1 || chip->ecc.size != 512)) {
>  		dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
>  			chip->ecc.strength, chip->ecc.size);
>  		return -EINVAL;
> @@ -2574,7 +2574,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
>  
>  	switch (chip->ecc.size) {
>  	case 512:
> -		if (chip->ecc.algo == NAND_ECC_HAMMING)
> +		if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING)
>  			cfg->ecc_level = 15;
>  		else
>  			cfg->ecc_level = chip->ecc.strength;
> diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
> index 2e5d6c113b56..3640c7e45e15 100644
> --- a/drivers/mtd/nand/raw/davinci_nand.c
> +++ b/drivers/mtd/nand/raw/davinci_nand.c
> @@ -593,11 +593,11 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
>  		pdata->ecc_bits = 0;
>  		/*
>  		 * This driver expects Hamming based ECC when ecc_mode is set
> -		 * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
> +		 * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_ALGO_HAMMING to
>  		 * avoid adding an extra ->ecc_algo field to
>  		 * davinci_nand_pdata.
>  		 */
> -		info->chip.ecc.algo = NAND_ECC_HAMMING;
> +		info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		break;
>  	case NAND_ECC_HW:
>  		if (pdata->ecc_bits == 4) {
> @@ -629,7 +629,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
>  			info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
>  			info->chip.ecc.bytes = 10;
>  			info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
> -			info->chip.ecc.algo = NAND_ECC_BCH;
> +			info->chip.ecc.algo = NAND_ECC_ALGO_BCH;
>  
>  			/*
>  			 * Update ECC layout if needed ... for 1-bit HW ECC, the
> @@ -656,7 +656,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
>  			info->chip.ecc.correct = nand_davinci_correct_1bit;
>  			info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
>  			info->chip.ecc.bytes = 3;
> -			info->chip.ecc.algo = NAND_ECC_HAMMING;
> +			info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		}
>  		info->chip.ecc.size = 512;
>  		info->chip.ecc.strength = pdata->ecc_bits;
> diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
> index 088692b2e27a..da89389faaae 100644
> --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c
> +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
> @@ -748,7 +748,7 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
>  		} else {
>  			/* otherwise fall back to default software ECC */
>  			chip->ecc.mode = NAND_ECC_SOFT;
> -			chip->ecc.algo = NAND_ECC_HAMMING;
> +			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		}
>  		break;
>  
> diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> index 00ae7a910b03..b2ae759dd14e 100644
> --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
> +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> @@ -926,7 +926,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
>  		}
>  	} else {
>  		chip->ecc.mode = NAND_ECC_SOFT;
> -		chip->ecc.algo = NAND_ECC_HAMMING;
> +		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  	}
>  
>  	ret = fsl_ifc_sram_init(priv);
> diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
> index 627deb26db51..49592b7e03a3 100644
> --- a/drivers/mtd/nand/raw/fsl_upm.c
> +++ b/drivers/mtd/nand/raw/fsl_upm.c
> @@ -164,7 +164,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
>  	fun->chip.legacy.read_buf = fun_read_buf;
>  	fun->chip.legacy.write_buf = fun_write_buf;
>  	fun->chip.ecc.mode = NAND_ECC_SOFT;
> -	fun->chip.ecc.algo = NAND_ECC_HAMMING;
> +	fun->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
>  	if (fun->mchip_count > 1)
>  		fun->chip.legacy.select_chip = fun_select_chip;
>  
> diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
> index 3909752b14c5..ced570987e85 100644
> --- a/drivers/mtd/nand/raw/fsmc_nand.c
> +++ b/drivers/mtd/nand/raw/fsmc_nand.c
> @@ -911,7 +911,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
>  		break;
>  
>  	case NAND_ECC_SOFT:
> -		if (nand->ecc.algo == NAND_ECC_BCH) {
> +		if (nand->ecc.algo == NAND_ECC_ALGO_BCH) {
>  			dev_info(host->dev,
>  				 "Using 4-bit SW BCH ECC scheme\n");
>  			break;
> diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c
> index 938077e5c6a9..667807c7100b 100644
> --- a/drivers/mtd/nand/raw/gpio.c
> +++ b/drivers/mtd/nand/raw/gpio.c
> @@ -276,7 +276,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
>  	nand_set_flash_node(chip, pdev->dev.of_node);
>  	chip->legacy.IO_ADDR_W	= chip->legacy.IO_ADDR_R;
>  	chip->ecc.mode		= NAND_ECC_SOFT;
> -	chip->ecc.algo		= NAND_ECC_HAMMING;
> +	chip->ecc.algo		= NAND_ECC_ALGO_HAMMING;
>  	chip->options		= gpiomtd->plat.options;
>  	chip->legacy.chip_delay	= gpiomtd->plat.chip_delay;
>  	chip->legacy.cmd_ctrl	= gpio_nand_cmd_ctrl;
> diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
> index 260a0430313e..3969cca7d925 100644
> --- a/drivers/mtd/nand/raw/marvell_nand.c
> +++ b/drivers/mtd/nand/raw/marvell_nand.c
> @@ -780,7 +780,7 @@ static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip)
>  		 * When enabling BCH, set threshold to 0 to always know the
>  		 * number of corrected bitflips.
>  		 */
> -		if (chip->ecc.algo == NAND_ECC_BCH)
> +		if (chip->ecc.algo == NAND_ECC_ALGO_BCH)
>  			writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL);
>  	}
>  }
> @@ -792,7 +792,7 @@ static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip)
>  
>  	if (ndcr & NDCR_ECC_EN) {
>  		writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR);
> -		if (chip->ecc.algo == NAND_ECC_BCH)
> +		if (chip->ecc.algo == NAND_ECC_ALGO_BCH)
>  			writel_relaxed(0, nfc->regs + NDECCCTRL);
>  	}
>  }
> @@ -966,7 +966,7 @@ static int marvell_nfc_hw_ecc_check_bitflips(struct nand_chip *chip,
>  	if (ndsr & NDSR_CORERR) {
>  		writel_relaxed(ndsr, nfc->regs + NDSR);
>  
> -		if (chip->ecc.algo == NAND_ECC_BCH)
> +		if (chip->ecc.algo == NAND_ECC_ALGO_BCH)
>  			bf = NDSR_ERRCNT(ndsr);
>  		else
>  			bf = 1;
> @@ -2215,7 +2215,7 @@ static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd,
>  	ecc->size = l->data_bytes;
>  
>  	if (ecc->strength == 1) {
> -		chip->ecc.algo = NAND_ECC_HAMMING;
> +		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw;
>  		ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page;
>  		ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw;
> @@ -2225,7 +2225,7 @@ static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd,
>  		ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw;
>  		ecc->write_oob = ecc->write_oob_raw;
>  	} else {
> -		chip->ecc.algo = NAND_ECC_BCH;
> +		chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  		ecc->strength = 16;
>  		ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw;
>  		ecc->read_page = marvell_nfc_hw_ecc_bch_read_page;
> diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c
> index 18ecb096a32d..a67eded226db 100644
> --- a/drivers/mtd/nand/raw/mpc5121_nfc.c
> +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c
> @@ -689,7 +689,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
>  	chip->legacy.get_features = nand_get_set_features_notsupp;
>  	chip->bbt_options = NAND_BBT_USE_FLASH;
>  	chip->ecc.mode = NAND_ECC_SOFT;
> -	chip->ecc.algo = NAND_ECC_HAMMING;
> +	chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	/* Support external chip-select logic on ADS5121 board */
>  	if (of_machine_is_compatible("fsl,mpc5121ads")) {
> diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
> index 09dacb83cb5a..c2e9759cfba8 100644
> --- a/drivers/mtd/nand/raw/mxc_nand.c
> +++ b/drivers/mtd/nand/raw/mxc_nand.c
> @@ -1846,7 +1846,7 @@ static int mxcnd_probe(struct platform_device *pdev)
>  		this->ecc.mode = NAND_ECC_HW;
>  	} else {
>  		this->ecc.mode = NAND_ECC_SOFT;
> -		this->ecc.algo = NAND_ECC_HAMMING;
> +		this->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  	}
>  
>  	/* NAND bus width determines access functions used by upper layer */
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index fd0bfe9bf7ae..4cf53b9dddee 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -5076,9 +5076,9 @@ enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np)
>  }
>  
>  static const char * const nand_ecc_algos[] = {
> -	[NAND_ECC_HAMMING]	= "hamming",
> -	[NAND_ECC_BCH]		= "bch",
> -	[NAND_ECC_RS]		= "rs",
> +	[NAND_ECC_ALGO_HAMMING]	= "hamming",
> +	[NAND_ECC_ALGO_BCH]	= "bch",
> +	[NAND_ECC_ALGO_RS]	= "rs",
>  };
>  
>  static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
> @@ -5089,7 +5089,7 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
>  
>  	err = of_property_read_string(np, "nand-ecc-algo", &pm);
>  	if (!err) {
> -		for (ecc_algo = NAND_ECC_HAMMING;
> +		for (ecc_algo = NAND_ECC_ALGO_HAMMING;
>  		     ecc_algo < ARRAY_SIZE(nand_ecc_algos);
>  		     ecc_algo++) {
>  			if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
> @@ -5104,12 +5104,12 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
>  	err = of_property_read_string(np, "nand-ecc-mode", &pm);
>  	if (!err) {
>  		if (!strcasecmp(pm, "soft"))
> -			return NAND_ECC_HAMMING;
> +			return NAND_ECC_ALGO_HAMMING;
>  		else if (!strcasecmp(pm, "soft_bch"))
> -			return NAND_ECC_BCH;
> +			return NAND_ECC_ALGO_BCH;
>  	}
>  
> -	return NAND_ECC_UNKNOWN;
> +	return NAND_ECC_ALGO_UNKNOWN;
>  }
>  
>  static int of_get_nand_ecc_step_size(struct device_node *np)
> @@ -5178,7 +5178,7 @@ static int nand_dt_init(struct nand_chip *chip)
>  	if (ecc_mode >= 0)
>  		chip->ecc.mode = ecc_mode;
>  
> -	if (ecc_algo != NAND_ECC_UNKNOWN)
> +	if (ecc_algo != NAND_ECC_ALGO_UNKNOWN)
>  		chip->ecc.algo = ecc_algo;
>  
>  	if (ecc_strength >= 0)
> @@ -5302,7 +5302,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
>  		return -EINVAL;
>  
>  	switch (ecc->algo) {
> -	case NAND_ECC_HAMMING:
> +	case NAND_ECC_ALGO_HAMMING:
>  		ecc->calculate = nand_calculate_ecc;
>  		ecc->correct = nand_correct_data;
>  		ecc->read_page = nand_read_page_swecc;
> @@ -5323,7 +5323,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
>  			ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
>  
>  		return 0;
> -	case NAND_ECC_BCH:
> +	case NAND_ECC_ALGO_BCH:
>  		if (!mtd_nand_has_bch()) {
>  			WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
>  			return -EINVAL;
> @@ -5763,7 +5763,7 @@ static int nand_scan_tail(struct nand_chip *chip)
>  	 * If no default placement scheme is given, select an appropriate one.
>  	 */
>  	if (!mtd->ooblayout &&
> -	    !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
> +	    !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_ALGO_BCH)) {
>  		switch (mtd->oobsize) {
>  		case 8:
>  		case 16:
> @@ -5858,7 +5858,7 @@ static int nand_scan_tail(struct nand_chip *chip)
>  			pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
>  				ecc->size, mtd->writesize);
>  			ecc->mode = NAND_ECC_SOFT;
> -			ecc->algo = NAND_ECC_HAMMING;
> +			ecc->algo = NAND_ECC_ALGO_HAMMING;
>  			break;
>  
>  		default:
> @@ -6124,7 +6124,7 @@ EXPORT_SYMBOL(nand_scan_with_ids);
>  void nand_cleanup(struct nand_chip *chip)
>  {
>  	if (chip->ecc.mode == NAND_ECC_SOFT &&
> -	    chip->ecc.algo == NAND_ECC_BCH)
> +	    chip->ecc.algo == NAND_ECC_ALGO_BCH)
>  		nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
>  
>  	nanddev_cleanup(&chip->base);
> diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c
> index 3589b4fce0d4..a43b4d17bc69 100644
> --- a/drivers/mtd/nand/raw/nand_micron.c
> +++ b/drivers/mtd/nand/raw/nand_micron.c
> @@ -543,7 +543,7 @@ static int micron_nand_init(struct nand_chip *chip)
>  		chip->ecc.bytes = chip->base.eccreq.strength * 2;
>  		chip->ecc.size = 512;
>  		chip->ecc.strength = chip->base.eccreq.strength;
> -		chip->ecc.algo = NAND_ECC_BCH;
> +		chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  		chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
>  		chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
>  
> diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
> index 0a5cb77966cc..9bcf1b9d4987 100644
> --- a/drivers/mtd/nand/raw/nandsim.c
> +++ b/drivers/mtd/nand/raw/nandsim.c
> @@ -2235,7 +2235,7 @@ static int ns_attach_chip(struct nand_chip *chip)
>  	}
>  
>  	chip->ecc.mode = NAND_ECC_SOFT;
> -	chip->ecc.algo = NAND_ECC_BCH;
> +	chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  	chip->ecc.size = 512;
>  	chip->ecc.strength = bch;
>  	chip->ecc.bytes = eccbytes;
> @@ -2275,7 +2275,7 @@ static int __init ns_init_module(void)
>  	nand_set_controller_data(chip, (void *)ns);
>  
>  	chip->ecc.mode   = NAND_ECC_SOFT;
> -	chip->ecc.algo   = NAND_ECC_HAMMING;
> +	chip->ecc.algo   = NAND_ECC_ALGO_HAMMING;
>  	/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
>  	/* and 'badblocks' parameters to work */
>  	chip->options   |= NAND_SKIP_BBTSCAN;
> diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
> index eb7fcfd9276b..967ddbda1c48 100644
> --- a/drivers/mtd/nand/raw/omap2.c
> +++ b/drivers/mtd/nand/raw/omap2.c
> @@ -2011,7 +2011,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
>  	 */
>  	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
>  		chip->ecc.mode = NAND_ECC_SOFT;
> -		chip->ecc.algo = NAND_ECC_HAMMING;
> +		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		return 0;
>  	}
>  
> diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c
> index 880b54ca1b41..7a5cfa3d883f 100644
> --- a/drivers/mtd/nand/raw/orion_nand.c
> +++ b/drivers/mtd/nand/raw/orion_nand.c
> @@ -140,7 +140,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
>  	nc->legacy.cmd_ctrl = orion_nand_cmd_ctrl;
>  	nc->legacy.read_buf = orion_nand_read_buf;
>  	nc->ecc.mode = NAND_ECC_SOFT;
> -	nc->ecc.algo = NAND_ECC_HAMMING;
> +	nc->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	if (board->chip_delay)
>  		nc->legacy.chip_delay = board->chip_delay;
> diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c
> index d8eca8c3fdcd..3eddc284614d 100644
> --- a/drivers/mtd/nand/raw/pasemi_nand.c
> +++ b/drivers/mtd/nand/raw/pasemi_nand.c
> @@ -133,7 +133,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
>  	chip->legacy.write_buf = pasemi_write_buf;
>  	chip->legacy.chip_delay = 0;
>  	chip->ecc.mode = NAND_ECC_SOFT;
> -	chip->ecc.algo = NAND_ECC_HAMMING;
> +	chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	/* Enable the following for a flash based bad block table */
>  	chip->bbt_options = NAND_BBT_USE_FLASH;
> diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c
> index 556182f26057..dbc089c8872f 100644
> --- a/drivers/mtd/nand/raw/plat_nand.c
> +++ b/drivers/mtd/nand/raw/plat_nand.c
> @@ -67,7 +67,7 @@ static int plat_nand_probe(struct platform_device *pdev)
>  	data->chip.bbt_options |= pdata->chip.bbt_options;
>  
>  	data->chip.ecc.mode = NAND_ECC_SOFT;
> -	data->chip.ecc.algo = NAND_ECC_HAMMING;
> +	data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	platform_set_drvdata(pdev, data);
>  
> diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c
> index f86dff311464..dfe5a0f07385 100644
> --- a/drivers/mtd/nand/raw/s3c2410.c
> +++ b/drivers/mtd/nand/raw/s3c2410.c
> @@ -938,11 +938,11 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip)
>  	case NAND_ECC_SOFT:
>  		/*
>  		 * This driver expects Hamming based ECC when ecc_mode is set
> -		 * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
> +		 * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_ALGO_HAMMING to
>  		 * avoid adding an extra ecc_algo field to
>  		 * s3c2410_platform_nand.
>  		 */
> -		chip->ecc.algo = NAND_ECC_HAMMING;
> +		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		dev_info(info->device, "soft ECC\n");
>  		break;
>  
> diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
> index a661b8bb2dd5..9dbd6fdbe264 100644
> --- a/drivers/mtd/nand/raw/sh_flctl.c
> +++ b/drivers/mtd/nand/raw/sh_flctl.c
> @@ -1045,7 +1045,7 @@ static int flctl_chip_attach_chip(struct nand_chip *chip)
>  		flctl->flcmncr_base |= _4ECCEN;
>  	} else {
>  		chip->ecc.mode = NAND_ECC_SOFT;
> -		chip->ecc.algo = NAND_ECC_HAMMING;
> +		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  	}
>  
>  	return 0;
> diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c
> index 243b34cfbc1b..72a3a7f98282 100644
> --- a/drivers/mtd/nand/raw/socrates_nand.c
> +++ b/drivers/mtd/nand/raw/socrates_nand.c
> @@ -154,7 +154,7 @@ static int socrates_nand_probe(struct platform_device *ofdev)
>  	nand_chip->legacy.dev_ready = socrates_nand_device_ready;
>  
>  	nand_chip->ecc.mode = NAND_ECC_SOFT;	/* enable ECC */
> -	nand_chip->ecc.algo = NAND_ECC_HAMMING;
> +	nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	/* TODO: I have no idea what real delay is. */
>  	nand_chip->legacy.chip_delay = 20;	/* 20us command delay time */
> diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c
> index 246871e01027..2154b6f860dd 100644
> --- a/drivers/mtd/nand/raw/tango_nand.c
> +++ b/drivers/mtd/nand/raw/tango_nand.c
> @@ -512,7 +512,7 @@ static int tango_attach_chip(struct nand_chip *chip)
>  	struct nand_ecc_ctrl *ecc = &chip->ecc;
>  
>  	ecc->mode = NAND_ECC_HW;
> -	ecc->algo = NAND_ECC_BCH;
> +	ecc->algo = NAND_ECC_ALGO_BCH;
>  	ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
>  
>  	ecc->read_page_raw = tango_read_page_raw;
> diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
> index f9d046b2cd3b..e2e13effc8a6 100644
> --- a/drivers/mtd/nand/raw/tegra_nand.c
> +++ b/drivers/mtd/nand/raw/tegra_nand.c
> @@ -479,7 +479,7 @@ static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl,
>  {
>  	struct tegra_nand_chip *nand = to_tegra_chip(chip);
>  
> -	if (chip->ecc.algo == NAND_ECC_BCH && enable)
> +	if (chip->ecc.algo == NAND_ECC_ALGO_BCH && enable)
>  		writel_relaxed(nand->bch_config, ctrl->regs + BCH_CONFIG);
>  	else
>  		writel_relaxed(0, ctrl->regs + BCH_CONFIG);
> @@ -877,7 +877,7 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize)
>  	int strength_len, bits_per_step;
>  
>  	switch (chip->ecc.algo) {
> -	case NAND_ECC_RS:
> +	case NAND_ECC_ALGO_RS:
>  		bits_per_step = BITS_PER_STEP_RS;
>  		if (chip->options & NAND_IS_BOOT_MEDIUM) {
>  			strength = rs_strength_bootable;
> @@ -887,7 +887,7 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize)
>  			strength_len = ARRAY_SIZE(rs_strength);
>  		}
>  		break;
> -	case NAND_ECC_BCH:
> +	case NAND_ECC_ALGO_BCH:
>  		bits_per_step = BITS_PER_STEP_BCH;
>  		if (chip->options & NAND_IS_BOOT_MEDIUM) {
>  			strength = bch_strength_bootable;
> @@ -935,14 +935,14 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
>  	if (chip->options & NAND_BUSWIDTH_16)
>  		nand->config |= CONFIG_BUS_WIDTH_16;
>  
> -	if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
> +	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) {
>  		if (mtd->writesize < 2048)
> -			chip->ecc.algo = NAND_ECC_RS;
> +			chip->ecc.algo = NAND_ECC_ALGO_RS;
>  		else
> -			chip->ecc.algo = NAND_ECC_BCH;
> +			chip->ecc.algo = NAND_ECC_ALGO_BCH;
>  	}
>  
> -	if (chip->ecc.algo == NAND_ECC_BCH && mtd->writesize < 2048) {
> +	if (chip->ecc.algo == NAND_ECC_ALGO_BCH && mtd->writesize < 2048) {
>  		dev_err(ctrl->dev, "BCH supports 2K or 4K page size only\n");
>  		return -EINVAL;
>  	}
> @@ -963,7 +963,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
>  			   CONFIG_SKIP_SPARE_SIZE_4;
>  
>  	switch (chip->ecc.algo) {
> -	case NAND_ECC_RS:
> +	case NAND_ECC_ALGO_RS:
>  		bits_per_step = BITS_PER_STEP_RS * chip->ecc.strength;
>  		mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops);
>  		nand->config_ecc |= CONFIG_HW_ECC | CONFIG_ECC_SEL |
> @@ -984,7 +984,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
>  			return -EINVAL;
>  		}
>  		break;
> -	case NAND_ECC_BCH:
> +	case NAND_ECC_ALGO_BCH:
>  		bits_per_step = BITS_PER_STEP_BCH * chip->ecc.strength;
>  		mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops);
>  		nand->bch_config = BCH_ENABLE;
> @@ -1013,7 +1013,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
>  	}
>  
>  	dev_info(ctrl->dev, "Using %s with strength %d per 512 byte step\n",
> -		 chip->ecc.algo == NAND_ECC_BCH ? "BCH" : "RS",
> +		 chip->ecc.algo == NAND_ECC_ALGO_BCH ? "BCH" : "RS",
>  		 chip->ecc.strength);
>  
>  	chip->ecc.bytes = DIV_ROUND_UP(bits_per_step, BITS_PER_BYTE);
> diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c
> index 94bfba994326..909072e82a68 100644
> --- a/drivers/mtd/nand/raw/xway_nand.c
> +++ b/drivers/mtd/nand/raw/xway_nand.c
> @@ -181,7 +181,7 @@ static int xway_nand_probe(struct platform_device *pdev)
>  	data->chip.legacy.chip_delay = 30;
>  
>  	data->chip.ecc.mode = NAND_ECC_SOFT;
> -	data->chip.ecc.algo = NAND_ECC_HAMMING;
> +	data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
>  
>  	platform_set_drvdata(pdev, data);
>  	nand_set_controller_data(&data->chip, data);
> diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> index 6699ec7f4d40..8d040312c301 100644
> --- a/include/linux/mtd/rawnand.h
> +++ b/include/linux/mtd/rawnand.h
> @@ -108,16 +108,16 @@ enum nand_ecc_placement {
>  
>  /**
>   * enum nand_ecc_algo - NAND ECC algorithm
> - * @NAND_ECC_UNKNOWN: Unknown algorithm
> - * @NAND_ECC_HAMMING: Hamming algorithm
> - * @NAND_ECC_BCH: Bose-Chaudhuri-Hocquenghem algorithm
> - * @NAND_ECC_RS: Reed-Solomon algorithm
> + * @NAND_ECC_ALGO_UNKNOWN: Unknown algorithm
> + * @NAND_ECC_ALGO_HAMMING: Hamming algorithm
> + * @NAND_ECC_ALGO_BCH: Bose-Chaudhuri-Hocquenghem algorithm
> + * @NAND_ECC_ALGO_RS: Reed-Solomon algorithm
>   */
>  enum nand_ecc_algo {
> -	NAND_ECC_UNKNOWN,
> -	NAND_ECC_HAMMING,
> -	NAND_ECC_BCH,
> -	NAND_ECC_RS,
> +	NAND_ECC_ALGO_UNKNOWN,
> +	NAND_ECC_ALGO_HAMMING,
> +	NAND_ECC_ALGO_BCH,
> +	NAND_ECC_ALGO_RS,
>  };
>  
>  /*


^ permalink raw reply

* [PATCH v4 1/3] dt-bindings: hwmon: Add Baikal-T1 PVT sensor binding
From: Serge Semin @ 2020-05-28 14:28 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Rob Herring
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Rob Herring,
	Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, linux-mips,
	linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20200528142805.29115-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 SoC is equipped with an embedded process, voltage and
temperature sensor to monitor the chip internal environment like
temperature, supply voltage and transistors performance.

This bindings describes the external Baikal-T1 PVT control interfaces
like MMIO registers space, interrupt request number and clocks source.
These are then used by the corresponding hwmon device driver to
implement the sysfs files-based access to the sensors functionality.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-mips@vger.kernel.org

---

Changelog v2:
- Lowercase the node-name in the example.
- Add dual-license header.
- Don't use a multi-arg clock phandle reference in the examples dt-binding
  property. Thus reundant include pre-processor statement can be removed.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Discard label definition from the example.
- Align settings of the "#thermal-sensor-cells" property with two spaces
  ahead of the property line start.

Changelog v3:
- Add "baikal,pvt-temp-trim-millicelsius" property support.

Changelog v4:
- Rename the "baikal,pvt-temp-trim-millicelsius" property to
  "baikal,pvt-temp-offset-millicelsius".
---
 .../bindings/hwmon/baikal,bt1-pvt.yaml        | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
new file mode 100644
index 000000000000..84ae4cdd08ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/baikal,bt1-pvt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 PVT Sensor
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  Baikal-T1 SoC provides an embedded process, voltage and temperature
+  sensor to monitor an internal SoC environment (chip temperature, supply
+  voltage and process monitor) and on time detect critical situations,
+  which may cause the system instability and even damages. The IP-block
+  is based on the Analog Bits PVT sensor, but is equipped with a dedicated
+  control wrapper, which provides a MMIO registers-based access to the
+  sensor core functionality (APB3-bus based) and exposes an additional
+  functions like thresholds/data ready interrupts, its status and masks,
+  measurements timeout. Its internal structure is depicted on the next
+  diagram:
+
+     Analog Bits core                     Bakal-T1 PVT control block
+  +--------------------+                  +------------------------+
+  | Temperature sensor |-+         +------| Sensors control        |
+  |--------------------| |<---En---|      |------------------------|
+  | Voltage sensor     |-|<--Mode--| +--->| Sampled data           |
+  |--------------------| |<--Trim--+ |    |------------------------|
+  | Low-Vt sensor      |-|           | +--| Thresholds comparator  |
+  |--------------------| |---Data----| |  |------------------------|
+  | High-Vt sensor     |-|           | +->| Interrupts status      |
+  |--------------------| |--Valid--+-+ |  |------------------------|
+  | Standard-Vt sensor |-+         +---+--| Interrupts mask        |
+  +--------------------+                  |------------------------|
+           ^                              | Interrupts timeout     |
+           |                              +------------------------+
+           |                                        ^  ^
+  Rclk-----+----------------------------------------+  |
+  APB3-------------------------------------------------+
+
+  This bindings describes the external Baikal-T1 PVT control interfaces
+  like MMIO registers space, interrupt request number and clocks source.
+  These are then used by the corresponding hwmon device driver to
+  implement the sysfs files-based access to the sensors functionality.
+
+properties:
+  compatible:
+    const: baikal,bt1-pvt
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: PVT reference clock
+      - description: APB3 interface clock
+
+  clock-names:
+    items:
+      - const: ref
+      - const: pclk
+
+  "#thermal-sensor-cells":
+    description: Baikal-T1 can be referenced as the CPU thermal-sensor
+    const: 0
+
+  baikal,pvt-temp-offset-millicelsius:
+    description: |
+      Temperature sensor trimming factor. It can be used to manually adjust the
+      temperature measurements within 7.130 degrees Celsius.
+    maxItems: 1
+    items:
+      default: 0
+      minimum: 0
+      maximum: 7130
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+
+    pvt@1f200000 {
+      compatible = "baikal,bt1-pvt";
+      reg = <0x1f200000 0x1000>;
+      #thermal-sensor-cells = <0>;
+
+      interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
+
+      baikal,pvt-temp-trim-millicelsius = <1000>;
+
+      clocks = <&ccu_sys>, <&ccu_sys>;
+      clock-names = "ref", "pclk";
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH v4 3/3] hwmon: Add Baikal-T1 PVT sensor driver
From: Serge Semin @ 2020-05-28 14:28 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Alexey Malahov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	devicetree, linux-hwmon, linux-doc, linux-kernel
In-Reply-To: <20200528142805.29115-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 SoC provides an embedded process, voltage and temperature
sensor to monitor an internal SoC environment (chip temperature, supply
voltage and process monitor) and on time detect critical situations,
which may cause the system instability and even damages. The IP-block
is based on the Analog Bits PVT sensor, but is equipped with a
dedicated control wrapper, which provides a MMIO registers-based access
to the sensor core functionality (APB3-bus based) and exposes an
additional functions like thresholds/data ready interrupts, its status
and masks, measurements timeout. All of these is used to create a hwmon
driver being added to the kernel by this commit.

The driver implements support for the hardware monitoring capabilities
of Baikal-T1 process, voltage and temperature sensors. PVT IP-core
consists of one temperature and four voltage sensors, each of which is
implemented as a dedicated hwmon channel config.

The driver can optionally provide the hwmon alarms for each sensor the
PVT controller supports. The alarms functionality is made compile-time
configurable due to the hardware interface implementation peculiarity,
which is connected with an ability to convert data from only one sensor
at a time. Additional limitation is that the controller performs the
thresholds checking synchronously with the data conversion procedure.
Due to these limitations in order to have the hwmon alarms
automatically detected the driver code must switch from one sensor to
another, read converted data and manually check the threshold status
bits. Depending on the measurements timeout settings this design may
cause additional burden on the system performance. By default if the
alarms kernel config is disabled the data conversion is performed by
the driver on demand when read operation is requested via corresponding
_input-file.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- Discard handwritten IO-access wrappers. Use normal readl/writel instead.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Remove probe-status info string printout.

Changelog v3:
- Add bt1-pvt into the Documentation/hwmon/index.rst file.
- Discard explicit "default n" from the SENSORS_BT1_PVT_ALARMS config.
- Use "depends on SENSORS_BT1_PVT" statement instead of if-endif kbuild
  config clause.
- Alphabetically order the include macro operators.
- Discard unneeded include macro in the header file.
- Use new generic interface of the hwmon alarms notifications introduced
  in the first patch (based on hwmon_notify_event()).
- Add more descriptive information regarding the temp1_trim attribute.
- Discard setting the platforms device private data by using
  platform_set_drvdata(). It's redundant since unused in the driver.
- Pass "pvt" hwmon name instead of dev_name(dev) to
  devm_hwmon_device_register_with_info().
- Add "baikal,pvt-temp-trim-millicelsius" temperature trim DT property
  support.
- Discard kernel log warnings printed from the ISR when either min or
  max threshold levels are crossed.
- Discard CONFIG_OF dependency since there is non at compile-time.

Changelog v4:
- Rename temp1_trim to the temp1_offset and use the standard API to
  expose the attribute.
- Rename "baikal,pvt-temp-trim-millicelsius" to
  "baikal,pvt-temp-offset-millicelsius".
- Switch "const static" order to be "static const" where it's applicable.
- Add missing headers "linux/io.h" and "linux/of.h".
- Add static qualifier to the pvt_hwmon_write() method, which has been
  missed there by mistake.
---
 Documentation/hwmon/bt1-pvt.rst |  117 ++++
 Documentation/hwmon/index.rst   |    1 +
 drivers/hwmon/Kconfig           |   25 +
 drivers/hwmon/Makefile          |    1 +
 drivers/hwmon/bt1-pvt.c         | 1146 +++++++++++++++++++++++++++++++
 drivers/hwmon/bt1-pvt.h         |  244 +++++++
 6 files changed, 1534 insertions(+)
 create mode 100644 Documentation/hwmon/bt1-pvt.rst
 create mode 100644 drivers/hwmon/bt1-pvt.c
 create mode 100644 drivers/hwmon/bt1-pvt.h

diff --git a/Documentation/hwmon/bt1-pvt.rst b/Documentation/hwmon/bt1-pvt.rst
new file mode 100644
index 000000000000..cbb0c0613132
--- /dev/null
+++ b/Documentation/hwmon/bt1-pvt.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver bt1-pvt
+=====================
+
+Supported chips:
+
+  * Baikal-T1 PVT sensor (in SoC)
+
+    Prefix: 'bt1-pvt'
+
+    Addresses scanned: -
+
+    Datasheet: Provided by BAIKAL ELECTRONICS upon request and under NDA
+
+Authors:
+    Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+    Serge Semin <Sergey.Semin@baikalelectronics.ru>
+
+Description
+-----------
+
+This driver implements support for the hardware monitoring capabilities of the
+embedded into Baikal-T1 process, voltage and temperature sensors. PVT IP-core
+consists of one temperature and four voltage sensors, which can be used to
+monitor the chip internal environment like heating, supply voltage and
+transistors performance. The driver can optionally provide the hwmon alarms
+for each sensor the PVT controller supports. The alarms functionality is made
+compile-time configurable due to the hardware interface implementation
+peculiarity, which is connected with an ability to convert data from only one
+sensor at a time. Additional limitation is that the controller performs the
+thresholds checking synchronously with the data conversion procedure. Due to
+these in order to have the hwmon alarms automatically detected the driver code
+must switch from one sensor to another, read converted data and manually check
+the threshold status bits. Depending on the measurements timeout settings
+(update_interval sysfs node value) this design may cause additional burden on
+the system performance. So in case if alarms are unnecessary in your system
+design it's recommended to have them disabled to prevent the PVT IRQs being
+periodically raised to get the data cache/alarms status up to date. By default
+in alarm-less configuration the data conversion is performed by the driver
+on demand when read operation is requested via corresponding _input-file.
+
+Temperature Monitoring
+----------------------
+
+Temperature is measured with 10-bit resolution and reported in millidegree
+Celsius. The driver performs all the scaling by itself therefore reports true
+temperatures that don't need any user-space adjustments. While the data
+translation formulae isn't linear, which gives us non-linear discreteness,
+it's close to one, but giving a bit better accuracy for higher temperatures.
+The temperature input is mapped as follows (the last column indicates the input
+ranges)::
+
+	temp1: CPU embedded diode	-48.38C - +147.438C
+
+In case if the alarms kernel config is enabled in the driver the temperature input
+has associated min and max limits which trigger an alarm when crossed.
+
+Voltage Monitoring
+------------------
+
+The voltage inputs are also sampled with 10-bit resolution and reported in
+millivolts. But in this case the data translation formulae is linear, which
+provides a constant measurements discreteness. The data scaling is also
+performed by the driver, so returning true millivolts. The voltage inputs are
+mapped as follows (the last column indicates the input ranges)::
+
+	in0: VDD		(processor core)		0.62V - 1.168V
+	in1: Low-Vt		(low voltage threshold)		0.62V - 1.168V
+	in2: High-Vt		(high voltage threshold)	0.62V - 1.168V
+	in3: Standard-Vt	(standard voltage threshold)	0.62V - 1.168V
+
+In case if the alarms config is enabled in the driver the voltage inputs
+have associated min and max limits which trigger an alarm when crossed.
+
+Sysfs Attributes
+----------------
+
+Following is a list of all sysfs attributes that the driver provides, their
+permissions and a short description:
+
+=============================== ======= =======================================
+Name				Perm	Description
+=============================== ======= =======================================
+update_interval			RW	Measurements update interval per
+					sensor.
+temp1_type			RO	Sensor type (always 1 as CPU embedded
+					diode).
+temp1_label			RO	CPU Core Temperature sensor.
+temp1_input			RO	Measured temperature in millidegree
+					Celsius.
+temp1_min			RW	Low limit for temp input.
+temp1_max			RW	High limit for temp input.
+temp1_min_alarm			RO	Temperature input alarm. Returns 1 if
+					temperature input went below min limit,
+					0 otherwise.
+temp1_max_alarm			RO	Temperature input alarm. Returns 1 if
+					temperature input went above max limit,
+					0 otherwise.
+temp1_offset			RW	Temperature offset in millidegree
+					Celsius which is added to the
+					temperature reading by the chip. It can
+					be used to manually adjust the
+					temperature measurements within 7.130
+					degrees Celsius.
+in[0-3]_label			RO	CPU Voltage sensor (either core or
+					low/high/standard thresholds).
+in[0-3]_input			RO	Measured voltage in millivolts.
+in[0-3]_min			RW	Low limit for voltage input.
+in[0-3]_max			RW	High limit for voltage input.
+in[0-3]_min_alarm		RO	Voltage input alarm. Returns 1 if
+					voltage input went below min limit,
+					0 otherwise.
+in[0-3]_max_alarm		RO	Voltage input alarm. Returns 1 if
+					voltage input went above max limit,
+					0 otherwise.
+=============================== ======= =======================================
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 8ef62fd39787..3f322d2f1d20 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -43,6 +43,7 @@ Hardware Monitoring Kernel Drivers
    asc7621
    aspeed-pwm-tacho
    bel-pfe
+   bt1-pvt
    coretemp
    da9052
    da9055
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 4c62f900bf7e..d1c6c5d46c08 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -404,6 +404,31 @@ config SENSORS_ATXP1
 	  This driver can also be built as a module. If so, the module
 	  will be called atxp1.
 
+config SENSORS_BT1_PVT
+	tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	help
+	  If you say yes here you get support for Baikal-T1 PVT sensor
+	  embedded into the SoC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bt1-pvt.
+
+config SENSORS_BT1_PVT_ALARMS
+	bool "Enable Baikal-T1 PVT sensor alarms"
+	depends on SENSORS_BT1_PVT
+	help
+	  Baikal-T1 PVT IP-block provides threshold registers for each
+	  supported sensor. But the corresponding interrupts might be
+	  generated by the thresholds comparator only in synchronization with
+	  a data conversion. Additionally there is only one sensor data can
+	  be converted at a time. All of these makes the interface impossible
+	  to be used for the hwmon alarms implementation without periodic
+	  switch between the PVT sensors. By default the data conversion is
+	  performed on demand from the user-space. If this config is enabled
+	  the data conversion will be periodically performed and the data will be
+	  saved in the internal driver cache.
+
 config SENSORS_DRIVETEMP
 	tristate "Hard disk drives with temperature sensors"
 	depends on SCSI && ATA
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b0b9c8e57176..408b86a5e71d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_SENSORS_ASC7621)	+= asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)	+= aspeed-pwm-tacho.o
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
+obj-$(CONFIG_SENSORS_BT1_PVT)	+= bt1-pvt.o
 obj-$(CONFIG_SENSORS_CORETEMP)	+= coretemp.o
 obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
 obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
new file mode 100644
index 000000000000..1a9772fb1f73
--- /dev/null
+++ b/drivers/hwmon/bt1-pvt.c
@@ -0,0 +1,1146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/seqlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include "bt1-pvt.h"
+
+/*
+ * For the sake of the code simplification we created the sensors info table
+ * with the sensor names, activation modes, threshold registers base address
+ * and the thresholds bit fields.
+ */
+static const struct pvt_sensor_info pvt_info[] = {
+	PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES),
+	PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES),
+	PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES),
+	PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES),
+	PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES),
+};
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * to PVT data and vice-versa are following:
+ * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
+ *     1.7204e2,
+ * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
+ *     3.1020e-1*(N^1) - 4.838e1,
+ * where T = [-48.380, 147.438]C and N = [0, 1023].
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what they look like after
+ * the alterations:
+ * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
+ *     17204e2) / 1e4,
+ * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
+ *     48380,
+ * where T = [-48380, 147438] mC and N = [0, 1023].
+ */
+static const struct pvt_poly poly_temp_to_N = {
+	.total_divider = 10000,
+	.terms = {
+		{4, 18322, 10000, 10000},
+		{3, 2343, 10000, 10},
+		{2, 87018, 10000, 10},
+		{1, 39269, 1000, 1},
+		{0, 1720400, 1, 1}
+	}
+};
+
+static const struct pvt_poly poly_N_to_temp = {
+	.total_divider = 1,
+	.terms = {
+		{4, -16743, 1000, 1},
+		{3, 81542, 1000, 1},
+		{2, -182010, 1000, 1},
+		{1, 310200, 1000, 1},
+		{0, -48380, 1, 1}
+	}
+};
+
+/*
+ * Similar alterations are performed for the voltage conversion equations.
+ * The original formulae are:
+ * N = 1.8658e3*V - 1.1572e3,
+ * V = (N + 1.1572e3) / 1.8658e3,
+ * where V = [0.620, 1.168] V and N = [0, 1023].
+ * After the optimization they looks as follows:
+ * N = (18658e-3*V - 11572) / 10,
+ * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
+ */
+static const struct pvt_poly poly_volt_to_N = {
+	.total_divider = 10,
+	.terms = {
+		{1, 18658, 1000, 1},
+		{0, -11572, 1, 1}
+	}
+};
+
+static const struct pvt_poly poly_N_to_volt = {
+	.total_divider = 10,
+	.terms = {
+		{1, 100000, 18658, 1},
+		{0, 115720000, 1, 18658}
+	}
+};
+
+/*
+ * Here is the polynomial calculation function, which performs the
+ * redistributed terms calculations. It's pretty straightforward. We walk
+ * over each degree term up to the free one, and perform the redistributed
+ * multiplication of the term coefficient, its divider (as for the rationale
+ * fraction representation), data power and the rational fraction divider
+ * leftover. Then all of this is collected in a total sum variable, which
+ * value is normalized by the total divider before being returned.
+ */
+static long pvt_calc_poly(const struct pvt_poly *poly, long data)
+{
+	const struct pvt_poly_term *term = poly->terms;
+	long tmp, ret = 0;
+	int deg;
+
+	do {
+		tmp = term->coef;
+		for (deg = 0; deg < term->deg; ++deg)
+			tmp = mult_frac(tmp, data, term->divider);
+		ret += tmp / term->divider_leftover;
+	} while ((term++)->deg);
+
+	return ret / poly->total_divider;
+}
+
+static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
+{
+	u32 old;
+
+	old = readl_relaxed(reg);
+	writel((old & ~mask) | (data & mask), reg);
+
+	return old & mask;
+}
+
+/*
+ * Baikal-T1 PVT mode can be updated only when the controller is disabled.
+ * So first we disable it, then set the new mode together with the controller
+ * getting back enabled. The same concerns the temperature trim and
+ * measurements timeout. If it is necessary the interface mutex is supposed
+ * to be locked at the time the operations are performed.
+ */
+static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
+{
+	u32 old;
+
+	mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
+		   mode | old);
+}
+
+static inline u32 pvt_calc_trim(long temp)
+{
+	temp = clamp_val(temp, 0, PVT_TRIM_TEMP);
+
+	return DIV_ROUND_UP(temp, PVT_TRIM_STEP);
+}
+
+static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
+{
+	u32 old;
+
+	trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
+		   trim | old);
+}
+
+static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
+{
+	u32 old;
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	writel(tout, pvt->regs + PVT_TTIMEOUT);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
+}
+
+/*
+ * This driver can optionally provide the hwmon alarms for each sensor the PVT
+ * controller supports. The alarms functionality is made compile-time
+ * configurable due to the hardware interface implementation peculiarity
+ * described further in this comment. So in case if alarms are unnecessary in
+ * your system design it's recommended to have them disabled to prevent the PVT
+ * IRQs being periodically raised to get the data cache/alarms status up to
+ * date.
+ *
+ * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor,
+ * but is equipped with a dedicated control wrapper. It exposes the PVT
+ * sub-block registers space via the APB3 bus. In addition the wrapper provides
+ * a common interrupt vector of the sensors conversion completion events and
+ * threshold value alarms. Alas the wrapper interface hasn't been fully thought
+ * through. There is only one sensor can be activated at a time, for which the
+ * thresholds comparator is enabled right after the data conversion is
+ * completed. Due to this if alarms need to be implemented for all available
+ * sensors we can't just set the thresholds and enable the interrupts. We need
+ * to enable the sensors one after another and let the controller to detect
+ * the alarms by itself at each conversion. This also makes pointless to handle
+ * the alarms interrupts, since in occasion they happen synchronously with
+ * data conversion completion. The best driver design would be to have the
+ * completion interrupts enabled only and keep the converted value in the
+ * driver data cache. This solution is implemented if hwmon alarms are enabled
+ * in this driver. In case if the alarms are disabled, the conversion is
+ * performed on demand at the time a sensors input file is read.
+ */
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+#define pvt_hard_isr NULL
+
+static irqreturn_t pvt_soft_isr(int irq, void *data)
+{
+	const struct pvt_sensor_info *info;
+	struct pvt_hwmon *pvt = data;
+	struct pvt_cache *cache;
+	u32 val, thres_sts, old;
+
+	/*
+	 * DVALID bit will be cleared by reading the data. We need to save the
+	 * status before the next conversion happens. Threshold events will be
+	 * handled a bit later.
+	 */
+	thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
+
+	/*
+	 * Then lets recharge the PVT interface with the next sampling mode.
+	 * Lock the interface mutex to serialize trim, timeouts and alarm
+	 * thresholds settings.
+	 */
+	cache = &pvt->cache[pvt->sensor];
+	info = &pvt_info[pvt->sensor];
+	pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ?
+		      PVT_SENSOR_FIRST : (pvt->sensor + 1);
+
+	/*
+	 * For some reason we have to mask the interrupt before changing the
+	 * mode, otherwise sometimes the temperature mode doesn't get
+	 * activated even though the actual mode in the ctrl register
+	 * corresponds to one. Then we read the data. By doing so we also
+	 * recharge the data conversion. After this the mode corresponding
+	 * to the next sensor in the row is set. Finally we enable the
+	 * interrupts back.
+	 */
+	mutex_lock(&pvt->iface_mtx);
+
+	old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+			 PVT_INTR_DVALID);
+
+	val = readl(pvt->regs + PVT_DATA);
+
+	pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	/*
+	 * We can now update the data cache with data just retrieved from the
+	 * sensor. Lock write-seqlock to make sure the reader has a coherent
+	 * data.
+	 */
+	write_seqlock(&cache->data_seqlock);
+
+	cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val);
+
+	write_sequnlock(&cache->data_seqlock);
+
+	/*
+	 * While PVT core is doing the next mode data conversion, we'll check
+	 * whether the alarms were triggered for the current sensor. Note that
+	 * according to the documentation only one threshold IRQ status can be
+	 * set at a time, that's why if-else statement is utilized.
+	 */
+	if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) {
+		WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo);
+		hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm,
+				   info->channel);
+	} else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) {
+		WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi);
+		hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm,
+				   info->channel);
+	}
+
+	return IRQ_HANDLED;
+}
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+	return 0644;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+	return 0444;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			 long *val)
+{
+	struct pvt_cache *cache = &pvt->cache[type];
+	unsigned int seq;
+	u32 data;
+
+	do {
+		seq = read_seqbegin(&cache->data_seqlock);
+		data = cache->data;
+	} while (read_seqretry(&cache->data_seqlock, seq));
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	u32 data;
+
+	/* No need in serialization, since it is just read from MMIO. */
+	data = readl(pvt->regs + pvt_info[type].thres_base);
+
+	if (is_low)
+		data = FIELD_GET(PVT_THRES_LO_MASK, data);
+	else
+		data = FIELD_GET(PVT_THRES_HI_MASK, data);
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			   bool is_low, long val)
+{
+	u32 data, limit, mask;
+	int ret;
+
+	if (type == PVT_TEMP) {
+		val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
+		data = pvt_calc_poly(&poly_temp_to_N, val);
+	} else {
+		val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
+		data = pvt_calc_poly(&poly_volt_to_N, val);
+	}
+
+	/* Serialize limit update, since a part of the register is changed. */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	/* Make sure the upper and lower ranges don't intersect. */
+	limit = readl(pvt->regs + pvt_info[type].thres_base);
+	if (is_low) {
+		limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
+		data = clamp_val(data, PVT_DATA_MIN, limit);
+		data = FIELD_PREP(PVT_THRES_LO_MASK, data);
+		mask = PVT_THRES_LO_MASK;
+	} else {
+		limit = FIELD_GET(PVT_THRES_LO_MASK, limit);
+		data = clamp_val(data, limit, PVT_DATA_MAX);
+		data = FIELD_PREP(PVT_THRES_HI_MASK, data);
+		mask = PVT_THRES_HI_MASK;
+	}
+
+	pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	if (is_low)
+		*val = !!READ_ONCE(pvt->cache[type].thres_sts_lo);
+	else
+		*val = !!READ_ONCE(pvt->cache[type].thres_sts_hi);
+
+	return 0;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+	HWMON_CHANNEL_INFO(chip,
+			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
+			   HWMON_T_MIN | HWMON_T_MIN_ALARM |
+			   HWMON_T_MAX | HWMON_T_MAX_ALARM |
+			   HWMON_T_OFFSET),
+	HWMON_CHANNEL_INFO(in,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM),
+	NULL
+};
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static irqreturn_t pvt_hard_isr(int irq, void *data)
+{
+	struct pvt_hwmon *pvt = data;
+	struct pvt_cache *cache;
+	u32 val;
+
+	/*
+	 * Mask the DVALID interrupt so after exiting from the handler a
+	 * repeated conversion wouldn't happen.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+
+	/*
+	 * Nothing special for alarm-less driver. Just read the data, update
+	 * the cache and notify a waiter of this event.
+	 */
+	val = readl(pvt->regs + PVT_DATA);
+	if (!(val & PVT_DATA_VALID)) {
+		dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
+		return IRQ_HANDLED;
+	}
+
+	cache = &pvt->cache[pvt->sensor];
+
+	WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val));
+
+	complete(&cache->conversion);
+
+	return IRQ_HANDLED;
+}
+
+#define pvt_soft_isr NULL
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+	return 0;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+	return 0;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			 long *val)
+{
+	struct pvt_cache *cache = &pvt->cache[type];
+	u32 data;
+	int ret;
+
+	/*
+	 * Lock PVT conversion interface until data cache is updated. The
+	 * data read procedure is following: set the requested PVT sensor
+	 * mode, enable IRQ and conversion, wait until conversion is finished,
+	 * then disable conversion and IRQ, and read the cached data.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	pvt->sensor = type;
+	pvt_set_mode(pvt, pvt_info[type].mode);
+
+	/*
+	 * Unmask the DVALID interrupt and enable the sensors conversions.
+	 * Do the reverse procedure when conversion is done.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+
+	wait_for_completion(&cache->conversion);
+
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+
+	data = READ_ONCE(cache->data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			   bool is_low, long val)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+	HWMON_CHANNEL_INFO(chip,
+			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
+			   HWMON_T_OFFSET),
+	HWMON_CHANNEL_INFO(in,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL),
+	NULL
+};
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type,
+					      int ch)
+{
+	switch (type) {
+	case hwmon_temp:
+		if (ch < 0 || ch >= PVT_TEMP_CHS)
+			return false;
+		break;
+	case hwmon_in:
+		if (ch < 0 || ch >= PVT_VOLT_CHS)
+			return false;
+		break;
+	default:
+		break;
+	}
+
+	/* The rest of the types are independent from the channel number. */
+	return true;
+}
+
+static umode_t pvt_hwmon_is_visible(const void *data,
+				    enum hwmon_sensor_types type,
+				    u32 attr, int ch)
+{
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return 0;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return 0644;
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_input:
+		case hwmon_temp_type:
+		case hwmon_temp_label:
+			return 0444;
+		case hwmon_temp_min:
+		case hwmon_temp_max:
+			return pvt_limit_is_visible(ch);
+		case hwmon_temp_min_alarm:
+		case hwmon_temp_max_alarm:
+			return pvt_alarm_is_visible(ch);
+		case hwmon_temp_offset:
+			return 0644;
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+		case hwmon_in_label:
+			return 0444;
+		case hwmon_in_min:
+		case hwmon_in_max:
+			return pvt_limit_is_visible(PVT_VOLT + ch);
+		case hwmon_in_min_alarm:
+		case hwmon_in_max_alarm:
+			return pvt_alarm_is_visible(PVT_VOLT + ch);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int pvt_read_trim(struct pvt_hwmon *pvt, long *val)
+{
+	u32 data;
+
+	data = readl(pvt->regs + PVT_CTRL);
+	*val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP;
+
+	return 0;
+}
+
+static int pvt_write_trim(struct pvt_hwmon *pvt, long val)
+{
+	u32 trim;
+	int ret;
+
+	/*
+	 * Serialize trim update, since a part of the register is changed and
+	 * the controller is supposed to be disabled during this operation.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	trim = pvt_calc_trim(val);
+	pvt_set_trim(pvt, trim);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val)
+{
+	unsigned long rate;
+	ktime_t kt;
+	u32 data;
+
+	rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+	if (!rate)
+		return -ENODEV;
+
+	/*
+	 * Don't bother with mutex here, since we just read data from MMIO.
+	 * We also have to scale the ticks timeout up to compensate the
+	 * ms-ns-data translations.
+	 */
+	data = readl(pvt->regs + PVT_TTIMEOUT) + 1;
+
+	/*
+	 * Calculate ref-clock based delay (Ttotal) between two consecutive
+	 * data samples of the same sensor. So we first must calculate the
+	 * delay introduced by the internal ref-clock timer (Tref * Fclk).
+	 * Then add the constant timeout cuased by each conversion latency
+	 * (Tmin). The basic formulae for each conversion is following:
+	 *   Ttotal = Tref * Fclk + Tmin
+	 * Note if alarms are enabled the sensors are polled one after
+	 * another, so in order to have the delay being applicable for each
+	 * sensor the requested value must be equally redistirbuted.
+	 */
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	kt = ktime_set(PVT_SENSORS_NUM * (u64)data, 0);
+	kt = ktime_divns(kt, rate);
+	kt = ktime_add_ns(kt, PVT_SENSORS_NUM * PVT_TOUT_MIN);
+#else
+	kt = ktime_set(data, 0);
+	kt = ktime_divns(kt, rate);
+	kt = ktime_add_ns(kt, PVT_TOUT_MIN);
+#endif
+
+	/* Return the result in msec as hwmon sysfs interface requires. */
+	*val = ktime_to_ms(kt);
+
+	return 0;
+}
+
+static int pvt_write_timeout(struct pvt_hwmon *pvt, long val)
+{
+	unsigned long rate;
+	ktime_t kt;
+	u32 data;
+	int ret;
+
+	rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+	if (!rate)
+		return -ENODEV;
+
+	/*
+	 * If alarms are enabled, the requested timeout must be divided
+	 * between all available sensors to have the requested delay
+	 * applicable to each individual sensor.
+	 */
+	kt = ms_to_ktime(val);
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	kt = ktime_divns(kt, PVT_SENSORS_NUM);
+#endif
+
+	/*
+	 * Subtract a constant lag, which always persists due to the limited
+	 * PVT sampling rate. Make sure the timeout is not negative.
+	 */
+	kt = ktime_sub_ns(kt, PVT_TOUT_MIN);
+	if (ktime_to_ns(kt) < 0)
+		kt = ktime_set(0, 0);
+
+	/*
+	 * Finally recalculate the timeout in terms of the reference clock
+	 * period.
+	 */
+	data = ktime_divns(kt * rate, NSEC_PER_SEC);
+
+	/*
+	 * Update the measurements delay, but lock the interface first, since
+	 * we have to disable PVT in order to have the new delay actually
+	 * updated.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	pvt_set_tout(pvt, data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+static int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			  u32 attr, int ch, long *val)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return pvt_read_timeout(pvt, val);
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_input:
+			return pvt_read_data(pvt, ch, val);
+		case hwmon_temp_type:
+			*val = 1;
+			return 0;
+		case hwmon_temp_min:
+			return pvt_read_limit(pvt, ch, true, val);
+		case hwmon_temp_max:
+			return pvt_read_limit(pvt, ch, false, val);
+		case hwmon_temp_min_alarm:
+			return pvt_read_alarm(pvt, ch, true, val);
+		case hwmon_temp_max_alarm:
+			return pvt_read_alarm(pvt, ch, false, val);
+		case hwmon_temp_offset:
+			return pvt_read_trim(pvt, val);
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+			return pvt_read_data(pvt, PVT_VOLT + ch, val);
+		case hwmon_in_min:
+			return pvt_read_limit(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max:
+			return pvt_read_limit(pvt, PVT_VOLT + ch, false, val);
+		case hwmon_in_min_alarm:
+			return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max_alarm:
+			return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int pvt_hwmon_read_string(struct device *dev,
+				 enum hwmon_sensor_types type,
+				 u32 attr, int ch, const char **str)
+{
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_label:
+			*str = pvt_info[ch].label;
+			return 0;
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_label:
+			*str = pvt_info[PVT_VOLT + ch].label;
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+			   u32 attr, int ch, long val)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return pvt_write_timeout(pvt, val);
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_min:
+			return pvt_write_limit(pvt, ch, true, val);
+		case hwmon_temp_max:
+			return pvt_write_limit(pvt, ch, false, val);
+		case hwmon_temp_offset:
+			return pvt_write_trim(pvt, val);
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_min:
+			return pvt_write_limit(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max:
+			return pvt_write_limit(pvt, PVT_VOLT + ch, false, val);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops pvt_hwmon_ops = {
+	.is_visible = pvt_hwmon_is_visible,
+	.read = pvt_hwmon_read,
+	.read_string = pvt_hwmon_read_string,
+	.write = pvt_hwmon_write
+};
+
+static const struct hwmon_chip_info pvt_hwmon_info = {
+	.ops = &pvt_hwmon_ops,
+	.info = pvt_channel_info
+};
+
+static void pvt_clear_data(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	int idx;
+
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		complete_all(&pvt->cache[idx].conversion);
+#endif
+
+	mutex_destroy(&pvt->iface_mtx);
+}
+
+static struct pvt_hwmon *pvt_create_data(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pvt_hwmon *pvt;
+	int ret, idx;
+
+	pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
+	if (!pvt)
+		return ERR_PTR(-ENOMEM);
+
+	ret = devm_add_action(dev, pvt_clear_data, pvt);
+	if (ret) {
+		dev_err(dev, "Can't add PVT data clear action\n");
+		return ERR_PTR(ret);
+	}
+
+	pvt->dev = dev;
+	pvt->sensor = PVT_SENSOR_FIRST;
+	mutex_init(&pvt->iface_mtx);
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		seqlock_init(&pvt->cache[idx].data_seqlock);
+#else
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		init_completion(&pvt->cache[idx].conversion);
+#endif
+
+	return pvt;
+}
+
+static int pvt_request_regs(struct pvt_hwmon *pvt)
+{
+	struct platform_device *pdev = to_platform_device(pvt->dev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pvt->dev, "Couldn't find PVT memresource\n");
+		return -EINVAL;
+	}
+
+	pvt->regs = devm_ioremap_resource(pvt->dev, res);
+	if (IS_ERR(pvt->regs)) {
+		dev_err(pvt->dev, "Couldn't map PVT registers\n");
+		return PTR_ERR(pvt->regs);
+	}
+
+	return 0;
+}
+
+static void pvt_disable_clks(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+
+	clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks);
+}
+
+static int pvt_request_clks(struct pvt_hwmon *pvt)
+{
+	int ret;
+
+	pvt->clks[PVT_CLOCK_APB].id = "pclk";
+	pvt->clks[PVT_CLOCK_REF].id = "ref";
+
+	ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
+		return ret;
+	}
+
+	ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't enable the PVT clocks\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void pvt_init_iface(struct pvt_hwmon *pvt)
+{
+	u32 trim, temp;
+
+	/*
+	 * Make sure all interrupts and controller are disabled so not to
+	 * accidentally have ISR executed before the driver data is fully
+	 * initialized. Clear the IRQ status as well.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	readl(pvt->regs + PVT_CLR_INTR);
+	readl(pvt->regs + PVT_DATA);
+
+	/* Setup default sensor mode, timeout and temperature trim. */
+	pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+	pvt_set_tout(pvt, PVT_TOUT_DEF);
+
+	trim = PVT_TRIM_DEF;
+	if (!of_property_read_u32(pvt->dev->of_node,
+	     "baikal,pvt-temp-offset-millicelsius", &temp))
+		trim = pvt_calc_trim(temp);
+
+	pvt_set_trim(pvt, trim);
+}
+
+static int pvt_request_irq(struct pvt_hwmon *pvt)
+{
+	struct platform_device *pdev = to_platform_device(pvt->dev);
+	int ret;
+
+	pvt->irq = platform_get_irq(pdev, 0);
+	if (pvt->irq < 0)
+		return pvt->irq;
+
+	ret = devm_request_threaded_irq(pvt->dev, pvt->irq,
+					pvt_hard_isr, pvt_soft_isr,
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+					IRQF_SHARED | IRQF_TRIGGER_HIGH |
+					IRQF_ONESHOT,
+#else
+					IRQF_SHARED | IRQF_TRIGGER_HIGH,
+#endif
+					"pvt", pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int pvt_create_hwmon(struct pvt_hwmon *pvt)
+{
+	pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt,
+		&pvt_hwmon_info, NULL);
+	if (IS_ERR(pvt->hwmon)) {
+		dev_err(pvt->dev, "Couldn't create hwmon device\n");
+		return PTR_ERR(pvt->hwmon);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+static void pvt_disable_iface(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+
+	mutex_lock(&pvt->iface_mtx);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+	mutex_unlock(&pvt->iface_mtx);
+}
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+	int ret;
+
+	ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Can't add PVT disable interface action\n");
+		return ret;
+	}
+
+	/*
+	 * Enable sensors data conversion and IRQ. We need to lock the
+	 * interface mutex since hwmon has just been created and the
+	 * corresponding sysfs files are accessible from user-space,
+	 * which theoretically may cause races.
+	 */
+	mutex_lock(&pvt->iface_mtx);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+	return 0;
+}
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_probe(struct platform_device *pdev)
+{
+	struct pvt_hwmon *pvt;
+	int ret;
+
+	pvt = pvt_create_data(pdev);
+	if (IS_ERR(pvt))
+		return PTR_ERR(pvt);
+
+	ret = pvt_request_regs(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_request_clks(pvt);
+	if (ret)
+		return ret;
+
+	pvt_init_iface(pvt);
+
+	ret = pvt_request_irq(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_create_hwmon(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_enable_iface(pvt);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id pvt_of_match[] = {
+	{ .compatible = "baikal,bt1-pvt" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pvt_of_match);
+
+static struct platform_driver pvt_driver = {
+	.probe = pvt_probe,
+	.driver = {
+		.name = "bt1-pvt",
+		.of_match_table = pvt_of_match
+	}
+};
+module_platform_driver(pvt_driver);
+
+MODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 PVT driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
new file mode 100644
index 000000000000..5eac73e94885
--- /dev/null
+++ b/drivers/hwmon/bt1-pvt.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+#ifndef __HWMON_BT1_PVT_H__
+#define __HWMON_BT1_PVT_H__
+
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/seqlock.h>
+
+/* Baikal-T1 PVT registers and their bitfields */
+#define PVT_CTRL			0x00
+#define PVT_CTRL_EN			BIT(0)
+#define PVT_CTRL_MODE_FLD		1
+#define PVT_CTRL_MODE_MASK		GENMASK(3, PVT_CTRL_MODE_FLD)
+#define PVT_CTRL_MODE_TEMP		0x0
+#define PVT_CTRL_MODE_VOLT		0x1
+#define PVT_CTRL_MODE_LVT		0x2
+#define PVT_CTRL_MODE_HVT		0x4
+#define PVT_CTRL_MODE_SVT		0x6
+#define PVT_CTRL_TRIM_FLD		4
+#define PVT_CTRL_TRIM_MASK		GENMASK(8, PVT_CTRL_TRIM_FLD)
+#define PVT_DATA			0x04
+#define PVT_DATA_VALID			BIT(10)
+#define PVT_DATA_DATA_FLD		0
+#define PVT_DATA_DATA_MASK		GENMASK(9, PVT_DATA_DATA_FLD)
+#define PVT_TTHRES			0x08
+#define PVT_VTHRES			0x0C
+#define PVT_LTHRES			0x10
+#define PVT_HTHRES			0x14
+#define PVT_STHRES			0x18
+#define PVT_THRES_LO_FLD		0
+#define PVT_THRES_LO_MASK		GENMASK(9, PVT_THRES_LO_FLD)
+#define PVT_THRES_HI_FLD		10
+#define PVT_THRES_HI_MASK		GENMASK(19, PVT_THRES_HI_FLD)
+#define PVT_TTIMEOUT			0x1C
+#define PVT_INTR_STAT			0x20
+#define PVT_INTR_MASK			0x24
+#define PVT_RAW_INTR_STAT		0x28
+#define PVT_INTR_DVALID			BIT(0)
+#define PVT_INTR_TTHRES_LO		BIT(1)
+#define PVT_INTR_TTHRES_HI		BIT(2)
+#define PVT_INTR_VTHRES_LO		BIT(3)
+#define PVT_INTR_VTHRES_HI		BIT(4)
+#define PVT_INTR_LTHRES_LO		BIT(5)
+#define PVT_INTR_LTHRES_HI		BIT(6)
+#define PVT_INTR_HTHRES_LO		BIT(7)
+#define PVT_INTR_HTHRES_HI		BIT(8)
+#define PVT_INTR_STHRES_LO		BIT(9)
+#define PVT_INTR_STHRES_HI		BIT(10)
+#define PVT_INTR_ALL			GENMASK(10, 0)
+#define PVT_CLR_INTR			0x2C
+
+/*
+ * PVT sensors-related limits and default values
+ * @PVT_TEMP_MIN: Minimal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_MAX: Maximal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_CHS: Number of temperature hwmon channels.
+ * @PVT_VOLT_MIN: Minimal voltage in mV.
+ * @PVT_VOLT_MAX: Maximal voltage in mV.
+ * @PVT_VOLT_CHS: Number of voltage hwmon channels.
+ * @PVT_DATA_MIN: Minimal PVT raw data value.
+ * @PVT_DATA_MAX: Maximal PVT raw data value.
+ * @PVT_TRIM_MIN: Minimal temperature sensor trim value.
+ * @PVT_TRIM_MAX: Maximal temperature sensor trim value.
+ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value
+ *		  when one is determined for Baikal-T1 SoC).
+ * @PVT_TRIM_TEMP: Maximum temperature encoded by the trim factor.
+ * @PVT_TRIM_STEP: Temperature stride corresponding to the trim value.
+ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds.
+ * @PVT_TOUT_DEF: Default data measurements timeout. In case if alarms are
+ *		  activated the PVT IRQ is enabled to be raised after each
+ *		  conversion in order to have the thresholds checked and the
+ *		  converted value cached. Too frequent conversions may cause
+ *		  the system CPU overload. Lets set the 50ms delay between
+ *		  them by default to prevent this.
+ */
+#define PVT_TEMP_MIN		-48380L
+#define PVT_TEMP_MAX		147438L
+#define PVT_TEMP_CHS		1
+#define PVT_VOLT_MIN		620L
+#define PVT_VOLT_MAX		1168L
+#define PVT_VOLT_CHS		4
+#define PVT_DATA_MIN		0
+#define PVT_DATA_MAX		(PVT_DATA_DATA_MASK >> PVT_DATA_DATA_FLD)
+#define PVT_TRIM_MIN		0
+#define PVT_TRIM_MAX		(PVT_CTRL_TRIM_MASK >> PVT_CTRL_TRIM_FLD)
+#define PVT_TRIM_TEMP		7130
+#define PVT_TRIM_STEP		(PVT_TRIM_TEMP / PVT_TRIM_MAX)
+#define PVT_TRIM_DEF		0
+#define PVT_TOUT_MIN		(NSEC_PER_SEC / 3000)
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+# define PVT_TOUT_DEF		60000
+#else
+# define PVT_TOUT_DEF		0
+#endif
+
+/*
+ * enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT
+ *			  sampling mode)
+ * @PVT_SENSOR*: helpers to traverse the sensors in loops.
+ * @PVT_TEMP: PVT Temperature sensor.
+ * @PVT_VOLT: PVT Voltage sensor.
+ * @PVT_LVT: PVT Low-Voltage threshold sensor.
+ * @PVT_HVT: PVT High-Voltage threshold sensor.
+ * @PVT_SVT: PVT Standard-Voltage threshold sensor.
+ */
+enum pvt_sensor_type {
+	PVT_SENSOR_FIRST,
+	PVT_TEMP = PVT_SENSOR_FIRST,
+	PVT_VOLT,
+	PVT_LVT,
+	PVT_HVT,
+	PVT_SVT,
+	PVT_SENSOR_LAST = PVT_SVT,
+	PVT_SENSORS_NUM
+};
+
+/*
+ * enum pvt_clock_type - Baikal-T1 PVT clocks.
+ * @PVT_CLOCK_APB: APB clock.
+ * @PVT_CLOCK_REF: PVT reference clock.
+ */
+enum pvt_clock_type {
+	PVT_CLOCK_APB,
+	PVT_CLOCK_REF,
+	PVT_CLOCK_NUM
+};
+
+/*
+ * struct pvt_sensor_info - Baikal-T1 PVT sensor informational structure
+ * @channel: Sensor channel ID.
+ * @label: hwmon sensor label.
+ * @mode: PVT mode corresponding to the channel.
+ * @thres_base: upper and lower threshold values of the sensor.
+ * @thres_sts_lo: low threshold status bitfield.
+ * @thres_sts_hi: high threshold status bitfield.
+ * @type: Sensor type.
+ * @attr_min_alarm: Min alarm attribute ID.
+ * @attr_min_alarm: Max alarm attribute ID.
+ */
+struct pvt_sensor_info {
+	int channel;
+	const char *label;
+	u32 mode;
+	unsigned long thres_base;
+	u32 thres_sts_lo;
+	u32 thres_sts_hi;
+	enum hwmon_sensor_types type;
+	u32 attr_min_alarm;
+	u32 attr_max_alarm;
+};
+
+#define PVT_SENSOR_INFO(_ch, _label, _type, _mode, _thres)	\
+	{							\
+		.channel = _ch,					\
+		.label = _label,				\
+		.mode = PVT_CTRL_MODE_ ##_mode,			\
+		.thres_base = PVT_ ##_thres,			\
+		.thres_sts_lo = PVT_INTR_ ##_thres## _LO,	\
+		.thres_sts_hi = PVT_INTR_ ##_thres## _HI,	\
+		.type = _type,					\
+		.attr_min_alarm = _type## _min,			\
+		.attr_max_alarm = _type## _max,			\
+	}
+
+/*
+ * struct pvt_cache - PVT sensors data cache
+ * @data: data cache in raw format.
+ * @thres_sts_lo: low threshold status saved on the previous data conversion.
+ * @thres_sts_hi: high threshold status saved on the previous data conversion.
+ * @data_seqlock: cached data seq-lock.
+ * @conversion: data conversion completion.
+ */
+struct pvt_cache {
+	u32 data;
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	seqlock_t data_seqlock;
+	u32 thres_sts_lo;
+	u32 thres_sts_hi;
+#else
+	struct completion conversion;
+#endif
+};
+
+/*
+ * struct pvt_hwmon - Baikal-T1 PVT private data
+ * @dev: device structure of the PVT platform device.
+ * @hwmon: hwmon device structure.
+ * @regs: pointer to the Baikal-T1 PVT registers region.
+ * @irq: PVT events IRQ number.
+ * @clks: Array of the PVT clocks descriptor (APB/ref clocks).
+ * @ref_clk: Pointer to the reference clocks descriptor.
+ * @iface_mtx: Generic interface mutex (used to lock the alarm registers
+ *	       when the alarms enabled, or the data conversion interface
+ *	       if alarms are disabled).
+ * @sensor: current PVT sensor the data conversion is being performed for.
+ * @cache: data cache descriptor.
+ */
+struct pvt_hwmon {
+	struct device *dev;
+	struct device *hwmon;
+
+	void __iomem *regs;
+	int irq;
+
+	struct clk_bulk_data clks[PVT_CLOCK_NUM];
+
+	struct mutex iface_mtx;
+	enum pvt_sensor_type sensor;
+	struct pvt_cache cache[PVT_SENSORS_NUM];
+};
+
+/*
+ * struct pvt_poly_term - a term descriptor of the PVT data translation
+ *			  polynomial
+ * @deg: degree of the term.
+ * @coef: multiplication factor of the term.
+ * @divider: distributed divider per each degree.
+ * @divider_leftover: divider leftover, which couldn't be redistributed.
+ */
+struct pvt_poly_term {
+	unsigned int deg;
+	long coef;
+	long divider;
+	long divider_leftover;
+};
+
+/*
+ * struct pvt_poly - PVT data translation polynomial descriptor
+ * @total_divider: total data divider.
+ * @terms: polynomial terms up to a free one.
+ */
+struct pvt_poly {
+	long total_divider;
+	struct pvt_poly_term terms[];
+};
+
+#endif /* __HWMON_BT1_PVT_H__ */
-- 
2.26.2


^ permalink raw reply related

* [PATCH v4 0/3] hwmon: Add Baikal-T1 SoC Process, Voltage and Temp sensor support
From: Serge Semin @ 2020-05-28 14:28 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Maxim Kaurkin,
	Alexey Malahov, Pavel Parkhomenko, Ramil Zaripov,
	Ekaterina Skachko, Vadim Vlasov, Alexey Kolotnikov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	linux-hwmon, devicetree, linux-kernel

In order to keep track of Baikal-T1 SoC power consumption and make sure
the chip heating is within the normal temperature limits, there is
a dedicated hardware monitor sensor embedded into the SoC. It is based
on the Analog Bits PVT sensor but equipped with a vendor-specific control
wrapper, which ease an access to the sensors functionality. Fist of all it
provides an accessed to the sampled Temperature, Voltage and
Low/Standard/High Voltage thresholds. In addition the wrapper generates
an interrupt in case if one enabled for alarm thresholds or data ready
event. All of these functionality is implemented in the Baikal-T1 PVT
driver submitted within this patchset. Naturally there is also a patch,
which creates a corresponding yaml-based dt-binding file for the sensor.

This patchset is rebased and tested on the mainline Linux kernel 5.6-rc4:
base-commit: 0e698dfa2822 ("Linux 5.7-rc4")
tag: v5.7-rc4

Note new vendor prefix for Baikal-T1 PVT device will be added in the
framework of the next patchset:
https://lkml.org/lkml/2020/5/6/1047

Changelog v2:
- Don't use a multi-arg clock phandle reference in the examples dt-bindings
  property. Thus reundant include pre-processor statement can be removed.
- Rearrange the SoBs with adding Maxim' co-development tag.
- Lowercase the node-name in the dt-schema example.
- Add dual license header to the dt-bindings file.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Discard label definition from the binding example.
- Discard handwritten IO-access wrappers. Use normal readl/writel instead.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Remove probe-status info string printout.
- Our corporate email server doesn't change Message-Id anymore, so the patchset
  is resubmitted being in the cover-letter-threaded format.

Link: https://lore.kernel.org/linux-hwmon/20200510103211.27905-1-Sergey.Semin@baikalelectronics.ru/
Changelog v3:
- Add bt1-pvt into the Documentation/hwmon/index.rst file.
- Discard explicit "default n" from the SENSORS_BT1_PVT_ALARMS config.
- Use "depends on SENSORS_BT1_PVT" statement instead of if-endif kbuild
  config clause.
- Alphabetically order the include macro operators.
- Discard unneeded include macro in the header file.
- Use new generic interface of the hwmon alarms notifications introduced
  in the first patch (based on hwmon_notify_event()).
- Add more descriptive information regarding the temp1_trim attribute.
- Discard setting the platforms device private data by using
  platform_set_drvdata(). It's redundant since unused in the driver.
- Pass "pvt" hwmon name instead of dev_name(dev) to
  devm_hwmon_device_register_with_info().
- Add "baikal,pvt-temp-trim-millicelsius" temperature trim DT property
  support.
- Discard kernel log warnings printed from the ISR when either min or
  max threshold levels are crossed.
- Discard CONFIG_OF dependency since there is non at compile-time.

Link: https://lore.kernel.org/linux-hwmon/20200526133823.20466-1-Sergey.Semin@baikalelectronics.ru
Changelog v4:
- Rename temp1_trim to the temp1_offset and use the standard API to
  expose the attribute.
- Rename "baikal,pvt-temp-trim-millicelsius" DT property to
  "baikal,pvt-temp-offset-millicelsius".
- Switch "const static" order to be "static const" where it's applicable.
- Add missing headers "linux/io.h" and "linux/of.h".
- Add static qualifier to the pvt_hwmon_write() method, which has been
  missed there by mistake.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-hwmon@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Guenter Roeck (1):
  hwmon: Add notification support

Serge Semin (2):
  dt-bindings: hwmon: Add Baikal-T1 PVT sensor binding
  hwmon: Add Baikal-T1 PVT sensor driver

 .../bindings/hwmon/baikal,bt1-pvt.yaml        |  107 ++
 Documentation/hwmon/bt1-pvt.rst               |  117 ++
 Documentation/hwmon/index.rst                 |    1 +
 drivers/hwmon/Kconfig                         |   25 +
 drivers/hwmon/Makefile                        |    1 +
 drivers/hwmon/bt1-pvt.c                       | 1146 +++++++++++++++++
 drivers/hwmon/bt1-pvt.h                       |  244 ++++
 drivers/hwmon/hwmon.c                         |   69 +-
 include/linux/hwmon.h                         |    3 +
 9 files changed, 1710 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
 create mode 100644 Documentation/hwmon/bt1-pvt.rst
 create mode 100644 drivers/hwmon/bt1-pvt.c
 create mode 100644 drivers/hwmon/bt1-pvt.h

-- 
2.26.2


^ permalink raw reply

* [PATCH v4 2/3] hwmon: Add notification support
From: Serge Semin @ 2020-05-28 14:28 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Alexey Malahov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	devicetree, linux-hwmon, linux-kernel
In-Reply-To: <20200528142805.29115-1-Sergey.Semin@baikalelectronics.ru>

From: Guenter Roeck <linux@roeck-us.net>

For hwmon drivers using the hwmon_device_register_with_info() API, it
is desirable to have a generic notification mechanism available. This
mechanism can be used to notify userspace as well as the thermal
subsystem if the driver experiences any events, such as warning or
critical alarms.

Implement hwmon_notify_event() to provide this mechanism. The function
generates a sysfs event and a udev event. If the device is registered
with the thermal subsystem and the event is associated with a temperature
sensor, also notify the thermal subsystem that a thermal event occurred.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org

---

Guenter: I have no plan to push this since there are currently no users
(and thus no means to test the code). However, I want to make sure that
the patch is available if/when needed.
Serge: There is a user now. It's Baikal-T1 PVT driver. So the patch can
be merged in.

Changelog v2: Skipped

Changelog v3:
- Fix merge conflicts found in struct hwmon_thermal_data and in
  hwmon_thermal_add_sensors().
- Use hwmon_attr_base() to get the hwmon attribute base index.
- Use the base value to get the sysfs-attribute name.
  hwmon_thermal_notify() is supposed to be called with normal index.
---
 drivers/hwmon/hwmon.c | 69 +++++++++++++++++++++++++++++++++++++++++--
 include/linux/hwmon.h |  3 ++
 2 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 6a30fb453f7a..e9bc33c18a33 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -15,6 +15,7 @@
 #include <linux/gfp.h>
 #include <linux/hwmon.h>
 #include <linux/idr.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -31,7 +32,7 @@ struct hwmon_device {
 	const char *name;
 	struct device dev;
 	const struct hwmon_chip_info *chip;
-
+	struct list_head tzdata;
 	struct attribute_group group;
 	const struct attribute_group **groups;
 };
@@ -55,12 +56,12 @@ struct hwmon_device_attribute {
 
 /*
  * Thermal zone information
- * In addition to the reference to the hwmon device,
- * also provides the sensor index.
  */
 struct hwmon_thermal_data {
+	struct list_head node;		/* hwmon tzdata list entry */
 	struct device *dev;		/* Reference to hwmon device */
 	int index;			/* sensor index */
+	struct thermal_zone_device *tzd;/* thermal zone device */
 };
 
 static ssize_t
@@ -156,10 +157,17 @@ static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
 	.get_temp = hwmon_thermal_get_temp,
 };
 
+static void hwmon_thermal_remove_sensor(void *data)
+{
+	list_del(data);
+}
+
 static int hwmon_thermal_add_sensor(struct device *dev, int index)
 {
+	struct hwmon_device *hwdev = to_hwmon_device(dev);
 	struct hwmon_thermal_data *tdata;
 	struct thermal_zone_device *tzd;
+	int err;
 
 	tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
 	if (!tdata)
@@ -177,13 +185,37 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index)
 	if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
 		return PTR_ERR(tzd);
 
+	err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node);
+	if (err)
+		return err;
+
+	tdata->tzd = tzd;
+	list_add(&tdata->node, &hwdev->tzdata);
+
 	return 0;
 }
+
+static void hwmon_thermal_notify(struct device *dev, int index)
+{
+	struct hwmon_device *hwdev = to_hwmon_device(dev);
+	struct hwmon_thermal_data *tzdata;
+
+	list_for_each_entry(tzdata, &hwdev->tzdata, node) {
+		if (tzdata->index == index) {
+			thermal_zone_device_update(tzdata->tzd,
+						   THERMAL_EVENT_UNSPECIFIED);
+		}
+	}
+}
+
 #else
 static int hwmon_thermal_add_sensor(struct device *dev, int index)
 {
 	return 0;
 }
+
+static void hwmon_thermal_notify(struct device *dev, int index) { }
+
 #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
 
 static int hwmon_attr_base(enum hwmon_sensor_types type)
@@ -511,6 +543,35 @@ static const int __templates_size[] = {
 	[hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates),
 };
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel)
+{
+	char sattr[MAX_SYSFS_ATTR_NAME_LENGTH];
+	const char * const *templates;
+	const char *template;
+	int base;
+
+	if (type >= ARRAY_SIZE(__templates))
+		return -EINVAL;
+	if (attr >= __templates_size[type])
+		return -EINVAL;
+
+	templates = __templates[type];
+	template = templates[attr];
+
+	base = hwmon_attr_base(type);
+
+	scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel);
+	sysfs_notify(&dev->kobj, NULL, sattr);
+	kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+
+	if (type == hwmon_temp)
+		hwmon_thermal_notify(dev, channel);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hwmon_notify_event);
+
 static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
 {
 	int i, n;
@@ -661,6 +722,8 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
 	if (err)
 		goto free_hwmon;
 
+	INIT_LIST_HEAD(&hwdev->tzdata);
+
 	if (dev && dev->of_node && chip && chip->ops->read &&
 	    chip->info[0]->type == hwmon_chip &&
 	    (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 5e609f25878c..363d4a814aa1 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -436,6 +436,9 @@ devm_hwmon_device_register_with_info(struct device *dev,
 void hwmon_device_unregister(struct device *dev);
 void devm_hwmon_device_unregister(struct device *dev);
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel);
+
 /**
  * hwmon_is_bad_char - Is the char invalid in a hwmon name
  * @ch: the char to be considered
-- 
2.26.2


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox