* Re: [RESEND PATCH v4] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
From: Lee Jones @ 2017-04-10 14:54 UTC (permalink / raw)
To: Quentin Schulz
Cc: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
jic23-DgEjT+Ai2ygdnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
icenowy-ymACFijhrKM,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <cf07853a-4382-5172-b24f-5324fb4308d3-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
On Mon, 10 Apr 2017, Quentin Schulz wrote:
> Hi Lee,
>
> Just to make sure you didn't miss that patch (can't find it in
> for-mfd-next and for-mfd-next-next).
It has not been missed, but is yet to be reviewed.
> On 04/04/2017 08:34, Quentin Schulz wrote:
> > The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
> > battery voltage, battery charge and discharge currents, AC-in and VBUS
> > voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
> >
> > This adds support for most of AXP20X and AXP22X ADCs.
> >
> > Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > Acked-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> > Reviewed-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > ---
> >
> > Lee, could you merge this through the mfd tree please?
> > => https://lkml.org/lkml/2017/3/22/47
> >
> > v4:
> > - added missing space at the beginning of a comment,
> > - tidied axp20x_adc_offset_voltage and axp20x_write_raw to use switch case
> > instead of if conditions,
> > - added MODULE_DEVICE_TABLE for axp20x_adc_id_match for module autoloading,
> > - merged two lines in axp20x_remove,
> >
> > v3:
> > - moved from switch to if condition in axp20x_adc_raw and
> > axp22x_adc_raw,
> > - removed DT support as DT node has been dropped,
> > - use of platform_device_id
> > - correctly defined the name of the iio device (name used to probe the
> > driver),
> > - added goto for errors in probe,
> > - added iio_map_array_unregister to the remove function,
> >
> > v2:
> > - removed unused defines,
> > - changed BIT(x) to 1 << x when describing bits purpose for which 2 <<
> > x or 3 << x exists, to be consistent,
> > - changed ADC rate defines to macro formulas,
> > - reordered IIO channels, now different measures (current/voltage) of
> > the same part of the PMIC (e.g. battery), have the same IIO channel in
> > their respective IIO type. When a part of the PMIC have only one
> > measure, a number is jumped,
> > - left IIO channel mapping in DT to use iio_map structure,
> > - removed indexing of ADC internal temperature,
> > - removed unused iio_dev structure in axp20x_adc_iio,
> > - added a structure for data specific to AXP20X or AXP22X PMICs instead
> > of using an ID and an if condition when needing to separate the
> > behaviour of both,
> > - added a comment on batt_chrg_i really being on 12bits rather than
> > what the Chinese datasheets say (13 bits),
> > - corrected the offset for AXP22X PMIC temperature,
> > - set the ADC rate to a value (100Hz) shared by the AXP20X and AXP22X,
> > - created macro formulas to compute the ADC rate for each,
> > - added a condition on presence of ADC_EN2 reg before setting/resetting
> > it,
> > - switched from devm_iio_device_unregister to the non-devm function
> > because of the need for a remove function,
> > - removed some dead code,
> >
> > drivers/iio/adc/Kconfig | 10 +
> > drivers/iio/adc/Makefile | 1 +
> > drivers/iio/adc/axp20x_adc.c | 617 +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 628 insertions(+)
> > create mode 100644 drivers/iio/adc/axp20x_adc.c
> >
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index d777a97..d15e1bd 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC
> > To compile this driver as a module, choose M here: the module will be
> > called at91-sama5d2_adc.
> >
> > +config AXP20X_ADC
> > + tristate "X-Powers AXP20X and AXP22X ADC driver"
> > + depends on MFD_AXP20X
> > + help
> > + Say yes here to have support for X-Powers power management IC (PMIC)
> > + AXP20X and AXP22X ADC devices.
> > +
> > + To compile this driver as a module, choose M here: the module will be
> > + called axp20x_adc.
> > +
> > config AXP288_ADC
> > tristate "X-Powers AXP288 ADC driver"
> > depends on MFD_AXP20X
> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> > index b11bb57..17899b5 100644
> > --- a/drivers/iio/adc/Makefile
> > +++ b/drivers/iio/adc/Makefile
> > @@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
> > obj-$(CONFIG_AD799X) += ad799x.o
> > obj-$(CONFIG_AT91_ADC) += at91_adc.o
> > obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> > +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
> > obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> > obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
> > obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> > diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> > new file mode 100644
> > index 0000000..11e1771
> > --- /dev/null
> > +++ b/drivers/iio/adc/axp20x_adc.c
> > @@ -0,0 +1,617 @@
> > +/* ADC driver for AXP20X and AXP22X PMICs
> > + *
> > + * Copyright (c) 2016 Free Electrons NextThing Co.
> > + * Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + */
> > +
> > +#include <linux/completion.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/thermal.h>
> > +
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/driver.h>
> > +#include <linux/iio/machine.h>
> > +#include <linux/mfd/axp20x.h>
> > +
> > +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
> > +
> > +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
> > +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
> > +
> > +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> > +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
> > +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
> > +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
> > +
> > +#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
> > +#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
> > +#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
> > +
> > +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
> > + { \
> > + .type = _type, \
> > + .indexed = 1, \
> > + .channel = _channel, \
> > + .address = _reg, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > + BIT(IIO_CHAN_INFO_SCALE), \
> > + .datasheet_name = _name, \
> > + }
> > +
> > +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
> > + { \
> > + .type = _type, \
> > + .indexed = 1, \
> > + .channel = _channel, \
> > + .address = _reg, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > + BIT(IIO_CHAN_INFO_SCALE) |\
> > + BIT(IIO_CHAN_INFO_OFFSET),\
> > + .datasheet_name = _name, \
> > + }
> > +
> > +struct axp_data;
> > +
> > +struct axp20x_adc_iio {
> > + struct regmap *regmap;
> > + struct axp_data *data;
> > +};
> > +
> > +enum axp20x_adc_channel_v {
> > + AXP20X_ACIN_V = 0,
> > + AXP20X_VBUS_V,
> > + AXP20X_TS_IN,
> > + AXP20X_GPIO0_V,
> > + AXP20X_GPIO1_V,
> > + AXP20X_IPSOUT_V,
> > + AXP20X_BATT_V,
> > +};
> > +
> > +enum axp20x_adc_channel_i {
> > + AXP20X_ACIN_I = 0,
> > + AXP20X_VBUS_I,
> > + AXP20X_BATT_CHRG_I,
> > + AXP20X_BATT_DISCHRG_I,
> > +};
> > +
> > +enum axp22x_adc_channel_v {
> > + AXP22X_TS_IN = 0,
> > + AXP22X_BATT_V,
> > +};
> > +
> > +enum axp22x_adc_channel_i {
> > + AXP22X_BATT_CHRG_I = 1,
> > + AXP22X_BATT_DISCHRG_I,
> > +};
> > +
> > +static struct iio_map axp20x_maps[] = {
> > + {
> > + .consumer_dev_name = "axp20x-usb-power-supply",
> > + .consumer_channel = "vbus_v",
> > + .adc_channel_label = "vbus_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-usb-power-supply",
> > + .consumer_channel = "vbus_i",
> > + .adc_channel_label = "vbus_i",
> > + }, {
> > + .consumer_dev_name = "axp20x-ac-power-supply",
> > + .consumer_channel = "acin_v",
> > + .adc_channel_label = "acin_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-ac-power-supply",
> > + .consumer_channel = "acin_i",
> > + .adc_channel_label = "acin_i",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_v",
> > + .adc_channel_label = "batt_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_chrg_i",
> > + .adc_channel_label = "batt_chrg_i",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_dischrg_i",
> > + .adc_channel_label = "batt_dischrg_i",
> > + }, { /* sentinel */ }
> > +};
> > +
> > +static struct iio_map axp22x_maps[] = {
> > + {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_v",
> > + .adc_channel_label = "batt_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_chrg_i",
> > + .adc_channel_label = "batt_chrg_i",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_dischrg_i",
> > + .adc_channel_label = "batt_dischrg_i",
> > + }, { /* sentinel */ }
> > +};
> > +
> > +/*
> > + * Channels are mapped by physical system. Their channels share the same index.
> > + * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
> > + * The only exception is for the battery. batt_v will be in_voltage6_raw and
> > + * charge current in_current6_raw and discharge current will be in_current7_raw.
> > + */
> > +static const struct iio_chan_spec axp20x_adc_channels[] = {
> > + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
> > + AXP20X_ACIN_V_ADC_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
> > + AXP20X_ACIN_I_ADC_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
> > + AXP20X_VBUS_V_ADC_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
> > + AXP20X_VBUS_I_ADC_H),
> > + {
> > + .type = IIO_TEMP,
> > + .address = AXP20X_TEMP_ADC_H,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > + BIT(IIO_CHAN_INFO_SCALE) |
> > + BIT(IIO_CHAN_INFO_OFFSET),
> > + .datasheet_name = "pmic_temp",
> > + },
> > + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
> > + AXP20X_GPIO0_V_ADC_H),
> > + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
> > + AXP20X_GPIO1_V_ADC_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
> > + AXP20X_IPSOUT_V_HIGH_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
> > + AXP20X_BATT_V_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> > + AXP20X_BATT_CHRG_I_H),
> > + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> > + AXP20X_BATT_DISCHRG_I_H),
> > +};
> > +
> > +static const struct iio_chan_spec axp22x_adc_channels[] = {
> > + {
> > + .type = IIO_TEMP,
> > + .address = AXP22X_PMIC_TEMP_H,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > + BIT(IIO_CHAN_INFO_SCALE) |
> > + BIT(IIO_CHAN_INFO_OFFSET),
> > + .datasheet_name = "pmic_temp",
> > + },
> > + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
> > + AXP20X_BATT_V_H),
> > + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> > + AXP20X_BATT_CHRG_I_H),
> > + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> > + AXP20X_BATT_DISCHRG_I_H),
> > +};
> > +
> > +static int axp20x_adc_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val)
> > +{
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > + int size = 12;
> > +
> > + /*
> > + * N.B.: Unlike the Chinese datasheets tell, the charging current is
> > + * stored on 12 bits, not 13 bits. Only discharging current is on 13
> > + * bits.
> > + */
> > + if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I)
> > + size = 13;
> > + else
> > + size = 12;
> > +
> > + *val = axp20x_read_variable_width(info->regmap, chan->address, size);
> > + if (*val < 0)
> > + return *val;
> > +
> > + return IIO_VAL_INT;
> > +}
> > +
> > +static int axp22x_adc_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val)
> > +{
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > + int size;
> > +
> > + /*
> > + * N.B.: Unlike the Chinese datasheets tell, the charging current is
> > + * stored on 12 bits, not 13 bits. Only discharging current is on 13
> > + * bits.
> > + */
> > + if (chan->type == IIO_CURRENT && chan->channel == AXP22X_BATT_DISCHRG_I)
> > + size = 13;
> > + else
> > + size = 12;
> > +
> > + *val = axp20x_read_variable_width(info->regmap, chan->address, size);
> > + if (*val < 0)
> > + return *val;
> > +
> > + return IIO_VAL_INT;
> > +}
> > +
> > +static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
> > +{
> > + switch (channel) {
> > + case AXP20X_ACIN_V:
> > + case AXP20X_VBUS_V:
> > + *val = 1;
> > + *val2 = 700000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case AXP20X_GPIO0_V:
> > + case AXP20X_GPIO1_V:
> > + *val = 0;
> > + *val2 = 500000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case AXP20X_BATT_V:
> > + *val = 1;
> > + *val2 = 100000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case AXP20X_IPSOUT_V:
> > + *val = 1;
> > + *val2 = 400000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp20x_adc_scale_current(int channel, int *val, int *val2)
> > +{
> > + switch (channel) {
> > + case AXP20X_ACIN_I:
> > + *val = 0;
> > + *val2 = 625000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case AXP20X_VBUS_I:
> > + *val = 0;
> > + *val2 = 375000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case AXP20X_BATT_DISCHRG_I:
> > + case AXP20X_BATT_CHRG_I:
> > + *val = 0;
> > + *val2 = 500000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
> > + int *val2)
> > +{
> > + switch (chan->type) {
> > + case IIO_VOLTAGE:
> > + return axp20x_adc_scale_voltage(chan->channel, val, val2);
> > +
> > + case IIO_CURRENT:
> > + return axp20x_adc_scale_current(chan->channel, val, val2);
> > +
> > + case IIO_TEMP:
> > + *val = 100;
> > + return IIO_VAL_INT;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
> > + int *val2)
> > +{
> > + switch (chan->type) {
> > + case IIO_VOLTAGE:
> > + if (chan->channel != AXP22X_BATT_V)
> > + return -EINVAL;
> > +
> > + *val = 1;
> > + *val2 = 100000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case IIO_CURRENT:
> > + *val = 0;
> > + *val2 = 500000;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +
> > + case IIO_TEMP:
> > + *val = 100;
> > + return IIO_VAL_INT;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
> > + int *val)
> > +{
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > + int ret;
> > +
> > + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val);
> > + if (ret < 0)
> > + return ret;
> > +
> > + switch (channel) {
> > + case AXP20X_GPIO0_V:
> > + *val &= AXP20X_GPIO10_IN_RANGE_GPIO0;
> > + break;
> > +
> > + case AXP20X_GPIO1_V:
> > + *val &= AXP20X_GPIO10_IN_RANGE_GPIO1;
> > + break;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + *val = !!(*val) * 700000;
> > +
> > + return IIO_VAL_INT;
> > +}
> > +
> > +static int axp20x_adc_offset(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val)
> > +{
> > + switch (chan->type) {
> > + case IIO_VOLTAGE:
> > + return axp20x_adc_offset_voltage(indio_dev, chan->channel, val);
> > +
> > + case IIO_TEMP:
> > + *val = -1447;
> > + return IIO_VAL_INT;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp20x_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val,
> > + int *val2, long mask)
> > +{
> > + switch (mask) {
> > + case IIO_CHAN_INFO_OFFSET:
> > + return axp20x_adc_offset(indio_dev, chan, val);
> > +
> > + case IIO_CHAN_INFO_SCALE:
> > + return axp20x_adc_scale(chan, val, val2);
> > +
> > + case IIO_CHAN_INFO_RAW:
> > + return axp20x_adc_raw(indio_dev, chan, val);
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp22x_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val,
> > + int *val2, long mask)
> > +{
> > + switch (mask) {
> > + case IIO_CHAN_INFO_OFFSET:
> > + *val = -2677;
> > + return IIO_VAL_INT;
> > +
> > + case IIO_CHAN_INFO_SCALE:
> > + return axp22x_adc_scale(chan, val, val2);
> > +
> > + case IIO_CHAN_INFO_RAW:
> > + return axp22x_adc_raw(indio_dev, chan, val);
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int axp20x_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int val, int val2,
> > + long mask)
> > +{
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > + unsigned int reg, regval;
> > +
> > + /*
> > + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
> > + * for (independently) GPIO0 and GPIO1 when in ADC mode.
> > + */
> > + if (mask != IIO_CHAN_INFO_OFFSET)
> > + return -EINVAL;
> > +
> > + if (val != 0 && val != 700000)
> > + return -EINVAL;
> > +
> > + switch (chan->channel) {
> > + case AXP20X_GPIO0_V:
> > + reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
> > + regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val);
> > + break;
> > +
> > + case AXP20X_GPIO1_V:
> > + reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
> > + regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val);
> > + break;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg,
> > + regval);
> > +}
> > +
> > +static const struct iio_info axp20x_adc_iio_info = {
> > + .read_raw = axp20x_read_raw,
> > + .write_raw = axp20x_write_raw,
> > + .driver_module = THIS_MODULE,
> > +};
> > +
> > +static const struct iio_info axp22x_adc_iio_info = {
> > + .read_raw = axp22x_read_raw,
> > + .driver_module = THIS_MODULE,
> > +};
> > +
> > +static int axp20x_adc_rate(int rate)
> > +{
> > + return AXP20X_ADC_RATE_HZ(rate);
> > +}
> > +
> > +static int axp22x_adc_rate(int rate)
> > +{
> > + return AXP22X_ADC_RATE_HZ(rate);
> > +}
> > +
> > +struct axp_data {
> > + const struct iio_info *iio_info;
> > + int num_channels;
> > + struct iio_chan_spec const *channels;
> > + unsigned long adc_en1_mask;
> > + int (*adc_rate)(int rate);
> > + bool adc_en2;
> > + struct iio_map *maps;
> > +};
> > +
> > +static const struct axp_data axp20x_data = {
> > + .iio_info = &axp20x_adc_iio_info,
> > + .num_channels = ARRAY_SIZE(axp20x_adc_channels),
> > + .channels = axp20x_adc_channels,
> > + .adc_en1_mask = AXP20X_ADC_EN1_MASK,
> > + .adc_rate = axp20x_adc_rate,
> > + .adc_en2 = true,
> > + .maps = axp20x_maps,
> > +};
> > +
> > +static const struct axp_data axp22x_data = {
> > + .iio_info = &axp22x_adc_iio_info,
> > + .num_channels = ARRAY_SIZE(axp22x_adc_channels),
> > + .channels = axp22x_adc_channels,
> > + .adc_en1_mask = AXP22X_ADC_EN1_MASK,
> > + .adc_rate = axp22x_adc_rate,
> > + .adc_en2 = false,
> > + .maps = axp22x_maps,
> > +};
> > +
> > +static const struct platform_device_id axp20x_adc_id_match[] = {
> > + { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
> > + { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
> > + { /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
> > +
> > +static int axp20x_probe(struct platform_device *pdev)
> > +{
> > + struct axp20x_adc_iio *info;
> > + struct iio_dev *indio_dev;
> > + struct axp20x_dev *axp20x_dev;
> > + int ret;
> > +
> > + axp20x_dev = dev_get_drvdata(pdev->dev.parent);
> > +
> > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + info = iio_priv(indio_dev);
> > + platform_set_drvdata(pdev, indio_dev);
> > +
> > + info->regmap = axp20x_dev->regmap;
> > + indio_dev->dev.parent = &pdev->dev;
> > + indio_dev->dev.of_node = pdev->dev.of_node;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > + info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data;
> > +
> > + indio_dev->name = platform_get_device_id(pdev)->name;
> > + indio_dev->info = info->data->iio_info;
> > + indio_dev->num_channels = info->data->num_channels;
> > + indio_dev->channels = info->data->channels;
> > +
> > + /* Enable the ADCs on IP */
> > + regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
> > +
> > + if (info->data->adc_en2)
> > + /* Enable GPIO0/1 and internal temperature ADCs */
> > + regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> > + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
> > +
> > + /* Configure ADCs rate */
> > + regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
> > + info->data->adc_rate(100));
> > +
> > + ret = iio_map_array_register(indio_dev, info->data->maps);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret);
> > + goto fail_map;
> > + }
> > +
> > + ret = iio_device_register(indio_dev);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "could not register the device\n");
> > + goto fail_register;
> > + }
> > +
> > + return 0;
> > +
> > +fail_register:
> > + iio_map_array_unregister(indio_dev);
> > +
> > +fail_map:
> > + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> > +
> > + if (info->data->adc_en2)
> > + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> > +
> > + return ret;
> > +}
> > +
> > +static int axp20x_remove(struct platform_device *pdev)
> > +{
> > + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > +
> > + iio_device_unregister(indio_dev);
> > + iio_map_array_unregister(indio_dev);
> > +
> > + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> > +
> > + if (info->data->adc_en2)
> > + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver axp20x_adc_driver = {
> > + .driver = {
> > + .name = "axp20x-adc",
> > + },
> > + .id_table = axp20x_adc_id_match,
> > + .probe = axp20x_probe,
> > + .remove = axp20x_remove,
> > +};
> > +
> > +module_platform_driver(axp20x_adc_driver);
> > +
> > +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
> > +MODULE_AUTHOR("Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
> > +MODULE_LICENSE("GPL");
> >
>
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH v8 1/2] media: i2c/ov5645: add the device tree binding document
From: Rob Herring @ 2017-04-10 14:57 UTC (permalink / raw)
To: Todor Tomov
Cc: mchehab, laurent.pinchart, hans.verkuil, sakari.ailus,
linux-media, linux-kernel, mark.rutland, devicetree
In-Reply-To: <1491228148-28505-1-git-send-email-todor.tomov@linaro.org>
On Mon, Apr 03, 2017 at 05:02:28PM +0300, Todor Tomov wrote:
> Add the document for ov5645 device tree binding.
>
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> .../devicetree/bindings/media/i2c/ov5645.txt | 54 ++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5645.txt
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH V5 1/2] dt-bindings: thermal: add support for Broadcom's Northstar thermal
From: Rob Herring @ 2017-04-10 15:00 UTC (permalink / raw)
To: Rafał Miłecki
Cc: Mark Rutland, devicetree, Florian Fainelli, Scott Branden,
Stephen Warren, Ray Jui, linux-pm, Lee Jones, Eduardo Valentin,
Eric Anholt, bcm-kernel-feedback-list, linux-rpi-kernel,
Rafał Miłecki, Zhang Rui, linux-arm-kernel
In-Reply-To: <20170403154829.29780-1-zajec5@gmail.com>
On Mon, Apr 03, 2017 at 05:48:28PM +0200, Rafał Miłecki wrote:
> From: Rafał Miłecki <rafal@milecki.pl>
>
> This commit documents binding for thermal used in Northstar family SoCs.
>
> There isn't any known Northstar device with active cooling system so DT
> example has empty cooling-maps node. There is also no support for CPU
> frequency throttling so I put only a critical trip in the example.
>
> Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
> ---
> V3: Add thermal-zones to the example
> V5: Extend example by including trips and cooling-maps. Update commit message
> ---
> .../devicetree/bindings/thermal/brcm,ns-thermal | 37 ++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/brcm,ns-thermal
Acked-by: Rob Herring <robh@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/2] power: tps65217_charger: Add properties like voltage and current charge
From: Rob Herring @ 2017-04-10 15:02 UTC (permalink / raw)
To: Enric Balletbo i Serra
Cc: Sebastian Reichel, Lee Jones, linux-pm, devicetree, linux-kernel
In-Reply-To: <20170403164446.28076-2-enric.balletbo@collabora.com>
On Mon, Apr 03, 2017 at 06:44:46PM +0200, Enric Balletbo i Serra wrote:
> Allow the possibility to configure the charge and the current voltage of
> the charger and also the NTC type for battery temperature measurement.
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
> .../bindings/power/supply/tps65217_charger.txt | 15 ++
> drivers/power/supply/tps65217_charger.c | 187 +++++++++++++++++++--
> include/linux/mfd/tps65217.h | 2 +
> 3 files changed, 192 insertions(+), 12 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt b/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
> index a11072c..600da6e 100644
> --- a/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
> +++ b/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
> @@ -6,6 +6,18 @@ Required Properties:
> Should be <0> for the USB charger and <1> for the AC adapter.
> -interrupt-names: Should be "USB" and "AC"
>
> +Optional properties:
> +-ti,charge-voltage-uvolt: set the charge voltage. The value can be: 4100000,
> + 4150000, 4200000, 4250000; default: 4100000
> +
> +-ti,charge-current-uamp: set the charging current. The value can be: 300000,
> + 400000, 500000, 700000; default: 500000
These should be standard properties. (Also, you are not using standard
unit suffixes. See bindings/property-units.txt.)
> +
> +-ti,ntc-type: set the NTC type for battery temperature measurement. The value
> + must be 0 or 1, where:
> + 0 – 100k (curve 1, B = 3960)
> + 1 – 10k (curve 2, B = 3480) (default)
> +
> This node is a subnode of the tps65217 PMIC.
>
> Example:
> @@ -14,4 +26,7 @@ Example:
> compatible = "ti,tps65217-charger";
> interrupts = <0>, <1>;
> interrupt-names = "USB", "AC";
> + ti,charge-voltage-uvolt = <4100000>;
> + ti,charge-current-uamp = <500000>;
> + ti,ntc-type = <1>;
> };
^ permalink raw reply
* Re: [PATCH v2] dt-bindings: arm,nvic: Binding for ARM NVIC interrupt controller on Cortex-M
From: Rob Herring @ 2017-04-10 15:04 UTC (permalink / raw)
To: Kumar Gala; +Cc: Device Tree Mailing List, Mark Rutland
In-Reply-To: <20170403175842.17289-1-kumar.gala-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
On Mon, Apr 03, 2017 at 12:58:42PM -0500, Kumar Gala wrote:
> Signed-off-by: Kumar Gala <kumar.gala-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> * Dropped arm,nvic, fixed up example to match
>
> .../bindings/interrupt-controller/arm,nvic.txt | 36 ++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/arm,nvic.txt
Applied, thanks.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 0/9] Fix issues and factorize arm/arm64 capacity information code
From: Russell King - ARM Linux @ 2017-04-10 15:05 UTC (permalink / raw)
To: Juri Lelli
Cc: mark.rutland, devicetree, lorenzo.pieralisi, vincent.guittot,
linux-pm, peterz, catalin.marinas, broonie, will.deacon,
linux-kernel, dietmar.eggemann, robh+dt, gregkh, sudeep.holla,
morten.rasmussen, linux-arm-kernel
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
On Mon, Mar 27, 2017 at 02:18:16PM +0100, Juri Lelli wrote:
> arm and arm64 topology.c share a lot of code related to parsing of capacity
> information. This is v3 of a solution [1] (based on Will's, Catalin's and
> Mark's off-line suggestions) to move such common code in a single place:
> drivers/base/arch_topology.c (by creating such file and conditionally compiling
> it for arm and arm64 only).
I think overall this is okay, with the exception of one patch which seems
to be wrongly worded. Once that's resolved, then:
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
for the series.
Thanks.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* Re: [PATCH 1/3] dt-bindings: power: supply: New bindings for LEGO MINDSTORMS EV3 battery
From: Rob Herring @ 2017-04-10 15:05 UTC (permalink / raw)
To: David Lechner
Cc: linux-pm, devicetree, linux-arm-kernel, Sebastian Reichel,
Mark Rutland, Sekhar Nori, Kevin Hilman, linux-kernel
In-Reply-To: <1491251029-23256-2-git-send-email-david@lechnology.com>
On Mon, Apr 03, 2017 at 03:23:47PM -0500, David Lechner wrote:
> This add a new device tree binding for LEGO MINDSTORMS EV3 battery. The EV3
> has some built-in capability for monitoring the attached battery.
>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
> .../bindings/power/supply/lego_ev3_battery.txt | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/supply/lego_ev3_battery.txt
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v7 00/13] mmc: Add support to Marvell Xenon SD Host Controller
From: Ulf Hansson @ 2017-04-10 15:14 UTC (permalink / raw)
To: Gregory CLEMENT
Cc: Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Jason Cooper, Andrew Lunn, Sebastian Hesselbarth,
Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Mike Turquette, Stephen Boyd, linux-clk,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Ziji Hu,
Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan
In-Reply-To: <cover.51071caa64e5df81264a91b9a81ed1e94f47d2c4.1490886907.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
On 30 March 2017 at 17:22, Gregory CLEMENT
<gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
> Hello,
>
> This the seventh version of the series adding support for the SDHCI
> Xenon controller. It can be currently found on the Armada 37xx and the
> Armada 7K/8K but will be also used in more Marvell SoC (and not only
> the mvebu ones actually).
>
> v6->v7:
> - Add comments on vqmmc and vmmc in examples in dt binding doc.
>
> - Fix all the issues pointed out by Ulf and Adrian:
>
> - Align the prefix of function and variable names.
> - Replace the if-else with switch statements when checking MMC_TIMING_*
> - Remove the spinlocks in set_ios.
> - Optimize the delay loop as Adrian patch does.
> - Add release of phy params structures
>
> - Add check of Vqmmc supply in Xenon signal voltage switch. If Vqmmc
> regulator doesn't exist, skip standard SD signal voltage regulator
> switch process.
>
> - Remove parse of child node mmc-card. Wait for a better solution.
>
> v5->v6:
>
> - Add a generic "mmc-card" parse in core layer.
>
> - Fix the spelling issues in Xenon dt binding doc and drivers.
>
> - Remove descriptions to common mmc properties from Xenon dt binding
> doc.
>
> - Split compatible string "marvell,armada-8k-sdhci" into
> "marvell,armada-ap806-sdhci" and "marvell,armada-cp110-sdhci".
>
> - Also updates the example in Xenon dt binding doc.
>
> - Remove unnecessary dependency on MMC_SDHCI from Xenon entry in
> Kconfig.
>
> - Move Xenon specific dt parse into a separate function.
>
> - Adjust warnings and condition check in Xenon PHY setting, to remove
> fragile hs200->hs400/hs400->hs200 sequence check function.
>
> - Enable PHY Slow Mode in MMC_TIMING_LEGACY timing if PHY Slow Mode is
> required in dts.
>
> - Add a patch allowing dts backwards compatible for the clock
>
> v4->v5:
>
> - Remove the patch to export sdhci_execute_tuning(). It is already
> exported in v4.10.
>
> - Introduce a patch adding a missing clock for the sdhci controller
> present on the CP master for A7K/A8K. There is no build dependency
> but obviously this patch is need to use the sdhci controller present
> on the CP part.
>
> - Adjust Xenon return setup, to avoid being overwritten by
> sdhci_add_host().
>
> - Change Xenon register definition prefix to "XENON_".
>
> - Fix typos in Xenon driver and dt-binding docs.
>
> - Change compatible string "marvell,armada-7000-sdhci" to
> "marvell,armada-8k-sdhci". Actually the Armada 7K SoCs are a subset
> of the Armada 8K SoCs. Moreover, the use of the '000' is not
> consistent with all the other compatible string already used for the
> Armada 7K/8K family.
>
> - Added the Tested-by from Russell King on an Armada 8K based board.
>
> v3 -> v4:
> For this version a few change have been done:
> - fixes 2 bug reported by kbuild-bot
> - remove extra of_node_put()
> - convert 0 in false for function returning boolean
>
> - add a device tree node for the sdhci controller present on the CP
> master for A7K/A8K. It also led to rename the sdhci0 node on AP to
> ap_sdhci0 to make a distinction with the one present on CP master.
>
> v2 -> v3
> I think that now most (if not all) the remarks had been taking into
> account since the second version. According to Ziji Hu, here are the
> following changes:
> " Changes in V3:
> Adjust and improve Xenon DT bindings. Move some caps setting from driver into
> DT. Use mmc-card sub-node to represent eMMC type.
> Remove PHY Sampling Fixed Delay Line scan in lower speed mode.
> Improve Xenon probe and ->init_card() functions.
> Export sdhci_enable_sdio_irq() and implement own SDIO IRQ control.
> Split PHY patch into two smaller patches.
> Temporarily remove AXI clock before its implementation is improved."
>
> Besides this changes I also
> - Removed the sdhci-xenon-phy.h and moved its content in the
> shc-xenon-phy.c file.
> - Fixed the tuning-count usage
> - Managed the error case for clk_prepare_enable
>
> For the record the change from v1 was:
> " Changes in V2:
> rebase on v4.9-rc2.
> Re-write Xenon bindings. Ajust Xenon DT property naming.
> Add a new DT property to indicate eMMC card type, instead of using
> variable card_candidate.
> Clear quirks SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 in Xenon platform data
> Add support to HS400 retuning."
>
> Thanks,
>
> Gregory
>
> Gregory CLEMENT (4):
> clk: apn806: Turn the eMMC clock as optional for dts backwards compatible
> arm64: dts: marvell: add eMMC support for Armada 37xx
> arm64: dts: marvell: add sdhci support for Armada 7K/8K
> arm64: configs: enable SDHCI driver for Xenon
>
> Hu Ziji (8):
> mmc: sdhci: Export sdhci_set_ios() from sdhci.c
> mmc: sdhci: Export sdhci_start_signal_voltage_switch() in sdhci.c
> mmc: sdhci: Export sdhci_enable_sdio_irq() from sdhci.c
> dt: bindings: Add bindings for Marvell Xenon SD Host Controller
> mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
> mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
> mmc: sdhci-xenon: Add SoC PHY PAD voltage control
> MAINTAINERS: add entry for Marvell Xenon MMC Host Controller drivers
>
> Konstantin Porotchkin (1):
> clk: apn806: Add eMMC clock to system controller driver
>
> Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt | 170 +-
> MAINTAINERS | 7 +-
> arch/arm64/boot/dts/marvell/armada-3720-db.dts | 9 +-
> arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 11 +-
> arch/arm64/boot/dts/marvell/armada-7040-db.dts | 14 +-
> arch/arm64/boot/dts/marvell/armada-8040-db.dts | 12 +-
> arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 14 +-
> arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 11 +-
> arch/arm64/configs/defconfig | 1 +-
> drivers/clk/mvebu/ap806-system-controller.c | 21 +-
> drivers/mmc/host/Kconfig | 8 +-
> drivers/mmc/host/Makefile | 3 +-
> drivers/mmc/host/sdhci-xenon-phy.c | 835 +++++++-
> drivers/mmc/host/sdhci-xenon.c | 548 +++++-
> drivers/mmc/host/sdhci-xenon.h | 101 +-
> drivers/mmc/host/sdhci.c | 11 +-
> drivers/mmc/host/sdhci.h | 4 +-
> 17 files changed, 1774 insertions(+), 6 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt
> create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
> create mode 100644 drivers/mmc/host/sdhci-xenon.c
> create mode 100644 drivers/mmc/host/sdhci-xenon.h
>
> base-commit: a645cc1df4ff41ba54a2fb839962b8ff142121d9
> --
> git-series 0.9.1
Thanks, applied patch 1->10 for next! 11->13 will have to go via the
SoC maintainer tree.
Kind regards
Uffe
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] mmc: core: add mmc-card hardware reset enable support
From: Rob Herring @ 2017-04-10 15:17 UTC (permalink / raw)
To: Richard Leitner
Cc: Jaehoon Chung, ulf.hansson, mark.rutland, shawn.lin,
adrian.hunter, linus.walleij, linux-mmc, linux-kernel, devicetree,
dev
In-Reply-To: <7b238b63-9d23-91ef-b76f-82b4f3677de3@skidata.com>
On Wed, Apr 05, 2017 at 08:23:29AM +0200, Richard Leitner wrote:
> On 04/05/2017 06:40 AM, Jaehoon Chung wrote:
> > Hi,
> >
> > On 04/04/2017 11:16 PM, Richard Leitner wrote:
> >> Some eMMCs disable their hardware reset line (RST_N) by default. To enable
> >> it the host must set the corresponding bit in ECSD. An example for such
> >> a device is the Micron MTFCxGACAANA-4M.
> >>
> >> This patch adds a new mmc-card devicetree property to let the host enable
> >> this feature during card initialization.
> >>
> >> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
> >> ---
> >> Documentation/devicetree/bindings/mmc/mmc-card.txt | 3 +++
> >> drivers/mmc/core/mmc.c | 21 +++++++++++++++++++++
> >> 2 files changed, 24 insertions(+)
> >>
> >> diff --git a/Documentation/devicetree/bindings/mmc/mmc-card.txt b/Documentation/devicetree/bindings/mmc/mmc-card.txt
> >> index a70fcd6..8590a40 100644
> >> --- a/Documentation/devicetree/bindings/mmc/mmc-card.txt
> >> +++ b/Documentation/devicetree/bindings/mmc/mmc-card.txt
> >> @@ -12,6 +12,9 @@ Required properties:
> >> Optional properties:
> >> -broken-hpi : Use this to indicate that the mmc-card has a broken hpi
> >> implementation, and that hpi should not be used
> >> +-enable-hw-reset : some eMMC devices have disabled the hw reset functionality
> >> + (RST_N_FUNCTION) by default. By adding this property the
> >> + host will enable it during initialization.
> >
> > As i know, RST_N_FUNCTION is controlled bit[1:0]
> > 0x0 : RST_n disabled (by default)
> > 0x1 : permanently enabled
> > 0x2 : permanently disabled
> >
> > I think that it needs to add the description about these..
>
> Ok.
>
> >>
> >> Example:
> >>
> >> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> >> index b502601..518d0e3 100644
> >> --- a/drivers/mmc/core/mmc.c
> >> +++ b/drivers/mmc/core/mmc.c
> >> @@ -1520,9 +1520,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> >> int err;
> >> u32 cid[4];
> >> u32 rocr;
> >> + struct device_node *np;
> >> + bool enable_rst_n = false;
> >>
> >> WARN_ON(!host->claimed);
> >>
> >> + np = mmc_of_find_child_device(host, 0);
> >> + if (np && of_device_is_compatible(np, "mmc-card"))
> >> + enable_rst_n = of_property_read_bool(np, "enable-hw-reset");
> >> + of_node_put(np);
> >> +
> >> /* Set correct bus mode for MMC before attempting init */
> >> if (!mmc_host_is_spi(host))
> >> mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
> >> @@ -1810,6 +1817,20 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> >> }
> >> }
> >>
> >> + /*
> >> + * try to enable RST_N if requested
> >> + * This is needed because some eMMC chips disable this function by
> >> + * default.
> >> + */
> >> + if (enable_rst_n) {
> >> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> + EXT_CSD_RST_N_FUNCTION, EXT_CSD_RST_N_ENABLED,
> >> + card->ext_csd.generic_cmd6_time);
> >> + if (err && err != -EBADMSG)
> >> + pr_warn("%s: Enabling RST_N feature failed\n",
> >> + mmc_hostname(card->host));
> >> + }
> >
> > If enabled hw-reset, it doesn't need to re-enable this bit.
>
> Ok. I can add a check to prevent setting it, if it is set already.
>
> > i didn't check the mmc-util..
> > If mmc-util provides the changing this, the using mmc-util is better than this.
>
> mmc-utils is providing a enable/disable hwreset feature. But as this
> setting is required for my hardware to allow rebooting it, I thought it
> would be better if it's in the kernel. So I/the hw doesn't have to
> depend on userspace tools.
Doesn't really seem like something you'd want a user to have to care
about, so in DT seems fine to me.
Rob
^ permalink raw reply
* [PATCH v3 0/2] Add STM32 SAI support
From: olivier moysan @ 2017-04-10 15:19 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
This patch-set handles the Serial Audio Interface (SAI) IP on STM32 platforms.
The SAI IP is composed of two Sub-block A and B. (see the figure below)
Each sub-block makes use of individual and shared resources.
Shared resources:
- Reset line. Reset common and sub-block registers.
- Bus interface clocks. This clock is not exposed in device
as it it managed by clock driver, according to kernel clocks gating.
- Common register. SAI IP exhibits a common configuration register
to manage synchronization modes. NB: These modes are not yet implemented
in this version of the SAI driver.
- Interrupt. Sub-blocks have their own interrupt status registers but they
share the same interrupt line.
Individual resources:
- Register set
- DMA request line.
- Communication interface.
Each sub-block has its own GPIOs and associated bus lines.
- Kernel clock.
Each sub-block has its own dedicated clock for its communication interface.
To reflect this architecture Sub-block A and B are handled by 2 child devices.
Sub-block A and B devices can be configured independently either as transmitter
or receiver. A PCM device is associated to each sub-block.
A sub-block has to select the appropriated parent clock at runtime,
depending on the audio stream sampling rate to be played or captured.
Two parent clocks must be provided to support sampling rates multiples
of 8 kHz or 11.025kHz.
Interface clock +--------------------------------+
+---------------> | SAI IP |
Reset | +-----------------+ |
+---------------> | |common registers | |
| +-----------------+ |
| |
| +----------------------+ |
| | Sub-block A | |
Kernel clock A | | | | Bus A
+--------------------> | +----------------+ +---------------->
| | |A registers | | |
| | +----------------+ | |
| +----------------------+ |
| |
| +----------------------+ |
| | Sub-block B | |
Kernel clock B | | | | Bus B
+--------------------> | +----------------+ +----------------->
| | |B registers | | |
| | +----------------+ | |
| +----------------------+ |
| |
+--------------------------------+
v3: correct DT bindings
- remove aliases
- use audio graph card
olivier moysan (2):
ASoC: stm32: add bindings for SAI
ASoC: stm32: add SAI driver
.../devicetree/bindings/sound/st,stm32-sai.txt | 89 +++
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/stm/Kconfig | 8 +
sound/soc/stm/Makefile | 6 +
sound/soc/stm/stm32_sai.c | 115 +++
sound/soc/stm/stm32_sai.h | 200 +++++
sound/soc/stm/stm32_sai_sub.c | 884 +++++++++++++++++++++
8 files changed, 1304 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-sai.txt
create mode 100644 sound/soc/stm/Kconfig
create mode 100644 sound/soc/stm/Makefile
create mode 100644 sound/soc/stm/stm32_sai.c
create mode 100644 sound/soc/stm/stm32_sai.h
create mode 100644 sound/soc/stm/stm32_sai_sub.c
--
1.9.1
^ permalink raw reply
* [PATCH v3 1/2] ASoC: stm32: add bindings for SAI
From: olivier moysan @ 2017-04-10 15:19 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
In-Reply-To: <1491837596-2924-1-git-send-email-olivier.moysan@st.com>
This patch adds documentation of device tree bindings for the
STM32 SAI ASoC driver.
Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
.../devicetree/bindings/sound/st,stm32-sai.txt | 89 ++++++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-sai.txt
diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt
new file mode 100644
index 0000000..c59a3d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt
@@ -0,0 +1,89 @@
+STMicroelectronics STM32 Serial Audio Interface (SAI).
+
+The SAI interface (Serial Audio Interface) offers a wide set of audio protocols
+as I2S standards, LSB or MSB-justified, PCM/DSP, TDM, and AC'97.
+The SAI contains two independent audio sub-blocks. Each sub-block has
+its own clock generator and I/O lines controller.
+
+Required properties:
+ - compatible: Should be "st,stm32f4-sai"
+ - reg: Base address and size of SAI common register set.
+ - clocks: Must contain phandle and clock specifier pairs for each entry
+ in clock-names.
+ - clock-names: Must contain "x8k" and "x11k"
+ "x8k": SAI parent clock for sampling rates multiple of 8kHz.
+ "x11k": SAI parent clock for sampling rates multiple of 11.025kHz.
+ - interrupts: cpu DAI interrupt line shared by SAI sub-blocks
+
+Optional properties:
+ - resets: Reference to a reset controller asserting the SAI
+
+SAI subnodes:
+Two subnodes corresponding to SAI sub-block instances A et B can be defined.
+Subnode can be omitted for unsused sub-block.
+
+SAI subnodes required properties:
+ - compatible: Should be "st,stm32-sai-sub-a" or "st,stm32-sai-sub-b"
+ for SAI sub-block A or B respectively.
+ - reg: Base address and size of SAI sub-block register set.
+ - clocks: Must contain one phandle and clock specifier pair
+ for sai_ck which feeds the internal clock generator.
+ - clock-names: Must contain "sai_ck".
+ - dmas: see Documentation/devicetree/bindings/dma/stm32-dma.txt
+ - dma-names: identifier string for each DMA request line
+ "tx": if sai sub-block is configured as playback DAI
+ "rx": if sai sub-block is configured as capture DAI
+ - pinctrl-names: should contain only value "default"
+ - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+
+Example:
+sound_card {
+ compatible = "audio-graph-card";
+ dais = <&sai1b_port>;
+};
+
+sai1: sai1@40015800 {
+ compatible = "st,stm32f4-sai";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0x40015800 0x4>;
+ clocks = <&rcc 1 CLK_SAIQ_PDIV>, <&rcc 1 CLK_I2SQ_PDIV>;
+ clock-names = "x8k", "x11k";
+ interrupts = <87>;
+
+ sai1b: audio-controller@40015824 {
+ #sound-dai-cells = <0>;
+ compatible = "st,stm32-sai-sub-b";
+ reg = <0x40015824 0x1C>;
+ clocks = <&rcc 1 CLK_SAI2>;
+ clock-names = "sai_ck";
+ dmas = <&dma2 5 0 0x400 0x0>;
+ dma-names = "tx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sai1b>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sai1b_port: port@0 {
+ reg = <0>;
+ cpu_endpoint: endpoint {
+ remote-endpoint = <&codec_endpoint>;
+ audio-graph-card,format = "i2s";
+ audio-graph-card,bitclock-master = <&codec_endpoint>;
+ audio-graph-card,frame-master = <&codec_endpoint>;
+ };
+ };
+ };
+ };
+};
+
+audio-codec {
+ codec_port: port {
+ codec_endpoint: endpoint {
+ remote-endpoint = <&cpu_endpoint>;
+ };
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/2] ASoC: stm32: add SAI driver
From: olivier moysan @ 2017-04-10 15:19 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
In-Reply-To: <1491837596-2924-1-git-send-email-olivier.moysan@st.com>
This patch implements SAI ASoC driver for STM32.
Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/stm/Kconfig | 8 +
sound/soc/stm/Makefile | 6 +
sound/soc/stm/stm32_sai.c | 115 ++++++
sound/soc/stm/stm32_sai.h | 200 ++++++++++
sound/soc/stm/stm32_sai_sub.c | 884 ++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1215 insertions(+)
create mode 100644 sound/soc/stm/Kconfig
create mode 100644 sound/soc/stm/Makefile
create mode 100644 sound/soc/stm/stm32_sai.c
create mode 100644 sound/soc/stm/stm32_sai.h
create mode 100644 sound/soc/stm/stm32_sai_sub.c
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92e..3836ebe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -63,6 +63,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21..5440cf7 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 0000000..972970f
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+ tristate "STMicroelectronics STM32 SOC audio support"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on SND_SOC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y if you want to enable ASoC-support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..e466a47
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,6 @@
+# SAI
+snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
+
+snd-soc-stm32-sai-objs := stm32_sai.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
new file mode 100644
index 0000000..2a27a26
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.c
@@ -0,0 +1,115 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+
+#include "stm32_sai.h"
+
+static const struct of_device_id stm32_sai_ids[] = {
+ { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
+ {}
+};
+
+static int stm32_sai_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct stm32_sai_data *sai;
+ struct reset_control *rst;
+ struct resource *res;
+ void __iomem *base;
+ const struct of_device_id *of_id;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ of_id = of_match_device(stm32_sai_ids, &pdev->dev);
+ if (of_id)
+ sai->version = (enum stm32_sai_version)of_id->data;
+ else
+ return -EINVAL;
+
+ sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
+ if (IS_ERR(sai->clk_x8k)) {
+ dev_err(&pdev->dev, "missing x8k parent clock\n");
+ return PTR_ERR(sai->clk_x8k);
+ }
+
+ sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
+ if (IS_ERR(sai->clk_x11k)) {
+ dev_err(&pdev->dev, "missing x11k parent clock\n");
+ return PTR_ERR(sai->clk_x11k);
+ }
+
+ /* init irqs */
+ sai->irq = platform_get_irq(pdev, 0);
+ if (sai->irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ return sai->irq;
+ }
+
+ /* reset */
+ rst = reset_control_get(&pdev->dev, NULL);
+ if (!IS_ERR(rst)) {
+ reset_control_assert(rst);
+ udelay(2);
+ reset_control_deassert(rst);
+ }
+
+ sai->pdev = pdev;
+ platform_set_drvdata(pdev, sai);
+
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static int stm32_sai_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_sai_ids);
+
+static struct platform_driver stm32_sai_driver = {
+ .driver = {
+ .name = "st,stm32-sai",
+ .of_match_table = stm32_sai_ids,
+ },
+ .probe = stm32_sai_probe,
+ .remove = stm32_sai_remove,
+};
+
+module_platform_driver(stm32_sai_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
new file mode 100644
index 0000000..a801fda
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.h
@@ -0,0 +1,200 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+/******************** SAI Register Map **************************************/
+
+/* common register */
+#define STM_SAI_GCR 0x00
+
+/* Sub-block A&B registers offsets, relative to A&B sub-block addresses */
+#define STM_SAI_CR1_REGX 0x00 /* A offset: 0x04. B offset: 0x24 */
+#define STM_SAI_CR2_REGX 0x04
+#define STM_SAI_FRCR_REGX 0x08
+#define STM_SAI_SLOTR_REGX 0x0C
+#define STM_SAI_IMR_REGX 0x10
+#define STM_SAI_SR_REGX 0x14
+#define STM_SAI_CLRFR_REGX 0x18
+#define STM_SAI_DR_REGX 0x1C
+
+/******************** Bit definition for SAI_GCR register *******************/
+#define SAI_GCR_SYNCIN_SHIFT 0
+#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
+#define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT)
+
+#define SAI_GCR_SYNCOUT_SHIFT 4
+#define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT)
+#define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT)
+
+/******************* Bit definition for SAI_XCR1 register *******************/
+#define SAI_XCR1_RX_TX_SHIFT 0
+#define SAI_XCR1_RX_TX BIT(SAI_XCR1_RX_TX_SHIFT)
+#define SAI_XCR1_SLAVE_SHIFT 1
+#define SAI_XCR1_SLAVE BIT(SAI_XCR1_SLAVE_SHIFT)
+
+#define SAI_XCR1_PRTCFG_SHIFT 2
+#define SAI_XCR1_PRTCFG_MASK GENMASK(3, SAI_XCR1_PRTCFG_SHIFT)
+#define SAI_XCR1_PRTCFG_SET(x) ((x) << SAI_XCR1_PRTCFG_SHIFT)
+
+#define SAI_XCR1_DS_SHIFT 5
+#define SAI_XCR1_DS_MASK GENMASK(7, SAI_XCR1_DS_SHIFT)
+#define SAI_XCR1_DS_SET(x) ((x) << SAI_XCR1_DS_SHIFT)
+
+#define SAI_XCR1_LSBFIRST_SHIFT 8
+#define SAI_XCR1_LSBFIRST BIT(SAI_XCR1_LSBFIRST_SHIFT)
+#define SAI_XCR1_CKSTR_SHIFT 9
+#define SAI_XCR1_CKSTR BIT(SAI_XCR1_CKSTR_SHIFT)
+
+#define SAI_XCR1_SYNCEN_SHIFT 10
+#define SAI_XCR1_SYNCEN_MASK GENMASK(11, SAI_XCR1_SYNCEN_SHIFT)
+#define SAI_XCR1_SYNCEN_SET(x) ((x) << SAI_XCR1_SYNCEN_SHIFT)
+
+#define SAI_XCR1_MONO_SHIFT 12
+#define SAI_XCR1_MONO BIT(SAI_XCR1_MONO_SHIFT)
+#define SAI_XCR1_OUTDRIV_SHIFT 13
+#define SAI_XCR1_OUTDRIV BIT(SAI_XCR1_OUTDRIV_SHIFT)
+#define SAI_XCR1_SAIEN_SHIFT 16
+#define SAI_XCR1_SAIEN BIT(SAI_XCR1_SAIEN_SHIFT)
+#define SAI_XCR1_DMAEN_SHIFT 17
+#define SAI_XCR1_DMAEN BIT(SAI_XCR1_DMAEN_SHIFT)
+#define SAI_XCR1_NODIV_SHIFT 19
+#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
+
+#define SAI_XCR1_MCKDIV_SHIFT 20
+#define SAI_XCR1_MCKDIV_WIDTH 4
+#define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
+
+#define SAI_XCR1_OSR_SHIFT 26
+#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
+
+/******************* Bit definition for SAI_XCR2 register *******************/
+#define SAI_XCR2_FTH_SHIFT 0
+#define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT)
+#define SAI_XCR2_FTH_SET(x) ((x) << SAI_XCR2_FTH_SHIFT)
+
+#define SAI_XCR2_FFLUSH_SHIFT 3
+#define SAI_XCR2_FFLUSH BIT(SAI_XCR2_FFLUSH_SHIFT)
+#define SAI_XCR2_TRIS_SHIFT 4
+#define SAI_XCR2_TRIS BIT(SAI_XCR2_TRIS_SHIFT)
+#define SAI_XCR2_MUTE_SHIFT 5
+#define SAI_XCR2_MUTE BIT(SAI_XCR2_MUTE_SHIFT)
+#define SAI_XCR2_MUTEVAL_SHIFT 6
+#define SAI_XCR2_MUTEVAL BIT(SAI_XCR2_MUTEVAL_SHIFT)
+
+#define SAI_XCR2_MUTECNT_SHIFT 7
+#define SAI_XCR2_MUTECNT_MASK GENMASK(12, SAI_XCR2_MUTECNT_SHIFT)
+#define SAI_XCR2_MUTECNT_SET(x) ((x) << SAI_XCR2_MUTECNT_SHIFT)
+
+#define SAI_XCR2_CPL_SHIFT 13
+#define SAI_XCR2_CPL BIT(SAI_XCR2_CPL_SHIFT)
+
+#define SAI_XCR2_COMP_SHIFT 14
+#define SAI_XCR2_COMP_MASK GENMASK(15, SAI_XCR2_COMP_SHIFT)
+#define SAI_XCR2_COMP_SET(x) ((x) << SAI_XCR2_COMP_SHIFT)
+
+/****************** Bit definition for SAI_XFRCR register *******************/
+#define SAI_XFRCR_FRL_SHIFT 0
+#define SAI_XFRCR_FRL_MASK GENMASK(7, SAI_XFRCR_FRL_SHIFT)
+#define SAI_XFRCR_FRL_SET(x) ((x) << SAI_XFRCR_FRL_SHIFT)
+
+#define SAI_XFRCR_FSALL_SHIFT 8
+#define SAI_XFRCR_FSALL_MASK GENMASK(14, SAI_XFRCR_FSALL_SHIFT)
+#define SAI_XFRCR_FSALL_SET(x) ((x) << SAI_XFRCR_FSALL_SHIFT)
+
+#define SAI_XFRCR_FSDEF_SHIFT 16
+#define SAI_XFRCR_FSDEF BIT(SAI_XFRCR_FSDEF_SHIFT)
+#define SAI_XFRCR_FSPOL_SHIFT 17
+#define SAI_XFRCR_FSPOL BIT(SAI_XFRCR_FSPOL_SHIFT)
+#define SAI_XFRCR_FSOFF_SHIFT 18
+#define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT)
+
+/****************** Bit definition for SAI_XSLOTR register ******************/
+
+#define SAI_XSLOTR_FBOFF_SHIFT 0
+#define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
+#define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT)
+
+#define SAI_XSLOTR_SLOTSZ_SHIFT 6
+#define SAI_XSLOTR_SLOTSZ_MASK GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT)
+#define SAI_XSLOTR_SLOTSZ_SET(x) ((x) << SAI_XSLOTR_SLOTSZ_SHIFT)
+
+#define SAI_XSLOTR_NBSLOT_SHIFT 8
+#define SAI_XSLOTR_NBSLOT_MASK GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT)
+#define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT)
+
+#define SAI_XSLOTR_SLOTEN_SHIFT 16
+#define SAI_XSLOTR_SLOTEN_WIDTH 16
+#define SAI_XSLOTR_SLOTEN_MASK GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT)
+#define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT)
+
+/******************* Bit definition for SAI_XIMR register *******************/
+#define SAI_XIMR_OVRUDRIE BIT(0)
+#define SAI_XIMR_MUTEDETIE BIT(1)
+#define SAI_XIMR_WCKCFGIE BIT(2)
+#define SAI_XIMR_FREQIE BIT(3)
+#define SAI_XIMR_CNRDYIE BIT(4)
+#define SAI_XIMR_AFSDETIE BIT(5)
+#define SAI_XIMR_LFSDETIE BIT(6)
+
+#define SAI_XIMR_SHIFT 0
+#define SAI_XIMR_MASK GENMASK(6, SAI_XIMR_SHIFT)
+
+/******************** Bit definition for SAI_XSR register *******************/
+#define SAI_XSR_OVRUDR BIT(0)
+#define SAI_XSR_MUTEDET BIT(1)
+#define SAI_XSR_WCKCFG BIT(2)
+#define SAI_XSR_FREQ BIT(3)
+#define SAI_XSR_CNRDY BIT(4)
+#define SAI_XSR_AFSDET BIT(5)
+#define SAI_XSR_LFSDET BIT(6)
+
+#define SAI_XSR_SHIFT 0
+#define SAI_XSR_MASK GENMASK(6, SAI_XSR_SHIFT)
+
+/****************** Bit definition for SAI_XCLRFR register ******************/
+#define SAI_XCLRFR_COVRUDR BIT(0)
+#define SAI_XCLRFR_CMUTEDET BIT(1)
+#define SAI_XCLRFR_CWCKCFG BIT(2)
+#define SAI_XCLRFR_CFREQ BIT(3)
+#define SAI_XCLRFR_CCNRDY BIT(4)
+#define SAI_XCLRFR_CAFSDET BIT(5)
+#define SAI_XCLRFR_CLFSDET BIT(6)
+
+#define SAI_XCLRFR_SHIFT 0
+#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
+
+enum stm32_sai_version {
+ SAI_STM32F4
+};
+
+/**
+ * struct stm32_sai_data - private data of SAI instance driver
+ * @pdev: device data pointer
+ * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz
+ * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz
+ * @version: SOC version
+ * @irq: SAI interrupt line
+ */
+struct stm32_sai_data {
+ struct platform_device *pdev;
+ struct clk *clk_x8k;
+ struct clk *clk_x11k;
+ int version;
+ int irq;
+};
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
new file mode 100644
index 0000000..ae4706c
--- /dev/null
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -0,0 +1,884 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "stm32_sai.h"
+
+#define SAI_FREE_PROTOCOL 0x0
+
+#define SAI_SLOT_SIZE_AUTO 0x0
+#define SAI_SLOT_SIZE_16 0x1
+#define SAI_SLOT_SIZE_32 0x2
+
+#define SAI_DATASIZE_8 0x2
+#define SAI_DATASIZE_10 0x3
+#define SAI_DATASIZE_16 0x4
+#define SAI_DATASIZE_20 0x5
+#define SAI_DATASIZE_24 0x6
+#define SAI_DATASIZE_32 0x7
+
+#define STM_SAI_FIFO_SIZE 8
+#define STM_SAI_DAI_NAME_SIZE 15
+
+#define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK)
+#define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE)
+
+#define STM_SAI_A_ID 0x0
+#define STM_SAI_B_ID 0x1
+
+#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B")
+
+/**
+ * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
+ * @pdev: device data pointer
+ * @regmap: SAI register map pointer
+ * @dma_params: dma configuration data for rx or tx channel
+ * @cpu_dai_drv: DAI driver data pointer
+ * @cpu_dai: DAI runtime data pointer
+ * @substream: PCM substream data pointer
+ * @pdata: SAI block parent data pointer
+ * @sai_ck: kernel clock feeding the SAI clock generator
+ * @phys_addr: SAI registers physical base address
+ * @mclk_rate: SAI block master clock frequency (Hz). set at init
+ * @id: SAI sub block id corresponding to sub-block A or B
+ * @dir: SAI block direction (playback or capture). set at init
+ * @master: SAI block mode flag. (true=master, false=slave) set at init
+ * @fmt: SAI block format. relevant only for custom protocols. set at init
+ * @sync: SAI block synchronization mode. (none, internal or external)
+ * @fs_length: frame synchronization length. depends on protocol settings
+ * @slots: rx or tx slot number
+ * @slot_width: rx or tx slot width in bits
+ * @slot_mask: rx or tx active slots mask. set at init or at runtime
+ * @data_size: PCM data width. corresponds to PCM substream width.
+ */
+struct stm32_sai_sub_data {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct snd_dmaengine_dai_dma_data dma_params;
+ struct snd_soc_dai_driver *cpu_dai_drv;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_pcm_substream *substream;
+ struct stm32_sai_data *pdata;
+ struct clk *sai_ck;
+ dma_addr_t phys_addr;
+ unsigned int mclk_rate;
+ unsigned int id;
+ int dir;
+ bool master;
+ int fmt;
+ int sync;
+ int fs_length;
+ int slots;
+ int slot_width;
+ int slot_mask;
+ int data_size;
+};
+
+enum stm32_sai_fifo_th {
+ STM_SAI_FIFO_TH_EMPTY,
+ STM_SAI_FIFO_TH_QUARTER,
+ STM_SAI_FIFO_TH_HALF,
+ STM_SAI_FIFO_TH_3_QUARTER,
+ STM_SAI_FIFO_TH_FULL,
+};
+
+static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_CR1_REGX:
+ case STM_SAI_CR2_REGX:
+ case STM_SAI_FRCR_REGX:
+ case STM_SAI_SLOTR_REGX:
+ case STM_SAI_IMR_REGX:
+ case STM_SAI_SR_REGX:
+ case STM_SAI_CLRFR_REGX:
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_CR1_REGX:
+ case STM_SAI_CR2_REGX:
+ case STM_SAI_FRCR_REGX:
+ case STM_SAI_SLOTR_REGX:
+ case STM_SAI_IMR_REGX:
+ case STM_SAI_SR_REGX:
+ case STM_SAI_CLRFR_REGX:
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config stm32_sai_sub_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = STM_SAI_DR_REGX,
+ .readable_reg = stm32_sai_sub_readable_reg,
+ .volatile_reg = stm32_sai_sub_volatile_reg,
+ .writeable_reg = stm32_sai_sub_writeable_reg,
+ .fast_io = true,
+};
+
+static irqreturn_t stm32_sai_isr(int irq, void *devid)
+{
+ struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
+ struct snd_pcm_substream *substream = sai->substream;
+ struct platform_device *pdev = sai->pdev;
+ unsigned int sr, imr, flags;
+ snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING;
+
+ regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr);
+ regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr);
+
+ flags = sr & imr;
+ if (!flags)
+ return IRQ_NONE;
+
+ regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+ SAI_XCLRFR_MASK);
+
+ if (flags & SAI_XIMR_OVRUDRIE) {
+ dev_err(&pdev->dev, "IT %s\n",
+ STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (flags & SAI_XIMR_MUTEDETIE)
+ dev_dbg(&pdev->dev, "IT mute detected\n");
+
+ if (flags & SAI_XIMR_WCKCFGIE) {
+ dev_err(&pdev->dev, "IT wrong clock configuration\n");
+ status = SNDRV_PCM_STATE_DISCONNECTED;
+ }
+
+ if (flags & SAI_XIMR_CNRDYIE)
+ dev_warn(&pdev->dev, "IT Codec not ready\n");
+
+ if (flags & SAI_XIMR_AFSDETIE) {
+ dev_warn(&pdev->dev, "IT Anticipated frame synchro\n");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (flags & SAI_XIMR_LFSDETIE) {
+ dev_warn(&pdev->dev, "IT Late frame synchro\n");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (status != SNDRV_PCM_STATE_RUNNING) {
+ snd_pcm_stream_lock(substream);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
+ sai->mclk_rate = freq;
+ dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
+ }
+
+ return 0;
+}
+
+static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int slotr, slotr_mask, slot_size;
+
+ dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
+ tx_mask, rx_mask, slots, slot_width);
+
+ switch (slot_width) {
+ case 16:
+ slot_size = SAI_SLOT_SIZE_16;
+ break;
+ case 32:
+ slot_size = SAI_SLOT_SIZE_32;
+ break;
+ default:
+ slot_size = SAI_SLOT_SIZE_AUTO;
+ break;
+ }
+
+ slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) |
+ SAI_XSLOTR_NBSLOT_SET(slots - 1);
+ slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK;
+
+ /* tx/rx mask set in machine init, if slot number defined in DT */
+ if (STM_SAI_IS_PLAYBACK(sai)) {
+ sai->slot_mask = tx_mask;
+ slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask);
+ }
+
+ if (STM_SAI_IS_CAPTURE(sai)) {
+ sai->slot_mask = rx_mask;
+ slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask);
+ }
+
+ slotr_mask |= SAI_XSLOTR_SLOTEN_MASK;
+
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
+
+ sai->slot_width = slot_width;
+ sai->slots = slots;
+
+ return 0;
+}
+
+static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1 = 0, frcr = 0;
+ int cr1_mask = 0, frcr_mask = 0;
+ int ret;
+
+ dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ /* SCK active high for all protocols */
+ case SND_SOC_DAIFMT_I2S:
+ cr1 |= SAI_XCR1_CKSTR;
+ frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF;
+ break;
+ /* Left justified */
+ case SND_SOC_DAIFMT_MSB:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+ break;
+ /* Right justified */
+ case SND_SOC_DAIFMT_LSB:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ frcr |= SAI_XFRCR_FSPOL;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR;
+ frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF |
+ SAI_XFRCR_FSDEF;
+
+ /* DAI clock strobing. Invert setting previously set */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cr1 ^= SAI_XCR1_CKSTR;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ frcr ^= SAI_XFRCR_FSPOL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert fs & sck */
+ cr1 ^= SAI_XCR1_CKSTR;
+ frcr ^= SAI_XFRCR_FSPOL;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+ cr1_mask |= SAI_XCR1_CKSTR;
+ frcr_mask |= SAI_XFRCR_FSPOL;
+
+ regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is master */
+ cr1 |= SAI_XCR1_SLAVE;
+ sai->master = false;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ sai->master = true;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+ cr1_mask |= SAI_XCR1_SLAVE;
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ sai->fmt = fmt;
+
+ return 0;
+}
+
+static int stm32_sai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int imr, cr2, ret;
+
+ sai->substream = substream;
+
+ ret = clk_prepare_enable(sai->sai_ck);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable ITs */
+ regmap_update_bits(sai->regmap, STM_SAI_SR_REGX,
+ SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK);
+
+ regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+ SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+
+ imr = SAI_XIMR_OVRUDRIE;
+ if (STM_SAI_IS_CAPTURE(sai)) {
+ regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2);
+ if (cr2 & SAI_XCR2_MUTECNT_MASK)
+ imr |= SAI_XIMR_MUTEDETIE;
+ }
+
+ if (sai->master)
+ imr |= SAI_XIMR_WCKCFGIE;
+ else
+ imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE;
+
+ regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
+ SAI_XIMR_MASK, imr);
+
+ return 0;
+}
+
+static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1, cr1_mask, ret;
+ int fth = STM_SAI_FIFO_TH_HALF;
+
+ /* FIFO config */
+ regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth));
+
+ /* Mode, data format and channel config */
+ cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Data format not supported");
+ return -EINVAL;
+ }
+ cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK;
+
+ cr1_mask |= SAI_XCR1_RX_TX;
+ if (STM_SAI_IS_CAPTURE(sai))
+ cr1 |= SAI_XCR1_RX_TX;
+
+ cr1_mask |= SAI_XCR1_MONO;
+ if ((sai->slots == 2) && (params_channels(params) == 1))
+ cr1 |= SAI_XCR1_MONO;
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ /* DMA config */
+ sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32);
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params);
+
+ return 0;
+}
+
+static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int slotr, slot_sz;
+
+ regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr);
+
+ /*
+ * If SLOTSZ is set to auto in SLOTR, align slot width on data size
+ * By default slot width = data size, if not forced from DT
+ */
+ slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK;
+ if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO))
+ sai->slot_width = sai->data_size;
+
+ if (sai->slot_width < sai->data_size) {
+ dev_err(cpu_dai->dev,
+ "Data size %d larger than slot width\n",
+ sai->data_size);
+ return -EINVAL;
+ }
+
+ /* Slot number is set to 2, if not specified in DT */
+ if (!sai->slots)
+ sai->slots = 2;
+
+ /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_NBSLOT_MASK,
+ SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
+
+ /* Set default slots mask if not already set from DT */
+ if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) {
+ sai->slot_mask = (1 << sai->slots) - 1;
+ regmap_update_bits(sai->regmap,
+ STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
+ SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
+ }
+
+ dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n",
+ sai->slots, sai->slot_width);
+
+ return 0;
+}
+
+static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int fs_active, offset, format;
+ int frcr, frcr_mask;
+
+ format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ sai->fs_length = sai->slot_width * sai->slots;
+
+ fs_active = sai->fs_length / 2;
+ if ((format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B))
+ fs_active = 1;
+
+ frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1));
+ frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1));
+ frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK;
+
+ dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n",
+ sai->fs_length, fs_active);
+
+ regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+ if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) {
+ offset = sai->slot_width - sai->data_size;
+
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_FBOFF_MASK,
+ SAI_XSLOTR_FBOFF_SET(offset));
+ }
+}
+
+static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1, mask, div = 0;
+ int sai_clk_rate, ret;
+
+ if (!sai->mclk_rate) {
+ dev_err(cpu_dai->dev, "Mclk rate is null\n");
+ return -EINVAL;
+ }
+
+ if (!(params_rate(params) % 11025))
+ clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
+ else
+ clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
+ sai_clk_rate = clk_get_rate(sai->sai_ck);
+
+ /*
+ * mclk_rate = 256 * fs
+ * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+ * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+ */
+ if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
+ div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
+
+ if (div > SAI_XCR1_MCKDIV_MAX) {
+ dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
+ return -EINVAL;
+ }
+ dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
+
+ mask = SAI_XCR1_MCKDIV_MASK;
+ cr1 = SAI_XCR1_MCKDIV_SET(div);
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ sai->data_size = params_width(params);
+
+ ret = stm32_sai_set_slots(cpu_dai);
+ if (ret < 0)
+ return ret;
+ stm32_sai_set_frame(cpu_dai);
+
+ ret = stm32_sai_set_config(cpu_dai, substream, params);
+ if (ret)
+ return ret;
+
+ if (sai->master)
+ ret = stm32_sai_configure_clock(cpu_dai, params);
+
+ return ret;
+}
+
+static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n");
+
+ regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
+
+ /* Enable SAI */
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
+ if (ret < 0)
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
+
+ regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN,
+ (unsigned int)~SAI_XCR1_DMAEN);
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN,
+ (unsigned int)~SAI_XCR1_SAIEN);
+ if (ret < 0)
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
+
+ clk_disable_unprepare(sai->sai_ck);
+ sai->substream = NULL;
+}
+
+static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
+
+ sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
+ sai->dma_params.maxburst = 1;
+ /* Buswidth will be set by framework at runtime */
+ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+
+ if (STM_SAI_IS_PLAYBACK(sai))
+ snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL);
+ else
+ snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
+ .set_sysclk = stm32_sai_set_sysclk,
+ .set_fmt = stm32_sai_set_dai_fmt,
+ .set_tdm_slot = stm32_sai_set_dai_tdm_slot,
+ .startup = stm32_sai_startup,
+ .hw_params = stm32_sai_hw_params,
+ .trigger = stm32_sai_trigger,
+ .shutdown = stm32_sai_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+ .buffer_bytes_max = 8 * PAGE_SIZE,
+ .period_bytes_min = 1024, /* 5ms at 48kHz */
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 8,
+};
+
+static struct snd_soc_dai_driver stm32_sai_playback_dai[] = {
+{
+ .probe = stm32_sai_dai_probe,
+ .id = 1, /* avoid call to fmt_single_name() */
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ /* DMA does not support 24 bits transfers */
+ .formats =
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &stm32_sai_pcm_dai_ops,
+ }
+};
+
+static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {
+{
+ .probe = stm32_sai_dai_probe,
+ .id = 1, /* avoid call to fmt_single_name() */
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ /* DMA does not support 24 bits transfers */
+ .formats =
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &stm32_sai_pcm_dai_ops,
+ }
+};
+
+static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
+ .pcm_hardware = &stm32_sai_pcm_hw,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static const struct snd_soc_component_driver stm32_component = {
+ .name = "stm32-sai",
+};
+
+static const struct of_device_id stm32_sai_sub_ids[] = {
+ { .compatible = "st,stm32-sai-sub-a",
+ .data = (void *)STM_SAI_A_ID},
+ { .compatible = "st,stm32-sai-sub-b",
+ .data = (void *)STM_SAI_B_ID},
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids);
+
+static int stm32_sai_sub_parse_of(struct platform_device *pdev,
+ struct stm32_sai_sub_data *sai)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ void __iomem *base;
+
+ if (!np)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev_err(&pdev->dev, "res %pr\n", res);
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ sai->phys_addr = res->start;
+ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &stm32_sai_sub_regmap_config);
+
+ /* Get direction property */
+ if (of_property_match_string(np, "dma-names", "tx") >= 0) {
+ sai->dir = SNDRV_PCM_STREAM_PLAYBACK;
+ } else if (of_property_match_string(np, "dma-names", "rx") >= 0) {
+ sai->dir = SNDRV_PCM_STREAM_CAPTURE;
+ } else {
+ dev_err(&pdev->dev, "Unsupported direction\n");
+ return -EINVAL;
+ }
+
+ sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
+ if (IS_ERR(sai->sai_ck)) {
+ dev_err(&pdev->dev, "missing kernel clock sai_ck\n");
+ return PTR_ERR(sai->sai_ck);
+ }
+
+ return 0;
+}
+
+static int stm32_sai_sub_dais_init(struct platform_device *pdev,
+ struct stm32_sai_sub_data *sai)
+{
+ sai->cpu_dai_drv = devm_kzalloc(&pdev->dev,
+ sizeof(struct snd_soc_dai_driver),
+ GFP_KERNEL);
+ if (!sai->cpu_dai_drv)
+ return -ENOMEM;
+
+ sai->cpu_dai_drv->name = dev_name(&pdev->dev);
+ if (STM_SAI_IS_PLAYBACK(sai)) {
+ memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai,
+ sizeof(stm32_sai_playback_dai));
+ sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name;
+ } else {
+ memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai,
+ sizeof(stm32_sai_capture_dai));
+ sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name;
+ }
+
+ return 0;
+}
+
+static int stm32_sai_sub_probe(struct platform_device *pdev)
+{
+ struct stm32_sai_sub_data *sai;
+ const struct of_device_id *of_id;
+ int ret;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
+ sai->id = (uintptr_t)of_id->data;
+
+ sai->pdev = pdev;
+ platform_set_drvdata(pdev, sai);
+
+ sai->pdata = dev_get_drvdata(pdev->dev.parent);
+ if (!sai->pdata) {
+ dev_err(&pdev->dev, "Parent device data not available\n");
+ return -EINVAL;
+ }
+
+ ret = stm32_sai_sub_parse_of(pdev, sai);
+ if (ret)
+ return ret;
+
+ ret = stm32_sai_sub_dais_init(pdev, sai);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
+ IRQF_SHARED, dev_name(&pdev->dev), sai);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request returned %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
+ sai->cpu_dai_drv, 1);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &stm32_sai_pcm_config, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm dma\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver stm32_sai_sub_driver = {
+ .driver = {
+ .name = "st,stm32-sai-sub",
+ .of_match_table = stm32_sai_sub_ids,
+ },
+ .probe = stm32_sai_sub_probe,
+};
+
+module_platform_driver(stm32_sai_sub_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai-sub");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* Re: [PATCH v3 1/8] dt-bindings: Document STM32 DCMI bindings
From: Rob Herring @ 2017-04-10 15:20 UTC (permalink / raw)
To: Hugues Fruchet
Cc: Mark Rutland, Maxime Coquelin, Alexandre Torgue,
Mauro Carvalho Chehab, Hans Verkuil,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA, Benjamin Gaignard,
Yannick Fertre
In-Reply-To: <1491320678-17246-2-git-send-email-hugues.fruchet-qxv4g6HH51o@public.gmane.org>
On Tue, Apr 04, 2017 at 05:44:31PM +0200, Hugues Fruchet wrote:
> This adds documentation of device tree bindings for the STM32 DCMI
> (Digital Camera Memory Interface).
>
> Signed-off-by: Hugues Fruchet <hugues.fruchet-qxv4g6HH51o@public.gmane.org>
> ---
> .../devicetree/bindings/media/st,stm32-dcmi.txt | 46 ++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dcmi.txt
One nit below, otherwise:
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>
> diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt b/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt
> new file mode 100644
> index 0000000..c0f6f4b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.txt
> @@ -0,0 +1,46 @@
> +STMicroelectronics STM32 Digital Camera Memory Interface (DCMI)
> +
> +Required properties:
> +- compatible: "st,stm32-dcmi"
> +- reg: physical base address and length of the registers set for the device
> +- interrupts: should contain IRQ line for the DCMI
> +- resets: reference to a reset controller,
> + see Documentation/devicetree/bindings/reset/st,stm32-rcc.txt
> +- clocks: list of clock specifiers, corresponding to entries in
> + the clock-names property
> +- clock-names: must contain "mclk", which is the DCMI peripherial clock
> +- pinctrl: the pincontrol settings to configure muxing properly
> + for pins that connect to DCMI device.
> + See Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt.
> +- dmas: phandle to DMA controller node,
> + see Documentation/devicetree/bindings/dma/stm32-dma.txt
> +- dma-names: must contain "tx", which is the transmit channel from DCMI to DMA
> +
> +DCMI supports a single port node with parallel bus. It should contain one
> +'port' child node with child 'endpoint' node. Please refer to the bindings
> +defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> +
> + dcmi: dcmi@50050000 {
> + compatible = "st,stm32-dcmi";
> + reg = <0x50050000 0x400>;
> + interrupts = <78>;
> + resets = <&rcc STM32F4_AHB2_RESET(DCMI)>;
> + clocks = <&rcc 0 STM32F4_AHB2_CLOCK(DCMI)>;
> + clock-names = "mclk";
> + pinctrl-names = "default";
> + pinctrl-0 = <&dcmi_pins>;
> + dmas = <&dma2 1 1 0x414 0x3>;
> + dma-names = "tx";
> + port {
> + dcmi_0: endpoint@0 {
Unit address in not valid without a reg prop, so drop it.
> + remote-endpoint = <...>;
> + bus-width = <8>;
> + hsync-active = <0>;
> + vsync-active = <0>;
> + pclk-sample = <1>;
> + };
> + };
> + };
> +
> --
> 1.9.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 0/6] mvmdio updates
From: Russell King - ARM Linux @ 2017-04-10 15:27 UTC (permalink / raw)
To: Marcin Wojtas, Stefan Chulski, Andrew Lunn, Thomas Petazzoni
Cc: devicetree, Mark Rutland, netdev, Rob Herring
This series of patches update mvmdio for Armada 8k CP110. A number of
issues were found:
1. The driver fails to disable an interrupt when something goes wrong
in the probe function.
2. The interrupt is specified in DT to be optional, but the driver
unconditionally writes to the interrupt mask register, which may
not exist.
3. The DT binding specifies
"reg: address and length of the SMI register"
however, when supporting the interrupt, the size must cover the
interrupt register as well. Update the binding documentation
with this information that was previously omitted.
4. If the register size is too small, have the driver print an error
and disable use of the interrupt.
5. Armada 8k needs three clocks for the MDIO interface, otherwise the
SoC hangs (since it is part of one of the ethernet interfaces.)
GOP clock, MG core clock and MG clock are needed on 8k. Augment the
binding and driver to allow three clocks to be specified.
.../devicetree/bindings/net/marvell-orion-mdio.txt | 19 ++++++++--
drivers/net/ethernet/marvell/mvmdio.c | 44 +++++++++++++++++-----
2 files changed, 50 insertions(+), 13 deletions(-)
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH 3/6] dt-bindings: correct marvell orion MDIO binding document
From: Russell King @ 2017-04-10 15:28 UTC (permalink / raw)
To: Marcin Wojtas, Stefan Chulski, Andrew Lunn, Thomas Petazzoni
Cc: Rob Herring, Mark Rutland, netdev, devicetree
In-Reply-To: <20170410152728.GT17774@n2100.armlinux.org.uk>
Correct the Marvell Orion MDIO binding document to properly reflect the
cases where an interrupt is present. Augment the examples to show this.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
.../devicetree/bindings/net/marvell-orion-mdio.txt | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
index 9417e54c26c0..ca733ff68ab9 100644
--- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
+++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
@@ -7,7 +7,10 @@ interface.
Required properties:
- compatible: "marvell,orion-mdio"
-- reg: address and length of the SMI register
+- reg: address and length of the MDIO registers. When an interrupt is
+ not present, the length is the size of the SMI register (4 bytes)
+ otherwise it must be 0x84 bytes to cover the interrupt control
+ registers.
Optional properties:
- interrupts: interrupt line number for the SMI error/done interrupt
@@ -17,7 +20,7 @@ The child nodes of the MDIO driver are the individual PHY devices
connected to this MDIO bus. They must have a "reg" property given the
PHY address on the MDIO bus.
-Example at the SoC level:
+Example at the SoC level without an interrupt property:
mdio {
#address-cells = <1>;
@@ -26,6 +29,16 @@ mdio {
reg = <0xd0072004 0x4>;
};
+Example with an interrupt property:
+
+mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "marvell,orion-mdio";
+ reg = <0xd0072004 0x84>;
+ interrupts = <30>;
+};
+
And at the board level:
mdio {
--
2.7.4
^ permalink raw reply related
* [PATCH 5/6] dt-bindings: allow up to three clocks for orion-mdio
From: Russell King @ 2017-04-10 15:28 UTC (permalink / raw)
To: Marcin Wojtas, Stefan Chulski, Andrew Lunn, Thomas Petazzoni
Cc: Rob Herring, Mark Rutland, netdev, devicetree
In-Reply-To: <20170410152728.GT17774@n2100.armlinux.org.uk>
Armada 8040 needs three clocks to be enabled for MDIO accesses to work.
Update the binding to allow the extra clocks to be specified.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
Documentation/devicetree/bindings/net/marvell-orion-mdio.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
index ca733ff68ab9..ccdabdcc8618 100644
--- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
+++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
@@ -14,7 +14,7 @@ interface.
Optional properties:
- interrupts: interrupt line number for the SMI error/done interrupt
-- clocks: Phandle to the clock control device and gate bit
+- clocks: phandle for up to three required clocks for the MDIO instance
The child nodes of the MDIO driver are the individual PHY devices
connected to this MDIO bus. They must have a "reg" property given the
--
2.7.4
^ permalink raw reply related
* Re: [PATCH 2/2] devicetree: Document the max31760 device binding.
From: Rob Herring @ 2017-04-10 15:42 UTC (permalink / raw)
To: John Muir
Cc: Jean Delvare, Guenter Roeck, Jonathan Corbet, Pawel Moll,
Ian Campbell, Kumar Gala, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Anatol Pomazau, Mark Segal
In-Reply-To: <20170404192034.158901-3-john-eXjPKP/gKhgAvxtiuMwx3w@public.gmane.org>
On Tue, Apr 04, 2017 at 12:20:34PM -0700, John Muir wrote:
> Signed-off-by: John Muir <john-eXjPKP/gKhgAvxtiuMwx3w@public.gmane.org>
> ---
> .../devicetree/bindings/hwmon/max31760.txt | 58 ++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/hwmon/max31760.txt
>
> diff --git a/Documentation/devicetree/bindings/hwmon/max31760.txt b/Documentation/devicetree/bindings/hwmon/max31760.txt
> new file mode 100644
> index 000000000000..43787a77c322
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/max31760.txt
> @@ -0,0 +1,58 @@
> +MAX31760 fan controller
> +-----------------------
> +
> +This device supports I2C only. Many properties of this device are configurable
> +thorugh the hwmon interface. See also Documentation/hwmon/max31760.
I really think we need to describe the fans as separate nodes and
preferably with a common binding. This is the second fan controller
binding recently[1].
Features of the "hwmon interface" are not relevant to the binding.
Bindings describe h/w.
> +
> +Required node properties:
> +- compatible : "maxim,max31760"
> +- reg : The I2C address of the device. This is 0x50 - 0x57 depending on the
> + hardware configuration.
> +
> +Optional node properties:
> +- maxim,fan1-enabled - 1 to enable, 0 to disable. Default: 1.
> +- maxim,fan2-enabled - 1 to enable, 0 to disable. Default: 1.
> +- maxim,fan1-label - String: Hwmon fan1_label.
> +- maxim,fan2-label - String: Hwmon fan2_label.
Perhaps 2 fan sub nodes. reg for fan number, status for enabled, and
label for label.
> +- maxim,fan-fail-full-only - Set to 1 to assert a fan failure only when the
> + PWM is at 100%. Default: 0.
Make this a boolean.
> +- maxim,fan-rd-signal - Set to 1 if fan(s) provide a rotation
> + detection (RD) signal, or 0 if the fan
> + generates square-wave pulses. Default: 0.
Make this a boolean.
> +- maxim,fan-rd-polarity - 0: RD is low when the fan is running.
> + 1: RD is high when the fan is running.
> + Only relevant when fan-rd-signal is 1.
> + Default: 0.
> +- maxim,fan-signal-enabled - Set to 1 if externally driving FF/FS low
> + should force PWM output to 100%. Default: 0.
> +- maxim,fan-spin-up-enabled - For fan startup: Set to 1 to set the PWM to
> + 100% until tach is detected or two seconds
> + have passed before reducing to the target
> + value. Default: 0.
> +- maxim,pwm-polarity - 0: 100% PWM is when PWM is high.
> + 1: 100% PWM is when PWM is low.
> + Default: 0.
> +- maxim,pwm-pulse-stretch-enabled
> + - 1 to enable PWM pulse stretching, 0 to
> + disable. Default: 0.
> +- maxim,pwm-zero-fan-can-fail - 0: Fan failure detection disabled when PWM is
> + ramping to 0%.
> + 1: Fan failure detection enabled for all PWM
> + values.
> + Default: 0.
All these can be boolean...
> +- maxim,temp1-label - String: Hwmon temp1_label.
> +- maxim,temp2-label - String: Hwmon temp2_label.
> +- maxim,temp2-ideality - Set ideality factor for the remote temperature
> + sensor. Integer with range 0 to 63,
> + representing a multiplication factor of 0.9844
> + to 1.0489. Default: 24 (1.0080).
No maxim,temp1-ideality? Not sure what to do with these, but perhaps
also as sub-nodes. Surely we have some bindings already for devices with
multiple temp sensors. Don't invent something custom here.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [GIT PULL] PCI: Support for configurable PCI endpoint
From: Bjorn Helgaas @ 2017-04-10 15:43 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: devicetree, Joao Pinto, linux-doc, linux-pci, nsekhar,
linux-kernel, hch, Bjorn Helgaas, linux-omap, linux-arm-kernel
In-Reply-To: <20170405085243.18123-1-kishon@ti.com>
On Wed, Apr 05, 2017 at 02:22:20PM +0530, Kishon Vijay Abraham I wrote:
> Hi Bjorn,
>
> Please find the pull request for PCI endpoint support below. I've
> also included all the history here.
Thanks, I applied these (with v7 of the first patch) to pci/host-designware
for v4.12.
> Changes from v5:
> *) remove #syscon-cells property added in v5 and used
> of_parse_phandle_with_fixed_args
> *) fix compilation error in make.cross ARCH=blackfin that was because
> pci_endpoint_test.c driver depends on COMPILE_TEST.
>
> Changes from v4:
> *) add #syscon-cells property and used of_parse_phandle_with_args
> to perform a configuration in syscon module (as suggested by
> Rob Herring)
> *) Remove unnecessary white space.
>
> Changes from v3:
> *) fixed a typo and adapted to https://lkml.org/lkml/2017/3/13/562.
>
> Changes from v2:
> *) changed the configfs structure as suggested by Christoph Hellwig. With
> this change the framework creates configfs entry for EP function driver
> and EP controller. Previously these entries have to be created by the
> the user. (Haven't changed the epc core or epf core except for invoking
> configfs APIs to create entries for EP function driver and EP controller.
> That's mostly because the EP function device can still be created by
> directly invoking the epf core API without using configfs).
> *) Now the user has to use configfs entry 'start' to start the link.
> This was previously done by the function driver. However in the case of
> multi function EP, the function driver shouldn't start the link.
>
> Changes from v1:
> *) The preparation patches for adding EP support is removed and is sent
> separately
> *) Added device ID for DRA74x/DRA72x and used it instead of
> using "PCI_ANY_ID"
> *) Added userguide for PCI endpoint test function
>
> Major Improvements from RFC:
> *) support multi-function devices (hw supported not virtual)
> *) Access host side buffers
> *) Raise MSI interrupts
> *) Add user space program to use the host side PCI driver
> *) Adapt all other users of designware to use the new design (only
> compile tested. Since I have only dra7xx boards, the new design
> has only been tested in dra7xx. I'd require the help of others
> to test the platforms they have access to).
>
> This is based on
> git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
> pci/host-designware
>
> Thanks
> Kishon
>
> The following changes since commit 7ea64dcf602c21b3e5062ca90111ca4459fab403:
>
> __end__ (2017-04-04 15:29:37 -0500)
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/kishon/pci-endpoint.git tags/pci-endpoint-for-4.12
>
> for you to fetch changes up to a5c85ba45c9682456077d7277196e91f8ea5fd5c:
>
> ARM: DRA7: clockdomain: Change the CLKTRCTRL of CM_PCIE_CLKSTCTRL to SW_WKUP (2017-04-05 14:05:28 +0530)
>
> ----------------------------------------------------------------
> pci: endpoint: for 4.12
>
> *) Add PCI endpoint core layer
> *) Modify designware and dra7xx driver to be configured in EP mode
> *) Add a PCI endpoint *test* function driver and corresponding host
> driver
>
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>
> ----------------------------------------------------------------
> Kishon Vijay Abraham I (23):
> PCI: endpoint: Add EP core layer to enable EP controller and EP functions
> Documentation: PCI: Guide to use PCI Endpoint Core Layer
> PCI: endpoint: Introduce configfs entry for configuring EP functions
> Documentation: PCI: Guide to use PCI endpoint configfs
> PCI: endpoint: Create configfs entry for EPC device and EPF driver
> Documentation: PCI: Add specification for the *PCI test* function device
> PCI: endpoint: functions: Add an EP function to test PCI
> Documentation: PCI: Add binding documentation for pci-test endpoint function
> PCI: dwc: designware: Add EP mode support
> dt-bindings: PCI: Add DT bindings for PCI designware EP mode
> PCI: dwc: dra7xx: Facilitate wrapper and MSI interrupts to be enabled independently
> PCI: dwc: dra7xx: Add EP mode support
> dt-bindings: PCI: dra7xx: Add DT bindings for PCI dra7xx EP mode
> PCI: dwc: dra7xx: Workaround for errata id i870
> dt-bindings: PCI: dra7xx: Add DT bindings to enable unaligned access
> PCI: Add device IDs for DRA74x and DRA72x
> misc: Add host side PCI driver for PCI test function device
> Documentation: misc-devices: Add Documentation for pci-endpoint-test driver
> tools: PCI: Add a userspace tool to test PCI endpoint
> tools: PCI: Add sample test script to invoke pcitest
> Documentation: PCI: Add userguide for PCI endpoint test function
> MAINTAINERS: Add PCI Endpoint maintainer
> ARM: DRA7: clockdomain: Change the CLKTRCTRL of CM_PCIE_CLKSTCTRL to SW_WKUP
>
> Documentation/PCI/00-INDEX | 10 +++
> Documentation/PCI/endpoint/function/binding/pci-test.txt | 17 +++++
> Documentation/PCI/endpoint/pci-endpoint-cfs.txt | 105 ++++++++++++++++++++++++++
> Documentation/PCI/endpoint/pci-endpoint.txt | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> Documentation/PCI/endpoint/pci-test-function.txt | 66 ++++++++++++++++
> Documentation/PCI/endpoint/pci-test-howto.txt | 179 +++++++++++++++++++++++++++++++++++++++++++
> Documentation/devicetree/bindings/pci/designware-pcie.txt | 26 +++++--
> Documentation/devicetree/bindings/pci/ti-pci.txt | 42 +++++++++--
> Documentation/misc-devices/pci-endpoint-test.txt | 35 +++++++++
> MAINTAINERS | 9 +++
> arch/arm/mach-omap2/clockdomains7xx_data.c | 2 +-
> drivers/Makefile | 2 +
> drivers/misc/Kconfig | 7 ++
> drivers/misc/Makefile | 1 +
> drivers/misc/pci_endpoint_test.c | 534 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/pci/Kconfig | 1 +
> drivers/pci/dwc/Kconfig | 36 ++++++++-
> drivers/pci/dwc/Makefile | 5 +-
> drivers/pci/dwc/pci-dra7xx.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
> drivers/pci/dwc/pcie-designware-ep.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/pci/dwc/pcie-designware.c | 125 ++++++++++++++++++++++++++++++
> drivers/pci/dwc/pcie-designware.h | 112 +++++++++++++++++++++++++++
> drivers/pci/endpoint/Kconfig | 31 ++++++++
> drivers/pci/endpoint/Makefile | 7 ++
> drivers/pci/endpoint/functions/Kconfig | 12 +++
> drivers/pci/endpoint/functions/Makefile | 5 ++
> drivers/pci/endpoint/functions/pci-epf-test.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/pci/endpoint/pci-ep-cfs.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/pci/endpoint/pci-epc-core.c | 579 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/pci/endpoint/pci-epc-mem.c | 143 +++++++++++++++++++++++++++++++++++
> drivers/pci/endpoint/pci-epf-core.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/mod_devicetable.h | 10 +++
> include/linux/pci-ep-cfs.h | 41 ++++++++++
> include/linux/pci-epc.h | 144 +++++++++++++++++++++++++++++++++++
> include/linux/pci-epf.h | 162 +++++++++++++++++++++++++++++++++++++++
> include/linux/pci_ids.h | 2 +
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/pcitest.h | 19 +++++
> tools/pci/pcitest.c | 186 +++++++++++++++++++++++++++++++++++++++++++++
> tools/pci/pcitest.sh | 56 ++++++++++++++
> 40 files changed, 4869 insertions(+), 40 deletions(-)
> create mode 100644 Documentation/PCI/endpoint/function/binding/pci-test.txt
> create mode 100644 Documentation/PCI/endpoint/pci-endpoint-cfs.txt
> create mode 100644 Documentation/PCI/endpoint/pci-endpoint.txt
> create mode 100644 Documentation/PCI/endpoint/pci-test-function.txt
> create mode 100644 Documentation/PCI/endpoint/pci-test-howto.txt
> create mode 100644 Documentation/misc-devices/pci-endpoint-test.txt
> create mode 100644 drivers/misc/pci_endpoint_test.c
> create mode 100644 drivers/pci/dwc/pcie-designware-ep.c
> create mode 100644 drivers/pci/endpoint/Kconfig
> create mode 100644 drivers/pci/endpoint/Makefile
> create mode 100644 drivers/pci/endpoint/functions/Kconfig
> create mode 100644 drivers/pci/endpoint/functions/Makefile
> create mode 100644 drivers/pci/endpoint/functions/pci-epf-test.c
> create mode 100644 drivers/pci/endpoint/pci-ep-cfs.c
> create mode 100644 drivers/pci/endpoint/pci-epc-core.c
> create mode 100644 drivers/pci/endpoint/pci-epc-mem.c
> create mode 100644 drivers/pci/endpoint/pci-epf-core.c
> create mode 100644 include/linux/pci-ep-cfs.h
> create mode 100644 include/linux/pci-epc.h
> create mode 100644 include/linux/pci-epf.h
> create mode 100644 include/uapi/linux/pcitest.h
> create mode 100644 tools/pci/pcitest.c
> create mode 100644 tools/pci/pcitest.sh
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/2] devicetree: Document the max31760 device binding.
From: Rob Herring @ 2017-04-10 15:44 UTC (permalink / raw)
To: John Muir
Cc: Jean Delvare, Guenter Roeck, Jonathan Corbet, Pawel Moll,
Ian Campbell, Kumar Gala, devicetree@vger.kernel.org, linux-hwmon,
linux-doc@vger.kernel.org, Anatol Pomazau, Mark Segal
In-Reply-To: <20170410154249.r77b6vf3ctzn4kua@rob-hp-laptop>
On Mon, Apr 10, 2017 at 10:42 AM, Rob Herring <robh@kernel.org> wrote:
> On Tue, Apr 04, 2017 at 12:20:34PM -0700, John Muir wrote:
>> Signed-off-by: John Muir <john@jmuir.com>
>> ---
>> .../devicetree/bindings/hwmon/max31760.txt | 58 ++++++++++++++++++++++
>> 1 file changed, 58 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/hwmon/max31760.txt
>>
>> diff --git a/Documentation/devicetree/bindings/hwmon/max31760.txt b/Documentation/devicetree/bindings/hwmon/max31760.txt
>> new file mode 100644
>> index 000000000000..43787a77c322
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/hwmon/max31760.txt
>> @@ -0,0 +1,58 @@
>> +MAX31760 fan controller
>> +-----------------------
>> +
>> +This device supports I2C only. Many properties of this device are configurable
>> +thorugh the hwmon interface. See also Documentation/hwmon/max31760.
>
> I really think we need to describe the fans as separate nodes and
> preferably with a common binding. This is the second fan controller
> binding recently[1].
Forgot the link: https://patchwork.kernel.org/patch/9643643/
^ permalink raw reply
* [PATCH v3 0/2] Add STM32H7 DAC driver
From: Fabrice Gasnier @ 2017-04-10 15:49 UTC (permalink / raw)
To: jic23, linux, robh+dt, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, benjamin.gaignard, lars, alexandre.torgue,
linux-iio, pmeerw, mcoquelin.stm32, knaack.h, fabrice.gasnier,
benjamin.gaignard
This patchset adds support for the STM32H7 DAC controller
It's a 12-bit, voltage output digital-to-analog converter. It has two
output channels, each with its own converter, trigger sources and
waveform generator.
Each channel can be used independently, so common resources are managed
in stm32-dac-core driver (e.g. clock, reset, regulator, registers).
One IIO device is instantiated per DAC output channel, in stm32-dac
driver, so each channel can have its own trigger.
Examples, using this driver to generate DC voltage output on channel1:
# set max DC voltage / enable / min DC voltage / disable on out1
cd /sys/bus/iio/devices/iio\:device0
echo 4095 > out_voltage1_raw
echo 0 > out_voltage1_powerdown
echo 0 > out_voltage1_raw
echo 1 > out_voltage1_powerdown
cat out_voltage_powerdown_mode_available
three_state
---
Changes in v3:
- Fix powerdown mode, use 'three_state' as documented in sysfs-bus-iio
- For now, drop support for triggers & waveform generator that requires more work.
This needs to be discussed more deeply.
Changes in v2:
- Update dt binding, use 'reg' property to select channel
- Use 'powerdown' attribute instead of 'enable'
- Added set_trigger callback
- Use 'offset' attribute in waveform generation mode to add DC offset
- rework ABI for waveform generation mode
- Various typos, comments
Fabrice Gasnier (2):
dt-bindings: iio: stm32-dac: Add support for STM32 DAC
iio: dac: add support for stm32 DAC
.../devicetree/bindings/iio/dac/st,stm32-dac.txt | 61 ++++
drivers/iio/dac/Kconfig | 15 +
drivers/iio/dac/Makefile | 2 +
drivers/iio/dac/stm32-dac-core.c | 180 +++++++++++
drivers/iio/dac/stm32-dac-core.h | 51 ++++
drivers/iio/dac/stm32-dac.c | 334 +++++++++++++++++++++
6 files changed, 643 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
create mode 100644 drivers/iio/dac/stm32-dac-core.c
create mode 100644 drivers/iio/dac/stm32-dac-core.h
create mode 100644 drivers/iio/dac/stm32-dac.c
--
1.9.1
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: iio: stm32-dac: Add support for STM32 DAC
From: Fabrice Gasnier @ 2017-04-10 15:49 UTC (permalink / raw)
To: jic23, linux, robh+dt, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, benjamin.gaignard, lars, alexandre.torgue,
linux-iio, pmeerw, mcoquelin.stm32, knaack.h, fabrice.gasnier,
benjamin.gaignard
In-Reply-To: <1491839390-2449-1-git-send-email-fabrice.gasnier@st.com>
Document STMicroelectronics STM32 DAC (digital-to-analog converter).
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v2:
- use 'reg' instead of 'st,dac-channel' property
- remove alignment from description
---
.../devicetree/bindings/iio/dac/st,stm32-dac.txt | 61 ++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
diff --git a/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
new file mode 100644
index 0000000..bcee71f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
@@ -0,0 +1,61 @@
+STMicroelectronics STM32 DAC
+
+The STM32 DAC is a 12-bit voltage output digital-to-analog converter. The DAC
+may be configured in 8 or 12-bit mode. It has two output channels, each with
+its own converter.
+It has built-in noise and triangle waveform generator and supports external
+triggers for conversions. The DAC's output buffer allows a high drive output
+current.
+
+Contents of a stm32 dac root node:
+-----------------------------------
+Required properties:
+- compatible: Must be "st,stm32h7-dac-core".
+- reg: Offset and length of the device's register set.
+- clocks: Must contain an entry for pclk (which feeds the peripheral bus
+ interface)
+- clock-names: Must be "pclk".
+- vref-supply: Phandle to the vref+ input analog reference supply.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- resets: Must contain the phandle to the reset controller.
+- A pinctrl state named "default" for each DAC channel may be defined to set
+ DAC_OUTx pin in mode of operation for analog output on external pin.
+
+Contents of a stm32 dac child node:
+-----------------------------------
+DAC core node should contain at least one subnode, representing a
+DAC instance/channel available on the machine.
+
+Required properties:
+- compatible: Must be "st,stm32-dac".
+- reg: Must be either 1 or 2, to define (single) channel in use
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Example:
+ dac: dac@40007400 {
+ compatible = "st,stm32h7-dac-core";
+ reg = <0x40007400 0x400>;
+ clocks = <&clk>;
+ clock-names = "pclk";
+ vref-supply = <®_vref>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&dac_out1 &dac_out2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac1: dac@1 {
+ compatible = "st,stm32-dac";
+ #io-channels-cells = <1>;
+ reg = <1>;
+ };
+
+ dac2: dac@2 {
+ compatible = "st,stm32-dac";
+ #io-channels-cells = <1>;
+ reg = <2>;
+ };
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/2] iio: dac: add support for stm32 DAC
From: Fabrice Gasnier @ 2017-04-10 15:49 UTC (permalink / raw)
To: jic23, linux, robh+dt, linux-arm-kernel, devicetree, linux-kernel
Cc: linux-iio, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
knaack.h, pmeerw, fabrice.gasnier, benjamin.gaignard,
benjamin.gaignard
In-Reply-To: <1491839390-2449-1-git-send-email-fabrice.gasnier@st.com>
Add support for STMicroelectronics STM32 DAC. It's a 12-bit, voltage
output digital-to-analog converter. It has two output channels, each
with its own converter.
It supports 8 bits or 12bits left/right aligned data format. Only
12bits right-aligned is used here. It has built-in noise or
triangle waveform generator, and supports external triggers for
conversions.
Each channel can be used independently, with separate trigger, then
separate IIO devices are used to handle this. Core driver is intended
to share common resources such as clock, reset, reference voltage and
registers.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v3:
- Fix powerdown, no need for 'enable', use 'three_state' as standard name
for 'left floating' as per ABI documentation, instead of 'Hi-Z'.
Changes in v2:
- Define 'Hi-Z'/'enable' powerdown modes instead of using 'enable'
attribute normally not used for DACs.
- use 'reg' instead of 'st,dac-channel' property
- Use macro to differentiate channels
- Fix typos, remove leading '&' for functions
- Add comments on single channel per device
- Use devm_iio_device_register variant, removes need for .remove
---
drivers/iio/dac/Kconfig | 15 ++
drivers/iio/dac/Makefile | 2 +
drivers/iio/dac/stm32-dac-core.c | 180 +++++++++++++++++++++
drivers/iio/dac/stm32-dac-core.h | 51 ++++++
drivers/iio/dac/stm32-dac.c | 334 +++++++++++++++++++++++++++++++++++++++
5 files changed, 582 insertions(+)
create mode 100644 drivers/iio/dac/stm32-dac-core.c
create mode 100644 drivers/iio/dac/stm32-dac-core.h
create mode 100644 drivers/iio/dac/stm32-dac.c
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index d3084028..7198648 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -274,6 +274,21 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
+config STM32_DAC
+ tristate "STMicroelectronics STM32 DAC"
+ depends on (ARCH_STM32 && OF) || COMPILE_TEST
+ depends on REGULATOR
+ select STM32_DAC_CORE
+ help
+ Say yes here to build support for STMicroelectronics STM32 Digital
+ to Analog Converter (DAC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-dac.
+
+config STM32_DAC_CORE
+ tristate
+
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index f01bf4a..afe8ae7 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,4 +29,6 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
+obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
+obj-$(CONFIG_STM32_DAC) += stm32-dac.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
new file mode 100644
index 0000000..75e4878
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac-core.c
@@ -0,0 +1,180 @@
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include "stm32-dac-core.h"
+
+/**
+ * struct stm32_dac_priv - stm32 DAC core private data
+ * @pclk: peripheral clock common for all DACs
+ * @rst: peripheral reset control
+ * @vref: regulator reference
+ * @common: Common data for all DAC instances
+ */
+struct stm32_dac_priv {
+ struct clk *pclk;
+ struct reset_control *rst;
+ struct regulator *vref;
+ struct stm32_dac_common common;
+};
+
+static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
+{
+ return container_of(com, struct stm32_dac_priv, common);
+}
+
+static const struct regmap_config stm32_dac_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x3fc,
+};
+
+static int stm32_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_dac_priv *priv;
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *mmio;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ priv->common.regmap = regmap;
+
+ priv->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(priv->vref)) {
+ ret = PTR_ERR(priv->vref);
+ dev_err(dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref get voltage failed, %d\n", ret);
+ goto err_vref;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk)) {
+ ret = PTR_ERR(priv->pclk);
+ dev_err(dev, "pclk get failed\n");
+ goto err_vref;
+ }
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret < 0) {
+ dev_err(dev, "pclk enable failed\n");
+ goto err_vref;
+ }
+
+ priv->rst = devm_reset_control_get(dev, NULL);
+ if (!IS_ERR(priv->rst)) {
+ reset_control_assert(priv->rst);
+ udelay(2);
+ reset_control_deassert(priv->rst);
+ }
+
+ /* When clock speed is higher than 80MHz, set HFSEL */
+ priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
+ ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
+ priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
+ if (ret)
+ goto err_pclk;
+
+ platform_set_drvdata(pdev, &priv->common);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to populate DT children\n");
+ goto err_pclk;
+ }
+
+ return 0;
+
+err_pclk:
+ clk_disable_unprepare(priv->pclk);
+err_vref:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static int stm32_dac_remove(struct platform_device *pdev)
+{
+ struct stm32_dac_common *common = platform_get_drvdata(pdev);
+ struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+
+ of_platform_depopulate(&pdev->dev);
+ clk_disable_unprepare(priv->pclk);
+ regulator_disable(priv->vref);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_dac_of_match[] = {
+ { .compatible = "st,stm32h7-dac-core", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
+
+static struct platform_driver stm32_dac_driver = {
+ .probe = stm32_dac_probe,
+ .remove = stm32_dac_remove,
+ .driver = {
+ .name = "stm32-dac-core",
+ .of_match_table = stm32_dac_of_match,
+ },
+};
+module_platform_driver(stm32_dac_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-dac-core");
diff --git a/drivers/iio/dac/stm32-dac-core.h b/drivers/iio/dac/stm32-dac-core.h
new file mode 100644
index 0000000..daf0993
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac-core.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_DAC_CORE_H
+#define __STM32_DAC_CORE_H
+
+#include <linux/regmap.h>
+
+/* STM32 DAC registers */
+#define STM32_DAC_CR 0x00
+#define STM32_DAC_DHR12R1 0x08
+#define STM32_DAC_DHR12R2 0x14
+#define STM32_DAC_DOR1 0x2C
+#define STM32_DAC_DOR2 0x30
+
+/* STM32_DAC_CR bit fields */
+#define STM32_DAC_CR_EN1 BIT(0)
+#define STM32H7_DAC_CR_HFSEL BIT(15)
+#define STM32_DAC_CR_EN2 BIT(16)
+
+/**
+ * struct stm32_dac_common - stm32 DAC driver common data (for all instances)
+ * @regmap: DAC registers shared via regmap
+ * @vref_mv: reference voltage (mv)
+ * @hfsel: high speed bus clock selected
+ */
+struct stm32_dac_common {
+ struct regmap *regmap;
+ int vref_mv;
+ bool hfsel;
+};
+
+#endif
diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
new file mode 100644
index 0000000..50f8ec0
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac.c
@@ -0,0 +1,334 @@
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Amelie Delaunay <amelie.delaunay@st.com>
+ * Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "stm32-dac-core.h"
+
+#define STM32_DAC_CHANNEL_1 1
+#define STM32_DAC_CHANNEL_2 2
+#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
+
+/**
+ * struct stm32_dac - private data of DAC driver
+ * @common: reference to DAC common data
+ */
+struct stm32_dac {
+ struct stm32_dac_common *common;
+};
+
+static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+ u32 en, val;
+ int ret;
+
+ ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
+ if (ret < 0)
+ return ret;
+ if (STM32_DAC_IS_CHAN_1(channel))
+ en = FIELD_GET(STM32_DAC_CR_EN1, val);
+ else
+ en = FIELD_GET(STM32_DAC_CR_EN2, val);
+
+ return !!en;
+}
+
+static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
+ bool enable)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+ u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
+ u32 en = enable ? msk : 0;
+ int ret;
+
+ ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "%s failed\n", en ?
+ "Enable" : "Disable");
+ return ret;
+ }
+
+ /*
+ * When HFSEL is set, it is not allowed to write the DHRx register
+ * during 8 clock cycles after the ENx bit is set. It is not allowed
+ * to make software/hardware trigger during this period either.
+ */
+ if (en && dac->common->hfsel)
+ udelay(1);
+
+ return 0;
+}
+
+static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
+{
+ int ret;
+
+ if (STM32_DAC_IS_CHAN_1(channel))
+ ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
+ else
+ ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
+
+ return ret ? ret : IIO_VAL_INT;
+}
+
+static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
+{
+ int ret;
+
+ if (STM32_DAC_IS_CHAN_1(channel))
+ ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
+ else
+ ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
+
+ return ret;
+}
+
+static int stm32_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return stm32_dac_get_value(dac, chan->channel, val);
+ case IIO_CHAN_INFO_SCALE:
+ *val = dac->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int stm32_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return stm32_dac_set_value(dac, chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ if (!readval)
+ return regmap_write(dac->common->regmap, reg, writeval);
+ else
+ return regmap_read(dac->common->regmap, reg, readval);
+}
+
+static const struct iio_info stm32_dac_iio_info = {
+ .read_raw = stm32_dac_read_raw,
+ .write_raw = stm32_dac_write_raw,
+ .debugfs_reg_access = stm32_dac_debugfs_reg_access,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const stm32_dac_powerdown_modes[] = {
+ "three_state",
+};
+
+static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ return 0;
+}
+
+static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ return 0;
+}
+
+static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
+
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret ? 0 : 1);
+}
+
+static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ ret = stm32_dac_set_enable_state(indio_dev, chan->channel, !powerdown);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static const struct iio_enum stm32_dac_powerdown_mode_en = {
+ .items = stm32_dac_powerdown_modes,
+ .num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
+ .get = stm32_dac_get_powerdown_mode,
+ .set = stm32_dac_set_powerdown_mode,
+};
+
+static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = stm32_dac_read_powerdown,
+ .write = stm32_dac_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
+ {},
+};
+
+#define STM32_DAC_CHANNEL(chan, name) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* scan_index is always 0 as num_channels is 1 */ \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .datasheet_name = name, \
+ .ext_info = stm32_dac_ext_info \
+}
+
+static const struct iio_chan_spec stm32_dac_channels[] = {
+ STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
+ STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
+};
+
+static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
+{
+ struct device_node *np = indio_dev->dev.of_node;
+ unsigned int i;
+ u32 channel;
+ int ret;
+
+ ret = of_property_read_u32(np, "reg", &channel);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to read reg property\n");
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
+ if (stm32_dac_channels[i].channel == channel)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32_dac_channels)) {
+ dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
+ return -EINVAL;
+ }
+
+ indio_dev->channels = &stm32_dac_channels[i];
+ /*
+ * Expose only one channel here, as they can be used independently,
+ * with separate trigger. Then separate IIO devices are instantiated
+ * to manage this.
+ */
+ indio_dev->num_channels = 1;
+
+ return 0;
+};
+
+static int stm32_dac_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct iio_dev *indio_dev;
+ struct stm32_dac *dac;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
+ if (!indio_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, indio_dev);
+
+ dac = iio_priv(indio_dev);
+ dac->common = dev_get_drvdata(pdev->dev.parent);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = stm32_dac_chan_of_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id stm32_dac_of_match[] = {
+ { .compatible = "st,stm32-dac", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
+
+static struct platform_driver stm32_dac_driver = {
+ .probe = stm32_dac_probe,
+ .driver = {
+ .name = "stm32-dac",
+ .of_match_table = stm32_dac_of_match,
+ },
+};
+module_platform_driver(stm32_dac_driver);
+
+MODULE_ALIAS("platform:stm32-dac");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* Re: [PATCH v2 2/5] iio: dac: add support for stm32 DAC
From: Fabrice Gasnier @ 2017-04-10 15:56 UTC (permalink / raw)
To: Jonathan Cameron, linux, robh+dt, linux-arm-kernel, devicetree,
linux-kernel
Cc: mark.rutland, benjamin.gaignard, lars, alexandre.torgue,
linux-iio, pmeerw, mcoquelin.stm32, knaack.h, benjamin.gaignard
In-Reply-To: <ceac5030-3a3c-dfdc-aa5d-9b5ea0165a0c@kernel.org>
On 04/09/2017 10:39 AM, Jonathan Cameron wrote:
> On 06/04/17 17:11, Fabrice Gasnier wrote:
>> Add support for STMicroelectronics STM32 DAC. It's a 12-bit, voltage
>> output digital-to-analog converter. It has two output channels, each
>> with its own converter.
>> It supports 8 bits or 12bits left/right aligned data format. Only
>> 12bits right-aligned is used here. It has built-in noise or
>> triangle waveform generator, and supports external triggers for
>> conversions.
>> Each channel can be used independently, with separate trigger, then
>> separate IIO devices are used to handle this. Core driver is intended
>> to share common resources such as clock, reset, reference voltage and
>> registers.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> A few little bits and pieces from the change to powerdown modes...
>
> I have also been thinking about whether we can take this prior to sorting
> out the DDS side, but worry a little that we may end up wanting to change
> the way we define the channels - so that might need sorting to some degree
> before we can get the basics in place.
Hi Jonathan,
Just sent updated v3 with powerdown changes as per your comments.
>
> Jonathan
>> ---
>> Changes in v2:
>> - Define 'Hi-Z'/'enable' powerdown modes instead of using 'enable'
>> attribute normally not used for DACs.
>> - use 'reg' instead of 'st,dac-channel' property
>> - Use macro to differentiate channels
>> - Fix typos, remove leading '&' for functions
>> - Add comments on single channel per device
>> - Use devm_iio_device_register variant, removes need for .remove
>> ---
>> drivers/iio/dac/Kconfig | 15 ++
>> drivers/iio/dac/Makefile | 2 +
>> drivers/iio/dac/stm32-dac-core.c | 180 ++++++++++++++++++++
>> drivers/iio/dac/stm32-dac-core.h | 51 ++++++
>> drivers/iio/dac/stm32-dac.c | 350 +++++++++++++++++++++++++++++++++++++++
>> 5 files changed, 598 insertions(+)
>> create mode 100644 drivers/iio/dac/stm32-dac-core.c
>> create mode 100644 drivers/iio/dac/stm32-dac-core.h
>> create mode 100644 drivers/iio/dac/stm32-dac.c
>>
>> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
>> index d3084028..7198648 100644
>> --- a/drivers/iio/dac/Kconfig
>> +++ b/drivers/iio/dac/Kconfig
>> @@ -274,6 +274,21 @@ config MCP4922
>> To compile this driver as a module, choose M here: the module
>> will be called mcp4922.
>>
>> +config STM32_DAC
>> + tristate "STMicroelectronics STM32 DAC"
>> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> + depends on REGULATOR
>> + select STM32_DAC_CORE
>> + help
>> + Say yes here to build support for STMicroelectronics STM32 Digital
>> + to Analog Converter (DAC).
>> +
>> + This driver can also be built as a module. If so, the module
>> + will be called stm32-dac.
>> +
>> +config STM32_DAC_CORE
>> + tristate
>> +
>> config VF610_DAC
>> tristate "Vybrid vf610 DAC driver"
>> depends on OF
>> diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
>> index f01bf4a..afe8ae7 100644
>> --- a/drivers/iio/dac/Makefile
>> +++ b/drivers/iio/dac/Makefile
>> @@ -29,4 +29,6 @@ obj-$(CONFIG_MAX517) += max517.o
>> obj-$(CONFIG_MAX5821) += max5821.o
>> obj-$(CONFIG_MCP4725) += mcp4725.o
>> obj-$(CONFIG_MCP4922) += mcp4922.o
>> +obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
>> +obj-$(CONFIG_STM32_DAC) += stm32-dac.o
>> obj-$(CONFIG_VF610_DAC) += vf610_dac.o
>> diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
>> new file mode 100644
>> index 0000000..75e4878
>> --- /dev/null
>> +++ b/drivers/iio/dac/stm32-dac-core.c
>> @@ -0,0 +1,180 @@
>> +/*
>> + * This file is part of STM32 DAC driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +
>> +#include "stm32-dac-core.h"
>> +
>> +/**
>> + * struct stm32_dac_priv - stm32 DAC core private data
>> + * @pclk: peripheral clock common for all DACs
>> + * @rst: peripheral reset control
>> + * @vref: regulator reference
>> + * @common: Common data for all DAC instances
>> + */
>> +struct stm32_dac_priv {
>> + struct clk *pclk;
>> + struct reset_control *rst;
>> + struct regulator *vref;
>> + struct stm32_dac_common common;
>> +};
>> +
>> +static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
>> +{
>> + return container_of(com, struct stm32_dac_priv, common);
>> +}
>> +
>> +static const struct regmap_config stm32_dac_regmap_cfg = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = sizeof(u32),
>> + .max_register = 0x3fc,
>> +};
>> +
>> +static int stm32_dac_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct stm32_dac_priv *priv;
>> + struct regmap *regmap;
>> + struct resource *res;
>> + void __iomem *mmio;
>> + int ret;
>> +
>> + if (!dev->of_node)
>> + return -ENODEV;
>> +
>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + mmio = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(mmio))
>> + return PTR_ERR(mmio);
>> +
>> + regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
>> + if (IS_ERR(regmap))
>> + return PTR_ERR(regmap);
>> + priv->common.regmap = regmap;
>> +
>> + priv->vref = devm_regulator_get(dev, "vref");
>> + if (IS_ERR(priv->vref)) {
>> + ret = PTR_ERR(priv->vref);
>> + dev_err(dev, "vref get failed, %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = regulator_enable(priv->vref);
>> + if (ret < 0) {
>> + dev_err(dev, "vref enable failed\n");
>> + return ret;
>> + }
>> +
>> + ret = regulator_get_voltage(priv->vref);
>> + if (ret < 0) {
>> + dev_err(dev, "vref get voltage failed, %d\n", ret);
>> + goto err_vref;
>> + }
>> + priv->common.vref_mv = ret / 1000;
>> + dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
>> +
>> + priv->pclk = devm_clk_get(dev, "pclk");
>> + if (IS_ERR(priv->pclk)) {
>> + ret = PTR_ERR(priv->pclk);
>> + dev_err(dev, "pclk get failed\n");
>> + goto err_vref;
>> + }
>> +
>> + ret = clk_prepare_enable(priv->pclk);
>> + if (ret < 0) {
>> + dev_err(dev, "pclk enable failed\n");
>> + goto err_vref;
>> + }
>> +
>> + priv->rst = devm_reset_control_get(dev, NULL);
>> + if (!IS_ERR(priv->rst)) {
>> + reset_control_assert(priv->rst);
>> + udelay(2);
>> + reset_control_deassert(priv->rst);
>> + }
>> +
>> + /* When clock speed is higher than 80MHz, set HFSEL */
>> + priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
>> + ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
>> + priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
>> + if (ret)
>> + goto err_pclk;
>> +
>> + platform_set_drvdata(pdev, &priv->common);
>> +
>> + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to populate DT children\n");
>> + goto err_pclk;
>> + }
>> +
>> + return 0;
>> +
>> +err_pclk:
>> + clk_disable_unprepare(priv->pclk);
>> +err_vref:
>> + regulator_disable(priv->vref);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_dac_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_dac_common *common = platform_get_drvdata(pdev);
>> + struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
>> +
>> + of_platform_depopulate(&pdev->dev);
>> + clk_disable_unprepare(priv->pclk);
>> + regulator_disable(priv->vref);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_dac_of_match[] = {
>> + { .compatible = "st,stm32h7-dac-core", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
>> +
>> +static struct platform_driver stm32_dac_driver = {
>> + .probe = stm32_dac_probe,
>> + .remove = stm32_dac_remove,
>> + .driver = {
>> + .name = "stm32-dac-core",
>> + .of_match_table = stm32_dac_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_dac_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:stm32-dac-core");
>> diff --git a/drivers/iio/dac/stm32-dac-core.h b/drivers/iio/dac/stm32-dac-core.h
>> new file mode 100644
>> index 0000000..daf0993
>> --- /dev/null
>> +++ b/drivers/iio/dac/stm32-dac-core.h
>> @@ -0,0 +1,51 @@
>> +/*
>> + * This file is part of STM32 DAC driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __STM32_DAC_CORE_H
>> +#define __STM32_DAC_CORE_H
>> +
>> +#include <linux/regmap.h>
>> +
>> +/* STM32 DAC registers */
>> +#define STM32_DAC_CR 0x00
>> +#define STM32_DAC_DHR12R1 0x08
>> +#define STM32_DAC_DHR12R2 0x14
>> +#define STM32_DAC_DOR1 0x2C
>> +#define STM32_DAC_DOR2 0x30
>> +
>> +/* STM32_DAC_CR bit fields */
>> +#define STM32_DAC_CR_EN1 BIT(0)
>> +#define STM32H7_DAC_CR_HFSEL BIT(15)
>> +#define STM32_DAC_CR_EN2 BIT(16)
>> +
>> +/**
>> + * struct stm32_dac_common - stm32 DAC driver common data (for all instances)
>> + * @regmap: DAC registers shared via regmap
>> + * @vref_mv: reference voltage (mv)
>> + * @hfsel: high speed bus clock selected
>> + */
>> +struct stm32_dac_common {
>> + struct regmap *regmap;
>> + int vref_mv;
>> + bool hfsel;
>> +};
>> +
>> +#endif
>> diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
>> new file mode 100644
>> index 0000000..c0d993a
>> --- /dev/null
>> +++ b/drivers/iio/dac/stm32-dac.c
>> @@ -0,0 +1,350 @@
>> +/*
>> + * This file is part of STM32 DAC driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Authors: Amelie Delaunay <amelie.delaunay@st.com>
>> + * Fabrice Gasnier <fabrice.gasnier@st.com>
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/delay.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "stm32-dac-core.h"
>> +
>> +#define STM32_DAC_CHANNEL_1 1
>> +#define STM32_DAC_CHANNEL_2 2
>> +#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
>> +
>> +/**
>> + * struct stm32_dac - private data of DAC driver
>> + * @common: reference to DAC common data
>> + */
>> +struct stm32_dac {
>> + struct stm32_dac_common *common;
>> +};
>> +
>> +static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> + u32 en, val;
>> + int ret;
>> +
>> + ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
>> + if (ret < 0)
>> + return ret;
>> + if (STM32_DAC_IS_CHAN_1(channel))
>> + en = FIELD_GET(STM32_DAC_CR_EN1, val);
>> + else
>> + en = FIELD_GET(STM32_DAC_CR_EN2, val);
>> +
>> + return !!en;
>> +}
>> +
>> +static int stm32_dac_enable(struct iio_dev *indio_dev, int channel)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> + u32 en = STM32_DAC_IS_CHAN_1(channel) ?
>> + STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
>> + int ret;
>> +
>> + ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, en, en);
>> + if (ret < 0) {
>> + dev_err(&indio_dev->dev, "Enable failed\n");
>> + return ret;
>> + }
>> +
>> + /*
>> + * When HFSEL is set, it is not allowed to write the DHRx register
>> + * during 8 clock cycles after the ENx bit is set. It is not allowed
>> + * to make software/hardware trigger during this period either.
>> + */
>> + if (dac->common->hfsel)
>> + udelay(1);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_dac_disable(struct iio_dev *indio_dev, int channel)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> + u32 en = STM32_DAC_IS_CHAN_1(channel) ?
>> + STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
>> + int ret;
>> +
>> + ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, en, 0);
>> + if (ret)
>> + dev_err(&indio_dev->dev, "Disable failed\n");
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
>> +{
>> + int ret;
>> +
>> + if (STM32_DAC_IS_CHAN_1(channel))
>> + ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
>> + else
>> + ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
>> +
>> + return ret ? ret : IIO_VAL_INT;
>> +}
>> +
>> +static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
>> +{
>> + int ret;
>> +
>> + if (STM32_DAC_IS_CHAN_1(channel))
>> + ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
>> + else
>> + ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_dac_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + return stm32_dac_get_value(dac, chan->channel, val);
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = dac->common->vref_mv;
>> + *val2 = chan->scan_type.realbits;
>> + return IIO_VAL_FRACTIONAL_LOG2;
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int stm32_dac_write_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int val, int val2, long mask)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + return stm32_dac_set_value(dac, chan->channel, val);
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
>> + unsigned reg, unsigned writeval,
>> + unsigned *readval)
>> +{
>> + struct stm32_dac *dac = iio_priv(indio_dev);
>> +
>> + if (!readval)
>> + return regmap_write(dac->common->regmap, reg, writeval);
>> + else
>> + return regmap_read(dac->common->regmap, reg, readval);
>> +}
>> +
>> +static const struct iio_info stm32_dac_iio_info = {
>> + .read_raw = stm32_dac_read_raw,
>> + .write_raw = stm32_dac_write_raw,
>> + .debugfs_reg_access = stm32_dac_debugfs_reg_access,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static const char * const stm32_dac_powerdown_modes[] = {
>> + "Hi-Z",
>> + "enable",
> I wouldn't expect to see enable in here, that is handled by the power down
> attribute.
>
> Hi-Z is the currently defined threestate I think?
>
> Documenation/ABI/testing/sysfs-bus-iio
Yes, documentation states: "three_state: left floating".
Thanks for pointing that out.
>
> Fine to propose additions to this but they need documenting in that general
> file.
>> +};
>> +
>> +static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan)
>> +{
>> + return stm32_dac_is_enabled(indio_dev, chan->channel);
>> +}
>> +
>> +static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan,
>> + unsigned int type)
>> +{
>> + if (type)
>> + return stm32_dac_enable(indio_dev, chan->channel);
>> + else
>> + return stm32_dac_disable(indio_dev, chan->channel);
>> +}
>> +
>> +static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + const struct iio_chan_spec *chan,
>> + char *buf)
>> +{
>> + int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
>> +
>> + if (ret < 0)
>> + return ret;
>> +
>> + return sprintf(buf, "%d\n", ret);
>> +}
>> +
>> +static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + const struct iio_chan_spec *chan,
>> + const char *buf, size_t len)
>> +{
>> + unsigned int en;
>> + int ret;
>> +
>> + ret = kstrtouint(buf, 0, &en);
> strtobool perhaps?
Done in v3. I also reworked a little all around powerdown mode.
Thanks,
Fabrice
>> + if (ret)
>> + return ret;
>> +
>> + ret = stm32_dac_set_powerdown_mode(indio_dev, chan, en);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return len;
>> +}
>> +
>> +static const struct iio_enum stm32_dac_powerdown_mode_en = {
>> + .items = stm32_dac_powerdown_modes,
>> + .num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
>> + .get = stm32_dac_get_powerdown_mode,
>> + .set = stm32_dac_set_powerdown_mode,
>> +};
>> +
>> +static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
>> + {
>> + .name = "powerdown",
>> + .read = stm32_dac_read_powerdown,
>> + .write = stm32_dac_write_powerdown,
>> + .shared = IIO_SEPARATE,
>> + },
>> + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
>> + IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
>> + {},
>> +};
>> +
>> +#define STM32_DAC_CHANNEL(chan, name) { \
>> + .type = IIO_VOLTAGE, \
>> + .indexed = 1, \
>> + .output = 1, \
>> + .channel = chan, \
>> + .info_mask_separate = \
>> + BIT(IIO_CHAN_INFO_RAW) | \
>> + BIT(IIO_CHAN_INFO_SCALE), \
>> + /* scan_index is always 0 as num_channels is 1 */ \
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .realbits = 12, \
>> + .storagebits = 16, \
>> + }, \
>> + .datasheet_name = name, \
>> + .ext_info = stm32_dac_ext_info \
>> +}
>> +
>> +static const struct iio_chan_spec stm32_dac_channels[] = {
>> + STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
>> + STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
>> +};
>> +
>> +static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
>> +{
>> + struct device_node *np = indio_dev->dev.of_node;
>> + unsigned int i;
>> + u32 channel;
>> + int ret;
>> +
>> + ret = of_property_read_u32(np, "reg", &channel);
>> + if (ret) {
>> + dev_err(&indio_dev->dev, "Failed to read reg property\n");
>> + return ret;
>> + }
>> +
>> + for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
>> + if (stm32_dac_channels[i].channel == channel)
>> + break;
>> + }
>> + if (i >= ARRAY_SIZE(stm32_dac_channels)) {
>> + dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
>> + return -EINVAL;
>> + }
>> +
>> + indio_dev->channels = &stm32_dac_channels[i];
>> + /*
>> + * Expose only one channel here, as they can be used independently,
>> + * with separate trigger. Then separate IIO devices are instantiated
>> + * to manage this.
>> + */
>> + indio_dev->num_channels = 1;
>> +
>> + return 0;
>> +};
>> +
>> +static int stm32_dac_probe(struct platform_device *pdev)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + struct iio_dev *indio_dev;
>> + struct stm32_dac *dac;
>> + int ret;
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> + platform_set_drvdata(pdev, indio_dev);
>> +
>> + dac = iio_priv(indio_dev);
>> + dac->common = dev_get_drvdata(pdev->dev.parent);
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &stm32_dac_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + ret = stm32_dac_chan_of_init(indio_dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return devm_iio_device_register(&pdev->dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id stm32_dac_of_match[] = {
>> + { .compatible = "st,stm32-dac", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
>> +
>> +static struct platform_driver stm32_dac_driver = {
>> + .probe = stm32_dac_probe,
>> + .driver = {
>> + .name = "stm32-dac",
>> + .of_match_table = stm32_dac_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_dac_driver);
>> +
>> +MODULE_ALIAS("platform:stm32-dac");
>> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
>> +MODULE_LICENSE("GPL v2");
>>
>
^ permalink raw reply
* Re: [PATCHv3 02/10] serdev: add serdev_device_wait_until_sent
From: Rob Herring @ 2017-04-10 16:12 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Sebastian Reichel, Marcel Holtmann, Gustavo Padovan,
Johan Hedberg, Samuel Thibault, Pavel Machek, Tony Lindgren,
Jiri Slaby, Mark Rutland, open list:BLUETOOTH DRIVERS,
linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andrey Smirnov
In-Reply-To: <20170410140313.GA31894-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
+ Andrey
On Mon, Apr 10, 2017 at 9:03 AM, Greg Kroah-Hartman
<gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org> wrote:
> On Mon, Apr 10, 2017 at 08:46:57AM -0500, Rob Herring wrote:
>> On Sat, Apr 8, 2017 at 11:57 AM, Greg Kroah-Hartman
>> <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org> wrote:
>> > On Tue, Mar 28, 2017 at 05:59:31PM +0200, Sebastian Reichel wrote:
>> >> Add method, which waits until the transmission buffer has been sent.
>> >> Note, that the change in ttyport_write_wakeup is related, since
>> >> tty_wait_until_sent will hang without that change.
>> >>
>> >> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> >> Acked-by: Pavel Machek <pavel-+ZI9xUNit7I@public.gmane.org>
>> >> Signed-off-by: Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> >> ---
>> >> Changes since PATCHv2:
>> >> * Avoid goto in ttyport_write_wakeup
>> >> ---
>> >> drivers/tty/serdev/core.c | 11 +++++++++++
>> >> drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++----
>> >> include/linux/serdev.h | 3 +++
>> >> 3 files changed, 28 insertions(+), 4 deletions(-)
>> >>
>> >> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
>> >> index f4c6c90add78..a63b74031e22 100644
>> >> --- a/drivers/tty/serdev/core.c
>> >> +++ b/drivers/tty/serdev/core.c
>> >> @@ -173,6 +173,17 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
>> >> }
>> >> EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
>> >>
>> >> +void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout)
>> >> +{
>> >> + struct serdev_controller *ctrl = serdev->ctrl;
>> >> +
>> >> + if (!ctrl || !ctrl->ops->wait_until_sent)
>> >> + return;
>> >> +
>> >> + ctrl->ops->wait_until_sent(ctrl, timeout);
>> >> +}
>> >> +EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent);
>> >
>> > Is this still needed now that we have serdev_device_write() with an
>> > unlimited timeout available?
>>
>> Yes, because only this waits until the data is on the wire.
>
> What "wire" is that? The serial wire? How do you know this? Many usb
> to serial devices have no way to determine this, given that there is
> another uart hanging off of the end of a USB connection.
Okay, maybe it's just out of linux s/w buffers for h/w which you don't
know. It is the same semantics as tty_wait_until_sent which is
documented as: "Wait for characters pending in a tty driver to hit the
wire, or for a timeout to occur (eg due to flow control)"
> Doesn't serdev_device_write() return when the write is finished?
No, it returns when data is accepted by the tty layer (as serdev has
no buffering of its own).
> I
> think we need some good documentation here for all of the different
> variants of how to send data, as I'm sure confused...
Fair enough. However, I think that's somewhat orthogonal to this function.
There's 2 modes of sending data which are basically sync and async operation.
- serdev_device_write_buf - Only sends what can be immediately
accepted by lower layers. Caller should provide write_wakeup callback
to be notified.
- serdev_device_write - Sleeps until all data is accepted by lower
layers or a timeout occurs.
It's valid to call serdev_device_wait_until_sent in either case.
Yes, the naming is not the best, but we kept serdev_device_write_buf
to avoid any cross tree merges. We can remove or rename later as it is
just a wrapper now for serdev_device_write.
Rob
^ permalink raw reply
* [git:media_tree/master] [media] ARM: dts: exynos: add HDMI controller phandle to exynos4.dtsi
From: Mauro Carvalho Chehab @ 2017-04-10 16:12 UTC (permalink / raw)
To: linuxtv-commits-dJidKbW2IEtAfugRpC6u6w
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil,
Krzysztof Kozlowski, Marek Szyprowski
This is an automatic generated email to let you know that the following patch were queued:
Subject: [media] ARM: dts: exynos: add HDMI controller phandle to exynos4.dtsi
Author: Hans Verkuil <hans.verkuil@cisco.com>
Date: Tue Dec 13 12:37:16 2016 -0200
Add the new hdmi phandle to exynos4.dtsi. This phandle is needed by the
s5p-cec driver to initialize the CEC notifier framework.
Tested with my Odroid U3.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: linux-samsung-soc@vger.kernel.org
CC: devicetree@vger.kernel.org
CC: Krzysztof Kozlowski <krzk@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
arch/arm/boot/dts/exynos4.dtsi | 1 +
1 file changed, 1 insertion(+)
---
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 18def1c774d5..84fcdff140ae 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -771,6 +771,7 @@
clocks = <&clock CLK_HDMI_CEC>;
clock-names = "hdmicec";
samsung,syscon-phandle = <&pmu_system_controller>;
+ hdmi-phandle = <&hdmi>;
pinctrl-names = "default";
pinctrl-0 = <&hdmi_cec>;
status = "disabled";
_______________________________________________
linuxtv-commits mailing list
linuxtv-commits@linuxtv.org
https://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox