Linux-Aspeed Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ARM: dts: aspeed: tiogapass: Enable VUART
From: Vijay Khemka @ 2019-03-05 20:01 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20190130181427.2469674-1-vijaykhemka@fb.com>

Please review below patch.

Regards
-Vijay

?On 1/30/19, 10:14 AM, "Vijay Khemka" <vijaykhemka@fb.com> wrote:

    Enabling vuart for Facebook tiogapass
    
    Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
    ---
     arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts | 5 +++++
     1 file changed, 5 insertions(+)
    
    diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
    index 42e0d7a8e8d0..a058fb2985f7 100644
    --- a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
    +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
    @@ -64,6 +64,11 @@
     	status = "okay";
     };
     
    +&vuart {
    +	// VUART Host Console
    +	status = "okay";
    +};
    +
     &uart1 {
     	// Host Console
     	status = "okay";
    -- 
    2.17.1
    
    


^ permalink raw reply

* [PATCH 2/6] drivers/misc: Add Aspeed XDMA engine driver
From: Arnd Bergmann @ 2019-03-05  8:01 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-3-git-send-email-eajames@linux.ibm.com>

On Mon, Mar 4, 2019 at 10:37 PM Eddie James <eajames@linux.ibm.com> wrote:
>
> The XDMA engine embedded in the AST2500 SOC performs PCI DMA operations
> between the SOC (acting as a BMC) and a host processor in a server.
>
> This commit adds a driver to control the XDMA engine and adds functions
> to initialize the hardware and memory and start DMA operations.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>

Hi Eddie,

Thanks for your submission! Overall this looks well-implemented, but
I fear we already have too many ways of doing the same thing at
the moment, and I would hope to avoid adding yet another user space
interface for a specific hardware that does this.

Your interface appears to be a fairly low-level variant, just doing
single DMA transfers through ioctls, but configuring the PCIe
endpoint over sysfs.

Please have a look at the drivers/pci/endpoint framework first
and see if you can work on top of that interface instead.
Even if it doesn't quite do what you need here, we may be
able to extend it in a way that works for you, and lets others
use the same user interface extensions in the future.

It may also be necessary to split out the DMA engine portion
into a regular drivers/dma/ back-end to make that fit in with
the PCIe endpoint framework.

If you have already tried this without success, please let us
know in the description what problems you have hit, and why you
decided to create a new framework instead.

> +/*
> + * aspeed_xdma_op
> + *
> + * upstream: boolean indicating the direction of the DMA operation; upstream
> + *           means a transfer from the BMC to the host
> + *
> + * host_addr: the DMA address on the host side, typically configured by PCI
> + *            subsystem
> + *
> + * len: the size of the transfer in bytes; it should be a multiple of 16 bytes
> + */
> +struct aspeed_xdma_op {
> +       __u8 upstream;
> +       __u64 host_addr;
> +       __u32 len;
> +} __packed;

Side-note: packed structures are generally not great user space
interfaces. Regardless of where we end up with this, I'd recommend
naturally aligning each member inside of the structure, and using
explicit padding here.


     Arnd

^ permalink raw reply

* [PATCH] usb: introduce usb_ep_type_string() function
From: Chunfeng Yun @ 2019-03-05  2:22 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20190301162141.GB19937@kroah.com>

Hi,
On Fri, 2019-03-01 at 17:21 +0100, Greg Kroah-Hartman wrote:
> On Fri, Mar 01, 2019 at 02:58:23PM +0800, Chunfeng Yun wrote:
> > In some places, the code prints a human-readable USB endpoint
> > transfer type (e.g. "bulk"). This involves a switch statement
> > sometimes wrapped around in ({ ... }) block leading to code
> > repetition.
> > To make this scenario easier, here introduces usb_ep_type_string()
> > function, which returns a human-readable name of provided
> > endpoint type.
> > It also changes a few places switch was used to use this
> > new function.
> > 
> > Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
> > ---
> >  drivers/usb/common/common.c              | 16 ++++++++++++++++
> >  drivers/usb/core/endpoint.c              | 18 ++----------------
> >  drivers/usb/core/hcd.c                   | 17 ++---------------
> >  drivers/usb/dwc3/debugfs.c               | 23 ++++-------------------
> >  drivers/usb/gadget/udc/aspeed-vhub/epn.c |  6 +-----
> >  drivers/usb/gadget/udc/dummy_hcd.c       | 16 +---------------
> >  drivers/usb/host/xhci-trace.h            | 19 ++-----------------
> >  include/linux/usb/ch9.h                  |  8 ++++++++
> >  8 files changed, 36 insertions(+), 87 deletions(-)
> > 
> > diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
> > index 48277bbc15e4..2174dd9ec176 100644
> > --- a/drivers/usb/common/common.c
> > +++ b/drivers/usb/common/common.c
> > @@ -16,6 +16,22 @@
> >  #include <linux/usb/otg.h>
> >  #include <linux/of_platform.h>
> >  
> > +static const char *const ep_type_names[] = {
> > +	[USB_ENDPOINT_XFER_CONTROL] = "ctrl",
> > +	[USB_ENDPOINT_XFER_ISOC] = "isoc",
> > +	[USB_ENDPOINT_XFER_BULK] = "bulk",
> > +	[USB_ENDPOINT_XFER_INT] = "intr",
> > +};
> > +
> > +const char *usb_ep_type_string(int ep_type)
> > +{
> > +	if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names))
> > +		return "unknown";
> > +
> > +	return ep_type_names[ep_type];
> > +}
> > +EXPORT_SYMBOL_GPL(usb_ep_type_string);
> > +
> >  const char *usb_otg_state_string(enum usb_otg_state state)
> >  {
> >  	static const char *const names[] = {
> > diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
> > index 1c2c04079676..afa43f9a47b2 100644
> > --- a/drivers/usb/core/endpoint.c
> > +++ b/drivers/usb/core/endpoint.c
> > @@ -60,23 +60,9 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
> >  			 char *buf)
> >  {
> >  	struct ep_device *ep = to_ep_device(dev);
> > -	char *type = "unknown";
> > +	int ep_type = usb_endpoint_type(ep->desc);
> >  
> > -	switch (usb_endpoint_type(ep->desc)) {
> > -	case USB_ENDPOINT_XFER_CONTROL:
> > -		type = "Control";
> > -		break;
> > -	case USB_ENDPOINT_XFER_ISOC:
> > -		type = "Isoc";
> > -		break;
> > -	case USB_ENDPOINT_XFER_BULK:
> > -		type = "Bulk";
> > -		break;
> > -	case USB_ENDPOINT_XFER_INT:
> > -		type = "Interrupt";
> > -		break;
> > -	}
> > -	return sprintf(buf, "%s\n", type);
> > +	return sprintf(buf, "%s\n", usb_ep_type_string(ep_type));
> 
> You just changed a user/kernel API here, the strings are now different
> from what they used to be :(
How about using these type string to keep them unchanged?

I need help from Felipe and Mathias to check whether the changes in dwc3
and xhci have side effect or not, and waiting for their comments

Thanks
> 
> That's not ok, odds are you will break tools if you do this.
> 
> >  }
> >  static DEVICE_ATTR_RO(type);
> >  
> > diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> > index 015b126ce455..193ee92b2fdb 100644
> > --- a/drivers/usb/core/hcd.c
> > +++ b/drivers/usb/core/hcd.c
> > @@ -1875,23 +1875,10 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
> >  		/* kick hcd */
> >  		unlink1(hcd, urb, -ESHUTDOWN);
> >  		dev_dbg (hcd->self.controller,
> > -			"shutdown urb %pK ep%d%s%s\n",
> > +			"shutdown urb %pK ep%d%s-%s\n",
> >  			urb, usb_endpoint_num(&ep->desc),
> >  			is_in ? "in" : "out",
> > -			({	char *s;
> > -
> > -				 switch (usb_endpoint_type(&ep->desc)) {
> > -				 case USB_ENDPOINT_XFER_CONTROL:
> > -					s = ""; break;
> > -				 case USB_ENDPOINT_XFER_BULK:
> > -					s = "-bulk"; break;
> > -				 case USB_ENDPOINT_XFER_INT:
> > -					s = "-intr"; break;
> > -				 default:
> > -					s = "-iso"; break;
> > -				};
> > -				s;
> > -			}));
> > +			usb_ep_type_string(usb_endpoint_type(&ep->desc)));
> 
> You also changed the message here for control endpoints, but that's not
> a big deal, it's ok.
> 
> >  		usb_put_urb (urb);
> >  
> >  		/* list contents may have changed */
> > diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
> > index 1c792710348f..d9e2a63835fe 100644
> > --- a/drivers/usb/dwc3/debugfs.c
> > +++ b/drivers/usb/dwc3/debugfs.c
> > @@ -750,28 +750,13 @@ static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
> >  	unsigned long		flags;
> >  
> >  	spin_lock_irqsave(&dwc->lock, flags);
> > -	if (!(dep->flags & DWC3_EP_ENABLED) ||
> > -			!dep->endpoint.desc) {
> > -		seq_printf(s, "--\n");
> > +	if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
> > +		seq_puts(s, "unknown\n");
> >  		goto out;
> >  	}
> >  
> > -	switch (usb_endpoint_type(dep->endpoint.desc)) {
> > -	case USB_ENDPOINT_XFER_CONTROL:
> > -		seq_printf(s, "control\n");
> > -		break;
> > -	case USB_ENDPOINT_XFER_ISOC:
> > -		seq_printf(s, "isochronous\n");
> > -		break;
> > -	case USB_ENDPOINT_XFER_BULK:
> > -		seq_printf(s, "bulk\n");
> > -		break;
> > -	case USB_ENDPOINT_XFER_INT:
> > -		seq_printf(s, "interrupt\n");
> > -		break;
> > -	default:
> > -		seq_printf(s, "--\n");
> > -	}
> > +	seq_printf(s, "%s\n",
> > +		   usb_ep_type_string(usb_endpoint_type(dep->endpoint.desc)));
> 
> It is debugfs, so no real API guarantees at all, but you did change the
> output format, and that's not always good.
got it
> 
> >  
> >  out:
> >  	spin_unlock_irqrestore(&dwc->lock, flags);
> > diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> > index 4a28e3fbeb0b..165b0a43670e 100644
> > --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> > +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> > @@ -593,10 +593,6 @@ static int ast_vhub_epn_disable(struct usb_ep* u_ep)
> >  static int ast_vhub_epn_enable(struct usb_ep* u_ep,
> >  			       const struct usb_endpoint_descriptor *desc)
> >  {
> > -	static const char *ep_type_string[] __maybe_unused = { "ctrl",
> > -							       "isoc",
> > -							       "bulk",
> > -							       "intr" };
> >  	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
> >  	struct ast_vhub_dev *dev;
> >  	struct ast_vhub *vhub;
> > @@ -646,7 +642,7 @@ static int ast_vhub_epn_enable(struct usb_ep* u_ep,
> >  	ep->epn.wedged = false;
> >  
> >  	EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
> > -	      ep->epn.is_in ? "in" : "out", ep_type_string[type],
> > +	      ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
> >  	      usb_endpoint_num(desc), maxpacket);
> >  
> >  	/* Can we use DMA descriptor mode ? */
> > diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
> > index baf72f95f0f1..40c6a484e232 100644
> > --- a/drivers/usb/gadget/udc/dummy_hcd.c
> > +++ b/drivers/usb/gadget/udc/dummy_hcd.c
> > @@ -617,21 +617,7 @@ static int dummy_enable(struct usb_ep *_ep,
> >  		_ep->name,
> >  		desc->bEndpointAddress & 0x0f,
> >  		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
> > -		({ char *val;
> > -		 switch (usb_endpoint_type(desc)) {
> > -		 case USB_ENDPOINT_XFER_BULK:
> > -			 val = "bulk";
> > -			 break;
> > -		 case USB_ENDPOINT_XFER_ISOC:
> > -			 val = "iso";
> > -			 break;
> > -		 case USB_ENDPOINT_XFER_INT:
> > -			 val = "intr";
> > -			 break;
> > -		 default:
> > -			 val = "ctrl";
> > -			 break;
> > -		 } val; }),
> > +		usb_ep_type_string(usb_endpoint_type(desc)),
> >  		max, ep->stream_en ? "enabled" : "disabled");
> >  
> >  	/* at this point real hardware should be NAKing transfers
> > diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
> > index 88b427434bd8..bac73d688f11 100644
> > --- a/drivers/usb/host/xhci-trace.h
> > +++ b/drivers/usb/host/xhci-trace.h
> > @@ -289,23 +289,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
> >  	),
> >  	TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
> >  			__entry->epnum, __entry->dir_in ? "in" : "out",
> > -			({ char *s;
> > -			switch (__entry->type) {
> > -			case USB_ENDPOINT_XFER_INT:
> > -				s = "intr";
> > -				break;
> > -			case USB_ENDPOINT_XFER_CONTROL:
> > -				s = "control";
> > -				break;
> > -			case USB_ENDPOINT_XFER_BULK:
> > -				s = "bulk";
> > -				break;
> > -			case USB_ENDPOINT_XFER_ISOC:
> > -				s = "isoc";
> > -				break;
> > -			default:
> > -				s = "UNKNOWN";
> > -			} s; }), __entry->urb, __entry->pipe, __entry->slot_id,
> > +			usb_ep_type_string(__entry->type),
> > +			__entry->urb, __entry->pipe, __entry->slot_id,
> >  			__entry->actual, __entry->length, __entry->num_mapped_sgs,
> >  			__entry->num_sgs, __entry->stream, __entry->flags
> >  		)
> 
> Another api change with "control" :(
> 
> I like the idea, but you can not break user/kernel apis like this
> without knowing the affects that could happen.
> 
> thanks,
> 
> greg k-h



^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-05  1:55 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <e521ac77-e040-4777-b2c0-4731b3cce797@www.fastmail.com>

On Mon, Mar 4, 2019 at 5:53 PM Andrew Jeffery <andrew@aj.id.au> wrote:
>
>
>
> On Tue, 5 Mar 2019, at 05:01, Patrick Venture wrote:
> > On Mon, Mar 4, 2019 at 7:45 AM Patrick Venture <venture@google.com> wrote:
> > >
> > > On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > >
> > > > Hi Patrick.
> > > >
> > > > I've got some minor comments, otherwise it looks reasonable to me.
> > > >
> > > > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > > > in the BMC's physical address space.  This feature is especially useful
> > > > > when using this bridge to send large files to the BMC.
> > > > >
> > > > > The host may use this to send down a firmware image by staging data at a
> > > > > specific memory address, and in a coordinated effort with the BMC's
> > > > > software stack and kernel, transmit the bytes.
> > > > >
> > > > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > > > configure it via ioctl to allow the host to write bytes to an agreed
> > > > > upon location.  In the primary use-case, the region to use is known
> > > > > apriori on the BMC, and the host requests this information.  Once this
> > > > > request is received, the BMC's software stack will enable the bridge and
> > > > > the region and then using some software flow control (possibly via IPMI
> > > > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > > > will disable the bridge and unset any region involved.
> > > > >
> > > > > The default behavior of this bridge when present is: enabled and all
> > > > > regions marked read-write.  This driver will fix the regions to be
> > > > > read-only and then disable the bridge entirely.
> > > > >
> > > > > The memory regions protected are:
> > > > >  * BMC flash MMIO window
> > > > >  * System flash MMIO windows
> > > > >  * SOC IO (peripheral MMIO)
> > > > >  * DRAM
> > > > >
> > > > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > > > the DRAM section is write-enabled, then it can write to all of it.
> > > > >
> > > > > Signed-off-by: Patrick Venture <venture@google.com>
> > > > > ---
> > > > > Changes for v5:
> > > > > - Fixup missing exit condition and remove extra jump.
> > > > > Changes for v4:
> > > > > - Added ioctl for reading back the memory-region configuration.
> > > > > - Cleaned up some unused variables.
> > > > > Changes for v3:
> > > > > - Deleted unused read and write methods.
> > > > > Changes for v2:
> > > > > - Dropped unused reads.
> > > > > - Moved call to disable bridge to before registering device.
> > > > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > > > - Updated the commit message. <<< TODO
> > > > > - Updated the bit flipped for SCU180_ENP2A
> > > > > - Dropped boolean region_specified variable.
> > > > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > > > - Updated commit message.
> > > > > ---
> > > > >  drivers/misc/Kconfig                 |   8 +
> > > > >  drivers/misc/Makefile                |   1 +
> > > > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > > > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > > > >  4 files changed, 524 insertions(+)
> > > > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > > > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > > > >
> > > > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > > > index f417b06e11c5..9de1bafe5606 100644
> > > > > --- a/drivers/misc/Kconfig
> > > > > +++ b/drivers/misc/Kconfig
> > > > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > > > >         bus. System Configuration interface is one of the possible means
> > > > >         of generating transactions on this bus.
> > > > >
> > > > > +config ASPEED_P2A_CTRL
> > > > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > > > +     help
> > > > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > > > through
> > > > > +       ioctl()s, the driver also provides an interface for userspace
> > > > > mappings to
> > > > > +       a pre-defined region.
> > > > > +
> > > > >  config ASPEED_LPC_CTRL
> > > > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > > > index e39ccbbc1b3a..57577aee354f 100644
> > > > > --- a/drivers/misc/Makefile
> > > > > +++ b/drivers/misc/Makefile
> > > > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > > > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > > > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > > > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > > > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > > > >  obj-$(CONFIG_OCXL)           += ocxl/
> > > > >  obj-y                                += cardreader/
> > > > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > > > new file mode 100644
> > > > > index 000000000000..6bde4f64632d
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > > > @@ -0,0 +1,456 @@
> > > > > +/*
> > > > > + * Copyright 2019 Google Inc
> > > > > + *
> > > > > + * This program is free software; you can redistribute it and/or
> > > > > + * modify it under the terms of the GNU General Public License
> > > > > + * as published by the Free Software Foundation; either version
> > > > > + * 2 of the License, or (at your option) any later version.
> > > >
> > > > Should be a SPDX header instead.
> > >
> > > Ok, so delete the above and drop in:
> >
> > Was set straight on this.
> >
> > >
> > > """
> > > /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > > """
> > >
> > > Or just add that to the top above the Google GNU copyright line?  (I'm
> > > not a lawyer).
> > >
> > > >
> > > > > + *
> > > > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > > > allows
> > > > > + * the host to read and write to various regions of the BMC's memory.
> > > > > + */
> > > > > +
> > > > > +#include <linux/fs.h>
> > > > > +#include <linux/io.h>
> > > > > +#include <linux/mfd/syscon.h>
> > > > > +#include <linux/miscdevice.h>
> > > > > +#include <linux/mm.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/mutex.h>
> > > > > +#include <linux/of_address.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <linux/platform_device.h>
> > > > > +#include <linux/regmap.h>
> > > > > +#include <linux/slab.h>
> > > > > +#include <linux/uaccess.h>
> > > > > +
> > > > > +#include <linux/aspeed-p2a-ctrl.h>
> > > > > +
> > > > > +#define DEVICE_NAME  "aspeed-p2a-ctrl"
> > > > > +
> > > > > +/* SCU2C is a Misc. Control Register. */
> > > > > +#define SCU2C 0x2c
> > > > > +/* SCU180 is the PCIe Configuration Setting Control Register. */
> > > > > +#define SCU180 0x180
> > > > > +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA
> > > > > device
> > > > > + * on the PCI bus. */
> > > >
> > > > As this wraps to multiple lines the trailing `*/` should be on a separate line.
> > > > There are a few instances of this throughout.
> > >
> > > Can do.  I will also run checkpath -- I had done it initially, but I
> > > forgot on later editions.
> > >
> > > >
> > > > > +#define SCU180_ENP2A BIT(1)
> > > > > +
> > > > > +/* The ast2400/2500 both have six ranges. */
> > > > > +#define P2A_REGION_COUNT 6
> > > > > +
> > > > > +struct region {
> > > > > +     u32 min;
> > > > > +     u32 max;
> > > > > +     u32 bit;
> > > > > +};
> > > > > +
> > > > > +struct aspeed_p2a_model_data {
> > > > > +     /* min, max, bit */
> > > > > +     struct region regions[P2A_REGION_COUNT];
> > > > > +};
> > > > > +
> > > > > +struct aspeed_p2a_ctrl {
> > > > > +     struct miscdevice miscdev;
> > > > > +     struct regmap *regmap;
> > > > > +
> > > > > +     const struct aspeed_p2a_model_data *config;
> > > > > +
> > > > > +     /* Access to these needs to be locked, held via probe, mapping ioctl,
> > > > > +      * and release, remove.
> > > > > +      */
> > > > > +     struct mutex tracking;
> > > > > +     u32 readers;
> > > > > +     u32 readerwriters[P2A_REGION_COUNT];
> > > > > +
> > > > > +     phys_addr_t mem_base;
> > > > > +     resource_size_t mem_size;
> > > > > +};
> > > > > +
> > > > > +struct aspeed_p2a_user {
> > > > > +     struct file *file;
> > > > > +     struct aspeed_p2a_ctrl *parent;
> > > > > +
> > > > > +     /** The entire memory space is opened for reading once the bridge is
> > > > > +      * enabled, therefore this needs only to be tracked once per user.
> > > > > +      * If any user has it open for read, the bridge must stay enabled.
> > > > > +      */
> > > >
> > > > Generally the descriptions for the members are described in a kerneldoc
> > > > comment above the struct definition. Also you're mixing the kernel-doc
> > > > comment opener (`/**`) with non-kernel-doc comments (`/*` on the
> > > > tracking` mutex in `struct aspeed_p2a_ctrl` above).
> > >
> > > Ok, so anything that isn't really detailing the members, or functions,
> > > shouldn't be kerneldoc style.  Will fix throughout.
> > >
> > > >
> > > > > +     u32 read;
> > > > > +
> > > > > +     /** Each entry of the array corresponds to a P2A Region.  If the user
> > > > > +      * opens for read or readwrite, the reference goes up here.  On
> > > > > release,
> > > > > +      * this array is walked and references adjusted accordingly.
> > > > > +      */
> > > > > +     u32 readwrite[P2A_REGION_COUNT];
> > > > > +};
> > > > > +
> > > > > +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > > +{
> > > > > +     regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A,
> > > > > SCU180_ENP2A);
> > > >
> > > > Wrap this at 80 chars.
> > >
> > > Roger.
> > >
> > > >
> > > > > +}
> > > > > +
> > > > > +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > > +{
> > > > > +     regmap_update_bits(p2witha_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> > > > > +}
> > > > > +
> > > > > +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct
> > > > > *vma)
> > > > > +{
> > > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > > > +
> > > > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > > > +             return -EINVAL;
> > > > > +
> > > > > +     unsigned long vsize = vma->vm_end - vma->vm_start;
> > > > > +     pgprot_t prot = vma->vm_page_prot;
> > > > > +
> > > > > +     if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> > > > > +             return -EINVAL;
> > > > > +
> > > > > +     /* ast2400/2500 AHB accesses are not cache coherent */
> > > > > +     prot = pgprot_noncached(prot);
> > > > > +
> > > > > +     if (remap_pfn_range(vma, vma->vm_start,
> > > > > +             (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> > > > > +             vsize, prot))
> > > > > +             return -EAGAIN;
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> > > > > +             struct aspeed_p2a_ctrl *ctrl,
> > > > > +             struct aspeed_p2a_ctrl_mapping *map)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 base, end;
> > > > > +
> > > > > +     base = map->addr;
> > > > > +     end = map->addr + (map->length - 1);
> > > > > +
> > > > > +     /* If the value is a legal u32, it will find a match. */
> > > > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > > > +             const struct region *curr = &ctrl->config->regions[i];
> > > > > +
> > > > > +             /* If the top of this region is lower than your base, skip it.
> > > > > +              */
> > > > > +             if (curr->max < base)
> > > > > +                     continue;
> > > > > +
> > > > > +             /* If the bottom of this region is higher than your end, bail.
> > > > > +              */
> > > > > +             if (curr->min > end)
> > > > > +                     break;
> > > > > +             /* Lock this and update it, therefore it someone else is
> > > > > +              * closing their file out, this'll preserve the increment.
> > > > > +              */
> > > > > +             mutex_lock(&ctrl->tracking);
> > > > > +             ctrl->readerwriters[i] += 1;
> > > > > +             mutex_unlock(&ctrl->tracking);
> > > > > +
> > > > > +             /* Track with the user, so when they close their file, we can
> > > > > +              * decrement properly.
> > > > > +              */
> > > >
> > > > The comments about tracking for decrement-on-release purposes is
> > > > useful, but I think the other comments in this loop are probably
> > > > unnecessary. Up to you though.
> > >
> > > I've often found drivers under-documented, so I went in the other direction.
> > >
> > > >
> > > > > +             priv->readwrite[i] += 1;
> > > > > +
> > > > > +             /* Enable the region as read-write. */
> > > > > +             regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> > > > > +     }
> > > > > +}
> > > > > +
> > > > > +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> > > > > +             unsigned long data)
> > > > > +{
> > > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > > > +     void __user *arg = (void __user *)data;
> > > > > +     struct aspeed_p2a_ctrl_mapping map;
> > > > > +
> > > > > +     if (copy_from_user(&map, arg, sizeof(map)))
> > > > > +             return -EFAULT;
> > > > > +
> > > > > +     switch (cmd) {
> > > > > +     case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> > > > > +             /* If they want a region to be read-only, since the entire
> > > > > +              * region is read-only once enabled, we just need to track this
> > > > > +              * user wants to read from the bridge, and if it's not enabled.
> > > > > +              * Enable it.
> > > > > +              */
> > > > > +             if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> > > > > +                     mutex_lock(&ctrl->tracking);
> > > > > +                     ctrl->readers += 1;
> > > > > +                     mutex_unlock(&ctrl->tracking);
> > > > > +
> > > > > +                     /* Track with the user, so when they close their file,
> > > > > +                      * we can decrement properly.
> > > > > +                      */
> > > > > +                     priv->read += 1;
> > > > > +             } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> > > > > +                     aspeed_p2a_region_acquire(priv, ctrl, &map);
> > > > > +             } else {
> > > > > +                     /* Invalid map flags. */
> > > > > +                     return -EINVAL;
> > > > > +             }
> > > > > +
> > > > > +             aspeed_p2a_enable_bridge(ctrl);
> > > > > +             return 0;
> > > > > +     case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> > > > > +             /* This is a request for the memory-region and corresponding
> > > > > +              * length that is used by the driver for mmap. */
> > > > > +
> > > > > +             map.flags = 0;
> > > > > +             map.addr = ctrl->mem_base;
> > > > > +             map.length = ctrl->mem_size;
> > > >
> > > > Thinking out loud here - do we want to allow ourselves an escape hatch
> > > > for supporting multiple reserved memory locations? Otherwise we might
> > > > need a new ioctl() for it. On the flip-side, not actually having this use-case
> > > > means we might break the implementation anyway, so it could be a
> > > > double-edged sword. Thoughts?
> > >
> > > How about this, I use a different structure that has an index -- so
> > > you pass in the structure with the index set, requesting the region
> > > information, and it returns the details for that memory-region until
> > > the index exceeds the number of regions and returns -ENODEV.
> > >
> > > However, to avoid extra complexity today, the dts will only support
> > > one memory region...  The complexity of dealing with checking what
> > > region they want to use in mmap by checking what region has been
> > > enabled by that specific user may be too much of a pain though for the
> > > probable life of this driver.  So yeah, a bit of a double-edged sword
> > > of complexity to approach the extra complexity even partially.  I
> > > think having one memory-region is fine for now, and I can't see the
> > > future.  Perhaps having the ioctl structure change and no other
> > > changes is sufficient while being extendable in the future if
> > > someone's need should arise.  That way there won't be an obsolete
> > > ioctl or ambiguous ioctl (code that calls the old one and its result
> > > is quasi-incorrect).
> > >
> > > >
> > > > > +
> > > > > +             return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> > > > > +     }
> > > > > +
> > > > > +     return -EINVAL;
> > > > > +}
> > > > > +
> > > > > +
> > > > > +/**
> > > > > + * When a user opens this file, we create a structure to track their
> > > > > mappings.
> > > > > + *
> > > > > + * A user can map a region as read-only (bridge enabled), or
> > > > > read-write (bit
> > > > > + * flipped, and bridge enabled).  Either way, this tracking is used,
> > > > > s.t. when
> > > > > + * they release the device references are handled.
> > > > > + *
> > > > > + * The bridge is not enabled until a user calls an ioctl to map a
> > > > > region,
> > > > > + * simply opening the device does not enable it.
> > > > > + */
> > > > > +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> > > > > +{
> > > > > +     struct aspeed_p2a_user *priv;
> > > > > +
> > > > > +     priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> > > > > +     if (!priv)
> > > > > +             return -ENOMEM;
> > > > > +
> > > > > +     priv->file = file;
> > > > > +     priv->read = 0;
> > > > > +     memset(priv->readwrite, 0, sizeof(priv->readwrite));
> > > > > +
> > > > > +     /* The file's private_data is initialized to the p2a_ctrl. */
> > > > > +     priv->parent = file->private_data;
> > > > > +
> > > > > +     /* Set the file's private_data to the user's data. */
> > > > > +     file->private_data = priv;
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * This will close the users mappings.  It will go through what they
> > > > > had opened
> > > > > + * for readwrite, and decrement those counts.  If at the end, this is
> > > > > the last
> > > > > + * user, it'll close the bridge.
> > > > > + */
> > > > > +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 value;
> > > > > +     u32 bits = 0;
> > > > > +     bool open_regions = false;
> > > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > > +
> > > > > +     /* Lock others from changing these values until everything is updated
> > > > > +      * in one pass */
> > > > > +     mutex_lock(&priv->parent->tracking);
> > > > > +
> > > > > +     priv->parent->readers -= priv->read;;
> > > > > +
> > > > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > > > +             priv->parent->readerwriters[i] -= priv->readwrite[i];
> > > > > +
> > > > > +             if (priv->parent->readerwriters[i] > 0)
> > > > > +                     open_regions = true;
> > > > > +             else
> > > > > +                     bits |= priv->parent->config->regions[i].bit;
> > > > > +     }
> > > > > +
> > > > > +     /* Setting a bit to 1 disables the region, so let's just OR with the
> > > > > +      * above to disable any.
> > > > > +      */
> > > > > +
> > > > > +     /* Note, if another user is trying to ioctl, they can't grab tracking,
> > > > > +      * and therefore can't grab either register mutex.
> > > > > +      * If another user is trying to close, they can't grab tracking
> > > > > either.
> > > > > +      */
> > > > > +     regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> > > > > +
> > > > > +     /* If parent->readers is zero and open windows is 0, disable the
> > > > > +      * bridge.
> > > > > +      */
> > > > > +     if (!open_regions && priv->parent->readers == 0)
> > > > > +             aspeed_p2a_disable_bridge(priv->parent);
> > > > > +
> > > > > +     mutex_unlock(&priv->parent->tracking);
> > > > > +
> > > > > +     kfree(priv);
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +static const struct file_operations aspeed_p2a_ctrl_fops = {
> > > > > +     .owner = THIS_MODULE,
> > > > > +     .mmap = aspeed_p2a_mmap,
> > > > > +     .unlocked_ioctl = aspeed_p2a_ioctl,
> > > > > +     .open = aspeed_p2a_open,
> > > > > +     .release = aspeed_p2a_release,
> > > > > +};
> > > > > +
> > > > > +/** The regions are controlled by SCU2C */
> > > > > +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 value = 0;
> > > > > +
> > > > > +     for (i = 0; i < P2A_REGION_COUNT; i++)
> > > > > +             value |= p2a_ctrl->config->regions[i].bit;
> > > > > +
> > > > > +     regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> > > > > +
> > > > > +     /* Disable the bridge. */
> > > > > +     aspeed_p2a_disable_bridge(p2a_ctrl);
> > > > > +}
> > > > > +
> > > > > +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> > > > > +{
> > > > > +     struct aspeed_p2a_ctrl *misc_ctrl;
> > > > > +     struct device *dev;
> > > > > +     struct resource *res, resm;
> > > > > +     struct device_node *node;
> > > > > +     int rc = 0;
> > > > > +
> > > > > +     dev = &pdev->dev;
> > > > > +
> > > > > +     misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> > > > > +     if (!misc_ctrl)
> > > > > +             return -ENOMEM;
> > > > > +
> > > > > +     mutex_init(&misc_ctrl->tracking);
> > > > > +     misc_ctrl->readers = 0;
> > > > > +     memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> > > > > +
> > > > > +     misc_ctrl->mem_size = 0;
> > > > > +     misc_ctrl->mem_base = 0;
> > > > > +
> > > > > +     /* optional. */
> > > > > +     node = of_parse_phandle(dev->of_node, "memory-region", 0);
> > > > > +     if (node) {
> > > > > +             rc = of_address_to_resource(node, 0, &resm);
> > > > > +             of_node_put(node);
> > > > > +             if (rc) {
> > > > > +                     dev_err(dev, "Couldn't address to resource for reserved memory\n");
> > > >
> > > > I think the message could be improved, something like "No reserved memory
> > > > specified". Having said that, I don't think it should be an error condition either;
> > > > our experience with aspeed-lpc-ctrl was that it was useful for the memory-region
> > > > property to be optional. You already have:
> > >
> > > Ok.
> >
> > Ok, when I read this, I see it as an error condition.  In this case
> > the node was specified but was invalid.
>
> Sorry, yeah you're right. I'm going to blame a 6-week old child and a lack of sleep :)

Haha. No worries!  Thanks for all your time reviewing this.  I really
appreciate your cycles on this.

>
> Cheers,
>
> Andrew
>
> >
> > >
> > > >
> > > > > +
> > > > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > > > +             return -EINVAL;
> > > >
> > > > in the mmap() callback, but we don't get that far unless someone has specified a
> > > > zero-sized reserved-memory node. I think supporting memory-region as optional
> > > > is just a matter of adding the same check to the GET_MEMORY_CONFIG ioctl().
> > > >
> > > > > +                     return -ENOMEM;
> > > >
> > > > The system isn't out of memory so this isn't an ENOMEM condition. I think ENODEV
> > > > is more appropriate, but if we make the memory region optional then this goes
> > > > away anyway.
> > >
> > > Roger.
> > >
> > > >
> > > > > +             }
> > > > > +
> > > > > +             misc_ctrl->mem_size = resource_size(&resm);
> > > > > +             misc_ctrl->mem_base = resm.start;
> > > > > +     }
> > > > > +
> > > > > +     node = of_parse_phandle(dev->of_node, "syscon", 0);
> > > > > +     if (!node) {
> > > > > +             dev_err(dev, "Couldn't find syscon property\n");
> > > > > +             return -ENODEV;
> > > > > +     }
> > > > > +
> > > > > +     misc_ctrl->regmap = syscon_node_to_regmap(node);
> > > > > +     if (IS_ERR(misc_ctrl->regmap)) {
> > > > > +             dev_err(dev, "Couldn't get regmap\n");
> > > > > +             return -ENODEV;
> > > > > +     }
> > > > > +     of_node_put(node);
> > > > > +
> > > > > +     misc_ctrl->config = of_device_get_match_data(dev);
> > > > > +
> > > > > +     dev_set_drvdata(&pdev->dev, misc_ctrl);
> > > > > +
> > > > > +     aspeed_p2a_disable_all(misc_ctrl);
> > > > > +
> > > > > +     misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> > > > > +     misc_ctrl->miscdev.name = DEVICE_NAME;
> > > > > +     misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> > > > > +     misc_ctrl->miscdev.parent = dev;
> > > > > +
> > > > > +     rc = misc_register(&misc_ctrl->miscdev);
> > > > > +     if (rc)
> > > > > +             dev_err(dev, "Unable to register device\n");
> > > > > +
> > > > > +     return rc;
> > > > > +}
> > > > > +
> > > > > +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> > > > > +{
> > > > > +     struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> > > > > +
> > > > > +     misc_deregister(&p2a_ctrl->miscdev);
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +/*
> > > > > + * bit | SCU2C | ast2400
> > > > > + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> > > > > + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> > > > > + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> > > > > + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> > > > > + *
> > > > > + * bit | SCU2C | ast2500
> > > > > + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> > > > > + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> > > > > + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> > > > > + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> > > > > + */
> > > >
> > > > The comment is probably unnecessary given the structure declarations
> > > > below.
> > >
> > > Fair enough, will drop it.
> > >
> > > >
> > > > > +
> > > > > +#define SCU2C_DRAM   BIT(25)
> > > > > +#define SCU2C_SPI    BIT(24)
> > > > > +#define SCU2C_SOC    BIT(23)
> > > > > +#define SCU2C_FLASH  BIT(22)
> > > > > +
> > > > > +static const struct aspeed_p2a_model_data ast2400_model_data = {
> > > > > +     .regions = {
> > > > > +             {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> > > > > +             {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> > > > > +             {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> > > > > +             {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> > > > > +             {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> > > > > +             {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> > > > > +     }
> > > > > +};
> > > > > +
> > > > > +static const struct aspeed_p2a_model_data ast2500_model_data = {
> > > > > +     .regions = {
> > > > > +             {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> > > > > +             {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> > > > > +             {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> > > > > +             {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> > > > > +             {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> > > > > +             {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> > > > > +     }
> > > > > +};
> > > > > +
> > > > > +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> > > > > +     { .compatible = "aspeed,ast2400-p2a-ctrl",
> > > > > +       .data = &ast2400_model_data },
> > > > > +     { .compatible = "aspeed,ast2500-p2a-ctrl",
> > > > > +       .data = &ast2500_model_data },
> > > > > +     { },
> > > > > +};
> > > > > +
> > > > > +static struct platform_driver aspeed_p2a_ctrl_driver = {
> > > > > +     .driver = {
> > > > > +             .name           = DEVICE_NAME,
> > > > > +             .of_match_table = aspeed_p2a_ctrl_match,
> > > > > +     },
> > > > > +     .probe = aspeed_p2a_ctrl_probe,
> > > > > +     .remove = aspeed_p2a_ctrl_remove,
> > > > > +};
> > > > > +
> > > > > +module_platform_driver(aspeed_p2a_ctrl_driver);
> > > > > +
> > > > > +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> > > > > +MODULE_LICENSE("GPL");
> > > > > +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> > > > > +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC
> > > > > mappings");
> > > > > diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > > b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > > new file mode 100644
> > > > > index 000000000000..e839cdc31db9
> > > > > --- /dev/null
> > > > > +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > > @@ -0,0 +1,59 @@
> > > > > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > > > > +/*
> > > > > + * Copyright 2019 Google Inc
> > > > > + *
> > > > > + * This program is free software; you can redistribute it and/or
> > > > > + * modify it under the terms of the GNU General Public License
> > > > > + * as published by the Free Software Foundation; either version
> > > > > + * 2 of the License, or (at your option) any later version.
> > > >
> > > > SPDX again.
> > >
> > > So, only SPDX?  I'm not super on the ball with this type of thing.  I
> > > just mirrored what I saw in other recent aspeed drivers.
> > >
> > > >
> > > > > + *
> > > > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > > > allows
> > > > > + * the host to read and write to various regions of the BMC's memory.
> > > > > + */
> > > > > +
> > > > > +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > > > +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > > > +
> > > > > +#include <linux/ioctl.h>
> > > > > +#include <linux/types.h>
> > > > > +
> > > > > +#define ASPEED_P2A_CTRL_READ_ONLY 0
> > > > > +#define ASPEED_P2A_CTRL_READWRITE 1
> > > > > +
> > > > > +/*
> > > > > + * This driver provides a mechanism for enabling or disabling the
> > > > > read-write
> > > > > + * property of specific windows into the ASPEED BMC's memory.
> > > > > + *
> > > > > + * A user can map a region of the BMC's memory as read-only or
> > > > > read-write, with
> > > > > + * the caveat that once any region is mapped, all regions are unlocked
> > > > > for
> > > > > + * reading.
> > > > > + */
> > > > > +
> > > > > +/**
> > > > > + * Unlock a region of BMC physical memory for access from the host.
> > > > > + *
> > > > > + * Also used to read back the optional memory-region configuration for
> > > > > the
> > > > > + * driver.
> > > > > + */
> > > > > +struct aspeed_p2a_ctrl_mapping {
> > > > > +     __u32 addr;
> > > > > +     __u32 length;
> > > > > +     __u32 flags;
> > > > > +};
> > > > > +
> > > > > +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> > > > > +
> > > > > +/**
> > > > > + * This IOCTL is meant to configure a region or regions of memory
> > > > > given a
> > > > > + * starting address and length to be readable by the host, or
> > > > > + * readable-writeable. */
> > > > > +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW
> > > > > _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > > > > +             0x00, struct aspeed_p2a_ctrl_mapping)
> > > > > +
> > > > > +/**
> > > > > + * This IOCTL is meant to read back to the user the base address and
> > > > > length of
> > > > > + * the memory-region specified to the driver for use with mmap. */
> > > > > +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG
> > > > > _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > > >
> > > > Wrap this at 80?
> > > >
> > > > Cheers,
> > > >
> > > > Andrew
> > > >
> > > > > +             0x01, struct aspeed_p2a_ctrl_mapping)
> > > > > +
> > > > > +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> > > > > --
> > > > > 2.21.0.rc2.261.ga7da99ff1b-goog
> > > > >
> > > > >
> >

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Andrew Jeffery @ 2019-03-05  1:53 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <CAO=notxn5JGKWgLhuaJ+RT3F-y3H-MTaBgedmRHsSvhfxJ9B5Q@mail.gmail.com>



On Tue, 5 Mar 2019, at 05:01, Patrick Venture wrote:
> On Mon, Mar 4, 2019 at 7:45 AM Patrick Venture <venture@google.com> wrote:
> >
> > On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> > >
> > > Hi Patrick.
> > >
> > > I've got some minor comments, otherwise it looks reasonable to me.
> > >
> > > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > > in the BMC's physical address space.  This feature is especially useful
> > > > when using this bridge to send large files to the BMC.
> > > >
> > > > The host may use this to send down a firmware image by staging data at a
> > > > specific memory address, and in a coordinated effort with the BMC's
> > > > software stack and kernel, transmit the bytes.
> > > >
> > > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > > configure it via ioctl to allow the host to write bytes to an agreed
> > > > upon location.  In the primary use-case, the region to use is known
> > > > apriori on the BMC, and the host requests this information.  Once this
> > > > request is received, the BMC's software stack will enable the bridge and
> > > > the region and then using some software flow control (possibly via IPMI
> > > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > > will disable the bridge and unset any region involved.
> > > >
> > > > The default behavior of this bridge when present is: enabled and all
> > > > regions marked read-write.  This driver will fix the regions to be
> > > > read-only and then disable the bridge entirely.
> > > >
> > > > The memory regions protected are:
> > > >  * BMC flash MMIO window
> > > >  * System flash MMIO windows
> > > >  * SOC IO (peripheral MMIO)
> > > >  * DRAM
> > > >
> > > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > > the DRAM section is write-enabled, then it can write to all of it.
> > > >
> > > > Signed-off-by: Patrick Venture <venture@google.com>
> > > > ---
> > > > Changes for v5:
> > > > - Fixup missing exit condition and remove extra jump.
> > > > Changes for v4:
> > > > - Added ioctl for reading back the memory-region configuration.
> > > > - Cleaned up some unused variables.
> > > > Changes for v3:
> > > > - Deleted unused read and write methods.
> > > > Changes for v2:
> > > > - Dropped unused reads.
> > > > - Moved call to disable bridge to before registering device.
> > > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > > - Updated the commit message. <<< TODO
> > > > - Updated the bit flipped for SCU180_ENP2A
> > > > - Dropped boolean region_specified variable.
> > > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > > - Updated commit message.
> > > > ---
> > > >  drivers/misc/Kconfig                 |   8 +
> > > >  drivers/misc/Makefile                |   1 +
> > > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > > >  4 files changed, 524 insertions(+)
> > > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > > >
> > > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > > index f417b06e11c5..9de1bafe5606 100644
> > > > --- a/drivers/misc/Kconfig
> > > > +++ b/drivers/misc/Kconfig
> > > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > > >         bus. System Configuration interface is one of the possible means
> > > >         of generating transactions on this bus.
> > > >
> > > > +config ASPEED_P2A_CTRL
> > > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > > +     help
> > > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > > through
> > > > +       ioctl()s, the driver also provides an interface for userspace
> > > > mappings to
> > > > +       a pre-defined region.
> > > > +
> > > >  config ASPEED_LPC_CTRL
> > > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > > index e39ccbbc1b3a..57577aee354f 100644
> > > > --- a/drivers/misc/Makefile
> > > > +++ b/drivers/misc/Makefile
> > > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > > >  obj-$(CONFIG_OCXL)           += ocxl/
> > > >  obj-y                                += cardreader/
> > > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > > new file mode 100644
> > > > index 000000000000..6bde4f64632d
> > > > --- /dev/null
> > > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > > @@ -0,0 +1,456 @@
> > > > +/*
> > > > + * Copyright 2019 Google Inc
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU General Public License
> > > > + * as published by the Free Software Foundation; either version
> > > > + * 2 of the License, or (at your option) any later version.
> > >
> > > Should be a SPDX header instead.
> >
> > Ok, so delete the above and drop in:
> 
> Was set straight on this.
> 
> >
> > """
> > /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > """
> >
> > Or just add that to the top above the Google GNU copyright line?  (I'm
> > not a lawyer).
> >
> > >
> > > > + *
> > > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > > allows
> > > > + * the host to read and write to various regions of the BMC's memory.
> > > > + */
> > > > +
> > > > +#include <linux/fs.h>
> > > > +#include <linux/io.h>
> > > > +#include <linux/mfd/syscon.h>
> > > > +#include <linux/miscdevice.h>
> > > > +#include <linux/mm.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/mutex.h>
> > > > +#include <linux/of_address.h>
> > > > +#include <linux/of_device.h>
> > > > +#include <linux/platform_device.h>
> > > > +#include <linux/regmap.h>
> > > > +#include <linux/slab.h>
> > > > +#include <linux/uaccess.h>
> > > > +
> > > > +#include <linux/aspeed-p2a-ctrl.h>
> > > > +
> > > > +#define DEVICE_NAME  "aspeed-p2a-ctrl"
> > > > +
> > > > +/* SCU2C is a Misc. Control Register. */
> > > > +#define SCU2C 0x2c
> > > > +/* SCU180 is the PCIe Configuration Setting Control Register. */
> > > > +#define SCU180 0x180
> > > > +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA
> > > > device
> > > > + * on the PCI bus. */
> > >
> > > As this wraps to multiple lines the trailing `*/` should be on a separate line.
> > > There are a few instances of this throughout.
> >
> > Can do.  I will also run checkpath -- I had done it initially, but I
> > forgot on later editions.
> >
> > >
> > > > +#define SCU180_ENP2A BIT(1)
> > > > +
> > > > +/* The ast2400/2500 both have six ranges. */
> > > > +#define P2A_REGION_COUNT 6
> > > > +
> > > > +struct region {
> > > > +     u32 min;
> > > > +     u32 max;
> > > > +     u32 bit;
> > > > +};
> > > > +
> > > > +struct aspeed_p2a_model_data {
> > > > +     /* min, max, bit */
> > > > +     struct region regions[P2A_REGION_COUNT];
> > > > +};
> > > > +
> > > > +struct aspeed_p2a_ctrl {
> > > > +     struct miscdevice miscdev;
> > > > +     struct regmap *regmap;
> > > > +
> > > > +     const struct aspeed_p2a_model_data *config;
> > > > +
> > > > +     /* Access to these needs to be locked, held via probe, mapping ioctl,
> > > > +      * and release, remove.
> > > > +      */
> > > > +     struct mutex tracking;
> > > > +     u32 readers;
> > > > +     u32 readerwriters[P2A_REGION_COUNT];
> > > > +
> > > > +     phys_addr_t mem_base;
> > > > +     resource_size_t mem_size;
> > > > +};
> > > > +
> > > > +struct aspeed_p2a_user {
> > > > +     struct file *file;
> > > > +     struct aspeed_p2a_ctrl *parent;
> > > > +
> > > > +     /** The entire memory space is opened for reading once the bridge is
> > > > +      * enabled, therefore this needs only to be tracked once per user.
> > > > +      * If any user has it open for read, the bridge must stay enabled.
> > > > +      */
> > >
> > > Generally the descriptions for the members are described in a kerneldoc
> > > comment above the struct definition. Also you're mixing the kernel-doc
> > > comment opener (`/**`) with non-kernel-doc comments (`/*` on the
> > > tracking` mutex in `struct aspeed_p2a_ctrl` above).
> >
> > Ok, so anything that isn't really detailing the members, or functions,
> > shouldn't be kerneldoc style.  Will fix throughout.
> >
> > >
> > > > +     u32 read;
> > > > +
> > > > +     /** Each entry of the array corresponds to a P2A Region.  If the user
> > > > +      * opens for read or readwrite, the reference goes up here.  On
> > > > release,
> > > > +      * this array is walked and references adjusted accordingly.
> > > > +      */
> > > > +     u32 readwrite[P2A_REGION_COUNT];
> > > > +};
> > > > +
> > > > +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > +{
> > > > +     regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A,
> > > > SCU180_ENP2A);
> > >
> > > Wrap this at 80 chars.
> >
> > Roger.
> >
> > >
> > > > +}
> > > > +
> > > > +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > +{
> > > > +     regmap_update_bits(p2witha_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> > > > +}
> > > > +
> > > > +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct
> > > > *vma)
> > > > +{
> > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > > +
> > > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > > +             return -EINVAL;
> > > > +
> > > > +     unsigned long vsize = vma->vm_end - vma->vm_start;
> > > > +     pgprot_t prot = vma->vm_page_prot;
> > > > +
> > > > +     if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> > > > +             return -EINVAL;
> > > > +
> > > > +     /* ast2400/2500 AHB accesses are not cache coherent */
> > > > +     prot = pgprot_noncached(prot);
> > > > +
> > > > +     if (remap_pfn_range(vma, vma->vm_start,
> > > > +             (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> > > > +             vsize, prot))
> > > > +             return -EAGAIN;
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> > > > +             struct aspeed_p2a_ctrl *ctrl,
> > > > +             struct aspeed_p2a_ctrl_mapping *map)
> > > > +{
> > > > +     int i;
> > > > +     u32 base, end;
> > > > +
> > > > +     base = map->addr;
> > > > +     end = map->addr + (map->length - 1);
> > > > +
> > > > +     /* If the value is a legal u32, it will find a match. */
> > > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > > +             const struct region *curr = &ctrl->config->regions[i];
> > > > +
> > > > +             /* If the top of this region is lower than your base, skip it.
> > > > +              */
> > > > +             if (curr->max < base)
> > > > +                     continue;
> > > > +
> > > > +             /* If the bottom of this region is higher than your end, bail.
> > > > +              */
> > > > +             if (curr->min > end)
> > > > +                     break;
> > > > +             /* Lock this and update it, therefore it someone else is
> > > > +              * closing their file out, this'll preserve the increment.
> > > > +              */
> > > > +             mutex_lock(&ctrl->tracking);
> > > > +             ctrl->readerwriters[i] += 1;
> > > > +             mutex_unlock(&ctrl->tracking);
> > > > +
> > > > +             /* Track with the user, so when they close their file, we can
> > > > +              * decrement properly.
> > > > +              */
> > >
> > > The comments about tracking for decrement-on-release purposes is
> > > useful, but I think the other comments in this loop are probably
> > > unnecessary. Up to you though.
> >
> > I've often found drivers under-documented, so I went in the other direction.
> >
> > >
> > > > +             priv->readwrite[i] += 1;
> > > > +
> > > > +             /* Enable the region as read-write. */
> > > > +             regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> > > > +     }
> > > > +}
> > > > +
> > > > +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> > > > +             unsigned long data)
> > > > +{
> > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > > +     void __user *arg = (void __user *)data;
> > > > +     struct aspeed_p2a_ctrl_mapping map;
> > > > +
> > > > +     if (copy_from_user(&map, arg, sizeof(map)))
> > > > +             return -EFAULT;
> > > > +
> > > > +     switch (cmd) {
> > > > +     case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> > > > +             /* If they want a region to be read-only, since the entire
> > > > +              * region is read-only once enabled, we just need to track this
> > > > +              * user wants to read from the bridge, and if it's not enabled.
> > > > +              * Enable it.
> > > > +              */
> > > > +             if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> > > > +                     mutex_lock(&ctrl->tracking);
> > > > +                     ctrl->readers += 1;
> > > > +                     mutex_unlock(&ctrl->tracking);
> > > > +
> > > > +                     /* Track with the user, so when they close their file,
> > > > +                      * we can decrement properly.
> > > > +                      */
> > > > +                     priv->read += 1;
> > > > +             } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> > > > +                     aspeed_p2a_region_acquire(priv, ctrl, &map);
> > > > +             } else {
> > > > +                     /* Invalid map flags. */
> > > > +                     return -EINVAL;
> > > > +             }
> > > > +
> > > > +             aspeed_p2a_enable_bridge(ctrl);
> > > > +             return 0;
> > > > +     case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> > > > +             /* This is a request for the memory-region and corresponding
> > > > +              * length that is used by the driver for mmap. */
> > > > +
> > > > +             map.flags = 0;
> > > > +             map.addr = ctrl->mem_base;
> > > > +             map.length = ctrl->mem_size;
> > >
> > > Thinking out loud here - do we want to allow ourselves an escape hatch
> > > for supporting multiple reserved memory locations? Otherwise we might
> > > need a new ioctl() for it. On the flip-side, not actually having this use-case
> > > means we might break the implementation anyway, so it could be a
> > > double-edged sword. Thoughts?
> >
> > How about this, I use a different structure that has an index -- so
> > you pass in the structure with the index set, requesting the region
> > information, and it returns the details for that memory-region until
> > the index exceeds the number of regions and returns -ENODEV.
> >
> > However, to avoid extra complexity today, the dts will only support
> > one memory region...  The complexity of dealing with checking what
> > region they want to use in mmap by checking what region has been
> > enabled by that specific user may be too much of a pain though for the
> > probable life of this driver.  So yeah, a bit of a double-edged sword
> > of complexity to approach the extra complexity even partially.  I
> > think having one memory-region is fine for now, and I can't see the
> > future.  Perhaps having the ioctl structure change and no other
> > changes is sufficient while being extendable in the future if
> > someone's need should arise.  That way there won't be an obsolete
> > ioctl or ambiguous ioctl (code that calls the old one and its result
> > is quasi-incorrect).
> >
> > >
> > > > +
> > > > +             return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> > > > +     }
> > > > +
> > > > +     return -EINVAL;
> > > > +}
> > > > +
> > > > +
> > > > +/**
> > > > + * When a user opens this file, we create a structure to track their
> > > > mappings.
> > > > + *
> > > > + * A user can map a region as read-only (bridge enabled), or
> > > > read-write (bit
> > > > + * flipped, and bridge enabled).  Either way, this tracking is used,
> > > > s.t. when
> > > > + * they release the device references are handled.
> > > > + *
> > > > + * The bridge is not enabled until a user calls an ioctl to map a
> > > > region,
> > > > + * simply opening the device does not enable it.
> > > > + */
> > > > +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> > > > +{
> > > > +     struct aspeed_p2a_user *priv;
> > > > +
> > > > +     priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> > > > +     if (!priv)
> > > > +             return -ENOMEM;
> > > > +
> > > > +     priv->file = file;
> > > > +     priv->read = 0;
> > > > +     memset(priv->readwrite, 0, sizeof(priv->readwrite));
> > > > +
> > > > +     /* The file's private_data is initialized to the p2a_ctrl. */
> > > > +     priv->parent = file->private_data;
> > > > +
> > > > +     /* Set the file's private_data to the user's data. */
> > > > +     file->private_data = priv;
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * This will close the users mappings.  It will go through what they
> > > > had opened
> > > > + * for readwrite, and decrement those counts.  If at the end, this is
> > > > the last
> > > > + * user, it'll close the bridge.
> > > > + */
> > > > +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> > > > +{
> > > > +     int i;
> > > > +     u32 value;
> > > > +     u32 bits = 0;
> > > > +     bool open_regions = false;
> > > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > > +
> > > > +     /* Lock others from changing these values until everything is updated
> > > > +      * in one pass */
> > > > +     mutex_lock(&priv->parent->tracking);
> > > > +
> > > > +     priv->parent->readers -= priv->read;;
> > > > +
> > > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > > +             priv->parent->readerwriters[i] -= priv->readwrite[i];
> > > > +
> > > > +             if (priv->parent->readerwriters[i] > 0)
> > > > +                     open_regions = true;
> > > > +             else
> > > > +                     bits |= priv->parent->config->regions[i].bit;
> > > > +     }
> > > > +
> > > > +     /* Setting a bit to 1 disables the region, so let's just OR with the
> > > > +      * above to disable any.
> > > > +      */
> > > > +
> > > > +     /* Note, if another user is trying to ioctl, they can't grab tracking,
> > > > +      * and therefore can't grab either register mutex.
> > > > +      * If another user is trying to close, they can't grab tracking
> > > > either.
> > > > +      */
> > > > +     regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> > > > +
> > > > +     /* If parent->readers is zero and open windows is 0, disable the
> > > > +      * bridge.
> > > > +      */
> > > > +     if (!open_regions && priv->parent->readers == 0)
> > > > +             aspeed_p2a_disable_bridge(priv->parent);
> > > > +
> > > > +     mutex_unlock(&priv->parent->tracking);
> > > > +
> > > > +     kfree(priv);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static const struct file_operations aspeed_p2a_ctrl_fops = {
> > > > +     .owner = THIS_MODULE,
> > > > +     .mmap = aspeed_p2a_mmap,
> > > > +     .unlocked_ioctl = aspeed_p2a_ioctl,
> > > > +     .open = aspeed_p2a_open,
> > > > +     .release = aspeed_p2a_release,
> > > > +};
> > > > +
> > > > +/** The regions are controlled by SCU2C */
> > > > +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > > +{
> > > > +     int i;
> > > > +     u32 value = 0;
> > > > +
> > > > +     for (i = 0; i < P2A_REGION_COUNT; i++)
> > > > +             value |= p2a_ctrl->config->regions[i].bit;
> > > > +
> > > > +     regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> > > > +
> > > > +     /* Disable the bridge. */
> > > > +     aspeed_p2a_disable_bridge(p2a_ctrl);
> > > > +}
> > > > +
> > > > +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> > > > +{
> > > > +     struct aspeed_p2a_ctrl *misc_ctrl;
> > > > +     struct device *dev;
> > > > +     struct resource *res, resm;
> > > > +     struct device_node *node;
> > > > +     int rc = 0;
> > > > +
> > > > +     dev = &pdev->dev;
> > > > +
> > > > +     misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> > > > +     if (!misc_ctrl)
> > > > +             return -ENOMEM;
> > > > +
> > > > +     mutex_init(&misc_ctrl->tracking);
> > > > +     misc_ctrl->readers = 0;
> > > > +     memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> > > > +
> > > > +     misc_ctrl->mem_size = 0;
> > > > +     misc_ctrl->mem_base = 0;
> > > > +
> > > > +     /* optional. */
> > > > +     node = of_parse_phandle(dev->of_node, "memory-region", 0);
> > > > +     if (node) {
> > > > +             rc = of_address_to_resource(node, 0, &resm);
> > > > +             of_node_put(node);
> > > > +             if (rc) {
> > > > +                     dev_err(dev, "Couldn't address to resource for reserved memory\n");
> > >
> > > I think the message could be improved, something like "No reserved memory
> > > specified". Having said that, I don't think it should be an error condition either;
> > > our experience with aspeed-lpc-ctrl was that it was useful for the memory-region
> > > property to be optional. You already have:
> >
> > Ok.
> 
> Ok, when I read this, I see it as an error condition.  In this case
> the node was specified but was invalid.

Sorry, yeah you're right. I'm going to blame a 6-week old child and a lack of sleep :)

Cheers,

Andrew

> 
> >
> > >
> > > > +
> > > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > > +             return -EINVAL;
> > >
> > > in the mmap() callback, but we don't get that far unless someone has specified a
> > > zero-sized reserved-memory node. I think supporting memory-region as optional
> > > is just a matter of adding the same check to the GET_MEMORY_CONFIG ioctl().
> > >
> > > > +                     return -ENOMEM;
> > >
> > > The system isn't out of memory so this isn't an ENOMEM condition. I think ENODEV
> > > is more appropriate, but if we make the memory region optional then this goes
> > > away anyway.
> >
> > Roger.
> >
> > >
> > > > +             }
> > > > +
> > > > +             misc_ctrl->mem_size = resource_size(&resm);
> > > > +             misc_ctrl->mem_base = resm.start;
> > > > +     }
> > > > +
> > > > +     node = of_parse_phandle(dev->of_node, "syscon", 0);
> > > > +     if (!node) {
> > > > +             dev_err(dev, "Couldn't find syscon property\n");
> > > > +             return -ENODEV;
> > > > +     }
> > > > +
> > > > +     misc_ctrl->regmap = syscon_node_to_regmap(node);
> > > > +     if (IS_ERR(misc_ctrl->regmap)) {
> > > > +             dev_err(dev, "Couldn't get regmap\n");
> > > > +             return -ENODEV;
> > > > +     }
> > > > +     of_node_put(node);
> > > > +
> > > > +     misc_ctrl->config = of_device_get_match_data(dev);
> > > > +
> > > > +     dev_set_drvdata(&pdev->dev, misc_ctrl);
> > > > +
> > > > +     aspeed_p2a_disable_all(misc_ctrl);
> > > > +
> > > > +     misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> > > > +     misc_ctrl->miscdev.name = DEVICE_NAME;
> > > > +     misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> > > > +     misc_ctrl->miscdev.parent = dev;
> > > > +
> > > > +     rc = misc_register(&misc_ctrl->miscdev);
> > > > +     if (rc)
> > > > +             dev_err(dev, "Unable to register device\n");
> > > > +
> > > > +     return rc;
> > > > +}
> > > > +
> > > > +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> > > > +{
> > > > +     struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> > > > +
> > > > +     misc_deregister(&p2a_ctrl->miscdev);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/*
> > > > + * bit | SCU2C | ast2400
> > > > + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> > > > + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> > > > + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> > > > + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> > > > + *
> > > > + * bit | SCU2C | ast2500
> > > > + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> > > > + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> > > > + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> > > > + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> > > > + */
> > >
> > > The comment is probably unnecessary given the structure declarations
> > > below.
> >
> > Fair enough, will drop it.
> >
> > >
> > > > +
> > > > +#define SCU2C_DRAM   BIT(25)
> > > > +#define SCU2C_SPI    BIT(24)
> > > > +#define SCU2C_SOC    BIT(23)
> > > > +#define SCU2C_FLASH  BIT(22)
> > > > +
> > > > +static const struct aspeed_p2a_model_data ast2400_model_data = {
> > > > +     .regions = {
> > > > +             {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> > > > +             {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> > > > +             {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> > > > +             {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> > > > +             {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> > > > +             {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> > > > +     }
> > > > +};
> > > > +
> > > > +static const struct aspeed_p2a_model_data ast2500_model_data = {
> > > > +     .regions = {
> > > > +             {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> > > > +             {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> > > > +             {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> > > > +             {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> > > > +             {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> > > > +             {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> > > > +     }
> > > > +};
> > > > +
> > > > +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> > > > +     { .compatible = "aspeed,ast2400-p2a-ctrl",
> > > > +       .data = &ast2400_model_data },
> > > > +     { .compatible = "aspeed,ast2500-p2a-ctrl",
> > > > +       .data = &ast2500_model_data },
> > > > +     { },
> > > > +};
> > > > +
> > > > +static struct platform_driver aspeed_p2a_ctrl_driver = {
> > > > +     .driver = {
> > > > +             .name           = DEVICE_NAME,
> > > > +             .of_match_table = aspeed_p2a_ctrl_match,
> > > > +     },
> > > > +     .probe = aspeed_p2a_ctrl_probe,
> > > > +     .remove = aspeed_p2a_ctrl_remove,
> > > > +};
> > > > +
> > > > +module_platform_driver(aspeed_p2a_ctrl_driver);
> > > > +
> > > > +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> > > > +MODULE_LICENSE("GPL");
> > > > +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> > > > +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC
> > > > mappings");
> > > > diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > new file mode 100644
> > > > index 000000000000..e839cdc31db9
> > > > --- /dev/null
> > > > +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > > @@ -0,0 +1,59 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > > > +/*
> > > > + * Copyright 2019 Google Inc
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU General Public License
> > > > + * as published by the Free Software Foundation; either version
> > > > + * 2 of the License, or (at your option) any later version.
> > >
> > > SPDX again.
> >
> > So, only SPDX?  I'm not super on the ball with this type of thing.  I
> > just mirrored what I saw in other recent aspeed drivers.
> >
> > >
> > > > + *
> > > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > > allows
> > > > + * the host to read and write to various regions of the BMC's memory.
> > > > + */
> > > > +
> > > > +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > > +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > > +
> > > > +#include <linux/ioctl.h>
> > > > +#include <linux/types.h>
> > > > +
> > > > +#define ASPEED_P2A_CTRL_READ_ONLY 0
> > > > +#define ASPEED_P2A_CTRL_READWRITE 1
> > > > +
> > > > +/*
> > > > + * This driver provides a mechanism for enabling or disabling the
> > > > read-write
> > > > + * property of specific windows into the ASPEED BMC's memory.
> > > > + *
> > > > + * A user can map a region of the BMC's memory as read-only or
> > > > read-write, with
> > > > + * the caveat that once any region is mapped, all regions are unlocked
> > > > for
> > > > + * reading.
> > > > + */
> > > > +
> > > > +/**
> > > > + * Unlock a region of BMC physical memory for access from the host.
> > > > + *
> > > > + * Also used to read back the optional memory-region configuration for
> > > > the
> > > > + * driver.
> > > > + */
> > > > +struct aspeed_p2a_ctrl_mapping {
> > > > +     __u32 addr;
> > > > +     __u32 length;
> > > > +     __u32 flags;
> > > > +};
> > > > +
> > > > +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> > > > +
> > > > +/**
> > > > + * This IOCTL is meant to configure a region or regions of memory
> > > > given a
> > > > + * starting address and length to be readable by the host, or
> > > > + * readable-writeable. */
> > > > +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW
> > > > _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > > > +             0x00, struct aspeed_p2a_ctrl_mapping)
> > > > +
> > > > +/**
> > > > + * This IOCTL is meant to read back to the user the base address and
> > > > length of
> > > > + * the memory-region specified to the driver for use with mmap. */
> > > > +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG
> > > > _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > >
> > > Wrap this at 80?
> > >
> > > Cheers,
> > >
> > > Andrew
> > >
> > > > +             0x01, struct aspeed_p2a_ctrl_mapping)
> > > > +
> > > > +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> > > > --
> > > > 2.21.0.rc2.261.ga7da99ff1b-goog
> > > >
> > > >
>

^ permalink raw reply

* [PATCH 6/6] ARM: dts: aspeed: Add XDMA engine
From: Eddie James @ 2019-03-04 21:37 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

Add a node for the XDMA engine with all the necessary information.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 arch/arm/boot/dts/aspeed-g5.dtsi | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 85ed9db..a0856c7 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -234,6 +234,14 @@
 				reg-io-width = <4>;
 			};
 
+			xdma: xdma at 1e6e7000 {
+				compatible = "aspeed,ast2500-xdma";
+				reg = <0x1e6e7000 0x100>;
+				resets = <&syscon ASPEED_RESET_XDMA>;
+				interrupts = <6>;
+				status = "disabled";
+			};
+
 			adc: adc at 1e6e9000 {
 				compatible = "aspeed,ast2500-adc";
 				reg = <0x1e6e9000 0xb0>;
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 5/6] drivers/misc: xdma: Add debugfs entries
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

Add debugfs entries for the relevant XDMA engine registers and for
dumping the AST2500 reserved memory space.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/misc/aspeed-xdma.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/drivers/misc/aspeed-xdma.c b/drivers/misc/aspeed-xdma.c
index 0a1a093..a645a5f 100644
--- a/drivers/misc/aspeed-xdma.c
+++ b/drivers/misc/aspeed-xdma.c
@@ -142,6 +142,12 @@ struct aspeed_xdma {
 	struct list_head vga_blks_free;
 
 	struct miscdevice misc;
+	struct dentry *debugfs_dir;
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	struct debugfs_regset32 regset;
+	struct debugfs_reg32 regs[XDMA_NUM_DEBUGFS_REGS];
+#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
 };
 
 struct aspeed_xdma_client {
@@ -634,6 +640,92 @@ static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static ssize_t aspeed_xdma_debugfs_vga_read(struct file *file,
+					    char __user *buf, size_t len,
+					    loff_t *offset)
+{
+	int rc;
+	struct inode *inode = file_inode(file);
+	struct aspeed_xdma *ctx = inode->i_private;
+	void __iomem *vga = ioremap(ctx->vga_phys, ctx->vga_size);
+	loff_t offs = *offset;
+	void *tmp;
+
+	if (!vga)
+		return -ENOMEM;
+
+	if (len + offs > ctx->vga_size) {
+		iounmap(vga);
+		return -EINVAL;
+	}
+
+	tmp = kzalloc(len, GFP_KERNEL);
+	if (!tmp) {
+		iounmap(vga);
+		return -ENOMEM;
+	}
+
+	memcpy_fromio(tmp, vga + offs, len);
+
+	rc = copy_to_user(buf, tmp, len);
+	if (rc) {
+		iounmap(vga);
+		kfree(tmp);
+		return rc;
+	}
+
+	*offset = offs + len;
+
+	kfree(tmp);
+	iounmap(vga);
+
+	return len;
+}
+
+static const struct file_operations aspeed_xdma_debugfs_vga_fops = {
+	.owner	= THIS_MODULE,
+	.llseek	= generic_file_llseek,
+	.read	= aspeed_xdma_debugfs_vga_read,
+};
+
+static void aspeed_xdma_init_debugfs(struct aspeed_xdma *ctx)
+{
+	ctx->debugfs_dir = debugfs_create_dir(DEVICE_NAME, NULL);
+	if (IS_ERR_OR_NULL(ctx->debugfs_dir)) {
+		dev_warn(ctx->dev, "Failed to create debugfs directory.\n");
+		return;
+	}
+
+	debugfs_create_file("vga", 0444, ctx->debugfs_dir, ctx,
+			    &aspeed_xdma_debugfs_vga_fops);
+
+	ctx->regs[0].name = "addr";
+	ctx->regs[0].offset = XDMA_BMC_CMD_QUEUE_ADDR;
+	ctx->regs[1].name = "endp";
+	ctx->regs[1].offset = XDMA_BMC_CMD_QUEUE_ENDP;
+	ctx->regs[2].name = "writep";
+	ctx->regs[2].offset = XDMA_BMC_CMD_QUEUE_WRITEP;
+	ctx->regs[3].name = "readp";
+	ctx->regs[3].offset = XDMA_BMC_CMD_QUEUE_READP;
+	ctx->regs[4].name = "control";
+	ctx->regs[4].offset = XDMA_CTRL;
+	ctx->regs[5].name = "status";
+	ctx->regs[5].offset = XDMA_STATUS;
+
+	ctx->regset.regs = ctx->regs;
+	ctx->regset.nregs = XDMA_NUM_DEBUGFS_REGS;
+	ctx->regset.base = ctx->base;
+
+	debugfs_create_regset32("regs", 0444, ctx->debugfs_dir, &ctx->regset);
+}
+#else
+static void aspeed_xdma_init_debugfs(struct aspeed_xdma *ctx)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
+
 static void aspeed_xdma_free_vga_blks(struct aspeed_xdma *ctx)
 {
 	struct aspeed_xdma_vga_blk *free;
@@ -786,6 +878,8 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
 	device_create_file(dev, &dev_attr_use_bmc);
 	device_create_file(dev, &dev_attr_use_vga);
 
+	aspeed_xdma_init_debugfs(ctx);
+
 	return 0;
 }
 
@@ -793,6 +887,8 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
 {
 	struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
 
+	debugfs_remove_recursive(ctx->debugfs_dir);
+
 	device_remove_file(ctx->dev, &dev_attr_use_vga);
 	device_remove_file(ctx->dev, &dev_attr_use_bmc);
 
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 4/6] drivers/misc: xdma: Add PCI device configuration sysfs
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

The AST2500 has two PCI devices embedded. The XDMA engine can use either
device to perform DMA transfers. Users need the capability to choose
which device to use. This commit therefore adds two sysfs files that
toggle the AST2500 and XDMA engine between the two PCI devices.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/misc/aspeed-xdma.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/drivers/misc/aspeed-xdma.c b/drivers/misc/aspeed-xdma.c
index 16235b3..0a1a093 100644
--- a/drivers/misc/aspeed-xdma.c
+++ b/drivers/misc/aspeed-xdma.c
@@ -645,6 +645,66 @@ static void aspeed_xdma_free_vga_blks(struct aspeed_xdma *ctx)
 	}
 }
 
+static int aspeed_xdma_change_pcie_conf(struct aspeed_xdma *ctx, u32 val)
+{
+	int rc;
+
+	mutex_lock(&ctx->start_lock);
+	rc = wait_event_interruptible_timeout(ctx->wait,
+					      !test_bit(XDMA_IN_PRG,
+							&ctx->flags),
+					      msecs_to_jiffies(1000));
+	if (rc < 0) {
+		mutex_unlock(&ctx->start_lock);
+		return -EINTR;
+	}
+
+	/* previous op didn't complete, wake up waiters anyway */
+	if (!rc)
+		wake_up_interruptible_all(&ctx->wait);
+
+	reset_control_assert(ctx->reset);
+	msleep(10);
+
+	regmap_update_bits(ctx->scu, SCU_PCIE_CONF,
+			   SCU_PCIE_CONF_VGA_EN | SCU_PCIE_CONF_BMC_EN,
+			   val);
+	msleep(10);
+
+	reset_control_deassert(ctx->reset);
+	msleep(10);
+
+	aspeed_xdma_init_eng(ctx);
+
+	mutex_unlock(&ctx->start_lock);
+
+	return 0;
+}
+
+static ssize_t aspeed_xdma_use_bmc(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int rc;
+	struct aspeed_xdma *ctx = dev_get_drvdata(dev);
+
+	rc = aspeed_xdma_change_pcie_conf(ctx, SCU_PCIE_CONF_BMC_EN);
+	return rc ?: count;
+}
+static DEVICE_ATTR(use_bmc, 0200, NULL, aspeed_xdma_use_bmc);
+
+static ssize_t aspeed_xdma_use_vga(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int rc;
+	struct aspeed_xdma *ctx = dev_get_drvdata(dev);
+
+	rc = aspeed_xdma_change_pcie_conf(ctx, SCU_PCIE_CONF_VGA_EN);
+	return rc ?: count;
+}
+static DEVICE_ATTR(use_vga, 0200, NULL, aspeed_xdma_use_vga);
+
 static int aspeed_xdma_probe(struct platform_device *pdev)
 {
 	int irq;
@@ -723,6 +783,9 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
 		return rc;
 	}
 
+	device_create_file(dev, &dev_attr_use_bmc);
+	device_create_file(dev, &dev_attr_use_vga);
+
 	return 0;
 }
 
@@ -730,6 +793,9 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
 {
 	struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
 
+	device_remove_file(ctx->dev, &dev_attr_use_vga);
+	device_remove_file(ctx->dev, &dev_attr_use_bmc);
+
 	misc_deregister(&ctx->misc);
 
 	aspeed_xdma_free_vga_blks(ctx);
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 3/6] drivers/misc: xdma: Add user interface
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

This commits adds a miscdevice to provide a user interface to the XDMA
engine. The interface provides the write operation to start DMA
operations. The DMA parameters are passed as the data to the write call.
The actual data to transfer is NOT passed through write. Note that both
directions of DMA operation are accomplished through the write command;
BMC to host and host to BMC.

The XDMA engine is restricted to only accessing the reserved memory
space on the AST2500, typically used by the VGA. For this reason, this
commit also adds a simple memory manager for this reserved memory space
which can then be allocated in pages by users calling mmap. The space
allocated by a client will be the space used in the DMA operation. For
an "upstream" (BMC to host) operation, the data in the client's area
will be transferred to the host. For a "downstream" (host to BMC)
operation, the host data will be placed in the client's memory area.

Poll is also provided in order to determine when the DMA operation is
complete for non-blocking IO.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/misc/aspeed-xdma.c | 301 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 301 insertions(+)

diff --git a/drivers/misc/aspeed-xdma.c b/drivers/misc/aspeed-xdma.c
index 970ea92..16235b3 100644
--- a/drivers/misc/aspeed-xdma.c
+++ b/drivers/misc/aspeed-xdma.c
@@ -113,6 +113,12 @@ struct aspeed_xdma_cmd {
 	u32 resv1;
 };
 
+struct aspeed_xdma_vga_blk {
+	u32 phys;
+	u32 size;
+	struct list_head list;
+};
+
 struct aspeed_xdma_client;
 
 struct aspeed_xdma {
@@ -123,6 +129,8 @@ struct aspeed_xdma {
 
 	unsigned long flags;
 	unsigned int cmd_idx;
+	struct mutex list_lock;
+	struct mutex start_lock;
 	wait_queue_head_t wait;
 	struct aspeed_xdma_client *current_client;
 
@@ -131,6 +139,9 @@ struct aspeed_xdma {
 	dma_addr_t vga_dma;
 	void *cmdq;
 	void *vga_virt;
+	struct list_head vga_blks_free;
+
+	struct miscdevice misc;
 };
 
 struct aspeed_xdma_client {
@@ -298,6 +309,260 @@ static irqreturn_t aspeed_xdma_irq(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+static u32 aspeed_xdma_alloc_vga_blk(struct aspeed_xdma *ctx, u32 req_size)
+{
+	u32 phys = 0;
+	u32 size = PAGE_ALIGN(req_size);
+	struct aspeed_xdma_vga_blk *free;
+
+	mutex_lock(&ctx->list_lock);
+
+	list_for_each_entry(free, &ctx->vga_blks_free, list) {
+		if (free->size >= size) {
+			phys = free->phys;
+
+			if (size == free->size) {
+				dev_dbg(ctx->dev,
+					"Allocd %08x[%08x r(%08x)], del.\n",
+					phys, size, req_size);
+				list_del(&free->list);
+				kfree(free);
+			} else {
+				free->phys += size;
+				free->size -= size;
+				dev_dbg(ctx->dev, "Allocd %08x[%08x r(%08x)], "
+					"shrunk %08x[%08x].\n", phys, size,
+					req_size, free->phys, free->size);
+			}
+
+			break;
+		}
+	}
+
+	mutex_unlock(&ctx->list_lock);
+
+	return phys;
+}
+
+static void aspeed_xdma_free_vga_blk(struct aspeed_xdma *ctx, u32 phys,
+				     u32 req_size)
+{
+	u32 min_free = UINT_MAX;
+	u32 size = PAGE_ALIGN(req_size);
+	const u32 end = phys + size;
+	struct aspeed_xdma_vga_blk *free;
+
+	mutex_lock(&ctx->list_lock);
+
+	list_for_each_entry(free, &ctx->vga_blks_free, list) {
+		if (end == free->phys) {
+			u32 fend = free->phys + free->size;
+
+			dev_dbg(ctx->dev,
+				"Freed %08x[%08x r(%08x)], exp %08x[%08x].\n",
+				phys, size, req_size, free->phys, free->size);
+
+			free->phys = phys;
+			free->size = fend - free->phys;
+
+			mutex_unlock(&ctx->list_lock);
+			return;
+		}
+
+		if (free->phys < min_free)
+			min_free = free->phys;
+	}
+
+	free = kzalloc(sizeof(*free), GFP_KERNEL);
+	if (free) {
+		free->phys = phys;
+		free->size = size;
+
+		dev_dbg(ctx->dev, "Freed %08x[%08x r(%08x)], new.\n", phys,
+			size, req_size);
+
+		if (phys < min_free)
+			list_add(&free->list, &ctx->vga_blks_free);
+		else
+			list_add_tail(&free->list, &ctx->vga_blks_free);
+	} else {
+		dev_err(ctx->dev, "Failed to register freed block.\n");
+	}
+
+	mutex_unlock(&ctx->list_lock);
+}
+
+static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf,
+				 size_t len, loff_t *offset)
+{
+	int rc;
+	struct aspeed_xdma_op op;
+	struct aspeed_xdma_client *client = file->private_data;
+	struct aspeed_xdma *ctx = client->ctx;
+	u32 offs = client->phys ? (client->phys - ctx->vga_phys) :
+		XDMA_CMDQ_SIZE;
+
+	if (len != sizeof(struct aspeed_xdma_op))
+		return -EINVAL;
+
+	rc = copy_from_user(&op, buf, len);
+	if (rc)
+		return rc;
+
+	if (op.len > (ctx->vga_size - offs) || op.len < XDMA_BYTE_ALIGN)
+		return -EINVAL;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&ctx->start_lock))
+			return -EAGAIN;
+
+		if (test_bit(XDMA_IN_PRG, &ctx->flags)) {
+			mutex_unlock(&ctx->start_lock);
+			return -EAGAIN;
+		}
+	} else {
+		mutex_lock(&ctx->start_lock);
+
+		rc = wait_event_interruptible(ctx->wait,
+					      !test_bit(XDMA_IN_PRG,
+							&ctx->flags));
+		if (rc) {
+			mutex_unlock(&ctx->start_lock);
+			return -EINTR;
+		}
+	}
+
+	ctx->current_client = client;
+	set_bit(XDMA_IN_PRG, &client->flags);
+
+	aspeed_xdma_start(ctx, &op, ctx->vga_phys + offs);
+
+	mutex_unlock(&ctx->start_lock);
+
+	if (!(file->f_flags & O_NONBLOCK)) {
+		rc = wait_event_interruptible(ctx->wait,
+					      !test_bit(XDMA_IN_PRG,
+							&ctx->flags));
+		if (rc)
+			return -EINTR;
+	}
+
+	return len;
+}
+
+static __poll_t aspeed_xdma_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	__poll_t mask = 0;
+	__poll_t req = poll_requested_events(wait);
+	struct aspeed_xdma_client *client = file->private_data;
+	struct aspeed_xdma *ctx = client->ctx;
+
+	if (req & (EPOLLIN | EPOLLRDNORM)) {
+		if (test_bit(XDMA_IN_PRG, &client->flags))
+			poll_wait(file, &ctx->wait, wait);
+
+		if (!test_bit(XDMA_IN_PRG, &client->flags))
+			mask |= EPOLLIN | EPOLLRDNORM;
+	}
+
+	if (req & (EPOLLOUT | EPOLLWRNORM)) {
+		if (test_bit(XDMA_IN_PRG, &ctx->flags))
+			poll_wait(file, &ctx->wait, wait);
+
+		if (!test_bit(XDMA_IN_PRG, &ctx->flags))
+			mask |= EPOLLOUT | EPOLLWRNORM;
+	}
+
+	return mask;
+}
+
+static void aspeed_xdma_vma_close(struct vm_area_struct *vma)
+{
+	struct aspeed_xdma_client *client = vma->vm_private_data;
+
+	aspeed_xdma_free_vga_blk(client->ctx, client->phys, client->size);
+
+	client->phys = 0;
+	client->size = 0;
+}
+
+static const struct vm_operations_struct aspeed_xdma_vm_ops = {
+	.close =	aspeed_xdma_vma_close,
+};
+
+static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	int rc;
+	struct aspeed_xdma_client *client = file->private_data;
+	struct aspeed_xdma *ctx = client->ctx;
+
+	/* restrict file to one mapping */
+	if (client->size)
+		return -ENOMEM;
+
+	client->size = vma->vm_end - vma->vm_start;
+	client->phys = aspeed_xdma_alloc_vga_blk(ctx, client->size);
+	if (!client->phys) {
+		client->size = 0;
+		return -ENOMEM;
+	}
+
+	vma->vm_pgoff = (client->phys - ctx->vga_phys) >> PAGE_SHIFT;
+	vma->vm_ops = &aspeed_xdma_vm_ops;
+	vma->vm_private_data = client;
+
+	rc = dma_mmap_coherent(ctx->dev, vma, ctx->vga_virt, ctx->vga_dma,
+			       ctx->vga_size);
+	if (rc) {
+		aspeed_xdma_free_vga_blk(ctx, client->phys, client->size);
+
+		client->phys = 0;
+		client->size = 0;
+		return rc;
+	}
+
+	dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%08x], s[%08x]\n",
+		vma->vm_start, client->phys, client->size);
+
+	return 0;
+}
+
+static int aspeed_xdma_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+	struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc);
+	struct aspeed_xdma_client *client = kzalloc(sizeof(*client),
+						    GFP_KERNEL);
+
+	if (!client)
+		return -ENOMEM;
+
+	client->ctx = ctx;
+	file->private_data = client;
+	return 0;
+}
+
+static int aspeed_xdma_release(struct inode *inode, struct file *file)
+{
+	struct aspeed_xdma_client *client = file->private_data;
+
+	if (client->ctx->current_client == client)
+		client->ctx->current_client = NULL;
+
+	kfree(client);
+	return 0;
+}
+
+static const struct file_operations aspeed_xdma_fops = {
+	.owner			= THIS_MODULE,
+	.write			= aspeed_xdma_write,
+	.poll			= aspeed_xdma_poll,
+	.mmap			= aspeed_xdma_mmap,
+	.open			= aspeed_xdma_open,
+	.release		= aspeed_xdma_release,
+};
+
 static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
 {
 	int rc;
@@ -360,12 +625,26 @@ static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
 		return -ENOMEM;
 	}
 
+	aspeed_xdma_free_vga_blk(ctx, ctx->vga_phys, ctx->vga_size);
+	aspeed_xdma_alloc_vga_blk(ctx, XDMA_CMDQ_SIZE);
+
 	dev_dbg(ctx->dev, "VGA mapped at phys[%08x], size[%08x].\n",
 		ctx->vga_phys, ctx->vga_size);
 
 	return 0;
 }
 
+static void aspeed_xdma_free_vga_blks(struct aspeed_xdma *ctx)
+{
+	struct aspeed_xdma_vga_blk *free;
+	struct aspeed_xdma_vga_blk *tmp;
+
+	list_for_each_entry_safe(free, tmp, &ctx->vga_blks_free, list) {
+		list_del(&free->list);
+		kfree(free);
+	}
+}
+
 static int aspeed_xdma_probe(struct platform_device *pdev)
 {
 	int irq;
@@ -380,6 +659,9 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
 	ctx->dev = dev;
 	platform_set_drvdata(pdev, ctx);
 	init_waitqueue_head(&ctx->wait);
+	mutex_init(&ctx->list_lock);
+	mutex_init(&ctx->start_lock);
+	INIT_LIST_HEAD(&ctx->vga_blks_free);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	ctx->base = devm_ioremap_resource(dev, res);
@@ -425,6 +707,22 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
 
 	aspeed_xdma_init_eng(ctx);
 
+	ctx->misc.minor = MISC_DYNAMIC_MINOR;
+	ctx->misc.fops = &aspeed_xdma_fops;
+	ctx->misc.name = "xdma";
+	ctx->misc.parent = dev;
+	rc = misc_register(&ctx->misc);
+	if (rc) {
+		dev_err(dev, "Unable to register xdma miscdevice\n");
+
+		aspeed_xdma_free_vga_blks(ctx);
+		dma_free_coherent(dev, ctx->vga_size, ctx->vga_virt,
+				  ctx->vga_dma);
+		dma_release_declared_memory(dev);
+		reset_control_assert(ctx->reset);
+		return rc;
+	}
+
 	return 0;
 }
 
@@ -432,6 +730,9 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
 {
 	struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
 
+	misc_deregister(&ctx->misc);
+
+	aspeed_xdma_free_vga_blks(ctx);
 	dma_free_coherent(ctx->dev, ctx->vga_size, ctx->vga_virt,
 			  ctx->vga_dma);
 	dma_release_declared_memory(ctx->dev);
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 2/6] drivers/misc: Add Aspeed XDMA engine driver
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

The XDMA engine embedded in the AST2500 SOC performs PCI DMA operations
between the SOC (acting as a BMC) and a host processor in a server.

This commit adds a driver to control the XDMA engine and adds functions
to initialize the hardware and memory and start DMA operations.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 MAINTAINERS                      |   9 +
 drivers/misc/Kconfig             |   8 +
 drivers/misc/Makefile            |   1 +
 drivers/misc/aspeed-xdma.c       | 461 +++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/aspeed-xdma.h |  26 +++
 5 files changed, 505 insertions(+)
 create mode 100644 drivers/misc/aspeed-xdma.c
 create mode 100644 include/uapi/linux/aspeed-xdma.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 1e64279..8428f84 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2521,6 +2521,15 @@ S:	Maintained
 F:	drivers/media/platform/aspeed-video.c
 F:	Documentation/devicetree/bindings/media/aspeed-video.txt
 
+ASPEED XDMA ENGINE DRIVER
+M:	Eddie James <eajames@linux.ibm.com>
+L:	linux-aspeed at lists.ozlabs.org (moderated for non-subscribers)
+L:	linux-kernel at vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/misc/aspeed,xdma.txt
+F:	drivers/misc/aspeed-xdma.c
+F:	include/uapi/linux/aspeed-xdma.h
+
 ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
 M:	Corentin Chary <corentin.chary@gmail.com>
 L:	acpi4asus-user at lists.sourceforge.net
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 42ab8ec..8316f80 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -512,6 +512,14 @@ config ASPEED_LPC_SNOOP
 	  allows the BMC to listen on and save the data written by
 	  the host to an arbitrary LPC I/O port.
 
+config ASPEED_XDMA
+	tristate "Aspeed XDMA Engine Driver"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON && HAS_DMA
+	help
+	  Enable support for the Aspeed XDMA Engine found on the Aspeed AST2500
+	  SOC. The XDMA engine can perform automatic PCI DMA operations between
+	  the AST2500 (acting as a BMC) and a host processor.
+
 config PCI_ENDPOINT_TEST
 	depends on PCI
 	select CRC32
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d5b7d34..969a680 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_XDMA)	+= aspeed-xdma.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-y				+= cardreader/
diff --git a/drivers/misc/aspeed-xdma.c b/drivers/misc/aspeed-xdma.c
new file mode 100644
index 0000000..970ea92
--- /dev/null
+++ b/drivers/misc/aspeed-xdma.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#include <linux/aspeed-xdma.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#define DEVICE_NAME			"aspeed-xdma"
+
+#define SCU_STRAP			0x070
+#define  SCU_STRAP_VGA_MEM		GENMASK(3, 2)
+
+#define SCU_PCIE_CONF			0x180
+#define  SCU_PCIE_CONF_VGA_EN		BIT(0)
+#define  SCU_PCIE_CONF_VGA_EN_MMIO	BIT(1)
+#define  SCU_PCIE_CONF_VGA_EN_LPC	BIT(2)
+#define  SCU_PCIE_CONF_VGA_EN_MSI	BIT(3)
+#define  SCU_PCIE_CONF_VGA_EN_MCTP	BIT(4)
+#define  SCU_PCIE_CONF_VGA_EN_IRQ	BIT(5)
+#define  SCU_PCIE_CONF_VGA_EN_DMA	BIT(6)
+#define  SCU_PCIE_CONF_BMC_EN		BIT(8)
+#define  SCU_PCIE_CONF_BMC_EN_MMIO	BIT(9)
+#define  SCU_PCIE_CONF_BMC_EN_MSI	BIT(11)
+#define  SCU_PCIE_CONF_BMC_EN_MCTP	BIT(12)
+#define  SCU_PCIE_CONF_BMC_EN_IRQ	BIT(13)
+#define  SCU_PCIE_CONF_BMC_EN_DMA	BIT(14)
+#define  SCU_PCIE_CONF_RSVD		GENMASK(19, 18)
+
+#define SDMC_CONF			0x004
+#define  SDMC_CONF_MEM			GENMASK(1, 0)
+#define SDMC_REMAP			0x008
+#define  SDMC_REMAP_MAGIC		GENMASK(17, 16)
+
+#define XDMA_CMD_SIZE			4
+#define XDMA_CMDQ_SIZE			PAGE_SIZE
+#define XDMA_BYTE_ALIGN			16
+#define XDMA_MAX_LINE_SIZE		BIT(10)
+#define XDMA_NUM_CMDS			\
+	(XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd))
+#define XDMA_NUM_DEBUGFS_REGS		6
+
+#define XDMA_CMD_BMC_CHECK		BIT(0)
+#define XDMA_CMD_BMC_ADDR		GENMASK(29, 4)
+#define XDMA_CMD_BMC_DIR_US		BIT(31)
+
+#define XDMA_CMD_COMM1_HI_HOST_PITCH	GENMASK(14, 3)
+#define XDMA_CMD_COMM1_HI_BMC_PITCH	GENMASK(30, 19)
+
+#define XDMA_CMD_CONF_CHECK		BIT(1)
+#define XDMA_CMD_CONF_LINE_SIZE		GENMASK(14, 4)
+#define XDMA_CMD_CONF_IRQ_BMC		BIT(15)
+#define XDMA_CMD_CONF_NUM_LINES		GENMASK(27, 16)
+#define XDMA_CMD_CONF_IRQ		BIT(31)
+
+#define XDMA_DS_PCIE_REQ_SIZE_128	0
+#define XDMA_DS_PCIE_REQ_SIZE_256	1
+#define XDMA_DS_PCIE_REQ_SIZE_512	2
+#define XDMA_DS_PCIE_REQ_SIZE_1K	3
+#define XDMA_DS_PCIE_REQ_SIZE_2K	4
+#define XDMA_DS_PCIE_REQ_SIZE_4K	5
+
+#define XDMA_BMC_CMD_QUEUE_ADDR		0x10
+#define XDMA_BMC_CMD_QUEUE_ENDP		0x14
+#define XDMA_BMC_CMD_QUEUE_WRITEP	0x18
+#define XDMA_BMC_CMD_QUEUE_READP	0x1c
+#define  XDMA_BMC_CMD_QUEUE_READP_MAGIC	0xee882266
+#define XDMA_CTRL			0x20
+#define  XDMA_CTRL_US_COMP		BIT(4)
+#define  XDMA_CTRL_DS_COMP		BIT(5)
+#define  XDMA_CTRL_DS_DIRTY		BIT(6)
+#define  XDMA_CTRL_DS_PCIE_REQ_SIZE	GENMASK(19, 17)
+#define  XDMA_CTRL_DS_DATA_TIMEOUT	BIT(28)
+#define  XDMA_CTRL_DS_CHECK_ID		BIT(29)
+#define XDMA_STATUS			0x24
+#define  XDMA_STATUS_US_COMP		BIT(4)
+#define  XDMA_STATUS_DS_COMP		BIT(5)
+
+enum {
+	XDMA_IN_PRG,
+	XDMA_UPSTREAM,
+};
+
+struct aspeed_xdma_cmd {
+	u32 host_addr_lo;
+	u32 host_addr_hi;
+	u32 bmc_addr;
+	u32 comm1_hi;
+	u32 conf;
+	u32 id;
+	u32 resv0;
+	u32 resv1;
+};
+
+struct aspeed_xdma_client;
+
+struct aspeed_xdma {
+	struct device *dev;
+	void __iomem *base;
+	struct regmap *scu;
+	struct reset_control *reset;
+
+	unsigned long flags;
+	unsigned int cmd_idx;
+	wait_queue_head_t wait;
+	struct aspeed_xdma_client *current_client;
+
+	u32 vga_phys;
+	u32 vga_size;
+	dma_addr_t vga_dma;
+	void *cmdq;
+	void *vga_virt;
+};
+
+struct aspeed_xdma_client {
+	struct aspeed_xdma *ctx;
+
+	unsigned long flags;
+	u32 phys;
+	u32 size;
+};
+
+static u32 aspeed_xdma_reg_read(struct aspeed_xdma *ctx, u32 reg)
+{
+	u32 v = readl(ctx->base + reg);
+
+	dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v);
+	return v;
+}
+
+static void aspeed_xdma_reg_write(struct aspeed_xdma *ctx, u32 reg, u32 val)
+{
+	writel(val, ctx->base + reg);
+	dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, readl(ctx->base + reg));
+}
+
+static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx)
+{
+	const u32 ctrl = XDMA_CTRL_US_COMP | XDMA_CTRL_DS_COMP |
+		XDMA_CTRL_DS_DIRTY | FIELD_PREP(XDMA_CTRL_DS_PCIE_REQ_SIZE,
+						XDMA_DS_PCIE_REQ_SIZE_256) |
+		XDMA_CTRL_DS_DATA_TIMEOUT | XDMA_CTRL_DS_CHECK_ID;
+
+	aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_ENDP,
+			      XDMA_CMD_SIZE * XDMA_NUM_CMDS);
+	aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_READP,
+			      XDMA_BMC_CMD_QUEUE_READP_MAGIC);
+	aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_WRITEP, 0);
+	aspeed_xdma_reg_write(ctx, XDMA_CTRL, ctrl);
+
+	aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_ADDR, ctx->vga_phys);
+
+	ctx->cmd_idx = 0;
+	ctx->flags = 0;
+}
+
+static void aspeed_xdma_reset(struct aspeed_xdma *ctx)
+{
+	reset_control_assert(ctx->reset);
+
+	msleep(10);
+
+	reset_control_deassert(ctx->reset);
+
+	msleep(10);
+
+	aspeed_xdma_init_eng(ctx);
+}
+
+static void aspeed_xdma_start(struct aspeed_xdma *ctx,
+			      struct aspeed_xdma_op *op, u32 bmc_addr)
+{
+	u32 conf = XDMA_CMD_CONF_CHECK | XDMA_CMD_CONF_IRQ_BMC |
+		XDMA_CMD_CONF_IRQ;
+	unsigned int line_size = op->len / XDMA_BYTE_ALIGN;
+	unsigned int num_lines = 1;
+	unsigned int nidx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS;
+	struct aspeed_xdma_cmd *cmd =
+		&(((struct aspeed_xdma_cmd *)ctx->cmdq)[ctx->cmd_idx]);
+
+	if (line_size > XDMA_MAX_LINE_SIZE) {
+		unsigned int rem;
+		unsigned int total;
+
+		num_lines = line_size / XDMA_MAX_LINE_SIZE;
+		total = XDMA_MAX_LINE_SIZE * num_lines;
+		rem = line_size - total;
+		line_size = XDMA_MAX_LINE_SIZE;
+
+		if (rem) {
+			unsigned int offs = total * XDMA_BYTE_ALIGN;
+			u32 r_bmc_addr = bmc_addr + offs;
+			u64 r_host_addr = op->host_addr + (u64)offs;
+			struct aspeed_xdma_cmd *r_cmd =
+				&(((struct aspeed_xdma_cmd *)ctx->cmdq)[nidx]);
+
+			r_cmd->host_addr_lo =
+				(u32)(r_host_addr & 0xFFFFFFFFULL);
+			r_cmd->host_addr_hi = (u32)(r_host_addr >> 32ULL);
+			r_cmd->bmc_addr = (r_bmc_addr & XDMA_CMD_BMC_ADDR) |
+				XDMA_CMD_BMC_CHECK |
+				(op->upstream ? XDMA_CMD_BMC_DIR_US : 0);
+			r_cmd->conf = conf |
+				FIELD_PREP(XDMA_CMD_CONF_LINE_SIZE, rem) |
+				FIELD_PREP(XDMA_CMD_CONF_NUM_LINES, 1);
+			r_cmd->comm1_hi =
+				FIELD_PREP(XDMA_CMD_COMM1_HI_HOST_PITCH, 1) |
+				FIELD_PREP(XDMA_CMD_COMM1_HI_BMC_PITCH, 1);
+
+			/* do not trigger IRQ for first command */
+			conf = XDMA_CMD_CONF_CHECK;
+
+			nidx = (nidx + 1) % XDMA_NUM_CMDS;
+		}
+
+		/* undocumented formula to get required number of lines */
+		num_lines = (num_lines * 2) - 1;
+	}
+
+	/* ctrl == 0 indicates engine hasn't started properly; restart it */
+	if (!aspeed_xdma_reg_read(ctx, XDMA_CTRL))
+		aspeed_xdma_reset(ctx);
+
+	cmd->host_addr_lo = (u32)(op->host_addr & 0xFFFFFFFFULL);
+	cmd->host_addr_hi = (u32)(op->host_addr >> 32ULL);
+	cmd->bmc_addr = (bmc_addr & XDMA_CMD_BMC_ADDR) | XDMA_CMD_BMC_CHECK |
+		(op->upstream ? XDMA_CMD_BMC_DIR_US : 0);
+	cmd->conf = conf |
+		FIELD_PREP(XDMA_CMD_CONF_LINE_SIZE, line_size) |
+		FIELD_PREP(XDMA_CMD_CONF_NUM_LINES, num_lines);
+	cmd->comm1_hi = FIELD_PREP(XDMA_CMD_COMM1_HI_HOST_PITCH, line_size) |
+			FIELD_PREP(XDMA_CMD_COMM1_HI_BMC_PITCH, line_size);
+
+	memcpy(ctx->vga_virt, ctx->cmdq, XDMA_CMDQ_SIZE);
+
+	if (op->upstream)
+		set_bit(XDMA_UPSTREAM, &ctx->flags);
+	else
+		clear_bit(XDMA_UPSTREAM, &ctx->flags);
+
+	set_bit(XDMA_IN_PRG, &ctx->flags);
+
+	aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_WRITEP,
+			      nidx * XDMA_CMD_SIZE);
+	ctx->cmd_idx = nidx;
+}
+
+static void aspeed_xdma_done(struct aspeed_xdma *ctx)
+{
+	if (ctx->current_client) {
+		clear_bit(XDMA_IN_PRG, &ctx->current_client->flags);
+
+		ctx->current_client = NULL;
+	}
+
+	clear_bit(XDMA_IN_PRG, &ctx->flags);
+	wake_up_interruptible_all(&ctx->wait);
+}
+
+static irqreturn_t aspeed_xdma_irq(int irq, void *arg)
+{
+	struct aspeed_xdma *ctx = arg;
+	u32 status = aspeed_xdma_reg_read(ctx, XDMA_STATUS);
+
+	if (status & XDMA_STATUS_US_COMP) {
+		if (test_bit(XDMA_UPSTREAM, &ctx->flags))
+			aspeed_xdma_done(ctx);
+	}
+
+	if (status & XDMA_STATUS_DS_COMP) {
+		if (!test_bit(XDMA_UPSTREAM, &ctx->flags))
+			aspeed_xdma_done(ctx);
+	}
+
+	aspeed_xdma_reg_write(ctx, XDMA_STATUS, status);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
+{
+	int rc;
+	u32 scu_conf = 0;
+	u32 mem_size = 0x20000000;
+	const u32 pcie_conf = SCU_PCIE_CONF_VGA_EN | SCU_PCIE_CONF_VGA_EN_MSI |
+		SCU_PCIE_CONF_VGA_EN_MCTP | SCU_PCIE_CONF_VGA_EN_IRQ |
+		SCU_PCIE_CONF_VGA_EN_DMA | SCU_PCIE_CONF_BMC_EN_MSI |
+		SCU_PCIE_CONF_BMC_EN_MCTP | SCU_PCIE_CONF_BMC_EN_IRQ |
+		SCU_PCIE_CONF_BMC_EN_DMA | SCU_PCIE_CONF_RSVD;
+	const u32 mem_sizes[4] = { 0x8000000, 0x10000000, 0x20000000,
+				   0x40000000 };
+	const u32 vga_sizes[4] = { 0x800000, 0x1000000, 0x2000000, 0x4000000 };
+	void __iomem *sdmc_base = ioremap(0x1e6e0000, 0x100);
+
+	regmap_write(ctx->scu, SCU_PCIE_CONF, pcie_conf);
+
+	regmap_read(ctx->scu, SCU_STRAP, &scu_conf);
+	ctx->vga_size = vga_sizes[FIELD_GET(SCU_STRAP_VGA_MEM, scu_conf)];
+
+	if (sdmc_base) {
+		u32 sdmc = readl(sdmc_base + SDMC_CONF);
+		u32 remap = readl(sdmc_base + SDMC_REMAP);
+
+		remap |= SDMC_REMAP_MAGIC;
+		writel(remap, sdmc_base + SDMC_REMAP);
+		remap = readl(sdmc_base + SDMC_REMAP);
+
+		mem_size = mem_sizes[sdmc & SDMC_CONF_MEM];
+		iounmap(sdmc_base);
+	}
+
+	ctx->vga_phys = (mem_size - ctx->vga_size) + 0x80000000;
+
+	ctx->cmdq = devm_kzalloc(ctx->dev, XDMA_CMDQ_SIZE, GFP_KERNEL);
+	if (!ctx->cmdq) {
+		dev_err(ctx->dev, "Failed to allocate command queue.\n");
+		return -ENOMEM;
+	}
+
+	rc = dma_set_mask_and_coherent(ctx->dev, DMA_BIT_MASK(32));
+	if (rc) {
+		dev_err(ctx->dev, "Failed to set DMA mask: %d.\n", rc);
+		return rc;
+	}
+
+	rc = dma_declare_coherent_memory(ctx->dev, ctx->vga_phys,
+					 ctx->vga_phys, ctx->vga_size);
+	if (rc) {
+		dev_err(ctx->dev, "Failed to declare coherent memory: %d.\n",
+			rc);
+		return rc;
+	}
+
+	ctx->vga_virt = dma_alloc_coherent(ctx->dev, ctx->vga_size,
+					   &ctx->vga_dma, GFP_KERNEL);
+	if (!ctx->vga_virt) {
+		dev_err(ctx->dev, "Failed to allocate DMA.\n");
+		dma_release_declared_memory(ctx->dev);
+		return -ENOMEM;
+	}
+
+	dev_dbg(ctx->dev, "VGA mapped at phys[%08x], size[%08x].\n",
+		ctx->vga_phys, ctx->vga_size);
+
+	return 0;
+}
+
+static int aspeed_xdma_probe(struct platform_device *pdev)
+{
+	int irq;
+	int rc;
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+	struct aspeed_xdma *ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	platform_set_drvdata(pdev, ctx);
+	init_waitqueue_head(&ctx->wait);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ctx->base)) {
+		dev_err(dev, "Unable to ioremap registers\n");
+		return PTR_ERR(ctx->base);
+	}
+
+	irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (!irq) {
+		dev_err(dev, "Unable to find IRQ\n");
+		return -ENODEV;
+	}
+
+	rc = devm_request_irq(dev, irq, aspeed_xdma_irq, IRQF_SHARED,
+			      DEVICE_NAME, ctx);
+	if (rc < 0) {
+		dev_err(dev, "Unable to request IRQ %d\n", irq);
+		return rc;
+	}
+
+	ctx->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
+	if (IS_ERR(ctx->scu)) {
+		dev_err(ctx->dev, "Unable to grab SCU regs\n");
+		return PTR_ERR(ctx->scu);
+	}
+
+	ctx->reset = devm_reset_control_get_exclusive(dev, NULL);
+	if (IS_ERR(ctx->reset)) {
+		dev_err(dev, "Unable to request reset control\n");
+		return PTR_ERR(ctx->reset);
+	}
+
+	reset_control_deassert(ctx->reset);
+
+	msleep(10);
+
+	rc = aspeed_xdma_init_mem(ctx);
+	if (rc) {
+		reset_control_assert(ctx->reset);
+		return rc;
+	}
+
+	aspeed_xdma_init_eng(ctx);
+
+	return 0;
+}
+
+static int aspeed_xdma_remove(struct platform_device *pdev)
+{
+	struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+
+	dma_free_coherent(ctx->dev, ctx->vga_size, ctx->vga_virt,
+			  ctx->vga_dma);
+	dma_release_declared_memory(ctx->dev);
+	reset_control_assert(ctx->reset);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_xdma_match[] = {
+	{ .compatible = "aspeed,ast2500-xdma" },
+	{ },
+};
+
+static struct platform_driver aspeed_xdma_driver = {
+	.probe = aspeed_xdma_probe,
+	.remove = aspeed_xdma_remove,
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = aspeed_xdma_match,
+	},
+};
+
+module_platform_driver(aspeed_xdma_driver);
+
+MODULE_AUTHOR("Eddie James");
+MODULE_DESCRIPTION("Aspeed XDMA Engine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h
new file mode 100644
index 0000000..9c1659d
--- /dev/null
+++ b/include/uapi/linux/aspeed-xdma.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright IBM Corp 2019 */
+
+#ifndef _UAPI_LINUX_ASPEED_XDMA_H_
+#define _UAPI_LINUX_ASPEED_XDMA_H_
+
+#include <linux/types.h>
+
+/*
+ * aspeed_xdma_op
+ *
+ * upstream: boolean indicating the direction of the DMA operation; upstream
+ *           means a transfer from the BMC to the host
+ *
+ * host_addr: the DMA address on the host side, typically configured by PCI
+ *            subsystem
+ *
+ * len: the size of the transfer in bytes; it should be a multiple of 16 bytes
+ */
+struct aspeed_xdma_op {
+	__u8 upstream;
+	__u64 host_addr;
+	__u32 len;
+} __packed;
+
+#endif /* _UAPI_LINUX_ASPEED_XDMA_H_ */
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 1/6] dt-bindings: misc: Add Aspeed XDMA engine binding documentation
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1551735420-16202-1-git-send-email-eajames@linux.ibm.com>

Document the bindings.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 .../devicetree/bindings/misc/aspeed,xdma.txt       | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/aspeed,xdma.txt

diff --git a/Documentation/devicetree/bindings/misc/aspeed,xdma.txt b/Documentation/devicetree/bindings/misc/aspeed,xdma.txt
new file mode 100644
index 0000000..85e82ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/aspeed,xdma.txt
@@ -0,0 +1,23 @@
+* Device tree bindings for the Aspeed XDMA engine
+
+The XDMA Engine embedded in the AST2500 SOC can perform automatic DMA
+operations over PCI between the AST2500 (acting as a BMC) and a host processor.
+
+Required properties:
+
+ - compatible		"aspeed,ast2500-xdma"
+ - reg			contains the offset and length of the memory region
+			assigned to the XDMA registers
+ - resets		reset specifier for the syscon reset associated with
+			the XDMA engine
+ - interrupts		the interrupt associated with the XDMA engine on this
+			platform
+
+Example:
+
+    xdma at 1e6e7000 {
+        compatible = "aspeed,ast2500-xdma";
+        reg = <0x1e6e7000 0x100>;
+        resets = <&syscon ASPEED_RESET_XDMA>;
+        interrupts = <6>;
+    };
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 0/6] drivers/misc: Add XDMA engine driver
From: Eddie James @ 2019-03-04 21:36 UTC (permalink / raw)
  To: linux-aspeed

The XDMA engine embedded in the AST2500 SOC performs PCI DMA operations
between the SOC (acting as a BMC) and a host processor in a server.

This series adds a driver to control the XDMA engine in order to easily
perform DMA operations to and from the host processor.

Eddie James (6):
  dt-bindings: misc: Add Aspeed XDMA engine binding documentation
  drivers/misc: Add Aspeed XDMA engine driver
  drivers/misc: xdma: Add user interface
  drivers/misc: xdma: Add PCI device configuration sysfs
  drivers/misc: xdma: Add debugfs entries
  ARM: dts: aspeed: Add XDMA engine

 .../devicetree/bindings/misc/aspeed,xdma.txt       |  23 +
 MAINTAINERS                                        |   9 +
 arch/arm/boot/dts/aspeed-g5.dtsi                   |   8 +
 drivers/misc/Kconfig                               |   8 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/aspeed-xdma.c                         | 924 +++++++++++++++++++++
 include/uapi/linux/aspeed-xdma.h                   |  26 +
 7 files changed, 999 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/aspeed,xdma.txt
 create mode 100644 drivers/misc/aspeed-xdma.c
 create mode 100644 include/uapi/linux/aspeed-xdma.h

-- 
1.8.3.1


^ permalink raw reply

* [PATCH v6 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-04 18:55 UTC (permalink / raw)
  To: linux-aspeed

The ASPEED AST2400, and AST2500 in some configurations include a
PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
in the BMC's physical address space.  This feature is especially useful
when using this bridge to send large files to the BMC.

The host may use this to send down a firmware image by staging data at a
specific memory address, and in a coordinated effort with the BMC's
software stack and kernel, transmit the bytes.

This driver enables the BMC to unlock the PCI bridge on demand, and
configure it via ioctl to allow the host to write bytes to an agreed
upon location.  In the primary use-case, the region to use is known
apriori on the BMC, and the host requests this information.  Once this
request is received, the BMC's software stack will enable the bridge and
the region and then using some software flow control (possibly via IPMI
packets), copy the bytes down.  Once the process is complete, the BMC
will disable the bridge and unset any region involved.

The default behavior of this bridge when present is: enabled and all
regions marked read-write.  This driver will fix the regions to be
read-only and then disable the bridge entirely.

The memory regions protected are:
 * BMC flash MMIO window
 * System flash MMIO windows
 * SOC IO (peripheral MMIO)
 * DRAM

The DRAM region itself is all of DRAM and cannot be further specified.
Once the PCI bridge is enabled, the host can read all of DRAM, and if
the DRAM section is write-enabled, then it can write to all of it.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v6:
- Cleaned up documentation
- Added missing machine-readable copyright lines.
- Fixed over 80 chars instances.
- Changed error from invalid memory-region node to ENODEV.
Changes for v5:
- Fixup missing exit condition and remove extra jump.
Changes for v4:
- Added ioctl for reading back the memory-region configuration.
- Cleaned up some unused variables.
Changes for v3:
- Deleted unused read and write methods.
Changes for v2:
- Dropped unused reads.
- Moved call to disable bridge to before registering device.
- Switch from using regs to using a syscon regmap. <<< IN PROGRESS
- Updated the commit message. <<< TODO
- Updated the bit flipped for SCU180_ENP2A
- Dropped boolean region_specified variable.
- Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
- Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
- Updated commit message.
---
 drivers/misc/Kconfig                 |   8 +
 drivers/misc/Makefile                |   1 +
 drivers/misc/aspeed-p2a-ctrl.c       | 447 +++++++++++++++++++++++++++
 include/uapi/linux/aspeed-p2a-ctrl.h |  62 ++++
 4 files changed, 518 insertions(+)
 create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
 create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f417b06e11c5..9de1bafe5606 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
 	  bus. System Configuration interface is one of the possible means
 	  of generating transactions on this bus.
 
+config ASPEED_P2A_CTRL
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
+	help
+	  Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings through
+	  ioctl()s, the driver also provides an interface for userspace mappings to
+	  a pre-defined region.
+
 config ASPEED_LPC_CTRL
 	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
 	tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e39ccbbc1b3a..57577aee354f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_P2A_CTRL)	+= aspeed-p2a-ctrl.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-y				+= cardreader/
diff --git a/drivers/misc/aspeed-p2a-ctrl.c b/drivers/misc/aspeed-p2a-ctrl.c
new file mode 100644
index 000000000000..f79e122a71e2
--- /dev/null
+++ b/drivers/misc/aspeed-p2a-ctrl.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/aspeed-p2a-ctrl.h>
+
+#define DEVICE_NAME	"aspeed-p2a-ctrl"
+
+/* SCU2C is a Misc. Control Register. */
+#define SCU2C 0x2c
+/* SCU180 is the PCIe Configuration Setting Control Register. */
+#define SCU180 0x180
+/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device
+ * on the PCI bus.
+ */
+#define SCU180_ENP2A BIT(1)
+
+/* The ast2400/2500 both have six ranges. */
+#define P2A_REGION_COUNT 6
+
+struct region {
+	u32 min;
+	u32 max;
+	u32 bit;
+};
+
+struct aspeed_p2a_model_data {
+	/* min, max, bit */
+	struct region regions[P2A_REGION_COUNT];
+};
+
+struct aspeed_p2a_ctrl {
+	struct miscdevice miscdev;
+	struct regmap *regmap;
+
+	const struct aspeed_p2a_model_data *config;
+
+	/* Access to these needs to be locked, held via probe, mapping ioctl,
+	 * and release, remove.
+	 */
+	struct mutex tracking;
+	u32 readers;
+	u32 readerwriters[P2A_REGION_COUNT];
+
+	phys_addr_t mem_base;
+	resource_size_t mem_size;
+};
+
+struct aspeed_p2a_user {
+	struct file *file;
+	struct aspeed_p2a_ctrl *parent;
+
+	/* The entire memory space is opened for reading once the bridge is
+	 * enabled, therefore this needs only to be tracked once per user.
+	 * If any user has it open for read, the bridge must stay enabled.
+	 */
+	u32 read;
+
+	/* Each entry of the array corresponds to a P2A Region.  If the user
+	 * opens for read or readwrite, the reference goes up here.  On
+	 * release, this array is walked and references adjusted accordingly.
+	 */
+	u32 readwrite[P2A_REGION_COUNT];
+};
+
+static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap,
+		SCU180, SCU180_ENP2A, SCU180_ENP2A);
+}
+
+static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
+}
+
+static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+
+	if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
+		return -EINVAL;
+
+	unsigned long vsize = vma->vm_end - vma->vm_start;
+	pgprot_t prot = vma->vm_page_prot;
+
+	if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
+		return -EINVAL;
+
+	/* ast2400/2500 AHB accesses are not cache coherent */
+	prot = pgprot_noncached(prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+		(ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
+		vsize, prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
+		struct aspeed_p2a_ctrl *ctrl,
+		struct aspeed_p2a_ctrl_mapping *map)
+{
+	int i;
+	u32 base, end;
+
+	base = map->addr;
+	end = map->addr + (map->length - 1);
+
+	/* If the value is a legal u32, it will find a match. */
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		const struct region *curr = &ctrl->config->regions[i];
+
+		/* If the top of this region is lower than your base, skip it.
+		 */
+		if (curr->max < base)
+			continue;
+
+		/* If the bottom of this region is higher than your end, bail.
+		 */
+		if (curr->min > end)
+			break;
+		/* Lock this and update it, therefore it someone else is
+		 * closing their file out, this'll preserve the increment.
+		 */
+		mutex_lock(&ctrl->tracking);
+		ctrl->readerwriters[i] += 1;
+		mutex_unlock(&ctrl->tracking);
+
+		/* Track with the user, so when they close their file, we can
+		 * decrement properly.
+		 */
+		priv->readwrite[i] += 1;
+
+		/* Enable the region as read-write. */
+		regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
+	}
+}
+
+static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
+		unsigned long data)
+{
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+	void __user *arg = (void __user *)data;
+	struct aspeed_p2a_ctrl_mapping map;
+
+	if (copy_from_user(&map, arg, sizeof(map)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
+		/* If they want a region to be read-only, since the entire
+		 * region is read-only once enabled, we just need to track this
+		 * user wants to read from the bridge, and if it's not enabled.
+		 * Enable it.
+		 */
+		if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
+			mutex_lock(&ctrl->tracking);
+			ctrl->readers += 1;
+			mutex_unlock(&ctrl->tracking);
+
+			/* Track with the user, so when they close their file,
+			 * we can decrement properly.
+			 */
+			priv->read += 1;
+		} else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
+			aspeed_p2a_region_acquire(priv, ctrl, &map);
+		} else {
+			/* Invalid map flags. */
+			return -EINVAL;
+		}
+
+		aspeed_p2a_enable_bridge(ctrl);
+		return 0;
+	case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
+		/* This is a request for the memory-region and corresponding
+		 * length that is used by the driver for mmap.
+		 */
+
+		map.flags = 0;
+		map.addr = ctrl->mem_base;
+		map.length = ctrl->mem_size;
+
+		return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
+	}
+
+	return -EINVAL;
+}
+
+
+/*
+ * When a user opens this file, we create a structure to track their mappings.
+ *
+ * A user can map a region as read-only (bridge enabled), or read-write (bit
+ * flipped, and bridge enabled).  Either way, this tracking is used, s.t. when
+ * they release the device references are handled.
+ *
+ * The bridge is not enabled until a user calls an ioctl to map a region,
+ * simply opening the device does not enable it.
+ */
+static int aspeed_p2a_open(struct inode *inode, struct file *file)
+{
+	struct aspeed_p2a_user *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->file = file;
+	priv->read = 0;
+	memset(priv->readwrite, 0, sizeof(priv->readwrite));
+
+	/* The file's private_data is initialized to the p2a_ctrl. */
+	priv->parent = file->private_data;
+
+	/* Set the file's private_data to the user's data. */
+	file->private_data = priv;
+
+	return 0;
+}
+
+/*
+ * This will close the users mappings.  It will go through what they had opened
+ * for readwrite, and decrement those counts.  If at the end, this is the last
+ * user, it'll close the bridge.
+ */
+static int aspeed_p2a_release(struct inode *inode, struct file *file)
+{
+	int i;
+	u32 value;
+	u32 bits = 0;
+	bool open_regions = false;
+	struct aspeed_p2a_user *priv = file->private_data;
+
+	/* Lock others from changing these values until everything is updated
+	 * in one pass.
+	 */
+	mutex_lock(&priv->parent->tracking);
+
+	priv->parent->readers -= priv->read;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		priv->parent->readerwriters[i] -= priv->readwrite[i];
+
+		if (priv->parent->readerwriters[i] > 0)
+			open_regions = true;
+		else
+			bits |= priv->parent->config->regions[i].bit;
+	}
+
+	/* Setting a bit to 1 disables the region, so let's just OR with the
+	 * above to disable any.
+	 */
+
+	/* Note, if another user is trying to ioctl, they can't grab tracking,
+	 * and therefore can't grab either register mutex.
+	 * If another user is trying to close, they can't grab tracking either.
+	 */
+	regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
+
+	/* If parent->readers is zero and open windows is 0, disable the
+	 * bridge.
+	 */
+	if (!open_regions && priv->parent->readers == 0)
+		aspeed_p2a_disable_bridge(priv->parent);
+
+	mutex_unlock(&priv->parent->tracking);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct file_operations aspeed_p2a_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.mmap = aspeed_p2a_mmap,
+	.unlocked_ioctl = aspeed_p2a_ioctl,
+	.open = aspeed_p2a_open,
+	.release = aspeed_p2a_release,
+};
+
+/* The regions are controlled by SCU2C */
+static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	int i;
+	u32 value = 0;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++)
+		value |= p2a_ctrl->config->regions[i].bit;
+
+	regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
+
+	/* Disable the bridge. */
+	aspeed_p2a_disable_bridge(p2a_ctrl);
+}
+
+static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *misc_ctrl;
+	struct device *dev;
+	struct resource *res, resm;
+	struct device_node *node;
+	int rc = 0;
+
+	dev = &pdev->dev;
+
+	misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
+	if (!misc_ctrl)
+		return -ENOMEM;
+
+	mutex_init(&misc_ctrl->tracking);
+	misc_ctrl->readers = 0;
+	memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
+
+	misc_ctrl->mem_size = 0;
+	misc_ctrl->mem_base = 0;
+
+	/* optional. */
+	node = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (node) {
+		rc = of_address_to_resource(node, 0, &resm);
+		of_node_put(node);
+		if (rc) {
+			dev_err(dev, "Couldn't address to resource for reserved memory\n");
+			return -ENODEV;
+		}
+
+		misc_ctrl->mem_size = resource_size(&resm);
+		misc_ctrl->mem_base = resm.start;
+	}
+
+	node = of_parse_phandle(dev->of_node, "syscon", 0);
+	if (!node) {
+		dev_err(dev, "Couldn't find syscon property\n");
+		return -ENODEV;
+	}
+
+	misc_ctrl->regmap = syscon_node_to_regmap(node);
+	if (IS_ERR(misc_ctrl->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+	of_node_put(node);
+
+	misc_ctrl->config = of_device_get_match_data(dev);
+
+	dev_set_drvdata(&pdev->dev, misc_ctrl);
+
+	aspeed_p2a_disable_all(misc_ctrl);
+
+	misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
+	misc_ctrl->miscdev.name = DEVICE_NAME;
+	misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
+	misc_ctrl->miscdev.parent = dev;
+
+	rc = misc_register(&misc_ctrl->miscdev);
+	if (rc)
+		dev_err(dev, "Unable to register device\n");
+
+	return rc;
+}
+
+static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&p2a_ctrl->miscdev);
+
+	return 0;
+}
+
+#define SCU2C_DRAM	BIT(25)
+#define SCU2C_SPI	BIT(24)
+#define SCU2C_SOC	BIT(23)
+#define SCU2C_FLASH	BIT(22)
+
+static const struct aspeed_p2a_model_data ast2400_model_data = {
+	.regions = {
+		{0x00000000, 0x17FFFFFF, SCU2C_FLASH},
+		{0x18000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
+		{0x30000000, 0x3FFFFFFF, SCU2C_SPI},
+		{0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
+		{0x60000000, 0xFFFFFFFF, SCU2C_SOC},
+	}
+};
+
+static const struct aspeed_p2a_model_data ast2500_model_data = {
+	.regions = {
+		{0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
+		{0x10000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
+		{0x40000000, 0x5FFFFFFF, SCU2C_SOC},
+		{0x60000000, 0x7FFFFFFF, SCU2C_SPI},
+		{0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
+	}
+};
+
+static const struct of_device_id aspeed_p2a_ctrl_match[] = {
+	{ .compatible = "aspeed,ast2400-p2a-ctrl",
+	  .data = &ast2400_model_data },
+	{ .compatible = "aspeed,ast2500-p2a-ctrl",
+	  .data = &ast2500_model_data },
+	{ },
+};
+
+static struct platform_driver aspeed_p2a_ctrl_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_p2a_ctrl_match,
+	},
+	.probe = aspeed_p2a_ctrl_probe,
+	.remove = aspeed_p2a_ctrl_remove,
+};
+
+module_platform_driver(aspeed_p2a_ctrl_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick Venture <venture@google.com>");
+MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");
diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h b/include/uapi/linux/aspeed-p2a-ctrl.h
new file mode 100644
index 000000000000..c1d08f7bce6d
--- /dev/null
+++ b/include/uapi/linux/aspeed-p2a-ctrl.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
+#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ASPEED_P2A_CTRL_READ_ONLY 0
+#define ASPEED_P2A_CTRL_READWRITE 1
+
+/*
+ * This driver provides a mechanism for enabling or disabling the read-write
+ * property of specific windows into the ASPEED BMC's memory.
+ *
+ * A user can map a region of the BMC's memory as read-only or read-write, with
+ * the caveat that once any region is mapped, all regions are unlocked for
+ * reading.
+ */
+
+/*
+ * Unlock a region of BMC physical memory for access from the host.
+ *
+ * Also used to read back the optional memory-region configuration for the
+ * driver.
+ */
+struct aspeed_p2a_ctrl_mapping {
+	__u32 addr;
+	__u32 length;
+	__u32 flags;
+};
+
+#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
+
+/*
+ * This IOCTL is meant to configure a region or regions of memory given a
+ * starting address and length to be readable by the host, or
+ * readable-writeable.
+ */
+#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x00, struct aspeed_p2a_ctrl_mapping)
+
+/*
+ * This IOCTL is meant to read back to the user the base address and length of
+ * the memory-region specified to the driver for use with mmap.
+ */
+#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG \
+	_IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x01, struct aspeed_p2a_ctrl_mapping)
+
+#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
-- 
2.21.0.352.gf09ad66450-goog


^ permalink raw reply related

* [PATCH v6 1/2] dt-bindings: misc: aspeed-p2a-ctrl: add support
From: Patrick Venture @ 2019-03-04 18:55 UTC (permalink / raw)
  To: linux-aspeed

Document the ast2400, ast2500 PCI-to-AHB bridge control driver bindings.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v6:
- None
Changes for v5:
- None
Changes for v4:
- None
Changes for v3:
- None
Changes for v2:
- Added comment about syscon required parameter.
---
 .../bindings/misc/aspeed-p2a-ctrl.txt         | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt

diff --git a/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
new file mode 100644
index 000000000000..1092d62d1c92
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
@@ -0,0 +1,32 @@
+======================================================================
+Device tree bindings for Aspeed AST2400/AST2500 PCI-to-AHB Bridge Control Driver
+======================================================================
+
+The bridge is available on platforms with the VGA enabled on the Aspeed device.
+In this case, the host has access to a 64KiB window into all of the BMC's
+memory.  The BMC can disable this bridge.  If the bridge is enabled, the host
+has read access to all the regions of memory, however the host only has read
+and write access depending on a register controlled by the BMC.
+
+Required properties:
+===================
+
+ - compatible: must be one of:
+	- "aspeed,ast2400-p2a-ctrl"
+	- "aspeed,ast2500-p2a-ctrl"
+
+ - syscon: handle to syscon device node controlling PCI.
+
+Optional properties:
+===================
+
+- memory-region: A phandle to a reserved_memory region to be used for the PCI
+		to AHB mapping
+
+Example:
+
+p2a: p2a-control at 1e6e2000 {
+	compatible = "aspeed,ast2400-p2a-ctrl";
+	memory-region = <&reserved_memory>;
+	syscon = <&syscon>;
+};
-- 
2.21.0.352.gf09ad66450-goog


^ permalink raw reply related

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-04 18:31 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <CAO=notwGb2_HpcaMNRnaJfcjqoqWj1fws8A_OEH=kzH7PxJy0w@mail.gmail.com>

On Mon, Mar 4, 2019 at 7:45 AM Patrick Venture <venture@google.com> wrote:
>
> On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > Hi Patrick.
> >
> > I've got some minor comments, otherwise it looks reasonable to me.
> >
> > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > in the BMC's physical address space.  This feature is especially useful
> > > when using this bridge to send large files to the BMC.
> > >
> > > The host may use this to send down a firmware image by staging data at a
> > > specific memory address, and in a coordinated effort with the BMC's
> > > software stack and kernel, transmit the bytes.
> > >
> > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > configure it via ioctl to allow the host to write bytes to an agreed
> > > upon location.  In the primary use-case, the region to use is known
> > > apriori on the BMC, and the host requests this information.  Once this
> > > request is received, the BMC's software stack will enable the bridge and
> > > the region and then using some software flow control (possibly via IPMI
> > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > will disable the bridge and unset any region involved.
> > >
> > > The default behavior of this bridge when present is: enabled and all
> > > regions marked read-write.  This driver will fix the regions to be
> > > read-only and then disable the bridge entirely.
> > >
> > > The memory regions protected are:
> > >  * BMC flash MMIO window
> > >  * System flash MMIO windows
> > >  * SOC IO (peripheral MMIO)
> > >  * DRAM
> > >
> > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > the DRAM section is write-enabled, then it can write to all of it.
> > >
> > > Signed-off-by: Patrick Venture <venture@google.com>
> > > ---
> > > Changes for v5:
> > > - Fixup missing exit condition and remove extra jump.
> > > Changes for v4:
> > > - Added ioctl for reading back the memory-region configuration.
> > > - Cleaned up some unused variables.
> > > Changes for v3:
> > > - Deleted unused read and write methods.
> > > Changes for v2:
> > > - Dropped unused reads.
> > > - Moved call to disable bridge to before registering device.
> > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > - Updated the commit message. <<< TODO
> > > - Updated the bit flipped for SCU180_ENP2A
> > > - Dropped boolean region_specified variable.
> > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > - Updated commit message.
> > > ---
> > >  drivers/misc/Kconfig                 |   8 +
> > >  drivers/misc/Makefile                |   1 +
> > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > >  4 files changed, 524 insertions(+)
> > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > >
> > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > index f417b06e11c5..9de1bafe5606 100644
> > > --- a/drivers/misc/Kconfig
> > > +++ b/drivers/misc/Kconfig
> > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > >         bus. System Configuration interface is one of the possible means
> > >         of generating transactions on this bus.
> > >
> > > +config ASPEED_P2A_CTRL
> > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > +     help
> > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > through
> > > +       ioctl()s, the driver also provides an interface for userspace
> > > mappings to
> > > +       a pre-defined region.
> > > +
> > >  config ASPEED_LPC_CTRL
> > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > index e39ccbbc1b3a..57577aee354f 100644
> > > --- a/drivers/misc/Makefile
> > > +++ b/drivers/misc/Makefile
> > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > >  obj-$(CONFIG_OCXL)           += ocxl/
> > >  obj-y                                += cardreader/
> > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > new file mode 100644
> > > index 000000000000..6bde4f64632d
> > > --- /dev/null
> > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > @@ -0,0 +1,456 @@
> > > +/*
> > > + * Copyright 2019 Google Inc
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU General Public License
> > > + * as published by the Free Software Foundation; either version
> > > + * 2 of the License, or (at your option) any later version.
> >
> > Should be a SPDX header instead.
>
> Ok, so delete the above and drop in:

Was set straight on this.

>
> """
> /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> """
>
> Or just add that to the top above the Google GNU copyright line?  (I'm
> not a lawyer).
>
> >
> > > + *
> > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > allows
> > > + * the host to read and write to various regions of the BMC's memory.
> > > + */
> > > +
> > > +#include <linux/fs.h>
> > > +#include <linux/io.h>
> > > +#include <linux/mfd/syscon.h>
> > > +#include <linux/miscdevice.h>
> > > +#include <linux/mm.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/of_address.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/regmap.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/uaccess.h>
> > > +
> > > +#include <linux/aspeed-p2a-ctrl.h>
> > > +
> > > +#define DEVICE_NAME  "aspeed-p2a-ctrl"
> > > +
> > > +/* SCU2C is a Misc. Control Register. */
> > > +#define SCU2C 0x2c
> > > +/* SCU180 is the PCIe Configuration Setting Control Register. */
> > > +#define SCU180 0x180
> > > +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA
> > > device
> > > + * on the PCI bus. */
> >
> > As this wraps to multiple lines the trailing `*/` should be on a separate line.
> > There are a few instances of this throughout.
>
> Can do.  I will also run checkpath -- I had done it initially, but I
> forgot on later editions.
>
> >
> > > +#define SCU180_ENP2A BIT(1)
> > > +
> > > +/* The ast2400/2500 both have six ranges. */
> > > +#define P2A_REGION_COUNT 6
> > > +
> > > +struct region {
> > > +     u32 min;
> > > +     u32 max;
> > > +     u32 bit;
> > > +};
> > > +
> > > +struct aspeed_p2a_model_data {
> > > +     /* min, max, bit */
> > > +     struct region regions[P2A_REGION_COUNT];
> > > +};
> > > +
> > > +struct aspeed_p2a_ctrl {
> > > +     struct miscdevice miscdev;
> > > +     struct regmap *regmap;
> > > +
> > > +     const struct aspeed_p2a_model_data *config;
> > > +
> > > +     /* Access to these needs to be locked, held via probe, mapping ioctl,
> > > +      * and release, remove.
> > > +      */
> > > +     struct mutex tracking;
> > > +     u32 readers;
> > > +     u32 readerwriters[P2A_REGION_COUNT];
> > > +
> > > +     phys_addr_t mem_base;
> > > +     resource_size_t mem_size;
> > > +};
> > > +
> > > +struct aspeed_p2a_user {
> > > +     struct file *file;
> > > +     struct aspeed_p2a_ctrl *parent;
> > > +
> > > +     /** The entire memory space is opened for reading once the bridge is
> > > +      * enabled, therefore this needs only to be tracked once per user.
> > > +      * If any user has it open for read, the bridge must stay enabled.
> > > +      */
> >
> > Generally the descriptions for the members are described in a kerneldoc
> > comment above the struct definition. Also you're mixing the kernel-doc
> > comment opener (`/**`) with non-kernel-doc comments (`/*` on the
> > tracking` mutex in `struct aspeed_p2a_ctrl` above).
>
> Ok, so anything that isn't really detailing the members, or functions,
> shouldn't be kerneldoc style.  Will fix throughout.
>
> >
> > > +     u32 read;
> > > +
> > > +     /** Each entry of the array corresponds to a P2A Region.  If the user
> > > +      * opens for read or readwrite, the reference goes up here.  On
> > > release,
> > > +      * this array is walked and references adjusted accordingly.
> > > +      */
> > > +     u32 readwrite[P2A_REGION_COUNT];
> > > +};
> > > +
> > > +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > +{
> > > +     regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A,
> > > SCU180_ENP2A);
> >
> > Wrap this at 80 chars.
>
> Roger.
>
> >
> > > +}
> > > +
> > > +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > +{
> > > +     regmap_update_bits(p2witha_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> > > +}
> > > +
> > > +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct
> > > *vma)
> > > +{
> > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > +
> > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > +             return -EINVAL;
> > > +
> > > +     unsigned long vsize = vma->vm_end - vma->vm_start;
> > > +     pgprot_t prot = vma->vm_page_prot;
> > > +
> > > +     if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> > > +             return -EINVAL;
> > > +
> > > +     /* ast2400/2500 AHB accesses are not cache coherent */
> > > +     prot = pgprot_noncached(prot);
> > > +
> > > +     if (remap_pfn_range(vma, vma->vm_start,
> > > +             (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> > > +             vsize, prot))
> > > +             return -EAGAIN;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> > > +             struct aspeed_p2a_ctrl *ctrl,
> > > +             struct aspeed_p2a_ctrl_mapping *map)
> > > +{
> > > +     int i;
> > > +     u32 base, end;
> > > +
> > > +     base = map->addr;
> > > +     end = map->addr + (map->length - 1);
> > > +
> > > +     /* If the value is a legal u32, it will find a match. */
> > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > +             const struct region *curr = &ctrl->config->regions[i];
> > > +
> > > +             /* If the top of this region is lower than your base, skip it.
> > > +              */
> > > +             if (curr->max < base)
> > > +                     continue;
> > > +
> > > +             /* If the bottom of this region is higher than your end, bail.
> > > +              */
> > > +             if (curr->min > end)
> > > +                     break;
> > > +             /* Lock this and update it, therefore it someone else is
> > > +              * closing their file out, this'll preserve the increment.
> > > +              */
> > > +             mutex_lock(&ctrl->tracking);
> > > +             ctrl->readerwriters[i] += 1;
> > > +             mutex_unlock(&ctrl->tracking);
> > > +
> > > +             /* Track with the user, so when they close their file, we can
> > > +              * decrement properly.
> > > +              */
> >
> > The comments about tracking for decrement-on-release purposes is
> > useful, but I think the other comments in this loop are probably
> > unnecessary. Up to you though.
>
> I've often found drivers under-documented, so I went in the other direction.
>
> >
> > > +             priv->readwrite[i] += 1;
> > > +
> > > +             /* Enable the region as read-write. */
> > > +             regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> > > +     }
> > > +}
> > > +
> > > +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> > > +             unsigned long data)
> > > +{
> > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > > +     void __user *arg = (void __user *)data;
> > > +     struct aspeed_p2a_ctrl_mapping map;
> > > +
> > > +     if (copy_from_user(&map, arg, sizeof(map)))
> > > +             return -EFAULT;
> > > +
> > > +     switch (cmd) {
> > > +     case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> > > +             /* If they want a region to be read-only, since the entire
> > > +              * region is read-only once enabled, we just need to track this
> > > +              * user wants to read from the bridge, and if it's not enabled.
> > > +              * Enable it.
> > > +              */
> > > +             if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> > > +                     mutex_lock(&ctrl->tracking);
> > > +                     ctrl->readers += 1;
> > > +                     mutex_unlock(&ctrl->tracking);
> > > +
> > > +                     /* Track with the user, so when they close their file,
> > > +                      * we can decrement properly.
> > > +                      */
> > > +                     priv->read += 1;
> > > +             } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> > > +                     aspeed_p2a_region_acquire(priv, ctrl, &map);
> > > +             } else {
> > > +                     /* Invalid map flags. */
> > > +                     return -EINVAL;
> > > +             }
> > > +
> > > +             aspeed_p2a_enable_bridge(ctrl);
> > > +             return 0;
> > > +     case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> > > +             /* This is a request for the memory-region and corresponding
> > > +              * length that is used by the driver for mmap. */
> > > +
> > > +             map.flags = 0;
> > > +             map.addr = ctrl->mem_base;
> > > +             map.length = ctrl->mem_size;
> >
> > Thinking out loud here - do we want to allow ourselves an escape hatch
> > for supporting multiple reserved memory locations? Otherwise we might
> > need a new ioctl() for it. On the flip-side, not actually having this use-case
> > means we might break the implementation anyway, so it could be a
> > double-edged sword. Thoughts?
>
> How about this, I use a different structure that has an index -- so
> you pass in the structure with the index set, requesting the region
> information, and it returns the details for that memory-region until
> the index exceeds the number of regions and returns -ENODEV.
>
> However, to avoid extra complexity today, the dts will only support
> one memory region...  The complexity of dealing with checking what
> region they want to use in mmap by checking what region has been
> enabled by that specific user may be too much of a pain though for the
> probable life of this driver.  So yeah, a bit of a double-edged sword
> of complexity to approach the extra complexity even partially.  I
> think having one memory-region is fine for now, and I can't see the
> future.  Perhaps having the ioctl structure change and no other
> changes is sufficient while being extendable in the future if
> someone's need should arise.  That way there won't be an obsolete
> ioctl or ambiguous ioctl (code that calls the old one and its result
> is quasi-incorrect).
>
> >
> > > +
> > > +             return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> > > +     }
> > > +
> > > +     return -EINVAL;
> > > +}
> > > +
> > > +
> > > +/**
> > > + * When a user opens this file, we create a structure to track their
> > > mappings.
> > > + *
> > > + * A user can map a region as read-only (bridge enabled), or
> > > read-write (bit
> > > + * flipped, and bridge enabled).  Either way, this tracking is used,
> > > s.t. when
> > > + * they release the device references are handled.
> > > + *
> > > + * The bridge is not enabled until a user calls an ioctl to map a
> > > region,
> > > + * simply opening the device does not enable it.
> > > + */
> > > +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> > > +{
> > > +     struct aspeed_p2a_user *priv;
> > > +
> > > +     priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> > > +     if (!priv)
> > > +             return -ENOMEM;
> > > +
> > > +     priv->file = file;
> > > +     priv->read = 0;
> > > +     memset(priv->readwrite, 0, sizeof(priv->readwrite));
> > > +
> > > +     /* The file's private_data is initialized to the p2a_ctrl. */
> > > +     priv->parent = file->private_data;
> > > +
> > > +     /* Set the file's private_data to the user's data. */
> > > +     file->private_data = priv;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +/**
> > > + * This will close the users mappings.  It will go through what they
> > > had opened
> > > + * for readwrite, and decrement those counts.  If at the end, this is
> > > the last
> > > + * user, it'll close the bridge.
> > > + */
> > > +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> > > +{
> > > +     int i;
> > > +     u32 value;
> > > +     u32 bits = 0;
> > > +     bool open_regions = false;
> > > +     struct aspeed_p2a_user *priv = file->private_data;
> > > +
> > > +     /* Lock others from changing these values until everything is updated
> > > +      * in one pass */
> > > +     mutex_lock(&priv->parent->tracking);
> > > +
> > > +     priv->parent->readers -= priv->read;;
> > > +
> > > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > > +             priv->parent->readerwriters[i] -= priv->readwrite[i];
> > > +
> > > +             if (priv->parent->readerwriters[i] > 0)
> > > +                     open_regions = true;
> > > +             else
> > > +                     bits |= priv->parent->config->regions[i].bit;
> > > +     }
> > > +
> > > +     /* Setting a bit to 1 disables the region, so let's just OR with the
> > > +      * above to disable any.
> > > +      */
> > > +
> > > +     /* Note, if another user is trying to ioctl, they can't grab tracking,
> > > +      * and therefore can't grab either register mutex.
> > > +      * If another user is trying to close, they can't grab tracking
> > > either.
> > > +      */
> > > +     regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> > > +
> > > +     /* If parent->readers is zero and open windows is 0, disable the
> > > +      * bridge.
> > > +      */
> > > +     if (!open_regions && priv->parent->readers == 0)
> > > +             aspeed_p2a_disable_bridge(priv->parent);
> > > +
> > > +     mutex_unlock(&priv->parent->tracking);
> > > +
> > > +     kfree(priv);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static const struct file_operations aspeed_p2a_ctrl_fops = {
> > > +     .owner = THIS_MODULE,
> > > +     .mmap = aspeed_p2a_mmap,
> > > +     .unlocked_ioctl = aspeed_p2a_ioctl,
> > > +     .open = aspeed_p2a_open,
> > > +     .release = aspeed_p2a_release,
> > > +};
> > > +
> > > +/** The regions are controlled by SCU2C */
> > > +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> > > +{
> > > +     int i;
> > > +     u32 value = 0;
> > > +
> > > +     for (i = 0; i < P2A_REGION_COUNT; i++)
> > > +             value |= p2a_ctrl->config->regions[i].bit;
> > > +
> > > +     regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> > > +
> > > +     /* Disable the bridge. */
> > > +     aspeed_p2a_disable_bridge(p2a_ctrl);
> > > +}
> > > +
> > > +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> > > +{
> > > +     struct aspeed_p2a_ctrl *misc_ctrl;
> > > +     struct device *dev;
> > > +     struct resource *res, resm;
> > > +     struct device_node *node;
> > > +     int rc = 0;
> > > +
> > > +     dev = &pdev->dev;
> > > +
> > > +     misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> > > +     if (!misc_ctrl)
> > > +             return -ENOMEM;
> > > +
> > > +     mutex_init(&misc_ctrl->tracking);
> > > +     misc_ctrl->readers = 0;
> > > +     memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> > > +
> > > +     misc_ctrl->mem_size = 0;
> > > +     misc_ctrl->mem_base = 0;
> > > +
> > > +     /* optional. */
> > > +     node = of_parse_phandle(dev->of_node, "memory-region", 0);
> > > +     if (node) {
> > > +             rc = of_address_to_resource(node, 0, &resm);
> > > +             of_node_put(node);
> > > +             if (rc) {
> > > +                     dev_err(dev, "Couldn't address to resource for reserved memory\n");
> >
> > I think the message could be improved, something like "No reserved memory
> > specified". Having said that, I don't think it should be an error condition either;
> > our experience with aspeed-lpc-ctrl was that it was useful for the memory-region
> > property to be optional. You already have:
>
> Ok.

Ok, when I read this, I see it as an error condition.  In this case
the node was specified but was invalid.

>
> >
> > > +
> > > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > > +             return -EINVAL;
> >
> > in the mmap() callback, but we don't get that far unless someone has specified a
> > zero-sized reserved-memory node. I think supporting memory-region as optional
> > is just a matter of adding the same check to the GET_MEMORY_CONFIG ioctl().
> >
> > > +                     return -ENOMEM;
> >
> > The system isn't out of memory so this isn't an ENOMEM condition. I think ENODEV
> > is more appropriate, but if we make the memory region optional then this goes
> > away anyway.
>
> Roger.
>
> >
> > > +             }
> > > +
> > > +             misc_ctrl->mem_size = resource_size(&resm);
> > > +             misc_ctrl->mem_base = resm.start;
> > > +     }
> > > +
> > > +     node = of_parse_phandle(dev->of_node, "syscon", 0);
> > > +     if (!node) {
> > > +             dev_err(dev, "Couldn't find syscon property\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     misc_ctrl->regmap = syscon_node_to_regmap(node);
> > > +     if (IS_ERR(misc_ctrl->regmap)) {
> > > +             dev_err(dev, "Couldn't get regmap\n");
> > > +             return -ENODEV;
> > > +     }
> > > +     of_node_put(node);
> > > +
> > > +     misc_ctrl->config = of_device_get_match_data(dev);
> > > +
> > > +     dev_set_drvdata(&pdev->dev, misc_ctrl);
> > > +
> > > +     aspeed_p2a_disable_all(misc_ctrl);
> > > +
> > > +     misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> > > +     misc_ctrl->miscdev.name = DEVICE_NAME;
> > > +     misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> > > +     misc_ctrl->miscdev.parent = dev;
> > > +
> > > +     rc = misc_register(&misc_ctrl->miscdev);
> > > +     if (rc)
> > > +             dev_err(dev, "Unable to register device\n");
> > > +
> > > +     return rc;
> > > +}
> > > +
> > > +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> > > +{
> > > +     struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> > > +
> > > +     misc_deregister(&p2a_ctrl->miscdev);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +/*
> > > + * bit | SCU2C | ast2400
> > > + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> > > + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> > > + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> > > + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> > > + *
> > > + * bit | SCU2C | ast2500
> > > + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> > > + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> > > + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> > > + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> > > + */
> >
> > The comment is probably unnecessary given the structure declarations
> > below.
>
> Fair enough, will drop it.
>
> >
> > > +
> > > +#define SCU2C_DRAM   BIT(25)
> > > +#define SCU2C_SPI    BIT(24)
> > > +#define SCU2C_SOC    BIT(23)
> > > +#define SCU2C_FLASH  BIT(22)
> > > +
> > > +static const struct aspeed_p2a_model_data ast2400_model_data = {
> > > +     .regions = {
> > > +             {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> > > +             {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> > > +             {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> > > +             {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> > > +             {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> > > +             {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> > > +     }
> > > +};
> > > +
> > > +static const struct aspeed_p2a_model_data ast2500_model_data = {
> > > +     .regions = {
> > > +             {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> > > +             {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> > > +             {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> > > +             {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> > > +             {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> > > +             {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> > > +     }
> > > +};
> > > +
> > > +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> > > +     { .compatible = "aspeed,ast2400-p2a-ctrl",
> > > +       .data = &ast2400_model_data },
> > > +     { .compatible = "aspeed,ast2500-p2a-ctrl",
> > > +       .data = &ast2500_model_data },
> > > +     { },
> > > +};
> > > +
> > > +static struct platform_driver aspeed_p2a_ctrl_driver = {
> > > +     .driver = {
> > > +             .name           = DEVICE_NAME,
> > > +             .of_match_table = aspeed_p2a_ctrl_match,
> > > +     },
> > > +     .probe = aspeed_p2a_ctrl_probe,
> > > +     .remove = aspeed_p2a_ctrl_remove,
> > > +};
> > > +
> > > +module_platform_driver(aspeed_p2a_ctrl_driver);
> > > +
> > > +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> > > +MODULE_LICENSE("GPL");
> > > +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> > > +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC
> > > mappings");
> > > diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h
> > > b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > new file mode 100644
> > > index 000000000000..e839cdc31db9
> > > --- /dev/null
> > > +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> > > @@ -0,0 +1,59 @@
> > > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > > +/*
> > > + * Copyright 2019 Google Inc
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU General Public License
> > > + * as published by the Free Software Foundation; either version
> > > + * 2 of the License, or (at your option) any later version.
> >
> > SPDX again.
>
> So, only SPDX?  I'm not super on the ball with this type of thing.  I
> just mirrored what I saw in other recent aspeed drivers.
>
> >
> > > + *
> > > + * Provides a simple driver to control the ASPEED P2A interface which
> > > allows
> > > + * the host to read and write to various regions of the BMC's memory.
> > > + */
> > > +
> > > +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > > +
> > > +#include <linux/ioctl.h>
> > > +#include <linux/types.h>
> > > +
> > > +#define ASPEED_P2A_CTRL_READ_ONLY 0
> > > +#define ASPEED_P2A_CTRL_READWRITE 1
> > > +
> > > +/*
> > > + * This driver provides a mechanism for enabling or disabling the
> > > read-write
> > > + * property of specific windows into the ASPEED BMC's memory.
> > > + *
> > > + * A user can map a region of the BMC's memory as read-only or
> > > read-write, with
> > > + * the caveat that once any region is mapped, all regions are unlocked
> > > for
> > > + * reading.
> > > + */
> > > +
> > > +/**
> > > + * Unlock a region of BMC physical memory for access from the host.
> > > + *
> > > + * Also used to read back the optional memory-region configuration for
> > > the
> > > + * driver.
> > > + */
> > > +struct aspeed_p2a_ctrl_mapping {
> > > +     __u32 addr;
> > > +     __u32 length;
> > > +     __u32 flags;
> > > +};
> > > +
> > > +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> > > +
> > > +/**
> > > + * This IOCTL is meant to configure a region or regions of memory
> > > given a
> > > + * starting address and length to be readable by the host, or
> > > + * readable-writeable. */
> > > +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW
> > > _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > > +             0x00, struct aspeed_p2a_ctrl_mapping)
> > > +
> > > +/**
> > > + * This IOCTL is meant to read back to the user the base address and
> > > length of
> > > + * the memory-region specified to the driver for use with mmap. */
> > > +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG
> > > _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> >
> > Wrap this at 80?
> >
> > Cheers,
> >
> > Andrew
> >
> > > +             0x01, struct aspeed_p2a_ctrl_mapping)
> > > +
> > > +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> > > --
> > > 2.21.0.rc2.261.ga7da99ff1b-goog
> > >
> > >

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-04 16:35 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <CAO=notyD9+FhER1i2aGx099VPtXWpRVxNK-pzKiFjRdgCKSpjw@mail.gmail.com>

On Mon, Mar 4, 2019 at 8:31 AM Patrick Venture <venture@google.com> wrote:
>
> On Mon, Mar 4, 2019 at 8:31 AM Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> >
> > On Mon, Mar 04, 2019 at 07:45:31AM -0800, Patrick Venture wrote:
> > > On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > >
> > > > Hi Patrick.
> > > >
> > > > I've got some minor comments, otherwise it looks reasonable to me.
> > > >
> > > > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > > > in the BMC's physical address space.  This feature is especially useful
> > > > > when using this bridge to send large files to the BMC.
> > > > >
> > > > > The host may use this to send down a firmware image by staging data at a
> > > > > specific memory address, and in a coordinated effort with the BMC's
> > > > > software stack and kernel, transmit the bytes.
> > > > >
> > > > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > > > configure it via ioctl to allow the host to write bytes to an agreed
> > > > > upon location.  In the primary use-case, the region to use is known
> > > > > apriori on the BMC, and the host requests this information.  Once this
> > > > > request is received, the BMC's software stack will enable the bridge and
> > > > > the region and then using some software flow control (possibly via IPMI
> > > > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > > > will disable the bridge and unset any region involved.
> > > > >
> > > > > The default behavior of this bridge when present is: enabled and all
> > > > > regions marked read-write.  This driver will fix the regions to be
> > > > > read-only and then disable the bridge entirely.
> > > > >
> > > > > The memory regions protected are:
> > > > >  * BMC flash MMIO window
> > > > >  * System flash MMIO windows
> > > > >  * SOC IO (peripheral MMIO)
> > > > >  * DRAM
> > > > >
> > > > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > > > the DRAM section is write-enabled, then it can write to all of it.
> > > > >
> > > > > Signed-off-by: Patrick Venture <venture@google.com>
> > > > > ---
> > > > > Changes for v5:
> > > > > - Fixup missing exit condition and remove extra jump.
> > > > > Changes for v4:
> > > > > - Added ioctl for reading back the memory-region configuration.
> > > > > - Cleaned up some unused variables.
> > > > > Changes for v3:
> > > > > - Deleted unused read and write methods.
> > > > > Changes for v2:
> > > > > - Dropped unused reads.
> > > > > - Moved call to disable bridge to before registering device.
> > > > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > > > - Updated the commit message. <<< TODO
> > > > > - Updated the bit flipped for SCU180_ENP2A
> > > > > - Dropped boolean region_specified variable.
> > > > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > > > - Updated commit message.
> > > > > ---
> > > > >  drivers/misc/Kconfig                 |   8 +
> > > > >  drivers/misc/Makefile                |   1 +
> > > > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > > > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > > > >  4 files changed, 524 insertions(+)
> > > > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > > > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > > > >
> > > > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > > > index f417b06e11c5..9de1bafe5606 100644
> > > > > --- a/drivers/misc/Kconfig
> > > > > +++ b/drivers/misc/Kconfig
> > > > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > > > >         bus. System Configuration interface is one of the possible means
> > > > >         of generating transactions on this bus.
> > > > >
> > > > > +config ASPEED_P2A_CTRL
> > > > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > > > +     help
> > > > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > > > through
> > > > > +       ioctl()s, the driver also provides an interface for userspace
> > > > > mappings to
> > > > > +       a pre-defined region.
> > > > > +
> > > > >  config ASPEED_LPC_CTRL
> > > > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > > > index e39ccbbc1b3a..57577aee354f 100644
> > > > > --- a/drivers/misc/Makefile
> > > > > +++ b/drivers/misc/Makefile
> > > > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > > > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > > > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > > > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > > > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > > > >  obj-$(CONFIG_OCXL)           += ocxl/
> > > > >  obj-y                                += cardreader/
> > > > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > > > new file mode 100644
> > > > > index 000000000000..6bde4f64632d
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > > > @@ -0,0 +1,456 @@
> > > > > +/*
> > > > > + * Copyright 2019 Google Inc
> > > > > + *
> > > > > + * This program is free software; you can redistribute it and/or
> > > > > + * modify it under the terms of the GNU General Public License
> > > > > + * as published by the Free Software Foundation; either version
> > > > > + * 2 of the License, or (at your option) any later version.
> > > >
> > > > Should be a SPDX header instead.
> > >
> > > Ok, so delete the above and drop in:
> > >
> > > """
> > > /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > > """
> >
> > No no no no no no!
> >
> > > Or just add that to the top above the Google GNU copyright line?  (I'm
> > > not a lawyer).
> >
> > Please go read Documentation/process/license-rules.txt, it should
> > explain everything.  And if not, go talk to your legal council, they
> > know all about this and what is needed.
>
> Roger that.

Quick and easy read.  Thanks.

>
> >
> > thanks,
> >
> > greg k-h

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-04 16:31 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20190304163116.GC2301@kroah.com>

On Mon, Mar 4, 2019 at 8:31 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> On Mon, Mar 04, 2019 at 07:45:31AM -0800, Patrick Venture wrote:
> > On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> > >
> > > Hi Patrick.
> > >
> > > I've got some minor comments, otherwise it looks reasonable to me.
> > >
> > > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > > in the BMC's physical address space.  This feature is especially useful
> > > > when using this bridge to send large files to the BMC.
> > > >
> > > > The host may use this to send down a firmware image by staging data at a
> > > > specific memory address, and in a coordinated effort with the BMC's
> > > > software stack and kernel, transmit the bytes.
> > > >
> > > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > > configure it via ioctl to allow the host to write bytes to an agreed
> > > > upon location.  In the primary use-case, the region to use is known
> > > > apriori on the BMC, and the host requests this information.  Once this
> > > > request is received, the BMC's software stack will enable the bridge and
> > > > the region and then using some software flow control (possibly via IPMI
> > > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > > will disable the bridge and unset any region involved.
> > > >
> > > > The default behavior of this bridge when present is: enabled and all
> > > > regions marked read-write.  This driver will fix the regions to be
> > > > read-only and then disable the bridge entirely.
> > > >
> > > > The memory regions protected are:
> > > >  * BMC flash MMIO window
> > > >  * System flash MMIO windows
> > > >  * SOC IO (peripheral MMIO)
> > > >  * DRAM
> > > >
> > > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > > the DRAM section is write-enabled, then it can write to all of it.
> > > >
> > > > Signed-off-by: Patrick Venture <venture@google.com>
> > > > ---
> > > > Changes for v5:
> > > > - Fixup missing exit condition and remove extra jump.
> > > > Changes for v4:
> > > > - Added ioctl for reading back the memory-region configuration.
> > > > - Cleaned up some unused variables.
> > > > Changes for v3:
> > > > - Deleted unused read and write methods.
> > > > Changes for v2:
> > > > - Dropped unused reads.
> > > > - Moved call to disable bridge to before registering device.
> > > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > > - Updated the commit message. <<< TODO
> > > > - Updated the bit flipped for SCU180_ENP2A
> > > > - Dropped boolean region_specified variable.
> > > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > > - Updated commit message.
> > > > ---
> > > >  drivers/misc/Kconfig                 |   8 +
> > > >  drivers/misc/Makefile                |   1 +
> > > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > > >  4 files changed, 524 insertions(+)
> > > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > > >
> > > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > > index f417b06e11c5..9de1bafe5606 100644
> > > > --- a/drivers/misc/Kconfig
> > > > +++ b/drivers/misc/Kconfig
> > > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > > >         bus. System Configuration interface is one of the possible means
> > > >         of generating transactions on this bus.
> > > >
> > > > +config ASPEED_P2A_CTRL
> > > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > > +     help
> > > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > > through
> > > > +       ioctl()s, the driver also provides an interface for userspace
> > > > mappings to
> > > > +       a pre-defined region.
> > > > +
> > > >  config ASPEED_LPC_CTRL
> > > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > > index e39ccbbc1b3a..57577aee354f 100644
> > > > --- a/drivers/misc/Makefile
> > > > +++ b/drivers/misc/Makefile
> > > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > > >  obj-$(CONFIG_OCXL)           += ocxl/
> > > >  obj-y                                += cardreader/
> > > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > > new file mode 100644
> > > > index 000000000000..6bde4f64632d
> > > > --- /dev/null
> > > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > > @@ -0,0 +1,456 @@
> > > > +/*
> > > > + * Copyright 2019 Google Inc
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU General Public License
> > > > + * as published by the Free Software Foundation; either version
> > > > + * 2 of the License, or (at your option) any later version.
> > >
> > > Should be a SPDX header instead.
> >
> > Ok, so delete the above and drop in:
> >
> > """
> > /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > """
>
> No no no no no no!
>
> > Or just add that to the top above the Google GNU copyright line?  (I'm
> > not a lawyer).
>
> Please go read Documentation/process/license-rules.txt, it should
> explain everything.  And if not, go talk to your legal council, they
> know all about this and what is needed.

Roger that.

>
> thanks,
>
> greg k-h

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Greg Kroah-Hartman @ 2019-03-04 16:31 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <CAO=notwGb2_HpcaMNRnaJfcjqoqWj1fws8A_OEH=kzH7PxJy0w@mail.gmail.com>

On Mon, Mar 04, 2019 at 07:45:31AM -0800, Patrick Venture wrote:
> On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > Hi Patrick.
> >
> > I've got some minor comments, otherwise it looks reasonable to me.
> >
> > On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > > The ASPEED AST2400, and AST2500 in some configurations include a
> > > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > > in the BMC's physical address space.  This feature is especially useful
> > > when using this bridge to send large files to the BMC.
> > >
> > > The host may use this to send down a firmware image by staging data at a
> > > specific memory address, and in a coordinated effort with the BMC's
> > > software stack and kernel, transmit the bytes.
> > >
> > > This driver enables the BMC to unlock the PCI bridge on demand, and
> > > configure it via ioctl to allow the host to write bytes to an agreed
> > > upon location.  In the primary use-case, the region to use is known
> > > apriori on the BMC, and the host requests this information.  Once this
> > > request is received, the BMC's software stack will enable the bridge and
> > > the region and then using some software flow control (possibly via IPMI
> > > packets), copy the bytes down.  Once the process is complete, the BMC
> > > will disable the bridge and unset any region involved.
> > >
> > > The default behavior of this bridge when present is: enabled and all
> > > regions marked read-write.  This driver will fix the regions to be
> > > read-only and then disable the bridge entirely.
> > >
> > > The memory regions protected are:
> > >  * BMC flash MMIO window
> > >  * System flash MMIO windows
> > >  * SOC IO (peripheral MMIO)
> > >  * DRAM
> > >
> > > The DRAM region itself is all of DRAM and cannot be further specified.
> > > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > > the DRAM section is write-enabled, then it can write to all of it.
> > >
> > > Signed-off-by: Patrick Venture <venture@google.com>
> > > ---
> > > Changes for v5:
> > > - Fixup missing exit condition and remove extra jump.
> > > Changes for v4:
> > > - Added ioctl for reading back the memory-region configuration.
> > > - Cleaned up some unused variables.
> > > Changes for v3:
> > > - Deleted unused read and write methods.
> > > Changes for v2:
> > > - Dropped unused reads.
> > > - Moved call to disable bridge to before registering device.
> > > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > > - Updated the commit message. <<< TODO
> > > - Updated the bit flipped for SCU180_ENP2A
> > > - Dropped boolean region_specified variable.
> > > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > > - Updated commit message.
> > > ---
> > >  drivers/misc/Kconfig                 |   8 +
> > >  drivers/misc/Makefile                |   1 +
> > >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> > >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> > >  4 files changed, 524 insertions(+)
> > >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> > >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> > >
> > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > index f417b06e11c5..9de1bafe5606 100644
> > > --- a/drivers/misc/Kconfig
> > > +++ b/drivers/misc/Kconfig
> > > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> > >         bus. System Configuration interface is one of the possible means
> > >         of generating transactions on this bus.
> > >
> > > +config ASPEED_P2A_CTRL
> > > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > > +     help
> > > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > > through
> > > +       ioctl()s, the driver also provides an interface for userspace
> > > mappings to
> > > +       a pre-defined region.
> > > +
> > >  config ASPEED_LPC_CTRL
> > >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > index e39ccbbc1b3a..57577aee354f 100644
> > > --- a/drivers/misc/Makefile
> > > +++ b/drivers/misc/Makefile
> > > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> > >  obj-$(CONFIG_CXL_BASE)               += cxl/
> > >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> > >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> > >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> > >  obj-$(CONFIG_OCXL)           += ocxl/
> > >  obj-y                                += cardreader/
> > > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > > b/drivers/misc/aspeed-p2a-ctrl.c
> > > new file mode 100644
> > > index 000000000000..6bde4f64632d
> > > --- /dev/null
> > > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > > @@ -0,0 +1,456 @@
> > > +/*
> > > + * Copyright 2019 Google Inc
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU General Public License
> > > + * as published by the Free Software Foundation; either version
> > > + * 2 of the License, or (at your option) any later version.
> >
> > Should be a SPDX header instead.
> 
> Ok, so delete the above and drop in:
> 
> """
> /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> """

No no no no no no!

> Or just add that to the top above the Google GNU copyright line?  (I'm
> not a lawyer).

Please go read Documentation/process/license-rules.txt, it should
explain everything.  And if not, go talk to your legal council, they
know all about this and what is needed.

thanks,

greg k-h

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-03-04 15:45 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <19097a78-7066-4fcd-aa79-aca4a8c3cb0c@www.fastmail.com>

On Sun, Mar 3, 2019 at 4:04 PM Andrew Jeffery <andrew@aj.id.au> wrote:
>
> Hi Patrick.
>
> I've got some minor comments, otherwise it looks reasonable to me.
>
> On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> > The ASPEED AST2400, and AST2500 in some configurations include a
> > PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> > in the BMC's physical address space.  This feature is especially useful
> > when using this bridge to send large files to the BMC.
> >
> > The host may use this to send down a firmware image by staging data at a
> > specific memory address, and in a coordinated effort with the BMC's
> > software stack and kernel, transmit the bytes.
> >
> > This driver enables the BMC to unlock the PCI bridge on demand, and
> > configure it via ioctl to allow the host to write bytes to an agreed
> > upon location.  In the primary use-case, the region to use is known
> > apriori on the BMC, and the host requests this information.  Once this
> > request is received, the BMC's software stack will enable the bridge and
> > the region and then using some software flow control (possibly via IPMI
> > packets), copy the bytes down.  Once the process is complete, the BMC
> > will disable the bridge and unset any region involved.
> >
> > The default behavior of this bridge when present is: enabled and all
> > regions marked read-write.  This driver will fix the regions to be
> > read-only and then disable the bridge entirely.
> >
> > The memory regions protected are:
> >  * BMC flash MMIO window
> >  * System flash MMIO windows
> >  * SOC IO (peripheral MMIO)
> >  * DRAM
> >
> > The DRAM region itself is all of DRAM and cannot be further specified.
> > Once the PCI bridge is enabled, the host can read all of DRAM, and if
> > the DRAM section is write-enabled, then it can write to all of it.
> >
> > Signed-off-by: Patrick Venture <venture@google.com>
> > ---
> > Changes for v5:
> > - Fixup missing exit condition and remove extra jump.
> > Changes for v4:
> > - Added ioctl for reading back the memory-region configuration.
> > - Cleaned up some unused variables.
> > Changes for v3:
> > - Deleted unused read and write methods.
> > Changes for v2:
> > - Dropped unused reads.
> > - Moved call to disable bridge to before registering device.
> > - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> > - Updated the commit message. <<< TODO
> > - Updated the bit flipped for SCU180_ENP2A
> > - Dropped boolean region_specified variable.
> > - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> > - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> > - Updated commit message.
> > ---
> >  drivers/misc/Kconfig                 |   8 +
> >  drivers/misc/Makefile                |   1 +
> >  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
> >  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
> >  4 files changed, 524 insertions(+)
> >  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
> >  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> >
> > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > index f417b06e11c5..9de1bafe5606 100644
> > --- a/drivers/misc/Kconfig
> > +++ b/drivers/misc/Kconfig
> > @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
> >         bus. System Configuration interface is one of the possible means
> >         of generating transactions on this bus.
> >
> > +config ASPEED_P2A_CTRL
> > +     depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> > +     tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> > +     help
> > +       Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings
> > through
> > +       ioctl()s, the driver also provides an interface for userspace
> > mappings to
> > +       a pre-defined region.
> > +
> >  config ASPEED_LPC_CTRL
> >       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> >       tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > index e39ccbbc1b3a..57577aee354f 100644
> > --- a/drivers/misc/Makefile
> > +++ b/drivers/misc/Makefile
> > @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)       += vexpress-syscfg.o
> >  obj-$(CONFIG_CXL_BASE)               += cxl/
> >  obj-$(CONFIG_ASPEED_LPC_CTRL)        += aspeed-lpc-ctrl.o
> >  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
> > +obj-$(CONFIG_ASPEED_P2A_CTRL)        += aspeed-p2a-ctrl.o
> >  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
> >  obj-$(CONFIG_OCXL)           += ocxl/
> >  obj-y                                += cardreader/
> > diff --git a/drivers/misc/aspeed-p2a-ctrl.c
> > b/drivers/misc/aspeed-p2a-ctrl.c
> > new file mode 100644
> > index 000000000000..6bde4f64632d
> > --- /dev/null
> > +++ b/drivers/misc/aspeed-p2a-ctrl.c
> > @@ -0,0 +1,456 @@
> > +/*
> > + * Copyright 2019 Google Inc
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version
> > + * 2 of the License, or (at your option) any later version.
>
> Should be a SPDX header instead.

Ok, so delete the above and drop in:

"""
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
"""

Or just add that to the top above the Google GNU copyright line?  (I'm
not a lawyer).

>
> > + *
> > + * Provides a simple driver to control the ASPEED P2A interface which
> > allows
> > + * the host to read and write to various regions of the BMC's memory.
> > + */
> > +
> > +#include <linux/fs.h>
> > +#include <linux/io.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/miscdevice.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +#include <linux/uaccess.h>
> > +
> > +#include <linux/aspeed-p2a-ctrl.h>
> > +
> > +#define DEVICE_NAME  "aspeed-p2a-ctrl"
> > +
> > +/* SCU2C is a Misc. Control Register. */
> > +#define SCU2C 0x2c
> > +/* SCU180 is the PCIe Configuration Setting Control Register. */
> > +#define SCU180 0x180
> > +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA
> > device
> > + * on the PCI bus. */
>
> As this wraps to multiple lines the trailing `*/` should be on a separate line.
> There are a few instances of this throughout.

Can do.  I will also run checkpath -- I had done it initially, but I
forgot on later editions.

>
> > +#define SCU180_ENP2A BIT(1)
> > +
> > +/* The ast2400/2500 both have six ranges. */
> > +#define P2A_REGION_COUNT 6
> > +
> > +struct region {
> > +     u32 min;
> > +     u32 max;
> > +     u32 bit;
> > +};
> > +
> > +struct aspeed_p2a_model_data {
> > +     /* min, max, bit */
> > +     struct region regions[P2A_REGION_COUNT];
> > +};
> > +
> > +struct aspeed_p2a_ctrl {
> > +     struct miscdevice miscdev;
> > +     struct regmap *regmap;
> > +
> > +     const struct aspeed_p2a_model_data *config;
> > +
> > +     /* Access to these needs to be locked, held via probe, mapping ioctl,
> > +      * and release, remove.
> > +      */
> > +     struct mutex tracking;
> > +     u32 readers;
> > +     u32 readerwriters[P2A_REGION_COUNT];
> > +
> > +     phys_addr_t mem_base;
> > +     resource_size_t mem_size;
> > +};
> > +
> > +struct aspeed_p2a_user {
> > +     struct file *file;
> > +     struct aspeed_p2a_ctrl *parent;
> > +
> > +     /** The entire memory space is opened for reading once the bridge is
> > +      * enabled, therefore this needs only to be tracked once per user.
> > +      * If any user has it open for read, the bridge must stay enabled.
> > +      */
>
> Generally the descriptions for the members are described in a kerneldoc
> comment above the struct definition. Also you're mixing the kernel-doc
> comment opener (`/**`) with non-kernel-doc comments (`/*` on the
> tracking` mutex in `struct aspeed_p2a_ctrl` above).

Ok, so anything that isn't really detailing the members, or functions,
shouldn't be kerneldoc style.  Will fix throughout.

>
> > +     u32 read;
> > +
> > +     /** Each entry of the array corresponds to a P2A Region.  If the user
> > +      * opens for read or readwrite, the reference goes up here.  On
> > release,
> > +      * this array is walked and references adjusted accordingly.
> > +      */
> > +     u32 readwrite[P2A_REGION_COUNT];
> > +};
> > +
> > +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > +{
> > +     regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A,
> > SCU180_ENP2A);
>
> Wrap this at 80 chars.

Roger.

>
> > +}
> > +
> > +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> > +{
> > +     regmap_update_bits(p2witha_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> > +}
> > +
> > +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct
> > *vma)
> > +{
> > +     struct aspeed_p2a_user *priv = file->private_data;
> > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > +
> > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > +             return -EINVAL;
> > +
> > +     unsigned long vsize = vma->vm_end - vma->vm_start;
> > +     pgprot_t prot = vma->vm_page_prot;
> > +
> > +     if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> > +             return -EINVAL;
> > +
> > +     /* ast2400/2500 AHB accesses are not cache coherent */
> > +     prot = pgprot_noncached(prot);
> > +
> > +     if (remap_pfn_range(vma, vma->vm_start,
> > +             (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> > +             vsize, prot))
> > +             return -EAGAIN;
> > +
> > +     return 0;
> > +}
> > +
> > +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> > +             struct aspeed_p2a_ctrl *ctrl,
> > +             struct aspeed_p2a_ctrl_mapping *map)
> > +{
> > +     int i;
> > +     u32 base, end;
> > +
> > +     base = map->addr;
> > +     end = map->addr + (map->length - 1);
> > +
> > +     /* If the value is a legal u32, it will find a match. */
> > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > +             const struct region *curr = &ctrl->config->regions[i];
> > +
> > +             /* If the top of this region is lower than your base, skip it.
> > +              */
> > +             if (curr->max < base)
> > +                     continue;
> > +
> > +             /* If the bottom of this region is higher than your end, bail.
> > +              */
> > +             if (curr->min > end)
> > +                     break;
> > +             /* Lock this and update it, therefore it someone else is
> > +              * closing their file out, this'll preserve the increment.
> > +              */
> > +             mutex_lock(&ctrl->tracking);
> > +             ctrl->readerwriters[i] += 1;
> > +             mutex_unlock(&ctrl->tracking);
> > +
> > +             /* Track with the user, so when they close their file, we can
> > +              * decrement properly.
> > +              */
>
> The comments about tracking for decrement-on-release purposes is
> useful, but I think the other comments in this loop are probably
> unnecessary. Up to you though.

I've often found drivers under-documented, so I went in the other direction.

>
> > +             priv->readwrite[i] += 1;
> > +
> > +             /* Enable the region as read-write. */
> > +             regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> > +     }
> > +}
> > +
> > +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> > +             unsigned long data)
> > +{
> > +     struct aspeed_p2a_user *priv = file->private_data;
> > +     struct aspeed_p2a_ctrl *ctrl = priv->parent;
> > +     void __user *arg = (void __user *)data;
> > +     struct aspeed_p2a_ctrl_mapping map;
> > +
> > +     if (copy_from_user(&map, arg, sizeof(map)))
> > +             return -EFAULT;
> > +
> > +     switch (cmd) {
> > +     case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> > +             /* If they want a region to be read-only, since the entire
> > +              * region is read-only once enabled, we just need to track this
> > +              * user wants to read from the bridge, and if it's not enabled.
> > +              * Enable it.
> > +              */
> > +             if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> > +                     mutex_lock(&ctrl->tracking);
> > +                     ctrl->readers += 1;
> > +                     mutex_unlock(&ctrl->tracking);
> > +
> > +                     /* Track with the user, so when they close their file,
> > +                      * we can decrement properly.
> > +                      */
> > +                     priv->read += 1;
> > +             } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> > +                     aspeed_p2a_region_acquire(priv, ctrl, &map);
> > +             } else {
> > +                     /* Invalid map flags. */
> > +                     return -EINVAL;
> > +             }
> > +
> > +             aspeed_p2a_enable_bridge(ctrl);
> > +             return 0;
> > +     case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> > +             /* This is a request for the memory-region and corresponding
> > +              * length that is used by the driver for mmap. */
> > +
> > +             map.flags = 0;
> > +             map.addr = ctrl->mem_base;
> > +             map.length = ctrl->mem_size;
>
> Thinking out loud here - do we want to allow ourselves an escape hatch
> for supporting multiple reserved memory locations? Otherwise we might
> need a new ioctl() for it. On the flip-side, not actually having this use-case
> means we might break the implementation anyway, so it could be a
> double-edged sword. Thoughts?

How about this, I use a different structure that has an index -- so
you pass in the structure with the index set, requesting the region
information, and it returns the details for that memory-region until
the index exceeds the number of regions and returns -ENODEV.

However, to avoid extra complexity today, the dts will only support
one memory region...  The complexity of dealing with checking what
region they want to use in mmap by checking what region has been
enabled by that specific user may be too much of a pain though for the
probable life of this driver.  So yeah, a bit of a double-edged sword
of complexity to approach the extra complexity even partially.  I
think having one memory-region is fine for now, and I can't see the
future.  Perhaps having the ioctl structure change and no other
changes is sufficient while being extendable in the future if
someone's need should arise.  That way there won't be an obsolete
ioctl or ambiguous ioctl (code that calls the old one and its result
is quasi-incorrect).

>
> > +
> > +             return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +
> > +/**
> > + * When a user opens this file, we create a structure to track their
> > mappings.
> > + *
> > + * A user can map a region as read-only (bridge enabled), or
> > read-write (bit
> > + * flipped, and bridge enabled).  Either way, this tracking is used,
> > s.t. when
> > + * they release the device references are handled.
> > + *
> > + * The bridge is not enabled until a user calls an ioctl to map a
> > region,
> > + * simply opening the device does not enable it.
> > + */
> > +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> > +{
> > +     struct aspeed_p2a_user *priv;
> > +
> > +     priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> > +     if (!priv)
> > +             return -ENOMEM;
> > +
> > +     priv->file = file;
> > +     priv->read = 0;
> > +     memset(priv->readwrite, 0, sizeof(priv->readwrite));
> > +
> > +     /* The file's private_data is initialized to the p2a_ctrl. */
> > +     priv->parent = file->private_data;
> > +
> > +     /* Set the file's private_data to the user's data. */
> > +     file->private_data = priv;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * This will close the users mappings.  It will go through what they
> > had opened
> > + * for readwrite, and decrement those counts.  If at the end, this is
> > the last
> > + * user, it'll close the bridge.
> > + */
> > +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> > +{
> > +     int i;
> > +     u32 value;
> > +     u32 bits = 0;
> > +     bool open_regions = false;
> > +     struct aspeed_p2a_user *priv = file->private_data;
> > +
> > +     /* Lock others from changing these values until everything is updated
> > +      * in one pass */
> > +     mutex_lock(&priv->parent->tracking);
> > +
> > +     priv->parent->readers -= priv->read;;
> > +
> > +     for (i = 0; i < P2A_REGION_COUNT; i++) {
> > +             priv->parent->readerwriters[i] -= priv->readwrite[i];
> > +
> > +             if (priv->parent->readerwriters[i] > 0)
> > +                     open_regions = true;
> > +             else
> > +                     bits |= priv->parent->config->regions[i].bit;
> > +     }
> > +
> > +     /* Setting a bit to 1 disables the region, so let's just OR with the
> > +      * above to disable any.
> > +      */
> > +
> > +     /* Note, if another user is trying to ioctl, they can't grab tracking,
> > +      * and therefore can't grab either register mutex.
> > +      * If another user is trying to close, they can't grab tracking
> > either.
> > +      */
> > +     regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> > +
> > +     /* If parent->readers is zero and open windows is 0, disable the
> > +      * bridge.
> > +      */
> > +     if (!open_regions && priv->parent->readers == 0)
> > +             aspeed_p2a_disable_bridge(priv->parent);
> > +
> > +     mutex_unlock(&priv->parent->tracking);
> > +
> > +     kfree(priv);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct file_operations aspeed_p2a_ctrl_fops = {
> > +     .owner = THIS_MODULE,
> > +     .mmap = aspeed_p2a_mmap,
> > +     .unlocked_ioctl = aspeed_p2a_ioctl,
> > +     .open = aspeed_p2a_open,
> > +     .release = aspeed_p2a_release,
> > +};
> > +
> > +/** The regions are controlled by SCU2C */
> > +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> > +{
> > +     int i;
> > +     u32 value = 0;
> > +
> > +     for (i = 0; i < P2A_REGION_COUNT; i++)
> > +             value |= p2a_ctrl->config->regions[i].bit;
> > +
> > +     regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> > +
> > +     /* Disable the bridge. */
> > +     aspeed_p2a_disable_bridge(p2a_ctrl);
> > +}
> > +
> > +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> > +{
> > +     struct aspeed_p2a_ctrl *misc_ctrl;
> > +     struct device *dev;
> > +     struct resource *res, resm;
> > +     struct device_node *node;
> > +     int rc = 0;
> > +
> > +     dev = &pdev->dev;
> > +
> > +     misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> > +     if (!misc_ctrl)
> > +             return -ENOMEM;
> > +
> > +     mutex_init(&misc_ctrl->tracking);
> > +     misc_ctrl->readers = 0;
> > +     memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> > +
> > +     misc_ctrl->mem_size = 0;
> > +     misc_ctrl->mem_base = 0;
> > +
> > +     /* optional. */
> > +     node = of_parse_phandle(dev->of_node, "memory-region", 0);
> > +     if (node) {
> > +             rc = of_address_to_resource(node, 0, &resm);
> > +             of_node_put(node);
> > +             if (rc) {
> > +                     dev_err(dev, "Couldn't address to resource for reserved memory\n");
>
> I think the message could be improved, something like "No reserved memory
> specified". Having said that, I don't think it should be an error condition either;
> our experience with aspeed-lpc-ctrl was that it was useful for the memory-region
> property to be optional. You already have:

Ok.

>
> > +
> > +     if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> > +             return -EINVAL;
>
> in the mmap() callback, but we don't get that far unless someone has specified a
> zero-sized reserved-memory node. I think supporting memory-region as optional
> is just a matter of adding the same check to the GET_MEMORY_CONFIG ioctl().
>
> > +                     return -ENOMEM;
>
> The system isn't out of memory so this isn't an ENOMEM condition. I think ENODEV
> is more appropriate, but if we make the memory region optional then this goes
> away anyway.

Roger.

>
> > +             }
> > +
> > +             misc_ctrl->mem_size = resource_size(&resm);
> > +             misc_ctrl->mem_base = resm.start;
> > +     }
> > +
> > +     node = of_parse_phandle(dev->of_node, "syscon", 0);
> > +     if (!node) {
> > +             dev_err(dev, "Couldn't find syscon property\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     misc_ctrl->regmap = syscon_node_to_regmap(node);
> > +     if (IS_ERR(misc_ctrl->regmap)) {
> > +             dev_err(dev, "Couldn't get regmap\n");
> > +             return -ENODEV;
> > +     }
> > +     of_node_put(node);
> > +
> > +     misc_ctrl->config = of_device_get_match_data(dev);
> > +
> > +     dev_set_drvdata(&pdev->dev, misc_ctrl);
> > +
> > +     aspeed_p2a_disable_all(misc_ctrl);
> > +
> > +     misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> > +     misc_ctrl->miscdev.name = DEVICE_NAME;
> > +     misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> > +     misc_ctrl->miscdev.parent = dev;
> > +
> > +     rc = misc_register(&misc_ctrl->miscdev);
> > +     if (rc)
> > +             dev_err(dev, "Unable to register device\n");
> > +
> > +     return rc;
> > +}
> > +
> > +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> > +{
> > +     struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> > +
> > +     misc_deregister(&p2a_ctrl->miscdev);
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * bit | SCU2C | ast2400
> > + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> > + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> > + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> > + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> > + *
> > + * bit | SCU2C | ast2500
> > + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> > + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> > + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> > + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> > + */
>
> The comment is probably unnecessary given the structure declarations
> below.

Fair enough, will drop it.

>
> > +
> > +#define SCU2C_DRAM   BIT(25)
> > +#define SCU2C_SPI    BIT(24)
> > +#define SCU2C_SOC    BIT(23)
> > +#define SCU2C_FLASH  BIT(22)
> > +
> > +static const struct aspeed_p2a_model_data ast2400_model_data = {
> > +     .regions = {
> > +             {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> > +             {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> > +             {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> > +             {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> > +             {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> > +             {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> > +     }
> > +};
> > +
> > +static const struct aspeed_p2a_model_data ast2500_model_data = {
> > +     .regions = {
> > +             {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> > +             {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> > +             {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> > +             {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> > +             {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> > +             {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> > +     }
> > +};
> > +
> > +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> > +     { .compatible = "aspeed,ast2400-p2a-ctrl",
> > +       .data = &ast2400_model_data },
> > +     { .compatible = "aspeed,ast2500-p2a-ctrl",
> > +       .data = &ast2500_model_data },
> > +     { },
> > +};
> > +
> > +static struct platform_driver aspeed_p2a_ctrl_driver = {
> > +     .driver = {
> > +             .name           = DEVICE_NAME,
> > +             .of_match_table = aspeed_p2a_ctrl_match,
> > +     },
> > +     .probe = aspeed_p2a_ctrl_probe,
> > +     .remove = aspeed_p2a_ctrl_remove,
> > +};
> > +
> > +module_platform_driver(aspeed_p2a_ctrl_driver);
> > +
> > +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> > +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC
> > mappings");
> > diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h
> > b/include/uapi/linux/aspeed-p2a-ctrl.h
> > new file mode 100644
> > index 000000000000..e839cdc31db9
> > --- /dev/null
> > +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> > @@ -0,0 +1,59 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > +/*
> > + * Copyright 2019 Google Inc
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version
> > + * 2 of the License, or (at your option) any later version.
>
> SPDX again.

So, only SPDX?  I'm not super on the ball with this type of thing.  I
just mirrored what I saw in other recent aspeed drivers.

>
> > + *
> > + * Provides a simple driver to control the ASPEED P2A interface which
> > allows
> > + * the host to read and write to various regions of the BMC's memory.
> > + */
> > +
> > +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> > +
> > +#include <linux/ioctl.h>
> > +#include <linux/types.h>
> > +
> > +#define ASPEED_P2A_CTRL_READ_ONLY 0
> > +#define ASPEED_P2A_CTRL_READWRITE 1
> > +
> > +/*
> > + * This driver provides a mechanism for enabling or disabling the
> > read-write
> > + * property of specific windows into the ASPEED BMC's memory.
> > + *
> > + * A user can map a region of the BMC's memory as read-only or
> > read-write, with
> > + * the caveat that once any region is mapped, all regions are unlocked
> > for
> > + * reading.
> > + */
> > +
> > +/**
> > + * Unlock a region of BMC physical memory for access from the host.
> > + *
> > + * Also used to read back the optional memory-region configuration for
> > the
> > + * driver.
> > + */
> > +struct aspeed_p2a_ctrl_mapping {
> > +     __u32 addr;
> > +     __u32 length;
> > +     __u32 flags;
> > +};
> > +
> > +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> > +
> > +/**
> > + * This IOCTL is meant to configure a region or regions of memory
> > given a
> > + * starting address and length to be readable by the host, or
> > + * readable-writeable. */
> > +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW
> > _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> > +             0x00, struct aspeed_p2a_ctrl_mapping)
> > +
> > +/**
> > + * This IOCTL is meant to read back to the user the base address and
> > length of
> > + * the memory-region specified to the driver for use with mmap. */
> > +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG
> > _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
>
> Wrap this at 80?
>
> Cheers,
>
> Andrew
>
> > +             0x01, struct aspeed_p2a_ctrl_mapping)
> > +
> > +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> > --
> > 2.21.0.rc2.261.ga7da99ff1b-goog
> >
> >

^ permalink raw reply

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Andrew Jeffery @ 2019-03-04  0:04 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20190228015223.34563-1-venture@google.com>

Hi Patrick.

I've got some minor comments, otherwise it looks reasonable to me.

On Thu, 28 Feb 2019, at 12:22, Patrick Venture wrote:
> The ASPEED AST2400, and AST2500 in some configurations include a
> PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> in the BMC's physical address space.  This feature is especially useful
> when using this bridge to send large files to the BMC.
> 
> The host may use this to send down a firmware image by staging data at a
> specific memory address, and in a coordinated effort with the BMC's
> software stack and kernel, transmit the bytes.
> 
> This driver enables the BMC to unlock the PCI bridge on demand, and
> configure it via ioctl to allow the host to write bytes to an agreed
> upon location.  In the primary use-case, the region to use is known
> apriori on the BMC, and the host requests this information.  Once this
> request is received, the BMC's software stack will enable the bridge and
> the region and then using some software flow control (possibly via IPMI
> packets), copy the bytes down.  Once the process is complete, the BMC
> will disable the bridge and unset any region involved.
> 
> The default behavior of this bridge when present is: enabled and all
> regions marked read-write.  This driver will fix the regions to be
> read-only and then disable the bridge entirely.
> 
> The memory regions protected are:
>  * BMC flash MMIO window
>  * System flash MMIO windows
>  * SOC IO (peripheral MMIO)
>  * DRAM
> 
> The DRAM region itself is all of DRAM and cannot be further specified.
> Once the PCI bridge is enabled, the host can read all of DRAM, and if
> the DRAM section is write-enabled, then it can write to all of it.
> 
> Signed-off-by: Patrick Venture <venture@google.com>
> ---
> Changes for v5:
> - Fixup missing exit condition and remove extra jump.
> Changes for v4:
> - Added ioctl for reading back the memory-region configuration.
> - Cleaned up some unused variables.
> Changes for v3:
> - Deleted unused read and write methods.
> Changes for v2:
> - Dropped unused reads.
> - Moved call to disable bridge to before registering device.
> - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> - Updated the commit message. <<< TODO
> - Updated the bit flipped for SCU180_ENP2A
> - Dropped boolean region_specified variable.
> - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> - Updated commit message.
> ---
>  drivers/misc/Kconfig                 |   8 +
>  drivers/misc/Makefile                |   1 +
>  drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
>  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
>  4 files changed, 524 insertions(+)
>  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
>  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
> 
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index f417b06e11c5..9de1bafe5606 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
>  	  bus. System Configuration interface is one of the possible means
>  	  of generating transactions on this bus.
>  
> +config ASPEED_P2A_CTRL
> +	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> +	tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> +	help
> +	  Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings 
> through
> +	  ioctl()s, the driver also provides an interface for userspace 
> mappings to
> +	  a pre-defined region.
> +
>  config ASPEED_LPC_CTRL
>  	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
>  	tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index e39ccbbc1b3a..57577aee354f 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
>  obj-$(CONFIG_CXL_BASE)		+= cxl/
>  obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
>  obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
> +obj-$(CONFIG_ASPEED_P2A_CTRL)	+= aspeed-p2a-ctrl.o
>  obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
>  obj-$(CONFIG_OCXL)		+= ocxl/
>  obj-y				+= cardreader/
> diff --git a/drivers/misc/aspeed-p2a-ctrl.c 
> b/drivers/misc/aspeed-p2a-ctrl.c
> new file mode 100644
> index 000000000000..6bde4f64632d
> --- /dev/null
> +++ b/drivers/misc/aspeed-p2a-ctrl.c
> @@ -0,0 +1,456 @@
> +/*
> + * Copyright 2019 Google Inc
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.

Should be a SPDX header instead.

> + *
> + * Provides a simple driver to control the ASPEED P2A interface which 
> allows
> + * the host to read and write to various regions of the BMC's memory.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/aspeed-p2a-ctrl.h>
> +
> +#define DEVICE_NAME	"aspeed-p2a-ctrl"
> +
> +/* SCU2C is a Misc. Control Register. */
> +#define SCU2C 0x2c
> +/* SCU180 is the PCIe Configuration Setting Control Register. */
> +#define SCU180 0x180
> +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA 
> device
> + * on the PCI bus. */

As this wraps to multiple lines the trailing `*/` should be on a separate line.
There are a few instances of this throughout.

> +#define SCU180_ENP2A BIT(1)
> +
> +/* The ast2400/2500 both have six ranges. */
> +#define P2A_REGION_COUNT 6
> +
> +struct region {
> +	u32 min;
> +	u32 max;
> +	u32 bit;
> +};
> +
> +struct aspeed_p2a_model_data {
> +	/* min, max, bit */
> +	struct region regions[P2A_REGION_COUNT];
> +};
> +
> +struct aspeed_p2a_ctrl {
> +	struct miscdevice miscdev;
> +	struct regmap *regmap;
> +
> +	const struct aspeed_p2a_model_data *config;
> +
> +	/* Access to these needs to be locked, held via probe, mapping ioctl,
> +	 * and release, remove.
> +	 */
> +	struct mutex tracking;
> +	u32 readers;
> +	u32 readerwriters[P2A_REGION_COUNT];
> +
> +	phys_addr_t mem_base;
> +	resource_size_t mem_size;
> +};
> +
> +struct aspeed_p2a_user {
> +	struct file *file;
> +	struct aspeed_p2a_ctrl *parent;
> +
> +	/** The entire memory space is opened for reading once the bridge is
> +	 * enabled, therefore this needs only to be tracked once per user.
> +	 * If any user has it open for read, the bridge must stay enabled.
> +	 */

Generally the descriptions for the members are described in a kerneldoc
comment above the struct definition. Also you're mixing the kernel-doc
comment opener (`/**`) with non-kernel-doc comments (`/*` on the
tracking` mutex in `struct aspeed_p2a_ctrl` above).

> +	u32 read;
> +
> +	/** Each entry of the array corresponds to a P2A Region.  If the user
> +	 * opens for read or readwrite, the reference goes up here.  On 
> release,
> +	 * this array is walked and references adjusted accordingly.
> +	 */
> +	u32 readwrite[P2A_REGION_COUNT];
> +};
> +
> +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 
> SCU180_ENP2A);

Wrap this at 80 chars.

> +}
> +
> +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> +}
> +
> +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct 
> *vma)
> +{
> +	struct aspeed_p2a_user *priv = file->private_data;
> +	struct aspeed_p2a_ctrl *ctrl = priv->parent;
> +
> +	if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> +		return -EINVAL;
> +
> +	unsigned long vsize = vma->vm_end - vma->vm_start;
> +	pgprot_t prot = vma->vm_page_prot;
> +
> +	if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> +		return -EINVAL;
> +
> +	/* ast2400/2500 AHB accesses are not cache coherent */
> +	prot = pgprot_noncached(prot);
> +
> +	if (remap_pfn_range(vma, vma->vm_start,
> +		(ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> +		vsize, prot))
> +		return -EAGAIN;
> +
> +	return 0;
> +}
> +
> +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> +		struct aspeed_p2a_ctrl *ctrl,
> +		struct aspeed_p2a_ctrl_mapping *map)
> +{
> +	int i;
> +	u32 base, end;
> +
> +	base = map->addr;
> +	end = map->addr + (map->length - 1);
> +
> +	/* If the value is a legal u32, it will find a match. */
> +	for (i = 0; i < P2A_REGION_COUNT; i++) {
> +		const struct region *curr = &ctrl->config->regions[i];
> +
> +		/* If the top of this region is lower than your base, skip it.
> +		 */
> +		if (curr->max < base)
> +			continue;
> +
> +		/* If the bottom of this region is higher than your end, bail.
> +		 */
> +		if (curr->min > end)
> +			break;
> +		/* Lock this and update it, therefore it someone else is
> +		 * closing their file out, this'll preserve the increment.
> +		 */
> +		mutex_lock(&ctrl->tracking);
> +		ctrl->readerwriters[i] += 1;
> +		mutex_unlock(&ctrl->tracking);
> +
> +		/* Track with the user, so when they close their file, we can
> +		 * decrement properly.
> +		 */

The comments about tracking for decrement-on-release purposes is
useful, but I think the other comments in this loop are probably
unnecessary. Up to you though.

> +		priv->readwrite[i] += 1;
> +
> +		/* Enable the region as read-write. */
> +		regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> +	}
> +}
> +
> +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> +		unsigned long data)
> +{
> +	struct aspeed_p2a_user *priv = file->private_data;
> +	struct aspeed_p2a_ctrl *ctrl = priv->parent;
> +	void __user *arg = (void __user *)data;
> +	struct aspeed_p2a_ctrl_mapping map;
> +
> +	if (copy_from_user(&map, arg, sizeof(map)))
> +		return -EFAULT;
> +
> +	switch (cmd) {
> +	case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> +		/* If they want a region to be read-only, since the entire
> +		 * region is read-only once enabled, we just need to track this
> +		 * user wants to read from the bridge, and if it's not enabled.
> +		 * Enable it.
> +		 */
> +		if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> +			mutex_lock(&ctrl->tracking);
> +			ctrl->readers += 1;
> +			mutex_unlock(&ctrl->tracking);
> +
> +			/* Track with the user, so when they close their file,
> +			 * we can decrement properly.
> +			 */
> +			priv->read += 1;
> +		} else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> +			aspeed_p2a_region_acquire(priv, ctrl, &map);
> +		} else {
> +			/* Invalid map flags. */
> +			return -EINVAL;
> +		}
> +
> +		aspeed_p2a_enable_bridge(ctrl);
> +		return 0;
> +	case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> +		/* This is a request for the memory-region and corresponding
> +		 * length that is used by the driver for mmap. */
> +
> +		map.flags = 0;
> +		map.addr = ctrl->mem_base;
> +		map.length = ctrl->mem_size;

Thinking out loud here - do we want to allow ourselves an escape hatch
for supporting multiple reserved memory locations? Otherwise we might
need a new ioctl() for it. On the flip-side, not actually having this use-case
means we might break the implementation anyway, so it could be a
double-edged sword. Thoughts?

> +
> +		return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +
> +/**
> + * When a user opens this file, we create a structure to track their 
> mappings.
> + *
> + * A user can map a region as read-only (bridge enabled), or 
> read-write (bit
> + * flipped, and bridge enabled).  Either way, this tracking is used, 
> s.t. when
> + * they release the device references are handled.
> + *
> + * The bridge is not enabled until a user calls an ioctl to map a 
> region,
> + * simply opening the device does not enable it.
> + */
> +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> +{
> +	struct aspeed_p2a_user *priv;
> +
> +	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->file = file;
> +	priv->read = 0;
> +	memset(priv->readwrite, 0, sizeof(priv->readwrite));
> +
> +	/* The file's private_data is initialized to the p2a_ctrl. */
> +	priv->parent = file->private_data;
> +
> +	/* Set the file's private_data to the user's data. */
> +	file->private_data = priv;
> +
> +	return 0;
> +}
> +
> +/**
> + * This will close the users mappings.  It will go through what they 
> had opened
> + * for readwrite, and decrement those counts.  If at the end, this is 
> the last
> + * user, it'll close the bridge.
> + */
> +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> +{
> +	int i;
> +	u32 value;
> +	u32 bits = 0;
> +	bool open_regions = false;
> +	struct aspeed_p2a_user *priv = file->private_data;
> +
> +	/* Lock others from changing these values until everything is updated
> +	 * in one pass */
> +	mutex_lock(&priv->parent->tracking);
> +
> +	priv->parent->readers -= priv->read;;
> +
> +	for (i = 0; i < P2A_REGION_COUNT; i++) {
> +		priv->parent->readerwriters[i] -= priv->readwrite[i];
> +
> +		if (priv->parent->readerwriters[i] > 0)
> +			open_regions = true;
> +		else
> +			bits |= priv->parent->config->regions[i].bit;
> +	}
> +
> +	/* Setting a bit to 1 disables the region, so let's just OR with the
> +	 * above to disable any.
> +	 */
> +
> +	/* Note, if another user is trying to ioctl, they can't grab tracking,
> +	 * and therefore can't grab either register mutex.
> +	 * If another user is trying to close, they can't grab tracking 
> either.
> +	 */
> +	regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> +
> +	/* If parent->readers is zero and open windows is 0, disable the
> +	 * bridge.
> +	 */
> +	if (!open_regions && priv->parent->readers == 0)
> +		aspeed_p2a_disable_bridge(priv->parent);
> +
> +	mutex_unlock(&priv->parent->tracking);
> +
> +	kfree(priv);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations aspeed_p2a_ctrl_fops = {
> +	.owner = THIS_MODULE,
> +	.mmap = aspeed_p2a_mmap,
> +	.unlocked_ioctl = aspeed_p2a_ioctl,
> +	.open = aspeed_p2a_open,
> +	.release = aspeed_p2a_release,
> +};
> +
> +/** The regions are controlled by SCU2C */
> +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +	int i;
> +	u32 value = 0;
> +
> +	for (i = 0; i < P2A_REGION_COUNT; i++)
> +		value |= p2a_ctrl->config->regions[i].bit;
> +
> +	regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> +
> +	/* Disable the bridge. */
> +	aspeed_p2a_disable_bridge(p2a_ctrl);
> +}
> +
> +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> +{
> +	struct aspeed_p2a_ctrl *misc_ctrl;
> +	struct device *dev;
> +	struct resource *res, resm;
> +	struct device_node *node;
> +	int rc = 0;
> +
> +	dev = &pdev->dev;
> +
> +	misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> +	if (!misc_ctrl)
> +		return -ENOMEM;
> +
> +	mutex_init(&misc_ctrl->tracking);
> +	misc_ctrl->readers = 0;
> +	memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> +
> +	misc_ctrl->mem_size = 0;
> +	misc_ctrl->mem_base = 0;
> +
> +	/* optional. */
> +	node = of_parse_phandle(dev->of_node, "memory-region", 0);
> +	if (node) {
> +		rc = of_address_to_resource(node, 0, &resm);
> +		of_node_put(node);
> +		if (rc) {
> +			dev_err(dev, "Couldn't address to resource for reserved memory\n");

I think the message could be improved, something like "No reserved memory
specified". Having said that, I don't think it should be an error condition either;
our experience with aspeed-lpc-ctrl was that it was useful for the memory-region
property to be optional. You already have:

> +
> +	if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> +		return -EINVAL;

in the mmap() callback, but we don't get that far unless someone has specified a
zero-sized reserved-memory node. I think supporting memory-region as optional
is just a matter of adding the same check to the GET_MEMORY_CONFIG ioctl().

> +			return -ENOMEM;

The system isn't out of memory so this isn't an ENOMEM condition. I think ENODEV
is more appropriate, but if we make the memory region optional then this goes
away anyway.

> +		}
> +
> +		misc_ctrl->mem_size = resource_size(&resm);
> +		misc_ctrl->mem_base = resm.start;
> +	}
> +
> +	node = of_parse_phandle(dev->of_node, "syscon", 0);
> +	if (!node) {
> +		dev_err(dev, "Couldn't find syscon property\n");
> +		return -ENODEV;
> +	}
> +
> +	misc_ctrl->regmap = syscon_node_to_regmap(node);
> +	if (IS_ERR(misc_ctrl->regmap)) {
> +		dev_err(dev, "Couldn't get regmap\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(node);
> +
> +	misc_ctrl->config = of_device_get_match_data(dev);
> +
> +	dev_set_drvdata(&pdev->dev, misc_ctrl);
> +
> +	aspeed_p2a_disable_all(misc_ctrl);
> +
> +	misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> +	misc_ctrl->miscdev.name = DEVICE_NAME;
> +	misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> +	misc_ctrl->miscdev.parent = dev;
> +
> +	rc = misc_register(&misc_ctrl->miscdev);
> +	if (rc)
> +		dev_err(dev, "Unable to register device\n");
> +
> +	return rc;
> +}
> +
> +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> +{
> +	struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> +
> +	misc_deregister(&p2a_ctrl->miscdev);
> +
> +	return 0;
> +}
> +
> +/*
> + * bit | SCU2C | ast2400
> + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> + *
> + * bit | SCU2C | ast2500
> + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> + */

The comment is probably unnecessary given the structure declarations
below.

> +
> +#define SCU2C_DRAM	BIT(25)
> +#define SCU2C_SPI	BIT(24)
> +#define SCU2C_SOC	BIT(23)
> +#define SCU2C_FLASH	BIT(22)
> +
> +static const struct aspeed_p2a_model_data ast2400_model_data = {
> +	.regions = {
> +		{0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> +		{0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> +		{0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> +		{0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> +		{0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> +		{0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> +	}
> +};
> +
> +static const struct aspeed_p2a_model_data ast2500_model_data = {
> +	.regions = {
> +		{0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> +		{0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> +		{0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> +		{0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> +		{0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> +		{0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> +	}
> +};
> +
> +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> +	{ .compatible = "aspeed,ast2400-p2a-ctrl",
> +	  .data = &ast2400_model_data },
> +	{ .compatible = "aspeed,ast2500-p2a-ctrl",
> +	  .data = &ast2500_model_data },
> +	{ },
> +};
> +
> +static struct platform_driver aspeed_p2a_ctrl_driver = {
> +	.driver = {
> +		.name		= DEVICE_NAME,
> +		.of_match_table = aspeed_p2a_ctrl_match,
> +	},
> +	.probe = aspeed_p2a_ctrl_probe,
> +	.remove = aspeed_p2a_ctrl_remove,
> +};
> +
> +module_platform_driver(aspeed_p2a_ctrl_driver);
> +
> +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC 
> mappings");
> diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h 
> b/include/uapi/linux/aspeed-p2a-ctrl.h
> new file mode 100644
> index 000000000000..e839cdc31db9
> --- /dev/null
> +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> @@ -0,0 +1,59 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright 2019 Google Inc
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.

SPDX again.

> + *
> + * Provides a simple driver to control the ASPEED P2A interface which 
> allows
> + * the host to read and write to various regions of the BMC's memory.
> + */
> +
> +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +#define ASPEED_P2A_CTRL_READ_ONLY 0
> +#define ASPEED_P2A_CTRL_READWRITE 1
> +
> +/*
> + * This driver provides a mechanism for enabling or disabling the 
> read-write
> + * property of specific windows into the ASPEED BMC's memory.
> + *
> + * A user can map a region of the BMC's memory as read-only or 
> read-write, with
> + * the caveat that once any region is mapped, all regions are unlocked 
> for
> + * reading.
> + */
> +
> +/**
> + * Unlock a region of BMC physical memory for access from the host.
> + *
> + * Also used to read back the optional memory-region configuration for 
> the
> + * driver.
> + */
> +struct aspeed_p2a_ctrl_mapping {
> +	__u32 addr;
> +	__u32 length;
> +	__u32 flags;
> +};
> +
> +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> +
> +/**
> + * This IOCTL is meant to configure a region or regions of memory 
> given a
> + * starting address and length to be readable by the host, or
> + * readable-writeable. */
> +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW 
> _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> +		0x00, struct aspeed_p2a_ctrl_mapping)
> +
> +/**
> + * This IOCTL is meant to read back to the user the base address and 
> length of
> + * the memory-region specified to the driver for use with mmap. */
> +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG 
> _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \

Wrap this at 80?

Cheers,

Andrew

> +		0x01, struct aspeed_p2a_ctrl_mapping)
> +
> +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> -- 
> 2.21.0.rc2.261.ga7da99ff1b-goog
> 
>

^ permalink raw reply

* [PATCH] usb: introduce usb_ep_type_string() function
From: Greg Kroah-Hartman @ 2019-03-01 16:21 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <e2ff70a3608dce315b52aea710398ba79d0f6f93.1551422947.git.chunfeng.yun@mediatek.com>

On Fri, Mar 01, 2019 at 02:58:23PM +0800, Chunfeng Yun wrote:
> In some places, the code prints a human-readable USB endpoint
> transfer type (e.g. "bulk"). This involves a switch statement
> sometimes wrapped around in ({ ... }) block leading to code
> repetition.
> To make this scenario easier, here introduces usb_ep_type_string()
> function, which returns a human-readable name of provided
> endpoint type.
> It also changes a few places switch was used to use this
> new function.
> 
> Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
> ---
>  drivers/usb/common/common.c              | 16 ++++++++++++++++
>  drivers/usb/core/endpoint.c              | 18 ++----------------
>  drivers/usb/core/hcd.c                   | 17 ++---------------
>  drivers/usb/dwc3/debugfs.c               | 23 ++++-------------------
>  drivers/usb/gadget/udc/aspeed-vhub/epn.c |  6 +-----
>  drivers/usb/gadget/udc/dummy_hcd.c       | 16 +---------------
>  drivers/usb/host/xhci-trace.h            | 19 ++-----------------
>  include/linux/usb/ch9.h                  |  8 ++++++++
>  8 files changed, 36 insertions(+), 87 deletions(-)
> 
> diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
> index 48277bbc15e4..2174dd9ec176 100644
> --- a/drivers/usb/common/common.c
> +++ b/drivers/usb/common/common.c
> @@ -16,6 +16,22 @@
>  #include <linux/usb/otg.h>
>  #include <linux/of_platform.h>
>  
> +static const char *const ep_type_names[] = {
> +	[USB_ENDPOINT_XFER_CONTROL] = "ctrl",
> +	[USB_ENDPOINT_XFER_ISOC] = "isoc",
> +	[USB_ENDPOINT_XFER_BULK] = "bulk",
> +	[USB_ENDPOINT_XFER_INT] = "intr",
> +};
> +
> +const char *usb_ep_type_string(int ep_type)
> +{
> +	if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names))
> +		return "unknown";
> +
> +	return ep_type_names[ep_type];
> +}
> +EXPORT_SYMBOL_GPL(usb_ep_type_string);
> +
>  const char *usb_otg_state_string(enum usb_otg_state state)
>  {
>  	static const char *const names[] = {
> diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
> index 1c2c04079676..afa43f9a47b2 100644
> --- a/drivers/usb/core/endpoint.c
> +++ b/drivers/usb/core/endpoint.c
> @@ -60,23 +60,9 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
>  			 char *buf)
>  {
>  	struct ep_device *ep = to_ep_device(dev);
> -	char *type = "unknown";
> +	int ep_type = usb_endpoint_type(ep->desc);
>  
> -	switch (usb_endpoint_type(ep->desc)) {
> -	case USB_ENDPOINT_XFER_CONTROL:
> -		type = "Control";
> -		break;
> -	case USB_ENDPOINT_XFER_ISOC:
> -		type = "Isoc";
> -		break;
> -	case USB_ENDPOINT_XFER_BULK:
> -		type = "Bulk";
> -		break;
> -	case USB_ENDPOINT_XFER_INT:
> -		type = "Interrupt";
> -		break;
> -	}
> -	return sprintf(buf, "%s\n", type);
> +	return sprintf(buf, "%s\n", usb_ep_type_string(ep_type));

You just changed a user/kernel API here, the strings are now different
from what they used to be :(

That's not ok, odds are you will break tools if you do this.

>  }
>  static DEVICE_ATTR_RO(type);
>  
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index 015b126ce455..193ee92b2fdb 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -1875,23 +1875,10 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
>  		/* kick hcd */
>  		unlink1(hcd, urb, -ESHUTDOWN);
>  		dev_dbg (hcd->self.controller,
> -			"shutdown urb %pK ep%d%s%s\n",
> +			"shutdown urb %pK ep%d%s-%s\n",
>  			urb, usb_endpoint_num(&ep->desc),
>  			is_in ? "in" : "out",
> -			({	char *s;
> -
> -				 switch (usb_endpoint_type(&ep->desc)) {
> -				 case USB_ENDPOINT_XFER_CONTROL:
> -					s = ""; break;
> -				 case USB_ENDPOINT_XFER_BULK:
> -					s = "-bulk"; break;
> -				 case USB_ENDPOINT_XFER_INT:
> -					s = "-intr"; break;
> -				 default:
> -					s = "-iso"; break;
> -				};
> -				s;
> -			}));
> +			usb_ep_type_string(usb_endpoint_type(&ep->desc)));

You also changed the message here for control endpoints, but that's not
a big deal, it's ok.

>  		usb_put_urb (urb);
>  
>  		/* list contents may have changed */
> diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
> index 1c792710348f..d9e2a63835fe 100644
> --- a/drivers/usb/dwc3/debugfs.c
> +++ b/drivers/usb/dwc3/debugfs.c
> @@ -750,28 +750,13 @@ static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
>  	unsigned long		flags;
>  
>  	spin_lock_irqsave(&dwc->lock, flags);
> -	if (!(dep->flags & DWC3_EP_ENABLED) ||
> -			!dep->endpoint.desc) {
> -		seq_printf(s, "--\n");
> +	if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
> +		seq_puts(s, "unknown\n");
>  		goto out;
>  	}
>  
> -	switch (usb_endpoint_type(dep->endpoint.desc)) {
> -	case USB_ENDPOINT_XFER_CONTROL:
> -		seq_printf(s, "control\n");
> -		break;
> -	case USB_ENDPOINT_XFER_ISOC:
> -		seq_printf(s, "isochronous\n");
> -		break;
> -	case USB_ENDPOINT_XFER_BULK:
> -		seq_printf(s, "bulk\n");
> -		break;
> -	case USB_ENDPOINT_XFER_INT:
> -		seq_printf(s, "interrupt\n");
> -		break;
> -	default:
> -		seq_printf(s, "--\n");
> -	}
> +	seq_printf(s, "%s\n",
> +		   usb_ep_type_string(usb_endpoint_type(dep->endpoint.desc)));

It is debugfs, so no real API guarantees at all, but you did change the
output format, and that's not always good.

>  
>  out:
>  	spin_unlock_irqrestore(&dwc->lock, flags);
> diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> index 4a28e3fbeb0b..165b0a43670e 100644
> --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
> @@ -593,10 +593,6 @@ static int ast_vhub_epn_disable(struct usb_ep* u_ep)
>  static int ast_vhub_epn_enable(struct usb_ep* u_ep,
>  			       const struct usb_endpoint_descriptor *desc)
>  {
> -	static const char *ep_type_string[] __maybe_unused = { "ctrl",
> -							       "isoc",
> -							       "bulk",
> -							       "intr" };
>  	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
>  	struct ast_vhub_dev *dev;
>  	struct ast_vhub *vhub;
> @@ -646,7 +642,7 @@ static int ast_vhub_epn_enable(struct usb_ep* u_ep,
>  	ep->epn.wedged = false;
>  
>  	EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
> -	      ep->epn.is_in ? "in" : "out", ep_type_string[type],
> +	      ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
>  	      usb_endpoint_num(desc), maxpacket);
>  
>  	/* Can we use DMA descriptor mode ? */
> diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
> index baf72f95f0f1..40c6a484e232 100644
> --- a/drivers/usb/gadget/udc/dummy_hcd.c
> +++ b/drivers/usb/gadget/udc/dummy_hcd.c
> @@ -617,21 +617,7 @@ static int dummy_enable(struct usb_ep *_ep,
>  		_ep->name,
>  		desc->bEndpointAddress & 0x0f,
>  		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
> -		({ char *val;
> -		 switch (usb_endpoint_type(desc)) {
> -		 case USB_ENDPOINT_XFER_BULK:
> -			 val = "bulk";
> -			 break;
> -		 case USB_ENDPOINT_XFER_ISOC:
> -			 val = "iso";
> -			 break;
> -		 case USB_ENDPOINT_XFER_INT:
> -			 val = "intr";
> -			 break;
> -		 default:
> -			 val = "ctrl";
> -			 break;
> -		 } val; }),
> +		usb_ep_type_string(usb_endpoint_type(desc)),
>  		max, ep->stream_en ? "enabled" : "disabled");
>  
>  	/* at this point real hardware should be NAKing transfers
> diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
> index 88b427434bd8..bac73d688f11 100644
> --- a/drivers/usb/host/xhci-trace.h
> +++ b/drivers/usb/host/xhci-trace.h
> @@ -289,23 +289,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
>  	),
>  	TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
>  			__entry->epnum, __entry->dir_in ? "in" : "out",
> -			({ char *s;
> -			switch (__entry->type) {
> -			case USB_ENDPOINT_XFER_INT:
> -				s = "intr";
> -				break;
> -			case USB_ENDPOINT_XFER_CONTROL:
> -				s = "control";
> -				break;
> -			case USB_ENDPOINT_XFER_BULK:
> -				s = "bulk";
> -				break;
> -			case USB_ENDPOINT_XFER_ISOC:
> -				s = "isoc";
> -				break;
> -			default:
> -				s = "UNKNOWN";
> -			} s; }), __entry->urb, __entry->pipe, __entry->slot_id,
> +			usb_ep_type_string(__entry->type),
> +			__entry->urb, __entry->pipe, __entry->slot_id,
>  			__entry->actual, __entry->length, __entry->num_mapped_sgs,
>  			__entry->num_sgs, __entry->stream, __entry->flags
>  		)

Another api change with "control" :(

I like the idea, but you can not break user/kernel apis like this
without knowing the affects that could happen.

thanks,

greg k-h

^ permalink raw reply

* [PATCH] usb: introduce usb_ep_type_string() function
From: Chunfeng Yun @ 2019-03-01  7:04 UTC (permalink / raw)
  To: linux-aspeed

In some places, the code prints a human-readable USB endpoint
transfer type (e.g. "bulk"). This involves a switch statement
sometimes wrapped around in ({ ... }) block leading to code
repetition.
To make this scenario easier, here introduces usb_ep_type_string()
function, which returns a human-readable name of provided
endpoint type.
It also changes a few places switch was used to use this
new function.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
 drivers/usb/common/common.c              | 16 ++++++++++++++++
 drivers/usb/core/endpoint.c              | 18 ++----------------
 drivers/usb/core/hcd.c                   | 17 ++---------------
 drivers/usb/dwc3/debugfs.c               | 23 ++++-------------------
 drivers/usb/gadget/udc/aspeed-vhub/epn.c |  6 +-----
 drivers/usb/gadget/udc/dummy_hcd.c       | 16 +---------------
 drivers/usb/host/xhci-trace.h            | 19 ++-----------------
 include/linux/usb/ch9.h                  |  8 ++++++++
 8 files changed, 36 insertions(+), 87 deletions(-)

diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 48277bbc15e4..2174dd9ec176 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -16,6 +16,22 @@
 #include <linux/usb/otg.h>
 #include <linux/of_platform.h>
 
+static const char *const ep_type_names[] = {
+	[USB_ENDPOINT_XFER_CONTROL] = "ctrl",
+	[USB_ENDPOINT_XFER_ISOC] = "isoc",
+	[USB_ENDPOINT_XFER_BULK] = "bulk",
+	[USB_ENDPOINT_XFER_INT] = "intr",
+};
+
+const char *usb_ep_type_string(int ep_type)
+{
+	if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names))
+		return "unknown";
+
+	return ep_type_names[ep_type];
+}
+EXPORT_SYMBOL_GPL(usb_ep_type_string);
+
 const char *usb_otg_state_string(enum usb_otg_state state)
 {
 	static const char *const names[] = {
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 1c2c04079676..afa43f9a47b2 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -60,23 +60,9 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
 			 char *buf)
 {
 	struct ep_device *ep = to_ep_device(dev);
-	char *type = "unknown";
+	int ep_type = usb_endpoint_type(ep->desc);
 
-	switch (usb_endpoint_type(ep->desc)) {
-	case USB_ENDPOINT_XFER_CONTROL:
-		type = "Control";
-		break;
-	case USB_ENDPOINT_XFER_ISOC:
-		type = "Isoc";
-		break;
-	case USB_ENDPOINT_XFER_BULK:
-		type = "Bulk";
-		break;
-	case USB_ENDPOINT_XFER_INT:
-		type = "Interrupt";
-		break;
-	}
-	return sprintf(buf, "%s\n", type);
+	return sprintf(buf, "%s\n", usb_ep_type_string(ep_type));
 }
 static DEVICE_ATTR_RO(type);
 
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 015b126ce455..193ee92b2fdb 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1875,23 +1875,10 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
 		/* kick hcd */
 		unlink1(hcd, urb, -ESHUTDOWN);
 		dev_dbg (hcd->self.controller,
-			"shutdown urb %pK ep%d%s%s\n",
+			"shutdown urb %pK ep%d%s-%s\n",
 			urb, usb_endpoint_num(&ep->desc),
 			is_in ? "in" : "out",
-			({	char *s;
-
-				 switch (usb_endpoint_type(&ep->desc)) {
-				 case USB_ENDPOINT_XFER_CONTROL:
-					s = ""; break;
-				 case USB_ENDPOINT_XFER_BULK:
-					s = "-bulk"; break;
-				 case USB_ENDPOINT_XFER_INT:
-					s = "-intr"; break;
-				 default:
-					s = "-iso"; break;
-				};
-				s;
-			}));
+			usb_ep_type_string(usb_endpoint_type(&ep->desc)));
 		usb_put_urb (urb);
 
 		/* list contents may have changed */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 1c792710348f..d9e2a63835fe 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -750,28 +750,13 @@ static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
 	unsigned long		flags;
 
 	spin_lock_irqsave(&dwc->lock, flags);
-	if (!(dep->flags & DWC3_EP_ENABLED) ||
-			!dep->endpoint.desc) {
-		seq_printf(s, "--\n");
+	if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
+		seq_puts(s, "unknown\n");
 		goto out;
 	}
 
-	switch (usb_endpoint_type(dep->endpoint.desc)) {
-	case USB_ENDPOINT_XFER_CONTROL:
-		seq_printf(s, "control\n");
-		break;
-	case USB_ENDPOINT_XFER_ISOC:
-		seq_printf(s, "isochronous\n");
-		break;
-	case USB_ENDPOINT_XFER_BULK:
-		seq_printf(s, "bulk\n");
-		break;
-	case USB_ENDPOINT_XFER_INT:
-		seq_printf(s, "interrupt\n");
-		break;
-	default:
-		seq_printf(s, "--\n");
-	}
+	seq_printf(s, "%s\n",
+		   usb_ep_type_string(usb_endpoint_type(dep->endpoint.desc)));
 
 out:
 	spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
index 4a28e3fbeb0b..165b0a43670e 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
@@ -593,10 +593,6 @@ static int ast_vhub_epn_disable(struct usb_ep* u_ep)
 static int ast_vhub_epn_enable(struct usb_ep* u_ep,
 			       const struct usb_endpoint_descriptor *desc)
 {
-	static const char *ep_type_string[] __maybe_unused = { "ctrl",
-							       "isoc",
-							       "bulk",
-							       "intr" };
 	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
 	struct ast_vhub_dev *dev;
 	struct ast_vhub *vhub;
@@ -646,7 +642,7 @@ static int ast_vhub_epn_enable(struct usb_ep* u_ep,
 	ep->epn.wedged = false;
 
 	EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
-	      ep->epn.is_in ? "in" : "out", ep_type_string[type],
+	      ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
 	      usb_endpoint_num(desc), maxpacket);
 
 	/* Can we use DMA descriptor mode ? */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index baf72f95f0f1..40c6a484e232 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -617,21 +617,7 @@ static int dummy_enable(struct usb_ep *_ep,
 		_ep->name,
 		desc->bEndpointAddress & 0x0f,
 		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
-		({ char *val;
-		 switch (usb_endpoint_type(desc)) {
-		 case USB_ENDPOINT_XFER_BULK:
-			 val = "bulk";
-			 break;
-		 case USB_ENDPOINT_XFER_ISOC:
-			 val = "iso";
-			 break;
-		 case USB_ENDPOINT_XFER_INT:
-			 val = "intr";
-			 break;
-		 default:
-			 val = "ctrl";
-			 break;
-		 } val; }),
+		usb_ep_type_string(usb_endpoint_type(desc)),
 		max, ep->stream_en ? "enabled" : "disabled");
 
 	/* at this point real hardware should be NAKing transfers
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 88b427434bd8..bac73d688f11 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -289,23 +289,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
 	),
 	TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
 			__entry->epnum, __entry->dir_in ? "in" : "out",
-			({ char *s;
-			switch (__entry->type) {
-			case USB_ENDPOINT_XFER_INT:
-				s = "intr";
-				break;
-			case USB_ENDPOINT_XFER_CONTROL:
-				s = "control";
-				break;
-			case USB_ENDPOINT_XFER_BULK:
-				s = "bulk";
-				break;
-			case USB_ENDPOINT_XFER_ISOC:
-				s = "isoc";
-				break;
-			default:
-				s = "UNKNOWN";
-			} s; }), __entry->urb, __entry->pipe, __entry->slot_id,
+			usb_ep_type_string(__entry->type),
+			__entry->urb, __entry->pipe, __entry->slot_id,
 			__entry->actual, __entry->length, __entry->num_mapped_sgs,
 			__entry->num_sgs, __entry->stream, __entry->flags
 		)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 523aa088f6ab..da82606be605 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -36,6 +36,14 @@
 #include <linux/device.h>
 #include <uapi/linux/usb/ch9.h>
 
+/**
+ * usb_ep_type_string() - Returns human readable-name of the endpoint type.
+ * @ep_type: The endpoint type to return human-readable name for.  If it's not
+ *   any of the types: USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT},
+ *   usually got by usb_endpoint_type(), the string 'unknown' will be returned.
+ */
+extern const char *usb_ep_type_string(int ep_type);
+
 /**
  * usb_speed_string() - Returns human readable-name of the speed.
  * @speed: The speed to return human-readable name for.  If it's not
-- 
2.20.1


^ permalink raw reply related

* [PATCH v5 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-02-28  1:52 UTC (permalink / raw)
  To: linux-aspeed

The ASPEED AST2400, and AST2500 in some configurations include a
PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
in the BMC's physical address space.  This feature is especially useful
when using this bridge to send large files to the BMC.

The host may use this to send down a firmware image by staging data at a
specific memory address, and in a coordinated effort with the BMC's
software stack and kernel, transmit the bytes.

This driver enables the BMC to unlock the PCI bridge on demand, and
configure it via ioctl to allow the host to write bytes to an agreed
upon location.  In the primary use-case, the region to use is known
apriori on the BMC, and the host requests this information.  Once this
request is received, the BMC's software stack will enable the bridge and
the region and then using some software flow control (possibly via IPMI
packets), copy the bytes down.  Once the process is complete, the BMC
will disable the bridge and unset any region involved.

The default behavior of this bridge when present is: enabled and all
regions marked read-write.  This driver will fix the regions to be
read-only and then disable the bridge entirely.

The memory regions protected are:
 * BMC flash MMIO window
 * System flash MMIO windows
 * SOC IO (peripheral MMIO)
 * DRAM

The DRAM region itself is all of DRAM and cannot be further specified.
Once the PCI bridge is enabled, the host can read all of DRAM, and if
the DRAM section is write-enabled, then it can write to all of it.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v5:
- Fixup missing exit condition and remove extra jump.
Changes for v4:
- Added ioctl for reading back the memory-region configuration.
- Cleaned up some unused variables.
Changes for v3:
- Deleted unused read and write methods.
Changes for v2:
- Dropped unused reads.
- Moved call to disable bridge to before registering device.
- Switch from using regs to using a syscon regmap. <<< IN PROGRESS
- Updated the commit message. <<< TODO
- Updated the bit flipped for SCU180_ENP2A
- Dropped boolean region_specified variable.
- Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
- Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
- Updated commit message.
---
 drivers/misc/Kconfig                 |   8 +
 drivers/misc/Makefile                |   1 +
 drivers/misc/aspeed-p2a-ctrl.c       | 456 +++++++++++++++++++++++++++
 include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
 4 files changed, 524 insertions(+)
 create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
 create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f417b06e11c5..9de1bafe5606 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
 	  bus. System Configuration interface is one of the possible means
 	  of generating transactions on this bus.
 
+config ASPEED_P2A_CTRL
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
+	help
+	  Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings through
+	  ioctl()s, the driver also provides an interface for userspace mappings to
+	  a pre-defined region.
+
 config ASPEED_LPC_CTRL
 	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
 	tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e39ccbbc1b3a..57577aee354f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_P2A_CTRL)	+= aspeed-p2a-ctrl.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-y				+= cardreader/
diff --git a/drivers/misc/aspeed-p2a-ctrl.c b/drivers/misc/aspeed-p2a-ctrl.c
new file mode 100644
index 000000000000..6bde4f64632d
--- /dev/null
+++ b/drivers/misc/aspeed-p2a-ctrl.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/aspeed-p2a-ctrl.h>
+
+#define DEVICE_NAME	"aspeed-p2a-ctrl"
+
+/* SCU2C is a Misc. Control Register. */
+#define SCU2C 0x2c
+/* SCU180 is the PCIe Configuration Setting Control Register. */
+#define SCU180 0x180
+/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device
+ * on the PCI bus. */
+#define SCU180_ENP2A BIT(1)
+
+/* The ast2400/2500 both have six ranges. */
+#define P2A_REGION_COUNT 6
+
+struct region {
+	u32 min;
+	u32 max;
+	u32 bit;
+};
+
+struct aspeed_p2a_model_data {
+	/* min, max, bit */
+	struct region regions[P2A_REGION_COUNT];
+};
+
+struct aspeed_p2a_ctrl {
+	struct miscdevice miscdev;
+	struct regmap *regmap;
+
+	const struct aspeed_p2a_model_data *config;
+
+	/* Access to these needs to be locked, held via probe, mapping ioctl,
+	 * and release, remove.
+	 */
+	struct mutex tracking;
+	u32 readers;
+	u32 readerwriters[P2A_REGION_COUNT];
+
+	phys_addr_t mem_base;
+	resource_size_t mem_size;
+};
+
+struct aspeed_p2a_user {
+	struct file *file;
+	struct aspeed_p2a_ctrl *parent;
+
+	/** The entire memory space is opened for reading once the bridge is
+	 * enabled, therefore this needs only to be tracked once per user.
+	 * If any user has it open for read, the bridge must stay enabled.
+	 */
+	u32 read;
+
+	/** Each entry of the array corresponds to a P2A Region.  If the user
+	 * opens for read or readwrite, the reference goes up here.  On release,
+	 * this array is walked and references adjusted accordingly.
+	 */
+	u32 readwrite[P2A_REGION_COUNT];
+};
+
+static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, SCU180_ENP2A);
+}
+
+static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
+}
+
+static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+
+	if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
+		return -EINVAL;
+
+	unsigned long vsize = vma->vm_end - vma->vm_start;
+	pgprot_t prot = vma->vm_page_prot;
+
+	if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
+		return -EINVAL;
+
+	/* ast2400/2500 AHB accesses are not cache coherent */
+	prot = pgprot_noncached(prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+		(ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
+		vsize, prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
+		struct aspeed_p2a_ctrl *ctrl,
+		struct aspeed_p2a_ctrl_mapping *map)
+{
+	int i;
+	u32 base, end;
+
+	base = map->addr;
+	end = map->addr + (map->length - 1);
+
+	/* If the value is a legal u32, it will find a match. */
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		const struct region *curr = &ctrl->config->regions[i];
+
+		/* If the top of this region is lower than your base, skip it.
+		 */
+		if (curr->max < base)
+			continue;
+
+		/* If the bottom of this region is higher than your end, bail.
+		 */
+		if (curr->min > end)
+			break;
+		/* Lock this and update it, therefore it someone else is
+		 * closing their file out, this'll preserve the increment.
+		 */
+		mutex_lock(&ctrl->tracking);
+		ctrl->readerwriters[i] += 1;
+		mutex_unlock(&ctrl->tracking);
+
+		/* Track with the user, so when they close their file, we can
+		 * decrement properly.
+		 */
+		priv->readwrite[i] += 1;
+
+		/* Enable the region as read-write. */
+		regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
+	}
+}
+
+static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
+		unsigned long data)
+{
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+	void __user *arg = (void __user *)data;
+	struct aspeed_p2a_ctrl_mapping map;
+
+	if (copy_from_user(&map, arg, sizeof(map)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
+		/* If they want a region to be read-only, since the entire
+		 * region is read-only once enabled, we just need to track this
+		 * user wants to read from the bridge, and if it's not enabled.
+		 * Enable it.
+		 */
+		if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
+			mutex_lock(&ctrl->tracking);
+			ctrl->readers += 1;
+			mutex_unlock(&ctrl->tracking);
+
+			/* Track with the user, so when they close their file,
+			 * we can decrement properly.
+			 */
+			priv->read += 1;
+		} else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
+			aspeed_p2a_region_acquire(priv, ctrl, &map);
+		} else {
+			/* Invalid map flags. */
+			return -EINVAL;
+		}
+
+		aspeed_p2a_enable_bridge(ctrl);
+		return 0;
+	case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
+		/* This is a request for the memory-region and corresponding
+		 * length that is used by the driver for mmap. */
+
+		map.flags = 0;
+		map.addr = ctrl->mem_base;
+		map.length = ctrl->mem_size;
+
+		return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
+	}
+
+	return -EINVAL;
+}
+
+
+/**
+ * When a user opens this file, we create a structure to track their mappings.
+ *
+ * A user can map a region as read-only (bridge enabled), or read-write (bit
+ * flipped, and bridge enabled).  Either way, this tracking is used, s.t. when
+ * they release the device references are handled.
+ *
+ * The bridge is not enabled until a user calls an ioctl to map a region,
+ * simply opening the device does not enable it.
+ */
+static int aspeed_p2a_open(struct inode *inode, struct file *file)
+{
+	struct aspeed_p2a_user *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->file = file;
+	priv->read = 0;
+	memset(priv->readwrite, 0, sizeof(priv->readwrite));
+
+	/* The file's private_data is initialized to the p2a_ctrl. */
+	priv->parent = file->private_data;
+
+	/* Set the file's private_data to the user's data. */
+	file->private_data = priv;
+
+	return 0;
+}
+
+/**
+ * This will close the users mappings.  It will go through what they had opened
+ * for readwrite, and decrement those counts.  If at the end, this is the last
+ * user, it'll close the bridge.
+ */
+static int aspeed_p2a_release(struct inode *inode, struct file *file)
+{
+	int i;
+	u32 value;
+	u32 bits = 0;
+	bool open_regions = false;
+	struct aspeed_p2a_user *priv = file->private_data;
+
+	/* Lock others from changing these values until everything is updated
+	 * in one pass */
+	mutex_lock(&priv->parent->tracking);
+
+	priv->parent->readers -= priv->read;;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		priv->parent->readerwriters[i] -= priv->readwrite[i];
+
+		if (priv->parent->readerwriters[i] > 0)
+			open_regions = true;
+		else
+			bits |= priv->parent->config->regions[i].bit;
+	}
+
+	/* Setting a bit to 1 disables the region, so let's just OR with the
+	 * above to disable any.
+	 */
+
+	/* Note, if another user is trying to ioctl, they can't grab tracking,
+	 * and therefore can't grab either register mutex.
+	 * If another user is trying to close, they can't grab tracking either.
+	 */
+	regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
+
+	/* If parent->readers is zero and open windows is 0, disable the
+	 * bridge.
+	 */
+	if (!open_regions && priv->parent->readers == 0)
+		aspeed_p2a_disable_bridge(priv->parent);
+
+	mutex_unlock(&priv->parent->tracking);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct file_operations aspeed_p2a_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.mmap = aspeed_p2a_mmap,
+	.unlocked_ioctl = aspeed_p2a_ioctl,
+	.open = aspeed_p2a_open,
+	.release = aspeed_p2a_release,
+};
+
+/** The regions are controlled by SCU2C */
+static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	int i;
+	u32 value = 0;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++)
+		value |= p2a_ctrl->config->regions[i].bit;
+
+	regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
+
+	/* Disable the bridge. */
+	aspeed_p2a_disable_bridge(p2a_ctrl);
+}
+
+static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *misc_ctrl;
+	struct device *dev;
+	struct resource *res, resm;
+	struct device_node *node;
+	int rc = 0;
+
+	dev = &pdev->dev;
+
+	misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
+	if (!misc_ctrl)
+		return -ENOMEM;
+
+	mutex_init(&misc_ctrl->tracking);
+	misc_ctrl->readers = 0;
+	memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
+
+	misc_ctrl->mem_size = 0;
+	misc_ctrl->mem_base = 0;
+
+	/* optional. */
+	node = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (node) {
+		rc = of_address_to_resource(node, 0, &resm);
+		of_node_put(node);
+		if (rc) {
+			dev_err(dev, "Couldn't address to resource for reserved memory\n");
+			return -ENOMEM;
+		}
+
+		misc_ctrl->mem_size = resource_size(&resm);
+		misc_ctrl->mem_base = resm.start;
+	}
+
+	node = of_parse_phandle(dev->of_node, "syscon", 0);
+	if (!node) {
+		dev_err(dev, "Couldn't find syscon property\n");
+		return -ENODEV;
+	}
+
+	misc_ctrl->regmap = syscon_node_to_regmap(node);
+	if (IS_ERR(misc_ctrl->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+	of_node_put(node);
+
+	misc_ctrl->config = of_device_get_match_data(dev);
+
+	dev_set_drvdata(&pdev->dev, misc_ctrl);
+
+	aspeed_p2a_disable_all(misc_ctrl);
+
+	misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
+	misc_ctrl->miscdev.name = DEVICE_NAME;
+	misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
+	misc_ctrl->miscdev.parent = dev;
+
+	rc = misc_register(&misc_ctrl->miscdev);
+	if (rc)
+		dev_err(dev, "Unable to register device\n");
+
+	return rc;
+}
+
+static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&p2a_ctrl->miscdev);
+
+	return 0;
+}
+
+/*
+ * bit | SCU2C | ast2400
+ *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
+ *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
+ *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
+ *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
+ *
+ * bit | SCU2C | ast2500
+ *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
+ *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
+ *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
+ *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
+ */
+
+#define SCU2C_DRAM	BIT(25)
+#define SCU2C_SPI	BIT(24)
+#define SCU2C_SOC	BIT(23)
+#define SCU2C_FLASH	BIT(22)
+
+static const struct aspeed_p2a_model_data ast2400_model_data = {
+	.regions = {
+		{0x00000000, 0x17FFFFFF, SCU2C_FLASH},
+		{0x18000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
+		{0x30000000, 0x3FFFFFFF, SCU2C_SPI},
+		{0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
+		{0x60000000, 0xFFFFFFFF, SCU2C_SOC},
+	}
+};
+
+static const struct aspeed_p2a_model_data ast2500_model_data = {
+	.regions = {
+		{0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
+		{0x10000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
+		{0x40000000, 0x5FFFFFFF, SCU2C_SOC},
+		{0x60000000, 0x7FFFFFFF, SCU2C_SPI},
+		{0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
+	}
+};
+
+static const struct of_device_id aspeed_p2a_ctrl_match[] = {
+	{ .compatible = "aspeed,ast2400-p2a-ctrl",
+	  .data = &ast2400_model_data },
+	{ .compatible = "aspeed,ast2500-p2a-ctrl",
+	  .data = &ast2500_model_data },
+	{ },
+};
+
+static struct platform_driver aspeed_p2a_ctrl_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_p2a_ctrl_match,
+	},
+	.probe = aspeed_p2a_ctrl_probe,
+	.remove = aspeed_p2a_ctrl_remove,
+};
+
+module_platform_driver(aspeed_p2a_ctrl_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick Venture <venture@google.com>");
+MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");
diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h b/include/uapi/linux/aspeed-p2a-ctrl.h
new file mode 100644
index 000000000000..e839cdc31db9
--- /dev/null
+++ b/include/uapi/linux/aspeed-p2a-ctrl.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
+#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ASPEED_P2A_CTRL_READ_ONLY 0
+#define ASPEED_P2A_CTRL_READWRITE 1
+
+/*
+ * This driver provides a mechanism for enabling or disabling the read-write
+ * property of specific windows into the ASPEED BMC's memory.
+ *
+ * A user can map a region of the BMC's memory as read-only or read-write, with
+ * the caveat that once any region is mapped, all regions are unlocked for
+ * reading.
+ */
+
+/**
+ * Unlock a region of BMC physical memory for access from the host.
+ *
+ * Also used to read back the optional memory-region configuration for the
+ * driver.
+ */
+struct aspeed_p2a_ctrl_mapping {
+	__u32 addr;
+	__u32 length;
+	__u32 flags;
+};
+
+#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
+
+/**
+ * This IOCTL is meant to configure a region or regions of memory given a
+ * starting address and length to be readable by the host, or
+ * readable-writeable. */
+#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x00, struct aspeed_p2a_ctrl_mapping)
+
+/**
+ * This IOCTL is meant to read back to the user the base address and length of
+ * the memory-region specified to the driver for use with mmap. */
+#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x01, struct aspeed_p2a_ctrl_mapping)
+
+#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
-- 
2.21.0.rc2.261.ga7da99ff1b-goog


^ permalink raw reply related

* [PATCH v5 1/2] dt-bindings: misc: aspeed-p2a-ctrl: add support
From: Patrick Venture @ 2019-02-28  1:52 UTC (permalink / raw)
  To: linux-aspeed

Document the ast2400, ast2500 PCI-to-AHB bridge control driver bindings.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v5:
- None
Changes for v4:
- None
Changes for v3:
- None
Changes for v2:
- Added comment about syscon required parameter.
---
 .../bindings/misc/aspeed-p2a-ctrl.txt         | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt

diff --git a/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
new file mode 100644
index 000000000000..1092d62d1c92
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
@@ -0,0 +1,32 @@
+======================================================================
+Device tree bindings for Aspeed AST2400/AST2500 PCI-to-AHB Bridge Control Driver
+======================================================================
+
+The bridge is available on platforms with the VGA enabled on the Aspeed device.
+In this case, the host has access to a 64KiB window into all of the BMC's
+memory.  The BMC can disable this bridge.  If the bridge is enabled, the host
+has read access to all the regions of memory, however the host only has read
+and write access depending on a register controlled by the BMC.
+
+Required properties:
+===================
+
+ - compatible: must be one of:
+	- "aspeed,ast2400-p2a-ctrl"
+	- "aspeed,ast2500-p2a-ctrl"
+
+ - syscon: handle to syscon device node controlling PCI.
+
+Optional properties:
+===================
+
+- memory-region: A phandle to a reserved_memory region to be used for the PCI
+		to AHB mapping
+
+Example:
+
+p2a: p2a-control at 1e6e2000 {
+	compatible = "aspeed,ast2400-p2a-ctrl";
+	memory-region = <&reserved_memory>;
+	syscon = <&syscon>;
+};
-- 
2.21.0.rc2.261.ga7da99ff1b-goog


^ permalink raw reply related

* [PATCH v4 2/2] drivers/misc: Add Aspeed P2A control driver
From: Patrick Venture @ 2019-02-28  1:25 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20190227165901.144340-1-venture@google.com>

On Wed, Feb 27, 2019 at 8:59 AM Patrick Venture <venture@google.com> wrote:
>
> The ASPEED AST2400, and AST2500 in some configurations include a
> PCI-to-AHB MMIO bridge.  This bridge allows a server to read and write
> in the BMC's physical address space.  This feature is especially useful
> when using this bridge to send large files to the BMC.
>
> The host may use this to send down a firmware image by staging data at a
> specific memory address, and in a coordinated effort with the BMC's
> software stack and kernel, transmit the bytes.
>
> This driver enables the BMC to unlock the PCI bridge on demand, and
> configure it via ioctl to allow the host to write bytes to an agreed
> upon location.  In the primary use-case, the region to use is known
> apriori on the BMC, and the host requests this information.  Once this
> request is received, the BMC's software stack will enable the bridge and
> the region and then using some software flow control (possibly via IPMI
> packets), copy the bytes down.  Once the process is complete, the BMC
> will disable the bridge and unset any region involved.
>
> The default behavior of this bridge when present is: enabled and all
> regions marked read-write.  This driver will fix the regions to be
> read-only and then disable the bridge entirely.
>
> The memory regions protected are:
>  * BMC flash MMIO window
>  * System flash MMIO windows
>  * SOC IO (peripheral MMIO)
>  * DRAM
>
> The DRAM region itself is all of DRAM and cannot be further specified.
> Once the PCI bridge is enabled, the host can read all of DRAM, and if
> the DRAM section is write-enabled, then it can write to all of it.
>
> Signed-off-by: Patrick Venture <venture@google.com>
> ---
> Changes for v4:
> - Added ioctl for reading back the memory-region configuration.
> - Cleaned up some unused variables.
> Changes for v3:
> - Deleted unused read and write methods.
> Changes for v2:
> - Dropped unused reads.
> - Moved call to disable bridge to before registering device.
> - Switch from using regs to using a syscon regmap. <<< IN PROGRESS
> - Updated the commit message. <<< TODO
> - Updated the bit flipped for SCU180_ENP2A
> - Dropped boolean region_specified variable.
> - Renamed p2a_ctrl in _probe to misc_ctrl per suggestion.
> - Renamed aspeed_p2a_region_search to aspeed_p2a_region_acquire
> - Updated commit message.
> ---
>  drivers/misc/Kconfig                 |   8 +
>  drivers/misc/Makefile                |   1 +
>  drivers/misc/aspeed-p2a-ctrl.c       | 451 +++++++++++++++++++++++++++
>  include/uapi/linux/aspeed-p2a-ctrl.h |  59 ++++
>  4 files changed, 519 insertions(+)
>  create mode 100644 drivers/misc/aspeed-p2a-ctrl.c
>  create mode 100644 include/uapi/linux/aspeed-p2a-ctrl.h
>
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index f417b06e11c5..9de1bafe5606 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG
>           bus. System Configuration interface is one of the possible means
>           of generating transactions on this bus.
>
> +config ASPEED_P2A_CTRL
> +       depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
> +       tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
> +       help
> +         Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings through
> +         ioctl()s, the driver also provides an interface for userspace mappings to
> +         a pre-defined region.
> +
>  config ASPEED_LPC_CTRL
>         depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
>         tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index e39ccbbc1b3a..57577aee354f 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
>  obj-$(CONFIG_CXL_BASE)         += cxl/
>  obj-$(CONFIG_ASPEED_LPC_CTRL)  += aspeed-lpc-ctrl.o
>  obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
> +obj-$(CONFIG_ASPEED_P2A_CTRL)  += aspeed-p2a-ctrl.o
>  obj-$(CONFIG_PCI_ENDPOINT_TEST)        += pci_endpoint_test.o
>  obj-$(CONFIG_OCXL)             += ocxl/
>  obj-y                          += cardreader/
> diff --git a/drivers/misc/aspeed-p2a-ctrl.c b/drivers/misc/aspeed-p2a-ctrl.c
> new file mode 100644
> index 000000000000..bcf68f086e74
> --- /dev/null
> +++ b/drivers/misc/aspeed-p2a-ctrl.c
> @@ -0,0 +1,451 @@
> +/*
> + * Copyright 2019 Google Inc
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + *
> + * Provides a simple driver to control the ASPEED P2A interface which allows
> + * the host to read and write to various regions of the BMC's memory.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/aspeed-p2a-ctrl.h>
> +
> +#define DEVICE_NAME    "aspeed-p2a-ctrl"
> +
> +/* SCU2C is a Misc. Control Register. */
> +#define SCU2C 0x2c
> +/* SCU180 is the PCIe Configuration Setting Control Register. */
> +#define SCU180 0x180
> +/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device
> + * on the PCI bus. */
> +#define SCU180_ENP2A BIT(1)
> +
> +/* The ast2400/2500 both have six ranges. */
> +#define P2A_REGION_COUNT 6
> +
> +struct region {
> +       u32 min;
> +       u32 max;
> +       u32 bit;
> +};
> +
> +struct aspeed_p2a_model_data {
> +       /* min, max, bit */
> +       struct region regions[P2A_REGION_COUNT];
> +};
> +
> +struct aspeed_p2a_ctrl {
> +       struct miscdevice miscdev;
> +       struct regmap *regmap;
> +
> +       const struct aspeed_p2a_model_data *config;
> +
> +       /* Access to these needs to be locked, held via probe, mapping ioctl,
> +        * and release, remove.
> +        */
> +       struct mutex tracking;
> +       u32 readers;
> +       u32 readerwriters[P2A_REGION_COUNT];
> +
> +       phys_addr_t mem_base;
> +       resource_size_t mem_size;
> +};
> +
> +struct aspeed_p2a_user {
> +       struct file *file;
> +       struct aspeed_p2a_ctrl *parent;
> +
> +       /** The entire memory space is opened for reading once the bridge is
> +        * enabled, therefore this needs only to be tracked once per user.
> +        * If any user has it open for read, the bridge must stay enabled.
> +        */
> +       u32 read;
> +
> +       /** Each entry of the array corresponds to a P2A Region.  If the user
> +        * opens for read or readwrite, the reference goes up here.  On release,
> +        * this array is walked and references adjusted accordingly.
> +        */
> +       u32 readwrite[P2A_REGION_COUNT];
> +};
> +
> +static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +       regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, SCU180_ENP2A);
> +}
> +
> +static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +       regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
> +}
> +
> +static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +       struct aspeed_p2a_user *priv = file->private_data;
> +       struct aspeed_p2a_ctrl *ctrl = priv->parent;
> +
> +       if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
> +               return -EINVAL;
> +
> +       unsigned long vsize = vma->vm_end - vma->vm_start;
> +       pgprot_t prot = vma->vm_page_prot;
> +
> +       if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
> +               return -EINVAL;
> +
> +       /* ast2400/2500 AHB accesses are not cache coherent */
> +       prot = pgprot_noncached(prot);
> +
> +       if (remap_pfn_range(vma, vma->vm_start,
> +               (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
> +               vsize, prot))
> +               return -EAGAIN;
> +
> +       return 0;
> +}
> +
> +static void aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
> +               struct aspeed_p2a_ctrl *ctrl,
> +               struct aspeed_p2a_ctrl_mapping *map)
> +{
> +       int i;
> +       u32 base, end;
> +
> +       base = map->addr;
> +       end = map->addr + (map->length - 1);
> +
> +       /* If the value is a legal u32, it will find a match. */
> +       for (i = 0; i < P2A_REGION_COUNT; i++) {
> +               const struct region *curr = &ctrl->config->regions[i];
> +
> +               /* If the top of this region is lower than your base, skip it.
> +                */
> +               if (curr->max < base)
> +                       continue;
> +
> +               /* If the bottom of this region is higher than your end, bail.
> +                */
> +               if (curr->min > end)
> +                       break;
> +               /* Lock this and update it, therefore it someone else is
> +                * closing their file out, this'll preserve the increment.
> +                */
> +               mutex_lock(&ctrl->tracking);
> +               ctrl->readerwriters[i] += 1;
> +               mutex_unlock(&ctrl->tracking);
> +
> +               /* Track with the user, so when they close their file, we can
> +                * decrement properly.
> +                */
> +               priv->readwrite[i] += 1;
> +
> +               /* Enable the region as read-write. */
> +               regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
> +       }
> +}
> +
> +static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
> +               unsigned long data)
> +{
> +       struct aspeed_p2a_user *priv = file->private_data;
> +       struct aspeed_p2a_ctrl *ctrl = priv->parent;
> +       void __user *arg = (void __user *)data;
> +       struct aspeed_p2a_ctrl_mapping map;
> +
> +       if (copy_from_user(&map, arg, sizeof(map)))
> +               return -EFAULT;
> +
> +       switch (cmd) {
> +       case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
> +               /* If they want a region to be read-only, since the entire
> +                * region is read-only once enabled, we just need to track this
> +                * user wants to read from the bridge, and if it's not enabled.
> +                * Enable it.
> +                */
> +               if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
> +                       mutex_lock(&ctrl->tracking);
> +                       ctrl->readers += 1;
> +                       mutex_unlock(&ctrl->tracking);
> +
> +                       /* Track with the user, so when they close their file,
> +                        * we can decrement properly.
> +                        */
> +                       priv->read += 1;
> +               } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
> +                       aspeed_p2a_region_acquire(priv, ctrl, &map);
> +               } else {
> +                       /* Invalid map flags. */
> +                       return -EINVAL;
> +               }
> +
> +               aspeed_p2a_enable_bridge(ctrl);
> +               return 0;
> +       case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
> +               /* This is a request for the memory-region and corresponding
> +                * length that is used by the driver for mmap. */
> +
> +               map.flags = 0;
> +               map.addr = ctrl->mem_base;
> +               map.length = ctrl->mem_size;
> +
> +               return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +
> +/**
> + * When a user opens this file, we create a structure to track their mappings.
> + *
> + * A user can map a region as read-only (bridge enabled), or read-write (bit
> + * flipped, and bridge enabled).  Either way, this tracking is used, s.t. when
> + * they release the device references are handled.
> + *
> + * The bridge is not enabled until a user calls an ioctl to map a region,
> + * simply opening the device does not enable it.
> + */
> +static int aspeed_p2a_open(struct inode *inode, struct file *file)
> +{
> +       struct aspeed_p2a_user *priv;
> +
> +       priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       priv->file = file;
> +       priv->read = 0;
> +       memset(priv->readwrite, 0, sizeof(priv->readwrite));
> +
> +       /* The file's private_data is initialized to the p2a_ctrl. */
> +       priv->parent = file->private_data;
> +
> +       /* Set the file's private_data to the user's data. */
> +       file->private_data = priv;
> +
> +       return 0;
> +}
> +
> +/**
> + * This will close the users mappings.  It will go through what they had opened
> + * for readwrite, and decrement those counts.  If at the end, this is the last
> + * user, it'll close the bridge.
> + */
> +static int aspeed_p2a_release(struct inode *inode, struct file *file)
> +{
> +       int i;
> +       u32 value;
> +       u32 bits = 0;
> +       bool open_regions = false;
> +       struct aspeed_p2a_user *priv = file->private_data;
> +
> +       /* Lock others from changing these values until everything is updated
> +        * in one pass */
> +       mutex_lock(&priv->parent->tracking);
> +
> +       priv->parent->readers -= priv->read;;
> +
> +       for (i = 0; i < P2A_REGION_COUNT; i++) {
> +               priv->parent->readerwriters[i] -= priv->readwrite[i];
> +
> +               if (priv->parent->readerwriters[i] > 0)
> +                       open_regions = true;
> +               else
> +                       bits |= priv->parent->config->regions[i].bit;
> +       }
> +
> +       /* Setting a bit to 1 disables the region, so let's just OR with the
> +        * above to disable any.
> +        */
> +
> +       /* Note, if another user is trying to ioctl, they can't grab tracking,
> +        * and therefore can't grab either register mutex.
> +        * If another user is trying to close, they can't grab tracking either.
> +        */
> +       regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
> +
> +       /* If parent->readers is zero and open windows is 0, disable the
> +        * bridge.
> +        */
> +       if (!open_regions && priv->parent->readers == 0)
> +               aspeed_p2a_disable_bridge(priv->parent);
> +
> +       mutex_unlock(&priv->parent->tracking);
> +
> +       kfree(priv);
> +
> +       return 0;
> +}
> +
> +static const struct file_operations aspeed_p2a_ctrl_fops = {
> +       .owner = THIS_MODULE,
> +       .mmap = aspeed_p2a_mmap,
> +       .unlocked_ioctl = aspeed_p2a_ioctl,
> +       .open = aspeed_p2a_open,
> +       .release = aspeed_p2a_release,
> +};
> +
> +/** The regions are controlled by SCU2C */
> +static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
> +{
> +       int i;
> +       u32 value = 0;
> +
> +       for (i = 0; i < P2A_REGION_COUNT; i++)
> +               value |= p2a_ctrl->config->regions[i].bit;
> +
> +       regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
> +
> +       /* Disable the bridge. */
> +       aspeed_p2a_disable_bridge(p2a_ctrl);
> +}
> +
> +static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
> +{
> +       struct aspeed_p2a_ctrl *misc_ctrl;
> +       struct device *dev;
> +       struct resource *res, resm;
> +       struct device_node *node;
> +       int rc = 0;
> +
> +       dev = &pdev->dev;
> +
> +       misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
> +       if (!misc_ctrl)
> +               return -ENOMEM;
> +
> +       mutex_init(&misc_ctrl->tracking);
> +       misc_ctrl->readers = 0;
> +       memset(misc_ctrl->readerwriters, 0, sizeof(misc_ctrl->readerwriters));
> +
> +       misc_ctrl->mem_size = 0;
> +       misc_ctrl->mem_base = 0;
> +
> +       /* optional. */
> +       node = of_parse_phandle(dev->of_node, "memory-region", 0);
> +       if (node) {
> +               rc = of_address_to_resource(node, 0, &resm);
> +               of_node_put(node);
> +               if (rc) {
> +                       dev_err(dev, "Couldn't address to resource for reserved memory\n");
> +                       return -ENOMEM;
> +               }
> +
> +               misc_ctrl->mem_size = resource_size(&resm);
> +               misc_ctrl->mem_base = resm.start;
> +       }
> +       node = of_parse_phandle(dev->of_node, "syscon", 0);
> +       if (!node)
> +               dev_err(dev, "Couldn't find syscon property\n");

Need to exit on this point.

> +
> +       misc_ctrl->regmap = syscon_node_to_regmap(node);
> +       of_node_put(node);
> +
> +       misc_ctrl->config = of_device_get_match_data(dev);
> +
> +       dev_set_drvdata(&pdev->dev, misc_ctrl);
> +
> +       aspeed_p2a_disable_all(misc_ctrl);
> +
> +       misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> +       misc_ctrl->miscdev.name = DEVICE_NAME;
> +       misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
> +       misc_ctrl->miscdev.parent = dev;
> +       rc = misc_register(&misc_ctrl->miscdev);
> +       if (rc) {
> +               dev_err(dev, "Unable to register device\n");
> +               goto err;

No need to jump anymore since there's nothing to skip.

> +       }
> +
> +err:
> +       return rc;
> +}
> +
> +static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
> +{
> +       struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
> +
> +       misc_deregister(&p2a_ctrl->miscdev);
> +
> +       return 0;
> +}
> +
> +/*
> + * bit | SCU2C | ast2400
> + *  25 | DRAM  | 0x40000000 - 0x5FFFFFFF
> + *  24 | SPI   | 0x30000000 - 0x3FFFFFFF
> + *  23 | SOC   | 0x18000000 - 0x1FFFFFFF, 0x60000000 - 0xFFFFFFFF
> + *  22 | FLASH | 0x00000000 - 0x17FFFFFF, 0x20000000 - 0x2FFFFFFF
> + *
> + * bit | SCU2C | ast2500
> + *  25 | DRAM  | 0x80000000 - 0xFFFFFFFF
> + *  24 | SPI   | 0x60000000 - 0x7FFFFFFF
> + *  23 | SOC   | 0x10000000 - 0x1FFFFFFF, 0x40000000 - 0x5FFFFFFF
> + *  22 | FLASH | 0x00000000 - 0x0FFFFFFF, 0x20000000 - 0x3FFFFFFF
> + */
> +
> +#define SCU2C_DRAM     BIT(25)
> +#define SCU2C_SPI      BIT(24)
> +#define SCU2C_SOC      BIT(23)
> +#define SCU2C_FLASH    BIT(22)
> +
> +static const struct aspeed_p2a_model_data ast2400_model_data = {
> +       .regions = {
> +               {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
> +               {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
> +               {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
> +               {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
> +               {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
> +               {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
> +       }
> +};
> +
> +static const struct aspeed_p2a_model_data ast2500_model_data = {
> +       .regions = {
> +               {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
> +               {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
> +               {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
> +               {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
> +               {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
> +               {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
> +       }
> +};
> +
> +static const struct of_device_id aspeed_p2a_ctrl_match[] = {
> +       { .compatible = "aspeed,ast2400-p2a-ctrl",
> +         .data = &ast2400_model_data },
> +       { .compatible = "aspeed,ast2500-p2a-ctrl",
> +         .data = &ast2500_model_data },
> +       { },
> +};
> +
> +static struct platform_driver aspeed_p2a_ctrl_driver = {
> +       .driver = {
> +               .name           = DEVICE_NAME,
> +               .of_match_table = aspeed_p2a_ctrl_match,
> +       },
> +       .probe = aspeed_p2a_ctrl_probe,
> +       .remove = aspeed_p2a_ctrl_remove,
> +};
> +
> +module_platform_driver(aspeed_p2a_ctrl_driver);
> +
> +MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Patrick Venture <venture@google.com>");
> +MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");
> diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h b/include/uapi/linux/aspeed-p2a-ctrl.h
> new file mode 100644
> index 000000000000..e839cdc31db9
> --- /dev/null
> +++ b/include/uapi/linux/aspeed-p2a-ctrl.h
> @@ -0,0 +1,59 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright 2019 Google Inc
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + *
> + * Provides a simple driver to control the ASPEED P2A interface which allows
> + * the host to read and write to various regions of the BMC's memory.
> + */
> +
> +#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
> +#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +#define ASPEED_P2A_CTRL_READ_ONLY 0
> +#define ASPEED_P2A_CTRL_READWRITE 1
> +
> +/*
> + * This driver provides a mechanism for enabling or disabling the read-write
> + * property of specific windows into the ASPEED BMC's memory.
> + *
> + * A user can map a region of the BMC's memory as read-only or read-write, with
> + * the caveat that once any region is mapped, all regions are unlocked for
> + * reading.
> + */
> +
> +/**
> + * Unlock a region of BMC physical memory for access from the host.
> + *
> + * Also used to read back the optional memory-region configuration for the
> + * driver.
> + */
> +struct aspeed_p2a_ctrl_mapping {
> +       __u32 addr;
> +       __u32 length;
> +       __u32 flags;
> +};
> +
> +#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
> +
> +/**
> + * This IOCTL is meant to configure a region or regions of memory given a
> + * starting address and length to be readable by the host, or
> + * readable-writeable. */
> +#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> +               0x00, struct aspeed_p2a_ctrl_mapping)
> +
> +/**
> + * This IOCTL is meant to read back to the user the base address and length of
> + * the memory-region specified to the driver for use with mmap. */
> +#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG _IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
> +               0x01, struct aspeed_p2a_ctrl_mapping)
> +
> +#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
> --
> 2.21.0.rc2.261.ga7da99ff1b-goog
>

^ permalink raw reply


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