[PATCH v5 1/1] iio: adc: ad7124: allow more than 8 channels

2021-03-11 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.
If multiple channels have the same configuration, only
one configuration register will be used. If there
are more configurations than available registers only
the last 8 used configurations will be allowed to exist
on the device in a LRU fashion.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/adc/ad7124.c | 458 +--
 1 file changed, 301 insertions(+), 157 deletions(-)

diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 766c7604..8b5b223938e8 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -5,12 +5,14 @@
  * Copyright 2018 Analog Devices Inc.
  */
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -86,6 +88,10 @@
 #define AD7124_SINC3_FILTER 2
 #define AD7124_SINC4_FILTER 0
 
+#define AD7124_CONF_ADDR_OFFSET20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS16
+
 enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@@ -136,25 +142,37 @@ struct ad7124_chip_info {
 };
 
 struct ad7124_channel_config {
+   bool live;
+   unsigned int cfg_slot;
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
-   unsigned int ain;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
+   unsigned int odr_sel_bits;
unsigned int filter_type;
 };
 
+struct ad7124_channel {
+   unsigned int nr;
+   struct ad7124_channel_config cfg;
+   unsigned int ain;
+   unsigned int slot;
+};
+
 struct ad7124_state {
const struct ad7124_chip_info *chip_info;
struct ad_sigma_delta sd;
-   struct ad7124_channel_config *channel_config;
+   struct ad7124_channel *channels;
struct regulator *vref[4];
struct clk *mclk;
unsigned int adc_control;
unsigned int num_channels;
+   struct mutex cfgs_lock; /* lock for configs access */
+   unsigned long cfg_slots_status; /* bitmap with slot status (1 means it 
is used) */
+   DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, 
AD7124_MAX_CONFIGS);
 };
 
 static const struct iio_chan_spec ad7124_channel_template = {
@@ -238,33 +256,9 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd,
return ad_sd_write_reg(>sd, AD7124_ADC_CONTROL, 2, st->adc_control);
 }
 
-static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
-{
-   struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
-   unsigned int val;
-
-   val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
- AD7124_CHANNEL_SETUP(channel);
-
-   return ad_sd_write_reg(>sd, AD7124_CHANNEL(channel), 2, val);
-}
-
-static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
-   .set_channel = ad7124_set_channel,
-   .set_mode = ad7124_set_mode,
-   .has_registers = true,
-   .addr_shift = 0,
-   .read_mask = BIT(6),
-   .data_reg = AD7124_DATA,
-   .irq_flags = IRQF_TRIGGER_FALLING,
-};
-
-static int ad7124_set_channel_odr(struct ad7124_state *st,
- unsigned int channel,
- unsigned int odr)
+static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int 
channel, unsigned int odr)
 {
unsigned int fclk, odr_sel_bits;
-   int ret;
 
fclk = clk_get_rate(st->mclk);
/*
@@ -280,36 +274,12 @@ static int ad7124_set_channel_odr(struct ad7124_state *st,
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
 
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
-   AD7124_FILTER_FS_MSK,
-   AD7124_FILTER_FS(odr_sel_bits), 3);
-   if (ret < 0)
-   return ret;
-   /* fADC = fCLK / (FS[10:0] x 32) */
-   st->channel_config[channel].odr =
-   DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
-
-   return 0;
-}
-
-static int ad7124_set_channel_gain(struct ad7124_state *st,
-  unsigned int channel,
-  unsigned int gain)
-{
-   unsigned int res;
-   int ret;
-
-   res = ad7124_find_closest_match(ad7124_gain,
-   ARRAY_SIZE(ad7124_gain), gain);
-   ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
-   

[PATCH v5 0/1] iio: adc: ad7124: allow more than 8 channels

2021-03-11 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

All configurations are marked as live if they are
programmed on the device. Any change that happens from
userspace (sampling rate, filters etc.) will invalidate
them.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.

If multiple channels have the same configuration, only
one configuration register will be used.

If there are more configurations needed than available registers
only the last 8 used configurations will be allowed to exist
on the device in a LRU fashion. (in case of raw reads).

If a read is requested on a channel whose configuration
is not programmed:
- check if there are similar configurations already programmed
if yes: - point channel to that config
if no:  - check if there are empty config slots
- if yes: write config, push into queue of LRU configs
- if no: pop one config, get it's config slot nr,
write new config on the old slot, push new config
in queue of LRU configs.

Alexandru Tachici (1):
  iio: adc: ad7124: allow more than 8 channels

Changelog v4 -> v5:
- replaced ad7124_configs_equal function with a memcmp
in order to check if two configs are equal
- moved members that do not count to equality at the
end of the ad7124_channel_config struct (live, cfg_slot)

 drivers/iio/adc/ad7124.c | 458 +--
 1 file changed, 301 insertions(+), 157 deletions(-)

-- 
2.20.1



[PATCH v4 1/1] iio: adc: ad7124: allow more than 8 channels

2021-03-01 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.
If multiple channels have the same configuration, only
one configuration register will be used. If there
are more configurations than available registers only
the last 8 used configurations will be allowed to exist
on the device in a LRU fashion.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/adc/ad7124.c | 470 ++-
 1 file changed, 313 insertions(+), 157 deletions(-)

diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 766c7604..9588df981e4e 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -5,12 +5,14 @@
  * Copyright 2018 Analog Devices Inc.
  */
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -86,6 +88,10 @@
 #define AD7124_SINC3_FILTER 2
 #define AD7124_SINC4_FILTER 0
 
+#define AD7124_CONF_ADDR_OFFSET20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS16
+
 enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@@ -136,25 +142,37 @@ struct ad7124_chip_info {
 };
 
 struct ad7124_channel_config {
+   bool live;
+   unsigned int cfg_slot;
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
-   unsigned int ain;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
+   unsigned int odr_sel_bits;
unsigned int filter_type;
 };
 
+struct ad7124_channel {
+   unsigned int nr;
+   struct ad7124_channel_config cfg;
+   unsigned int ain;
+   unsigned int slot;
+};
+
 struct ad7124_state {
const struct ad7124_chip_info *chip_info;
struct ad_sigma_delta sd;
-   struct ad7124_channel_config *channel_config;
+   struct ad7124_channel *channels;
struct regulator *vref[4];
struct clk *mclk;
unsigned int adc_control;
unsigned int num_channels;
+   struct mutex cfgs_lock; /* lock for configs access */
+   unsigned long cfg_slots_status; /* bitmap with slot status (1 means it 
is used) */
+   DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, 
AD7124_MAX_CONFIGS);
 };
 
 static const struct iio_chan_spec ad7124_channel_template = {
@@ -238,33 +256,9 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd,
return ad_sd_write_reg(>sd, AD7124_ADC_CONTROL, 2, st->adc_control);
 }
 
-static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
-{
-   struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
-   unsigned int val;
-
-   val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
- AD7124_CHANNEL_SETUP(channel);
-
-   return ad_sd_write_reg(>sd, AD7124_CHANNEL(channel), 2, val);
-}
-
-static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
-   .set_channel = ad7124_set_channel,
-   .set_mode = ad7124_set_mode,
-   .has_registers = true,
-   .addr_shift = 0,
-   .read_mask = BIT(6),
-   .data_reg = AD7124_DATA,
-   .irq_flags = IRQF_TRIGGER_FALLING,
-};
-
-static int ad7124_set_channel_odr(struct ad7124_state *st,
- unsigned int channel,
- unsigned int odr)
+static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int 
channel, unsigned int odr)
 {
unsigned int fclk, odr_sel_bits;
-   int ret;
 
fclk = clk_get_rate(st->mclk);
/*
@@ -280,36 +274,12 @@ static int ad7124_set_channel_odr(struct ad7124_state *st,
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
 
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
-   AD7124_FILTER_FS_MSK,
-   AD7124_FILTER_FS(odr_sel_bits), 3);
-   if (ret < 0)
-   return ret;
-   /* fADC = fCLK / (FS[10:0] x 32) */
-   st->channel_config[channel].odr =
-   DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
-
-   return 0;
-}
-
-static int ad7124_set_channel_gain(struct ad7124_state *st,
-  unsigned int channel,
-  unsigned int gain)
-{
-   unsigned int res;
-   int ret;
+   if (odr_sel_bits != st->channels[channel].cfg.odr_sel_bits)
+   st->channels[channel].cfg.live = false;
 
-   res = ad7124_find_closest_match(ad7124_gain,
-   

[PATCH v4 0/1] iio: adc: ad7124: allow more than 8 channels

2021-03-01 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

All configurations are marked as live if they are
programmed on the device. Any change that happens from
userspace (sampling rate, filters etc.) will invalidate
them.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.

If multiple channels have the same configuration, only
one configuration register will be used.

If there are more configurations needed than available registers
only the last 8 used configurations will be allowed to exist
on the device in a LRU fashion. (in case of raw reads).

If a read is requested on a channel whose configuration
is not programmed:
- check if there are similar configurations already programmed
if yes: - point channel to that config
if no:  - check if there are empty config slots
- if yes: write config, push into queue of LRU configs
- if no: pop one config, get it's config slot nr,
write new config on the old slot, push new config
in queue of LRU configs.

Alexandru Tachici (1):
  iio: adc: ad7124: allow more than 8 channels

Changelog v3 -> v4:
- fixed multi-line comments
- moved locking and unlocking out of the switch/case in write_raw
- check if there were any actual changes in write_raw to the
sample frequency/filter/scale by comparing old/new values
before marking a config. as changed
- maintain a bitmap in order to keep track of empty slots
on the device

 drivers/iio/adc/ad7124.c | 470 ++-
 1 file changed, 313 insertions(+), 157 deletions(-)

-- 
2.20.1



[PATCH v3 0/1] iio: adc: ad7124: allow more than 8 channels

2021-02-23 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

All configurations are marked as live if they are
programmed on the device. Any change that happens from
userspace (sampling rate, filters etc.) will invalidate
them.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.

If multiple channels have the same configuration, only
one configuration register will be used.

If there are more configurations needed than available registers
only the last 8 used configurations will be allowed to exist
on the device in a LRU fashion. (in case of raw reads).

If a read is requested on a channel whose configuration
is not programmed:
- check if there are similar configurations already programmed
if yes: - point channel to that config
if no:  - check if there are empty config slots
- if yes: write config, push into queue of LRU configs
- if no: pop one config, get it's config slot nr,
write new config on the old slot, push new config
in queue of LRU configs.

Alexandru Tachici (1):
  iio: adc: ad7124: allow more than 8 channels

 drivers/iio/adc/ad7124.c | 461 ++-
 1 file changed, 308 insertions(+), 153 deletions(-)

-- 
2.20.1



[PATCH v3 1/1] iio: adc: ad7124: allow more than 8 channels

2021-02-23 Thread alexandru.tachici
From: Alexandru Tachici 

Currently AD7124-8 driver cannot use more than 8 IIO channels
because it was assigning the channel configurations bijectively
to channels specified in the device-tree. This is not possible
to do when using more than 8 channels as AD7124-8 has only 8
configuration registers.

To allow the user to use all channels at once the driver
will keep in memory configurations for all channels but
will program only 8 of them at a time on the device.
If multiple channels have the same configuration, only
one configuration register will be used. If there
are more configurations than available registers only
the last 8 used configurations will be allowed to exist
on the device in a LRU fashion.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/adc/ad7124.c | 461 ++-
 1 file changed, 308 insertions(+), 153 deletions(-)

diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 766c7604..d5d0596eb6ed 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -11,6 +11,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -86,6 +87,10 @@
 #define AD7124_SINC3_FILTER 2
 #define AD7124_SINC4_FILTER 0
 
+#define AD7124_CONF_ADDR_OFFSET20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS16
+
 enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@@ -136,25 +141,36 @@ struct ad7124_chip_info {
 };
 
 struct ad7124_channel_config {
+   bool live;
+   unsigned int cfg_slot;
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
-   unsigned int ain;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
+   unsigned int odr_sel_bits;
unsigned int filter_type;
 };
 
+struct ad7124_channel {
+   unsigned int nr;
+   struct ad7124_channel_config cfg;
+   unsigned int ain;
+   unsigned int slot;
+};
+
 struct ad7124_state {
const struct ad7124_chip_info *chip_info;
struct ad_sigma_delta sd;
-   struct ad7124_channel_config *channel_config;
+   struct ad7124_channel *channels;
struct regulator *vref[4];
struct clk *mclk;
unsigned int adc_control;
unsigned int num_channels;
+   struct mutex cfgs_lock; /* lock for configs access */
+   DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, 
AD7124_MAX_CONFIGS);
 };
 
 static const struct iio_chan_spec ad7124_channel_template = {
@@ -238,33 +254,9 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd,
return ad_sd_write_reg(>sd, AD7124_ADC_CONTROL, 2, st->adc_control);
 }
 
-static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
-{
-   struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
-   unsigned int val;
-
-   val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
- AD7124_CHANNEL_SETUP(channel);
-
-   return ad_sd_write_reg(>sd, AD7124_CHANNEL(channel), 2, val);
-}
-
-static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
-   .set_channel = ad7124_set_channel,
-   .set_mode = ad7124_set_mode,
-   .has_registers = true,
-   .addr_shift = 0,
-   .read_mask = BIT(6),
-   .data_reg = AD7124_DATA,
-   .irq_flags = IRQF_TRIGGER_FALLING,
-};
-
-static int ad7124_set_channel_odr(struct ad7124_state *st,
- unsigned int channel,
- unsigned int odr)
+static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int 
channel, unsigned int odr)
 {
unsigned int fclk, odr_sel_bits;
-   int ret;
 
fclk = clk_get_rate(st->mclk);
/*
@@ -280,36 +272,10 @@ static int ad7124_set_channel_odr(struct ad7124_state *st,
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
 
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
-   AD7124_FILTER_FS_MSK,
-   AD7124_FILTER_FS(odr_sel_bits), 3);
-   if (ret < 0)
-   return ret;
-   /* fADC = fCLK / (FS[10:0] x 32) */
-   st->channel_config[channel].odr =
-   DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
-
-   return 0;
-}
-
-static int ad7124_set_channel_gain(struct ad7124_state *st,
-  unsigned int channel,
-  unsigned int gain)
-{
-   unsigned int res;
-   int ret;
-
-   res = ad7124_find_closest_match(ad7124_gain,
-   ARRAY_SIZE(ad7124_gain), gain);
-   ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
-   AD7124_CONFIG_PGA_MSK,
-   AD7124_CONFIG_PGA(res), 2);
-   if (ret < 0)
-   return ret;
-
-   st->channel_config[channel].pga_bits = res;
 
-   

[PATCH v2 0/2] iio: adc: ad7124: allow 16 channels

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

AD7124-8 can have up to 16 pseudo-differential channels
enabled simultaneously and only 8 configurations. In this
scenario we cannot assign one configuration per channel,
some channels will have to share configurations like, ODR,
gain and filter parameters.

Allow the user to specify channels and configurations
separately in device-tree and assign, if needed, the same
configuration to multiple channels.

If two channels share the configuration changing the
sampling rate of one will change the sampling rate of the
other too.

Alexandru Tachici (2):
  iio: adc: ad7124: allow 16 channels
  dt-bindings: iio: adc: ad7124: add config nodes

 .../bindings/iio/adc/adi,ad7124.yaml  |  72 +--
 drivers/iio/adc/ad7124.c  | 183 +++---
 2 files changed, 166 insertions(+), 89 deletions(-)

-- 
2.20.1



[PATCH v2 2/2] dt-bindings: iio: adc: ad7124: add config nodes

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

Document use of configurations in device-tree bindings.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/iio/adc/adi,ad7124.yaml  | 72 +++
 1 file changed, 57 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml 
b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
index fb3d0dae9bae..330064461d0a 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
@@ -62,20 +62,19 @@ required:
   - interrupts
 
 patternProperties:
-  "^channel@([0-9]|1[0-5])$":
-$ref: "adc.yaml"
+  "^config@(2[0-7])$":
 type: object
 description: |
-  Represents the external channels which are connected to the ADC.
+  Represents a channel configuration.
+  See Documentation/devicetree/bindings/iio/adc/adc.txt.
 
 properties:
   reg:
 description: |
-  The channel number. It can have up to 8 channels on ad7124-4
-  and 16 channels on ad7124-8, numbered from 0 to 15.
+  The config number. It can have up to 8 configuration.
 items:
-  minimum: 0
-  maximum: 15
+ minimum: 20
+ maximum: 27
 
   adi,reference-select:
 description: |
@@ -88,8 +87,6 @@ patternProperties:
 $ref: /schemas/types.yaml#/definitions/uint32
 enum: [0, 1, 3]
 
-  diff-channels: true
-
   bipolar: true
 
   adi,buffered-positive:
@@ -100,6 +97,35 @@ patternProperties:
 description: Enable buffered mode for negative input.
 type: boolean
 
+additionalProperties: false
+
+  "^channel@([0-9]|1[0-5])$":
+type: object
+description: |
+  Represents the external channels which are connected to the ADC.
+  See Documentation/devicetree/bindings/iio/adc/adc.txt.
+
+properties:
+  reg:
+description: |
+  The channel number. It can have up to 8 channels on ad7124-4
+  and 16 channels on ad7124-8, numbered from 0 to 15.
+items:
+ minimum: 0
+ maximum: 15
+
+  diff-channels: true
+
+  adi,configuration:
+description: |
+  The devices has 8 configuration and ad7124-8 support up to 16 
unipolar channels.
+  Each channel can be assigned one configuration. Some channels will 
be sharing the
+  same configuration.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+minimum: 20
+maximum: 27
+
 required:
   - reg
   - diff-channels
@@ -127,30 +153,46 @@ examples:
 #address-cells = <1>;
 #size-cells = <0>;
 
-channel@0 {
-  reg = <0>;
-  diff-channels = <0 1>;
+config@20 {
+  reg = <20>;
   adi,reference-select = <0>;
   adi,buffered-positive;
 };
 
-channel@1 {
-  reg = <1>;
+config@21 {
+  reg = <21>;
   bipolar;
-  diff-channels = <2 3>;
   adi,reference-select = <0>;
   adi,buffered-positive;
   adi,buffered-negative;
 };
 
+config@22 {
+  reg = <22>;
+};
+
+channel@0 {
+  reg = <0>;
+  diff-channels = <0 1>;
+  adi,configuration = <20>;
+};
+
+channel@1 {
+  reg = <1>;
+  diff-channels = <2 3>;
+  adi,configuration = <21>;
+};
+
 channel@2 {
   reg = <2>;
   diff-channels = <4 5>;
+  adi,configuration = <22>;
 };
 
 channel@3 {
   reg = <3>;
   diff-channels = <6 7>;
+  adi,configuration = <22>;
 };
   };
 };
-- 
2.20.1



[PATCH v2 1/2] iio: adc: ad7124: allow 16 channels

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

AD7124-8 can have up to 16 pseudo-differential channels
enabled simultaneously and only 8 configurations. In this
scenario we cannot assign one configuration per channel,
some channels will have to share configurations like, ODR,
gain and filter parameters.

This patch allows the user to specify channels and configurations
separately in device-tree and assign, if needed, the same
configuration to multiple channels.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/adc/ad7124.c | 183 +++
 1 file changed, 109 insertions(+), 74 deletions(-)

diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 766c7604..0df88bea336f 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -86,6 +86,12 @@
 #define AD7124_SINC3_FILTER 2
 #define AD7124_SINC4_FILTER 0
 
+#define AD7124_CONF_ADDR_OFFSET20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS16
+
+#define AD7124_REG_NO 57
+
 enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@@ -136,21 +142,28 @@ struct ad7124_chip_info {
 };
 
 struct ad7124_channel_config {
+   bool enable;
+   unsigned int nr;
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
-   unsigned int ain;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
unsigned int filter_type;
 };
 
+struct ad7124_channel {
+   struct ad7124_channel_config *cfg;
+   unsigned int ain;
+};
+
 struct ad7124_state {
const struct ad7124_chip_info *chip_info;
struct ad_sigma_delta sd;
-   struct ad7124_channel_config *channel_config;
+   struct ad7124_channel channels[AD7124_MAX_CHANNELS];
+   struct ad7124_channel_config configs[AD7124_MAX_CONFIGS];
struct regulator *vref[4];
struct clk *mclk;
unsigned int adc_control;
@@ -243,8 +256,8 @@ static int ad7124_set_channel(struct ad_sigma_delta *sd, 
unsigned int channel)
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
unsigned int val;
 
-   val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
- AD7124_CHANNEL_SETUP(channel);
+   val = st->channels[channel].ain | AD7124_CHANNEL_EN(1) |
+ AD7124_CHANNEL_SETUP(st->channels[channel].cfg->nr);
 
return ad_sd_write_reg(>sd, AD7124_CHANNEL(channel), 2, val);
 }
@@ -280,14 +293,13 @@ static int ad7124_set_channel_odr(struct ad7124_state *st,
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
 
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
+   ret = ad7124_spi_write_mask(st, 
AD7124_FILTER(st->channels[channel].cfg->nr),
AD7124_FILTER_FS_MSK,
AD7124_FILTER_FS(odr_sel_bits), 3);
if (ret < 0)
return ret;
/* fADC = fCLK / (FS[10:0] x 32) */
-   st->channel_config[channel].odr =
-   DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
+   st->channels[channel].cfg->odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 
32);
 
return 0;
 }
@@ -301,13 +313,13 @@ static int ad7124_set_channel_gain(struct ad7124_state 
*st,
 
res = ad7124_find_closest_match(ad7124_gain,
ARRAY_SIZE(ad7124_gain), gain);
-   ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
+   ret = ad7124_spi_write_mask(st, 
AD7124_CONFIG(st->channels[channel].cfg->nr),
AD7124_CONFIG_PGA_MSK,
AD7124_CONFIG_PGA(res), 2);
if (ret < 0)
return ret;
 
-   st->channel_config[channel].pga_bits = res;
+   st->channels[channel].cfg->pga_bits = res;
 
return 0;
 }
@@ -317,9 +329,9 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state 
*st,
 {
unsigned int fadc;
 
-   fadc = st->channel_config[channel].odr;
+   fadc = st->channels[channel].cfg->odr;
 
-   switch (st->channel_config[channel].filter_type) {
+   switch (st->channels[channel].cfg->filter_type) {
case AD7124_SINC3_FILTER:
return DIV_ROUND_CLOSEST(fadc * 230, 1000);
case AD7124_SINC4_FILTER:
@@ -349,11 +361,11 @@ static int ad7124_set_3db_filter_freq(struct ad7124_state 
*st,
new_odr = sinc3_3db_odr;
}
 
-   if (st->channel_config[channel].filter_type != new_filter) {
+   if (st->channels[channel].cfg->filter_type != new_filter) {
int ret;
 
-   st->channel_config[channel].filter_type = new_filter;
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
+   st->channels[channel].cfg->filter_type = new_filter;
+   ret = ad7124_spi_write_mask(st, 
AD7124_FILTER(st->channels[channel].cfg->nr),

[PATCH 1/2] iio: adc: ad7124: allow 16 channels

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

AD7124-8 can have up to 16 pseudo-differential channels
enabled simultaneously and only 8 configurations. In this
scenario we cannot assign one configuration per channel,
some channels will have to share configurations like, ODR,
gain and filter parameters.

This patch allows the user to specify channels and configurations
separately in device-tree and assign, if needed, the same
configuration to multiple channels.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/adc/ad7124.c | 196 ---
 1 file changed, 122 insertions(+), 74 deletions(-)

diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 766c7604..358e55e6dc1d 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -86,6 +86,12 @@
 #define AD7124_SINC3_FILTER 2
 #define AD7124_SINC4_FILTER 0
 
+#define AD7124_CONF_ADDR_OFFSET20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS16
+
+#define AD7124_REG_NO 57
+
 enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@@ -136,21 +142,29 @@ struct ad7124_chip_info {
 };
 
 struct ad7124_channel_config {
+   bool enable;
+   unsigned int nr;
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
-   unsigned int ain;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
unsigned int filter_type;
 };
 
+struct ad7124_channel {
+   struct ad7124_channel_config *cfg;
+   unsigned int ain;
+   unsigned int slot;
+};
+
 struct ad7124_state {
const struct ad7124_chip_info *chip_info;
struct ad_sigma_delta sd;
-   struct ad7124_channel_config *channel_config;
+   struct ad7124_channel channels[AD7124_MAX_CHANNELS];
+   struct ad7124_channel_config configs[AD7124_MAX_CONFIGS];
struct regulator *vref[4];
struct clk *mclk;
unsigned int adc_control;
@@ -242,9 +256,21 @@ static int ad7124_set_channel(struct ad_sigma_delta *sd, 
unsigned int channel)
 {
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
unsigned int val;
+   int i;
 
-   val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
- AD7124_CHANNEL_SETUP(channel);
+   if (channel == AD_SD_SLOT_DISABLE) {
+   for (i = 0; i < AD7124_MAX_CHANNELS; i++) {
+   /* disable channel associated with unused slot */
+   if (st->channels[i].slot == slot)
+   return ad_sd_write_reg(>sd, 
AD7124_CHANNEL(i), 2, 0);
+   }
+
+   return 0;
+   }
+
+   st->channels[channel].slot = slot;
+   val = st->channels[channel].ain | AD7124_CHANNEL_EN(1) |
+ AD7124_CHANNEL_SETUP(st->channels[channel].cfg->nr);
 
return ad_sd_write_reg(>sd, AD7124_CHANNEL(channel), 2, val);
 }
@@ -280,14 +306,13 @@ static int ad7124_set_channel_odr(struct ad7124_state *st,
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
 
-   ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
+   ret = ad7124_spi_write_mask(st, 
AD7124_FILTER(st->channels[channel].cfg->nr),
AD7124_FILTER_FS_MSK,
AD7124_FILTER_FS(odr_sel_bits), 3);
if (ret < 0)
return ret;
/* fADC = fCLK / (FS[10:0] x 32) */
-   st->channel_config[channel].odr =
-   DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
+   st->channels[channel].cfg->odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 
32);
 
return 0;
 }
@@ -301,13 +326,13 @@ static int ad7124_set_channel_gain(struct ad7124_state 
*st,
 
res = ad7124_find_closest_match(ad7124_gain,
ARRAY_SIZE(ad7124_gain), gain);
-   ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
+   ret = ad7124_spi_write_mask(st, 
AD7124_CONFIG(st->channels[channel].cfg->nr),
AD7124_CONFIG_PGA_MSK,
AD7124_CONFIG_PGA(res), 2);
if (ret < 0)
return ret;
 
-   st->channel_config[channel].pga_bits = res;
+   st->channels[channel].cfg->pga_bits = res;
 
return 0;
 }
@@ -317,9 +342,9 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state 
*st,
 {
unsigned int fadc;
 
-   fadc = st->channel_config[channel].odr;
+   fadc = st->channels[channel].cfg->odr;
 
-   switch (st->channel_config[channel].filter_type) {
+   switch (st->channels[channel].cfg->filter_type) {
case AD7124_SINC3_FILTER:
return DIV_ROUND_CLOSEST(fadc * 230, 1000);
case AD7124_SINC4_FILTER:
@@ -349,11 +374,11 @@ static int ad7124_set_3db_filter_freq(struct ad7124_state 
*st,
new_odr = sinc3_3db_odr;
}
 
-   if (st->channel_config[channel].filter_type != 

[PATCH 2/2] dt-bindings: iio: adc: ad7124: add config nodes

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

Document use of configurations in device-tree bindings.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/iio/adc/adi,ad7124.yaml  | 72 +++
 1 file changed, 57 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml 
b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
index fb3d0dae9bae..330064461d0a 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
@@ -62,20 +62,19 @@ required:
   - interrupts
 
 patternProperties:
-  "^channel@([0-9]|1[0-5])$":
-$ref: "adc.yaml"
+  "^config@(2[0-7])$":
 type: object
 description: |
-  Represents the external channels which are connected to the ADC.
+  Represents a channel configuration.
+  See Documentation/devicetree/bindings/iio/adc/adc.txt.
 
 properties:
   reg:
 description: |
-  The channel number. It can have up to 8 channels on ad7124-4
-  and 16 channels on ad7124-8, numbered from 0 to 15.
+  The config number. It can have up to 8 configuration.
 items:
-  minimum: 0
-  maximum: 15
+ minimum: 20
+ maximum: 27
 
   adi,reference-select:
 description: |
@@ -88,8 +87,6 @@ patternProperties:
 $ref: /schemas/types.yaml#/definitions/uint32
 enum: [0, 1, 3]
 
-  diff-channels: true
-
   bipolar: true
 
   adi,buffered-positive:
@@ -100,6 +97,35 @@ patternProperties:
 description: Enable buffered mode for negative input.
 type: boolean
 
+additionalProperties: false
+
+  "^channel@([0-9]|1[0-5])$":
+type: object
+description: |
+  Represents the external channels which are connected to the ADC.
+  See Documentation/devicetree/bindings/iio/adc/adc.txt.
+
+properties:
+  reg:
+description: |
+  The channel number. It can have up to 8 channels on ad7124-4
+  and 16 channels on ad7124-8, numbered from 0 to 15.
+items:
+ minimum: 0
+ maximum: 15
+
+  diff-channels: true
+
+  adi,configuration:
+description: |
+  The devices has 8 configuration and ad7124-8 support up to 16 
unipolar channels.
+  Each channel can be assigned one configuration. Some channels will 
be sharing the
+  same configuration.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+minimum: 20
+maximum: 27
+
 required:
   - reg
   - diff-channels
@@ -127,30 +153,46 @@ examples:
 #address-cells = <1>;
 #size-cells = <0>;
 
-channel@0 {
-  reg = <0>;
-  diff-channels = <0 1>;
+config@20 {
+  reg = <20>;
   adi,reference-select = <0>;
   adi,buffered-positive;
 };
 
-channel@1 {
-  reg = <1>;
+config@21 {
+  reg = <21>;
   bipolar;
-  diff-channels = <2 3>;
   adi,reference-select = <0>;
   adi,buffered-positive;
   adi,buffered-negative;
 };
 
+config@22 {
+  reg = <22>;
+};
+
+channel@0 {
+  reg = <0>;
+  diff-channels = <0 1>;
+  adi,configuration = <20>;
+};
+
+channel@1 {
+  reg = <1>;
+  diff-channels = <2 3>;
+  adi,configuration = <21>;
+};
+
 channel@2 {
   reg = <2>;
   diff-channels = <4 5>;
+  adi,configuration = <22>;
 };
 
 channel@3 {
   reg = <3>;
   diff-channels = <6 7>;
+  adi,configuration = <22>;
 };
   };
 };
-- 
2.20.1



[PATCH 0/2] iio: adc: ad7124: allow 16 channels

2021-02-04 Thread alexandru.tachici
From: Alexandru Tachici 

AD7124-8 can have up to 16 pseudo-differential channels
enabled simultaneously and only 8 configurations. In this
scenario we cannot assign one configuration per channel,
some channels will have to share configurations like, ODR,
gain and filter parameters.

Allow the user to specify channels and configurations
separately in device-tree and assign, if needed, the same
configuration to multiple channels.

If two channels share the configuration changing the
sampling rate of one will change the sampling rate of the
other too.

Alexandru Tachici (2):
  iio: adc: ad7124: allow 16 channels
  dt-bindings: iio: adc: ad7124: add config nodes

 .../bindings/iio/adc/adi,ad7124.yaml  |  72 +--
 drivers/iio/adc/ad7124.c  | 196 +++---
 2 files changed, 179 insertions(+), 89 deletions(-)

-- 
2.20.1



[PATCH 1/2] clk: ad9545: Add support

2021-01-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add support for AD9545 Quad Input, 10-Output, Dual DPLL/IEEE 1588,
1 pps Synchronizer and Jitter Cleaner.

Signed-off-by: Alexandru Tachici 
---
 drivers/clk/Kconfig|6 +
 drivers/clk/Makefile   |1 +
 drivers/clk/adi/Kconfig|   29 +
 drivers/clk/adi/Makefile   |9 +
 drivers/clk/adi/clk-ad9545-i2c.c   |   61 +
 drivers/clk/adi/clk-ad9545-spi.c   |   75 ++
 drivers/clk/adi/clk-ad9545.c   | 1678 
 drivers/clk/adi/clk-ad9545.h   |   16 +
 include/dt-bindings/clock/ad9545.h |   64 ++
 9 files changed, 1939 insertions(+)
 create mode 100644 drivers/clk/adi/Kconfig
 create mode 100644 drivers/clk/adi/Makefile
 create mode 100644 drivers/clk/adi/clk-ad9545-i2c.c
 create mode 100644 drivers/clk/adi/clk-ad9545-spi.c
 create mode 100644 drivers/clk/adi/clk-ad9545.c
 create mode 100644 drivers/clk/adi/clk-ad9545.h
 create mode 100644 include/dt-bindings/clock/ad9545.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 85856cff506c..80c3faf1f42e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -270,6 +270,11 @@ config CLK_LS1028A_PLLDIG
   features of the PLL are currently supported by the driver. By 
default,
   configured bypass mode with this PLL.
 
+config COMMON_CLK_ADI
+   def_bool COMMON_CLK
+   ---help---
+ Support for Analog Devices clock providers.
+
 config COMMON_CLK_XGENE
bool "Clock driver for APM XGene SoC"
default ARCH_XGENE
@@ -369,6 +374,7 @@ config COMMON_CLK_FIXED_MMIO
  Support for Memory Mapped IO Fixed clocks
 
 source "drivers/clk/actions/Kconfig"
+source "drivers/clk/adi/Kconfig"
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/baikal-t1/Kconfig"
 source "drivers/clk/bcm/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dbdc590e7de3..3adc7db7c671 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE)+= clk-xgene.o
 
 # please keep this section sorted lexicographically by directory path name
 obj-y  += actions/
+obj-$(CONFIG_COMMON_CLK_ADI)   += adi/
 obj-y  += analogbits/
 obj-$(CONFIG_COMMON_CLK_AT91)  += at91/
 obj-$(CONFIG_ARCH_ARTPEC)  += axis/
diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig
new file mode 100644
index ..768b8227f336
--- /dev/null
+++ b/drivers/clk/adi/Kconfig
@@ -0,0 +1,29 @@
+#
+# Analog Devices Clock Drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Analog Devices Clock Drivers"
+
+config COMMON_CLK_AD9545
+   tristate
+
+config COMMON_CLK_AD9545_I2C
+   tristate "Analog Devices AD9545 via I2C"
+   depends on REGMAP_I2C
+   select COMMON_CLK_AD9545
+   help
+ Say yes here to build support for Analog Devices AD9545
+ Quad Input, 10-Output, Dual DPLL/IEEE 1588,
+ 1 pps Synchronizer and Jitter Cleaner via I2C
+
+config COMMON_CLK_AD9545_SPI
+   tristate "Analog Devices AD9545 via SPI"
+   depends on REGMAP_SPI
+   select COMMON_CLK_AD9545
+   help
+ Say yes here to build support for Analog Devices AD9545
+ Quad Input, 10-Output, Dual DPLL/IEEE 1588,
+ 1 pps Synchronizer and Jitter Cleaner via SPI
+
+endmenu
diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile
new file mode 100644
index ..7ba1fded3013
--- /dev/null
+++ b/drivers/clk/adi/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+#
+# Makefile for AD9545 Network Clock Generator/Synchronizer
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_COMMON_CLK_AD9545) += clk-ad9545.o
+obj-$(CONFIG_COMMON_CLK_AD9545_I2C) += clk-ad9545-i2c.o
+obj-$(CONFIG_COMMON_CLK_AD9545_SPI) += clk-ad9545-spi.o
diff --git a/drivers/clk/adi/clk-ad9545-i2c.c b/drivers/clk/adi/clk-ad9545-i2c.c
new file mode 100644
index ..923fec28ece2
--- /dev/null
+++ b/drivers/clk/adi/clk-ad9545-i2c.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include "clk-ad9545.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static const struct regmap_config ad9545_regmap_config = {
+   .reg_bits = 16,
+   .val_bits = 8,
+   .max_register = 0x3A3B,
+   .use_single_rw = true,
+};
+
+static int ad9545_i2c_probe(struct i2c_client *client)
+{
+   struct regmap *regmap;
+
+   regmap = devm_regmap_init_i2c(client, _regmap_config);
+   if (IS_ERR(regmap)) {
+   dev_err(>dev, "devm_regmap_init_i2c failed!\n");
+   return PTR_ERR(regmap);
+   }
+
+   return ad9545_probe(>dev, regmap);
+}
+
+static const struct of_device_id 

[PATCH 2/2] dt-bindings: clock: ad9545: Add documentation

2021-01-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add dt bindings for ad9545.

Signed-off-by: Alexandru Tachici 
---
 .../devicetree/bindings/clock/clk-ad9545.yaml | 352 ++
 1 file changed, 352 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/clk-ad9545.yaml

diff --git a/Documentation/devicetree/bindings/clock/clk-ad9545.yaml 
b/Documentation/devicetree/bindings/clock/clk-ad9545.yaml
new file mode 100644
index ..3611eaaa145c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/clk-ad9545.yaml
@@ -0,0 +1,352 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/clk-ad9545.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9545 Quad Input, 10-Output, Dual DPLL/IEEE 1588
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices AD9545 Quad Input, 10-Output, Dual DPLL/IEEE 1588,
+  1 pps Synchronizer and Jitter Cleaner
+
+  Relevant documents:
+[1] ad9545 datasheet:
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ad9545.pdf
+
+[2] Defines used: include/dt-bindings/clock/ad9545.h
+
+properties:
+  compatible:
+enum:
+  - adi,ad9545
+
+  "#address-cells":
+const: 1
+
+  "#size-cells":
+const: 0
+
+  "#clock-cells":
+description: |
+  The first cell indicates the clock type and the second number is the 
clock address (see [2]).
+const: 2
+
+  reg:
+description: |
+  I2C address of the secondary device.
+minimum: 0
+maximum: 0xFF
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,freq-doubler:
+description: |
+  The system clock PLL provides the user with the option of doubling the 
reference frequency.
+type: boolean
+
+  adi,ref-crystal:
+description: |
+  At XOA,XOB there is a crystal connected that needs maintaining.
+  Otherwise it is assumed that there is a TCXO or OCXO connected.
+type: boolean
+
+  adi,ref-frequency-hz:
+description: |
+  Reference input frequency at XOA,XOB. This is used for the system clock.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+
+  clocks:
+items:
+  - description: Ref A clock input
+  - description: Ref AA clock input
+  - description: Ref B clock input
+  - description: Ref BB clock input
+maxItems: 4
+
+  clock-output-names:
+minItems: 1
+maxItems: 10
+
+  assigned-clocks:
+ minItems: 1
+ maxItems: 14
+
+  assigned-clock-rates:
+description: |
+  Can initialize frequency of the 2 auxiliary NCOs, 2 PLLs and 10 clock 
outputs.
+  Output clock rates must be one of the divisors of PLL assigned frequency.
+minItems: 1
+maxItems: 14
+
+patternProperties:
+  "^ref-input-clk@[0-3]$":
+description: |
+  Represents a reference clock input.
+type: object
+
+properties:
+  reg:
+description: |
+  The reference input number. It can have up to 4 input clocks 
numbered from 0 to 3.
+  (mapped: [refa, refaa, refb, refbb] -> [0, 1, 2, 3])
+maxItems: 1
+
+  adi,single-ended-mode:
+description: |
+  Single-ended configuration mode.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - enum: [0, 1, 2, 3]
+maxItems: 1
+
+  adi,differential-mode:
+description: |
+  Differential configuration mode.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - enum: [0, 1, 2]
+maxItems: 1
+
+  adi,r-divider-ratio:
+description: |
+  Each reference input has a dedicated divider.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - minimum: 1
+  - maximum: 1073741824
+maxItems: 1
+
+  adi,ref-dtol-pbb:
+description: |
+  REFx offset limit. Constitutes a fractional portion of the 
corresponding nominal period.
+  The 24-bit number represents fractional units of parts per billion 
(ppb) up to a
+  maximum of approximately 17 million ppb (1.7%).
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - minimum: 0
+  - maximum: 16777215
+  - default: 10
+
+  adi,ref-monitor-hysteresis-pbb:
+description: |
+  Basis points of the offset limit representing per ten thousand of 
REFx offset limit.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - enum: [0, 3125, 6250, 12500, 25000, 5, 75000, 87500]
+  - default: 12500
+
+  adi,ref-validation-timer-ms:
+description: |
+  Time required for a reference to remain in tolerance condition 
before being
+  available to be used.
+allOf:
+  - $ref: /schemas/types.yaml#/definitions/uint32
+  - minimum: 1
+  - maximum: 1048574
+  

[PATCH 0/2] clk: ad9545: Add support

2021-01-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add support for AD9545 Quad Input, 10-Output, Dual DPLL/IEEE 1588,
1 pps Synchronizer and Jitter Cleaner.

At the core of the device there are PLL blocks. Each block consists
of one DPLL and one APLL. The DPLL can be fed timestamps from one of
the 4 reference input dividers or one of the two auxiliary NCOs.

The APLL takes the output of the DPLL and up-converts this
frequency to 1.2-2.0 GHZ. First 6 output dividers are receiving
clock from APLL0 and last 4 outputs are receiving clock from APLL1.

Current approach is to register under common clock framework,
depending whether they are mentioned in the device-tree,
any of the used references input dividers, the two auxiliary
NCOs, PLL blocks, output dividers.

A clock tree example:
Ref-B
  Ref-B-Div
PLL0
  Q0C-div
PLL1
  Q1A-div
  Q1B-div


Alexandru Tachici (2):
  clk: ad9545: Add support
  dt-bindings: clock: ad9545: Add documentation

 .../devicetree/bindings/clock/clk-ad9545.yaml |  352 
 drivers/clk/Kconfig   |6 +
 drivers/clk/Makefile  |1 +
 drivers/clk/adi/Kconfig   |   29 +
 drivers/clk/adi/Makefile  |9 +
 drivers/clk/adi/clk-ad9545-i2c.c  |   61 +
 drivers/clk/adi/clk-ad9545-spi.c  |   75 +
 drivers/clk/adi/clk-ad9545.c  | 1678 +
 drivers/clk/adi/clk-ad9545.h  |   16 +
 include/dt-bindings/clock/ad9545.h|   64 +
 10 files changed, 2291 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/clk-ad9545.yaml
 create mode 100644 drivers/clk/adi/Kconfig
 create mode 100644 drivers/clk/adi/Makefile
 create mode 100644 drivers/clk/adi/clk-ad9545-i2c.c
 create mode 100644 drivers/clk/adi/clk-ad9545-spi.c
 create mode 100644 drivers/clk/adi/clk-ad9545.c
 create mode 100644 drivers/clk/adi/clk-ad9545.h
 create mode 100644 include/dt-bindings/clock/ad9545.h

-- 
2.20.1



[PATCH v3 2/3] hwmon: ltc2992: Add support for GPIOs.

2020-12-02 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 has 4 open-drain GPIOS. This patch exports to user
space the 4 GPIOs using the GPIO driver Linux API.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/Kconfig   |   1 +
 drivers/hwmon/ltc2992.c | 160 +++-
 2 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bf9e387270d6..8a8eb42fb1ec 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -861,6 +861,7 @@ config SENSORS_LTC2990
 config SENSORS_LTC2992
tristate "Linear Technology LTC2992"
depends on I2C
+   depends on GPIOLIB
help
  If you say yes here you get support for Linear Technology LTC2992
  I2C System Monitor. The LTC2992 measures current, voltage, and
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index c11d585a9600..69dbb5aa5dc2 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -8,6 +8,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -54,6 +55,9 @@
 #define LTC2992_G4_MAX_THRESH  0x74
 #define LTC2992_G4_MIN_THRESH  0x76
 #define LTC2992_FAULT3 0x92
+#define LTC2992_GPIO_STATUS0x95
+#define LTC2992_GPIO_IO_CTRL   0x96
+#define LTC2992_GPIO_CTRL  0x97
 
 #define LTC2992_POWER(x)   (LTC2992_POWER1 + ((x) * 0x32))
 #define LTC2992_POWER_MAX(x)   (LTC2992_POWER1_MAX + ((x) * 0x32))
@@ -96,8 +100,18 @@
 #define LTC2992_VADC_UV_LSB25000
 #define LTC2992_VADC_GPIO_UV_LSB   500
 
+#define LTC2992_GPIO_NR4
+#define LTC2992_GPIO1_BIT  7
+#define LTC2992_GPIO2_BIT  6
+#define LTC2992_GPIO3_BIT  0
+#define LTC2992_GPIO4_BIT  6
+#define LTC2992_GPIO_BIT(x)(LTC2992_GPIO_NR - (x) - 1)
+
 struct ltc2992_state {
struct i2c_client   *client;
+   struct gpio_chipgc;
+   struct mutexgpio_mutex; /* lock for gpio access */
+   const char  *gpio_names[LTC2992_GPIO_NR];
struct regmap   *regmap;
u32 r_sense_uohm[2];
 };
@@ -111,6 +125,8 @@ struct ltc2992_gpio_regs {
u8  alarm;
u8  min_alarm_msk;
u8  max_alarm_msk;
+   u8  ctrl;
+   u8  ctrl_bit;
 };
 
 static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
@@ -123,6 +139,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT1,
.min_alarm_msk = LTC2992_GPIO1_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO1_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO1_BIT,
},
{
.data = LTC2992_G2,
@@ -133,6 +151,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT2,
.min_alarm_msk = LTC2992_GPIO2_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO2_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO2_BIT,
},
{
.data = LTC2992_G3,
@@ -143,6 +163,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT3,
.min_alarm_msk = LTC2992_GPIO3_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO3_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO3_BIT,
},
{
.data = LTC2992_G4,
@@ -153,14 +175,20 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT3,
.min_alarm_msk = LTC2992_GPIO4_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO4_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_CTRL,
+   .ctrl_bit = LTC2992_GPIO4_BIT,
},
 };
 
+static const char *ltc2992_gpio_names[LTC2992_GPIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4",
+};
+
 static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 
reg_len)
 {
u8 regvals[4];
-   int ret;
int val;
+   int ret;
int i;
 
ret = regmap_bulk_read(st->regmap, addr, regvals, reg_len);
@@ -185,6 +213,132 @@ static int ltc2992_write_reg(struct ltc2992_state *st, u8 
addr, const u8 reg_len
return regmap_bulk_write(st->regmap, addr, regvals, reg_len);
 }
 
+static int ltc2992_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct ltc2992_state *st = gpiochip_get_data(chip);
+   unsigned long gpio_status;
+   int reg;
+
+   mutex_lock(>gpio_mutex);
+   reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1);
+   mutex_unlock(>gpio_mutex);
+
+   if (reg < 0)
+   return reg;
+
+   gpio_status = reg;
+
+   return 

[PATCH v3 3/3] dt-binding: hwmon: Add documentation for ltc2992

2020-12-02 Thread alexandru.tachici
From: Alexandru Tachici 

Add documentation for ltc2992.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,ltc2992.yaml   | 80 +++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
new file mode 100644
index ..64a8fcb7bc46
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,ltc2992.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology 2992 Power Monitor
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Linear Technology 2992 Dual Wide Range Power Monitor
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,ltc2992
+
+  reg:
+maxItems: 1
+
+  '#address-cells':
+const: 1
+
+  '#size-cells':
+const: 0
+
+  avcc-supply: true
+
+patternProperties:
+  "^channel@([0-1])$":
+type: object
+description: |
+  Represents the two supplies to be monitored.
+
+properties:
+  reg:
+description: |
+  The channel number. LTC2992 can monitor two supplies.
+items:
+  minimum: 0
+  maximum: 1
+
+  shunt-resistor-micro-ohms:
+description:
+  The value of curent sense resistor in microohms.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c1 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+ltc2992@6F {
+#address-cells = <1>;
+#size-cells = <0>;
+
+compatible = "adi,ltc2992";
+reg = <0x6F>;
+
+channel@0 {
+reg = <0x0>;
+shunt-resistor-micro-ohms = <1>;
+};
+
+channel@1 {
+reg = <0x1>;
+shunt-resistor-micro-ohms = <1>;
+};
+};
+};
+...
-- 
2.20.1



[PATCH v3 1/3] hwmon: ltc2992: Add support

2020-12-02 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/index.rst   |   1 +
 Documentation/hwmon/ltc2992.rst |  56 +++
 drivers/hwmon/Kconfig   |  11 +
 drivers/hwmon/Makefile  |   1 +
 drivers/hwmon/ltc2992.c | 813 
 5 files changed, 882 insertions(+)
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index e6b91ab12978..98575a8b1918 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -100,6 +100,7 @@ Hardware Monitoring Kernel Drivers
lm95234
lm95245
lochnagar
+   ltc2992
ltc2945
ltc2947
ltc2978
diff --git a/Documentation/hwmon/ltc2992.rst b/Documentation/hwmon/ltc2992.rst
new file mode 100644
index ..46aa1aa84a1a
--- /dev/null
+++ b/Documentation/hwmon/ltc2992.rst
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver ltc2992
+=
+
+Supported chips:
+  * Linear Technology LTC2992
+Prefix: 'ltc2992'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Linear Technology LTC2992 power 
monitor.
+
+LTC2992 is a rail-to-rail system monitor that measures current,
+voltage, and power of two supplies.
+
+Two ADCs simultaneously measure each supply’s current. A third ADC monitors
+the input voltages and four auxiliary external voltages.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write,
+all other attributes are read-only.
+
+in_reset_history   Reset all highest/lowest values.
+
+inX_input  Measured voltage.
+inX_lowest Minimum measured voltage.
+inX_highestMaximum measured voltage.
+inX_minMinimum voltage allowed.
+inX_maxMaximum voltage allowed.
+inX_min_alarm  An undervoltage occurred. Cleared on read.
+inX_max_alarm  An overvoltage occurred. Cleared on read.
+
+currX_inputMeasured current.
+currX_lowest   Minimum measured current.
+currX_highest  Maximum measured current.
+currX_min  Minimum current allowed.
+currX_max  Maximum current allowed.
+currX_min_alarmAn undercurrent occurred. Cleared on read.
+currX_max_alarmAn overcurrent occurred. Cleared on read.
+
+powerX_input   Measured power.
+powerX_input_lowestMinimum measured voltage.
+powerX_input_highest   Maximum measured voltage.
+powerX_min Minimum power.
+powerX_max Maximum power.
+powerX_min_alarm   An underpower occurred. Cleared on read.
+powerX_max_alarm   An overpower occurred. Cleared on read.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a850e4f0e0bd..bf9e387270d6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -858,6 +858,17 @@ config SENSORS_LTC2990
  This driver can also be built as a module. If so, the module will
  be called ltc2990.
 
+config SENSORS_LTC2992
+   tristate "Linear Technology LTC2992"
+   depends on I2C
+   help
+ If you say yes here you get support for Linear Technology LTC2992
+ I2C System Monitor. The LTC2992 measures current, voltage, and
+ power of two supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2992.
+
 config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 9db2903b61e5..d6172c4807c4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_LTC2947)   += ltc2947-core.o
 obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o
 obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o
 obj-$(CONFIG_SENSORS_LTC2990)  += ltc2990.o
+obj-$(CONFIG_SENSORS_LTC2992)  += ltc2992.o
 obj-$(CONFIG_SENSORS_LTC4151)  += ltc4151.o
 obj-$(CONFIG_SENSORS_LTC4215)  += ltc4215.o
 obj-$(CONFIG_SENSORS_LTC4222)  += ltc4222.o
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
new file mode 100644
index ..c11d585a9600
--- /dev/null
+++ b/drivers/hwmon/ltc2992.c
@@ -0,0 +1,813 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * LTC2992 - Dual Wide Range Power Monitor
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define LTC2992_CTRLB 

[PATCH v3 0/3] hwmon: ltc2992: Add support

2020-12-02 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages (GPIOs).

1. Use hwmon to create sysfs entries for current, voltage
and power of two 0V to 100V supplies. Create sysfs entries
for voltage sensed on the 4 GPIO pins.

2. Expose to userspace the 4 open-drain GPIOs provided by ltc2992.

3. DT bindings for ltc2992.

Alexandru Tachici (3):
  hwmon: ltc2992: Add support
  hwmon: ltc2992: Add support for GPIOs.
  dt-binding: hwmon: Add documentation for ltc2992

Changelog v2 -> v3:
- removed unnecessary includes
- removed unnecessary initialization of 'reg' in ltc2992_write_in() and 
ltc2992_write_power().
- removed i2c_check_functionality() in ltc2992_i2c_probe

 .../bindings/hwmon/adi,ltc2992.yaml   |  80 ++
 Documentation/hwmon/index.rst |   1 +
 Documentation/hwmon/ltc2992.rst   |  56 +
 drivers/hwmon/Kconfig |  12 +
 drivers/hwmon/Makefile|   1 +
 drivers/hwmon/ltc2992.c   | 971 ++
 6 files changed, 1121 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

-- 
2.20.1



[PATCH v2 3/3] dt-binding: hwmon: Add documentation for ltc2992

2020-11-11 Thread alexandru.tachici
From: Alexandru Tachici 

Add documentation for ltc2992.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,ltc2992.yaml   | 80 +++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
new file mode 100644
index ..64a8fcb7bc46
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,ltc2992.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology 2992 Power Monitor
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Linear Technology 2992 Dual Wide Range Power Monitor
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,ltc2992
+
+  reg:
+maxItems: 1
+
+  '#address-cells':
+const: 1
+
+  '#size-cells':
+const: 0
+
+  avcc-supply: true
+
+patternProperties:
+  "^channel@([0-1])$":
+type: object
+description: |
+  Represents the two supplies to be monitored.
+
+properties:
+  reg:
+description: |
+  The channel number. LTC2992 can monitor two supplies.
+items:
+  minimum: 0
+  maximum: 1
+
+  shunt-resistor-micro-ohms:
+description:
+  The value of curent sense resistor in microohms.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c1 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+ltc2992@6F {
+#address-cells = <1>;
+#size-cells = <0>;
+
+compatible = "adi,ltc2992";
+reg = <0x6F>;
+
+channel@0 {
+reg = <0x0>;
+shunt-resistor-micro-ohms = <1>;
+};
+
+channel@1 {
+reg = <0x1>;
+shunt-resistor-micro-ohms = <1>;
+};
+};
+};
+...
-- 
2.20.1



[PATCH v2 0/3] hwmon: ltc2992: Add support

2020-11-11 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages (GPIOs).

1. Use hwmon to create sysfs entries for current, voltage
and power of two 0V to 100V supplies. Create sysfs entries
for voltage sensed on the 4 GPIO pins.

2. Expose to userspace the 4 open-drain GPIOs provided by ltc2992.

3. DT bindings for ltc2992.

Alexandru Tachici (3):
  hwmon: ltc2992: Add support
  hwmon: ltc2992: Add support for GPIOs.
  dt-binding: hwmon: Add documentation for ltc2992

Changelog v1 -> v2:
- ltc2992_read_reg function returns the reg value directly
- historical min max values are reported now through lowest and highest sysfs
- added alarm sysfs for both min and max values
- added reset history option: writing to in_reset_history will reset all
lowest/highest values
- fixed missing static
- fixed dt bindings errors

 .../bindings/hwmon/adi,ltc2992.yaml   |  80 ++
 Documentation/hwmon/index.rst |   1 +
 Documentation/hwmon/ltc2992.rst   |  56 +
 drivers/hwmon/Kconfig |  12 +
 drivers/hwmon/Makefile|   1 +
 drivers/hwmon/ltc2992.c   | 976 ++
 6 files changed, 1126 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

-- 
2.20.1



[PATCH v2 2/3] hwmon: ltc2992: Add support for GPIOs.

2020-11-11 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 has 4 open-drain GPIOS. This patch exports to user
space the 4 GPIOs using the GPIO driver Linux API.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/Kconfig   |   1 +
 drivers/hwmon/ltc2992.c | 160 +++-
 2 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bf9e387270d6..8a8eb42fb1ec 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -861,6 +861,7 @@ config SENSORS_LTC2990
 config SENSORS_LTC2992
tristate "Linear Technology LTC2992"
depends on I2C
+   depends on GPIOLIB
help
  If you say yes here you get support for Linear Technology LTC2992
  I2C System Monitor. The LTC2992 measures current, voltage, and
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index ce679405ac81..78951212ef8b 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -56,6 +57,9 @@
 #define LTC2992_G4_MAX_THRESH  0x74
 #define LTC2992_G4_MIN_THRESH  0x76
 #define LTC2992_FAULT3 0x92
+#define LTC2992_GPIO_STATUS0x95
+#define LTC2992_GPIO_IO_CTRL   0x96
+#define LTC2992_GPIO_CTRL  0x97
 
 #define LTC2992_POWER(x)   (LTC2992_POWER1 + ((x) * 0x32))
 #define LTC2992_POWER_MAX(x)   (LTC2992_POWER1_MAX + ((x) * 0x32))
@@ -98,8 +102,18 @@
 #define LTC2992_VADC_UV_LSB25000
 #define LTC2992_VADC_GPIO_UV_LSB   500
 
+#define LTC2992_GPIO_NR4
+#define LTC2992_GPIO1_BIT  7
+#define LTC2992_GPIO2_BIT  6
+#define LTC2992_GPIO3_BIT  0
+#define LTC2992_GPIO4_BIT  6
+#define LTC2992_GPIO_BIT(x)(LTC2992_GPIO_NR - (x) - 1)
+
 struct ltc2992_state {
struct i2c_client   *client;
+   struct gpio_chipgc;
+   struct mutexgpio_mutex; /* lock for gpio access */
+   const char  *gpio_names[LTC2992_GPIO_NR];
struct regmap   *regmap;
u32 r_sense_uohm[2];
 };
@@ -113,6 +127,8 @@ struct ltc2992_gpio_regs {
u8  alarm;
u8  min_alarm_msk;
u8  max_alarm_msk;
+   u8  ctrl;
+   u8  ctrl_bit;
 };
 
 static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
@@ -125,6 +141,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT1,
.min_alarm_msk = LTC2992_GPIO1_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO1_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO1_BIT,
},
{
.data = LTC2992_G2,
@@ -135,6 +153,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT2,
.min_alarm_msk = LTC2992_GPIO2_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO2_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO2_BIT,
},
{
.data = LTC2992_G3,
@@ -145,6 +165,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT3,
.min_alarm_msk = LTC2992_GPIO3_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO3_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO3_BIT,
},
{
.data = LTC2992_G4,
@@ -155,14 +177,20 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.alarm = LTC2992_FAULT3,
.min_alarm_msk = LTC2992_GPIO4_FAULT_MSK(0),
.max_alarm_msk = LTC2992_GPIO4_FAULT_MSK(1),
+   .ctrl = LTC2992_GPIO_CTRL,
+   .ctrl_bit = LTC2992_GPIO4_BIT,
},
 };
 
+static const char *ltc2992_gpio_names[LTC2992_GPIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4",
+};
+
 static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 
reg_len)
 {
u8 regvals[4];
-   int ret;
int val;
+   int ret;
int i;
 
ret = regmap_bulk_read(st->regmap, addr, regvals, reg_len);
@@ -187,6 +215,132 @@ static int ltc2992_write_reg(struct ltc2992_state *st, u8 
addr, const u8 reg_len
return regmap_bulk_write(st->regmap, addr, regvals, reg_len);
 }
 
+static int ltc2992_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct ltc2992_state *st = gpiochip_get_data(chip);
+   unsigned long gpio_status;
+   int reg;
+
+   mutex_lock(>gpio_mutex);
+   reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1);
+   mutex_unlock(>gpio_mutex);
+
+   if (reg < 0)
+   return reg;
+
+   gpio_status = reg;
+
+   return 

[PATCH v2 1/3] hwmon: ltc2992: Add support

2020-11-11 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/index.rst   |   1 +
 Documentation/hwmon/ltc2992.rst |  56 +++
 drivers/hwmon/Kconfig   |  11 +
 drivers/hwmon/Makefile  |   1 +
 drivers/hwmon/ltc2992.c | 818 
 5 files changed, 887 insertions(+)
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index e6b91ab12978..98575a8b1918 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -100,6 +100,7 @@ Hardware Monitoring Kernel Drivers
lm95234
lm95245
lochnagar
+   ltc2992
ltc2945
ltc2947
ltc2978
diff --git a/Documentation/hwmon/ltc2992.rst b/Documentation/hwmon/ltc2992.rst
new file mode 100644
index ..46aa1aa84a1a
--- /dev/null
+++ b/Documentation/hwmon/ltc2992.rst
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver ltc2992
+=
+
+Supported chips:
+  * Linear Technology LTC2992
+Prefix: 'ltc2992'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Linear Technology LTC2992 power 
monitor.
+
+LTC2992 is a rail-to-rail system monitor that measures current,
+voltage, and power of two supplies.
+
+Two ADCs simultaneously measure each supply’s current. A third ADC monitors
+the input voltages and four auxiliary external voltages.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write,
+all other attributes are read-only.
+
+in_reset_history   Reset all highest/lowest values.
+
+inX_input  Measured voltage.
+inX_lowest Minimum measured voltage.
+inX_highestMaximum measured voltage.
+inX_minMinimum voltage allowed.
+inX_maxMaximum voltage allowed.
+inX_min_alarm  An undervoltage occurred. Cleared on read.
+inX_max_alarm  An overvoltage occurred. Cleared on read.
+
+currX_inputMeasured current.
+currX_lowest   Minimum measured current.
+currX_highest  Maximum measured current.
+currX_min  Minimum current allowed.
+currX_max  Maximum current allowed.
+currX_min_alarmAn undercurrent occurred. Cleared on read.
+currX_max_alarmAn overcurrent occurred. Cleared on read.
+
+powerX_input   Measured power.
+powerX_input_lowestMinimum measured voltage.
+powerX_input_highest   Maximum measured voltage.
+powerX_min Minimum power.
+powerX_max Maximum power.
+powerX_min_alarm   An underpower occurred. Cleared on read.
+powerX_max_alarm   An overpower occurred. Cleared on read.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a850e4f0e0bd..bf9e387270d6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -858,6 +858,17 @@ config SENSORS_LTC2990
  This driver can also be built as a module. If so, the module will
  be called ltc2990.
 
+config SENSORS_LTC2992
+   tristate "Linear Technology LTC2992"
+   depends on I2C
+   help
+ If you say yes here you get support for Linear Technology LTC2992
+ I2C System Monitor. The LTC2992 measures current, voltage, and
+ power of two supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2992.
+
 config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 9db2903b61e5..d6172c4807c4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_LTC2947)   += ltc2947-core.o
 obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o
 obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o
 obj-$(CONFIG_SENSORS_LTC2990)  += ltc2990.o
+obj-$(CONFIG_SENSORS_LTC2992)  += ltc2992.o
 obj-$(CONFIG_SENSORS_LTC4151)  += ltc4151.o
 obj-$(CONFIG_SENSORS_LTC4215)  += ltc4215.o
 obj-$(CONFIG_SENSORS_LTC4222)  += ltc4222.o
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
new file mode 100644
index ..ce679405ac81
--- /dev/null
+++ b/drivers/hwmon/ltc2992.c
@@ -0,0 +1,818 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * LTC2992 - Dual Wide Range Power Monitor
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define 

[PATCH 3/3] dt-binding: hwmon: Add documentation for ltc2992

2020-10-29 Thread alexandru.tachici
From: Alexandru Tachici 

Add documentation for ltc2992.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,ltc2992.yaml   | 78 +++
 1 file changed, 78 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
new file mode 100644
index ..1b603026ed2d
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,ltc2992.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology 2992 Power Monitor
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Linear Technology 2992 Dual Wide Range Power Monitor
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,ltc2992
+
+  reg:
+maxItems: 1
+
+  avcc-supply: true
+
+patternProperties:
+  "^channel@([0-1])$":
+type: object
+description: |
+  Represents the two supplies to be monitored.
+
+properties:
+  reg:
+description: |
+  The channel number. LTC2992 can monitor two supplies.
+items:
+ minimum: 0
+ maximum: 1
+
+  shunt-resistor-micro-ohms:
+description:
+  The value of curent sense resistor in microohms.
+  required:
+- reg
+
+  additionalProperties: false
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c1 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+ltc2992@6F {
+#address-cells = <1>;
+#size-cells = <0>;
+
+compatible = "adi,ltc2992";
+reg = <0x6F>;
+
+channel@0 {
+reg = <0x0>;
+shunt-resistor-micro-ohms = <1>;
+};
+
+channel@1 {
+reg = <0x1>;
+shunt-resistor-micro-ohms = <1>;
+};
+};
+};
+...
-- 
2.20.1



[PATCH 1/3] hwmon: ltc2992: Add support

2020-10-29 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/index.rst   |   1 +
 Documentation/hwmon/ltc2992.rst |  51 +++
 drivers/hwmon/Kconfig   |  11 +
 drivers/hwmon/Makefile  |   1 +
 drivers/hwmon/ltc2992.c | 735 
 5 files changed, 799 insertions(+)
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index e6b91ab12978..f759d70ae9fd 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -104,6 +104,7 @@ Hardware Monitoring Kernel Drivers
ltc2947
ltc2978
ltc2990
+   ltc2992
ltc3815
ltc4151
ltc4215
diff --git a/Documentation/hwmon/ltc2992.rst b/Documentation/hwmon/ltc2992.rst
new file mode 100644
index ..1dd48ef9f655
--- /dev/null
+++ b/Documentation/hwmon/ltc2992.rst
@@ -0,0 +1,51 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver ltc2992
+=
+
+Supported chips:
+  * Linear Technology LTC2992
+Prefix: 'ltc2992'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Linear Technology LTC2992 power 
monitor.
+
+LTC2992 is is a rail-to-rail system monitor that measures current,
+voltage, and power of two supplies.
+
+Two ADCs simultaneously measure each supply’s current. A third ADC monitors
+the input voltages and four auxiliary external voltages.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write,
+all other attributes are read-only.
+
+inX_input  Measured voltage.
+inX_minMinimum voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
+inX_alarm  An overvoltage or undervoltage occured. Cleared on read.
+
+currX_inputMeasured current.
+currX_min  Minimum current.
+currX_max  Maximum current.
+currX_min_alarmCurrent low alarm.
+currX_max_alarmCurrent high alarm.
+currX_alarmAn overvoltage or undervoltage occured. Cleared on read.
+
+powerX_input   Measured power.
+powerX_min Minimum power.
+powerX_max Maximum power.
+powerX_min_alarm   Power low alarm.
+powerX_max_alarm   Power high alarm.
+powerX_alarm   An overpower or underpower occured. Cleared on read.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a850e4f0e0bd..bf9e387270d6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -858,6 +858,17 @@ config SENSORS_LTC2990
  This driver can also be built as a module. If so, the module will
  be called ltc2990.
 
+config SENSORS_LTC2992
+   tristate "Linear Technology LTC2992"
+   depends on I2C
+   help
+ If you say yes here you get support for Linear Technology LTC2992
+ I2C System Monitor. The LTC2992 measures current, voltage, and
+ power of two supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2992.
+
 config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 9db2903b61e5..d6172c4807c4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_LTC2947)   += ltc2947-core.o
 obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o
 obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o
 obj-$(CONFIG_SENSORS_LTC2990)  += ltc2990.o
+obj-$(CONFIG_SENSORS_LTC2992)  += ltc2992.o
 obj-$(CONFIG_SENSORS_LTC4151)  += ltc4151.o
 obj-$(CONFIG_SENSORS_LTC4215)  += ltc4215.o
 obj-$(CONFIG_SENSORS_LTC4222)  += ltc4222.o
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
new file mode 100644
index ..940d92b4a1d0
--- /dev/null
+++ b/drivers/hwmon/ltc2992.c
@@ -0,0 +1,735 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * LTC2992 - Dual Wide Range Power Monitor
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define LTC2992_CTRLB  0x01
+#define LTC2992_FAULT1 0x03
+#define LTC2992_POWER1 0x05
+#define LTC2992_POWER1_MAX 0x08
+#define LTC2992_POWER1_MIN 0x0B
+#define LTC2992_POWER1_MAX_THRESH  0x0E
+#define 

[PATCH 2/3] hwmon: ltc2992: Add support for GPIOs.

2020-10-29 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 has 4 open-drain GPIOS. This patch exports to user
space the 4 GPIOs using the GPIO driver Linux API.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/Kconfig   |   1 +
 drivers/hwmon/ltc2992.c | 160 
 2 files changed, 161 insertions(+)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bf9e387270d6..8a8eb42fb1ec 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -861,6 +861,7 @@ config SENSORS_LTC2990
 config SENSORS_LTC2992
tristate "Linear Technology LTC2992"
depends on I2C
+   depends on GPIOLIB
help
  If you say yes here you get support for Linear Technology LTC2992
  I2C System Monitor. The LTC2992 measures current, voltage, and
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index 940d92b4a1d0..3fe6d34cdade 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -56,6 +57,9 @@
 #define LTC2992_G4_MAX_THRESH  0x74
 #define LTC2992_G4_MIN_THRESH  0x76
 #define LTC2992_FAULT3 0x92
+#define LTC2992_GPIO_STATUS0x95
+#define LTC2992_GPIO_IO_CTRL   0x96
+#define LTC2992_GPIO_CTRL  0x97
 
 #define LTC2992_POWER(x)   (LTC2992_POWER1 + ((x) * 0x32))
 #define LTC2992_POWER_MAX(x)   (LTC2992_POWER1_MAX + ((x) * 0x32))
@@ -95,8 +99,18 @@
 #define LTC2992_VADC_UV_LSB25000
 #define LTC2992_VADC_GPIO_UV_LSB   500
 
+#define LTC2992_GPIO_NR4
+#define LTC2992_GPIO1_BIT  7
+#define LTC2992_GPIO2_BIT  6
+#define LTC2992_GPIO3_BIT  0
+#define LTC2992_GPIO4_BIT  6
+#define LTC2992_GPIO_BIT(x)(LTC2992_GPIO_NR - (x) - 1)
+
 struct ltc2992_state {
struct i2c_client   *client;
+   struct gpio_chipgc;
+   struct mutexgpio_mutex; /* lock for gpio access */
+   const char  *gpio_names[LTC2992_GPIO_NR];
struct regmap   *regmap;
u32 r_sense_uohm[2];
 };
@@ -109,6 +123,8 @@ struct ltc2992_gpio_regs {
u8  min_thresh;
u8  alarm;
u8  alarm_msk;
+   u8  ctrl;
+   u8  ctrl_bit;
 };
 
 static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
@@ -120,6 +136,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.min_thresh = LTC2992_G1_MIN_THRESH,
.alarm = LTC2992_FAULT1,
.alarm_msk = LTC2992_GPIO1_FAULT_MSK,
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO1_BIT,
},
{
.data = LTC2992_G2,
@@ -129,6 +147,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.min_thresh = LTC2992_G2_MIN_THRESH,
.alarm = LTC2992_FAULT2,
.alarm_msk = LTC2992_GPIO2_FAULT_MSK,
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO2_BIT,
},
{
.data = LTC2992_G3,
@@ -138,6 +158,8 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.min_thresh = LTC2992_G3_MIN_THRESH,
.alarm = LTC2992_FAULT3,
.alarm_msk = LTC2992_GPIO3_FAULT_MSK,
+   .ctrl = LTC2992_GPIO_IO_CTRL,
+   .ctrl_bit = LTC2992_GPIO3_BIT,
},
{
.data = LTC2992_G4,
@@ -147,9 +169,15 @@ static const struct ltc2992_gpio_regs 
ltc2992_gpio_addr_map[] = {
.min_thresh = LTC2992_G4_MIN_THRESH,
.alarm = LTC2992_FAULT3,
.alarm_msk = LTC2992_GPIO4_FAULT_MSK,
+   .ctrl = LTC2992_GPIO_CTRL,
+   .ctrl_bit = LTC2992_GPIO4_BIT,
},
 };
 
+static const char *ltc2992_gpio_names[LTC2992_GPIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4",
+};
+
 static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 
reg_len, u32 *val)
 {
u8 regvals[4];
@@ -178,6 +206,134 @@ static int ltc2992_write_reg(struct ltc2992_state *st, u8 
addr, const u8 reg_len
return regmap_bulk_write(st->regmap, addr, regvals, reg_len);
 }
 
+static int ltc2992_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct ltc2992_state *st = gpiochip_get_data(chip);
+   unsigned long gpio_status;
+   u32 reg;
+   int ret;
+
+   mutex_lock(>gpio_mutex);
+   ret = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1, );
+   mutex_unlock(>gpio_mutex);
+
+   if (ret < 0)
+   return ret;
+
+   gpio_status = reg;
+
+   return !test_bit(LTC2992_GPIO_BIT(offset), _status);
+}
+
+static int ltc2992_gpio_get_multiple(struct gpio_chip *chip, unsigned long 
*mask,
+unsigned long *bits)

[PATCH 0/3] hwmon: ltc2992: Add support

2020-10-29 Thread alexandru.tachici
From: Alexandru Tachici 

LTC2992 is is a rail-to-rail system monitor that
measures current, voltage, and power of two supplies.

Two ADCs simultaneously measure each supply’s current.
A third ADC monitors the input voltages and four
auxiliary external voltages (GPIOs).

1. Use hwmon to create sysfs entries for current, voltage
and power of two 0V to 100V supplies. Create sysfs entries
for voltage sensed on the 4 GPIO pins.

2. Expose to userspace the 4 open-drain GPIOs provided by ltc2992.

3. DT bindings for ltc2992.

Alexandru Tachici (3):
  hwmon: ltc2992: Add support
  hwmon: ltc2992: Add support for GPIOs.
  dt-binding: hwmon: Add documentation for ltc2992

 .../bindings/hwmon/adi,ltc2992.yaml   |  78 ++
 Documentation/hwmon/index.rst |   1 +
 Documentation/hwmon/ltc2992.rst   |  51 +
 drivers/hwmon/Kconfig |  12 +
 drivers/hwmon/Makefile|   1 +
 drivers/hwmon/ltc2992.c   | 903 ++
 6 files changed, 1046 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
 create mode 100644 Documentation/hwmon/ltc2992.rst
 create mode 100644 drivers/hwmon/ltc2992.c

-- 
2.20.1



[PATCH v8 6/6] dt-bindings: hwmon: Add bindings for ADM1266

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 51 +++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..43b4f4f57b49
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+};
+};
+...
-- 
2.20.1



[PATCH v8 1/6] hwmon: pmbus: adm1266: add support

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 37 +++
 Documentation/hwmon/index.rst   |  1 +
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 65 +
 5 files changed, 113 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..9257f8a48650
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,37 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55ff4b7c5349..056f7107d7b8 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -30,6 +30,7 @@ Hardware Monitoring Kernel Drivers
adm1026
adm1031
adm1177
+   adm1266
adm1275
adm9240
ads7828
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a337195b1c39..da34083e1ffd 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c4b15db996ad..da41d22be1c9 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..79e8d90886b8
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "pmbus.h"
+#include 
+
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct i2c_client *client;
+};
+
+static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
+{
+   struct adm1266_data *data;
+   int i;
+
+   data = devm_kzalloc(>dev, sizeof(struct adm1266_data), 
GFP_KERNEL);
+   if (!data)
+   return -ENOMEM;
+
+   data->client = client;
+   data->info.pages = 17;
+   data->info.format[PSC_VOLTAGE_OUT] = linear;
+   for (i = 0; i < data->info.pages; i++)
+   data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+   return pmbus_do_probe(client, id, >info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+  

[PATCH v8 0/6] hwmon: pmbus: adm1266: add support

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Allow the current sate of the sequencer to be read
through debugfs.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. DT bindings for ADM1266.

Alexandru Tachici (6):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: add debugfs for states
  hwmon: pmbus: adm1266: read blackbox
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog v7 -> v8:
  - removed firmware/config programming
  - in adm1266_pmbus_block_xfer unlock buf mutex on error paths
  - added "depends on GPIOLIB" in kconfig ("select GPIOLIB" triggers a
  recursive dependency error)
  - removed cell access check (only blackbox cell will be used)
  - removed #if IS_ENABLED(CONFIG_NVMEM)
  - dt-bindings: removed adi,master-adm1266 property

 .../bindings/hwmon/adi,adm1266.yaml   |  51 ++
 Documentation/hwmon/adm1266.rst   |  37 ++
 Documentation/hwmon/index.rst |   1 +
 drivers/hwmon/pmbus/Kconfig   |  11 +
 drivers/hwmon/pmbus/Makefile  |   1 +
 drivers/hwmon/pmbus/adm1266.c | 513 ++
 6 files changed, 614 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

-- 
2.20.1



[PATCH v8 5/6] hwmon: pmbus: adm1266: read blackbox

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Using this feature, the device is capable of recording
to nonvolatile flash memory the vital data about the
system status that caused the system to perform a
black box write.

A blackbox is 64 bytes of data containing all the
status registers, last two states of the sequencer,
timestamp and counters. The mapping of this data is
described in the adm1266 datasheet.

On power-up the driver sets the unix time to
the adm1266 using the SET_RTC command. This value
is incremented by an internal clock and it is used
as timestamp for the black box feature.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 127 ++
 1 file changed, 127 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index df7954e2ce9f..1d83c4f42eaf 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,12 +15,19 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 #include 
+#include 
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
+#define ADM1266_SET_RTC0xDF
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -37,6 +44,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
@@ -45,11 +55,22 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct dentry *debugfs_dir;
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
+};
+
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
 /*
@@ -325,6 +346,104 @@ static void adm1266_init_debugfs(struct adm1266_data 
*data)
adm1266_state_read);
 }
 
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 
*read_buff)
+{
+   int record_count;
+   char index;
+   u8 buf[5];
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, 
buf);
+   if (ret < 0)
+   return ret;
+
+   if (ret != 4)
+   return -EIO;
+
+   record_count = buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, 
, read_buff);
+   if (ret < 0)
+   return ret;
+
+   if (ret != ADM1266_BLACKBOX_SIZE)
+   return -EIO;
+
+   read_buff += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, 
size_t bytes)
+{
+   struct adm1266_data *data = priv;
+   int ret;
+
+   if (offset + bytes > data->nvmem_config.size)
+   return -EINVAL;
+
+   if (offset == 0) {
+   memset(data->dev_mem, 0, data->nvmem_config.size);
+
+   ret = adm1266_nvmem_read_blackbox(data, data->dev_mem);
+   if (ret) {
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   }
+   }
+
+   memcpy(val, data->dev_mem + offset, bytes);
+
+   return 0;
+}
+
+static int adm1266_config_nvmem(struct adm1266_data *data)
+{
+   data->nvmem_config.name = dev_name(>client->dev);
+   data->nvmem_config.dev = >client->dev;
+   data->nvmem_config.root_only = true;
+   data->nvmem_config.read_only = true;
+   data->nvmem_config.owner = THIS_MODULE;
+   data->nvmem_config.reg_read = adm1266_nvmem_read;
+   data->nvmem_config.cells = adm1266_nvmem_cells;
+   data->nvmem_config.ncells = ARRAY_SIZE(adm1266_nvmem_cells);
+   data->nvmem_config.priv = data;
+   data->nvmem_config.stride = 1;
+   data->nvmem_config.word_size = 1;
+   data->nvmem_config.size = adm1266_nvmem_cells[0].bytes;
+
+   data->dev_mem = devm_kzalloc(>client->dev, 
data->nvmem_config.size, GFP_KERNEL);
+   if (!data->dev_mem)
+   return -ENOMEM;
+
+   data->nvmem = 

[PATCH v8 3/6] hwmon: pmbus: adm1266: Add support for GPIOs

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |   1 +
 drivers/hwmon/pmbus/adm1266.c | 204 ++
 2 files changed, 205 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index c04068b665e6..2db3a93a43ea 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -29,6 +29,7 @@ config SENSORS_PMBUS
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
select CRC8
+   depends on GPIOLIB
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 9db1c317029b..7ca5aa751b7e 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,18 +6,42 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -93,9 +117,185 @@ static int adm1266_pmbus_block_xfer(struct adm1266_data 
*data, u8 cmd, u8 w_len,
return ret;
 }
 
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long 
*mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + 
ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, 

[PATCH v8 2/6] hwmon: pmbus: adm1266: Add Block process call

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 78 +++
 2 files changed, 79 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da34083e1ffd..c04068b665e6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 79e8d90886b8..9db1c317029b 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -13,11 +14,85 @@
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
 struct adm1266_data {
struct pmbus_driver_info info;
struct i2c_client *client;
+   struct mutex buf_mutex;
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/*
+ * Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+static int adm1266_pmbus_block_xfer(struct adm1266_data *data, u8 cmd, u8 
w_len, u8 *data_w,
+   u8 *data_r)
+{
+   struct i2c_client *client = data->client;
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = I2C_M_DMA_SAFE,
+   .buf = data->write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD | I2C_M_DMA_SAFE,
+   .buf = data->read_buf,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr;
+   u8 crc;
+   int ret;
+
+   mutex_lock(>buf_mutex);
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   if (ret >= 0)
+   ret = -EPROTO;
+
+   mutex_unlock(>buf_mutex);
+
+   return ret;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, 0);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1, 
crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) {
+   mutex_unlock(>buf_mutex);
+   return -EBADMSG;
+   }
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+
+   ret = msgs[1].buf[0];
+   mutex_unlock(>buf_mutex);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -33,6 +108,9 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
for (i = 0; i < data->info.pages; i++)
data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+   mutex_init(>buf_mutex);
+
return pmbus_do_probe(client, id, >info);
 }
 
-- 
2.20.1



[PATCH v8 4/6] hwmon: pmbus: adm1266: add debugfs for states

2020-08-12 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs entry which prints the current state
of the adm1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 41 ++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 7ca5aa751b7e..df7954e2ce9f 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -19,6 +19,7 @@
 #include 
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -43,6 +44,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct dentry *debugfs_dir;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -292,6 +294,37 @@ static int adm1266_config_gpio(struct adm1266_data *data)
return ret;
 }
 
+static int adm1266_state_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   int ret;
+
+   ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+   if (ret < 0)
+   return ret;
+
+   seq_printf(s, "%d\n", ret);
+
+   return 0;
+}
+
+static void adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return;
+
+   debugfs_create_devm_seqfile(>client->dev, "sequencer_state", 
data->debugfs_dir,
+   adm1266_state_read);
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -315,7 +348,13 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
if (ret < 0)
return ret;
 
-   return pmbus_do_probe(client, id, >info);
+   ret = pmbus_do_probe(client, id, >info);
+   if (ret)
+   return ret;
+
+   adm1266_init_debugfs(data);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v5 1/2] iio: accel: adxl372: Add support for FIFO peak mode

2020-08-10 Thread alexandru.tachici
From: Stefan Popa 

By default, if all three channels (x, y, z) are enabled, sample sets of
concurrent 3-axis data is stored in the FIFO. This patch adds the option
to configure the FIFO to store peak acceleration (x, y and z) of every
over-threshold event. When pushing to iio buffer we push only enabled
axis data.

Currently the driver configures adxl372 to work in loop mode.
The inactivity and activity timings  decide how fast the chip
will loop through the awake and waiting states and the
thresholds on x,y,z axis decide when activity or inactivity
will be detected.

This patch adds standard events sysfs entries for the inactivity
and activity timings: thresh_falling_period/thresh_rising_period
and for the in_accel_x_thresh_falling/rising_value.

Signed-off-by: Stefan Popa 
Signed-off-by: Alexandru Tachici 
---
 drivers/iio/accel/adxl372.c | 302 +++-
 1 file changed, 294 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 67b8817995c0..4cad16e2f7b7 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -5,6 +5,7 @@
  * Copyright 2018 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -113,6 +114,11 @@
 #define ADXL372_STATUS_1_AWAKE(x)  (((x) >> 6) & 0x1)
 #define ADXL372_STATUS_1_ERR_USR_REGS(x)   (((x) >> 7) & 0x1)
 
+/* ADXL372_STATUS_2 */
+#define ADXL372_STATUS_2_INACT(x)  (((x) >> 4) & 0x1)
+#define ADXL372_STATUS_2_ACT(x)(((x) >> 5) & 0x1)
+#define ADXL372_STATUS_2_AC2(x)(((x) >> 6) & 0x1)
+
 /* ADXL372_INT1_MAP */
 #define ADXL372_INT1_MAP_DATA_RDY_MSK  BIT(0)
 #define ADXL372_INT1_MAP_DATA_RDY_MODE(x)  (((x) & 0x1) << 0)
@@ -131,8 +137,17 @@
 #define ADXL372_INT1_MAP_LOW_MSK   BIT(7)
 #define ADXL372_INT1_MAP_LOW_MODE(x)   (((x) & 0x1) << 7)
 
+/* ADX372_THRESH */
+#define ADXL372_THRESH_VAL_H_MSK   GENMASK(10, 3)
+#define ADXL372_THRESH_VAL_H_SEL(x)FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x)
+#define ADXL372_THRESH_VAL_L_MSK   GENMASK(2, 0)
+#define ADXL372_THRESH_VAL_L_SEL(x)FIELD_GET(ADXL372_THRESH_VAL_L_MSK, x)
+
 /* The ADXL372 includes a deep, 512 sample FIFO buffer */
 #define ADXL372_FIFO_SIZE  512
+#define ADXL372_X_AXIS_EN(x)   ((x) & BIT(0))
+#define ADXL372_Y_AXIS_EN(x)   ((x) & BIT(1))
+#define ADXL372_Z_AXIS_EN(x)   ((x) & BIT(2))
 
 /*
  * At +/- 200g with 12-bit resolution, scale is computed as:
@@ -222,6 +237,20 @@ static const struct adxl372_axis_lookup 
adxl372_axis_lookup_table[] = {
{ BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
 };
 
+static const struct iio_event_spec adxl372_events[] = {
+   {
+   .type = IIO_EV_TYPE_THRESH,
+   .dir = IIO_EV_DIR_RISING,
+   .mask_separate = BIT(IIO_EV_INFO_VALUE),
+   .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) | 
BIT(IIO_EV_INFO_ENABLE),
+   }, {
+   .type = IIO_EV_TYPE_THRESH,
+   .dir = IIO_EV_DIR_FALLING,
+   .mask_separate = BIT(IIO_EV_INFO_VALUE),
+   .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) | 
BIT(IIO_EV_INFO_ENABLE),
+   },
+};
+
 #define ADXL372_ACCEL_CHANNEL(index, reg, axis) {  \
.type = IIO_ACCEL,  \
.address = reg, \
@@ -238,6 +267,8 @@ static const struct adxl372_axis_lookup 
adxl372_axis_lookup_table[] = {
.storagebits = 16,  \
.shift = 4, \
},  \
+   .event_spec = adxl372_events,   \
+   .num_event_specs = ARRAY_SIZE(adxl372_events)   \
 }
 
 static const struct iio_chan_spec adxl372_channels[] = {
@@ -251,8 +282,10 @@ struct adxl372_state {
struct device   *dev;
struct regmap   *regmap;
struct iio_trigger  *dready_trig;
+   struct iio_trigger  *peak_datardy_trig;
enum adxl372_fifo_mode  fifo_mode;
enum adxl372_fifo_formatfifo_format;
+   unsigned intfifo_axis_mask;
enum adxl372_op_modeop_mode;
enum adxl372_act_proc_mode  act_proc_mode;
enum adxl372_odrodr;
@@ -264,6 +297,8 @@ struct adxl372_state {
u8  int2_bitmask;
u16 watermark;
__be16  fifo_buf[ADXL372_FIFO_SIZE];
+   boolpeak_fifo_mode_en;
+   struct mutexthreshold_m; /* lock for threshold */
 };
 
 static const unsigned 

[PATCH v5 2/2] iio: accel: adxl372: Add additional trigger ABI docs

2020-08-10 Thread alexandru.tachici
From: Alexandru Tachici 

Document use of additional trigger supplied by driver.

Signed-off-by: Alexandru Tachici 
---
 Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372 | 7 +++
 1 file changed, 7 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372 
b/Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372
new file mode 100644
index ..47e34f865ca1
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372
@@ -0,0 +1,7 @@
+What:  /sys/bus/iio/devices/triggerX/name = "adxl372-devX-peak"
+KernelVersion:
+Contact:   linux-...@vger.kernel.org
+Description:
+   The adxl372 accelerometer kernel module provides an additional 
trigger,
+   which sets the device in a mode in which it will record only 
the peak acceleration
+   sensed over the set period of time in the events sysfs.
-- 
2.20.1



[PATCH v5 0/2] iio: accel: adxl372: Add support for FIFO peak mode

2020-08-10 Thread alexandru.tachici
From: Alexandru Tachici 

This series adds the possibility to configure
the device, from sysfs, to work in peak mode. This enables
adxl372 to capture only over threshold accelerations.

1. Create sysfs files for falling_period/rising_period
and thresh_falling_value/thresh_rising_value in events/ dir.
Set INT1 reg for activity/inactivity and push
event code in events fifo on irq.

2. Document use of iio events sysfs files.

Alexandru Tachici (1):
  iio: accel: adxl372: Add additional trigger ABI docs

Stefan Popa (1):
  iio: accel: adxl372: Add support for FIFO peak mode

Changelog v4 -> v5:
  - squash iio event interface patch into peak mode patch
  - move patch to 100 lines length
  - use((x) & BIT(0)) construct for define ADXL372_Y_AXIS_EN(x)
  - add ABI docs to explain the use of the additional trigger

 .../ABI/testing/sysfs-bus-iio-accel-adxl372   |   7 +
 drivers/iio/accel/adxl372.c   | 302 +-
 2 files changed, 301 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372

-- 
2.20.1



[PATCH v4 3/3] docs: iio: Add adxl372 documentation

2020-08-03 Thread alexandru.tachici
From: Alexandru Tachici 

Add documentation for adxl372 3-axis accelerometer.

Signed-off-by: Alexandru Tachici 
---
 Documentation/iio/adxl372.rst | 46 +++
 Documentation/iio/index.rst   |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Documentation/iio/adxl372.rst

diff --git a/Documentation/iio/adxl372.rst b/Documentation/iio/adxl372.rst
new file mode 100644
index ..f8fe5f438400
--- /dev/null
+++ b/Documentation/iio/adxl372.rst
@@ -0,0 +1,46 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adxl372
+=
+
+Supported chips:
+  * Analog Devices ADXL372
+Prefix: 'adxl372'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL372.pdf
+
+Author: Stefan Popa 
+
+
+Description
+---
+
+The ADXL372 is an ultralow power, 3-axis, ±200 g MEMS accelerometer
+that consumes 22 μA at a 3200 Hz output data rate (ODR).
+
+The ADXL372 provides 12-bit output data at 100 mg/LSB scale factor.
+
+Using the FIFO Buffer
+-
+
+The ADXL372 includes a deep, 512 sample FIFO buffer.
+The 512 FIFO samples can be allotted in several ways, such as the following:
+
+170 sample sets of concurrent 3-axis data
+256 sample sets of concurrent 2-axis data (see scan_elements/in_accel_*_en)
+512 sample sets of single-axis data
+170 sets of impact event peak (x, y, z)
+
+By default when using the buffer adxl372 will store all
+acceleration data. To store only the peak acceleration data, the user must
+select the peak data trigger: adxl372-dev0-peak
+
+The user can set the thresholds for each axis for activity and inactivity in:
+- events/in_accel_*_thresh_rising_value
+- events/in_accel_*_thresh_falling_value
+
+An inactivity/activity event is detected when acceleration in all enabled
+axes remains below/above a specified threshold for a specified time. The user
+can set these timings in:
+- events/thresh_falling_period
+- events/thresh_rising_period
+
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 58b7a4ebac51..3d0acb1eef86 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -10,3 +10,4 @@ Industrial I/O
iio_configfs
 
ep93xx_adc
+   adxl372.rst
-- 
2.20.1



[PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO peak mode

2020-08-03 Thread alexandru.tachici
From: Stefan Popa 

By default, if all three channels (x, y, z) are enabled, sample sets of
concurrent 3-axis data is stored in the FIFO. This patch adds the option
to configure the FIFO to store peak acceleration (x, y and z) of every
over-threshold event. When pushing to iio buffer we push only enabled
axis data.

Signed-off-by: Stefan Popa 
---
 drivers/iio/accel/adxl372.c | 72 +++--
 1 file changed, 70 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 67b8817995c0..cce25cde6252 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -133,6 +133,9 @@
 
 /* The ADXL372 includes a deep, 512 sample FIFO buffer */
 #define ADXL372_FIFO_SIZE  512
+#define ADXL372_X_AXIS_EN(x)   (((x) >> 0) & 0x1)
+#define ADXL372_Y_AXIS_EN(x)   (((x) >> 1) & 0x1)
+#define ADXL372_Z_AXIS_EN(x)   (((x) >> 2) & 0x1)
 
 /*
  * At +/- 200g with 12-bit resolution, scale is computed as:
@@ -251,8 +254,10 @@ struct adxl372_state {
struct device   *dev;
struct regmap   *regmap;
struct iio_trigger  *dready_trig;
+   struct iio_trigger  *peak_datardy_trig;
enum adxl372_fifo_mode  fifo_mode;
enum adxl372_fifo_formatfifo_format;
+   unsigned intfifo_axis_mask;
enum adxl372_op_modeop_mode;
enum adxl372_act_proc_mode  act_proc_mode;
enum adxl372_odrodr;
@@ -264,6 +269,7 @@ struct adxl372_state {
u8  int2_bitmask;
u16 watermark;
__be16  fifo_buf[ADXL372_FIFO_SIZE];
+   boolpeak_fifo_mode_en;
 };
 
 static const unsigned long adxl372_channel_masks[] = {
@@ -522,6 +528,22 @@ static int adxl372_get_status(struct adxl372_state *st,
return ret;
 }
 
+static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
+{
+   __be16  axis_sample[3];
+   int i = 0;
+
+   memset(axis_sample, 0, 3 * sizeof(__be16));
+   if (ADXL372_X_AXIS_EN(st->fifo_axis_mask))
+   axis_sample[i++] = sample[0];
+   if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask))
+   axis_sample[i++] = sample[1];
+   if (ADXL372_Z_AXIS_EN(st->fifo_axis_mask))
+   axis_sample[i++] = sample[2];
+
+   memcpy(sample, axis_sample, 3 * sizeof(__be16));
+}
+
 static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
 {
struct iio_poll_func *pf = p;
@@ -553,8 +575,12 @@ static irqreturn_t adxl372_trigger_handler(int irq, void  
*p)
goto err;
 
/* Each sample is 2 bytes */
-   for (i = 0; i < fifo_entries; i += st->fifo_set_size)
+   for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
+   /* filter peak detection data */
+   if (st->peak_fifo_mode_en)
+   adxl372_arrange_axis_data(st, >fifo_buf[i]);
iio_push_to_buffers(indio_dev, >fifo_buf[i]);
+   }
}
 err:
iio_trigger_notify_done(indio_dev->trig);
@@ -815,13 +841,22 @@ static int adxl372_buffer_postenable(struct iio_dev 
*indio_dev)
}
 
st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
+   st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits;
st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
  indio_dev->masklength);
+
+   /* Configure the FIFO to store sets of impact event peak. */
+   if (st->peak_fifo_mode_en) {
+   st->fifo_set_size = 3;
+   st->fifo_format = ADXL372_XYZ_PEAK_FIFO;
+   }
+
/*
 * The 512 FIFO samples can be allotted in several ways, such as:
 * 170 sample sets of concurrent 3-axis data
 * 256 sample sets of concurrent 2-axis data (user selectable)
 * 512 sample sets of single-axis data
+* 170 sets of impact event peak (x, y, z)
 */
if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE)
st->watermark = (ADXL372_FIFO_SIZE  / st->fifo_set_size);
@@ -876,7 +911,7 @@ static int adxl372_validate_trigger(struct iio_dev 
*indio_dev,
 {
struct adxl372_state *st = iio_priv(indio_dev);
 
-   if (st->dready_trig != trig)
+   if (st->dready_trig != trig && st->peak_datardy_trig != trig)
return -EINVAL;
 
return 0;
@@ -887,6 +922,25 @@ static const struct iio_trigger_ops adxl372_trigger_ops = {
.set_trigger_state = adxl372_dready_trig_set_state,
 };
 
+static int adxl372_peak_dready_trig_set_state(struct iio_trigger *trig,
+ bool state)
+{
+   struct 

[PATCH v4 0/3] iio: accel: adxl372: add peak mode

2020-08-03 Thread alexandru.tachici
From: Alexandru Tachici 

This series adds the possibility to configure
the device, from sysfs, to work in peak mode. This enables
adxl372 to capture only over threshold accelerations.

1. Create sysfs files for falling_period/rising_period
and thresh_falling_value/thresh_rising_value in events/ dir.
Set INT1 reg for activity/inactivity and push
event code in events fifo on irq.

2. Document use of iio events sysfs files.

Alexandru Tachici (2):
  iio: accel: adxl372: add event interface
  docs: iio: Add adxl372 documentation

1. Device FIFO can now be set in peak mode and only over the
threshold accelerations will be stored. Driver sets adxl372
FIFO in peak mode when the peak iio:trigger is selected.

Stefan Popa (1):
  iio: accel: adxl372: Add support for FIFO peak mode

  Changelog v3 -> v4:
  - add mutex for threshold write so that writing the two
  registers becomes atomic
  - added a trigger for peak mode, when selected in the trigger
  user interface, adxl372 will start recording only peak acceleration
  data in the fifo
  - added a adxl372.rst doc file explaining the use of iio events sysfs

 Documentation/iio/adxl372.rst |  46 +
 Documentation/iio/index.rst   |   1 +
 drivers/iio/accel/adxl372.c   | 328 +-
 3 files changed, 367 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/iio/adxl372.rst

-- 
2.20.1



[PATCH v4 2/3] iio: accel: adxl372: add event interface

2020-08-03 Thread alexandru.tachici
From: Alexandru Tachici 

Currently the driver configures adxl372 to work in loop mode.
The inactivity and activity timings  decide how fast the chip
will loop through the awake and waiting states and the
thresholds on x,y,z axis decide when activity or inactivity
will be detected.

This patch adds standard events sysfs entries for the inactivity
and activity timings: thresh_falling_period/thresh_rising_period
and for the in_accel_x_thresh_falling/rising_value.

Signed-off-by: Alexandru Tachici 
---
 drivers/iio/accel/adxl372.c | 256 +++-
 1 file changed, 250 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index cce25cde6252..644c409862b5 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -5,6 +5,7 @@
  * Copyright 2018 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -113,6 +114,11 @@
 #define ADXL372_STATUS_1_AWAKE(x)  (((x) >> 6) & 0x1)
 #define ADXL372_STATUS_1_ERR_USR_REGS(x)   (((x) >> 7) & 0x1)
 
+/* ADXL372_STATUS_2 */
+#define ADXL372_STATUS_2_INACT(x)  (((x) >> 4) & 0x1)
+#define ADXL372_STATUS_2_ACT(x)(((x) >> 5) & 0x1)
+#define ADXL372_STATUS_2_AC2(x)(((x) >> 6) & 0x1)
+
 /* ADXL372_INT1_MAP */
 #define ADXL372_INT1_MAP_DATA_RDY_MSK  BIT(0)
 #define ADXL372_INT1_MAP_DATA_RDY_MODE(x)  (((x) & 0x1) << 0)
@@ -131,6 +137,14 @@
 #define ADXL372_INT1_MAP_LOW_MSK   BIT(7)
 #define ADXL372_INT1_MAP_LOW_MODE(x)   (((x) & 0x1) << 7)
 
+/* ADX372_THRESH */
+#define ADXL372_THRESH_VAL_H_MSK   GENMASK(10, 3)
+#define ADXL372_THRESH_VAL_H_SEL(x)\
+   FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x)
+#define ADXL372_THRESH_VAL_L_MSK   GENMASK(2, 0)
+#define ADXL372_THRESH_VAL_L_SEL(x)\
+   FIELD_GET(ADXL372_THRESH_VAL_L_MSK, x)
+
 /* The ADXL372 includes a deep, 512 sample FIFO buffer */
 #define ADXL372_FIFO_SIZE  512
 #define ADXL372_X_AXIS_EN(x)   (((x) >> 0) & 0x1)
@@ -225,6 +239,22 @@ static const struct adxl372_axis_lookup 
adxl372_axis_lookup_table[] = {
{ BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
 };
 
+static const struct iio_event_spec adxl372_events[] = {
+   {
+   .type = IIO_EV_TYPE_THRESH,
+   .dir = IIO_EV_DIR_RISING,
+   .mask_separate = BIT(IIO_EV_INFO_VALUE),
+   .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_ENABLE),
+   }, {
+   .type = IIO_EV_TYPE_THRESH,
+   .dir = IIO_EV_DIR_FALLING,
+   .mask_separate = BIT(IIO_EV_INFO_VALUE),
+   .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_ENABLE),
+   },
+};
+
 #define ADXL372_ACCEL_CHANNEL(index, reg, axis) {  \
.type = IIO_ACCEL,  \
.address = reg, \
@@ -241,6 +271,8 @@ static const struct adxl372_axis_lookup 
adxl372_axis_lookup_table[] = {
.storagebits = 16,  \
.shift = 4, \
},  \
+   .event_spec = adxl372_events,   \
+   .num_event_specs = ARRAY_SIZE(adxl372_events)   \
 }
 
 static const struct iio_chan_spec adxl372_channels[] = {
@@ -270,6 +302,7 @@ struct adxl372_state {
u16 watermark;
__be16  fifo_buf[ADXL372_FIFO_SIZE];
boolpeak_fifo_mode_en;
+   struct mutexthreshold_m; /* lock for threshold */
 };
 
 static const unsigned long adxl372_channel_masks[] = {
@@ -281,6 +314,49 @@ static const unsigned long adxl372_channel_masks[] = {
0
 };
 
+static ssize_t adxl372_read_threshold_value(struct iio_dev *indio_dev,
+   unsigned int addr,
+   u16 *threshold)
+{
+   struct adxl372_state *st = iio_priv(indio_dev);
+   __be16 raw_regval;
+   u16 regval;
+   int ret;
+
+   ret = regmap_bulk_read(st->regmap, addr, _regval,
+  sizeof(raw_regval));
+   if (ret < 0)
+   return ret;
+
+   regval = be16_to_cpu(raw_regval);
+   regval >>= 5;
+
+   *threshold = regval;
+
+   return 0;
+}
+
+static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev,
+unsigned int addr,
+u16 threshold)
+{
+   struct adxl372_state *st = iio_priv(indio_dev);
+

Wrong list

2020-07-27 Thread alexandru.tachici
Please disregard this patch.
I used the wrong git send-email cmd.


[PATCH v7 4/9] hwmon: pmbus: adm1266: add debugfs for states

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs entry which prints the current state
of the adm1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 41 ++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index be911de02cf6..0ea016b7e113 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -19,6 +19,7 @@
 #include 
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -43,6 +44,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct dentry *debugfs_dir;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -287,6 +289,37 @@ static int adm1266_config_gpio(struct adm1266_data *data)
return ret;
 }
 
+static int adm1266_state_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   int ret;
+
+   ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+   if (ret < 0)
+   return ret;
+
+   seq_printf(s, "%d\n", ret);
+
+   return 0;
+}
+
+static void adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return;
+
+   debugfs_create_devm_seqfile(>client->dev, "sequencer_state", 
data->debugfs_dir,
+   adm1266_state_read);
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -310,7 +343,13 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
if (ret < 0)
return ret;
 
-   return pmbus_do_probe(client, id, >info);
+   ret = pmbus_do_probe(client, id, >info);
+   if (ret)
+   return ret;
+
+   adm1266_init_debugfs(data);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v7 3/9] hwmon: pmbus: adm1266: Add support for GPIOs

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 204 ++
 1 file changed, 204 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 63975eba34ad..be911de02cf6 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,18 +6,42 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -88,9 +112,185 @@ static int adm1266_pmbus_block_xfer(struct adm1266_data 
*data, u8 cmd, u8 w_len,
return ret;
 }
 
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long 
*mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + 
ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, ))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long gpio_config;
+   unsigned long pdio_config;
+   unsigned long pin_cfg;
+   u8 write_cmd;
+   int ret;
+   int i;
+
+   for (i = 0; i < ADM1266_GPIO_NR; i++) {
+   write_cmd = 

[PATCH v7 5/9] hwmon: pmbus: adm1266: read blackbox

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Using this feature, the device is capable of recording
to nonvolatile flash memory the vital data about the
system status that caused the system to perform a
black box write.

A blackbox is 64 bytes of data containing all the
status registers, last two states of the sequencer,
timestamp and counters. The mapping of this data is
described in the adm1266 datasheet.

On power-up the driver sets the unix time to
the adm1266 using the SET_RTC command. This value
is incremented by an internal clock and it is used
as timestamp for the black box feature.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 165 ++
 1 file changed, 165 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 0ea016b7e113..b61a968d67f9 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,12 +15,19 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 #include 
+#include 
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
+#define ADM1266_SET_RTC0xDF
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -37,6 +44,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
@@ -45,11 +55,22 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct dentry *debugfs_dir;
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
+};
+
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
 /*
@@ -320,6 +341,142 @@ static void adm1266_init_debugfs(struct adm1266_data 
*data)
adm1266_state_read);
 }
 
+#if IS_ENABLED(CONFIG_NVMEM)
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 
*read_buff)
+{
+   int record_count;
+   char index;
+   u8 buf[5];
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, 
buf);
+   if (ret < 0)
+   return ret;
+
+   if (ret != 4)
+   return -EIO;
+
+   record_count = buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, 
, read_buff);
+   if (ret < 0)
+   return ret;
+
+   if (ret != ADM1266_BLACKBOX_SIZE)
+   return -EIO;
+
+   read_buff += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell, 
unsigned int offset,
+size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   return start_addr <= cell_end && cell_start <= end_addr;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data, const struct 
nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+
+   memset(mem_offset, 0, ADM1266_BLACKBOX_SIZE);
+
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   default:
+   return -EINVAL;
+   }
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+   const struct nvmem_cell_info *mem_cell;
+   struct adm1266_data *data = priv;
+   int ret;
+   int i;
+
+   for (i = 0; i < data->nvmem_config.ncells; i++) {
+   mem_cell = _nvmem_cells[i];
+   if (!adm1266_cell_is_accessed(mem_cell, offset, bytes))
+

[PATCH v7 2/9] hwmon: pmbus: adm1266: Add Block process call

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 73 +++
 2 files changed, 74 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da34083e1ffd..c04068b665e6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 79e8d90886b8..63975eba34ad 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -13,11 +14,80 @@
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
 struct adm1266_data {
struct pmbus_driver_info info;
struct i2c_client *client;
+   struct mutex buf_mutex;
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/*
+ * Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+static int adm1266_pmbus_block_xfer(struct adm1266_data *data, u8 cmd, u8 
w_len, u8 *data_w,
+   u8 *data_r)
+{
+   struct i2c_client *client = data->client;
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = I2C_M_DMA_SAFE,
+   .buf = data->write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD | I2C_M_DMA_SAFE,
+   .buf = data->read_buf,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr;
+   u8 crc;
+   int ret;
+
+   mutex_lock(>buf_mutex);
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   if (ret >= 0)
+   ret = -EPROTO;
+   return ret;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, 0);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1, 
crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1])
+   return -EBADMSG;
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+
+   ret = msgs[1].buf[0];
+   mutex_unlock(>buf_mutex);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -33,6 +103,9 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
for (i = 0; i < data->info.pages; i++)
data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+   mutex_init(>buf_mutex);
+
return pmbus_do_probe(client, id, >info);
 }
 
-- 
2.20.1



[PATCH v7 7/9] hwmon: pmbus: adm1266: program firmware

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the firmware Intel hex file to the nvmem,
of the master adm1266,  with offset 0, will now
trigger the firmware programming of all cascaded
devices simultaneously through pmbus.

During this process all adm1266 sequencers will be
stopped and at the end will be issued a hard reset
(see AN-1453 Programming the firmware).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 501 +-
 1 file changed, 500 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index f571fe1ee35d..f851c6617870 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -18,18 +19,31 @@
 #include 
 #include 
 #include "pmbus.h"
+#include 
 #include 
 #include 
 
+#define ADM1266_STORE_USER_ALL 0x15
+#define ADM1266_STATUS_MFR 0x80
+#define ADM1266_IC_DEVICE_REV  0xAE
 #define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_SEQUENCE_CONFIG0xD6
+#define ADM1266_SYSTEM_CONFIG  0xD7
+#define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
 #define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_SET_RTC0xDF
+#define ADM1266_LOGIC_CONFIG   0xE0
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_USER_DATA  0xE3
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_SWITCH_MEMORY  0xFA
+#define ADM1266_UPDATE_FW  0xFC
+#define ADM1266_FW_PASSWORD0xFD
 
 /* ADM1266 GPIO defines */
 #define ADM1266_GPIO_NR9
@@ -44,10 +58,35 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+/* ADM1266 FW_PASSWORD defines*/
+#define ADM1266_PASSWD_CMD_LEN 17
+#define ADM1266_CHANGE_PASSWORD1
+#define ADM1266_UNLOCK_DEV 2
+#define ADM1266_LOCK_DEV   3
+
+/* ADM1266 STATUS_MFR defines */
+#define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+
+/* ADM1266 GO_COMMAND defines */
+#define ADM1266_GO_COMMAND_STOPBIT(0)
+#define ADM1266_GO_COMMAND_SEQ_RES BIT(1)
+#define ADM1266_GO_COMMAND_HARD_RESBIT(2)
+
+#define ADM1266_FIRMWARE_OFFSET0x0
+#define ADM1266_FIRMWARE_SIZE  131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
 #define ADM1266_PMBUS_BLOCK_MAX255
+#define ADM1266_MAX_DEVICES16
+
+static LIST_HEAD(registered_masters);
+static DEFINE_MUTEX(registered_masters_lock);
+
+struct adm1266_data_ref {
+   struct adm1266_data *data;
+   struct list_head list;
+};
 
 struct adm1266_data {
struct pmbus_driver_info info;
@@ -57,6 +96,10 @@ struct adm1266_data {
struct dentry *debugfs_dir;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
+   bool master_dev;
+   struct list_head cascaded_devices_list;
+   struct mutex cascaded_devices_mutex; /* lock cascaded_devices_list */
+   u8 nr_devices;
u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -69,6 +112,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = 
{
.offset = ADM1266_BLACKBOX_OFFSET,
.bytes  = 2048,
},
+   {
+   .name   = "firmware",
+   .offset = ADM1266_FIRMWARE_OFFSET,
+   .bytes  = ADM1266_FIRMWARE_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -123,6 +171,27 @@ static int adm1266_pmbus_group_command(struct adm1266_data 
*data, struct i2c_cli
return ret;
 }
 
+static int adm1266_group_cmd(struct adm1266_data *data, u8 cmd, u8 
*write_data, u8 w_len,
+bool to_slaves)
+{
+   struct i2c_client *clients[ADM1266_MAX_DEVICES];
+   struct adm1266_data_ref *slave_ref;
+   int i = 0;
+
+   clients[i] = data->client;
+   i++;
+
+   if (!to_slaves)
+   return adm1266_pmbus_group_command(data, clients, 1, cmd, 
w_len, write_data);
+
+   list_for_each_entry(slave_ref, >cascaded_devices_list, list) {
+   clients[i] = slave_ref->data->client;
+   i++;
+   }
+
+   return adm1266_pmbus_group_command(data, clients, i, cmd, w_len, 
write_data);
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
@@ -448,6 +517,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
if (ret)
dev_err(>client->dev, "Could not read 

[PATCH v7 1/9] hwmon: pmbus: adm1266: add support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 37 +++
 Documentation/hwmon/index.rst   |  1 +
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 65 +
 5 files changed, 113 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..9257f8a48650
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,37 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55ff4b7c5349..056f7107d7b8 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -30,6 +30,7 @@ Hardware Monitoring Kernel Drivers
adm1026
adm1031
adm1177
+   adm1266
adm1275
adm9240
ads7828
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a337195b1c39..da34083e1ffd 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c4b15db996ad..da41d22be1c9 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..79e8d90886b8
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "pmbus.h"
+#include 
+
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct i2c_client *client;
+};
+
+static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
+{
+   struct adm1266_data *data;
+   int i;
+
+   data = devm_kzalloc(>dev, sizeof(struct adm1266_data), 
GFP_KERNEL);
+   if (!data)
+   return -ENOMEM;
+
+   data->client = client;
+   data->info.pages = 17;
+   data->info.format[PSC_VOLTAGE_OUT] = linear;
+   for (i = 0; i < data->info.pages; i++)
+   data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+   return pmbus_do_probe(client, id, >info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+  

[PATCH v7 6/9] hwmon: pmbus: adm1266: Add group command support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

The Group Command Protocol is used to send commands
to more than one PMBus device. Some devices working
together require that they must execute some commands
all at once.

The commands are sent in one continuous transmission.
When the devices detect the STOP condition that ends
the sending of commands, they all begin executing
the command they received.

This patch adds support for the group command protocol.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 50 +++
 1 file changed, 50 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index b61a968d67f9..f571fe1ee35d 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -73,6 +73,56 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
+/* PMBus Group command. */
+static int adm1266_pmbus_group_command(struct adm1266_data *data, struct 
i2c_client **clients,
+  u8 nr_clients, u8 cmd, u8 w_len, u8 
*data_w)
+{
+   struct i2c_msg *msgs;
+   u8 addr;
+   int ret;
+   int i;
+
+   msgs = kcalloc(nr_clients, sizeof(struct i2c_msg), GFP_KERNEL);
+   if (!msgs)
+   return -ENOMEM;
+
+   for (i = 0; i < nr_clients; i++) {
+   msgs[i].addr = clients[i]->addr;
+   msgs[i].len = w_len + 1;
+
+   msgs[i].buf = kcalloc(ADM1266_PMBUS_BLOCK_MAX + 2, sizeof(u8), 
GFP_KERNEL);
+   if (!msgs[i].buf) {
+   ret = -ENOMEM;
+   goto cleanup;
+   }
+
+   msgs[i].buf[0] = cmd;
+   memcpy([i].buf[1], data_w, w_len);
+
+   if (clients[i]->flags & I2C_CLIENT_PEC) {
+   u8 crc = 0;
+
+   addr = i2c_8bit_addr_from_msg([i]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[i].buf, msgs[i].len,
+  crc);
+
+   msgs[i].buf[msgs[i].len] = crc;
+   msgs[i].len++;
+   }
+   };
+
+   ret = i2c_transfer(data->client->adapter, msgs, nr_clients);
+
+cleanup:
+   for (i = i - 1; i >= 0; i--)
+   kfree(msgs[i].buf);
+
+   kfree(msgs);
+
+   return ret;
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
-- 
2.20.1



[PATCH v7 8/9] hwmon: pmbus: adm1266: program configuration

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the configuration Intel hex file to the nvmem,
of an adm1266, with offset 0x3, will now
trigger the configuration programming.

During this process the adm1266 sequencer will be
stopped and at the end will be issued a seq reset
(see AN-1453 Programming the configuration).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 179 +-
 1 file changed, 178 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index f851c6617870..50386c98d714 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -40,7 +40,10 @@
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_STATUS_MFR_2   0xED
+#define ADM1266_REFRESH_FLASH  0xF5
 #define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_MEMORY_CRC 0xF9
 #define ADM1266_SWITCH_MEMORY  0xFA
 #define ADM1266_UPDATE_FW  0xFC
 #define ADM1266_FW_PASSWORD0xFD
@@ -66,6 +69,11 @@
 
 /* ADM1266 STATUS_MFR defines */
 #define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+#define ADM1266_RUNNING_REFRESH(x) FIELD_GET(BIT(3), x)
+#define ADM1266_ALL_CRC_FAULT(x)   FIELD_GET(BIT(5), x)
+
+/* ADM1266 STATUS_MFR_2 defines */
+#define ADM1266_MAIN_CONFIG_FAULT(x)   FIELD_GET(GENMASK(9, 8), x)
 
 /* ADM1266 GO_COMMAND defines */
 #define ADM1266_GO_COMMAND_STOPBIT(0)
@@ -74,6 +82,8 @@
 
 #define ADM1266_FIRMWARE_OFFSET0x0
 #define ADM1266_FIRMWARE_SIZE  131072
+#define ADM1266_CONFIG_OFFSET  0x3
+#define ADM1266_CONFIG_SIZE131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
@@ -117,6 +127,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] 
= {
.offset = ADM1266_FIRMWARE_OFFSET,
.bytes  = ADM1266_FIRMWARE_SIZE,
},
+   {
+   .name   = "configuration",
+   .offset = ADM1266_CONFIG_OFFSET,
+   .bytes  = ADM1266_CONFIG_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -520,6 +535,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
case ADM1266_FIRMWARE_OFFSET:
/* firmware is write-only */
return 0;
+   case ADM1266_CONFIG_OFFSET:
+   /* configuration is write-only */
+   return 0;
default:
return -EINVAL;
}
@@ -676,6 +694,7 @@ static int adm1266_write_hex(struct adm1266_data *data,
u8 first_writes[7];
u8 byte_count;
u8 reg_address;
+   bool to_slaves = false;
int ret;
int i;
 
@@ -706,7 +725,10 @@ static int adm1266_write_hex(struct adm1266_data *data,
if (ret < 0)
return ret;
 
-   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, true);
+   if (offset == ADM1266_FIRMWARE_OFFSET)
+   to_slaves = true;
+
+   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, to_slaves);
if (ret < 0) {
dev_err(>client->dev, "Firmware write error: 
%d.", ret);
return ret;
@@ -731,6 +753,87 @@ static int adm1266_write_hex(struct adm1266_data *data,
return 0;
 }
 
+static int adm1266_verify_memory(struct adm1266_data *data)
+{
+   char cmd[2];
+   int ret;
+   int reg;
+
+   cmd[0] = 0x1;
+   cmd[1] = 0x0;
+   ret = adm1266_group_cmd(data, ADM1266_MEMORY_CRC, cmd,
+   sizeof(cmd), true);
+   if (ret < 0)
+   return ret;
+
+   /* after issuing a memory recalculate crc command, wait 1000 ms */
+   msleep(1000);
+
+   reg = pmbus_read_word_data(data->client, 0, 0xFF, ADM1266_STATUS_MFR_2);
+   if (reg < 0)
+   return reg;
+
+   if (ADM1266_MAIN_CONFIG_FAULT(reg)) {
+   dev_err(>client->dev, "Main memory corrupted.");
+   return -EFAULT;
+   }
+
+   return 0;
+}
+
+static int adm1266_refresh_memory(struct adm1266_data *data)
+{
+   unsigned int timeout = 9000;
+   int ret;
+   u8 cmd[2];
+
+   cmd[0] = 0x2;
+   ret = adm1266_group_cmd(data, ADM1266_REFRESH_FLASH, cmd, 1, true);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not refresh flash.");
+   return ret;
+   }
+
+   /* after issuing a refresh flash command, wait 9000 ms */
+   msleep(9000);
+
+   do {
+   msleep(1000);
+   timeout -= 1000;
+
+   ret = pmbus_read_byte_data(data->client, 0, ADM1266_STATUS_MFR);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not read status.");
+   

[PATCH v7 9/9] dt-bindings: hwmon: Add bindings for ADM1266

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..ad92686e2ee6
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+};
+};
+...
-- 
2.20.1



[PATCH v7 0/9] hwmon: pmbus: adm1266: add support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Allow the current sate of the sequencer to be read
through debugfs.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Add group command support. This will allow the driver
to stop/program all cascaded adm1266 devices at once.

7. Writing the firmware hex file with offset 0
to the nvmem of the master adm1266 will trigger
the firmware programming of all cascaded devices.
The master adm1266 of each device is specified in
the devicetree.

8. Writing the configuration hex file to 0x3
byte address of the nvmem file will trigger the
programming of that device in particular.

9. DT bindings for ADM1266.

Alexandru Tachici (9):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: add debugfs for states
  hwmon: pmbus: adm1266: read blackbox
  hwmon: pmbus: adm1266: Add group command support
  hwmon: pmbus: adm1266: program firmware
  hwmon: pmbus: adm1266: program configuration
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog: v5 -> v6:
  - added adm1266 to index.rst
  - changed max lines length from 80 to 100
  - replaced i2c_get_dma_safe_msg_buf with the use of kzalloc and 
cacheline_aligned
  - removed #ifdef CONFIG_GPIOLIB
  - removed ioctl commands, the state of the device can be read through debugfs
  - use the device managed version of nvmem_register
  - on power-up, set the UNIX time to adm1266 (this value is reset
  to 0 on each power-cycle).
  - removed patch adm1266: debugfs for blackbox info, rtc in blackbox is enough
  to help identify the current index
  - added two new nvmem cells for firmware and configuration

Changelog: v6 -> v7:
  - fixed compilation warning: removed unused variable ,entry, in
adm1266_init_debugfs

 .../bindings/hwmon/adi,adm1266.yaml   |   56 +
 Documentation/hwmon/adm1266.rst   |   37 +
 Documentation/hwmon/index.rst |1 +
 drivers/hwmon/pmbus/Kconfig   |   10 +
 drivers/hwmon/pmbus/Makefile  |1 +
 drivers/hwmon/pmbus/adm1266.c | 1272 +
 6 files changed, 1377 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

-- 
2.20.1



[PATCH v7 5/9] hwmon: pmbus: adm1266: read blackbox

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Using this feature, the device is capable of recording
to nonvolatile flash memory the vital data about the
system status that caused the system to perform a
black box write.

A blackbox is 64 bytes of data containing all the
status registers, last two states of the sequencer,
timestamp and counters. The mapping of this data is
described in the adm1266 datasheet.

On power-up the driver sets the unix time to
the adm1266 using the SET_RTC command. This value
is incremented by an internal clock and it is used
as timestamp for the black box feature.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 165 ++
 1 file changed, 165 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 0ea016b7e113..b61a968d67f9 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,12 +15,19 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 #include 
+#include 
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
+#define ADM1266_SET_RTC0xDF
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -37,6 +44,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
@@ -45,11 +55,22 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct dentry *debugfs_dir;
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
+};
+
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
 /*
@@ -320,6 +341,142 @@ static void adm1266_init_debugfs(struct adm1266_data 
*data)
adm1266_state_read);
 }
 
+#if IS_ENABLED(CONFIG_NVMEM)
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 
*read_buff)
+{
+   int record_count;
+   char index;
+   u8 buf[5];
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, 
buf);
+   if (ret < 0)
+   return ret;
+
+   if (ret != 4)
+   return -EIO;
+
+   record_count = buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, 
, read_buff);
+   if (ret < 0)
+   return ret;
+
+   if (ret != ADM1266_BLACKBOX_SIZE)
+   return -EIO;
+
+   read_buff += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell, 
unsigned int offset,
+size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   return start_addr <= cell_end && cell_start <= end_addr;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data, const struct 
nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+
+   memset(mem_offset, 0, ADM1266_BLACKBOX_SIZE);
+
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   default:
+   return -EINVAL;
+   }
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+   const struct nvmem_cell_info *mem_cell;
+   struct adm1266_data *data = priv;
+   int ret;
+   int i;
+
+   for (i = 0; i < data->nvmem_config.ncells; i++) {
+   mem_cell = _nvmem_cells[i];
+   if (!adm1266_cell_is_accessed(mem_cell, offset, bytes))
+

[PATCH v7 4/9] hwmon: pmbus: adm1266: add debugfs for states

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs entry which prints the current state
of the adm1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 41 ++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index be911de02cf6..0ea016b7e113 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -19,6 +19,7 @@
 #include 
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -43,6 +44,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct dentry *debugfs_dir;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -287,6 +289,37 @@ static int adm1266_config_gpio(struct adm1266_data *data)
return ret;
 }
 
+static int adm1266_state_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   int ret;
+
+   ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+   if (ret < 0)
+   return ret;
+
+   seq_printf(s, "%d\n", ret);
+
+   return 0;
+}
+
+static void adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return;
+
+   debugfs_create_devm_seqfile(>client->dev, "sequencer_state", 
data->debugfs_dir,
+   adm1266_state_read);
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -310,7 +343,13 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
if (ret < 0)
return ret;
 
-   return pmbus_do_probe(client, id, >info);
+   ret = pmbus_do_probe(client, id, >info);
+   if (ret)
+   return ret;
+
+   adm1266_init_debugfs(data);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v7 2/9] hwmon: pmbus: adm1266: Add Block process call

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 73 +++
 2 files changed, 74 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da34083e1ffd..c04068b665e6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 79e8d90886b8..63975eba34ad 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -13,11 +14,80 @@
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
 struct adm1266_data {
struct pmbus_driver_info info;
struct i2c_client *client;
+   struct mutex buf_mutex;
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/*
+ * Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+static int adm1266_pmbus_block_xfer(struct adm1266_data *data, u8 cmd, u8 
w_len, u8 *data_w,
+   u8 *data_r)
+{
+   struct i2c_client *client = data->client;
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = I2C_M_DMA_SAFE,
+   .buf = data->write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD | I2C_M_DMA_SAFE,
+   .buf = data->read_buf,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr;
+   u8 crc;
+   int ret;
+
+   mutex_lock(>buf_mutex);
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   if (ret >= 0)
+   ret = -EPROTO;
+   return ret;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, 0);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1, 
crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1])
+   return -EBADMSG;
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+
+   ret = msgs[1].buf[0];
+   mutex_unlock(>buf_mutex);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -33,6 +103,9 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
for (i = 0; i < data->info.pages; i++)
data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+   mutex_init(>buf_mutex);
+
return pmbus_do_probe(client, id, >info);
 }
 
-- 
2.20.1



[PATCH v7 6/9] hwmon: pmbus: adm1266: Add group command support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

The Group Command Protocol is used to send commands
to more than one PMBus device. Some devices working
together require that they must execute some commands
all at once.

The commands are sent in one continuous transmission.
When the devices detect the STOP condition that ends
the sending of commands, they all begin executing
the command they received.

This patch adds support for the group command protocol.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 50 +++
 1 file changed, 50 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index b61a968d67f9..f571fe1ee35d 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -73,6 +73,56 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
+/* PMBus Group command. */
+static int adm1266_pmbus_group_command(struct adm1266_data *data, struct 
i2c_client **clients,
+  u8 nr_clients, u8 cmd, u8 w_len, u8 
*data_w)
+{
+   struct i2c_msg *msgs;
+   u8 addr;
+   int ret;
+   int i;
+
+   msgs = kcalloc(nr_clients, sizeof(struct i2c_msg), GFP_KERNEL);
+   if (!msgs)
+   return -ENOMEM;
+
+   for (i = 0; i < nr_clients; i++) {
+   msgs[i].addr = clients[i]->addr;
+   msgs[i].len = w_len + 1;
+
+   msgs[i].buf = kcalloc(ADM1266_PMBUS_BLOCK_MAX + 2, sizeof(u8), 
GFP_KERNEL);
+   if (!msgs[i].buf) {
+   ret = -ENOMEM;
+   goto cleanup;
+   }
+
+   msgs[i].buf[0] = cmd;
+   memcpy([i].buf[1], data_w, w_len);
+
+   if (clients[i]->flags & I2C_CLIENT_PEC) {
+   u8 crc = 0;
+
+   addr = i2c_8bit_addr_from_msg([i]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[i].buf, msgs[i].len,
+  crc);
+
+   msgs[i].buf[msgs[i].len] = crc;
+   msgs[i].len++;
+   }
+   };
+
+   ret = i2c_transfer(data->client->adapter, msgs, nr_clients);
+
+cleanup:
+   for (i = i - 1; i >= 0; i--)
+   kfree(msgs[i].buf);
+
+   kfree(msgs);
+
+   return ret;
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
-- 
2.20.1



[PATCH v7 9/9] dt-bindings: hwmon: Add bindings for ADM1266

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..ad92686e2ee6
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+};
+};
+...
-- 
2.20.1



[PATCH v7 8/9] hwmon: pmbus: adm1266: program configuration

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the configuration Intel hex file to the nvmem,
of an adm1266, with offset 0x3, will now
trigger the configuration programming.

During this process the adm1266 sequencer will be
stopped and at the end will be issued a seq reset
(see AN-1453 Programming the configuration).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 179 +-
 1 file changed, 178 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index f851c6617870..50386c98d714 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -40,7 +40,10 @@
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_STATUS_MFR_2   0xED
+#define ADM1266_REFRESH_FLASH  0xF5
 #define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_MEMORY_CRC 0xF9
 #define ADM1266_SWITCH_MEMORY  0xFA
 #define ADM1266_UPDATE_FW  0xFC
 #define ADM1266_FW_PASSWORD0xFD
@@ -66,6 +69,11 @@
 
 /* ADM1266 STATUS_MFR defines */
 #define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+#define ADM1266_RUNNING_REFRESH(x) FIELD_GET(BIT(3), x)
+#define ADM1266_ALL_CRC_FAULT(x)   FIELD_GET(BIT(5), x)
+
+/* ADM1266 STATUS_MFR_2 defines */
+#define ADM1266_MAIN_CONFIG_FAULT(x)   FIELD_GET(GENMASK(9, 8), x)
 
 /* ADM1266 GO_COMMAND defines */
 #define ADM1266_GO_COMMAND_STOPBIT(0)
@@ -74,6 +82,8 @@
 
 #define ADM1266_FIRMWARE_OFFSET0x0
 #define ADM1266_FIRMWARE_SIZE  131072
+#define ADM1266_CONFIG_OFFSET  0x3
+#define ADM1266_CONFIG_SIZE131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
@@ -117,6 +127,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] 
= {
.offset = ADM1266_FIRMWARE_OFFSET,
.bytes  = ADM1266_FIRMWARE_SIZE,
},
+   {
+   .name   = "configuration",
+   .offset = ADM1266_CONFIG_OFFSET,
+   .bytes  = ADM1266_CONFIG_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -520,6 +535,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
case ADM1266_FIRMWARE_OFFSET:
/* firmware is write-only */
return 0;
+   case ADM1266_CONFIG_OFFSET:
+   /* configuration is write-only */
+   return 0;
default:
return -EINVAL;
}
@@ -676,6 +694,7 @@ static int adm1266_write_hex(struct adm1266_data *data,
u8 first_writes[7];
u8 byte_count;
u8 reg_address;
+   bool to_slaves = false;
int ret;
int i;
 
@@ -706,7 +725,10 @@ static int adm1266_write_hex(struct adm1266_data *data,
if (ret < 0)
return ret;
 
-   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, true);
+   if (offset == ADM1266_FIRMWARE_OFFSET)
+   to_slaves = true;
+
+   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, to_slaves);
if (ret < 0) {
dev_err(>client->dev, "Firmware write error: 
%d.", ret);
return ret;
@@ -731,6 +753,87 @@ static int adm1266_write_hex(struct adm1266_data *data,
return 0;
 }
 
+static int adm1266_verify_memory(struct adm1266_data *data)
+{
+   char cmd[2];
+   int ret;
+   int reg;
+
+   cmd[0] = 0x1;
+   cmd[1] = 0x0;
+   ret = adm1266_group_cmd(data, ADM1266_MEMORY_CRC, cmd,
+   sizeof(cmd), true);
+   if (ret < 0)
+   return ret;
+
+   /* after issuing a memory recalculate crc command, wait 1000 ms */
+   msleep(1000);
+
+   reg = pmbus_read_word_data(data->client, 0, 0xFF, ADM1266_STATUS_MFR_2);
+   if (reg < 0)
+   return reg;
+
+   if (ADM1266_MAIN_CONFIG_FAULT(reg)) {
+   dev_err(>client->dev, "Main memory corrupted.");
+   return -EFAULT;
+   }
+
+   return 0;
+}
+
+static int adm1266_refresh_memory(struct adm1266_data *data)
+{
+   unsigned int timeout = 9000;
+   int ret;
+   u8 cmd[2];
+
+   cmd[0] = 0x2;
+   ret = adm1266_group_cmd(data, ADM1266_REFRESH_FLASH, cmd, 1, true);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not refresh flash.");
+   return ret;
+   }
+
+   /* after issuing a refresh flash command, wait 9000 ms */
+   msleep(9000);
+
+   do {
+   msleep(1000);
+   timeout -= 1000;
+
+   ret = pmbus_read_byte_data(data->client, 0, ADM1266_STATUS_MFR);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not read status.");
+   

[PATCH v7 1/9] hwmon: pmbus: adm1266: add support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 37 +++
 Documentation/hwmon/index.rst   |  1 +
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 65 +
 5 files changed, 113 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..9257f8a48650
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,37 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55ff4b7c5349..056f7107d7b8 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -30,6 +30,7 @@ Hardware Monitoring Kernel Drivers
adm1026
adm1031
adm1177
+   adm1266
adm1275
adm9240
ads7828
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a337195b1c39..da34083e1ffd 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c4b15db996ad..da41d22be1c9 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..79e8d90886b8
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "pmbus.h"
+#include 
+
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct i2c_client *client;
+};
+
+static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
+{
+   struct adm1266_data *data;
+   int i;
+
+   data = devm_kzalloc(>dev, sizeof(struct adm1266_data), 
GFP_KERNEL);
+   if (!data)
+   return -ENOMEM;
+
+   data->client = client;
+   data->info.pages = 17;
+   data->info.format[PSC_VOLTAGE_OUT] = linear;
+   for (i = 0; i < data->info.pages; i++)
+   data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+   return pmbus_do_probe(client, id, >info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+  

[PATCH v7 0/9] hwmon: pmbus: adm1266: add support

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Allow the current sate of the sequencer to be read
through debugfs.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Add group command support. This will allow the driver
to stop/program all cascaded adm1266 devices at once.

7. Writing the firmware hex file with offset 0
to the nvmem of the master adm1266 will trigger
the firmware programming of all cascaded devices.
The master adm1266 of each device is specified in
the devicetree.

8. Writing the configuration hex file to 0x3
byte address of the nvmem file will trigger the
programming of that device in particular.

9. DT bindings for ADM1266.

Alexandru Tachici (9):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: add debugfs for states
  hwmon: pmbus: adm1266: read blackbox
  hwmon: pmbus: adm1266: Add group command support
  hwmon: pmbus: adm1266: program firmware
  hwmon: pmbus: adm1266: program configuration
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog: v5 -> v6:
  - added adm1266 to index.rst
  - changed max lines length from 80 to 100
  - replaced i2c_get_dma_safe_msg_buf with the use of kzalloc and 
cacheline_aligned
  - removed #ifdef CONFIG_GPIOLIB
  - removed ioctl commands, the state of the device can be read through debugfs
  - use the device managed version of nvmem_register
  - on power-up, set the UNIX time to adm1266 (this value is reset
  to 0 on each power-cycle).
  - removed patch adm1266: debugfs for blackbox info, rtc in blackbox is enough
  to help identify the current index
  - added two new nvmem cells for firmware and configuration

Changelog: v6 -> v7:
  - fixed compilation warning: removed unused variable ,entry, in
adm1266_init_debugfs

 .../bindings/hwmon/adi,adm1266.yaml   |   56 +
 Documentation/hwmon/adm1266.rst   |   37 +
 Documentation/hwmon/index.rst |1 +
 drivers/hwmon/pmbus/Kconfig   |   10 +
 drivers/hwmon/pmbus/Makefile  |1 +
 drivers/hwmon/pmbus/adm1266.c | 1272 +
 6 files changed, 1377 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

-- 
2.20.1



[PATCH v7 3/9] hwmon: pmbus: adm1266: Add support for GPIOs

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 204 ++
 1 file changed, 204 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 63975eba34ad..be911de02cf6 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,18 +6,42 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -88,9 +112,185 @@ static int adm1266_pmbus_block_xfer(struct adm1266_data 
*data, u8 cmd, u8 w_len,
return ret;
 }
 
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long 
*mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + 
ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, ))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long gpio_config;
+   unsigned long pdio_config;
+   unsigned long pin_cfg;
+   u8 write_cmd;
+   int ret;
+   int i;
+
+   for (i = 0; i < ADM1266_GPIO_NR; i++) {
+   write_cmd = 

[PATCH v7 7/9] hwmon: pmbus: adm1266: program firmware

2020-07-27 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the firmware Intel hex file to the nvmem,
of the master adm1266,  with offset 0, will now
trigger the firmware programming of all cascaded
devices simultaneously through pmbus.

During this process all adm1266 sequencers will be
stopped and at the end will be issued a hard reset
(see AN-1453 Programming the firmware).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 501 +-
 1 file changed, 500 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index f571fe1ee35d..f851c6617870 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -18,18 +19,31 @@
 #include 
 #include 
 #include "pmbus.h"
+#include 
 #include 
 #include 
 
+#define ADM1266_STORE_USER_ALL 0x15
+#define ADM1266_STATUS_MFR 0x80
+#define ADM1266_IC_DEVICE_REV  0xAE
 #define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_SEQUENCE_CONFIG0xD6
+#define ADM1266_SYSTEM_CONFIG  0xD7
+#define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
 #define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_SET_RTC0xDF
+#define ADM1266_LOGIC_CONFIG   0xE0
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_USER_DATA  0xE3
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_SWITCH_MEMORY  0xFA
+#define ADM1266_UPDATE_FW  0xFC
+#define ADM1266_FW_PASSWORD0xFD
 
 /* ADM1266 GPIO defines */
 #define ADM1266_GPIO_NR9
@@ -44,10 +58,35 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+/* ADM1266 FW_PASSWORD defines*/
+#define ADM1266_PASSWD_CMD_LEN 17
+#define ADM1266_CHANGE_PASSWORD1
+#define ADM1266_UNLOCK_DEV 2
+#define ADM1266_LOCK_DEV   3
+
+/* ADM1266 STATUS_MFR defines */
+#define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+
+/* ADM1266 GO_COMMAND defines */
+#define ADM1266_GO_COMMAND_STOPBIT(0)
+#define ADM1266_GO_COMMAND_SEQ_RES BIT(1)
+#define ADM1266_GO_COMMAND_HARD_RESBIT(2)
+
+#define ADM1266_FIRMWARE_OFFSET0x0
+#define ADM1266_FIRMWARE_SIZE  131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
 #define ADM1266_PMBUS_BLOCK_MAX255
+#define ADM1266_MAX_DEVICES16
+
+static LIST_HEAD(registered_masters);
+static DEFINE_MUTEX(registered_masters_lock);
+
+struct adm1266_data_ref {
+   struct adm1266_data *data;
+   struct list_head list;
+};
 
 struct adm1266_data {
struct pmbus_driver_info info;
@@ -57,6 +96,10 @@ struct adm1266_data {
struct dentry *debugfs_dir;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
+   bool master_dev;
+   struct list_head cascaded_devices_list;
+   struct mutex cascaded_devices_mutex; /* lock cascaded_devices_list */
+   u8 nr_devices;
u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -69,6 +112,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = 
{
.offset = ADM1266_BLACKBOX_OFFSET,
.bytes  = 2048,
},
+   {
+   .name   = "firmware",
+   .offset = ADM1266_FIRMWARE_OFFSET,
+   .bytes  = ADM1266_FIRMWARE_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -123,6 +171,27 @@ static int adm1266_pmbus_group_command(struct adm1266_data 
*data, struct i2c_cli
return ret;
 }
 
+static int adm1266_group_cmd(struct adm1266_data *data, u8 cmd, u8 
*write_data, u8 w_len,
+bool to_slaves)
+{
+   struct i2c_client *clients[ADM1266_MAX_DEVICES];
+   struct adm1266_data_ref *slave_ref;
+   int i = 0;
+
+   clients[i] = data->client;
+   i++;
+
+   if (!to_slaves)
+   return adm1266_pmbus_group_command(data, clients, 1, cmd, 
w_len, write_data);
+
+   list_for_each_entry(slave_ref, >cascaded_devices_list, list) {
+   clients[i] = slave_ref->data->client;
+   i++;
+   }
+
+   return adm1266_pmbus_group_command(data, clients, i, cmd, w_len, 
write_data);
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
@@ -448,6 +517,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
if (ret)
dev_err(>client->dev, "Could not read 

[PATCH v6 2/9] hwmon: pmbus: adm1266: Add Block process call

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 73 +++
 2 files changed, 74 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da34083e1ffd..c04068b665e6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 79e8d90886b8..63975eba34ad 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -13,11 +14,80 @@
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
 struct adm1266_data {
struct pmbus_driver_info info;
struct i2c_client *client;
+   struct mutex buf_mutex;
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/*
+ * Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+static int adm1266_pmbus_block_xfer(struct adm1266_data *data, u8 cmd, u8 
w_len, u8 *data_w,
+   u8 *data_r)
+{
+   struct i2c_client *client = data->client;
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = I2C_M_DMA_SAFE,
+   .buf = data->write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD | I2C_M_DMA_SAFE,
+   .buf = data->read_buf,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr;
+   u8 crc;
+   int ret;
+
+   mutex_lock(>buf_mutex);
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   if (ret >= 0)
+   ret = -EPROTO;
+   return ret;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, 0);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1, 
crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1])
+   return -EBADMSG;
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+
+   ret = msgs[1].buf[0];
+   mutex_unlock(>buf_mutex);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -33,6 +103,9 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
for (i = 0; i < data->info.pages; i++)
data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+   mutex_init(>buf_mutex);
+
return pmbus_do_probe(client, id, >info);
 }
 
-- 
2.20.1



[PATCH v6 8/9] hwmon: pmbus: adm1266: program configuration

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the configuration Intel hex file to the nvmem,
of an adm1266, with offset 0x3, will now
trigger the configuration programming.

During this process the adm1266 sequencer will be
stopped and at the end will be issued a seq reset
(see AN-1453 Programming the configuration).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 179 +-
 1 file changed, 178 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 376fc56abe04..2b07e38041da 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -40,7 +40,10 @@
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_STATUS_MFR_2   0xED
+#define ADM1266_REFRESH_FLASH  0xF5
 #define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_MEMORY_CRC 0xF9
 #define ADM1266_SWITCH_MEMORY  0xFA
 #define ADM1266_UPDATE_FW  0xFC
 #define ADM1266_FW_PASSWORD0xFD
@@ -66,6 +69,11 @@
 
 /* ADM1266 STATUS_MFR defines */
 #define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+#define ADM1266_RUNNING_REFRESH(x) FIELD_GET(BIT(3), x)
+#define ADM1266_ALL_CRC_FAULT(x)   FIELD_GET(BIT(5), x)
+
+/* ADM1266 STATUS_MFR_2 defines */
+#define ADM1266_MAIN_CONFIG_FAULT(x)   FIELD_GET(GENMASK(9, 8), x)
 
 /* ADM1266 GO_COMMAND defines */
 #define ADM1266_GO_COMMAND_STOPBIT(0)
@@ -74,6 +82,8 @@
 
 #define ADM1266_FIRMWARE_OFFSET0x0
 #define ADM1266_FIRMWARE_SIZE  131072
+#define ADM1266_CONFIG_OFFSET  0x3
+#define ADM1266_CONFIG_SIZE131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
@@ -117,6 +127,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] 
= {
.offset = ADM1266_FIRMWARE_OFFSET,
.bytes  = ADM1266_FIRMWARE_SIZE,
},
+   {
+   .name   = "configuration",
+   .offset = ADM1266_CONFIG_OFFSET,
+   .bytes  = ADM1266_CONFIG_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -521,6 +536,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
case ADM1266_FIRMWARE_OFFSET:
/* firmware is write-only */
return 0;
+   case ADM1266_CONFIG_OFFSET:
+   /* configuration is write-only */
+   return 0;
default:
return -EINVAL;
}
@@ -677,6 +695,7 @@ static int adm1266_write_hex(struct adm1266_data *data,
u8 first_writes[7];
u8 byte_count;
u8 reg_address;
+   bool to_slaves = false;
int ret;
int i;
 
@@ -707,7 +726,10 @@ static int adm1266_write_hex(struct adm1266_data *data,
if (ret < 0)
return ret;
 
-   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, true);
+   if (offset == ADM1266_FIRMWARE_OFFSET)
+   to_slaves = true;
+
+   ret = adm1266_group_cmd(data, reg_address, write_buf, 
byte_count, to_slaves);
if (ret < 0) {
dev_err(>client->dev, "Firmware write error: 
%d.", ret);
return ret;
@@ -732,6 +754,87 @@ static int adm1266_write_hex(struct adm1266_data *data,
return 0;
 }
 
+static int adm1266_verify_memory(struct adm1266_data *data)
+{
+   char cmd[2];
+   int ret;
+   int reg;
+
+   cmd[0] = 0x1;
+   cmd[1] = 0x0;
+   ret = adm1266_group_cmd(data, ADM1266_MEMORY_CRC, cmd,
+   sizeof(cmd), true);
+   if (ret < 0)
+   return ret;
+
+   /* after issuing a memory recalculate crc command, wait 1000 ms */
+   msleep(1000);
+
+   reg = pmbus_read_word_data(data->client, 0, 0xFF, ADM1266_STATUS_MFR_2);
+   if (reg < 0)
+   return reg;
+
+   if (ADM1266_MAIN_CONFIG_FAULT(reg)) {
+   dev_err(>client->dev, "Main memory corrupted.");
+   return -EFAULT;
+   }
+
+   return 0;
+}
+
+static int adm1266_refresh_memory(struct adm1266_data *data)
+{
+   unsigned int timeout = 9000;
+   int ret;
+   u8 cmd[2];
+
+   cmd[0] = 0x2;
+   ret = adm1266_group_cmd(data, ADM1266_REFRESH_FLASH, cmd, 1, true);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not refresh flash.");
+   return ret;
+   }
+
+   /* after issuing a refresh flash command, wait 9000 ms */
+   msleep(9000);
+
+   do {
+   msleep(1000);
+   timeout -= 1000;
+
+   ret = pmbus_read_byte_data(data->client, 0, ADM1266_STATUS_MFR);
+   if (ret < 0) {
+   dev_err(>client->dev, "Could not read status.");
+   

[PATCH v6 7/9] hwmon: pmbus: adm1266: program firmware

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Writing the firmware Intel hex file to the nvmem,
of the master adm1266,  with offset 0, will now
trigger the firmware programming of all cascaded
devices simultaneously through pmbus.

During this process all adm1266 sequencers will be
stopped and at the end will be issued a hard reset
(see AN-1453 Programming the firmware).

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 501 +-
 1 file changed, 500 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 34bd4e652729..376fc56abe04 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -18,18 +19,31 @@
 #include 
 #include 
 #include "pmbus.h"
+#include 
 #include 
 #include 
 
+#define ADM1266_STORE_USER_ALL 0x15
+#define ADM1266_STATUS_MFR 0x80
+#define ADM1266_IC_DEVICE_REV  0xAE
 #define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_SEQUENCE_CONFIG0xD6
+#define ADM1266_SYSTEM_CONFIG  0xD7
+#define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
 #define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_SET_RTC0xDF
+#define ADM1266_LOGIC_CONFIG   0xE0
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_USER_DATA  0xE3
 #define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
+#define ADM1266_MEMORY_CONFIG  0xF8
+#define ADM1266_SWITCH_MEMORY  0xFA
+#define ADM1266_UPDATE_FW  0xFC
+#define ADM1266_FW_PASSWORD0xFD
 
 /* ADM1266 GPIO defines */
 #define ADM1266_GPIO_NR9
@@ -44,10 +58,35 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+/* ADM1266 FW_PASSWORD defines*/
+#define ADM1266_PASSWD_CMD_LEN 17
+#define ADM1266_CHANGE_PASSWORD1
+#define ADM1266_UNLOCK_DEV 2
+#define ADM1266_LOCK_DEV   3
+
+/* ADM1266 STATUS_MFR defines */
+#define ADM1266_STATUS_PART_LOCKED(x)  FIELD_GET(BIT(2), x)
+
+/* ADM1266 GO_COMMAND defines */
+#define ADM1266_GO_COMMAND_STOPBIT(0)
+#define ADM1266_GO_COMMAND_SEQ_RES BIT(1)
+#define ADM1266_GO_COMMAND_HARD_RESBIT(2)
+
+#define ADM1266_FIRMWARE_OFFSET0x0
+#define ADM1266_FIRMWARE_SIZE  131072
 #define ADM1266_BLACKBOX_OFFSET0x7F700
 #define ADM1266_BLACKBOX_SIZE  64
 
 #define ADM1266_PMBUS_BLOCK_MAX255
+#define ADM1266_MAX_DEVICES16
+
+static LIST_HEAD(registered_masters);
+static DEFINE_MUTEX(registered_masters_lock);
+
+struct adm1266_data_ref {
+   struct adm1266_data *data;
+   struct list_head list;
+};
 
 struct adm1266_data {
struct pmbus_driver_info info;
@@ -57,6 +96,10 @@ struct adm1266_data {
struct dentry *debugfs_dir;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
+   bool master_dev;
+   struct list_head cascaded_devices_list;
+   struct mutex cascaded_devices_mutex; /* lock cascaded_devices_list */
+   u8 nr_devices;
u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -69,6 +112,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = 
{
.offset = ADM1266_BLACKBOX_OFFSET,
.bytes  = 2048,
},
+   {
+   .name   = "firmware",
+   .offset = ADM1266_FIRMWARE_OFFSET,
+   .bytes  = ADM1266_FIRMWARE_SIZE,
+   },
 };
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -123,6 +171,27 @@ static int adm1266_pmbus_group_command(struct adm1266_data 
*data, struct i2c_cli
return ret;
 }
 
+static int adm1266_group_cmd(struct adm1266_data *data, u8 cmd, u8 
*write_data, u8 w_len,
+bool to_slaves)
+{
+   struct i2c_client *clients[ADM1266_MAX_DEVICES];
+   struct adm1266_data_ref *slave_ref;
+   int i = 0;
+
+   clients[i] = data->client;
+   i++;
+
+   if (!to_slaves)
+   return adm1266_pmbus_group_command(data, clients, 1, cmd, 
w_len, write_data);
+
+   list_for_each_entry(slave_ref, >cascaded_devices_list, list) {
+   clients[i] = slave_ref->data->client;
+   i++;
+   }
+
+   return adm1266_pmbus_group_command(data, clients, i, cmd, w_len, 
write_data);
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
@@ -449,6 +518,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, 
const struct nvmem_c
if (ret)
dev_err(>client->dev, "Could not read 

[PATCH v6 4/9] hwmon: pmbus: adm1266: add debugfs for states

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs entry which prints the current state
of the adm1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 42 ++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index be911de02cf6..dbffc6d12e87 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -19,6 +19,7 @@
 #include 
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -43,6 +44,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct dentry *debugfs_dir;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -287,6 +289,38 @@ static int adm1266_config_gpio(struct adm1266_data *data)
return ret;
 }
 
+static int adm1266_state_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   int ret;
+
+   ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+   if (ret < 0)
+   return ret;
+
+   seq_printf(s, "%d\n", ret);
+
+   return 0;
+}
+
+static void adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *entry;
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return;
+
+   entry = debugfs_create_devm_seqfile(>client->dev, 
"sequencer_state",
+   data->debugfs_dir, 
adm1266_state_read);
+}
+
 static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
 {
struct adm1266_data *data;
@@ -310,7 +344,13 @@ static int adm1266_probe(struct i2c_client *client, const 
struct i2c_device_id *
if (ret < 0)
return ret;
 
-   return pmbus_do_probe(client, id, >info);
+   ret = pmbus_do_probe(client, id, >info);
+   if (ret)
+   return ret;
+
+   adm1266_init_debugfs(data);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v6 0/9] hwmon: pmbus: adm1266: add support

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Allow the current sate of the seqeuncer to be read
through debugfs.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Add group command support. This will allow the driver
to stop/program all cascaded adm1266 devices at once.

7. Writing the firmware hex file with offset 0
to the nvmem of the master adm1266 will trigger
the firmware programming of all cascaded devices.
The master adm1266 of each device is specified in
the devicetree.

8. Writing the configuration hex file to 0x3
byte address of the nvmem file will trigger the
programing of that device in particular.

9. dt bindings for ADM1266.

Alexandru Tachici (9):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: add debugfs for states
  hwmon: pmbus: adm1266: read blackbox
  hwmon: pmbus: adm1266: Add group command support
  hwmon: pmbus: adm1266: program firmware
  hwmon: pmbus: adm1266: program configuration
  dt-bindings: hwmon: Add bindings for ADM1266

 .../bindings/hwmon/adi,adm1266.yaml   |   56 +
 Documentation/hwmon/adm1266.rst   |   37 +
 Documentation/hwmon/index.rst |1 +
 drivers/hwmon/pmbus/Kconfig   |   10 +
 drivers/hwmon/pmbus/Makefile  |1 +
 drivers/hwmon/pmbus/adm1266.c | 1273 +
 6 files changed, 1378 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

-- 
2.20.1



[PATCH v6 1/9] hwmon: pmbus: adm1266: add support

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 37 +++
 Documentation/hwmon/index.rst   |  1 +
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 65 +
 5 files changed, 113 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..9257f8a48650
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,37 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55ff4b7c5349..056f7107d7b8 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -30,6 +30,7 @@ Hardware Monitoring Kernel Drivers
adm1026
adm1031
adm1177
+   adm1266
adm1275
adm9240
ads7828
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a337195b1c39..da34083e1ffd 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c4b15db996ad..da41d22be1c9 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..79e8d90886b8
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "pmbus.h"
+#include 
+
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct i2c_client *client;
+};
+
+static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
+{
+   struct adm1266_data *data;
+   int i;
+
+   data = devm_kzalloc(>dev, sizeof(struct adm1266_data), 
GFP_KERNEL);
+   if (!data)
+   return -ENOMEM;
+
+   data->client = client;
+   data->info.pages = 17;
+   data->info.format[PSC_VOLTAGE_OUT] = linear;
+   for (i = 0; i < data->info.pages; i++)
+   data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+   return pmbus_do_probe(client, id, >info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+  

[PATCH v6 5/9] hwmon: pmbus: adm1266: read blackbox

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Using this feature, the device is capable of recording
to nonvolatile flash memory the vital data about the
system status that caused the system to perform a
black box write.

A blackbox is 64 bytes of data containing all the
status registers, last two states of the sequencer,
timestamp and counters. The mapping of this data is
described in the adm1266 datasheet.

On power-up the driver sets the unix time to
the adm1266 using the SET_RTC command. This value
is incremented by an internal clock and it is used
as timestamp for the black box feature.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 165 ++
 1 file changed, 165 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index dbffc6d12e87..c06465100320 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,12 +15,19 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 #include 
+#include 
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
+#define ADM1266_SET_RTC0xDF
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -37,6 +44,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
@@ -45,11 +55,22 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct dentry *debugfs_dir;
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
 };
 
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
+};
+
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
 /*
@@ -321,6 +342,142 @@ static void adm1266_init_debugfs(struct adm1266_data 
*data)
data->debugfs_dir, 
adm1266_state_read);
 }
 
+#if IS_ENABLED(CONFIG_NVMEM)
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 
*read_buff)
+{
+   int record_count;
+   char index;
+   u8 buf[5];
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, 
buf);
+   if (ret < 0)
+   return ret;
+
+   if (ret != 4)
+   return -EIO;
+
+   record_count = buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, 
, read_buff);
+   if (ret < 0)
+   return ret;
+
+   if (ret != ADM1266_BLACKBOX_SIZE)
+   return -EIO;
+
+   read_buff += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell, 
unsigned int offset,
+size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   return start_addr <= cell_end && cell_start <= end_addr;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data, const struct 
nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+
+   memset(mem_offset, 0, ADM1266_BLACKBOX_SIZE);
+
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   default:
+   return -EINVAL;
+   }
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+   const struct nvmem_cell_info *mem_cell;
+   struct adm1266_data *data = priv;
+   int ret;
+   int i;
+
+   for (i = 0; i < data->nvmem_config.ncells; i++) {
+   mem_cell = _nvmem_cells[i];
+   if (!adm1266_cell_is_accessed(mem_cell, offset, 

[PATCH v6 3/9] hwmon: pmbus: adm1266: Add support for GPIOs

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 204 ++
 1 file changed, 204 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 63975eba34ad..be911de02cf6 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,18 +6,42 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include "pmbus.h"
 #include 
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 struct adm1266_data {
struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex buf_mutex;
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] cacheline_aligned;
@@ -88,9 +112,185 @@ static int adm1266_pmbus_block_xfer(struct adm1266_data 
*data, u8 cmd, u8 w_len,
return ret;
 }
 
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long 
*mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, 
read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + 
ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, ))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long gpio_config;
+   unsigned long pdio_config;
+   unsigned long pin_cfg;
+   u8 write_cmd;
+   int ret;
+   int i;
+
+   for (i = 0; i < ADM1266_GPIO_NR; i++) {
+   write_cmd = 

[PATCH v6 9/9] dt-bindings: hwmon: Add bindings for ADM1266

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..ad92686e2ee6
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+};
+};
+...
-- 
2.20.1



[PATCH v6 6/9] hwmon: pmbus: adm1266: Add group command support

2020-07-26 Thread alexandru.tachici
From: Alexandru Tachici 

The Group Command Protocol is used to send commands
to more than one PMBus device. Some devices working
together require that they must execute some commands
all at once.

The commands are sent in one continuous transmission.
When the devices detect the STOP condition that ends
the sending of commands, they all begin executing
the command they received.

This patch adds support for the group command protocol.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 50 +++
 1 file changed, 50 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index c06465100320..34bd4e652729 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -73,6 +73,56 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
+/* PMBus Group command. */
+static int adm1266_pmbus_group_command(struct adm1266_data *data, struct 
i2c_client **clients,
+  u8 nr_clients, u8 cmd, u8 w_len, u8 
*data_w)
+{
+   struct i2c_msg *msgs;
+   u8 addr;
+   int ret;
+   int i;
+
+   msgs = kcalloc(nr_clients, sizeof(struct i2c_msg), GFP_KERNEL);
+   if (!msgs)
+   return -ENOMEM;
+
+   for (i = 0; i < nr_clients; i++) {
+   msgs[i].addr = clients[i]->addr;
+   msgs[i].len = w_len + 1;
+
+   msgs[i].buf = kcalloc(ADM1266_PMBUS_BLOCK_MAX + 2, sizeof(u8), 
GFP_KERNEL);
+   if (!msgs[i].buf) {
+   ret = -ENOMEM;
+   goto cleanup;
+   }
+
+   msgs[i].buf[0] = cmd;
+   memcpy([i].buf[1], data_w, w_len);
+
+   if (clients[i]->flags & I2C_CLIENT_PEC) {
+   u8 crc = 0;
+
+   addr = i2c_8bit_addr_from_msg([i]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[i].buf, msgs[i].len,
+  crc);
+
+   msgs[i].buf[msgs[i].len] = crc;
+   msgs[i].len++;
+   }
+   };
+
+   ret = i2c_transfer(data->client->adapter, msgs, nr_clients);
+
+cleanup:
+   for (i = i - 1; i >= 0; i--)
+   kfree(msgs[i].buf);
+
+   kfree(msgs);
+
+   return ret;
+}
+
 /*
  * Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
-- 
2.20.1



[PATCH v5 4/7] hwmon: pmbus: adm1266: Add ioctl commands

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add two ioctl commands for reading the current state
of the adm1266 sequencer and sending commands.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst   | 15 +++
 .../userspace-api/ioctl/ioctl-number.rst  |  1 +
 drivers/hwmon/pmbus/adm1266.c | 97 +++
 include/uapi/linux/adm1266.h  | 16 +++
 4 files changed, 129 insertions(+)
 create mode 100644 include/uapi/linux/adm1266.h

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
index 65662115750c..5dc05808db60 100644
--- a/Documentation/hwmon/adm1266.rst
+++ b/Documentation/hwmon/adm1266.rst
@@ -33,3 +33,18 @@ inX_min  Minimum Voltage.
 inX_maxMaximum voltage.
 inX_min_alarm  Voltage low alarm.
 inX_max_alarm  Voltage high alarm.
+
+
+User API
+
+
+ioctls
+--
+
+ADM1266_SET_GO_COMMAND:
+
+  Issue a GO_COMMAND to the device.
+
+ADM1266_GET_STATUS:
+
+  Returns state of the sequencer.
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst 
b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 59472cd6a11d..df92ca274622 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -348,6 +348,7 @@ Code  Seq#Include File  
 Comments
 0xCC  00-0F  drivers/misc/ibmvmc.h   pseries 
VMC driver
 0xCD  01 linux/reiserfs_fs.h
 0xCF  02 fs/cifs/ioctl.c
+0xD1  00-0F  linux/adm1266.h
 0xDB  00-0F  drivers/char/mwave/mwavepub.h
 0xDD  00-3F  ZFCP 
device driver see drivers/s390/scsi/
  

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 76bf2c78e737..0960ead8d96a 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,11 +15,16 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 
+#include 
 #include "pmbus.h"
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GO_COMMAND 0xD8
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -46,6 +51,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct mutex ioctl_mutex; /* lock ioctl access */
 };
 
 /* Different from Block Read as it sends data and waits for the slave to
@@ -311,6 +317,93 @@ static int adm1266_config_gpio(struct adm1266_data *data)
 }
 #endif
 
+static int adm1266_set_go_command_op(struct adm1266_data *data, u8 val)
+{
+   val = FIELD_GET(GENMASK(4, 0), val);
+
+   return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, val);
+}
+
+static int adm1266_ioctl_unlocked(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+   int __user *argp = (int __user *)arg;
+   struct adm1266_data *data;
+   int val;
+   int ret;
+
+   data = fp->private_data;
+
+   if (!argp)
+   return -EINVAL;
+
+   switch (cmd) {
+   case ADM1266_SET_GO_COMMAND:
+   if (copy_from_user(, argp, sizeof(int)))
+   return -EFAULT;
+
+   return adm1266_set_go_command_op(data, val);
+   case ADM1266_GET_STATUS:
+   ret = i2c_smbus_read_word_data(data->client,
+  ADM1266_READ_STATE);
+
+   if (ret < 0)
+   return ret;
+
+   if (copy_to_user(argp, , sizeof(int)))
+   return -EFAULT;
+
+   break;
+   default:
+   return -ENOTTY;
+   }
+
+   return 0;
+}
+
+static long adm1266_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+   struct adm1266_data *data;
+   long ret;
+
+   data = fp->private_data;
+
+   mutex_lock(>ioctl_mutex);
+   ret = adm1266_ioctl_unlocked(fp, cmd, arg);
+   mutex_unlock(>ioctl_mutex);
+
+   return ret;
+}
+
+static int adm1266_open(struct inode *inode, struct file *filp)
+{
+   filp->private_data = PDE_DATA(inode);
+
+   return 0;
+}
+
+static const struct proc_ops adm1266_proc_ops = {
+   .proc_open  = adm1266_open,
+   .proc_ioctl = adm1266_ioctl,
+};
+
+static int adm1266_init_procfs(struct adm1266_data *data)
+{
+   struct proc_dir_entry *proc_dir;
+   u8 proc_fs_name[32];
+
+   mutex_init(>ioctl_mutex);
+
+   snprintf(proc_fs_name, 32, "adm1266-%x", data->client->addr);
+   proc_dir = proc_create_data(proc_fs_name, 0, NULL, _proc_ops,
+   data);
+
+   if (!proc_dir)
+   return 

[PATCH v5 1/7] hwmon: pmbus: adm1266: add support

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 35 +++
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 62 +
 4 files changed, 107 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..65662115750c
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,35 @@
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a337195b1c39..da34083e1ffd 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c4b15db996ad..da41d22be1c9 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..a7ef048a9a5c
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pmbus.h"
+
+static int adm1266_probe(struct i2c_client *client,
+const struct i2c_device_id *id)
+{
+   struct pmbus_driver_info *info;
+   u32 funcs;
+   int i;
+
+   info = devm_kzalloc(>dev, sizeof(struct pmbus_driver_info),
+   GFP_KERNEL);
+
+   info->pages = 17;
+   info->format[PSC_VOLTAGE_OUT] = linear;
+   funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+   for (i = 0; i < info->pages; i++)
+   info->func[i] = funcs;
+
+   return pmbus_do_probe(client, id, info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+   .driver = {
+  .name = "adm1266",
+  .of_match_table = adm1266_of_match,
+ },
+   .probe = adm1266_probe,
+   .remove = pmbus_do_remove,
+   .id_table = adm1266_id,
+};
+
+module_i2c_driver(adm1266_driver);
+
+MODULE_AUTHOR("Alexandru Tachici ");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1



[PATCH v5 6/7] hwmon: pmbus: adm1266: debugfs for blackbox info

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs file to print information in the
BLACKBOX_INFORMATION register. Contains information
about the number of stored records, logic index and id
of the latest record.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 56 ++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index b9e92ab1e39a..ea2dc481094b 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -60,6 +60,7 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex ioctl_mutex; /* lock ioctl access */
+   struct dentry *debugfs_dir;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
u8 *dev_mem;
@@ -406,6 +407,28 @@ static const struct proc_ops adm1266_proc_ops = {
.proc_ioctl = adm1266_ioctl,
 };
 
+static int adm1266_blackbox_information_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   u8 read_buf[5];
+   unsigned int latest_id;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   seq_puts(s, "BLACKBOX_INFORMATION:\n");
+   latest_id = read_buf[0] + (read_buf[1] << 8);
+   seq_printf(s, "Black box ID: %u\n", latest_id);
+   seq_printf(s, "Logic index: %u\n", read_buf[2]);
+   seq_printf(s, "Record count: %u\n", read_buf[3]);
+
+   return 0;
+}
+
 static int adm1266_init_procfs(struct adm1266_data *data)
 {
struct proc_dir_entry *proc_dir;
@@ -423,6 +446,29 @@ static int adm1266_init_procfs(struct adm1266_data *data)
return 0;
 }
 
+static int adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *entry;
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return -ENOENT;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return -ENOENT;
+
+   entry = debugfs_create_devm_seqfile(>client->dev,
+   "blackbox_information",
+   data->debugfs_dir,
+   adm1266_blackbox_information_read);
+   if (!entry)
+   return -ENOENT;
+
+   return 0;
+}
+
 static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf)
 {
u8 read_buf[5];
@@ -571,7 +617,15 @@ static int adm1266_probe(struct i2c_client *client,
for (i = 0; i < info->pages; i++)
info->func[i] = funcs;
 
-   return pmbus_do_probe(client, id, info);
+   ret = pmbus_do_probe(client, id, info);
+   if (ret)
+   return ret;
+
+   ret = adm1266_init_debugfs(data);
+   if (ret)
+   dev_warn(>dev, "Failed to register debugfs: %d\n", ret);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v5 0/7] hwmon: pmbus: adm1266: add support

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Add two ioctl commands for issuing GO_COMMAND
and reading the state of the adm1266 sequencer.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Expose BLACKBOX_INFO register through debugfs.

7. Device tree bindings for ADM1266.

Alexandru Tachici (7):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: Add ioctl commands
  hwmon: pmbus: adm1266: read blackbox
  hwmon: pmbus: adm1266: debugfs for blackbox info
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog v3 -> v4:
- moved pmbus_block_wr (pmbus process call) from pmbus_core.
to adm1266.c and renamed to pmbus_block_xfer
- in pmbus_block_xfer: fixed buffer size bug (from 255 to 257)
- in adm1266_gpio_get_multiple: handle pdios and gpios one at a time
to lower allocated space on stack
- in adm1266_gpio_dbg_show: replaced write_buf with u8 write_cmd var
- in adm1266_gpio_dbg_show: check number of bytes received from device
returned by pmbus_block_xfer.
- now use ioctl to send GO_COMMAND and retrieve current state of adm1266
- split blackbox commit into blackbox nvmem implementation and debugfs
blackbox info debugfs
- create adm1266 debugfs dir under /sys/kernel/debug/pmbus/hwmon for
blackbox_info

Changelog v4 -> v5:
- added WITH Linux-syscall-note to adm1266.h

 .../bindings/hwmon/adi,adm1266.yaml   |  56 ++
 Documentation/hwmon/adm1266.rst   |  50 ++
 .../userspace-api/ioctl/ioctl-number.rst  |   1 +
 drivers/hwmon/pmbus/Kconfig   |  10 +
 drivers/hwmon/pmbus/Makefile  |   1 +
 drivers/hwmon/pmbus/adm1266.c | 657 ++
 include/uapi/linux/adm1266.h  |  16 +
 7 files changed, 791 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c
 create mode 100644 include/uapi/linux/adm1266.h

-- 
2.20.1



[PATCH v5 2/7] hwmon: pmbus: adm1266: Add Block process call

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 79 +++
 2 files changed, 80 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da34083e1ffd..c04068b665e6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index a7ef048a9a5c..381d89a8569f 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -14,6 +15,82 @@
 
 #include "pmbus.h"
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/* Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+int pmbus_block_xfer(struct i2c_client *client, u8 cmd, u8 w_len,
+u8 *data_w, u8 *data_r)
+{
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 2];
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = 0,
+   .buf = write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr = 0;
+   u8 crc = 0;
+   int ret;
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   msgs[0].buf = i2c_get_dma_safe_msg_buf([0], 1);
+   if (!msgs[0].buf)
+   return -ENOMEM;
+
+   msgs[1].buf = i2c_get_dma_safe_msg_buf([1], 1);
+   if (!msgs[1].buf) {
+   i2c_put_dma_safe_msg_buf(msgs[0].buf, [0], false);
+   return -ENOMEM;
+   }
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   ret = -EPROTO;
+   goto cleanup;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1,
+  crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) {
+   ret = -EBADMSG;
+   goto cleanup;
+   }
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+   ret = msgs[1].buf[0];
+
+cleanup:
+   i2c_put_dma_safe_msg_buf(msgs[0].buf, [0], true);
+   i2c_put_dma_safe_msg_buf(msgs[1].buf, [1], true);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client,
 const struct i2c_device_id *id)
 {
@@ -24,6 +101,8 @@ static int adm1266_probe(struct i2c_client *client,
info = devm_kzalloc(>dev, sizeof(struct pmbus_driver_info),
GFP_KERNEL);
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+
info->pages = 17;
info->format[PSC_VOLTAGE_OUT] = linear;
funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
-- 
2.20.1



[PATCH v5 7/7] dt-bindings: hwmon: Add bindings for ADM1266

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..76b62be48d56
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+#address-cells = <1>;
+#size-cells = <0>;
+};
+};
+...
-- 
2.20.1



[PATCH v5 3/7] hwmon: pmbus: adm1266: Add support for GPIOs

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 233 +-
 1 file changed, 232 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 381d89a8569f..76bf2c78e737 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,8 +6,12 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -15,10 +19,35 @@
 
 #include "pmbus.h"
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
+   struct i2c_client *client;
+};
+
 /* Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
  * followed by a Read Block without the Read-Block command field and the
@@ -91,18 +120,220 @@ int pmbus_block_xfer(struct i2c_client *client, u8 cmd, 
u8 w_len,
return ret;
 }
 
+#ifdef CONFIG_GPIOLIB
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip,
+unsigned long *mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask,
+ ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, ))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file 

[PATCH v5 5/7] hwmon: pmbus: adm1266: read blackbox

2020-06-24 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 134 ++
 1 file changed, 134 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 0960ead8d96a..b9e92ab1e39a 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,6 +15,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -22,10 +24,13 @@
 #include 
 #include "pmbus.h"
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -42,6 +47,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -52,6 +60,17 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex ioctl_mutex; /* lock ioctl access */
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
+};
+
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
 };
 
 /* Different from Block Read as it sends data and waits for the slave to
@@ -404,6 +423,117 @@ static int adm1266_init_procfs(struct adm1266_data *data)
return 0;
 }
 
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf)
+{
+   u8 read_buf[5];
+   char index;
+   int record_count;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   record_count = read_buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = pmbus_block_xfer(data->client, ADM1266_READ_BLACKBOX, 1,
+  , buf);
+   if (ret < 0)
+   return ret;
+
+   buf += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell,
+unsigned int offset, size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   if (start_addr <= cell_end && cell_start <= end_addr)
+   return true;
+
+   return false;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data,
+const struct nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   default:
+   return -EINVAL;
+   }
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+   const struct nvmem_cell_info *mem_cell;
+   struct adm1266_data *data = priv;
+   int ret;
+   int i;
+
+   for (i = 0; i < data->nvmem_config.ncells; i++) {
+   mem_cell = _nvmem_cells[i];
+   if (!adm1266_cell_is_accessed(mem_cell, offset, bytes))
+   continue;
+
+   ret = adm1266_read_mem_cell(data, mem_cell);
+   if (ret < 0)
+   return ret;
+   }
+
+   memcpy(val, data->dev_mem + offset, bytes);
+
+   return 0;
+}
+
+static int adm1266_config_nvmem(struct adm1266_data *data)
+{
+   data->nvmem_config.name = dev_name(>client->dev);
+   data->nvmem_config.dev = >client->dev;
+   data->nvmem_config.root_only = true;
+   data->nvmem_config.read_only = true;
+   data->nvmem_config.owner = THIS_MODULE;
+   data->nvmem_config.reg_read = adm1266_nvmem_read;
+   data->nvmem_config.cells = adm1266_nvmem_cells;
+   data->nvmem_config.ncells = ARRAY_SIZE(adm1266_nvmem_cells);
+   data->nvmem_config.priv = data;

[PATCH v4 3/7] hwmon: pmbus: adm1266: Add support for GPIOs

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 233 +-
 1 file changed, 232 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 381d89a8569f..76bf2c78e737 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,8 +6,12 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -15,10 +19,35 @@
 
 #include "pmbus.h"
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
 
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
+   struct i2c_client *client;
+};
+
 /* Different from Block Read as it sends data and waits for the slave to
  * return a value dependent on that data. The protocol is simply a Write Block
  * followed by a Read Block without the Read-Block command field and the
@@ -91,18 +120,220 @@ int pmbus_block_xfer(struct i2c_client *client, u8 cmd, 
u8 w_len,
return ret;
 }
 
+#ifdef CONFIG_GPIOLIB
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip,
+unsigned long *mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+   unsigned long status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], ))
+   set_bit(gpio_nr, bits);
+   }
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   status = read_buf[0] + (read_buf[1] << 8);
+
+   *bits = 0;
+   for_each_set_bit_from(gpio_nr, mask,
+ ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, ))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file 

[PATCH v4 4/7] hwmon: pmbus: adm1266: Add ioctl commands

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Add two ioctl commands for reading the current state
of the adm1266 sequencer and sending commands.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst   | 15 +++
 .../userspace-api/ioctl/ioctl-number.rst  |  1 +
 drivers/hwmon/pmbus/adm1266.c | 97 +++
 include/uapi/linux/adm1266.h  | 16 +++
 4 files changed, 129 insertions(+)
 create mode 100644 include/uapi/linux/adm1266.h

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
index 65662115750c..5dc05808db60 100644
--- a/Documentation/hwmon/adm1266.rst
+++ b/Documentation/hwmon/adm1266.rst
@@ -33,3 +33,18 @@ inX_min  Minimum Voltage.
 inX_maxMaximum voltage.
 inX_min_alarm  Voltage low alarm.
 inX_max_alarm  Voltage high alarm.
+
+
+User API
+
+
+ioctls
+--
+
+ADM1266_SET_GO_COMMAND:
+
+  Issue a GO_COMMAND to the device.
+
+ADM1266_GET_STATUS:
+
+  Returns state of the sequencer.
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst 
b/Documentation/userspace-api/ioctl/ioctl-number.rst
index f759edafd938..db4d912e3d86 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -345,6 +345,7 @@ Code  Seq#Include File  
 Comments
 0xCC  00-0F  drivers/misc/ibmvmc.h   pseries 
VMC driver
 0xCD  01 linux/reiserfs_fs.h
 0xCF  02 fs/cifs/ioctl.c
+0xD1  00-0F  linux/adm1266.h
 0xDB  00-0F  drivers/char/mwave/mwavepub.h
 0xDD  00-3F  ZFCP 
device driver see drivers/s390/scsi/
  

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 76bf2c78e737..0960ead8d96a 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,11 +15,16 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 
+#include 
 #include "pmbus.h"
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GO_COMMAND 0xD8
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -46,6 +51,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct mutex ioctl_mutex; /* lock ioctl access */
 };
 
 /* Different from Block Read as it sends data and waits for the slave to
@@ -311,6 +317,93 @@ static int adm1266_config_gpio(struct adm1266_data *data)
 }
 #endif
 
+static int adm1266_set_go_command_op(struct adm1266_data *data, u8 val)
+{
+   val = FIELD_GET(GENMASK(4, 0), val);
+
+   return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, val);
+}
+
+static int adm1266_ioctl_unlocked(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+   int __user *argp = (int __user *)arg;
+   struct adm1266_data *data;
+   int val;
+   int ret;
+
+   data = fp->private_data;
+
+   if (!argp)
+   return -EINVAL;
+
+   switch (cmd) {
+   case ADM1266_SET_GO_COMMAND:
+   if (copy_from_user(, argp, sizeof(int)))
+   return -EFAULT;
+
+   return adm1266_set_go_command_op(data, val);
+   case ADM1266_GET_STATUS:
+   ret = i2c_smbus_read_word_data(data->client,
+  ADM1266_READ_STATE);
+
+   if (ret < 0)
+   return ret;
+
+   if (copy_to_user(argp, , sizeof(int)))
+   return -EFAULT;
+
+   break;
+   default:
+   return -ENOTTY;
+   }
+
+   return 0;
+}
+
+static long adm1266_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+   struct adm1266_data *data;
+   long ret;
+
+   data = fp->private_data;
+
+   mutex_lock(>ioctl_mutex);
+   ret = adm1266_ioctl_unlocked(fp, cmd, arg);
+   mutex_unlock(>ioctl_mutex);
+
+   return ret;
+}
+
+static int adm1266_open(struct inode *inode, struct file *filp)
+{
+   filp->private_data = PDE_DATA(inode);
+
+   return 0;
+}
+
+static const struct proc_ops adm1266_proc_ops = {
+   .proc_open  = adm1266_open,
+   .proc_ioctl = adm1266_ioctl,
+};
+
+static int adm1266_init_procfs(struct adm1266_data *data)
+{
+   struct proc_dir_entry *proc_dir;
+   u8 proc_fs_name[32];
+
+   mutex_init(>ioctl_mutex);
+
+   snprintf(proc_fs_name, 32, "adm1266-%x", data->client->addr);
+   proc_dir = proc_create_data(proc_fs_name, 0, NULL, _proc_ops,
+   data);
+
+   if (!proc_dir)
+   return 

[PATCH v4 1/7] hwmon: pmbus: adm1266: add support

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 35 +++
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 62 +
 4 files changed, 107 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..65662115750c
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,35 @@
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index de12a565006d..6949483aa732 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 5feb45806123..ed38f6d6f845 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..a7ef048a9a5c
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pmbus.h"
+
+static int adm1266_probe(struct i2c_client *client,
+const struct i2c_device_id *id)
+{
+   struct pmbus_driver_info *info;
+   u32 funcs;
+   int i;
+
+   info = devm_kzalloc(>dev, sizeof(struct pmbus_driver_info),
+   GFP_KERNEL);
+
+   info->pages = 17;
+   info->format[PSC_VOLTAGE_OUT] = linear;
+   funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+   for (i = 0; i < info->pages; i++)
+   info->func[i] = funcs;
+
+   return pmbus_do_probe(client, id, info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+   .driver = {
+  .name = "adm1266",
+  .of_match_table = adm1266_of_match,
+ },
+   .probe = adm1266_probe,
+   .remove = pmbus_do_remove,
+   .id_table = adm1266_id,
+};
+
+module_i2c_driver(adm1266_driver);
+
+MODULE_AUTHOR("Alexandru Tachici ");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1



[PATCH v4 7/7] dt-bindings: hwmon: Add bindings for ADM1266

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..76b62be48d56
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+#address-cells = <1>;
+#size-cells = <0>;
+};
+};
+...
-- 
2.20.1



[PATCH v4 2/7] hwmon: pmbus: adm1266: Add Block process call

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for ADM1266.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig   |  1 +
 drivers/hwmon/pmbus/adm1266.c | 79 +++
 2 files changed, 80 insertions(+)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 6949483aa732..dc6971a7c49e 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -28,6 +28,7 @@ config SENSORS_PMBUS
 
 config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+   select CRC8
help
  If you say yes here you get hardware monitoring support for Analog
  Devices ADM1266 Cascadable Super Sequencer.
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index a7ef048a9a5c..381d89a8569f 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,6 +6,7 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -14,6 +15,82 @@
 
 #include "pmbus.h"
 
+#define ADM1266_PMBUS_BLOCK_MAX255
+
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/* Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+int pmbus_block_xfer(struct i2c_client *client, u8 cmd, u8 w_len,
+u8 *data_w, u8 *data_r)
+{
+   u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 2];
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = 0,
+   .buf = write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD,
+   .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+   }
+   };
+   u8 addr = 0;
+   u8 crc = 0;
+   int ret;
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   msgs[0].buf = i2c_get_dma_safe_msg_buf([0], 1);
+   if (!msgs[0].buf)
+   return -ENOMEM;
+
+   msgs[1].buf = i2c_get_dma_safe_msg_buf([1], 1);
+   if (!msgs[1].buf) {
+   i2c_put_dma_safe_msg_buf(msgs[0].buf, [0], false);
+   return -ENOMEM;
+   }
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   ret = -EPROTO;
+   goto cleanup;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 1,
+  crc);
+
+   if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) {
+   ret = -EBADMSG;
+   goto cleanup;
+   }
+   }
+
+   memcpy(data_r, [1].buf[1], msgs[1].buf[0]);
+   ret = msgs[1].buf[0];
+
+cleanup:
+   i2c_put_dma_safe_msg_buf(msgs[0].buf, [0], true);
+   i2c_put_dma_safe_msg_buf(msgs[1].buf, [1], true);
+
+   return ret;
+}
+
 static int adm1266_probe(struct i2c_client *client,
 const struct i2c_device_id *id)
 {
@@ -24,6 +101,8 @@ static int adm1266_probe(struct i2c_client *client,
info = devm_kzalloc(>dev, sizeof(struct pmbus_driver_info),
GFP_KERNEL);
 
+   crc8_populate_msb(pmbus_crc_table, 0x7);
+
info->pages = 17;
info->format[PSC_VOLTAGE_OUT] = linear;
funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
-- 
2.20.1



[PATCH v4 6/7] hwmon: pmbus: adm1266: debugfs for blackbox info

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Add a debugfs file to print information in the
BLACKBOX_INFORMATION register. Contains information
about the number of stored records, logic index and id
of the latest record.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 56 ++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index b9e92ab1e39a..ea2dc481094b 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -60,6 +60,7 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex ioctl_mutex; /* lock ioctl access */
+   struct dentry *debugfs_dir;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
u8 *dev_mem;
@@ -406,6 +407,28 @@ static const struct proc_ops adm1266_proc_ops = {
.proc_ioctl = adm1266_ioctl,
 };
 
+static int adm1266_blackbox_information_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   u8 read_buf[5];
+   unsigned int latest_id;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   seq_puts(s, "BLACKBOX_INFORMATION:\n");
+   latest_id = read_buf[0] + (read_buf[1] << 8);
+   seq_printf(s, "Black box ID: %u\n", latest_id);
+   seq_printf(s, "Logic index: %u\n", read_buf[2]);
+   seq_printf(s, "Record count: %u\n", read_buf[3]);
+
+   return 0;
+}
+
 static int adm1266_init_procfs(struct adm1266_data *data)
 {
struct proc_dir_entry *proc_dir;
@@ -423,6 +446,29 @@ static int adm1266_init_procfs(struct adm1266_data *data)
return 0;
 }
 
+static int adm1266_init_debugfs(struct adm1266_data *data)
+{
+   struct dentry *entry;
+   struct dentry *root;
+
+   root = pmbus_get_debugfs_dir(data->client);
+   if (!root)
+   return -ENOENT;
+
+   data->debugfs_dir = debugfs_create_dir(data->client->name, root);
+   if (!data->debugfs_dir)
+   return -ENOENT;
+
+   entry = debugfs_create_devm_seqfile(>client->dev,
+   "blackbox_information",
+   data->debugfs_dir,
+   adm1266_blackbox_information_read);
+   if (!entry)
+   return -ENOENT;
+
+   return 0;
+}
+
 static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf)
 {
u8 read_buf[5];
@@ -571,7 +617,15 @@ static int adm1266_probe(struct i2c_client *client,
for (i = 0; i < info->pages; i++)
info->func[i] = funcs;
 
-   return pmbus_do_probe(client, id, info);
+   ret = pmbus_do_probe(client, id, info);
+   if (ret)
+   return ret;
+
+   ret = adm1266_init_debugfs(data);
+   if (ret)
+   dev_warn(>dev, "Failed to register debugfs: %d\n", ret);
+
+   return 0;
 }
 
 static const struct of_device_id adm1266_of_match[] = {
-- 
2.20.1



[PATCH v4 5/7] hwmon: pmbus: adm1266: read blackbox

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 134 ++
 1 file changed, 134 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 0960ead8d96a..b9e92ab1e39a 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -15,6 +15,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -22,10 +24,13 @@
 #include 
 #include "pmbus.h"
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -42,6 +47,9 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 #define ADM1266_PMBUS_BLOCK_MAX255
 
 DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -52,6 +60,17 @@ struct adm1266_data {
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct mutex ioctl_mutex; /* lock ioctl access */
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
+};
+
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
 };
 
 /* Different from Block Read as it sends data and waits for the slave to
@@ -404,6 +423,117 @@ static int adm1266_init_procfs(struct adm1266_data *data)
return 0;
 }
 
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf)
+{
+   u8 read_buf[5];
+   char index;
+   int record_count;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   record_count = read_buf[3];
+
+   for (index = 0; index < record_count; index++) {
+   ret = pmbus_block_xfer(data->client, ADM1266_READ_BLACKBOX, 1,
+  , buf);
+   if (ret < 0)
+   return ret;
+
+   buf += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell,
+unsigned int offset, size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   if (start_addr <= cell_end && cell_start <= end_addr)
+   return true;
+
+   return false;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data,
+const struct nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   dev_err(>client->dev, "Could not read blackbox!");
+   return ret;
+   default:
+   return -EINVAL;
+   }
+}
+
+static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+   const struct nvmem_cell_info *mem_cell;
+   struct adm1266_data *data = priv;
+   int ret;
+   int i;
+
+   for (i = 0; i < data->nvmem_config.ncells; i++) {
+   mem_cell = _nvmem_cells[i];
+   if (!adm1266_cell_is_accessed(mem_cell, offset, bytes))
+   continue;
+
+   ret = adm1266_read_mem_cell(data, mem_cell);
+   if (ret < 0)
+   return ret;
+   }
+
+   memcpy(val, data->dev_mem + offset, bytes);
+
+   return 0;
+}
+
+static int adm1266_config_nvmem(struct adm1266_data *data)
+{
+   data->nvmem_config.name = dev_name(>client->dev);
+   data->nvmem_config.dev = >client->dev;
+   data->nvmem_config.root_only = true;
+   data->nvmem_config.read_only = true;
+   data->nvmem_config.owner = THIS_MODULE;
+   data->nvmem_config.reg_read = adm1266_nvmem_read;
+   data->nvmem_config.cells = adm1266_nvmem_cells;
+   data->nvmem_config.ncells = ARRAY_SIZE(adm1266_nvmem_cells);
+   data->nvmem_config.priv = data;

[PATCH v4 0/7] hwmon: pmbus: adm1266: add support

2020-06-23 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block Write-Read Process Call command.
A PMBus specific implementation was required because
block write with I2C_SMBUS_PROC_CALL flag allows a
maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Add two ioctl commands for issuing GO_COMMAND
and reading the state of the adm1266 sequencer.

5. Blackboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Expose BLACKBOX_INFO register through debugfs.

7. Device tree bindings for ADM1266.

Alexandru Tachici (7):
  hwmon: pmbus: adm1266: add support
  hwmon: pmbus: adm1266: Add Block process call
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: Add ioctl commands
  hwmon: pmbus: adm1266: read blackbox
  hwmon: pmbus: adm1266: debugfs for blackbox info
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog v3 -> v4:
- moved pmbus_block_wr (pmbus process call) from pmbus_core.
to adm1266.c and renamed to pmbus_block_xfer
- in pmbus_block_xfer: fixed buffer size bug (from 255 to 257)
- in adm1266_gpio_get_multiple: handle pdios and gpios one at a time
to lower allocated space on stack
- in adm1266_gpio_dbg_show: replaced write_buf with u8 write_cmd var
- in adm1266_gpio_dbg_show: check number of bytes received from device
returned by pmbus_block_xfer.
- now use ioctl to send GO_COMMAND and retrieve current state of adm1266
- split blackbox commit into blackbox nvmem implementation and debugfs
blackbox info debugfs
- create adm1266 debugfs dir under /sys/kernel/debug/pmbus/hwmon for
blackbox_info

 .../bindings/hwmon/adi,adm1266.yaml   |  56 ++
 Documentation/hwmon/adm1266.rst   |  50 ++
 .../userspace-api/ioctl/ioctl-number.rst  |   1 +
 drivers/hwmon/pmbus/Kconfig   |  10 +
 drivers/hwmon/pmbus/Makefile  |   1 +
 drivers/hwmon/pmbus/adm1266.c | 657 ++
 include/uapi/linux/adm1266.h  |  16 +
 7 files changed, 791 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c
 create mode 100644 include/uapi/linux/adm1266.h

-- 
2.20.1



[PATCH v3 1/6] hwmon: pmbus: adm1266: add support

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Add pmbus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

Signed-off-by: Alexandru Tachici 
---
 Documentation/hwmon/adm1266.rst | 35 +++
 drivers/hwmon/pmbus/Kconfig |  9 +
 drivers/hwmon/pmbus/Makefile|  1 +
 drivers/hwmon/pmbus/adm1266.c   | 62 +
 4 files changed, 107 insertions(+)
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

diff --git a/Documentation/hwmon/adm1266.rst b/Documentation/hwmon/adm1266.rst
new file mode 100644
index ..65662115750c
--- /dev/null
+++ b/Documentation/hwmon/adm1266.rst
@@ -0,0 +1,35 @@
+Kernel driver adm1266
+=
+
+Supported chips:
+  * Analog Devices ADM1266
+Prefix: 'adm1266'
+Datasheet: 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+Author: Alexandru Tachici 
+
+
+Description
+---
+
+This driver supports hardware monitoring for Analog Devices ADM1266 sequencer.
+
+ADM1266 is a sequencer that features voltage readback from 17 channels via an
+integrated 12 bit SAR ADC, accessed using a PMBus interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Sysfs entries
+-
+
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
+
+inX_label  "voutx"
+inX_input  Measured voltage.
+inX_minMinimum Voltage.
+inX_maxMaximum voltage.
+inX_min_alarm  Voltage low alarm.
+inX_max_alarm  Voltage high alarm.
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index de12a565006d..6949483aa732 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -26,6 +26,15 @@ config SENSORS_PMBUS
  This driver can also be built as a module. If so, the module will
  be called pmbus.
 
+config SENSORS_ADM1266
+   tristate "Analog Devices ADM1266 Sequencer"
+   help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1266 Cascadable Super Sequencer.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1266.
+
 config SENSORS_ADM1275
tristate "Analog Devices ADM1275 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 5feb45806123..ed38f6d6f845 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_PMBUS)+= pmbus_core.o
 obj-$(CONFIG_SENSORS_PMBUS)+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)+= ibm-cffps.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
new file mode 100644
index ..a7ef048a9a5c
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1266 - Cascadable Super Sequencer with Margin
+ * Control and Fault Recording
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pmbus.h"
+
+static int adm1266_probe(struct i2c_client *client,
+const struct i2c_device_id *id)
+{
+   struct pmbus_driver_info *info;
+   u32 funcs;
+   int i;
+
+   info = devm_kzalloc(>dev, sizeof(struct pmbus_driver_info),
+   GFP_KERNEL);
+
+   info->pages = 17;
+   info->format[PSC_VOLTAGE_OUT] = linear;
+   funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+   for (i = 0; i < info->pages; i++)
+   info->func[i] = funcs;
+
+   return pmbus_do_probe(client, id, info);
+}
+
+static const struct of_device_id adm1266_of_match[] = {
+   { .compatible = "adi,adm1266" },
+   { }
+};
+MODULE_DEVICE_TABLE(of, adm1266_of_match);
+
+static const struct i2c_device_id adm1266_id[] = {
+   { "adm1266", 0 },
+   { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1266_id);
+
+static struct i2c_driver adm1266_driver = {
+   .driver = {
+  .name = "adm1266",
+  .of_match_table = adm1266_of_match,
+ },
+   .probe = adm1266_probe,
+   .remove = pmbus_do_remove,
+   .id_table = adm1266_id,
+};
+
+module_i2c_driver(adm1266_driver);
+
+MODULE_AUTHOR("Alexandru Tachici ");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1



[PATCH v3 0/6] hwmon: pmbus: adm1266: add support

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Add PMBus probing driver for the adm1266 Cascadable
Super Sequencer with Margin Control and Fault Recording.
Driver is using the pmbus_core, creating sysfs files
under hwmon for inputs: vh1->vh4 and vp1->vp13.

1. Add PMBus probing driver for inputs vh1->vh4
and vp1->vp13.

2. Add Block WR command. A pmbus specific implementation
was required because block write with I2C_SMBUS_PROC_CALL
flag allows a maximum of 32 bytes to be received.

3. This makes adm1266 driver expose GPIOs
to user-space. Currently are read only. Future
developments on the firmware will allow
them to be writable.

4. Add debugfs files for go_command and read_state.

5. Blakcboxes are 64 bytes of chip state related data
that is generated on faults. Use the nvmem kernel api
to expose the blackbox chip functionality to userspace.

6. Device tree bindings for ADM1266.

Alexandru Tachici (6):
  hwmon: pmbus: adm1266: add support
  hwmon: (pmbus/core) Add Block WR
  hwmon: pmbus: adm1266: Add support for GPIOs
  hwmon: pmbus: adm1266: add debugfs attr for states
  hwmon: pmbus: adm1266: read blackbox
  dt-bindings: hwmon: Add bindings for ADM1266

Changelog v2 -> v3:
- added block write-read process call in pmbus-core
- expose GPIOs/PDIOs to user-space as readonly
- allow the user to issue commands to adm1266
in go_command debugfs file
- allow the user to read state of the adm1266 sequencer
from debugfs file read_state
- chip generated blackboxes can now be read
from the nvmem file 

 .../bindings/hwmon/adi,adm1266.yaml   |  56 ++
 Documentation/hwmon/adm1266.rst   |  35 ++
 drivers/hwmon/pmbus/Kconfig   |  11 +-
 drivers/hwmon/pmbus/Makefile  |   1 +
 drivers/hwmon/pmbus/adm1266.c | 500 ++
 drivers/hwmon/pmbus/pmbus.h   |   4 +
 drivers/hwmon/pmbus/pmbus_core.c  |  88 +++
 7 files changed, 694 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
 create mode 100644 Documentation/hwmon/adm1266.rst
 create mode 100644 drivers/hwmon/pmbus/adm1266.c

-- 
2.20.1



[PATCH v3 5/6] hwmon: pmbus: adm1266: read blackbox

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Use the nvmem kernel api to expose the black box
chip functionality to userspace.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 160 ++
 1 file changed, 160 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 85d6795b79d3..831156004087 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -14,14 +14,19 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "pmbus.h"
 
+#define ADM1266_BLACKBOX_CONFIG0xD3
 #define ADM1266_PDIO_CONFIG0xD4
 #define ADM1266_GO_COMMAND 0xD8
 #define ADM1266_READ_STATE 0xD9
+#define ADM1266_READ_BLACKBOX  0xDE
 #define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_BLACKBOX_INFO  0xE6
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
 
@@ -38,12 +43,26 @@
 #define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
 #define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
 
+#define ADM1266_BLACKBOX_OFFSET0x7F700
+#define ADM1266_BLACKBOX_SIZE  64
+
 struct adm1266_data {
struct pmbus_driver_info info;
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
struct dentry *debugfs_dir;
+   struct nvmem_config nvmem_config;
+   struct nvmem_device *nvmem;
+   u8 *dev_mem;
+};
+
+static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
+   {
+   .name   = "blackbox",
+   .offset = ADM1266_BLACKBOX_OFFSET,
+   .bytes  = 2048,
+   },
 };
 
 #if IS_ENABLED(CONFIG_GPIOLIB)
@@ -261,6 +280,28 @@ static int adm1266_set_go_command_op(void *pdata, u64 val)
return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, reg);
 }
 
+static int adm1266_blackbox_information_read(struct seq_file *s, void *pdata)
+{
+   struct device *dev = s->private;
+   struct i2c_client *client = to_i2c_client(dev);
+   u8 read_buf[PMBUS_BLOCK_MAX + 1];
+   unsigned int latest_id;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   seq_puts(s, "BLACKBOX_INFORMATION:\n");
+   latest_id = read_buf[0] + (read_buf[1] << 8);
+   seq_printf(s, "Black box ID: %x\n", latest_id);
+   seq_printf(s, "Logic index: %x\n", read_buf[2]);
+   seq_printf(s, "Record count: %x\n", read_buf[3]);
+
+   return 0;
+}
+
 DEFINE_DEBUGFS_ATTRIBUTE(go_command_fops, NULL, adm1266_set_go_command_op,
 "%llu\n");
 DEFINE_DEBUGFS_ATTRIBUTE(read_state_fops, adm1266_get_state_op, NULL, 
"%llu\n");
@@ -277,6 +318,121 @@ static void adm1266_debug_init(struct adm1266_data *data)
   _command_fops);
debugfs_create_file_unsafe("read_state", 0400, root, data,
   _state_fops);
+   debugfs_create_devm_seqfile(>client->dev, "blackbox_information",
+   root, adm1266_blackbox_information_read);
+}
+
+static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf)
+{
+   u8 write_buf[PMBUS_BLOCK_MAX + 1];
+   u8 read_buf[PMBUS_BLOCK_MAX + 1];
+   int record_count;
+   int ret;
+   int i;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   record_count = read_buf[3];
+
+   for (i = 0; i < record_count; i++) {
+   write_buf[0] = i;
+   ret = pmbus_block_wr(data->client, ADM1266_READ_BLACKBOX, 1,
+write_buf, buf);
+   if (ret < 0)
+   return ret;
+
+   buf += ADM1266_BLACKBOX_SIZE;
+   }
+
+   return 0;
+}
+
+static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell,
+unsigned int offset, size_t bytes)
+{
+   unsigned int start_addr = offset;
+   unsigned int end_addr = offset + bytes;
+   unsigned int cell_start = mem_cell->offset;
+   unsigned int cell_end = mem_cell->offset + mem_cell->bytes;
+
+   if (start_addr <= cell_end && cell_start <= end_addr)
+   return true;
+
+   return false;
+}
+
+static int adm1266_read_mem_cell(struct adm1266_data *data,
+const struct nvmem_cell_info *mem_cell)
+{
+   u8 *mem_offset;
+   int ret;
+
+   switch (mem_cell->offset) {
+   case ADM1266_BLACKBOX_OFFSET:
+   mem_offset = data->dev_mem + mem_cell->offset;
+   ret = adm1266_nvmem_read_blackbox(data, mem_offset);
+   if (ret)
+   

[PATCH v3 2/6] hwmon: (pmbus/core) Add Block WR

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

PmBus devices support Block Write-Block Read Process
Call described in SMBus specification v 2.0 with the
exception that Block writes and reads are permitted to
have up 255 data bytes instead of max 32 bytes (SMBus).

This patch adds Block WR process call support for PMBus.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/Kconfig  |  2 +-
 drivers/hwmon/pmbus/pmbus.h  |  4 ++
 drivers/hwmon/pmbus/pmbus_core.c | 88 
 3 files changed, 93 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 6949483aa732..f11712fdcea8 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig PMBUS
tristate "PMBus support"
-   depends on I2C
+   depends on I2C && CRC8
help
  Say yes here if you want to enable PMBus support.
 
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 18e06fc6c53f..ae4e15c5dff2 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -392,6 +392,8 @@ enum pmbus_sensor_classes {
 #define PMBUS_PHASE_VIRTUALBIT(30) /* Phases on this page are virtual */
 #define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */
 
+#define PMBUS_BLOCK_MAX255
+
 enum pmbus_data_format { linear = 0, direct, vid };
 enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
 
@@ -467,6 +469,8 @@ int pmbus_read_word_data(struct i2c_client *client, int 
page, int phase,
 u8 reg);
 int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
  u16 word);
+int pmbus_block_wr(struct i2c_client *client, u8 cmd, u8 w_len, u8 *data_w,
+  u8 *data_r);
 int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
 int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
 int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 8d321bf7d15b..ef63468da3b5 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -6,6 +6,7 @@
  * Copyright (c) 2012 Guenter Roeck
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -44,6 +45,8 @@
 
 #define PMBUS_NAME_SIZE24
 
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
 struct pmbus_sensor {
struct pmbus_sensor *next;
char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
@@ -184,6 +187,89 @@ int pmbus_set_page(struct i2c_client *client, int page, 
int phase)
 }
 EXPORT_SYMBOL_GPL(pmbus_set_page);
 
+/* Block Write/Read command.
+ * @client: Handle to slave device
+ * @cmd: Byte interpreted by slave
+ * @w_len: Size of write data block; PMBus allows at most 255 bytes
+ * @data_w: byte array which will be written.
+ * @data_r: Byte array into which data will be read; big enough to hold
+ * the data returned by the slave. PMBus allows at most 255 bytes.
+ *
+ * Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ *
+ * Returns number of bytes read or negative errno.
+ */
+int pmbus_block_wr(struct i2c_client *client, u8 cmd, u8 w_len,
+  u8 *data_w, u8 *data_r)
+{
+   u8 write_buf[PMBUS_BLOCK_MAX + 1];
+   struct i2c_msg msgs[2] = {
+   {
+   .addr = client->addr,
+   .flags = 0,
+   .buf = write_buf,
+   .len = w_len + 2,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD,
+   .len = PMBUS_BLOCK_MAX,
+   }
+   };
+   u8 addr = 0;
+   u8 crc = 0;
+   int ret;
+
+   msgs[0].buf[0] = cmd;
+   msgs[0].buf[1] = w_len;
+   memcpy([0].buf[2], data_w, w_len);
+
+   msgs[0].buf = i2c_get_dma_safe_msg_buf([0], 1);
+   if (!msgs[0].buf)
+   return -ENOMEM;
+
+   msgs[1].buf = i2c_get_dma_safe_msg_buf([1], 1);
+   if (!msgs[1].buf) {
+   i2c_put_dma_safe_msg_buf(msgs[0].buf, [0], false);
+   return -ENOMEM;
+   }
+
+   ret = i2c_transfer(client->adapter, msgs, 2);
+   if (ret != 2) {
+   dev_err(>dev, "I2C transfer error.");
+   goto cleanup;
+   }
+
+   if (client->flags & I2C_CLIENT_PEC) {
+   addr = i2c_8bit_addr_from_msg([0]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[0].buf,  msgs[0].len, crc);
+
+   addr = i2c_8bit_addr_from_msg([1]);
+   crc = crc8(pmbus_crc_table, , 1, crc);
+   crc = crc8(pmbus_crc_table, msgs[1].buf,  msgs[1].buf[0] + 

[PATCH v3 6/6] dt-bindings: hwmon: Add bindings for ADM1266

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Add bindings for the Analog Devices ADM1266 sequencer.

Signed-off-by: Alexandru Tachici 
---
 .../bindings/hwmon/adi,adm1266.yaml   | 56 +++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml 
b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
new file mode 100644
index ..76b62be48d56
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adm1266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording
+
+maintainers:
+  - Alexandru Tachici 
+
+description: |
+  Analog Devices ADM1266 Cascadable Super Sequencer with Margin
+  Control and Fault Recording.
+  
https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1266.pdf
+
+properties:
+  compatible:
+enum:
+  - adi,adm1266
+
+  reg:
+description: |
+  I2C address of slave device.
+items:
+  minimum: 0x40
+  maximum: 0x4F
+
+  avcc-supply:
+description: |
+  Phandle to the Avcc power supply.
+
+  adi,master-adm1266:
+description: |
+  Represents phandle of a master ADM1266 device cascaded through the IDB.
+$ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+i2c0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+adm1266@40 {
+compatible = "adi,adm1266";
+reg = <0x40>;
+#address-cells = <1>;
+#size-cells = <0>;
+};
+};
+...
-- 
2.20.1



[PATCH v3 4/6] hwmon: pmbus: adm1266: add debugfs attr for states

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Add debugfs files for go_command and read_state.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 47 +++
 1 file changed, 47 insertions(+)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 190170300ef1..85d6795b79d3 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -19,6 +19,8 @@
 #include "pmbus.h"
 
 #define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GO_COMMAND 0xD8
+#define ADM1266_READ_STATE 0xD9
 #define ADM1266_GPIO_CONFIG0xE1
 #define ADM1266_PDIO_STATUS0xE9
 #define ADM1266_GPIO_STATUS0xEA
@@ -41,6 +43,7 @@ struct adm1266_data {
struct gpio_chip gc;
const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
struct i2c_client *client;
+   struct dentry *debugfs_dir;
 };
 
 #if IS_ENABLED(CONFIG_GPIOLIB)
@@ -234,6 +237,48 @@ static inline int adm1266_config_gpio(struct adm1266_data 
*data)
 }
 #endif
 
+static int adm1266_get_state_op(void *pdata, u64 *state)
+{
+   struct adm1266_data *data = pdata;
+   int ret;
+
+   ret = i2c_smbus_read_word_data(data->client, ADM1266_READ_STATE);
+   if (ret < 0)
+   return ret;
+
+   *state = ret;
+
+   return 0;
+}
+
+static int adm1266_set_go_command_op(void *pdata, u64 val)
+{
+   struct adm1266_data *data = pdata;
+   u8 reg;
+
+   reg = FIELD_GET(GENMASK(4, 0), val);
+
+   return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, reg);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(go_command_fops, NULL, adm1266_set_go_command_op,
+"%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(read_state_fops, adm1266_get_state_op, NULL, 
"%llu\n");
+
+static void adm1266_debug_init(struct adm1266_data *data)
+{
+   struct dentry *root;
+   char dir_name[30];
+
+   sprintf(dir_name, "adm1266-%x_debugfs", data->client->addr);
+   root = debugfs_create_dir(dir_name, NULL);
+   data->debugfs_dir = root;
+   debugfs_create_file_unsafe("go_command", 0200, root, data,
+  _command_fops);
+   debugfs_create_file_unsafe("read_state", 0400, root, data,
+  _state_fops);
+}
+
 static int adm1266_probe(struct i2c_client *client,
 const struct i2c_device_id *id)
 {
@@ -254,6 +299,8 @@ static int adm1266_probe(struct i2c_client *client,
if (ret < 0)
return ret;
 
+   adm1266_debug_init(data);
+
info = >info;
info->pages = 17;
info->format[PSC_VOLTAGE_OUT] = linear;
-- 
2.20.1



[PATCH v3 3/6] hwmon: pmbus: adm1266: Add support for GPIOs

2020-05-29 Thread alexandru.tachici
From: Alexandru Tachici 

Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They
are controlled by the internal sequencing engine.

This patch makes adm1266 driver expose GPIOs and PDIOs to user-space
using GPIO provider kernel api.

Signed-off-by: Alexandru Tachici 
---
 drivers/hwmon/pmbus/adm1266.c | 233 +-
 1 file changed, 232 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index a7ef048a9a5c..190170300ef1 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -6,7 +6,11 @@
  * Copyright 2020 Analog Devices Inc.
  */
 
+#include 
+#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -14,16 +18,243 @@
 
 #include "pmbus.h"
 
+#define ADM1266_PDIO_CONFIG0xD4
+#define ADM1266_GPIO_CONFIG0xE1
+#define ADM1266_PDIO_STATUS0xE9
+#define ADM1266_GPIO_STATUS0xEA
+
+/* ADM1266 GPIO defines */
+#define ADM1266_GPIO_NR9
+#define ADM1266_GPIO_FUNCTIONS(x)  FIELD_GET(BIT(0), x)
+#define ADM1266_GPIO_INPUT_EN(x)   FIELD_GET(BIT(2), x)
+#define ADM1266_GPIO_OUTPUT_EN(x)  FIELD_GET(BIT(3), x)
+#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x)
+
+/* ADM1266 PDIO defines */
+#define ADM1266_PDIO_NR16
+#define ADM1266_PDIO_PIN_CFG(x)FIELD_GET(GENMASK(15, 13), x)
+#define ADM1266_PDIO_GLITCH_FILT(x)FIELD_GET(GENMASK(12, 9), x)
+#define ADM1266_PDIO_OUT_CFG(x)FIELD_GET(GENMASK(2, 0), x)
+
+struct adm1266_data {
+   struct pmbus_driver_info info;
+   struct gpio_chip gc;
+   const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR];
+   struct i2c_client *client;
+};
+
+#if IS_ENABLED(CONFIG_GPIOLIB)
+static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = {
+   {1, 0},
+   {2, 1},
+   {3, 2},
+   {4, 8},
+   {5, 9},
+   {6, 10},
+   {7, 11},
+   {8, 6},
+   {9, 7},
+};
+
+static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = {
+   "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8",
+   "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6",
+   "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13",
+   "PDIO14", "PDIO15", "PDIO16",
+};
+
+static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 read_buf[PMBUS_BLOCK_MAX + 1];
+   unsigned long pins_status;
+   unsigned int pmbus_cmd;
+   int ret;
+
+   if (offset < ADM1266_GPIO_NR)
+   pmbus_cmd = ADM1266_GPIO_STATUS;
+   else
+   pmbus_cmd = ADM1266_PDIO_STATUS;
+
+   ret = i2c_smbus_read_block_data(data->client, pmbus_cmd,
+   read_buf);
+   if (ret < 0)
+   return ret;
+
+   pins_status = read_buf[0] + (read_buf[1] << 8);
+   if (offset < ADM1266_GPIO_NR)
+   return test_bit(adm1266_gpio_mapping[offset][1], _status);
+   else
+   return test_bit(offset - ADM1266_GPIO_NR, _status);
+}
+
+static int adm1266_gpio_get_multiple(struct gpio_chip *chip,
+unsigned long *mask,
+unsigned long *bits)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 gpio_data[PMBUS_BLOCK_MAX + 1];
+   u8 pdio_data[PMBUS_BLOCK_MAX + 1];
+   unsigned long gpio_status;
+   unsigned long pdio_status;
+   unsigned int gpio_nr;
+   int ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS,
+   gpio_data);
+   if (ret < 0)
+   return ret;
+
+   ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS,
+   pdio_data);
+   if (ret < 0)
+   return ret;
+
+   gpio_status = gpio_data[0] + (gpio_data[1] << 8);
+   pdio_status = pdio_data[0] + (pdio_data[1] << 8);
+   *bits = 0;
+   for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+   if (test_bit(adm1266_gpio_mapping[gpio_nr][1], _status))
+   set_bit(gpio_nr, bits);
+   }
+
+   for_each_set_bit_from(gpio_nr, mask,
+ ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
+   if (test_bit(gpio_nr - ADM1266_GPIO_NR, _status))
+   set_bit(gpio_nr, bits);
+   }
+
+   return 0;
+}
+
+static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+   struct adm1266_data *data = gpiochip_get_data(chip);
+   u8 write_buf[PMBUS_BLOCK_MAX + 1];
+   u8 read_buf[PMBUS_BLOCK_MAX + 1];
+   unsigned long gpio_config;
+   unsigned long pdio_config;
+   unsigned long pin_cfg;
+   int ret;
+   int i;
+
+   for (i = 0; i <