* Re: [PATCH kernel] vfio-pci/nvlink2: Fix ancient gcc warnings
From: Alex Williamson @ 2019-01-23 4:30 UTC (permalink / raw)
To: Alexey Kardashevskiy, Geert Uytterhoeven
Cc: linuxppc-dev, linux-kernel@vger.kernel.org, kvm
In-Reply-To: <20190123040711.21759-1-aik@ozlabs.ru>
Hi Geert,
The below patch comes about from the build regressions and improvements
list you've sent out, but something doesn't add up that we'd be testing
with an old compiler where initialization with { 0 } generates a
"missing braces around initialization" warning. Is this really the
case or are we missing something here? There's no harm that I can see
with Alexey's fix, but are these really just false positives from a
compiler bug that we should selectively ignore if the "fix" is less
clean? Thanks,
Alex
On Wed, 23 Jan 2019 15:07:11 +1100
Alexey Kardashevskiy <aik@ozlabs.ru> wrote:
> Using the {0} construct as a generic initializer is perfectly fine in C,
> however due to a bug in old gcc there is a warning:
>
> + /kisskb/src/drivers/vfio/pci/vfio_pci_nvlink2.c: warning: (near
> initialization for 'cap.header') [-Wmissing-braces]: => 181:9
>
> Since for whatever reason we still want to compile the modern kernel
> with such an old gcc without warnings, this changes the capabilities
> initialization.
>
> The gcc bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> drivers/vfio/pci/vfio_pci_nvlink2.c | 30 ++++++++++++++---------------
> 1 file changed, 15 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/vfio/pci/vfio_pci_nvlink2.c b/drivers/vfio/pci/vfio_pci_nvlink2.c
> index 054a2cf..91d945b 100644
> --- a/drivers/vfio/pci/vfio_pci_nvlink2.c
> +++ b/drivers/vfio/pci/vfio_pci_nvlink2.c
> @@ -178,11 +178,11 @@ static int vfio_pci_nvgpu_add_capability(struct vfio_pci_device *vdev,
> struct vfio_pci_region *region, struct vfio_info_cap *caps)
> {
> struct vfio_pci_nvgpu_data *data = region->data;
> - struct vfio_region_info_cap_nvlink2_ssatgt cap = { 0 };
> -
> - cap.header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT;
> - cap.header.version = 1;
> - cap.tgt = data->gpu_tgt;
> + struct vfio_region_info_cap_nvlink2_ssatgt cap = {
> + .header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT,
> + .header.version = 1,
> + .tgt = data->gpu_tgt
> + };
>
> return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
> }
> @@ -365,18 +365,18 @@ static int vfio_pci_npu2_add_capability(struct vfio_pci_device *vdev,
> struct vfio_pci_region *region, struct vfio_info_cap *caps)
> {
> struct vfio_pci_npu2_data *data = region->data;
> - struct vfio_region_info_cap_nvlink2_ssatgt captgt = { 0 };
> - struct vfio_region_info_cap_nvlink2_lnkspd capspd = { 0 };
> + struct vfio_region_info_cap_nvlink2_ssatgt captgt = {
> + .header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT,
> + .header.version = 1,
> + .tgt = data->gpu_tgt
> + };
> + struct vfio_region_info_cap_nvlink2_lnkspd capspd = {
> + .header.id = VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD,
> + .header.version = 1,
> + .link_speed = data->link_speed
> + };
> int ret;
>
> - captgt.header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT;
> - captgt.header.version = 1;
> - captgt.tgt = data->gpu_tgt;
> -
> - capspd.header.id = VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD;
> - capspd.header.version = 1;
> - capspd.link_speed = data->link_speed;
> -
> ret = vfio_info_add_capability(caps, &captgt.header, sizeof(captgt));
> if (ret)
> return ret;
^ permalink raw reply
* [PATCH kernel] vfio-pci/nvlink2: Fix ancient gcc warnings
From: Alexey Kardashevskiy @ 2019-01-23 4:07 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Alex Williamson, kvm
Using the {0} construct as a generic initializer is perfectly fine in C,
however due to a bug in old gcc there is a warning:
+ /kisskb/src/drivers/vfio/pci/vfio_pci_nvlink2.c: warning: (near
initialization for 'cap.header') [-Wmissing-braces]: => 181:9
Since for whatever reason we still want to compile the modern kernel
with such an old gcc without warnings, this changes the capabilities
initialization.
The gcc bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
drivers/vfio/pci/vfio_pci_nvlink2.c | 30 ++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci_nvlink2.c b/drivers/vfio/pci/vfio_pci_nvlink2.c
index 054a2cf..91d945b 100644
--- a/drivers/vfio/pci/vfio_pci_nvlink2.c
+++ b/drivers/vfio/pci/vfio_pci_nvlink2.c
@@ -178,11 +178,11 @@ static int vfio_pci_nvgpu_add_capability(struct vfio_pci_device *vdev,
struct vfio_pci_region *region, struct vfio_info_cap *caps)
{
struct vfio_pci_nvgpu_data *data = region->data;
- struct vfio_region_info_cap_nvlink2_ssatgt cap = { 0 };
-
- cap.header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT;
- cap.header.version = 1;
- cap.tgt = data->gpu_tgt;
+ struct vfio_region_info_cap_nvlink2_ssatgt cap = {
+ .header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT,
+ .header.version = 1,
+ .tgt = data->gpu_tgt
+ };
return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
}
@@ -365,18 +365,18 @@ static int vfio_pci_npu2_add_capability(struct vfio_pci_device *vdev,
struct vfio_pci_region *region, struct vfio_info_cap *caps)
{
struct vfio_pci_npu2_data *data = region->data;
- struct vfio_region_info_cap_nvlink2_ssatgt captgt = { 0 };
- struct vfio_region_info_cap_nvlink2_lnkspd capspd = { 0 };
+ struct vfio_region_info_cap_nvlink2_ssatgt captgt = {
+ .header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT,
+ .header.version = 1,
+ .tgt = data->gpu_tgt
+ };
+ struct vfio_region_info_cap_nvlink2_lnkspd capspd = {
+ .header.id = VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD,
+ .header.version = 1,
+ .link_speed = data->link_speed
+ };
int ret;
- captgt.header.id = VFIO_REGION_INFO_CAP_NVLINK2_SSATGT;
- captgt.header.version = 1;
- captgt.tgt = data->gpu_tgt;
-
- capspd.header.id = VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD;
- capspd.header.version = 1;
- capspd.link_speed = data->link_speed;
-
ret = vfio_info_add_capability(caps, &captgt.header, sizeof(captgt));
if (ret)
return ret;
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v9 00/22] Re-use nvram module
From: Finn Thain @ 2019-01-22 22:06 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Michael Schmitz, linux-fbdev, Arnd Bergmann, Martin K. Petersen,
Bartlomiej Zolnierkiewicz, James E.J. Bottomley, linux-kernel,
dri-devel, linux-scsi, linux-m68k, Geert Uytterhoeven,
Paul Mackerras, linuxppc-dev, Joshua Thompson
In-Reply-To: <20190122092234.GA15813@kroah.com>
On Tue, 22 Jan 2019, Greg Kroah-Hartman wrote:
> On Tue, Jan 15, 2019 at 03:18:56PM +1100, Finn Thain wrote:
> > The "generic" NVRAM module, drivers/char/generic_nvram.c, implements a
> > /dev/nvram misc device. This module is used only by 32-bit PowerPC
> > platforms.
> >
> > The RTC "CMOS" NVRAM module, drivers/char/nvram.c, also implements a
> > /dev/nvram misc device. This module is now used only by x86 and m68k
> > thanks to commit 3ba9faedc180 ("char: nvram: disable on ARM").
> >
> > The "generic" module cannot be used by x86 or m68k platforms because it
> > cannot co-exist with the "CMOS" module. One reason for that is the
> > CONFIG_GENERIC_NVRAM kludge in drivers/char/Makefile. Another reason is
> > that automatically loading the appropriate module would be impossible
> > because only one module can provide the char-major-10-144 alias.
> >
> > A multi-platform kernel binary needs a single, generic module. With this
> > patch series, drivers/char/nvram.c becomes more generic and some of the
> > arch-specific code gets moved under arch/. The nvram module is then
> > usable by all m68k, powerpc and x86 platforms.
> >
> > This allows for removal of drivers/char/generic_nvram.c as well as a
> > duplicate in arch/powerpc/kernel/nvram_64.c. By reducing the number of
> > /dev/nvram char misc device implementations, the number of bugs and
> > inconsistencies is also reduced.
> >
> > This approach reduces inconsistencies between PPC32 and PPC64 and also
> > between PPC_PMAC and MAC. A uniform API has benefits for userspace.
> >
> > For example, some error codes for some ioctl calls become consistent
> > across PowerPC platforms. The uniform API can potentially benefit any
> > bootloader that works across the various platforms having XPRAM
> > (e.g. Emile).
> >
> > This patch series was tested on Atari, Mac, PowerMac (both 32-bit and
> > 64-bit) and ThinkPad hardware. AFAIK, it has not yet been tested on
> > pSeries or CHRP.
> >
> > I think there are two possible merge strategies for this patch series.
> > The char misc maintainer could take the entire series. Alternatively,
> > the m68k maintainer could take patches 1 thru 16 (though some of these
> > have nothing to do with m68k) and after those patches reach mainline
> > the powerpc maintainer could take 17 thru 22.
>
> I just took the whole series, thanks for doing this, looks good.
>
Thanks, Greg.
I haven't seen any acks from powerpc maintainers yet...
--
> greg k-h
>
^ permalink raw reply
* Re: [PATCH] PCI: Use of_node_name_eq for node name comparisons
From: Bjorn Helgaas @ 2019-01-22 20:20 UTC (permalink / raw)
To: Rob Herring
Cc: devicetree, linux-pci, linux-kernel, Michael Bringmann,
Paul Mackerras, linuxppc-dev
In-Reply-To: <20181205195050.4759-18-robh@kernel.org>
[+cc Michael B]
On Wed, Dec 05, 2018 at 01:50:34PM -0600, Rob Herring wrote:
> Convert string compares of DT node names to use of_node_name_eq helper
> instead. This removes direct access to the node name pointer.
>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-pci@vger.kernel.org
> Signed-off-by: Rob Herring <robh@kernel.org>
I applied the pci/of.c change to pci/misc for v5.1, thanks!
I dropped the rpaphp_core.c part because there's another patch
in-flight [1] that touches the same code, and it would be easier if
Michael picked up this change and added it to his series.
Bjorn
[1] https://lore.kernel.org/lkml/20181214205120.16435.46952.stgit@powerkvm6.aus.stglabs.ibm.com/
> ---
> drivers/pci/hotplug/rpaphp_core.c | 2 +-
> drivers/pci/of.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
> index bcd5d357ca23..7697d9c92b98 100644
> --- a/drivers/pci/hotplug/rpaphp_core.c
> +++ b/drivers/pci/hotplug/rpaphp_core.c
> @@ -354,7 +354,7 @@ int rpaphp_add_slot(struct device_node *dn)
> const int *indexes, *names, *types, *power_domains;
> char *name, *type;
>
> - if (!dn->name || strcmp(dn->name, "pci"))
> + if (!of_node_name_eq(dn, "pci"))
> return 0;
>
> /* If this is not a hotplug slot, return without doing anything. */
> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
> index 4c4217d0c3f1..3d32da15c215 100644
> --- a/drivers/pci/of.c
> +++ b/drivers/pci/of.c
> @@ -113,7 +113,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
> * a fake root for all functions of a multi-function
> * device we go down them as well.
> */
> - if (!strcmp(node->name, "multifunc-device")) {
> + if (of_node_name_eq(node, "multifunc-device")) {
> for_each_child_of_node(node, node2) {
> if (__of_pci_pci_compare(node2, devfn)) {
> of_node_put(node);
> --
> 2.19.1
>
^ permalink raw reply
* Re: [RFC 5/6] powerpc/pci/hotplug: Use common drcinfo parsing
From: Bjorn Helgaas @ 2019-01-22 19:58 UTC (permalink / raw)
To: Michael Bringmann
Cc: Thomas Falcon, linux-pci, linux-kernel, Juliet Kim,
Paul Mackerras, Tyrel Datwyler, linuxppc-dev
In-Reply-To: <20190115002857.GG33971@google.com>
On Mon, Jan 14, 2019 at 06:28:57PM -0600, Bjorn Helgaas wrote:
> On Fri, Dec 14, 2018 at 02:51:31PM -0600, Michael Bringmann wrote:
> > The implementation of the pseries-specific drc info properties
> > is currently implemented in pseries-specific and non-pseries-specific
> > files. This patch set uses a new implementation of the device-tree
> > parsing code for the properties.
> >
> > This patch refactors parsing of the pseries-specific drc-info properties
> > out of rpaphp_core.c to use the common parser. In the case where an
> > architecture does not use these properties, an __weak copy of the
> > function is provided with dummy return values. Changes include creating
> > appropriate callback functions and passing callback-specific data
> > blocks into arch_find_drc_match. All functions that were used just
> > to support the previous parsing implementation have been moved.
> >
> > Signed-off-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
>
> This is fine with me. Any rpaphp_core.c maintainers want to comment?
> Tyrel?
>
> $ ./scripts/get_maintainer.pl -f drivers/pci/hotplug/rpaphp_core.c
> Tyrel Datwyler <tyreld@linux.vnet.ibm.com> (supporter:IBM Power PCI Hotplug Driver for RPA-compliant...)
> Benjamin Herrenschmidt <benh@kernel.crashing.org> (supporter:LINUX FOR POWERPC (32-BIT AND 64-BIT))
> Paul Mackerras <paulus@samba.org> (supporter:LINUX FOR POWERPC (32-BIT AND 64-BIT))
> Michael Ellerman <mpe@ellerman.id.au> (supporter:LINUX FOR POWERPC (32-BIT AND 64-BIT))
This looks like part of a larger series, but I only got this patch. So I
assume you'll route this elsewhere along with the rest of the series.
Here's my ack if it's useful:
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
> > ---
> > drivers/pci/hotplug/rpaphp_core.c | 232 ++++---------------------------------
> > 1 file changed, 28 insertions(+), 204 deletions(-)
> >
> > diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
> > index bcd5d35..9ad7384 100644
> > --- a/drivers/pci/hotplug/rpaphp_core.c
> > +++ b/drivers/pci/hotplug/rpaphp_core.c
> > @@ -154,182 +154,18 @@ static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
> > return speed;
> > }
> >
> > -static int get_children_props(struct device_node *dn, const int **drc_indexes,
> > - const int **drc_names, const int **drc_types,
> > - const int **drc_power_domains)
> > -{
> > - const int *indexes, *names, *types, *domains;
> > -
> > - indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
> > - names = of_get_property(dn, "ibm,drc-names", NULL);
> > - types = of_get_property(dn, "ibm,drc-types", NULL);
> > - domains = of_get_property(dn, "ibm,drc-power-domains", NULL);
> > -
> > - if (!indexes || !names || !types || !domains) {
> > - /* Slot does not have dynamically-removable children */
> > - return -EINVAL;
> > - }
> > - if (drc_indexes)
> > - *drc_indexes = indexes;
> > - if (drc_names)
> > - /* &drc_names[1] contains NULL terminated slot names */
> > - *drc_names = names;
> > - if (drc_types)
> > - /* &drc_types[1] contains NULL terminated slot types */
> > - *drc_types = types;
> > - if (drc_power_domains)
> > - *drc_power_domains = domains;
> > -
> > - return 0;
> > -}
> > -
> > -
> > /* Verify the existence of 'drc_name' and/or 'drc_type' within the
> > - * current node. First obtain it's my-drc-index property. Next,
> > - * obtain the DRC info from it's parent. Use the my-drc-index for
> > - * correlation, and obtain/validate the requested properties.
> > + * current node.
> > */
> >
> > -static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name,
> > - char *drc_type, unsigned int my_index)
> > -{
> > - char *name_tmp, *type_tmp;
> > - const int *indexes, *names;
> > - const int *types, *domains;
> > - int i, rc;
> > -
> > - rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
> > - if (rc < 0) {
> > - return -EINVAL;
> > - }
> > -
> > - name_tmp = (char *) &names[1];
> > - type_tmp = (char *) &types[1];
> > -
> > - /* Iterate through parent properties, looking for my-drc-index */
> > - for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
> > - if ((unsigned int) indexes[i + 1] == my_index)
> > - break;
> > -
> > - name_tmp += (strlen(name_tmp) + 1);
> > - type_tmp += (strlen(type_tmp) + 1);
> > - }
> > -
> > - if (((drc_name == NULL) || (drc_name && !strcmp(drc_name, name_tmp))) &&
> > - ((drc_type == NULL) || (drc_type && !strcmp(drc_type, type_tmp))))
> > - return 0;
> > -
> > - return -EINVAL;
> > -}
> > -
> > -static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,
> > - char *drc_type, unsigned int my_index)
> > -{
> > - struct property *info;
> > - unsigned int entries;
> > - struct of_drc_info drc;
> > - const __be32 *value;
> > - char cell_drc_name[MAX_DRC_NAME_LEN];
> > - int j, fndit;
> > -
> > - info = of_find_property(dn->parent, "ibm,drc-info", NULL);
> > - if (info == NULL)
> > - return -EINVAL;
> > -
> > - value = of_prop_next_u32(info, NULL, &entries);
> > - if (!value)
> > - return -EINVAL;
> > -
> > - for (j = 0; j < entries; j++) {
> > - of_read_drc_info_cell(&info, &value, &drc);
> > -
> > - /* Should now know end of current entry */
> > -
> > - if (my_index > drc.last_drc_index)
> > - continue;
> > -
> > - fndit = 1;
> > - break;
> > - }
> > - /* Found it */
> > -
> > - if (fndit)
> > - sprintf(cell_drc_name, "%s%d", drc.drc_name_prefix,
> > - my_index);
> > -
> > - if (((drc_name == NULL) ||
> > - (drc_name && !strcmp(drc_name, cell_drc_name))) &&
> > - ((drc_type == NULL) ||
> > - (drc_type && !strcmp(drc_type, drc.drc_type))))
> > - return 0;
> > -
> > - return -EINVAL;
> > -}
> > -
> > int rpaphp_check_drc_props(struct device_node *dn, char *drc_name,
> > char *drc_type)
> > {
> > - const unsigned int *my_index;
> > -
> > - my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
> > - if (!my_index) {
> > - /* Node isn't DLPAR/hotplug capable */
> > - return -EINVAL;
> > - }
> > -
> > - if (firmware_has_feature(FW_FEATURE_DRC_INFO))
> > - return rpaphp_check_drc_props_v2(dn, drc_name, drc_type,
> > - *my_index);
> > - else
> > - return rpaphp_check_drc_props_v1(dn, drc_name, drc_type,
> > - *my_index);
> > + return arch_find_drc_match(dn, NULL, drc_type, drc_name,
> > + true, false, NULL);
> > }
> > EXPORT_SYMBOL_GPL(rpaphp_check_drc_props);
> >
> > -
> > -static int is_php_type(char *drc_type)
> > -{
> > - unsigned long value;
> > - char *endptr;
> > -
> > - /* PCI Hotplug nodes have an integer for drc_type */
> > - value = simple_strtoul(drc_type, &endptr, 10);
> > - if (endptr == drc_type)
> > - return 0;
> > -
> > - return 1;
> > -}
> > -
> > -/**
> > - * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0
> > - * @dn: target &device_node
> > - * @indexes: passed to get_children_props()
> > - * @names: passed to get_children_props()
> > - * @types: returned from get_children_props()
> > - * @power_domains:
> > - *
> > - * This routine will return true only if the device node is
> > - * a hotpluggable slot. This routine will return false
> > - * for built-in pci slots (even when the built-in slots are
> > - * dlparable.)
> > - */
> > -static int is_php_dn(struct device_node *dn, const int **indexes,
> > - const int **names, const int **types, const int **power_domains)
> > -{
> > - const int *drc_types;
> > - int rc;
> > -
> > - rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
> > - if (rc < 0)
> > - return 0;
> > -
> > - if (!is_php_type((char *) &drc_types[1]))
> > - return 0;
> > -
> > - *types = drc_types;
> > - return 1;
> > -}
> > -
> > /**
> > * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
> > * @dn: device node of slot
> > @@ -346,54 +182,42 @@ static int is_php_dn(struct device_node *dn, const int **indexes,
> > *
> > * To remove a slot, it suffices to call rpaphp_deregister_slot().
> > */
> > -int rpaphp_add_slot(struct device_node *dn)
> > +
> > +static int rpaphp_add_slot_cb(struct device_node *dn,
> > + u32 drc_index, char *drc_name, char *drc_type,
> > + u32 drc_power_domain, void *data)
> > {
> > struct slot *slot;
> > int retval = 0;
> > - int i;
> > - const int *indexes, *names, *types, *power_domains;
> > - char *name, *type;
> > -
> > - if (!dn->name || strcmp(dn->name, "pci"))
> > - return 0;
> > -
> > - /* If this is not a hotplug slot, return without doing anything. */
> > - if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
> > - return 0;
> > -
> > - dbg("Entry %s: dn=%pOF\n", __func__, dn);
> >
> > - /* register PCI devices */
> > - name = (char *) &names[1];
> > - type = (char *) &types[1];
> > - for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
> > - int index;
> > + slot = alloc_slot_struct(dn, drc_index, drc_name, drc_power_domain);
> > + if (!slot)
> > + return -ENOMEM;
> >
> > - index = be32_to_cpu(indexes[i + 1]);
> > - slot = alloc_slot_struct(dn, index, name,
> > - be32_to_cpu(power_domains[i + 1]));
> > - if (!slot)
> > - return -ENOMEM;
> > + slot->type = simple_strtoul(drc_type, NULL, 10);
> > + if (retval)
> > + return -EINVAL;
> >
> > - slot->type = simple_strtoul(type, NULL, 10);
> > + dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
> > + drc_index, drc_name, drc_type);
> >
> > - dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
> > - index, name, type);
> > + retval = rpaphp_enable_slot(slot);
> > + if (!retval)
> > + retval = rpaphp_register_slot(slot);
> >
> > - retval = rpaphp_enable_slot(slot);
> > - if (!retval)
> > - retval = rpaphp_register_slot(slot);
> > + if (retval)
> > + dealloc_slot_struct(slot);
> >
> > - if (retval)
> > - dealloc_slot_struct(slot);
> > + return retval;
> > +}
> >
> > - name += strlen(name) + 1;
> > - type += strlen(type) + 1;
> > - }
> > - dbg("%s - Exit: rc[%d]\n", __func__, retval);
> > +int rpaphp_add_slot(struct device_node *dn)
> > +{
> > + if (!dn->name || strcmp(dn->name, "pci"))
> > + return 0;
> >
> > - /* XXX FIXME: reports a failure only if last entry in loop failed */
> > - return retval;
> > + return arch_find_drc_match(dn, rpaphp_add_slot_cb,
> > + NULL, NULL, false, true, NULL);
> > }
> > EXPORT_SYMBOL_GPL(rpaphp_add_slot);
> >
> >
^ permalink raw reply
* Re: [PATCH v13 00/10] powerpc: Switch to CONFIG_THREAD_INFO_IN_TASK
From: Christophe Leroy @ 2019-01-22 19:42 UTC (permalink / raw)
To: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
Nicholas Piggin, Mike Rapoport
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <87zhrx2bf1.fsf@concordia.ellerman.id.au>
Le 19/01/2019 à 11:23, Michael Ellerman a écrit :
> Christophe Leroy <christophe.leroy@c-s.fr> writes:
>
>> The purpose of this serie is to activate CONFIG_THREAD_INFO_IN_TASK which
>> moves the thread_info into task_struct.
>>
>> Moving thread_info into task_struct has the following advantages:
>> - It protects thread_info from corruption in the case of stack
>> overflows.
>> - Its address is harder to determine if stack addresses are
>> leaked, making a number of attacks more difficult.
>>
>> Changes since v12:
>> - Patch 1: Taken comment from Mike (re-introduced the 'panic' in case memblock allocation fails in setup_64.c
>> - Patch 1: Added alloc_stack() function in setup_32.c to also panic in case of allocation failure.
>
> Hi Christophe,
>
> I can't get this series to boot on qemu mac99. I'm getting eg:
>
> [ 0.981514] NFS: Registering the id_resolver key type
> [ 0.981752] Key type id_resolver registered
> [ 0.981868] Key type id_legacy registered
> [ 0.995711] Unrecoverable exception 0 at 0 (msr=0)
> [ 0.996091] Oops: Unrecoverable exception, sig: 6 [#1]
> [ 0.996314] BE PAGE_SIZE=4K MMU=Hash PowerMac
> [ 0.996617] Modules linked in:
> [ 0.996869] CPU: 0 PID: 416 Comm: modprobe Not tainted 5.0.0-rc2-gcc-7.3.0-00043-g53f2de798792 #342
> [ 0.997138] NIP: 00000000 LR: 00000000 CTR: 00000000
> [ 0.997309] REGS: ef237f50 TRAP: 0000 Not tainted (5.0.0-rc2-gcc-7.3.0-00043-g53f2de798792)
> [ 0.997508] MSR: 00000000 <> CR: 00000000 XER: 00000000
> [ 0.997712]
> [ 0.997712] GPR00: 00000000 ef238000 00000000 00000000 00000000 00000000 00000000 00000000
> [ 0.997712] GPR08: 00000000 00000000 00000000 00000000 00000000 00000000 c006477c ef13d8c0
> [ 0.997712] GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> [ 0.997712] GPR24: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> [ 0.998671] NIP [00000000] (null)
> [ 0.998774] LR [00000000] (null)
> [ 0.998895] Call Trace:
> [ 0.999030] Instruction dump:
> [ 0.999320] XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
> [ 0.999546] XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX 60000000 XXXXXXXX XXXXXXXX XXXXXXXX
> [ 1.000023] ---[ end trace 925ea3419844fe68 ]---
No such issue on my side. Do you have a ramdisk with anythink special or
a special config ? I see your kernel is modprobing something, know what
it is ?
Especially, what is the amount of memory in your config ? On my side
there is 128M:
Memory: 120292K/131072K available (6116K kernel code, 572K rwdata, 2036K
rodata,
340K init, 191K bss, 10780K reserved, 0K cma-reserved, 0K highmem)
Kernel virtual memory layout:
* 0xfffcf000..0xfffff000 : fixmap
* 0xff800000..0xffc00000 : highmem PTEs
* 0xfef36000..0xff800000 : early ioremap
* 0xc9000000..0xfef36000 : vmalloc & ioremap
This means the addresses in which your kernel faults are in vmalloc
area. It's not normal I believe, is it ?
On my side it gives the following on qemu mac99 (I have no disk).
[...]
Initialise system trusted keyrings
workingset: timestamp_bits=30 max_order=15 bucket_order=0
NFS: Registering the id_resolver key type
Key type id_resolver registered
Key type id_legacy registered
Key type asymmetric registered
Asymmetric key parser 'x509' registered
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler mq-deadline registered
io scheduler kyber registered
Using unsupported 800x600 (null) at 81000000, depth=32, pitch=3200
Console: switching to colour frame buffer device 100x37
fb0: Open Firmware frame buffer device on /pci@f2000000/QEMU,VGA@e
Generic non-volatile memory driver v1.1
brd: module loaded
loop: module loaded
MacIO PCI driver attached to Keylargo chipset
Macintosh Cuda and Egret driver.
mesh: configured for synchronous 5 MB/s
st: Version 20160209, fixed bufsize 32768, s/g segs 256
Detected ADB keyboard, type ANSI.
input: ADB keyboard as /devices/virtual/input/input0
random: fast init done
input: ADB mouse as /devices/virtual/input/input1
pata-macio 0.00020000:ata-3: Activating pata-macio chipset KeyLargo
ATA-3, Apple
bus ID 0
scsi host0: pata_macio
ata1: PATA max MWDMA2 irq 16
pata-macio 0.00021000:ata-3: Activating pata-macio chipset KeyLargo
ATA-3, Apple
bus ID 1
scsi host1: pata_macio
ata2: PATA max MWDMA2 irq 18
pcnet32: pcnet32.c:v1.35 21.Apr.2008 tsbogend@alpha.franken.de
PPP generic driver version 2.4.2
PPP Deflate Compression module registered
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
ohci-pci: OHCI PCI platform driver
ohci-pci 0000:00:0d.0: OHCI PCI host controller
ohci-pci 0000:00:0d.0: new USB bus registered, assigned bus number 1
ohci-pci 0000:00:0d.0: irq 28, io mem 0x80080000
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 3 ports detected
usbcore: registered new interface driver appletouch
rtc-generic rtc-generic: registered as rtc0
APM Battery Driver
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
oprofile: using timer interrupt.
Initializing XFRM netlink socket
NET: Registered protocol family 17
NET: Registered protocol family 15
Key type dns_resolver registered
drmem: No dynamic reconfiguration memory found
Loading compiled-in X.509 certificates
rtc-generic rtc-generic: setting system clock to 2019-01-22T19:35:11 UTC
(154818
5711)
Warning: unable to open an initial console.
ata2.00: ATAPI: QEMU DVD-ROM, 2.5+, max UDMA/100
scsi 1:0:0:0: CD-ROM QEMU QEMU DVD-ROM 2.5+ PQ: 0 ANSI: 5
sr 1:0:0:0: [sr0] scsi3-mmc drive: 4x/4x cd/rw xa/form2 tray
cdrom: Uniform CD-ROM driver Revision: 3.20
sr 1:0:0:0: Attached scsi generic sg0 type 5
VFS: Cannot open root device "(null)" or unknown-block(8,1): error -6
Please append a correct "root=" boot option; here are the available
partitions:
0100 4096 ram0
(driver?)
0101 4096 ram1
(driver?)
0102 4096 ram2
(driver?)
0103 4096 ram3
(driver?)
0104 4096 ram4
(driver?)
0105 4096 ram5
(driver?)
0106 4096 ram6
(driver?)
0107 4096 ram7
(driver?)
0108 4096 ram8
(driver?)
0109 4096 ram9
(driver?)
010a 4096 ram10
(driver?)
010b 4096 ram11
(driver?)
010c 4096 ram12
(driver?)
010d 4096 ram13
(driver?)
010e 4096 ram14
(driver?)
010f 4096 ram15
(driver?)
0b00 1048575 sr0
driver: sr
Kernel panic - not syncing: VFS: Unable to mount root fs on
unknown-block(8,1)
CPU: 0 PID: 1 Comm: swapper Not tainted 5.0.0-rc3+ #790
Call Trace:
[c7837e60] [c003dcf0] panic+0x12c/0x2e8 (unreliable)
[c7837ec0] [c07fc824] mount_block_root+0x1fc/0x2c0
[c7837f10] [c07fcc54] prepare_namespace+0x198/0x1d4
[c7837f30] [c000539c] kernel_init+0x18/0x10c
[c7837f40] [c00131d8] ret_from_kernel_thread+0x14/0x1c
Rebooting in 180 seconds..
Christophe
^ permalink raw reply
* [PATCH 4/4] powerpc/livepatch: return -ERRNO values in save_stack_trace_tsk_reliable()
From: Joe Lawrence @ 2019-01-22 15:57 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, live-patching
Cc: Nicolai Stange, Jiri Kosina, Josh Poimboeuf, Torsten Duwe
In-Reply-To: <20190122155724.27557-1-joe.lawrence@redhat.com>
To match its x86 counterpart, save_stack_trace_tsk_reliable() should
return -EINVAL in cases that it is currently returning 1. No caller is
currently differentiating non-zero error codes, but let's keep the
arch-specific implementations consistent.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
arch/powerpc/kernel/stacktrace.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/stacktrace.c b/arch/powerpc/kernel/stacktrace.c
index 28c3c25755d7..cf31ce6c1f53 100644
--- a/arch/powerpc/kernel/stacktrace.c
+++ b/arch/powerpc/kernel/stacktrace.c
@@ -133,7 +133,7 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
if (sp < stack_page + sizeof(struct thread_struct) ||
sp > stack_end - STACK_FRAME_MIN_SIZE) {
- return 1;
+ return -EINVAL;
}
for (firstframe = true; sp != stack_end;
@@ -143,16 +143,16 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
/* sanity check: ABI requires SP to be aligned 16 bytes. */
if (sp & 0xF)
- return 1;
+ return -EINVAL;
newsp = stack[0];
/* Stack grows downwards; unwinder may only go up. */
if (newsp <= sp)
- return 1;
+ return -EINVAL;
if (newsp != stack_end &&
newsp > stack_end - STACK_FRAME_MIN_SIZE) {
- return 1; /* invalid backlink, too far up. */
+ return -EINVAL; /* invalid backlink, too far up. */
}
/*
@@ -166,13 +166,13 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
/* Mark stacktraces with exception frames as unreliable. */
if (sp <= stack_end - STACK_INT_FRAME_SIZE &&
stack[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) {
- return 1;
+ return -EINVAL;
}
/* Examine the saved LR: it must point into kernel code. */
ip = stack[STACK_FRAME_LR_SAVE];
if (!__kernel_text_address(ip))
- return 1;
+ return -EINVAL;
/*
* FIXME: IMHO these tests do not belong in
@@ -185,7 +185,7 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
* as unreliable.
*/
if (ip == (unsigned long)kretprobe_trampoline)
- return 1;
+ return -EINVAL;
#endif
if (trace->nr_entries >= trace->max_entries)
--
2.20.1
^ permalink raw reply related
* [PATCH 3/4] powerpc/livepatch: small cleanups in save_stack_trace_tsk_reliable()
From: Joe Lawrence @ 2019-01-22 15:57 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, live-patching
Cc: Nicolai Stange, Jiri Kosina, Josh Poimboeuf, Torsten Duwe
In-Reply-To: <20190122155724.27557-1-joe.lawrence@redhat.com>
Mostly cosmetic changes:
- Group common stack pointer code at the top
- Simplify the first frame logic
- Code stackframe iteration into for...loop construct
- Check for trace->nr_entries overflow before adding any into the array
Suggested-by: Nicolai Stange <nstange@suse.de>
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
arch/powerpc/kernel/stacktrace.c | 40 +++++++++++---------------------
1 file changed, 14 insertions(+), 26 deletions(-)
diff --git a/arch/powerpc/kernel/stacktrace.c b/arch/powerpc/kernel/stacktrace.c
index 06688f4d557b..28c3c25755d7 100644
--- a/arch/powerpc/kernel/stacktrace.c
+++ b/arch/powerpc/kernel/stacktrace.c
@@ -95,20 +95,11 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
struct stack_trace *trace)
{
unsigned long sp;
+ unsigned long newsp;
unsigned long stack_page = (unsigned long)task_stack_page(tsk);
unsigned long stack_end;
int graph_idx = 0;
-
- /*
- * The last frame (unwinding first) may not yet have saved
- * its LR onto the stack.
- */
- int firstframe = 1;
-
- if (tsk == current)
- sp = current_stack_pointer();
- else
- sp = tsk->thread.ksp;
+ bool firstframe;
stack_end = stack_page + THREAD_SIZE;
if (!is_idle_task(tsk)) {
@@ -135,14 +126,20 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
stack_end -= STACK_FRAME_OVERHEAD;
}
+ if (tsk == current)
+ sp = current_stack_pointer();
+ else
+ sp = tsk->thread.ksp;
+
if (sp < stack_page + sizeof(struct thread_struct) ||
sp > stack_end - STACK_FRAME_MIN_SIZE) {
return 1;
}
- for (;;) {
+ for (firstframe = true; sp != stack_end;
+ firstframe = false, sp = newsp) {
unsigned long *stack = (unsigned long *) sp;
- unsigned long newsp, ip;
+ unsigned long ip;
/* sanity check: ABI requires SP to be aligned 16 bytes. */
if (sp & 0xF)
@@ -163,10 +160,8 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
* rest of the frame may be uninitialized, continue to
* the next.
*/
- if (firstframe) {
- firstframe = 0;
- goto next;
- }
+ if (firstframe)
+ continue;
/* Mark stacktraces with exception frames as unreliable. */
if (sp <= stack_end - STACK_INT_FRAME_SIZE &&
@@ -193,19 +188,12 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
return 1;
#endif
+ if (trace->nr_entries >= trace->max_entries)
+ return -E2BIG;
if (!trace->skip)
trace->entries[trace->nr_entries++] = ip;
else
trace->skip--;
-
-next:
- if (newsp == stack_end)
- break;
-
- if (trace->nr_entries >= trace->max_entries)
- return -E2BIG;
-
- sp = newsp;
}
return 0;
}
--
2.20.1
^ permalink raw reply related
* [PATCH 2/4] powerpc/livepatch: relax reliable stack tracer checks for first-frame
From: Joe Lawrence @ 2019-01-22 15:57 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, live-patching
Cc: Nicolai Stange, Jiri Kosina, Josh Poimboeuf, Torsten Duwe
In-Reply-To: <20190122155724.27557-1-joe.lawrence@redhat.com>
The bottom-most stack frame (the first to be unwound) may be largely
uninitialized, for the "Power Architecture 64-Bit ELF V2 ABI" only
requires its backchain pointer to be set.
The reliable stack tracer should be careful when verifying this frame:
skip checks on STACK_FRAME_LR_SAVE and STACK_FRAME_MARKER offsets that
may contain uninitialized residual data.
Fixes: df78d3f61480 ("powerpc/livepatch: Implement reliable stack tracing for the consistency model")
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
arch/powerpc/kernel/stacktrace.c | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/arch/powerpc/kernel/stacktrace.c b/arch/powerpc/kernel/stacktrace.c
index e2c50b55138f..06688f4d557b 100644
--- a/arch/powerpc/kernel/stacktrace.c
+++ b/arch/powerpc/kernel/stacktrace.c
@@ -84,6 +84,12 @@ save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
EXPORT_SYMBOL_GPL(save_stack_trace_regs);
#ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
+/*
+ * This function returns an error if it detects any unreliable features of the
+ * stack. Otherwise it guarantees that the stack trace is reliable.
+ *
+ * If the task is not 'current', the caller *must* ensure the task is inactive.
+ */
int
save_stack_trace_tsk_reliable(struct task_struct *tsk,
struct stack_trace *trace)
@@ -142,12 +148,6 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
if (sp & 0xF)
return 1;
- /* Mark stacktraces with exception frames as unreliable. */
- if (sp <= stack_end - STACK_INT_FRAME_SIZE &&
- stack[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) {
- return 1;
- }
-
newsp = stack[0];
/* Stack grows downwards; unwinder may only go up. */
if (newsp <= sp)
@@ -158,11 +158,26 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
return 1; /* invalid backlink, too far up. */
}
+ /*
+ * We can only trust the bottom frame's backlink, the
+ * rest of the frame may be uninitialized, continue to
+ * the next.
+ */
+ if (firstframe) {
+ firstframe = 0;
+ goto next;
+ }
+
+ /* Mark stacktraces with exception frames as unreliable. */
+ if (sp <= stack_end - STACK_INT_FRAME_SIZE &&
+ stack[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) {
+ return 1;
+ }
+
/* Examine the saved LR: it must point into kernel code. */
ip = stack[STACK_FRAME_LR_SAVE];
- if (!firstframe && !__kernel_text_address(ip))
+ if (!__kernel_text_address(ip))
return 1;
- firstframe = 0;
/*
* FIXME: IMHO these tests do not belong in
@@ -183,6 +198,7 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
else
trace->skip--;
+next:
if (newsp == stack_end)
break;
--
2.20.1
^ permalink raw reply related
* [PATCH 1/4] powerpc/64s: Clear on-stack exception marker upon exception return
From: Joe Lawrence @ 2019-01-22 15:57 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, live-patching
Cc: Nicolai Stange, Jiri Kosina, Josh Poimboeuf, Torsten Duwe
In-Reply-To: <20190122155724.27557-1-joe.lawrence@redhat.com>
From: Nicolai Stange <nstange@suse.de>
The ppc64 specific implementation of the reliable stacktracer,
save_stack_trace_tsk_reliable(), bails out and reports an "unreliable
trace" whenever it finds an exception frame on the stack. Stack frames
are classified as exception frames if the STACK_FRAME_REGS_MARKER magic,
as written by exception prologues, is found at a particular location.
However, as observed by Joe Lawrence, it is possible in practice that
non-exception stack frames can alias with prior exception frames and thus,
that the reliable stacktracer can find a stale STACK_FRAME_REGS_MARKER on
the stack. It in turn falsely reports an unreliable stacktrace and blocks
any live patching transition to finish. Said condition lasts until the
stack frame is overwritten/initialized by function call or other means.
In principle, we could mitigate this by making the exception frame
classification condition in save_stack_trace_tsk_reliable() stronger:
in addition to testing for STACK_FRAME_REGS_MARKER, we could also take into
account that for all exceptions executing on the kernel stack
- their stack frames's backlink pointers always match what is saved
in their pt_regs instance's ->gpr[1] slot and that
- their exception frame size equals STACK_INT_FRAME_SIZE, a value
uncommonly large for non-exception frames.
However, while these are currently true, relying on them would make the
reliable stacktrace implementation more sensitive towards future changes in
the exception entry code. Note that false negatives, i.e. not detecting
exception frames, would silently break the live patching consistency model.
Furthermore, certain other places (diagnostic stacktraces, perf, xmon)
rely on STACK_FRAME_REGS_MARKER as well.
Make the exception exit code clear the on-stack STACK_FRAME_REGS_MARKER
for those exceptions running on the "normal" kernel stack and returning
to kernelspace: because the topmost frame is ignored by the reliable stack
tracer anyway, returns to userspace don't need to take care of clearing
the marker.
Furthermore, as I don't have the ability to test this on Book 3E or
32 bits, limit the change to Book 3S and 64 bits.
Finally, make the HAVE_RELIABLE_STACKTRACE Kconfig option depend on
PPC_BOOK3S_64 for documentation purposes. Before this patch, it depended
on PPC64 && CPU_LITTLE_ENDIAN and because CPU_LITTLE_ENDIAN implies
PPC_BOOK3S_64, there's no functional change here.
Fixes: df78d3f61480 ("powerpc/livepatch: Implement reliable stack tracing for the consistency model")
Reported-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Nicolai Stange <nstange@suse.de>
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/kernel/entry_64.S | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 2890d36eb531..73bf87b1d274 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -220,7 +220,7 @@ config PPC
select HAVE_PERF_USER_STACK_DUMP
select HAVE_RCU_TABLE_FREE if SMP
select HAVE_REGS_AND_STACK_ACCESS_API
- select HAVE_RELIABLE_STACKTRACE if PPC64 && CPU_LITTLE_ENDIAN
+ select HAVE_RELIABLE_STACKTRACE if PPC_BOOK3S_64 && CPU_LITTLE_ENDIAN
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_VIRT_CPU_ACCOUNTING
select HAVE_IRQ_TIME_ACCOUNTING
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 435927f549c4..a2c168b395d2 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -1002,6 +1002,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
ld r2,_NIP(r1)
mtspr SPRN_SRR0,r2
+ /*
+ * Leaving a stale exception_marker on the stack can confuse
+ * the reliable stack unwinder later on. Clear it.
+ */
+ li r2,0
+ std r2,STACK_FRAME_OVERHEAD-16(r1)
+
ld r0,GPR0(r1)
ld r2,GPR2(r1)
ld r3,GPR3(r1)
--
2.20.1
^ permalink raw reply related
* [PATCH 0/4] powerpc/livepatch: reliable stack unwinder fixes
From: Joe Lawrence @ 2019-01-22 15:57 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, live-patching
Cc: Nicolai Stange, Jiri Kosina, Josh Poimboeuf, Torsten Duwe
This patchset fixes a false negative report (ie, unreliable) from the
ppc64 reliable stack unwinder, discussed here [1] when it may
inadvertently trip over a stale exception marker left on the stack.
The first two patches fix this bug. Nicolai's change clears the marker
from the stack when an exception is finished. The next patch modifies
the unwinder to only look for such on stack elements when the ABI
guarantees that they will actually be initialized.
The final two patches consist of code cleanups that Nicolai and I
spotted during the development of the fixes.
Testing included re-running the original test scenario (loading a
livepatch module on ppc64le) on a 5.0.0-rc2 kernel as well as a RHEL-7
backport. I ran internal tests on the RHEL-7 backport and no new test
failures were introduced. I believe that Nicolai has done the same
with respect to the first patch.
[1] https://lore.kernel.org/lkml/7f468285-b149-37e2-e782-c9e538b997a9@redhat.com/
Joe Lawrence (3):
powerpc/livepatch: relax reliable stack tracer checks for first-frame
powerpc/livepatch: small cleanups in save_stack_trace_tsk_reliable()
powerpc/livepatch: return -ERRNO values in
save_stack_trace_tsk_reliable()
Nicolai Stange (1):
powerpc/64s: Clear on-stack exception marker upon exception return
arch/powerpc/Kconfig | 2 +-
arch/powerpc/kernel/entry_64.S | 7 +++
arch/powerpc/kernel/stacktrace.c | 74 +++++++++++++++++---------------
3 files changed, 47 insertions(+), 36 deletions(-)
--
2.20.1
^ permalink raw reply
* [PATCH v4 3/3] powerpc/32: Add KASAN support
From: Christophe Leroy @ 2019-01-22 14:28 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
Nicholas Piggin, Aneesh Kumar K.V, Andrey Ryabinin,
Alexander Potapenko, Dmitry Vyukov
Cc: linux-mm, linuxppc-dev, linux-kernel, kasan-dev
In-Reply-To: <cover.1548166824.git.christophe.leroy@c-s.fr>
This patch adds KASAN support for PPC32.
Note that on book3s it will only work on the 603 because the other
ones use hash table and can therefore not share a single PTE table
covering the entire early KASAN shadow area.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/book3s/32/pgtable.h | 2 +
arch/powerpc/include/asm/kasan.h | 24 ++++++++
arch/powerpc/include/asm/nohash/32/pgtable.h | 2 +
arch/powerpc/include/asm/ppc_asm.h | 5 ++
arch/powerpc/include/asm/setup.h | 5 ++
arch/powerpc/include/asm/string.h | 14 +++++
arch/powerpc/kernel/Makefile | 9 ++-
arch/powerpc/kernel/early_32.c | 1 +
arch/powerpc/kernel/prom_init_check.sh | 10 +++-
arch/powerpc/kernel/setup-common.c | 2 +
arch/powerpc/kernel/setup_32.c | 3 +
arch/powerpc/lib/Makefile | 8 +++
arch/powerpc/lib/copy_32.S | 9 ++-
arch/powerpc/mm/Makefile | 3 +
arch/powerpc/mm/dump_linuxpagetables.c | 8 +++
arch/powerpc/mm/kasan_init.c | 86 ++++++++++++++++++++++++++++
arch/powerpc/mm/mem.c | 4 ++
18 files changed, 190 insertions(+), 6 deletions(-)
create mode 100644 arch/powerpc/include/asm/kasan.h
create mode 100644 arch/powerpc/mm/kasan_init.c
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 2890d36eb531..11dcaa80d3ff 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -175,6 +175,7 @@ config PPC
select GENERIC_TIME_VSYSCALL
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_JUMP_LABEL
+ select HAVE_ARCH_KASAN if PPC32
select HAVE_ARCH_KGDB
select HAVE_ARCH_MMAP_RND_BITS
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
diff --git a/arch/powerpc/include/asm/book3s/32/pgtable.h b/arch/powerpc/include/asm/book3s/32/pgtable.h
index 49d76adb9bc5..4543016f80ca 100644
--- a/arch/powerpc/include/asm/book3s/32/pgtable.h
+++ b/arch/powerpc/include/asm/book3s/32/pgtable.h
@@ -141,6 +141,8 @@ static inline bool pte_user(pte_t pte)
*/
#ifdef CONFIG_HIGHMEM
#define KVIRT_TOP PKMAP_BASE
+#elif defined(CONFIG_KASAN)
+#define KVIRT_TOP KASAN_SHADOW_START
#else
#define KVIRT_TOP (0xfe000000UL) /* for now, could be FIXMAP_BASE ? */
#endif
diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h
new file mode 100644
index 000000000000..5d0088429b62
--- /dev/null
+++ b/arch/powerpc/include/asm/kasan.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_KASAN_H
+#define __ASM_KASAN_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/page.h>
+#include <asm/pgtable-types.h>
+#include <asm/fixmap.h>
+
+#define KASAN_SHADOW_SCALE_SHIFT 3
+#define KASAN_SHADOW_SIZE ((~0UL - PAGE_OFFSET + 1) >> KASAN_SHADOW_SCALE_SHIFT)
+
+#define KASAN_SHADOW_START (ALIGN_DOWN(FIXADDR_START - KASAN_SHADOW_SIZE, \
+ PGDIR_SIZE))
+#define KASAN_SHADOW_END (KASAN_SHADOW_START + KASAN_SHADOW_SIZE)
+#define KASAN_SHADOW_OFFSET (KASAN_SHADOW_START - \
+ (PAGE_OFFSET >> KASAN_SHADOW_SCALE_SHIFT))
+
+void kasan_early_init(void);
+void kasan_init(void);
+
+#endif
+#endif
diff --git a/arch/powerpc/include/asm/nohash/32/pgtable.h b/arch/powerpc/include/asm/nohash/32/pgtable.h
index bed433358260..b3b52f02be1a 100644
--- a/arch/powerpc/include/asm/nohash/32/pgtable.h
+++ b/arch/powerpc/include/asm/nohash/32/pgtable.h
@@ -71,6 +71,8 @@ extern int icache_44x_need_flush;
*/
#ifdef CONFIG_HIGHMEM
#define KVIRT_TOP PKMAP_BASE
+#elif defined(CONFIG_KASAN)
+#define KVIRT_TOP KASAN_SHADOW_START
#else
#define KVIRT_TOP (0xfe000000UL) /* for now, could be FIXMAP_BASE ? */
#endif
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index e0637730a8e7..8d5291c721fa 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -251,6 +251,11 @@ GLUE(.,name):
#define _GLOBAL_TOC(name) _GLOBAL(name)
+#define KASAN_OVERRIDE(x, y) \
+ .weak x; \
+ .set x, y
+
+
#endif
/*
diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h
index 65676e2325b8..da7768aa996a 100644
--- a/arch/powerpc/include/asm/setup.h
+++ b/arch/powerpc/include/asm/setup.h
@@ -74,6 +74,11 @@ static inline void setup_spectre_v2(void) {};
#endif
void do_btb_flush_fixups(void);
+#ifndef CONFIG_KASAN
+static inline void kasan_early_init(void) { }
+static inline void kasan_init(void) { }
+#endif
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_POWERPC_SETUP_H */
diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h
index 1647de15a31e..64d44d4836b4 100644
--- a/arch/powerpc/include/asm/string.h
+++ b/arch/powerpc/include/asm/string.h
@@ -27,6 +27,20 @@ extern int memcmp(const void *,const void *,__kernel_size_t);
extern void * memchr(const void *,int,__kernel_size_t);
extern void * memcpy_flushcache(void *,const void *,__kernel_size_t);
+void *__memset(void *s, int c, __kernel_size_t count);
+void *__memcpy(void *to, const void *from, __kernel_size_t n);
+void *__memmove(void *to, const void *from, __kernel_size_t n);
+
+#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
+/*
+ * For files that are not instrumented (e.g. mm/slub.c) we
+ * should use not instrumented version of mem* functions.
+ */
+#define memcpy(dst, src, len) __memcpy(dst, src, len)
+#define memmove(dst, src, len) __memmove(dst, src, len)
+#define memset(s, c, n) __memset(s, c, n)
+#endif
+
#ifdef CONFIG_PPC64
#define __HAVE_ARCH_MEMSET32
#define __HAVE_ARCH_MEMSET64
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 879b36602748..fc4c42262694 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -16,8 +16,9 @@ CFLAGS_prom_init.o += -fPIC
CFLAGS_btext.o += -fPIC
endif
-CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
-CFLAGS_prom_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
+CFLAGS_early_32.o += -DDISABLE_BRANCH_PROFILING
+CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN) -DDISABLE_BRANCH_PROFILING
+CFLAGS_prom_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN) -DDISABLE_BRANCH_PROFILING
CFLAGS_btext.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
CFLAGS_prom.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
@@ -31,6 +32,10 @@ CFLAGS_REMOVE_btext.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_prom.o = $(CC_FLAGS_FTRACE)
endif
+KASAN_SANITIZE_early_32.o := n
+KASAN_SANITIZE_cputable.o := n
+KASAN_SANITIZE_prom_init.o := n
+
obj-y := cputable.o ptrace.o syscalls.o \
irq.o align.o signal_32.o pmc.o vdso.o \
process.o systbl.o idle.o \
diff --git a/arch/powerpc/kernel/early_32.c b/arch/powerpc/kernel/early_32.c
index b3e40d6d651c..3482118ffe76 100644
--- a/arch/powerpc/kernel/early_32.c
+++ b/arch/powerpc/kernel/early_32.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <asm/setup.h>
#include <asm/sections.h>
+#include <asm/asm-prototypes.h>
/*
* We're called here very early in the boot.
diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh
index 667df97d2595..da6bb16e0876 100644
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -16,8 +16,16 @@
# If you really need to reference something from prom_init.o add
# it to the list below:
+grep CONFIG_KASAN=y .config >/dev/null
+if [ $? -eq 0 ]
+then
+ MEMFCT="__memcpy __memset"
+else
+ MEMFCT="memcpy memset"
+fi
+
WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush
-_end enter_prom memcpy memset reloc_offset __secondary_hold
+_end enter_prom $MEMFCT reloc_offset __secondary_hold
__secondary_hold_acknowledge __secondary_hold_spinloop __start
strcmp strcpy strlcpy strlen strncmp strstr kstrtobool logo_linux_clut224
reloc_got2 kernstart_addr memstart_addr linux_banner _stext
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index ca00fbb97cf8..16ff1ea66805 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -978,6 +978,8 @@ void __init setup_arch(char **cmdline_p)
paging_init();
+ kasan_init();
+
/* Initialize the MMU context management stuff. */
mmu_context_init();
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index b46a9a33225b..fe6990dec6fc 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -17,6 +17,7 @@
#include <linux/console.h>
#include <linux/memblock.h>
#include <linux/export.h>
+#include <linux/kasan.h>
#include <asm/io.h>
#include <asm/prom.h>
@@ -75,6 +76,8 @@ notrace void __init machine_init(u64 dt_ptr)
unsigned int *addr = (unsigned int *)patch_site_addr(&patch__memset_nocache);
unsigned long insn;
+ kasan_early_init();
+
/* Configure static keys first, now that we're relocated. */
setup_feature_keys();
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 3bf9fc6fd36c..ce8d4a9f810a 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -8,6 +8,14 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
CFLAGS_REMOVE_code-patching.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_feature-fixups.o = $(CC_FLAGS_FTRACE)
+KASAN_SANITIZE_code-patching.o := n
+KASAN_SANITIZE_feature-fixups.o := n
+
+ifdef CONFIG_KASAN
+CFLAGS_code-patching.o += -DDISABLE_BRANCH_PROFILING
+CFLAGS_feature-fixups.o += -DDISABLE_BRANCH_PROFILING
+endif
+
obj-y += string.o alloc.o code-patching.o feature-fixups.o
obj-$(CONFIG_PPC32) += div64.o copy_32.o crtsavres.o strlen_32.o
diff --git a/arch/powerpc/lib/copy_32.S b/arch/powerpc/lib/copy_32.S
index ba66846fe973..4d8a1c73b4cf 100644
--- a/arch/powerpc/lib/copy_32.S
+++ b/arch/powerpc/lib/copy_32.S
@@ -91,7 +91,8 @@ EXPORT_SYMBOL(memset16)
* We therefore skip the optimised bloc that uses dcbz. This jump is
* replaced by a nop once cache is active. This is done in machine_init()
*/
-_GLOBAL(memset)
+_GLOBAL(__memset)
+KASAN_OVERRIDE(memset, __memset)
cmplwi 0,r5,4
blt 7f
@@ -163,12 +164,14 @@ EXPORT_SYMBOL(memset)
* We therefore jump to generic_memcpy which doesn't use dcbz. This jump is
* replaced by a nop once cache is active. This is done in machine_init()
*/
-_GLOBAL(memmove)
+_GLOBAL(__memmove)
+KASAN_OVERRIDE(memmove, __memmove)
cmplw 0,r3,r4
bgt backwards_memcpy
/* fall through */
-_GLOBAL(memcpy)
+_GLOBAL(__memcpy)
+KASAN_OVERRIDE(memcpy, __memcpy)
1: b generic_memcpy
patch_site 1b, patch__memcpy_nocache
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index f965fc33a8b7..d6b76f25f6de 100644
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -7,6 +7,8 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
CFLAGS_REMOVE_slb.o = $(CC_FLAGS_FTRACE)
+KASAN_SANITIZE_kasan_init.o := n
+
obj-y := fault.o mem.o pgtable.o mmap.o \
init_$(BITS).o pgtable_$(BITS).o \
init-common.o mmu_context.o drmem.o
@@ -55,3 +57,4 @@ obj-$(CONFIG_PPC_BOOK3S_64) += dump_linuxpagetables-book3s64.o
endif
obj-$(CONFIG_PPC_HTDUMP) += dump_hashpagetable.o
obj-$(CONFIG_PPC_MEM_KEYS) += pkeys.o
+obj-$(CONFIG_KASAN) += kasan_init.o
diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c
index 6aa41669ac1a..c862b48118f1 100644
--- a/arch/powerpc/mm/dump_linuxpagetables.c
+++ b/arch/powerpc/mm/dump_linuxpagetables.c
@@ -94,6 +94,10 @@ static struct addr_marker address_markers[] = {
{ 0, "Consistent mem start" },
{ 0, "Consistent mem end" },
#endif
+#ifdef CONFIG_KASAN
+ { 0, "kasan shadow mem start" },
+ { 0, "kasan shadow mem end" },
+#endif
#ifdef CONFIG_HIGHMEM
{ 0, "Highmem PTEs start" },
{ 0, "Highmem PTEs end" },
@@ -310,6 +314,10 @@ static void populate_markers(void)
address_markers[i++].start_address = IOREMAP_TOP +
CONFIG_CONSISTENT_SIZE;
#endif
+#ifdef CONFIG_KASAN
+ address_markers[i++].start_address = KASAN_SHADOW_START;
+ address_markers[i++].start_address = KASAN_SHADOW_END;
+#endif
#ifdef CONFIG_HIGHMEM
address_markers[i++].start_address = PKMAP_BASE;
address_markers[i++].start_address = PKMAP_ADDR(LAST_PKMAP);
diff --git a/arch/powerpc/mm/kasan_init.c b/arch/powerpc/mm/kasan_init.c
new file mode 100644
index 000000000000..69e17428c2e9
--- /dev/null
+++ b/arch/powerpc/mm/kasan_init.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define DISABLE_BRANCH_PROFILING
+
+#include <linux/kasan.h>
+#include <linux/printk.h>
+#include <linux/memblock.h>
+#include <linux/sched/task.h>
+#include <asm/pgalloc.h>
+
+void __init kasan_early_init(void)
+{
+ unsigned long addr = KASAN_SHADOW_START;
+ unsigned long end = KASAN_SHADOW_END;
+ unsigned long next;
+ pmd_t *pmd = pmd_offset(pud_offset(pgd_offset_k(addr), addr), addr);
+ int i;
+ phys_addr_t pa = __pa(kasan_early_shadow_page);
+
+ BUILD_BUG_ON(KASAN_SHADOW_START & ~PGDIR_MASK);
+
+ if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE))
+ panic("KASAN not supported with Hash MMU\n");
+
+ for (i = 0; i < PTRS_PER_PTE; i++)
+ __set_pte_at(&init_mm, (unsigned long)kasan_early_shadow_page,
+ kasan_early_shadow_pte + i,
+ pfn_pte(PHYS_PFN(pa), PAGE_KERNEL_RO), 0);
+
+ do {
+ next = pgd_addr_end(addr, end);
+ pmd_populate_kernel(&init_mm, pmd, kasan_early_shadow_pte);
+ } while (pmd++, addr = next, addr != end);
+}
+
+static void __init kasan_init_region(struct memblock_region *reg)
+{
+ void *start = __va(reg->base);
+ void *end = __va(reg->base + reg->size);
+ unsigned long k_start, k_end, k_cur, k_next;
+ pmd_t *pmd;
+
+ if (start >= end)
+ return;
+
+ k_start = (unsigned long)kasan_mem_to_shadow(start);
+ k_end = (unsigned long)kasan_mem_to_shadow(end);
+ pmd = pmd_offset(pud_offset(pgd_offset_k(k_start), k_start), k_start);
+
+ for (k_cur = k_start; k_cur != k_end; k_cur = k_next, pmd++) {
+ k_next = pgd_addr_end(k_cur, k_end);
+ if ((void *)pmd_page_vaddr(*pmd) == kasan_early_shadow_pte) {
+ pte_t *new = pte_alloc_one_kernel(&init_mm);
+
+ if (!new)
+ panic("kasan: pte_alloc_one_kernel() failed");
+ memcpy(new, kasan_early_shadow_pte, PTE_TABLE_SIZE);
+ pmd_populate_kernel(&init_mm, pmd, new);
+ }
+ };
+
+ for (k_cur = k_start; k_cur < k_end; k_cur += PAGE_SIZE) {
+ void *va = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ pte_t pte = pfn_pte(PHYS_PFN(__pa(va)), PAGE_KERNEL);
+
+ if (!va)
+ panic("kasan: memblock_alloc() failed");
+ pmd = pmd_offset(pud_offset(pgd_offset_k(k_cur), k_cur), k_cur);
+ pte_update(pte_offset_kernel(pmd, k_cur), ~0, pte_val(pte));
+ }
+ flush_tlb_kernel_range(k_start, k_end);
+}
+
+void __init kasan_init(void)
+{
+ struct memblock_region *reg;
+
+ for_each_memblock(memory, reg)
+ kasan_init_region(reg);
+
+ kasan_init_tags();
+
+ /* At this point kasan is fully initialized. Enable error messages */
+ init_task.kasan_depth = 0;
+ pr_info("KASAN init done\n");
+}
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 33cc6f676fa6..ae7db88b72d6 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -369,6 +369,10 @@ void __init mem_init(void)
pr_info(" * 0x%08lx..0x%08lx : highmem PTEs\n",
PKMAP_BASE, PKMAP_ADDR(LAST_PKMAP));
#endif /* CONFIG_HIGHMEM */
+#ifdef CONFIG_KASAN
+ pr_info(" * 0x%08lx..0x%08lx : kasan shadow mem\n",
+ KASAN_SHADOW_START, KASAN_SHADOW_END);
+#endif
#ifdef CONFIG_NOT_COHERENT_CACHE
pr_info(" * 0x%08lx..0x%08lx : consistent mem\n",
IOREMAP_TOP, IOREMAP_TOP + CONFIG_CONSISTENT_SIZE);
--
2.13.3
^ permalink raw reply related
* [PATCH v4 2/3] powerpc/32: Move early_init() in a separate file
From: Christophe Leroy @ 2019-01-22 14:28 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
Nicholas Piggin, Aneesh Kumar K.V, Andrey Ryabinin,
Alexander Potapenko, Dmitry Vyukov
Cc: linux-mm, linuxppc-dev, linux-kernel, kasan-dev
In-Reply-To: <cover.1548166824.git.christophe.leroy@c-s.fr>
In preparation of KASAN, move early_init() into a separate
file in order to allow deactivation of KASAN for that function.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/early_32.c | 35 +++++++++++++++++++++++++++++++++++
arch/powerpc/kernel/setup_32.c | 26 --------------------------
3 files changed, 36 insertions(+), 27 deletions(-)
create mode 100644 arch/powerpc/kernel/early_32.c
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index cb7f0bb9ee71..879b36602748 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -93,7 +93,7 @@ extra-y += vmlinux.lds
obj-$(CONFIG_RELOCATABLE) += reloc_$(BITS).o
-obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
+obj-$(CONFIG_PPC32) += entry_32.o setup_32.o early_32.o
obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_BOOTX_TEXT) += btext.o
diff --git a/arch/powerpc/kernel/early_32.c b/arch/powerpc/kernel/early_32.c
new file mode 100644
index 000000000000..b3e40d6d651c
--- /dev/null
+++ b/arch/powerpc/kernel/early_32.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Early init before relocation
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/setup.h>
+#include <asm/sections.h>
+
+/*
+ * We're called here very early in the boot.
+ *
+ * Note that the kernel may be running at an address which is different
+ * from the address that it was linked at, so we must use RELOC/PTRRELOC
+ * to access static data (including strings). -- paulus
+ */
+notrace unsigned long __init early_init(unsigned long dt_ptr)
+{
+ unsigned long offset = reloc_offset();
+
+ /* First zero the BSS */
+ memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start);
+
+ /*
+ * Identify the CPU type and fix up code sections
+ * that depend on which cpu we have.
+ */
+ identify_cpu(offset, mfspr(SPRN_PVR));
+
+ apply_feature_fixups();
+
+ return KERNELBASE + offset;
+}
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 5e761eb16a6d..b46a9a33225b 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -63,32 +63,6 @@ EXPORT_SYMBOL(DMA_MODE_READ);
EXPORT_SYMBOL(DMA_MODE_WRITE);
/*
- * We're called here very early in the boot.
- *
- * Note that the kernel may be running at an address which is different
- * from the address that it was linked at, so we must use RELOC/PTRRELOC
- * to access static data (including strings). -- paulus
- */
-notrace unsigned long __init early_init(unsigned long dt_ptr)
-{
- unsigned long offset = reloc_offset();
-
- /* First zero the BSS */
- memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start);
-
- /*
- * Identify the CPU type and fix up code sections
- * that depend on which cpu we have.
- */
- identify_cpu(offset, mfspr(SPRN_PVR));
-
- apply_feature_fixups();
-
- return KERNELBASE + offset;
-}
-
-
-/*
* This is run before start_kernel(), the kernel has been relocated
* and we are running with enough of the MMU enabled to have our
* proper kernel virtual addresses
--
2.13.3
^ permalink raw reply related
* [PATCH v4 1/3] powerpc/mm: prepare kernel for KAsan on PPC32
From: Christophe Leroy @ 2019-01-22 14:28 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
Nicholas Piggin, Aneesh Kumar K.V, Andrey Ryabinin,
Alexander Potapenko, Dmitry Vyukov
Cc: linux-mm, linuxppc-dev, linux-kernel, kasan-dev
In-Reply-To: <cover.1548166824.git.christophe.leroy@c-s.fr>
In kernel/cputable.c, explicitly use memcpy() in order
to allow GCC to replace it with __memcpy() when KASAN is
selected.
Since commit 400c47d81ca38 ("powerpc32: memset: only use dcbz once cache is
enabled"), memset() can be used before activation of the cache,
so no need to use memset_io() for zeroing the BSS.
Acked-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/cputable.c | 13 ++++++++++---
arch/powerpc/kernel/setup_32.c | 6 ++----
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 1eab54bc6ee9..cd12f362b61f 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -2147,7 +2147,11 @@ void __init set_cur_cpu_spec(struct cpu_spec *s)
struct cpu_spec *t = &the_cpu_spec;
t = PTRRELOC(t);
- *t = *s;
+ /*
+ * use memcpy() instead of *t = *s so that GCC replaces it
+ * by __memcpy() when KASAN is active
+ */
+ memcpy(t, s, sizeof(*t));
*PTRRELOC(&cur_cpu_spec) = &the_cpu_spec;
}
@@ -2161,8 +2165,11 @@ static struct cpu_spec * __init setup_cpu_spec(unsigned long offset,
t = PTRRELOC(t);
old = *t;
- /* Copy everything, then do fixups */
- *t = *s;
+ /*
+ * Copy everything, then do fixups. Use memcpy() instead of *t = *s
+ * so that GCC replaces it by __memcpy() when KASAN is active
+ */
+ memcpy(t, s, sizeof(*t));
/*
* If we are overriding a previous value derived from the real
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 947f904688b0..5e761eb16a6d 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -73,10 +73,8 @@ notrace unsigned long __init early_init(unsigned long dt_ptr)
{
unsigned long offset = reloc_offset();
- /* First zero the BSS -- use memset_io, some platforms don't have
- * caches on yet */
- memset_io((void __iomem *)PTRRELOC(&__bss_start), 0,
- __bss_stop - __bss_start);
+ /* First zero the BSS */
+ memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start);
/*
* Identify the CPU type and fix up code sections
--
2.13.3
^ permalink raw reply related
* [PATCH v4 0/3] KASAN for powerpc/32
From: Christophe Leroy @ 2019-01-22 14:28 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
Nicholas Piggin, Aneesh Kumar K.V, Andrey Ryabinin,
Alexander Potapenko, Dmitry Vyukov
Cc: linux-mm, linuxppc-dev, linux-kernel, kasan-dev
This serie adds KASAN support to powerpc/32
Tested on nohash/32 (8xx) and book3s/32 (mpc832x ie 603)
Changes in v4:
- Comments from Andrey (DISABLE_BRANCH_PROFILING, Activation of reports)
- Proper initialisation of shadow area in kasan_init()
- Panic in case Hash table is required.
- Added comments in patch one to explain why *t = *s becomes memcpy(t, s, ...)
- Call of kasan_init_tags()
Changes in v3:
- Removed the printk() in kasan_early_init() to avoid build failure (see https://github.com/linuxppc/issues/issues/218)
- Added necessary changes in asm/book3s/32/pgtable.h to get it work on powerpc 603 family
- Added a few KASAN_SANITIZE_xxx.o := n to successfully boot on powerpc 603 family
Changes in v2:
- Rebased.
- Using __set_pte_at() to build the early table.
- Worked around and got rid of the patch adding asm/page.h in asm/pgtable-types.h
==> might be fixed independently but not needed for this serie.
For book3s/32 (not 603), it cannot work as is because due to HASHPTE flag, we
can't use the same pagetable for several PGD entries, and because Hash table
management is not not active early enough at the time being.
Christophe Leroy (3):
powerpc/mm: prepare kernel for KAsan on PPC32
powerpc/32: Move early_init() in a separate file
powerpc/32: Add KASAN support
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/book3s/32/pgtable.h | 2 +
arch/powerpc/include/asm/kasan.h | 24 ++++++++
arch/powerpc/include/asm/nohash/32/pgtable.h | 2 +
arch/powerpc/include/asm/ppc_asm.h | 5 ++
arch/powerpc/include/asm/setup.h | 5 ++
arch/powerpc/include/asm/string.h | 14 +++++
arch/powerpc/kernel/Makefile | 11 +++-
arch/powerpc/kernel/cputable.c | 13 ++++-
arch/powerpc/kernel/early_32.c | 36 ++++++++++++
arch/powerpc/kernel/prom_init_check.sh | 10 +++-
arch/powerpc/kernel/setup-common.c | 2 +
arch/powerpc/kernel/setup_32.c | 31 +---------
arch/powerpc/lib/Makefile | 8 +++
arch/powerpc/lib/copy_32.S | 9 ++-
arch/powerpc/mm/Makefile | 3 +
arch/powerpc/mm/dump_linuxpagetables.c | 8 +++
arch/powerpc/mm/kasan_init.c | 86 ++++++++++++++++++++++++++++
arch/powerpc/mm/mem.c | 4 ++
19 files changed, 236 insertions(+), 38 deletions(-)
create mode 100644 arch/powerpc/include/asm/kasan.h
create mode 100644 arch/powerpc/kernel/early_32.c
create mode 100644 arch/powerpc/mm/kasan_init.c
--
2.13.3
^ permalink raw reply
* [PATCH] powerpc/traps: fix recoverability of machine check handling on book3s/32
From: Christophe Leroy @ 2019-01-22 14:11 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
Nicholas Piggin
Cc: linuxppc-dev, linux-kernel
Looks like book3s/32 doesn't set RI on machine check, so
checking RI before calling die() will always be fatal
allthought this is not an issue in most cases.
Fixes: b96672dd840f ("powerpc: Machine check interrupt is a non-maskable interrupt")
Fixes: daf00ae71dad ("powerpc/traps: restore recoverability of machine_check interrupts")
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Cc: stable@vger.kernel.org
---
arch/powerpc/kernel/traps.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 64936b60d521..c740f8bfccc9 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -763,15 +763,15 @@ void machine_check_exception(struct pt_regs *regs)
if (check_io_access(regs))
goto bail;
- /* Must die if the interrupt is not recoverable */
- if (!(regs->msr & MSR_RI))
- nmi_panic(regs, "Unrecoverable Machine check");
-
if (!nested)
nmi_exit();
die("Machine check", regs, SIGBUS);
+ /* Must die if the interrupt is not recoverable */
+ if (!(regs->msr & MSR_RI))
+ nmi_panic(regs, "Unrecoverable Machine check");
+
return;
bail:
--
2.13.3
^ permalink raw reply related
* [PATCH] powerpc/selftest: fix type of mftb() in null_syscall
From: Christophe Leroy @ 2019-01-22 13:54 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel, linux-kselftest
All callers of mftb() expect 'unsigned long', and the function itself
only returns lower part of the TB so it really is 'unsigned long'
not 'unsigned long long'
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
tools/testing/selftests/powerpc/benchmarks/null_syscall.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
index ecc14d68e101..908de689a902 100644
--- a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
+++ b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
@@ -25,7 +25,7 @@ unsigned long long clock_frequency;
unsigned long long timebase_frequency;
double timebase_multiplier;
-static inline unsigned long long mftb(void)
+static inline unsigned long mftb(void)
{
unsigned long low;
--
2.13.3
^ permalink raw reply related
* [PATCH] powerpc/32: Fix CONFIG_VIRT_CPU_ACCOUNTING_NATIVE for 40x/booke
From: Christophe Leroy @ 2019-01-22 13:53 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
40x/booke have another path to reach 3f from transfer_to_handler,
so ACCOUNT_CPU_USER_ENTRY() have to be moved there.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/entry_32.S | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index c2b66fbbf7f0..0bcec062c4b8 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -185,12 +185,6 @@ transfer_to_handler:
addi r12,r12,-1
stw r12,4(r11)
#endif
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
- CURRENT_THREAD_INFO(r9, r1)
- tophys(r9, r9)
- ACCOUNT_CPU_USER_ENTRY(r9, r11, r12)
-#endif
-
b 3f
2: /* if from kernel, check interrupted DOZE/NAP mode and
@@ -208,9 +202,14 @@ transfer_to_handler:
bt- 31-TLF_NAPPING,4f
bt- 31-TLF_SLEEPING,7f
#endif /* CONFIG_PPC_BOOK3S_32 || CONFIG_E500 */
+3:
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ CURRENT_THREAD_INFO(r9, r1)
+ tophys(r9, r9)
+ ACCOUNT_CPU_USER_ENTRY(r9, r11, r12)
+#endif
.globl transfer_to_handler_cont
transfer_to_handler_cont:
-3:
mflr r9
lwz r11,0(r9) /* virtual address of handler */
lwz r9,4(r9) /* where to go when done */
--
2.13.3
^ permalink raw reply related
* [PATCH] powerpc/32: Remove unneccessary MSR[RI] clearing for 8xx
From: Christophe Leroy @ 2019-01-22 13:52 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
MSR[RI] has already been cleared a few lines above.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/entry_32.S | 3 ---
1 file changed, 3 deletions(-)
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 0768dfd8a64e..c2b66fbbf7f0 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -997,9 +997,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX)
.globl exc_exit_restart
exc_exit_restart:
lwz r12,_NIP(r1)
-#if defined(CONFIG_PPC_8xx) && defined(CONFIG_PERF_EVENTS)
- mtspr SPRN_NRI, r0
-#endif
mtspr SPRN_SRR0,r12
mtspr SPRN_SRR1,r9
REST_4GPRS(9, r1)
--
2.13.3
^ permalink raw reply related
* Re: [PATCH v3 0/4] Add NXP AUDMIX device and machine drivers
From: Viorel Suman @ 2019-01-22 11:39 UTC (permalink / raw)
To: robh@kernel.org, nicoleotsuka@gmail.com
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, timur@kernel.org,
Xiubo.Lee@gmail.com, lgirdwood@gmail.com, S.j. Wang,
linux-kernel@vger.kernel.org, Daniel Baluta, tiwai@suse.com,
viorel.suman@gmail.com, Fabio Estevam, broonie@kernel.org,
dl-linux-imx, Cosmin Samoila, perex@perex.cz,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20190121152335.GA12284@bogus>
Hi Rob,
On Lu, 2019-01-21 at 09:23 -0600, Rob Herring wrote:
> On Fri, Jan 18, 2019 at 11:46:42AM -0800, Nicolin Chen wrote:
> >
> > On Fri, Jan 18, 2019 at 01:16:24PM +0000, Viorel Suman wrote:
> > >
> > > >
> > > > >
> > > > > 1. Moved "dais" node from machine driver DTS node to device
> > > > > driver
> > > > > DTS node
> > > > > as suggested by Rob.
> > > > That was not what I suggested. You still have a virtual node
> > > > which
> > > > looks to me to be unnecessary.
> > > To me removing virtual node implies that AUDMIX machine driver
> > > (imx-
> > > audmix.c + virtual node) shall be removed and machine driver code
> > > merged into device driver (fsl_audmix.c + device node) - please
> > > let me
> > > know if my understanding is wrong.
> > We could use a non-DT configuration right? From the driver logic,
> > DT just registers a device corresponding to the machine driver so
> > that it can probe(). We could register one in fsl_audmix instead.
> > Please refer to how fsl_ssi registers the sound card device. The
> > machine driver can get audmix_np from the parent device pointer,
> > and I think that's all you need.
> Yes.
Thank you, sent V4 which implements the approach suggested by Nicolin.
>
> >
> > Or maybe someone else would provide a better way. But it'd work.
> Or the machine driver could create the audmix device. That probably
> makes less sense, but either way there doesn't have to be a 1-1
> correspondence of DT nodes and (platform) devices.
>
> I'm not an ASoC expert, but why can't the machine driver just control
> the audmix directly (with DAIs as separate drivers)? Is the audmix
> ever going to a be a component for a different machine driver?
>
> Rob
Currently I'm not aware of any information with regard to if audmix is
ever going to work with other DAIs than SAI. Howerver from audmix IP
block integration perspective the only requirement is that the input
DAI must be connected to audmix over I2S bus, possible DAI options are
SAI, ESAI, etc - I think the approach to mix both device and machine
drivers into a single entity is not the best way to go.
/Viorel
^ permalink raw reply
* Re: [PATCH v3 0/4] Add NXP AUDMIX device and machine drivers
From: Viorel Suman @ 2019-01-22 11:19 UTC (permalink / raw)
To: nicoleotsuka@gmail.com
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, timur@kernel.org,
Xiubo.Lee@gmail.com, lgirdwood@gmail.com, S.j. Wang,
linux-kernel@vger.kernel.org, Daniel Baluta, tiwai@suse.com,
viorel.suman@gmail.com, Fabio Estevam, broonie@kernel.org,
dl-linux-imx, Cosmin Samoila, perex@perex.cz,
linuxppc-dev@lists.ozlabs.org, robh@kernel.org
In-Reply-To: <20190118194642.GA11798@Asurada-Nvidia.nvidia.com>
Hi Nicolin,
On Vi, 2019-01-18 at 11:46 -0800, Nicolin Chen wrote:
> On Fri, Jan 18, 2019 at 01:16:24PM +0000, Viorel Suman wrote:
> >
> > >
> > > >
> > > > 1. Moved "dais" node from machine driver DTS node to device
> > > > driver
> > > > DTS node
> > > > as suggested by Rob.
> > > That was not what I suggested. You still have a virtual node
> > > which
> > > looks to me to be unnecessary.
> > To me removing virtual node implies that AUDMIX machine driver
> > (imx-
> > audmix.c + virtual node) shall be removed and machine driver code
> > merged into device driver (fsl_audmix.c + device node) - please let
> > me
> > know if my understanding is wrong.
> We could use a non-DT configuration right? From the driver logic,
> DT just registers a device corresponding to the machine driver so
> that it can probe(). We could register one in fsl_audmix instead.
> Please refer to how fsl_ssi registers the sound card device. The
> machine driver can get audmix_np from the parent device pointer,
> and I think that's all you need.
>
> Or maybe someone else would provide a better way. But it'd work.
Sent V4 - it implements the approach you suggested. Thank you for the
hint, works well indeed.
Regards,
Viorel
^ permalink raw reply
* [PATCH v4 3/3] ASoC: fsl: Add Audio Mixer machine driver
From: Viorel Suman @ 2019-01-22 11:14 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Mark Rutland,
Jaroslav Kysela, Takashi Iwai, Timur Tabi, Nicolin Chen, Xiubo Li,
Fabio Estevam, Viorel Suman, S.j. Wang, Daniel Baluta,
Cosmin Samoila
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
linux-kernel@vger.kernel.org, Viorel Suman, dl-linux-imx,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1548155654-12985-1-git-send-email-viorel.suman@nxp.com>
This patch implements Audio Mixer machine driver for NXP iMX8 SOCs.
It connects together Audio Mixer and related SAI instances.
Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
---
sound/soc/fsl/Kconfig | 9 ++
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/imx-audmix.c | 327 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 338 insertions(+)
create mode 100644 sound/soc/fsl/imx-audmix.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 0af2e056..d87c842 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -303,6 +303,15 @@ config SND_SOC_FSL_ASOC_CARD
CS4271, CS4272 and SGTL5000.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
+config SND_SOC_IMX_AUDMIX
+ tristate "SoC Audio support for i.MX boards with AUDMIX"
+ select SND_SOC_FSL_AUDMIX
+ select SND_SOC_FSL_SAI
+ help
+ SoC Audio support for i.MX boards with Audio Mixer
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ an Audio Mixer.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 4172d5a..c0dd044 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -62,6 +62,7 @@ snd-soc-imx-es8328-objs := imx-es8328.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
+snd-soc-imx-audmix-objs := imx-audmix.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
+obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
new file mode 100644
index 0000000..72e37ca
--- /dev/null
+++ b/sound/soc/fsl/imx-audmix.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
+#include "fsl_sai.h"
+#include "fsl_audmix.h"
+
+struct imx_audmix {
+ struct platform_device *pdev;
+ struct snd_soc_card card;
+ struct platform_device *audmix_pdev;
+ struct platform_device *out_pdev;
+ struct clk *cpu_mclk;
+ int num_dai;
+ struct snd_soc_dai_link *dai;
+ int num_dai_conf;
+ struct snd_soc_codec_conf *dai_conf;
+ int num_dapm_routes;
+ struct snd_soc_dapm_route *dapm_routes;
+};
+
+static const u32 imx_audmix_rates[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints = {
+ .count = ARRAY_SIZE(imx_audmix_rates),
+ .list = imx_audmix_rates,
+};
+
+static int imx_audmix_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_audmix *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = rtd->card->dev;
+ unsigned long clk_rate = clk_get_rate(priv->cpu_mclk);
+ int ret;
+
+ if (clk_rate % 24576000 == 0) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &imx_audmix_rate_constraints);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_warn(dev, "mclk may be not supported %lu\n", clk_rate);
+ }
+
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 8);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ FSL_AUDMIX_FORMATS);
+}
+
+static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ u32 channels = params_channels(params);
+ int ret, dir;
+
+ /* For playback the AUDMIX is slave, and for record is master */
+ fmt |= tx ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM;
+ dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN;
+
+ /* set DAI configuration */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ if (ret) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_MAST1, 0, dir);
+ if (ret) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Per datasheet, AUDMIX expects 8 slots and 32 bits
+ * for every slot in TDM mode.
+ */
+ ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, BIT(channels) - 1,
+ BIT(channels) - 1, 8, 32);
+ if (ret)
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+
+ return ret;
+}
+
+static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ int ret;
+
+ if (!tx)
+ return 0;
+
+ /* For playback the AUDMIX is slave */
+ fmt |= SND_SOC_DAIFMT_CBM_CFM;
+
+ /* set AUDMIX DAI configuration */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ if (ret)
+ dev_err(dev, "failed to set AUDMIX DAI fmt: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops imx_audmix_fe_ops = {
+ .startup = imx_audmix_fe_startup,
+ .hw_params = imx_audmix_fe_hw_params,
+};
+
+static struct snd_soc_ops imx_audmix_be_ops = {
+ .hw_params = imx_audmix_be_hw_params,
+};
+
+static int imx_audmix_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *audmix_np = NULL, *out_cpu_np = NULL;
+ struct platform_device *audmix_pdev = NULL;
+ struct platform_device *cpu_pdev;
+ struct of_phandle_args args;
+ struct imx_audmix *priv;
+ int i, num_dai, ret;
+ const char *fe_name_pref = "HiFi-AUDMIX-FE-";
+ char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name;
+
+ if (pdev->dev.parent) {
+ audmix_np = pdev->dev.parent->of_node;
+ } else {
+ dev_err(&pdev->dev, "Missing parent device.\n");
+ return -EINVAL;
+ }
+
+ if (!audmix_np) {
+ dev_err(&pdev->dev, "Missign DT node for parent device.\n");
+ return -EINVAL;
+ }
+
+ audmix_pdev = of_find_device_by_node(audmix_np);
+ if (!audmix_pdev) {
+ dev_err(&pdev->dev, "Missing AUDMIX platform device for %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ num_dai = of_count_phandle_with_args(audmix_np, "dais", NULL);
+ if (num_dai != FSL_AUDMIX_MAX_DAIS) {
+ dev_err(&pdev->dev, "Need 2 dais to be provided for %s\n",
+ audmix_np->full_name);
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->num_dai = 2 * num_dai;
+ priv->dai = devm_kzalloc(&pdev->dev, priv->num_dai *
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->num_dai_conf = num_dai;
+ priv->dai_conf = devm_kzalloc(&pdev->dev, priv->num_dai_conf *
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!priv->dai_conf)
+ return -ENOMEM;
+
+ priv->num_dapm_routes = 3 * num_dai;
+ priv->dapm_routes = devm_kzalloc(&pdev->dev, priv->num_dapm_routes *
+ sizeof(struct snd_soc_dapm_route),
+ GFP_KERNEL);
+ if (!priv->dapm_routes)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dai; i++) {
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
+ &args);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n");
+ return ret;
+ }
+
+ cpu_pdev = of_find_device_by_node(args.np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+
+ dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
+ fe_name_pref, args.np->full_name + 1);
+
+ dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
+
+ if (i == 0) {
+ out_cpu_np = args.np;
+ capture_dai_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, "CPU-Capture");
+ }
+
+ priv->dai[i].name = dai_name;
+ priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
+ priv->dai[i].codec_dai_name = "snd-soc-dummy-dai";
+ priv->dai[i].codec_name = "snd-soc-dummy";
+ priv->dai[i].cpu_of_node = args.np;
+ priv->dai[i].cpu_dai_name = dev_name(&cpu_pdev->dev);
+ priv->dai[i].platform_of_node = args.np;
+ priv->dai[i].dynamic = 1;
+ priv->dai[i].dpcm_playback = 1;
+ priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
+ priv->dai[i].ignore_pmdown_time = 1;
+ priv->dai[i].ops = &imx_audmix_fe_ops;
+
+ /* Add AUDMIX Backend */
+ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "audmix-%d", i);
+ be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "AUDMIX-Playback-%d", i);
+ be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "AUDMIX-Capture-%d", i);
+
+ priv->dai[num_dai + i].name = be_name;
+ priv->dai[num_dai + i].codec_dai_name = "snd-soc-dummy-dai";
+ priv->dai[num_dai + i].codec_name = "snd-soc-dummy";
+ priv->dai[num_dai + i].cpu_of_node = audmix_np;
+ priv->dai[num_dai + i].cpu_dai_name = be_name;
+ priv->dai[num_dai + i].platform_name = "snd-soc-dummy";
+ priv->dai[num_dai + i].no_pcm = 1;
+ priv->dai[num_dai + i].dpcm_playback = 1;
+ priv->dai[num_dai + i].dpcm_capture = 1;
+ priv->dai[num_dai + i].ignore_pmdown_time = 1;
+ priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
+
+ priv->dai_conf[i].of_node = args.np;
+ priv->dai_conf[i].name_prefix = dai_name;
+
+ priv->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, "CPU-Playback");
+ priv->dapm_routes[i].sink = be_pb;
+ priv->dapm_routes[num_dai + i].source = be_pb;
+ priv->dapm_routes[num_dai + i].sink = be_cp;
+ priv->dapm_routes[2 * num_dai + i].source = be_cp;
+ priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name;
+ }
+
+ cpu_pdev = of_find_device_by_node(out_cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+ priv->cpu_mclk = devm_clk_get(&cpu_pdev->dev, "mclk1");
+ if (IS_ERR(priv->cpu_mclk)) {
+ ret = PTR_ERR(priv->cpu_mclk);
+ dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret);
+ return -EINVAL;
+ }
+
+ priv->audmix_pdev = audmix_pdev;
+ priv->out_pdev = cpu_pdev;
+
+ priv->card.dai_link = priv->dai;
+ priv->card.num_links = priv->num_dai;
+ priv->card.codec_conf = priv->dai_conf;
+ priv->card.num_configs = priv->num_dai_conf;
+ priv->card.dapm_routes = priv->dapm_routes;
+ priv->card.num_dapm_routes = priv->num_dapm_routes;
+ priv->card.dev = pdev->dev.parent;
+ priv->card.owner = THIS_MODULE;
+ priv->card.name = "imx-audmix";
+
+ platform_set_drvdata(pdev, &priv->card);
+ snd_soc_card_set_drvdata(&priv->card, priv);
+
+ ret = devm_snd_soc_register_card(pdev->dev.parent, &priv->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct platform_driver imx_audmix_driver = {
+ .probe = imx_audmix_probe,
+ .driver = {
+ .name = "imx-audmix",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(imx_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:imx-audmix");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v4 0/3] Add NXP AUDMIX device and machine drivers
From: Viorel Suman @ 2019-01-22 11:14 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Mark Rutland,
Jaroslav Kysela, Takashi Iwai, Timur Tabi, Nicolin Chen, Xiubo Li,
Fabio Estevam, Viorel Suman, S.j. Wang, Daniel Baluta,
Cosmin Samoila
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
linux-kernel@vger.kernel.org, Viorel Suman, dl-linux-imx,
linuxppc-dev@lists.ozlabs.org
The patchset adds NXP Audio Mixer (AUDMIX) device and machine
drivers and related DT bindings documentation.
Changes since V3:
1. Removed machine driver DT bindings documentation.
2. Trigger machine driver probe from device driver as suggested by Nicolin.
Changes since V2:
1. Moved "dais" node from machine driver DTS node to device driver DTS node
as suggested by Rob.
Changes since V1:
1. Original patch split into distinct patches for the device driver and
DT binding documentation.
2. Replaced AMIX with AUDMIX in both code and file names as it looks more
RM-compliant.
3. Removed polarity control from CPU DAI driver as suggested by Nicolin.
4. Added machine driver and related DT binding documentation.
Viorel Suman (3):
ASoC: fsl: Add Audio Mixer CPU DAI driver
ASoC: add fsl_audmix DT binding documentation
ASoC: fsl: Add Audio Mixer machine driver
.../devicetree/bindings/sound/fsl,audmix.txt | 54 ++
sound/soc/fsl/Kconfig | 16 +
sound/soc/fsl/Makefile | 5 +
sound/soc/fsl/fsl_audmix.c | 576 +++++++++++++++++++++
sound/soc/fsl/fsl_audmix.h | 102 ++++
sound/soc/fsl/imx-audmix.c | 327 ++++++++++++
6 files changed, 1080 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,audmix.txt
create mode 100644 sound/soc/fsl/fsl_audmix.c
create mode 100644 sound/soc/fsl/fsl_audmix.h
create mode 100644 sound/soc/fsl/imx-audmix.c
--
2.7.4
^ permalink raw reply
* [PATCH v4 2/3] ASoC: add fsl_audmix DT binding documentation
From: Viorel Suman @ 2019-01-22 11:14 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Mark Rutland,
Jaroslav Kysela, Takashi Iwai, Timur Tabi, Nicolin Chen, Xiubo Li,
Fabio Estevam, Viorel Suman, S.j. Wang, Daniel Baluta,
Cosmin Samoila
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
linux-kernel@vger.kernel.org, Viorel Suman, dl-linux-imx,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1548155654-12985-1-git-send-email-viorel.suman@nxp.com>
Add the DT binding documentation for NXP Audio Mixer
CPU DAI driver.
Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
---
.../devicetree/bindings/sound/fsl,audmix.txt | 54 ++++++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,audmix.txt
diff --git a/Documentation/devicetree/bindings/sound/fsl,audmix.txt b/Documentation/devicetree/bindings/sound/fsl,audmix.txt
new file mode 100644
index 0000000..45f807e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,audmix.txt
@@ -0,0 +1,54 @@
+NXP Audio Mixer (AUDMIX).
+
+The Audio Mixer is a on-chip functional module that allows mixing of two
+audio streams into a single audio stream. Audio Mixer has two input serial
+audio interfaces. These are driven by two Synchronous Audio interface
+modules (SAI). Each input serial interface carries 8 audio channels in its
+frame in TDM manner. Mixer mixes audio samples of corresponding channels
+from two interfaces into a single sample. Before mixing, audio samples of
+two inputs can be attenuated based on configuration. The output of the
+Audio Mixer is also a serial audio interface. Like input interfaces it has
+the same TDM frame format. This output is used to drive the serial DAC TDM
+interface of audio codec and also sent to the external pins along with the
+receive path of normal audio SAI module for readback by the CPU.
+
+The output of Audio Mixer can be selected from any of the three streams
+ - serial audio input 1
+ - serial audio input 2
+ - mixed audio
+
+Mixing operation is independent of audio sample rate but the two audio
+input streams must have same audio sample rate with same number of channels
+in TDM frame to be eligible for mixing.
+
+Device driver required properties:
+=================================
+ - compatible : Compatible list, contains "fsl,imx8qm-audmix"
+
+ - reg : Offset and length of the register set for the device.
+
+ - clocks : Must contain an entry for each entry in clock-names.
+
+ - clock-names : Must include the "ipg" for register access.
+
+ - power-domains : Must contain the phandle to AUDMIX power domain node
+
+ - dais : Must contain a list of phandles to AUDMIX connected
+ DAIs. The current implementation requires two phandles
+ to SAI interfaces to be provided, the first SAI in the
+ list being used to route the AUDMIX output.
+
+ - model : Must contain machine driver name which will configure
+ and instantiate the appropriate audio card.
+
+Device driver configuration example:
+======================================
+ audmix: audmix@59840000 {
+ compatible = "fsl,imx8qm-audmix";
+ reg = <0x0 0x59840000 0x0 0x10000>;
+ clocks = <&clk IMX8QXP_AUD_AUDMIX_IPG>;
+ clock-names = "ipg";
+ power-domains = <&pd_audmix>;
+ dais = <&sai4>, <&sai5>;
+ model = "imx-audmix";
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH v4 1/3] ASoC: fsl: Add Audio Mixer CPU DAI driver
From: Viorel Suman @ 2019-01-22 11:14 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Mark Rutland,
Jaroslav Kysela, Takashi Iwai, Timur Tabi, Nicolin Chen, Xiubo Li,
Fabio Estevam, Viorel Suman, S.j. Wang, Daniel Baluta,
Cosmin Samoila
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
linux-kernel@vger.kernel.org, Viorel Suman, dl-linux-imx,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1548155654-12985-1-git-send-email-viorel.suman@nxp.com>
This patch implements Audio Mixer CPU DAI driver for NXP iMX8 SOCs.
The Audio Mixer is a on-chip functional module that allows mixing of
two audio streams into a single audio stream.
Audio Mixer datasheet is available here:
https://www.nxp.com/docs/en/reference-manual/IMX8DQXPRM.pdf
Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
---
sound/soc/fsl/Kconfig | 7 +
sound/soc/fsl/Makefile | 3 +
sound/soc/fsl/fsl_audmix.c | 576 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/fsl/fsl_audmix.h | 102 ++++++++
4 files changed, 688 insertions(+)
create mode 100644 sound/soc/fsl/fsl_audmix.c
create mode 100644 sound/soc/fsl/fsl_audmix.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 7b1d997..0af2e056 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -24,6 +24,13 @@ config SND_SOC_FSL_SAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_AUDMIX
+ tristate "Audio Mixer (AUDMIX) module support"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Audio Mixer (AUDMIX)
+ support for the NXP iMX CPUs.
+
config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module (SSI) support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 3c0ff31..4172d5a 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,6 +12,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-audmix-objs := fsl_audmix.o
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
@@ -22,6 +23,8 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+
+obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
new file mode 100644
index 0000000..3356cb6
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_audmix.h"
+
+#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
+ SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+static const char
+ *tdm_sel[] = { "TDM1", "TDM2", },
+ *mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
+ *width_sel[] = { "16b", "18b", "20b", "24b", "32b", },
+ *endis_sel[] = { "Disabled", "Enabled", },
+ *updn_sel[] = { "Downward", "Upward", },
+ *mask_sel[] = { "Unmask", "Mask", };
+
+static const struct soc_enum fsl_audmix_enum[] = {
+/* FSL_AUDMIX_CTR enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel),
+/* FSL_AUDMIX_ATCR0 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel),
+/* FSL_AUDMIX_ATCR1 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel),
+};
+
+struct fsl_audmix_state {
+ u8 tdms;
+ u8 clk;
+ char msg[64];
+};
+
+static const struct fsl_audmix_state prms[4][4] = {{
+ /* DIS->DIS, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* DIS->TDM1*/
+ { .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
+ /* DIS->TDM2*/
+ { .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
+ /* DIS->MIX */
+ { .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
+}, { /* TDM1->DIS */
+ { .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
+ /* TDM1->TDM1, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM1->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
+ /* TDM1->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
+}, { /* TDM2->DIS */
+ { .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
+ /* TDM2->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
+ /* TDM2->TDM2, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM2->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
+}, { /* MIX->DIS */
+ { .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
+ /* MIX->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
+ /* MIX->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
+ /* MIX->MIX, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" }
+}, };
+
+static int fsl_audmix_state_trans(struct snd_soc_component *comp,
+ unsigned int *mask, unsigned int *ctr,
+ const struct fsl_audmix_state prm)
+{
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ /* Enforce all required TDMs are started */
+ if ((priv->tdms & prm.tdms) != prm.tdms) {
+ dev_dbg(comp->dev, prm.msg);
+ return -EINVAL;
+ }
+
+ switch (prm.clk) {
+ case 1:
+ case 2:
+ /* Set mix clock */
+ (*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
+ (*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int reg_val, val, mix_clk;
+ int ret = 0;
+
+ /* Get current state */
+ ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, ®_val);
+ if (ret)
+ return ret;
+
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ if (!(priv->tdms & BIT(val))) {
+ dev_err(comp->dev,
+ "The selected clock source has no TDM%d enabled!\n",
+ val + 1);
+ return -EINVAL;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ u32 out_src, mix_clk;
+ unsigned int reg_val, val, mask = 0, ctr = 0;
+ int ret = 0;
+
+ /* Get current state */
+ ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, ®_val);
+ if (ret)
+ return ret;
+
+ /* "From" state */
+ out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
+ >> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+
+ /* "To" state */
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /* Check if state is changing ... */
+ if (out_src == val)
+ return 0;
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ /* Check state transition constraints */
+ ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
+ if (ret)
+ return ret;
+
+ /* Complete transition to new state */
+ mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
+ ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
+ /* FSL_AUDMIX_CTR controls */
+ SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
+ snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
+ SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
+ snd_soc_get_enum_double, fsl_audmix_put_out_src),
+ SOC_ENUM("Output Width", fsl_audmix_enum[2]),
+ SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
+ SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
+ SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
+ SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
+ /* TDM1 Attenuation controls */
+ SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
+ SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
+ SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
+ 0, 0x3ffff, 0),
+ /* TDM2 Attenuation controls */
+ SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
+ SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
+ SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
+ 0, 0x3ffff, 0),
+};
+
+static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ u32 mask = 0, ctr = 0;
+
+ /* AUDMIX is working in DSP_A format only */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* For playback the AUDMIX is slave, and for record is master */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Output data will be written on positive edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Output data will be written on negative edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* Capture stream shall not be handled */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ priv->tdms |= BIT(dai->driver->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->tdms &= ~BIT(dai->driver->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
+ .set_fmt = fsl_audmix_dai_set_fmt,
+ .trigger = fsl_audmix_dai_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_audmix_dai[] = {
+ {
+ .id = 0,
+ .name = "audmix-0",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AUDMIX-Capture-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+ {
+ .id = 1,
+ .name = "audmix-1",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-1",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AUDMIX-Capture-1",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver fsl_audmix_component = {
+ .name = "fsl-audmix-dai",
+ .controls = fsl_audmix_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
+};
+
+static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_STR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATTNVAL0:
+ case FSL_AUDMIX_ATSTP0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ case FSL_AUDMIX_ATTNVAL1:
+ case FSL_AUDMIX_ATSTP1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default fsl_audmix_reg[] = {
+ { FSL_AUDMIX_CTR, 0x00060 },
+ { FSL_AUDMIX_STR, 0x00003 },
+ { FSL_AUDMIX_ATCR0, 0x00000 },
+ { FSL_AUDMIX_ATIVAL0, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN0, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT0, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL0, 0x00000 },
+ { FSL_AUDMIX_ATSTP0, 0x00000 },
+ { FSL_AUDMIX_ATCR1, 0x00000 },
+ { FSL_AUDMIX_ATIVAL1, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN1, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT1, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL1, 0x00000 },
+ { FSL_AUDMIX_ATSTP1, 0x00000 },
+};
+
+static const struct regmap_config fsl_audmix_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_AUDMIX_ATSTP1,
+ .reg_defaults = fsl_audmix_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
+ .readable_reg = fsl_audmix_readable_reg,
+ .writeable_reg = fsl_audmix_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int fsl_audmix_probe(struct platform_device *pdev)
+{
+ struct fsl_audmix *priv;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+ const char *sprop;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the addresses */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ipg", regs,
+ &fsl_audmix_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(priv->ipg_clk)) {
+ dev_err(&pdev->dev, "failed to get ipg clock\n");
+ return PTR_ERR(priv->ipg_clk);
+ }
+
+ platform_set_drvdata(pdev, priv);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_audmix_component,
+ fsl_audmix_dai,
+ ARRAY_SIZE(fsl_audmix_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+ return ret;
+ }
+
+ sprop = of_get_property(pdev->dev.of_node, "model", NULL);
+ if (sprop) {
+ priv->pdev = platform_device_register_data(&pdev->dev, sprop, 0,
+ NULL, 0);
+ if (IS_ERR(priv->pdev)) {
+ ret = PTR_ERR(priv->pdev);
+ dev_err(&pdev->dev,
+ "failed to register platform %s: %d\n", sprop,
+ ret);
+ }
+ } else {
+ dev_err(&pdev->dev, "[model] attribute missing.\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fsl_audmix_remove(struct platform_device *pdev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
+
+ if (priv->pdev)
+ platform_device_unregister(priv->pdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_audmix_runtime_resume(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(priv->regmap, false);
+ regcache_mark_dirty(priv->regmap);
+
+ return regcache_sync(priv->regmap);
+}
+
+static int fsl_audmix_runtime_suspend(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(priv->regmap, true);
+
+ clk_disable_unprepare(priv->ipg_clk);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops fsl_audmix_pm = {
+ SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend,
+ fsl_audmix_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id fsl_audmix_ids[] = {
+ { .compatible = "fsl,imx8qm-audmix", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
+
+static struct platform_driver fsl_audmix_driver = {
+ .probe = fsl_audmix_probe,
+ .remove = fsl_audmix_remove,
+ .driver = {
+ .name = "fsl-audmix",
+ .of_match_table = fsl_audmix_ids,
+ .pm = &fsl_audmix_pm,
+ },
+};
+module_platform_driver(fsl_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:fsl-audmix");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h
new file mode 100644
index 0000000..7812ffe
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#ifndef __FSL_AUDMIX_H
+#define __FSL_AUDMIX_H
+
+#define FSL_AUDMIX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+/* AUDMIX Registers */
+#define FSL_AUDMIX_CTR 0x200 /* Control */
+#define FSL_AUDMIX_STR 0x204 /* Status */
+
+#define FSL_AUDMIX_ATCR0 0x208 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL0 0x20c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP0 0x210 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN0 0x214 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT0 0x218 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL0 0x21c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP0 0x220 /* Attenuation step number */
+
+#define FSL_AUDMIX_ATCR1 0x228 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL1 0x22c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP1 0x230 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN1 0x234 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT1 0x238 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL1 0x23c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP1 0x240 /* Attenuation step number */
+
+/* AUDMIX Control Register */
+#define FSL_AUDMIX_CTR_MIXCLK_SHIFT 0
+#define FSL_AUDMIX_CTR_MIXCLK_MASK BIT(FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_MIXCLK(i) ((i) << FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC_SHIFT 1
+#define FSL_AUDMIX_CTR_OUTSRC_MASK (0x3 << FSL_AUDMIX_CTR_OUTSRC_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC(i) (((i) << FSL_AUDMIX_CTR_OUTSRC_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTSRC_MASK)
+#define FSL_AUDMIX_CTR_OUTWIDTH_SHIFT 3
+#define FSL_AUDMIX_CTR_OUTWIDTH_MASK (0x7 << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)
+#define FSL_AUDMIX_CTR_OUTWIDTH(i) (((i) << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTWIDTH_MASK)
+#define FSL_AUDMIX_CTR_OUTCKPOL_SHIFT 6
+#define FSL_AUDMIX_CTR_OUTCKPOL_MASK BIT(FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_OUTCKPOL(i) ((i) << FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF_SHIFT 7
+#define FSL_AUDMIX_CTR_MASKRTDF_MASK BIT(FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF(i) ((i) << FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF_SHIFT 8
+#define FSL_AUDMIX_CTR_MASKCKDF_MASK BIT(FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF(i) ((i) << FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE_SHIFT 9
+#define FSL_AUDMIX_CTR_SYNCMODE_MASK BIT(FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE(i) ((i) << FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC_SHIFT 10
+#define FSL_AUDMIX_CTR_SYNCSRC_MASK BIT(FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC(i) ((i) << FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+
+/* AUDMIX Status Register */
+#define FSL_AUDMIX_STR_RATEDIFF BIT(0)
+#define FSL_AUDMIX_STR_CLKDIFF BIT(1)
+#define FSL_AUDMIX_STR_MIXSTAT_SHIFT 2
+#define FSL_AUDMIX_STR_MIXSTAT_MASK (0x3 << FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+#define FSL_AUDMIX_STR_MIXSTAT(i) (((i) & FSL_AUDMIX_STR_MIXSTAT_MASK) \
+ >> FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+/* AUDMIX Attenuation Control Register */
+#define FSL_AUDMIX_ATCR_AT_EN BIT(0)
+#define FSL_AUDMIX_ATCR_AT_UPDN BIT(1)
+#define FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT 2
+#define FSL_AUDMIX_ATCR_ATSTPDFI_MASK \
+ (0xfff << FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT)
+
+/* AUDMIX Attenuation Initial Value Register */
+#define FSL_AUDMIX_ATIVAL_ATINVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Up Factor Register */
+#define FSL_AUDMIX_ATSTPUP_ATSTEPUP_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Down Factor Register */
+#define FSL_AUDMIX_ATSTPDN_ATSTEPDN_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Target Register */
+#define FSL_AUDMIX_ATSTPTGT_ATSTPTG_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Value Register */
+#define FSL_AUDMIX_ATTNVAL_ATCURVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Number Register */
+#define FSL_AUDMIX_ATSTP_STPCTR_MASK 0x3FFFF
+
+#define FSL_AUDMIX_MAX_DAIS 2
+struct fsl_audmix {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct clk *ipg_clk;
+ u8 tdms;
+};
+
+#endif /* __FSL_AUDMIX_H */
--
2.7.4
^ 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