* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Oskar Senft @ 2019-07-29 2:21 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <9855284dfa4c4d4a52441553691c7b4b08a9cc34.camel@ozlabs.org>
Hi Jeremy
Somewhat offtopic, but are you sure you want to enable the SuperIO
> device?
>
No :-D I'm aware of CVE-2019-6260. I just listed it as a potential source
of SIRQs.
> > The VUART is covered by this code and we don't have a PUART driver
> > yet.
> >
> > It might make sense to have this as a global setting which each driver
> > could read. But wouldn't this be an exercise for the future where we
> > actually have a second device? I don't think the Aspeed currently has
> > any other devices that could generate a SIRQ (except for the PUART for
> > which there's no driver).
>
> We have a bunch of SIRQ sources that we use (on OpenPOWER platforms) -
> the BT interface, the KCS interface, the UARTs, and the mbox hardware.
> It's not just the VUART/PUART :)
>
Interesting. From what Aspeed explained, the SIRQ polarity for UARTs is
inverted to that for other devices. I haven't looked into how other devices
work, to be honest. Do we set the polarity there explicitly?
> > Having said that, ideally I'd like the SIRQ polarity to be auto-
> > configured from the LPC/eSPI HW pin strap anyway. I have the code for
> > that almost done. Maybe we shouldn't even have the sysfs interface for
> > it and I should strip that out?
>
> Yeah, I think I agree with that. The only downside is that the
> individual drivers will now need to poke at the SCU to query the
> LPC/eSPI configuration. If you can find a nice way to do that, then that
> sounds like the best approach. Can you query it through the parent bus
> perhaps?
>
I'm experimenting with this:
syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
I'll need to think on how to make this work on both ast2400 and ast2500,
though. I actually need to have a look at the ast2400 data sheet wrt. VUART
registers, too!
The structure is this:
apb {
syscon {
...
}
vuart {
...
}
}
So the vuart is a sibling of syscon, which holds the registers. I guess
I'll have to go with "by_compatible"?
Oskar.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linux-aspeed/attachments/20190728/9c5140c3/attachment.htm>
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Benjamin Herrenschmidt @ 2019-07-29 2:20 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <6b31627a8c5a2616c83783550517961b7ed8f3cb.camel@ozlabs.org>
On Mon, 2019-07-29 at 08:51 +0800, Jeremy Kerr wrote:
> Hi Oskar,
>
> > For LPC, the polarity should be set to 1, for eSPI the default of 0 is
> > correct.
>
> Would there ever be a case where different SIRQs need different
> polarities? If not, we may want this to be global, rather than for each
> device (the VUART being one...)
Leave it per device. The way to configure this is completely device
dependent. In fact for most devices it can only be done by the host via
SIO.
Cheers,
Ben.
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Benjamin Herrenschmidt @ 2019-07-29 2:19 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CABoTLcRPXffZuKy-DTFUVKJQRydSzMDynYRgBbZ_470iVN1KnA@mail.gmail.com>
On Sun, 2019-07-28 at 00:00 -0400, Oskar Senft wrote:
> I was thinking exactly the same thing (which is why I pointed it out
> in the change description). Thanks for picking up on that :-D
>
> I considered both options but decided to follow what's been done for
> the sirq and lpc_address settings, as sirq_polarity should really go
> together with the other two. I guess we could argue that the
> sirq_polarity likely always has to have a specific setting for the
> specific platform, whereas the sirq and the lpc_address might be
> configurable in some way from the host at runtime.
>
> Another reason I decided against DTS is that the properties currently
> read from DTS are "standard properties" from the 8250 driver. So I
> wasn't sure if it's ok to add a property that clearly specific to the
> 8250_aspeed_vuart driver.
>
> If anyone has strong feelings I can either change it from sysfs to
> DTS or add DTS on top - it's quite easy to do. Thoughts?
No strong feelings. Adding properties should be ok but on the other
hand, I agree with keeping things consistent with the other LPC
settings (address & irq#).
Cheers,
Ben.
>
> Thanks
> Oskar.
>
> On Sat, Jul 27, 2019 at 9:30 PM Benjamin Herrenschmidt <
> benh at kernel.crashing.org> wrote:
> > On Sat, 2019-07-27 at 09:42 -0400, Oskar Senft wrote:
> > > Make the SIRQ polarity for Aspeed 24xx/25xx VUART configurable
> > via
> > > sysfs. It is required on some host platforms (e.g. TYAN S7106) to
> > > reconfigure this setting from the default to enable the host to
> > > receive
> > > interrupts from the VUART.
> > >
> > > The setting is configurable via sysfs rather than device-tree to
> > stay
> > > in
> > > line with other related configurable settings.
> >
> > If it's a fixed platform configuration that never changes at
> > runtime,
> > shouldn't it be in the device-tree instead ?
> >
> > Cheers,
> > Ben
> >
> > > Tested: Verified on TYAN S7106 mainboard.
> > > Signed-off-by: Oskar Senft <osk@google.com>
> > > ---
> > > .../ABI/stable/sysfs-driver-aspeed-vuart | 10 ++++-
> > > drivers/tty/serial/8250/8250_aspeed_vuart.c | 39
> > > +++++++++++++++++++
> > > 2 files changed, 48 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > > b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > > index 8062953ce77b..64fad87ad964 100644
> > > --- a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > > +++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > > @@ -6,10 +6,18 @@ Description: Configures which IO port
> > the
> > > host side of the UART
> > > Users: OpenBMC. Proposed changes should be mailed
> > to
> > > openbmc at lists.ozlabs.org
> > >
> > > -What: /sys/bus/platform/drivers/aspeed-
> > vuart*/sirq
> > > +What: /sys/bus/platform/drivers/aspeed-
> > vuart/*/sirq
> > > Date: April 2017
> > > Contact: Jeremy Kerr <jk@ozlabs.org>
> > > Description: Configures which interrupt number the host side of
> > > the UART will appear on the host <-> BMC LPC bus.
> > > Users: OpenBMC. Proposed changes should be mailed
> > to
> > > openbmc at lists.ozlabs.org
> > > +
> > > +What: /sys/bus/platform/drivers/aspeed-
> > > vuart/*/sirq_polarity
> > > +Date: July 2019
> > > +Contact: Oskar Senft <osk@google.com>
> > > +Description: Configures the polarity of the serial interrupt to
> > the
> > > + host via the BMC LPC bus.
> > > +Users: OpenBMC. Proposed changes should be mailed
> > to
> > > + openbmc at lists.ozlabs.org
> > > diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > > b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > > index 0438d9a905ce..ef0a6ff69841 100644
> > > --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > > +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > > @@ -22,6 +22,7 @@
> > >
> > > #define ASPEED_VUART_GCRA 0x20
> > > #define ASPEED_VUART_GCRA_VUART_EN BIT(0)
> > > +#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1)
> > > #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
> > > #define ASPEED_VUART_GCRB 0x24
> > > #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
> > > @@ -131,8 +132,46 @@ static ssize_t sirq_store(struct device
> > *dev,
> > > struct device_attribute *attr,
> > >
> > > static DEVICE_ATTR_RW(sirq);
> > >
> > > +static ssize_t sirq_polarity_show(struct device *dev,
> > > + struct device_attribute *attr,
> > char
> > > *buf)
> > > +{
> > > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> > > + u8 reg;
> > > +
> > > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> > > + reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > > +
> > > + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0);
> > > +}
> > > +
> > > +static ssize_t sirq_polarity_store(struct device *dev,
> > > + struct device_attribute *attr,
> > > + const char *buf, size_t count)
> > > +{
> > > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> > > + unsigned long val;
> > > + int err;
> > > + u8 reg;
> > > +
> > > + err = kstrtoul(buf, 0, &val);
> > > + if (err)
> > > + return err;
> > > +
> > > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> > > + if (val != 0)
> > > + reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > > + else
> > > + reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > > + writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
> > > +
> > > + return count;
> > > +}
> > > +
> > > +static DEVICE_ATTR_RW(sirq_polarity);
> > > +
> > > static struct attribute *aspeed_vuart_attrs[] = {
> > > &dev_attr_sirq.attr,
> > > + &dev_attr_sirq_polarity.attr,
> > > &dev_attr_lpc_address.attr,
> > > NULL,
> > > };
> >
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Jeremy Kerr @ 2019-07-29 2:02 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CABoTLcQF4_He15F5oO0Pxjdm6f7pLCxBF77cz7wR=jjWaednYQ@mail.gmail.com>
Hi Oskar,
> In my understanding the SIRQ polarity should be the same for all UARTs
> on the particular bus, i.e. both UARTs controlled by the SuperI/O and
> the VUART or PUART (pass-through). However, the host controls the
> UARTs on the SuperI/O itself.
Somewhat offtopic, but are you sure you want to enable the SuperIO
device?
> The VUART is covered by this code and we don't have a PUART driver
> yet.
>
> It might make sense to have this as a global setting which each driver
> could read. But wouldn't this be an exercise for the future where we
> actually have a second device? I don't think the Aspeed currently has
> any other devices that could generate a SIRQ (except for the PUART for
> which there's no driver).
We have a bunch of SIRQ sources that we use (on OpenPOWER platforms) -
the BT interface, the KCS interface, the UARTs, and the mbox hardware.
It's not just the VUART/PUART :)
> Having said that, ideally I'd like the SIRQ polarity to be auto-
> configured from the LPC/eSPI HW pin strap anyway. I have the code for
> that almost done. Maybe we shouldn't even have the sysfs interface for
> it and I should strip that out?
Yeah, I think I agree with that. The only downside is that the
individual drivers will now need to poke at the SCU to query the
LPC/eSPI configuration. If you can find a nice way to do that, then that
sounds like the best approach. Can you query it through the parent bus
perhaps?
Cheers,
Jeremy
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Oskar Senft @ 2019-07-29 1:14 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <6b31627a8c5a2616c83783550517961b7ed8f3cb.camel@ozlabs.org>
Hi Jeremy
In my understanding the SIRQ polarity should be the same for all UARTs on
the particular bus, i.e. both UARTs controlled by the SuperI/O and the
VUART or PUART (pass-through). However, the host controls the UARTs on the
SuperI/O itself. The VUART is covered by this code and we don't have a
PUART driver yet.
It might make sense to have this as a global setting which each driver
could read. But wouldn't this be an exercise for the future where we
actually have a second device? I don't think the Aspeed currently has any
other devices that could generate a SIRQ (except for the PUART for which
there's no driver).
Having said that, ideally I'd like the SIRQ polarity to be auto-configured
from the LPC/eSPI HW pin strap anyway. I have the code for that almost
done. Maybe we shouldn't even have the sysfs interface for it and I should
strip that out?
Thanks
Oskar.
On Sun, Jul 28, 2019 at 8:51 PM Jeremy Kerr <jk@ozlabs.org> wrote:
> Hi Oskar,
>
> > For LPC, the polarity should be set to 1, for eSPI the default of 0 is
> > correct.
>
> Would there ever be a case where different SIRQs need different
> polarities? If not, we may want this to be global, rather than for each
> device (the VUART being one...)
>
> Cheers,
>
>
> Jeremy
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linux-aspeed/attachments/20190728/b73f494d/attachment.htm>
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Jeremy Kerr @ 2019-07-29 0:51 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CABoTLcTSFvQruPRnoRPgiWjOXNDOCSnAXVesG7f1WAuLLLVSSg@mail.gmail.com>
Hi Oskar,
> For LPC, the polarity should be set to 1, for eSPI the default of 0 is
> correct.
Would there ever be a case where different SIRQs need different
polarities? If not, we may want this to be global, rather than for each
device (the VUART being one...)
Cheers,
Jeremy
^ permalink raw reply
* [v5 1/2] dt-bindings: gpio: aspeed: Add SGPIO support
From: Andrew Jeffery @ 2019-07-29 0:19 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CACRpkdZxsF9gQj0VicVLsPKXg6rKA1mLwbywmazOf0w8PLnOfA@mail.gmail.com>
On Mon, 29 Jul 2019, at 09:04, Linus Walleij wrote:
> On Mon, Jul 22, 2019 at 3:42 AM Andrew Jeffery <andrew@aj.id.au> wrote:
>
> > If the clock driver owns the control register, it also needs to know how
> > many GPIOs we want to emit on the bus. This seems like an awkward
> > configuration parameter for a clock driver.
> >
> > To avoid the weird parameter we could protect the control register
> > with a lock shared between the clock driver and the SGPIO driver. This
> > way the SGPIO driver could have the ngpios parameter, and request
> > the clock after its written the ngpios value to the control register. A
> > regmap would be useful here to avoid the resource clash and it also
> > provides the required lock.
>
> Nah. Too complicated.
>
> What about using the clock API locally (in the singleton driver,
> much as it is today) though, to give the right abstraction?
>
> See
> drivers/gpu/drm/pl111/pl111_display.c
> pl111_init_clock_divider() for an example of a local
> clock.
Thanks, I'll take a look at that.
>
> > However, that aside, we can't simply enable the bus in the clock
> > enable callback if enable is called per-bank, as it is called once on
> > the first request with further requests simply refcounted as you
> > mentioned. This is exactly the behaviour we can't tolerate with the
> > bus: it must only be enabled after the last GPIO bank is registered,
> > when we know the total number of GPIOs to emit.
>
> So the bus needs to know the total number of GPIOs or
> everything breaks, and that is the blocker for this
> divide-and-conquer approach.
>
> Why does the bus need to know the total number of GPIOs?
>
> (Maybe the answer is elsewhere in the thread...)
I didn't answer it explicitly, my apologies.
The behaviour is to periodically emit the state of all enabled GPIOs
(i.e. the ngpios value), one per bus clock cycle. There's no explicit
addressing scheme, the protocol encodes the value for a given GPIO
by its position in the data stream relative to a pulse on the "load data"
(LD) line, whose envelope covers the clock cycle for the last GPIO in
the sequence. Similar to SPI the bus has both out and in lines, which
cater to output/input GPIOs.
A rough timing diagram for a 16-GPIO configuration looks like what
I've pasted here:
https://gist.github.com/amboar/c9543af1957854474b8c05ab357f0675
Hope that helps.
Andrew
^ permalink raw reply
* [PATCH 0/6] pinctrl: aspeed: Add AST2600 pinmux support
From: Andrew Jeffery @ 2019-07-28 23:41 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CACRpkdb4pEdPHbo=3+fJXe9WG8K7A2_xVMtKWCJCfEawDO5wBw@mail.gmail.com>
On Mon, 29 Jul 2019, at 08:44, Linus Walleij wrote:
> On Thu, Jul 11, 2019 at 6:19 AM Andrew Jeffery <andrew@aj.id.au> wrote:
>
> > This series adds pinmux support for the AST2600. Some more rework was required
> > on top of the previous cleanup series, but this rework was focussed on
> > supporting features of the AST2600 pinmux rather than fixing issues with the
> > existing infrastructure for the ASPEED drivers. Due to the dependences it's
> > based on top of pinctrl/devel, so should avoid any more SPDX issues.
> >
> > ASPEED have been testing the patches on hardware, so even for an initial pass
> > there's some confidence in the implementation.
>
> I'm unsure if I need to wait for the DT bindings to be fixed on this
> series?
Yeah, I need to, sorry for the delay. Been distracted by other stuff.
Will send a v2 shortly.
Andrew
^ permalink raw reply
* [v5 2/2] gpio: aspeed: Add SGPIO driver
From: Linus Walleij @ 2019-07-28 23:37 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1563827815-15092-1-git-send-email-hongweiz@ami.com>
On Mon, Jul 22, 2019 at 10:37 PM Hongwei Zhang <hongweiz@ami.com> wrote:
> As you suspected it correctly, AST2500 utilizes all the 32 bits of the registers
> (data value, interrupt, etc...), such that using 8-bit bands
> [7:0]/[15:8]/23:16]/[31:24] of GPIO_200H for SGPIO_A/B/C/D .
> so registering 10 gpiochip drivers separately will make code more
> complicated, for example gpio_200 register (data_value reg) has to be
> shared by 4 gpiochip instances, and the same is true for gpio204 (interrupt reg),
> and other more registers.
> So we would prefer to keeping current implementation.
OK this is a pretty good argument. My review assumed one
32-bit register was not shared between banks but it is,
I see.
The above situation can be managed by regmap, but that will
just a different complexity so go with this approach then.
Yours,
Linus Walleij
^ permalink raw reply
* [v5 1/2] dt-bindings: gpio: aspeed: Add SGPIO support
From: Linus Walleij @ 2019-07-28 23:34 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <ef9d9c17-6e2d-4a4e-ac44-f8da4bb3b8eb@www.fastmail.com>
On Mon, Jul 22, 2019 at 3:42 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> If the clock driver owns the control register, it also needs to know how
> many GPIOs we want to emit on the bus. This seems like an awkward
> configuration parameter for a clock driver.
>
> To avoid the weird parameter we could protect the control register
> with a lock shared between the clock driver and the SGPIO driver. This
> way the SGPIO driver could have the ngpios parameter, and request
> the clock after its written the ngpios value to the control register. A
> regmap would be useful here to avoid the resource clash and it also
> provides the required lock.
Nah. Too complicated.
What about using the clock API locally (in the singleton driver,
much as it is today) though, to give the right abstraction?
See
drivers/gpu/drm/pl111/pl111_display.c
pl111_init_clock_divider() for an example of a local
clock.
> However, you're also going down the path of splitting the driver such
> that there's one instance per bank. With this approach we need to
> solve two problems: Accounting for the total number of GPIOs,
I don't see that as a big problem since each driver instance will
handle 8 GPIOs and don't need to know how many the
other instances have and whether they exist or not.
> and
> only enabling the bus after the last bank has had its driver probed.
That is a bigger problem and a good reason to stick with
some complex driver like this.
> The accounting might be handled by accumulating the number of
> GPIOs in each bank in the control register itself, e.g. the driver
> implementation does something like:
>
> spin_lock(...)
> ctrl = ioread32(...)
> ngpios = FIELD_GET(ASPEED_SGPIO_CTRL_NGPIOS, ctrl);
> ngpios += 8;
> ctrl &= ~ASPEED_SGPIO_CTRL_NGPIOS;
> ctrl |= FIELD_PREP(ASPEED_SGPIO_CTRL_NGPIOS, ngpios);
> iowrite32(ctrl, ...);
> spin_unlock(...);
But why. The gpio_chip only knows the ngpios for its own instance.
It has no business knowing about how many gpios are on the
other chips or not. If this is split across several instances this should
not be accounted that is the point.
> This works on cold boot of the device when the ngpios field is set to
> zero due to reset, however will fail on subsequent warm reboots if
> the GPIO IP block is protected from reset by the SoC's watchdog
> configuration: the field will not be zeroed in this case, and the
> value of the field is represented by `NR_BOOTS * NR_GPIOS`,
> which is incorrect. To do this correctly I guess we would need some
> other global state held in the driver implementation (zeroed when
> the kernel is loaded), and write the incremented value to the control
> register on each probe() invocation.
This is answered about I guess.
> However, that aside, we can't simply enable the bus in the clock
> enable callback if enable is called per-bank, as it is called once on
> the first request with further requests simply refcounted as you
> mentioned. This is exactly the behaviour we can't tolerate with the
> bus: it must only be enabled after the last GPIO bank is registered,
> when we know the total number of GPIOs to emit.
So the bus needs to know the total number of GPIOs or
everything breaks, and that is the blocker for this
divide-and-conquer approach.
Why does the bus need to know the total number of GPIOs?
(Maybe the answer is elsewhere in the thread...)
I guess I will accept it if it is really this complex in the
hardware.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 0/6] pinctrl: aspeed: Add AST2600 pinmux support
From: Linus Walleij @ 2019-07-28 23:14 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190711041942.23202-1-andrew@aj.id.au>
On Thu, Jul 11, 2019 at 6:19 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> This series adds pinmux support for the AST2600. Some more rework was required
> on top of the previous cleanup series, but this rework was focussed on
> supporting features of the AST2600 pinmux rather than fixing issues with the
> existing infrastructure for the ASPEED drivers. Due to the dependences it's
> based on top of pinctrl/devel, so should avoid any more SPDX issues.
>
> ASPEED have been testing the patches on hardware, so even for an initial pass
> there's some confidence in the implementation.
I'm unsure if I need to wait for the DT bindings to be fixed on this
series?
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] pinctrl: aspeed-g5: Delay acquisition of regmaps
From: Linus Walleij @ 2019-07-28 22:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190724080155.12209-1-andrew@aj.id.au>
On Wed, Jul 24, 2019 at 10:02 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> While sorting out some devicetree issues I found that the pinctrl driver
> was failing to acquire its GFX regmap even though the phandle was
> present in the devicetree:
>
> [ 0.124190] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: No GFX phandle found, some mux configurations may fail
>
> Without access to the GFX regmap we fail to configure the mux for the
> VPO function:
>
> [ 1.548866] pinctrl core: add 1 pinctrl maps
> [ 1.549826] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: found group selector 164 for VPO
> [ 1.550638] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: request pin 144 (V20) for 1e6e6000.display
> [ 1.551346] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: request pin 145 (U19) for 1e6e6000.display
> ...
> [ 1.562057] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: request pin 218 (T22) for 1e6e6000.display
> [ 1.562541] aspeed-g5-pinctrl 1e6e2000.syscon:pinctrl: request pin 219 (R20) for 1e6e6000.display
> [ 1.563113] Muxing pin 144 for VPO
> [ 1.563456] Want SCU8C[0x00000001]=0x1, got 0x0 from 0x00000000
> [ 1.564624] aspeed_gfx 1e6e6000.display: Error applying setting, reverse things back
>
> This turned out to be a simple problem of timing: The ASPEED pinctrl
> driver is probed during arch_initcall(), while GFX is processed much
> later. As such the GFX syscon is not yet registered during the pinctrl
> probe() and we get an -EPROBE_DEFER when we try to look it up, however
> we must not defer probing the pinctrl driver for the inability to mux
> some GFX-related functions.
>
> Switch to lazily grabbing the regmaps when they're first required by the
> mux configuration. This generates a bit of noise in the patch as we have
> to drop the `const` qualifier on arguments for several function
> prototypes, but has the benefit of working.
>
> I've smoke tested this for the ast2500-evb under qemu with a dummy
> graphics device. We now succeed in our attempts to configure the SoC's
> VPO pinmux function.
>
> Fixes: 7d29ed88acbb ("pinctrl: aspeed: Read and write bits in LPC and GFX controllers")
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Patch applied for fixes. Good rootcausing!
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Oskar Senft @ 2019-07-28 17:23 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CABoTLcRPXffZuKy-DTFUVKJQRydSzMDynYRgBbZ_470iVN1KnA@mail.gmail.com>
Update: I just learned from Aspeed that the SIRQ polarity is actually
dependent on the interface mode, LPC vs. eSPI.
For LPC, the polarity should be set to 1, for eSPI the default of 0 is
correct.
I'll amend the patch to read the interface mode from the HW pin strap
register and day the default accordingly at driver load time.
If there are no objections, I'll leave the sysfs part in there in case it
does need to be changed.
Oskar.
On Sun, Jul 28, 2019, 12:00 AM Oskar Senft <osk@google.com> wrote:
> I was thinking exactly the same thing (which is why I pointed it out in
> the change description). Thanks for picking up on that :-D
>
> I considered both options but decided to follow what's been done for the
> sirq and lpc_address settings, as sirq_polarity should really go together
> with the other two. I guess we could argue that the sirq_polarity likely
> always has to have a specific setting for the specific platform, whereas
> the sirq and the lpc_address might be configurable in some way from the
> host at runtime.
>
> Another reason I decided against DTS is that the properties currently read
> from DTS are "standard properties" from the 8250 driver. So I wasn't sure
> if it's ok to add a property that clearly specific to the 8250_aspeed_vuart
> driver.
>
> If anyone has strong feelings I can either change it from sysfs to DTS or
> add DTS on top - it's quite easy to do. Thoughts?
>
> Thanks
> Oskar.
>
> On Sat, Jul 27, 2019 at 9:30 PM Benjamin Herrenschmidt <
> benh at kernel.crashing.org> wrote:
>
>> On Sat, 2019-07-27 at 09:42 -0400, Oskar Senft wrote:
>> > Make the SIRQ polarity for Aspeed 24xx/25xx VUART configurable via
>> > sysfs. It is required on some host platforms (e.g. TYAN S7106) to
>> > reconfigure this setting from the default to enable the host to
>> > receive
>> > interrupts from the VUART.
>> >
>> > The setting is configurable via sysfs rather than device-tree to stay
>> > in
>> > line with other related configurable settings.
>>
>> If it's a fixed platform configuration that never changes at runtime,
>> shouldn't it be in the device-tree instead ?
>>
>> Cheers,
>> Ben
>>
>> > Tested: Verified on TYAN S7106 mainboard.
>> > Signed-off-by: Oskar Senft <osk@google.com>
>> > ---
>> > .../ABI/stable/sysfs-driver-aspeed-vuart | 10 ++++-
>> > drivers/tty/serial/8250/8250_aspeed_vuart.c | 39
>> > +++++++++++++++++++
>> > 2 files changed, 48 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
>> > b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
>> > index 8062953ce77b..64fad87ad964 100644
>> > --- a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
>> > +++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
>> > @@ -6,10 +6,18 @@ Description: Configures which IO port the
>> > host side of the UART
>> > Users: OpenBMC. Proposed changes should be mailed to
>> > openbmc at lists.ozlabs.org
>> >
>> > -What: /sys/bus/platform/drivers/aspeed-vuart*/sirq
>> > +What: /sys/bus/platform/drivers/aspeed-vuart/*/sirq
>> > Date: April 2017
>> > Contact: Jeremy Kerr <jk@ozlabs.org>
>> > Description: Configures which interrupt number the host side of
>> > the UART will appear on the host <-> BMC LPC bus.
>> > Users: OpenBMC. Proposed changes should be mailed to
>> > openbmc at lists.ozlabs.org
>> > +
>> > +What: /sys/bus/platform/drivers/aspeed-
>> > vuart/*/sirq_polarity
>> > +Date: July 2019
>> > +Contact: Oskar Senft <osk@google.com>
>> > +Description: Configures the polarity of the serial interrupt to the
>> > + host via the BMC LPC bus.
>> > +Users: OpenBMC. Proposed changes should be mailed to
>> > + openbmc at lists.ozlabs.org
>> > diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c
>> > b/drivers/tty/serial/8250/8250_aspeed_vuart.c
>> > index 0438d9a905ce..ef0a6ff69841 100644
>> > --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
>> > +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
>> > @@ -22,6 +22,7 @@
>> >
>> > #define ASPEED_VUART_GCRA 0x20
>> > #define ASPEED_VUART_GCRA_VUART_EN BIT(0)
>> > +#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1)
>> > #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
>> > #define ASPEED_VUART_GCRB 0x24
>> > #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
>> > @@ -131,8 +132,46 @@ static ssize_t sirq_store(struct device *dev,
>> > struct device_attribute *attr,
>> >
>> > static DEVICE_ATTR_RW(sirq);
>> >
>> > +static ssize_t sirq_polarity_show(struct device *dev,
>> > + struct device_attribute *attr, char
>> > *buf)
>> > +{
>> > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
>> > + u8 reg;
>> > +
>> > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
>> > + reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
>> > +
>> > + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0);
>> > +}
>> > +
>> > +static ssize_t sirq_polarity_store(struct device *dev,
>> > + struct device_attribute *attr,
>> > + const char *buf, size_t count)
>> > +{
>> > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
>> > + unsigned long val;
>> > + int err;
>> > + u8 reg;
>> > +
>> > + err = kstrtoul(buf, 0, &val);
>> > + if (err)
>> > + return err;
>> > +
>> > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
>> > + if (val != 0)
>> > + reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
>> > + else
>> > + reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
>> > + writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
>> > +
>> > + return count;
>> > +}
>> > +
>> > +static DEVICE_ATTR_RW(sirq_polarity);
>> > +
>> > static struct attribute *aspeed_vuart_attrs[] = {
>> > &dev_attr_sirq.attr,
>> > + &dev_attr_sirq_polarity.attr,
>> > &dev_attr_lpc_address.attr,
>> > NULL,
>> > };
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linux-aspeed/attachments/20190728/cd289cb0/attachment-0001.htm>
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Oskar Senft @ 2019-07-28 4:00 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <9fb8c99449cebd68fb5975890dedaa06ce7808ae.camel@kernel.crashing.org>
I was thinking exactly the same thing (which is why I pointed it out in the
change description). Thanks for picking up on that :-D
I considered both options but decided to follow what's been done for the
sirq and lpc_address settings, as sirq_polarity should really go together
with the other two. I guess we could argue that the sirq_polarity likely
always has to have a specific setting for the specific platform, whereas
the sirq and the lpc_address might be configurable in some way from the
host at runtime.
Another reason I decided against DTS is that the properties currently read
from DTS are "standard properties" from the 8250 driver. So I wasn't sure
if it's ok to add a property that clearly specific to the 8250_aspeed_vuart
driver.
If anyone has strong feelings I can either change it from sysfs to DTS or
add DTS on top - it's quite easy to do. Thoughts?
Thanks
Oskar.
On Sat, Jul 27, 2019 at 9:30 PM Benjamin Herrenschmidt <
benh@kernel.crashing.org> wrote:
> On Sat, 2019-07-27 at 09:42 -0400, Oskar Senft wrote:
> > Make the SIRQ polarity for Aspeed 24xx/25xx VUART configurable via
> > sysfs. It is required on some host platforms (e.g. TYAN S7106) to
> > reconfigure this setting from the default to enable the host to
> > receive
> > interrupts from the VUART.
> >
> > The setting is configurable via sysfs rather than device-tree to stay
> > in
> > line with other related configurable settings.
>
> If it's a fixed platform configuration that never changes at runtime,
> shouldn't it be in the device-tree instead ?
>
> Cheers,
> Ben
>
> > Tested: Verified on TYAN S7106 mainboard.
> > Signed-off-by: Oskar Senft <osk@google.com>
> > ---
> > .../ABI/stable/sysfs-driver-aspeed-vuart | 10 ++++-
> > drivers/tty/serial/8250/8250_aspeed_vuart.c | 39
> > +++++++++++++++++++
> > 2 files changed, 48 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > index 8062953ce77b..64fad87ad964 100644
> > --- a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > +++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> > @@ -6,10 +6,18 @@ Description: Configures which IO port the
> > host side of the UART
> > Users: OpenBMC. Proposed changes should be mailed to
> > openbmc at lists.ozlabs.org
> >
> > -What: /sys/bus/platform/drivers/aspeed-vuart*/sirq
> > +What: /sys/bus/platform/drivers/aspeed-vuart/*/sirq
> > Date: April 2017
> > Contact: Jeremy Kerr <jk@ozlabs.org>
> > Description: Configures which interrupt number the host side of
> > the UART will appear on the host <-> BMC LPC bus.
> > Users: OpenBMC. Proposed changes should be mailed to
> > openbmc at lists.ozlabs.org
> > +
> > +What: /sys/bus/platform/drivers/aspeed-
> > vuart/*/sirq_polarity
> > +Date: July 2019
> > +Contact: Oskar Senft <osk@google.com>
> > +Description: Configures the polarity of the serial interrupt to the
> > + host via the BMC LPC bus.
> > +Users: OpenBMC. Proposed changes should be mailed to
> > + openbmc at lists.ozlabs.org
> > diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > index 0438d9a905ce..ef0a6ff69841 100644
> > --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> > @@ -22,6 +22,7 @@
> >
> > #define ASPEED_VUART_GCRA 0x20
> > #define ASPEED_VUART_GCRA_VUART_EN BIT(0)
> > +#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1)
> > #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
> > #define ASPEED_VUART_GCRB 0x24
> > #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
> > @@ -131,8 +132,46 @@ static ssize_t sirq_store(struct device *dev,
> > struct device_attribute *attr,
> >
> > static DEVICE_ATTR_RW(sirq);
> >
> > +static ssize_t sirq_polarity_show(struct device *dev,
> > + struct device_attribute *attr, char
> > *buf)
> > +{
> > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> > + u8 reg;
> > +
> > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> > + reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > +
> > + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0);
> > +}
> > +
> > +static ssize_t sirq_polarity_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> > + unsigned long val;
> > + int err;
> > + u8 reg;
> > +
> > + err = kstrtoul(buf, 0, &val);
> > + if (err)
> > + return err;
> > +
> > + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> > + if (val != 0)
> > + reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > + else
> > + reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> > + writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
> > +
> > + return count;
> > +}
> > +
> > +static DEVICE_ATTR_RW(sirq_polarity);
> > +
> > static struct attribute *aspeed_vuart_attrs[] = {
> > &dev_attr_sirq.attr,
> > + &dev_attr_sirq_polarity.attr,
> > &dev_attr_lpc_address.attr,
> > NULL,
> > };
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linux-aspeed/attachments/20190728/31e179ee/attachment.htm>
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Benjamin Herrenschmidt @ 2019-07-28 1:30 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190727134242.49847-1-osk@google.com>
On Sat, 2019-07-27 at 09:42 -0400, Oskar Senft wrote:
> Make the SIRQ polarity for Aspeed 24xx/25xx VUART configurable via
> sysfs. It is required on some host platforms (e.g. TYAN S7106) to
> reconfigure this setting from the default to enable the host to
> receive
> interrupts from the VUART.
>
> The setting is configurable via sysfs rather than device-tree to stay
> in
> line with other related configurable settings.
If it's a fixed platform configuration that never changes at runtime,
shouldn't it be in the device-tree instead ?
Cheers,
Ben
> Tested: Verified on TYAN S7106 mainboard.
> Signed-off-by: Oskar Senft <osk@google.com>
> ---
> .../ABI/stable/sysfs-driver-aspeed-vuart | 10 ++++-
> drivers/tty/serial/8250/8250_aspeed_vuart.c | 39
> +++++++++++++++++++
> 2 files changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> index 8062953ce77b..64fad87ad964 100644
> --- a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> +++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
> @@ -6,10 +6,18 @@ Description: Configures which IO port the
> host side of the UART
> Users: OpenBMC. Proposed changes should be mailed to
> openbmc at lists.ozlabs.org
>
> -What: /sys/bus/platform/drivers/aspeed-vuart*/sirq
> +What: /sys/bus/platform/drivers/aspeed-vuart/*/sirq
> Date: April 2017
> Contact: Jeremy Kerr <jk@ozlabs.org>
> Description: Configures which interrupt number the host side of
> the UART will appear on the host <-> BMC LPC bus.
> Users: OpenBMC. Proposed changes should be mailed to
> openbmc at lists.ozlabs.org
> +
> +What: /sys/bus/platform/drivers/aspeed-
> vuart/*/sirq_polarity
> +Date: July 2019
> +Contact: Oskar Senft <osk@google.com>
> +Description: Configures the polarity of the serial interrupt to the
> + host via the BMC LPC bus.
> +Users: OpenBMC. Proposed changes should be mailed to
> + openbmc at lists.ozlabs.org
> diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> index 0438d9a905ce..ef0a6ff69841 100644
> --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
> +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
> @@ -22,6 +22,7 @@
>
> #define ASPEED_VUART_GCRA 0x20
> #define ASPEED_VUART_GCRA_VUART_EN BIT(0)
> +#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1)
> #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
> #define ASPEED_VUART_GCRB 0x24
> #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
> @@ -131,8 +132,46 @@ static ssize_t sirq_store(struct device *dev,
> struct device_attribute *attr,
>
> static DEVICE_ATTR_RW(sirq);
>
> +static ssize_t sirq_polarity_show(struct device *dev,
> + struct device_attribute *attr, char
> *buf)
> +{
> + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> + u8 reg;
> +
> + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> + reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0);
> +}
> +
> +static ssize_t sirq_polarity_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct aspeed_vuart *vuart = dev_get_drvdata(dev);
> + unsigned long val;
> + int err;
> + u8 reg;
> +
> + err = kstrtoul(buf, 0, &val);
> + if (err)
> + return err;
> +
> + reg = readb(vuart->regs + ASPEED_VUART_GCRA);
> + if (val != 0)
> + reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> + else
> + reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
> + writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR_RW(sirq_polarity);
> +
> static struct attribute *aspeed_vuart_attrs[] = {
> &dev_attr_sirq.attr,
> + &dev_attr_sirq_polarity.attr,
> &dev_attr_lpc_address.attr,
> NULL,
> };
^ permalink raw reply
* [PATCH] drivers/tty/serial/8250: Make Aspeed VUART SIRQ polarity configurable
From: Oskar Senft @ 2019-07-27 13:42 UTC (permalink / raw)
To: linux-aspeed
Make the SIRQ polarity for Aspeed 24xx/25xx VUART configurable via
sysfs. It is required on some host platforms (e.g. TYAN S7106) to
reconfigure this setting from the default to enable the host to receive
interrupts from the VUART.
The setting is configurable via sysfs rather than device-tree to stay in
line with other related configurable settings.
Tested: Verified on TYAN S7106 mainboard.
Signed-off-by: Oskar Senft <osk@google.com>
---
.../ABI/stable/sysfs-driver-aspeed-vuart | 10 ++++-
drivers/tty/serial/8250/8250_aspeed_vuart.c | 39 +++++++++++++++++++
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
index 8062953ce77b..64fad87ad964 100644
--- a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
+++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
@@ -6,10 +6,18 @@ Description: Configures which IO port the host side of the UART
Users: OpenBMC. Proposed changes should be mailed to
openbmc at lists.ozlabs.org
-What: /sys/bus/platform/drivers/aspeed-vuart*/sirq
+What: /sys/bus/platform/drivers/aspeed-vuart/*/sirq
Date: April 2017
Contact: Jeremy Kerr <jk@ozlabs.org>
Description: Configures which interrupt number the host side of
the UART will appear on the host <-> BMC LPC bus.
Users: OpenBMC. Proposed changes should be mailed to
openbmc at lists.ozlabs.org
+
+What: /sys/bus/platform/drivers/aspeed-vuart/*/sirq_polarity
+Date: July 2019
+Contact: Oskar Senft <osk@google.com>
+Description: Configures the polarity of the serial interrupt to the
+ host via the BMC LPC bus.
+Users: OpenBMC. Proposed changes should be mailed to
+ openbmc at lists.ozlabs.org
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
index 0438d9a905ce..ef0a6ff69841 100644
--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -22,6 +22,7 @@
#define ASPEED_VUART_GCRA 0x20
#define ASPEED_VUART_GCRA_VUART_EN BIT(0)
+#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1)
#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
#define ASPEED_VUART_GCRB 0x24
#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
@@ -131,8 +132,46 @@ static ssize_t sirq_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(sirq);
+static ssize_t sirq_polarity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+ reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0);
+}
+
+static ssize_t sirq_polarity_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+ if (val != 0)
+ reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
+ else
+ reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(sirq_polarity);
+
static struct attribute *aspeed_vuart_attrs[] = {
&dev_attr_sirq.attr,
+ &dev_attr_sirq_polarity.attr,
&dev_attr_lpc_address.attr,
NULL,
};
--
2.22.0.709.g102302147b-goog
^ permalink raw reply related
* [PATCH 06/17] ARM: dts: swift: Cleanup gpio-keys-polled properties
From: Adriana Kobylak @ 2019-07-26 17:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-7-andrew@aj.id.au>
Andrew Jeffery <andrew@aj.id.au> wrote on 07/26/2019 12:39:48 AM:
> From: Andrew Jeffery <andrew@aj.id.au>
> To: linux-aspeed at lists.ozlabs.org
> Cc: Andrew Jeffery <andrew@aj.id.au>, robh+dt at kernel.org,
> mark.rutland at arm.com, joel at jms.id.au, devicetree at vger.kernel.org,
> linux-arm-kernel at lists.infradead.org, linux-kernel at vger.kernel.org,
> Adriana Kobylak <anoo@us.ibm.com>
> Date: 07/26/2019 12:40 AM
> Subject: [EXTERNAL] [PATCH 06/17] ARM: dts: swift: Cleanup gpio-
> keys-polled properties
>
> dtbs_check gave the following warning:
>
> Warning (avoid_unnecessary_addr_size): /gpio-keys-polled:
> unnecessary #address-cells/#size-cells without "ranges" or child
> "reg" property
>
> Cc: Adriana Kobylak <anoo@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Adriana Kobylak <anoo@us.ibm.com>
Tested-by: Adriana Kobylak <anoo@us.ibm.com>
> ---
> arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts b/arch/arm/
> boot/dts/aspeed-bmc-opp-swift.dts
> index 2077e8d0e096..9f934509ca1b 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
> @@ -82,8 +82,6 @@
>
> gpio-keys-polled {
> compatible = "gpio-keys-polled";
> - #address-cells = <1>;
> - #size-cells = <0>;
> poll-interval = <1000>;
>
> scm0-presence {
> --
> 2.20.1
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linux-aspeed/attachments/20190726/a10d5722/attachment-0001.htm>
^ permalink raw reply
* [RFC PATCH 15/17] ipmi: kcs: aspeed: Implement v2 bindings
From: Wang, Haiyue @ 2019-07-26 17:30 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-16-andrew@aj.id.au>
? 2019-07-26 13:39, Andrew Jeffery ??:
> The v2 bindings allow us to extract the resources from the devicetree.
> The table in the driver is retained to derive the channel index, which
> removes the need for kcs_chan property from the v1 bindings. The v2
> bindings allow us to reduce the number of warnings generated by the
> existing devicetree nodes.
>
> Cc: Haiyue Wang<haiyue.wang@linux.intel.com>
> Cc: Corey Minyard<minyard@acm.org>
> Cc: Arnd Bergmann<arnd@arndb.de>
> Cc: Greg Kroah-Hartman<gregkh@linuxfoundation.org>
> Cc:openipmi-developer at lists.sourceforge.net
> Signed-off-by: Andrew Jeffery<andrew@aj.id.au>
> ---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 156 +++++++++++++++++++++++------
> 1 file changed, 127 insertions(+), 29 deletions(-)
Looks good, thanks for the hard work, the code is more clean! :)
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
^ permalink raw reply
* [RFC PATCH 14/17] ipmi: kcs: Finish configuring ASPEED KCS device before enable
From: Wang, Haiyue @ 2019-07-26 17:24 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <29a2d999-23bd-8e95-a1b8-f00e25a11df5@linux.intel.com>
? 2019-07-27 01:04, Wang, Haiyue ??:
> ? 2019-07-26 13:39, Andrew Jeffery ??:
>> The currently interrupts are configured after the channel was enabled.
>>
>> Cc: Haiyue Wang<haiyue.wang@linux.intel.com>
>> Cc: Corey Minyard<minyard@acm.org>
>> Cc: Arnd Bergmann<arnd@arndb.de>
>> Cc: Greg Kroah-Hartman<gregkh@linuxfoundation.org>
>> Cc:openipmi-developer at lists.sourceforge.net
>> Signed-off-by: Andrew Jeffery<andrew@aj.id.au>
>> ---
>> ? drivers/char/ipmi/kcs_bmc_aspeed.c | 7 ++++---
>> ? 1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c
>> b/drivers/char/ipmi/kcs_bmc_aspeed.c
>> index 3c955946e647..e3dd09022589 100644
>> --- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>> +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>> @@ -268,13 +268,14 @@ static int aspeed_kcs_probe(struct
>> platform_device *pdev)
>> ????? kcs_bmc->io_inputb = aspeed_kcs_inb;
>> ????? kcs_bmc->io_outputb = aspeed_kcs_outb;
>> ? +??? rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
>> +??? if (rc)
>> +??????? return rc;
>> +
>> ????? dev_set_drvdata(dev, kcs_bmc);
>
>
> Thanks for catching this, for not miss the data.
>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
^ permalink raw reply
* [RFC PATCH 14/17] ipmi: kcs: Finish configuring ASPEED KCS device before enable
From: Wang, Haiyue @ 2019-07-26 17:04 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-15-andrew@aj.id.au>
? 2019-07-26 13:39, Andrew Jeffery ??:
> The currently interrupts are configured after the channel was enabled.
>
> Cc: Haiyue Wang<haiyue.wang@linux.intel.com>
> Cc: Corey Minyard<minyard@acm.org>
> Cc: Arnd Bergmann<arnd@arndb.de>
> Cc: Greg Kroah-Hartman<gregkh@linuxfoundation.org>
> Cc:openipmi-developer at lists.sourceforge.net
> Signed-off-by: Andrew Jeffery<andrew@aj.id.au>
> ---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
> index 3c955946e647..e3dd09022589 100644
> --- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> @@ -268,13 +268,14 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> kcs_bmc->io_inputb = aspeed_kcs_inb;
> kcs_bmc->io_outputb = aspeed_kcs_outb;
>
> + rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
> + if (rc)
> + return rc;
> +
> dev_set_drvdata(dev, kcs_bmc);
Thanks for catching this, for not miss the data.
^ permalink raw reply
* [patch v4 5/5] Documentation: DT bindings AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
documentation for Dt bindings for DMA based UARTs in AST2500
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
Changes in v4:
-
Changes in v3:
- change logs added
.../bindings/serial/ast2500-dma-uart.txt | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
diff --git a/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt b/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
new file mode 100644
index 000000000000..6ebc60b51d4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
@@ -0,0 +1,43 @@
+DT Bindings DMA UART for AST25XX:
+
+node for DMA controller:
+ ast_uart_sdma: uart_sdma at 1e79e000 {
+ compatible = "aspeed,ast-uart-sdma";
+ reg = <0x1e79e000 0x400>;
+ interrupts = <50>;
+ status = "disabled";
+ };
+this node doesn't binds with any driver.
+DMA controller is handled as a separate SW layer,
+and is included in the same driver.
+This DMA controller node is included in DT
+just for Register and interrupt details
+
+
+
+node for DMA-UARTS :
+
+
+Required properties:
+
+- compatible: "aspeed,ast-sdma-uart"
+- reg: The base address of the UART register bank
+- interrupts: should contain interrupt specifier.
+- clocks: Clock driving the hardware;
+- pinctrl-0 : list of pinconfigurations
+- dma-channel: channel of DMA-controller which is used
+
+Example:
+
+ dma_uart1: dma_uart1 at 1e783000{
+ compatible = "aspeed,ast-sdma-uart";
+ reg = <0x1e783000 0x1000>;
+ reg-shift = <2>;
+ interrupts = <9>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+ dma-channel = <0>;
+ no-loopback-test;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default>;
+ status = "disabled";
+ };
--
2.17.1
^ permalink raw reply related
* [patch v4 4/5] defconfig and MAINTAINERS updated for AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
defconfig changes to add DMA based UART in AST2500
Maintainers File updated.
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
Changes in v4:
- config name changed to CONFIG_SERIAL_AST_DMA_UART as per convention
Changes in v3:
- Added changes logs
MAINTAINERS | 12 ++++++++++++
arch/arm/configs/aspeed_g5_defconfig | 1 +
2 files changed, 13 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 997e27ab492f..d814a52ecf99 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1347,6 +1347,18 @@ F: drivers/crypto/axis
F: drivers/pinctrl/pinctrl-artpec*
F: Documentation/devicetree/bindings/pinctrl/axis,artpec6-pinctrl.txt
+ARM/ASPEED DMA UART DRIVER
+M: sudheer v <sudheer.open@gmail.com>
+R: Joel Stanley <joel@jms.id.au>
+R: Andrew Jeffery <andrew@aj.id.au>
+R: Vinod Koul <vkoul@kernel.org>
+L: dmaengine at vger.kernel.org
+L: openbmc at lists.ozlabs.org
+L: linux-aspeed at lists.ozlabs.org
+S: Maintained
+F: drivers/tty/serial/8250/8250_aspeed_uart_dma.c
+F: Documentation/devicetree/bindings/serial/ast-sdma-uart.txt
+
ARM/ASPEED I2C DRIVER
M: Brendan Higgins <brendanhiggins@google.com>
R: Benjamin Herrenschmidt <benh@kernel.crashing.org>
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index 1849cbc161b4..fc17cc9ddc00 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -144,6 +144,7 @@ CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=6
CONFIG_SERIAL_8250_RUNTIME_UARTS=6
+CONFIG_SERIAL_AST_DMA_UART=y
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_ASPEED_VUART=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
--
2.17.1
^ permalink raw reply related
* [patch v4 3/5] DT nodes for AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
DT node for DMA controller(ast_uart_sdma) doesn't bind to any DMA controller driver.
This is because Software for DMA controller is not based on DMA framework,but is dedicated
and serves only UARTs in AST2500. ast_uart_sdma node is searched by compatible string in the
driver software.basic use of this node is to provide register base address of DMA controller and DMA irq number(<50>).
IRQ of DMA controller is of crucial importance, which does RX and TX of UART data.
uart nodes dma_uart1,2...etc binds to the platform driver.
irq numbers <9>,<32>,<33>,<34> in dma_uart nodes install ISRs which are of not much interest in uart data TX/RX .
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
changes from v3->v4:
-
changes from v2->v3:
- change logs added
arch/arm/boot/dts/aspeed-ast2500-evb.dts | 21 +++++++
arch/arm/boot/dts/aspeed-g5.dtsi | 71 ++++++++++++++++++++++--
2 files changed, 88 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 5dbb33c10c4f..4da09fbe94df 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -64,6 +64,27 @@
status = "okay";
};
+&ast_uart_sdma {
+ status = "okay";
+};
+
+&dma_uart1 {
+ status = "okay";
+};
+
+&dma_uart2 {
+ status = "okay";
+};
+
+&dma_uart3 {
+ status = "okay";
+};
+
+&dma_uart4 {
+ status = "okay";
+};
+
+
&mac0 {
status = "okay";
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 674746513031..fb7b3ed463de 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -23,10 +23,10 @@
i2c11 = &i2c11;
i2c12 = &i2c12;
i2c13 = &i2c13;
- serial0 = &uart1;
- serial1 = &uart2;
- serial2 = &uart3;
- serial3 = &uart4;
+ serial0 = &dma_uart1;
+ serial1 = &dma_uart2;
+ serial2 = &dma_uart3;
+ serial3 = &dma_uart4;
serial4 = &uart5;
serial5 = &vuart;
peci0 = &peci0;
@@ -497,6 +497,69 @@
status = "disabled";
};
+ ast_uart_sdma: uart_sdma at 1e79e000 {
+ compatible = "aspeed,ast-uart-sdma";
+ reg = <0x1e79e000 0x400>;
+ interrupts = <50>;
+ status = "disabled";
+ };
+
+ dma_uart1: dma_uart1 at 1e783000{
+ compatible = "aspeed,ast-sdma-uart";
+ reg = <0x1e783000 0x1000>;
+ reg-shift = <2>;
+ interrupts = <9>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+ dma-channel = <0>;
+ no-loopback-test;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd1_default
+ &pinctrl_rxd1_default>;
+ status = "disabled";
+ };
+
+ dma_uart2: dma_uart2 at 1e78d000{
+ compatible = "aspeed,ast-sdma-uart";
+ reg = <0x1e78d000 0x1000>;
+ reg-shift = <2>;
+ interrupts = <32>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART2CLK>;
+ dma-channel = <1>;
+ no-loopback-test;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd2_default
+ &pinctrl_rxd2_default>;
+ status = "disabled";
+ };
+
+ dma_uart3: dma_uart3 at 1e78e000{
+ compatible = "aspeed,ast-sdma-uart";
+ reg = <0x1e78e000 0x1000>;
+ reg-shift = <2>;
+ interrupts = <33>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART3CLK>;
+ dma-channel = <2>;
+ no-loopback-test;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd3_default
+ &pinctrl_rxd3_default>;
+ status = "disabled";
+ };
+
+ dma_uart4: dma_uart4 at 1e78f000{
+ compatible = "aspeed,ast-sdma-uart";
+ reg = <0x1e78f000 0x1000>;
+ reg-shift = <2>;
+ interrupts = <34>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART4CLK>;
+ dma-channel = <3>;
+ no-loopback-test;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd4_default
+ &pinctrl_rxd4_default>;
+ status = "disabled";
+ };
+
i2c: bus at 1e78a000 {
compatible = "simple-bus";
#address-cells = <1>;
--
2.17.1
^ permalink raw reply related
* [patch v4 2/5] build configuration for AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
build config for DMA based UART driver in AST2500.
Total Available UARTs in AST2500 are 4
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
Changes from v3->v4:
- config name changed to SERIAL_AST_DMA_UART
- new config AST_UART_DMA_RX_INTERRUPT introduced
for selectin between DMA interrupt based RX vs timer based uart RX
Changes in v2->v3:
- change logs added
drivers/tty/serial/8250/Kconfig | 43 ++++++++++++++++++++++++++++++++
drivers/tty/serial/8250/Makefile | 1 +
2 files changed, 44 insertions(+)
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 15c2c5463835..7052ab0f4894 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -189,6 +189,49 @@ config SERIAL_8250_RUNTIME_UARTS
with the module parameter "nr_uarts", or boot-time parameter
8250.nr_uarts
+config SERIAL_AST_DMA_UART
+ tristate "AST UART driver with DMA"
+ select SERIAL_CORE
+ help
+ UART driver with DMA support for Aspeed BMC AST25XX.
+ this driver supports UARTs in AST2500. It uses
+ DMA channel of DMA engines present in these chips.
+ since this dma engine is used only by UARTs it is not
+ added as a separate DMA driver instead added as a layer
+ within UART driver.
+
+config AST_UART_DMA_RX_INTERRUPT
+ bool "DMA interrupt of UART RX"
+ depends on SERIAL_AST_DMA_UART
+ default y
+ help
+ This config is Enabled by default,which means Rx part
+ of UART is handled by DMA interrupt.
+ if the version of chip AST2500 doesn't support
+ DMA interrupt based RX,then Disable this option.Refer
+ driver code to see how Rx is handled by timer,if Rx
+ interrupt is not available.
+
+
+config AST_NR_DMA_UARTS
+ int "Maximum number of uart dma serial ports"
+ depends on SERIAL_AST_DMA_UART
+ default "4"
+ help
+ Set this to the number of serial ports you want the driver
+ to support. This includes any ports discovered via ACPI or
+ PCI enumeration and any ports that may be added at run-time
+ via hot-plug, or any ISA multi-port serial cards.
+
+config AST_RUNTIME_DMA_UARTS
+ int "Number of uart dma serial ports to register at runtime"
+ depends on SERIAL_AST_DMA_UART
+ range 0 AST_NR_DMA_UARTS
+ default "4"
+ help
+ Set this to the maximum number of serial ports you want
+ the kernel to register at boot time.
+
config SERIAL_8250_EXTENDED
bool "Extended 8250/16550 serial driver options"
depends on SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 18751bc63a84..251f2e85efa1 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
+obj-$(CONFIG_SERIAL_AST_DMA_UART) += 8250_ast2500_uart_dma.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
--
2.17.1
^ permalink raw reply related
* [patch v4 1/5] AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
UART driver for Aspeed's bmc chip AST2500
Design approch:
AST2500 has dedicated Uart DMA controller which has 12 sets of Tx and RX channels
connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.
implementation details:
'struct ast_uart_port' is populated and registered with uart_core.
code is organised into two layers UART-layer and DMA-Layer,both of them are
in the same file.UART-layer requests Rx and Tx dma channels
and registers callbacks with DMA controller software Layer
Interrupt service routine for DMA controller is the crucial one for Handling all
the tx and rx data. ISRs installed for individual uarts are just dummy,and are helpful
only to report any spurious interrupts in hardware.
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
Changes from v3->v4:
- per port uart structures are registerd directly with uart core
Instead of registering through 8250 Frame work,
ast_uart_port is registered using uart_add_one_port
-SDMA_RX_FIX macro replaced with CONFIG_AST_UART_DMA_RX_INTERRUPT
-ast_uart_sdma_isr : DMA interrupt handler code is improvised
-replaced pr_debug with ftrace wherever appropriate
-dev_err is used in all error return cases
-uart driver structure ast25xx_uart_reg is modified
-driver name changed to ast2500-uart-dma-drv
-rx_timer initialisation and callback fn modified
Changes from v2->v3:
-custom debug replaced by in kerenl dynamic debug: pr_debug
-change-logs added
.../tty/serial/8250/8250_ast2500_uart_dma.c | 1901 +++++++++++++++++
1 file changed, 1901 insertions(+)
create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c
diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
new file mode 100644
index 000000000000..bc830d605372
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
@@ -0,0 +1,1901 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA UART Driver for ASPEED BMC chip: AST2500
+ *
+ * Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
+ * <open.sudheer@gmail.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include "8250.h"
+
+#define SERIAL8250_CONSOLE NULL
+#define TTY_AST_MAJOR 204
+#define TTY_AST_MINOR 68
+
+#define DMA_BUFF_SIZE 0x1000
+#define SDMA_RX_BUFF_SIZE 0x10000
+#define PASS_LIMIT 256
+#define UART_DMA_NR CONFIG_AST_NR_DMA_UARTS
+#define AST_UART_SDMA_CH 12
+
+/* enum ast_uart_chan_op
+ * operation codes passed to the DMA code by the user, and also used
+ * to inform the current channel owner of any changes to the system state
+ */
+enum ast_uart_chan_op {
+ AST_UART_DMAOP_TRIGGER,
+ AST_UART_DMAOP_STOP,
+ AST_UART_DMAOP_PAUSE,
+};
+
+/* ast_uart_dma_cbfn: buffer callback routinei type */
+typedef void (*ast_uart_dma_cbfn)(void *dev_id, u16 len);
+
+struct ast_sdma_info {
+ u8 ch_no;
+ u8 direction;
+ u8 enable;
+ void *priv;
+ char *sdma_virt_addr;
+ dma_addr_t dma_phy_addr;
+ /* cdriver callbacks */
+ ast_uart_dma_cbfn callback_fn; /* buffer done callback */
+};
+
+struct ast_sdma_ch {
+ struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
+ struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
+};
+
+struct ast_sdma {
+ void __iomem *reg_base;
+ int dma_irq;
+ struct ast_sdma_ch *dma_ch;
+ struct regmap *map;
+};
+
+#define UART_TX_SDMA_EN 0x00
+#define UART_RX_SDMA_EN 0x04
+#define UART_SDMA_CONF 0x08 /* Misc, Buffer size */
+#define UART_SDMA_TIMER 0x0C
+#define UART_TX_SDMA_REST 0x20
+#define UART_RX_SDMA_REST 0x24
+#define UART_TX_SDMA_IER 0x30
+#define UART_TX_SDMA_ISR 0x34
+#define UART_RX_SDMA_IER 0x38
+#define UART_RX_SDMA_ISR 0x3C
+#define UART_TX_R_POINT(x) (0x40 + ((x) * 0x20))
+#define UART_TX_W_POINT(x) (0x44 + ((x) * 0x20))
+#define UART_TX_SDMA_ADDR(x) (0x48 + ((x) * 0x20))
+#define UART_RX_R_POINT(x) (0x50 + ((x) * 0x20))
+#define UART_RX_W_POINT(x) (0x54 + ((x) * 0x20))
+#define UART_RX_SDMA_ADDR(x) (0x58 + ((x) * 0x20))
+#define SDMA_CH_EN(x) BIT(x)
+
+#define SDMA_TX_BUFF_SIZE_MASK (0x3)
+#define SDMA_SET_TX_BUFF_SIZE(x)(x)
+#define SDMA_BUFF_SIZE_1KB (0x0)
+#define SDMA_BUFF_SIZE_4KB (0x1)
+#define SDMA_BUFF_SIZE_16KB (0x2)
+#define SDMA_BUFF_SIZE_64KB (0x3)
+#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
+#define SDMA_SET_RX_BUFF_SIZE(x)((x) << 2)
+#define SDMA_TIMEOUT_DIS BIT(4)
+
+#define UART_SDMA11_INT BIT(11)
+#define UART_SDMA10_INT BIT(10)
+#define UART_SDMA9_INT BIT(9)
+#define UART_SDMA8_INT BIT(8)
+#define UART_SDMA7_INT BIT(7)
+#define UART_SDMA6_INT BIT(6)
+#define UART_SDMA5_INT BIT(5)
+#define UART_SDMA4_INT BIT(4)
+#define UART_SDMA3_INT BIT(3)
+#define UART_SDMA2_INT BIT(2)
+#define UART_SDMA1_INT BIT(1)
+#define UART_SDMA0_INT BIT(0)
+
+/*
+ * Configuration:
+ * share_irqs - whether we pass IRQF_SHARED to request_irq().
+ * This option is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
+
+struct ast_uart_port {
+ struct uart_port port;
+ unsigned short capabilities; /* port capabilities */
+ unsigned short bugs; /* port bugs */
+ unsigned int tx_loadsz; /* transmit fifo load size */
+ unsigned char acr;
+ unsigned char ier;
+ unsigned char lcr;
+ unsigned char mcr;
+ unsigned char mcr_mask; /* mask of user bits */
+ unsigned char mcr_force; /* mask of forced bits */
+ struct circ_buf rx_dma_buf;
+ struct circ_buf tx_dma_buf;
+ unsigned char dma_channel;
+ dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
+ dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ struct tasklet_struct rx_tasklet;
+#else
+ struct timer_list rx_timer;
+ unsigned int workaround;
+#endif
+ struct tasklet_struct tx_tasklet;
+ spinlock_t lock;
+ int tx_done;
+ int tx_count;
+ struct platform_device *ast_uart_pdev;
+/*
+ * Some bits in registers are cleared on a read, so they must
+ * be saved whenever the register is read but the bits will not
+ * be immediately processed.
+ */
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+ unsigned char lsr_saved_flags;
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+ unsigned char msr_saved_flags;
+
+ /*
+ * We provide a per-port pm hook.
+ */
+ void (*pm)(struct uart_port *port, unsigned int state,
+ unsigned int old);
+};
+
+static struct ast_uart_port ast_uart_ports[UART_DMA_NR];
+
+#define GET_DEV(ast_uart_port_priv_ptr)\
+ (ast_uart_port_priv_ptr->ast_uart_pdev->dev)
+
+static inline struct ast_uart_port *
+to_ast_dma_uart_port(struct uart_port *uart) {
+ return container_of(uart, struct ast_uart_port, port);
+}
+
+struct irq_info {
+ spinlock_t lock;
+ struct ast_uart_port *up;
+};
+
+static struct irq_info ast_uart_irq[1];
+static DEFINE_MUTEX(ast_uart_mutex);
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+ [PORT_UNKNOWN] = {
+ .name = "unknown",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_8250] = {
+ .name = "8250",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16450] = {
+ .name = "16450",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16550] = {
+ .name = "16550",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16550A] = {
+ .name = "16550A",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
+ | UART_FCR_DMA_SELECT,
+ .flags = UART_CAP_FIFO,
+ },
+};
+
+#define map_8250_reg_offset(up, offset) ((offset) << (up->port.regshift))
+
+
+
+// SDMA - software Layer : ast-uart-sdma.c
+static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
+ u32 val, u32 reg)
+{
+ trace_printk("uart dma-write : val: %x, reg: %x\n", val, reg);
+ writel(val, sdma->reg_base + reg);
+}
+
+static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
+{
+ return readl(sdma->reg_base + reg);
+}
+
+struct ast_sdma ast_uart_sdma;
+
+int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("ch = %d, tx buff = %x\n", ch, tx_buff);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+void ast_uart_set_sdma_time_out(u16 val)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
+}
+
+int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ local_irq_save(flags);
+ trace_printk("ch=%d op=%d\n", ch, op);
+ switch (op) {
+ case AST_UART_DMAOP_TRIGGER:
+ dma_ch->enable = 1;
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ ast_uart_set_sdma_time_out(0xffff);
+#endif
+ // set enable
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
+ UART_RX_SDMA_EN);
+ break;
+ case AST_UART_DMAOP_STOP:
+ // disable engine
+ dma_ch->enable = 0;
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN)
+ & ~(0x1 << ch), UART_RX_SDMA_EN);
+ // set reset
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
+ (0x1 << ch), UART_RX_SDMA_REST);
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
+ ~(0x1 << ch), UART_RX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
+ ast_uart_sdma_write(sdma,
+ dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
+ break;
+ case AST_UART_DMAOP_PAUSE:
+ // disable engine
+ dma_ch->enable = 0;
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+ ~(0x1 << ch), UART_RX_SDMA_EN);
+ break;
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+ trace_printk("ch=%d op=%d\n", ch, op);
+ local_irq_save(flags);
+ switch (op) {
+ case AST_UART_DMAOP_TRIGGER:
+ dma_ch->enable = 1;
+ // set enable
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
+ UART_TX_SDMA_EN);
+ break;
+ case AST_UART_DMAOP_STOP:
+ dma_ch->enable = 0;
+ // disable engine
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << ch), UART_TX_SDMA_EN);
+ // set reset
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_REST)
+ | (0x1 << ch), UART_TX_SDMA_REST);
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_REST)
+ & ~(0x1 << ch), UART_TX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
+ break;
+ case AST_UART_DMAOP_PAUSE:
+ dma_ch->enable = 0;
+ // disable engine
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << ch), UART_TX_SDMA_EN);
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+u32 ast_uart_get_tx_sdma_pt(u8 ch)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
+}
+
+int ast_uart_tx_sdma_update(u8 ch, u16 point)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("TX DMA CTRL [ch %d] point %d\n", ch, point);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
+ local_irq_restore(flags);
+ return 0;
+}
+
+int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn rtn, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+ trace_printk("TX DMA REQUEST ch = %d\n", ch);
+ local_irq_save(flags);
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ dma_ch->priv = id;
+ dma_ch->callback_fn = rtn;
+
+ // DMA IRQ En
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
+ UART_TX_SDMA_IER);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+int ast_uart_rx_sdma_update(u8 ch, u16 point)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("RX DMA CTRL [ch %d] point %x\n", ch, point);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
+ local_irq_restore(flags);
+ return 0;
+}
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn rtn, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ trace_printk("RX DMA REQUEST ch = %d\n", ch);
+ local_irq_save(flags);
+
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return 0;
+ }
+ dma_ch->priv = id;
+ dma_ch->callback_fn = rtn;
+ // DMA IRQ En
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
+ UART_RX_SDMA_IER);
+
+ local_irq_restore(flags);
+
+ return dma_ch->sdma_virt_addr;
+}
+
+#else
+char *ast_uart_rx_sdma_request(u8 ch, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ trace_printk("RX DMA REQUEST ch = %d\n", ch);
+
+ local_irq_save(flags);
+
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return ERR_PTR(-EBUSY);
+ }
+ dma_ch->priv = id;
+
+ local_irq_restore(flags);
+ return dma_ch->sdma_virt_addr;
+}
+#endif
+
+u16 ast_uart_get_rx_sdma_pt(u8 ch)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
+}
+
+static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
+{
+ u32 len;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ if (sdma_ch->enable == 0) {
+ pr_debug("sdma Please check ch_no %x %s!!!!!\n",
+ sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
+ if (sdma_ch->direction) {
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
+ } else {
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+ ~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
+ ast_uart_rx_sdma_update(sdma_ch->ch_no,
+ ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
+ trace_printk("OFFSET : UART_RX_SDMA_EN = %x\n ",
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
+ }
+ return;
+ }
+
+ if (sdma_ch->direction) {
+ len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
+ trace_printk("tx rp %x , wp %x\n",
+ ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
+ ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
+ );
+ } else {
+ trace_printk("rx rp %x , wp %x\n",
+ ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
+ ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
+ );
+ len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
+ }
+
+ trace_printk("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
+ sdma_ch->direction ? "tx" : "rx", len);
+
+ if (sdma_ch->callback_fn != NULL)
+ (sdma_ch->callback_fn)(sdma_ch->priv, len);
+}
+
+static irqreturn_t ast_uart_sdma_isr(int irq, void *dev_id)
+{
+ struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
+ int sdma_int_num = 0;
+ u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
+ u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
+
+ trace_printk("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
+ if ((tx_sts == 0) && (rx_sts == 0)) {
+ pr_err("SDMA IRQ ERROR !!!\n");
+ return IRQ_HANDLED;
+ }
+
+ while ((sdma_int_num = ffs(rx_sts))) {
+ ast_uart_sdma_write(sdma,
+ BIT(sdma_int_num-1), UART_RX_SDMA_ISR);
+ ast_sdma_bufffdone(
+ &(sdma->dma_ch->rx_dma_info[sdma_int_num-1]));
+ rx_sts = rx_sts & (~BIT(sdma_int_num-1));
+ }
+ while ((sdma_int_num = ffs(tx_sts))) {
+ ast_uart_sdma_write(sdma,
+ BIT(sdma_int_num-1), UART_TX_SDMA_ISR);
+ ast_sdma_bufffdone(
+ &(sdma->dma_ch->tx_dma_info[sdma_int_num-1]));
+ tx_sts = tx_sts & (~BIT(sdma_int_num-1));
+ }
+ return IRQ_HANDLED;
+}
+
+static int ast_uart_sdma_probe(void)
+{
+ int i;
+ struct device_node *node;
+ int ret;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ char *rx_dma_virt_addr;
+ dma_addr_t rx_dma_phy_addr;
+
+ sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
+ if (!sdma->dma_ch)
+ return -ENOMEM;
+
+ // sdma memory mapping
+ node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
+ if (!node)
+ return -ENODEV;
+
+ sdma->reg_base = of_iomap(node, 0);
+ if (IS_ERR(sdma->reg_base))
+ return PTR_ERR(sdma->map);
+ rx_dma_virt_addr = dma_alloc_coherent(NULL,
+ SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH,
+ &rx_dma_phy_addr, GFP_KERNEL);
+
+ if (!rx_dma_virt_addr) {
+ pr_err("rx_dma_virt_addr Err:dma alloc Failed\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < AST_UART_SDMA_CH; i++) {
+ // TX ------------------------
+ sdma->dma_ch->tx_dma_info[i].enable = 0;
+ sdma->dma_ch->tx_dma_info[i].ch_no = i;
+ sdma->dma_ch->tx_dma_info[i].direction = 1;
+ ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
+ // RX ------------------------
+ sdma->dma_ch->rx_dma_info[i].enable = 0;
+ sdma->dma_ch->rx_dma_info[i].ch_no = i;
+ sdma->dma_ch->rx_dma_info[i].direction = 0;
+ sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
+ rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
+ sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
+ rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
+ ast_uart_sdma_write(sdma,
+ sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
+ UART_RX_SDMA_ADDR(i));
+ ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
+ }
+
+ ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
+ ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
+ ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
+ ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
+#else
+ ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
+#endif
+
+ // TX
+ ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
+ ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
+
+ // RX
+ ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
+ ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
+
+ sdma->dma_irq = of_irq_get(node, 0);
+ ret = request_irq(sdma->dma_irq, ast_uart_sdma_isr, 0,
+ "sdma-intr", sdma);
+ if (ret) {
+ pr_err("Unable to get UART SDMA IRQ %x\n", ret);
+ return -ENODEV;
+ }
+
+ ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
+ SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
+ UART_SDMA_CONF);
+ return 0;
+}
+// END of SDMA Layer
+
+
+// UART Driver Layer
+static unsigned int ast_serial_in(struct ast_uart_port *up, int offset)
+{
+ offset = map_8250_reg_offset(up, offset);
+ return readb(up->port.membase + offset);
+}
+
+static void ast_serial_out(struct ast_uart_port *up, int offset, int value)
+{
+ /* Save the offset before it's remapped */
+ offset = map_8250_reg_offset(up, offset);
+ writeb(value, up->port.membase + offset);
+}
+
+#define serial_inp(up, offset) ast_serial_in(up, offset)
+#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct ast_uart_port *up)
+{
+ return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct ast_uart_port *up, int value)
+{
+ serial_outp(up, UART_DLL, value & 0xff);
+ serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+
+static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(
+ (struct uart_port *)data);
+ struct circ_buf *xmit = &up->port.state->xmit;
+ u32 tx_pt;
+
+ spin_lock(&up->port.lock);
+
+ up->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ dma_sync_single_for_device(up->port.dev, up->dma_tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ tx_pt = ast_uart_get_tx_sdma_pt(up->dma_channel);
+
+ if (tx_pt > xmit->head) {
+ if ((tx_pt & 0xfffc) == 0)
+ ast_uart_tx_sdma_update(up->dma_channel, 0xffff);
+ else
+ ast_uart_tx_sdma_update(up->dma_channel, 0);
+ } else {
+ ast_uart_tx_sdma_update(up->dma_channel, xmit->head);
+ }
+ ast_uart_tx_sdma_update(up->dma_channel, xmit->head);
+ spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_tx_buffdone(void *dev_id, u16 len)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)dev_id;
+ struct circ_buf *xmit = &up->port.state->xmit;
+
+ trace_printk("line[%d] : tx len = % d\n", up->port.line, len);
+ spin_lock(&up->port.lock);
+ xmit->tail = len;
+ trace_printk(" line[%d], xmit->head = %d, xmit->tail = % d\n",
+ up->port.line, xmit->head, xmit->tail);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (xmit->head != xmit->tail)
+ tasklet_schedule(&up->tx_tasklet);
+
+ spin_unlock(&up->port.lock);
+}
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(
+ (struct uart_port *)data);
+ struct tty_port *port = &up->port.state->port;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+
+ int count;
+ int copy = 0;
+
+ trace_printk("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+ up->port.line, rx_ring->head, rx_ring->tail);
+
+ spin_lock(&up->port.lock);
+ if (rx_ring->head > rx_ring->tail) {
+ count = rx_ring->head - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else if (rx_ring->head < rx_ring->tail) {
+ count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else {
+ count = 0;
+ }
+
+ if (copy != count)
+ dev_err(&GET_DEV(up), "RxData copy to tty layer failed\n");
+ if (count) {
+ rx_ring->tail += count;
+ rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+ up->port.icount.rx += count;
+ tty_flip_buffer_push(port);
+ ast_uart_rx_sdma_update(up->dma_channel, rx_ring->tail);
+ }
+ spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_rx_buffdone(void *dev_id, u16 len)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)dev_id;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+
+ trace_printk("line[%d], head = %d,len:%d\n",
+ up->port.line, up->rx_dma_buf.head, len);
+
+ spin_lock(&up->port.lock);
+ rx_ring->head = len;
+ spin_unlock(&up->port.lock);
+ tasklet_schedule(&up->rx_tasklet);
+}
+
+#else
+static void ast_uart_rx_timer_func(struct timer_list *t)
+{
+ struct ast_uart_port *up = from_timer(up, t, rx_timer);
+ struct tty_port *port = &up->port.state->port;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+ int count, copy = 0;
+
+ trace_printk("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+ up->port.line, rx_ring->head, rx_ring->tail);
+
+ rx_ring->head = ast_uart_get_rx_sdma_pt(up->dma_channel);
+
+ del_timer(&up->rx_timer);
+
+ if (rx_ring->head > rx_ring->tail) {
+ ast_uart_set_sdma_time_out(0xffff);
+ count = rx_ring->head - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else if (rx_ring->head < rx_ring->tail) {
+ ast_uart_set_sdma_time_out(0xffff);
+ count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else {
+ count = 0;
+ trace_printk("@@--%s-- ch = 0x%x\n", __func__, ch);
+ }
+
+ if (copy != count)
+ dev_err(&GET_DEV(up), "RxData copy to tty layer failed\n");
+
+ if (count) {
+ rx_ring->tail += count;
+ rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+ }
+ if (count) {
+ trace_printk("\n count = % d\n", count);
+ up->port.icount.rx += count;
+ spin_lock(&up->port.lock);
+ tty_flip_buffer_push(port);
+ spin_unlock(&up->port.lock);
+ trace_printk("update rx_ring->tail % x\n", rx_ring->tail);
+ ast_uart_rx_sdma_update(up->dma_channel, rx_ring->tail);
+ up->workaround = 1;
+ } else {
+ if (up->workaround) {
+ up->workaround++;
+ if (up->workaround > 1)
+ ast_uart_set_sdma_time_out(0);
+ else
+ ast_uart_set_sdma_time_out(0xffff);
+ }
+ }
+ add_timer(&up->rx_timer);
+}
+#endif
+
+/*
+ * FIFO support.
+ */
+inline void ast25xx_uart_clear_fifos(struct ast_uart_port *p)
+{
+ serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_outp(p, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_outp(p, UART_FCR, 0);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.
+ */
+static void autoconfig(struct ast_uart_port *up)
+{
+ unsigned long flags;
+
+ trace_printk("line[%d]\n", up->port.line);
+ if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+ return;
+
+ trace_printk("ttyDMA%d : autoconf (0x%04lx, 0x%p) : ", up->port.line,
+ up->port.iobase, up->port.membase);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ up->capabilities = 0;
+ up->bugs = 0;
+
+ up->port.type = PORT_16550A;
+ up->capabilities |= UART_CAP_FIFO;
+
+ up->port.fifosize = uart_config[up->port.type].fifo_size;
+ up->capabilities = uart_config[up->port.type].flags;
+ up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+ if (up->port.type == PORT_UNKNOWN)
+ goto out;
+
+ /*
+ * Reset the UART.
+ */
+ ast25xx_uart_clear_fifos(up);
+ ast_serial_in(up, UART_RX);
+ serial_outp(up, UART_IER, 0);
+
+out:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ trace_printk("type=%s\n", uart_config[up->port.type].name);
+}
+
+static inline void __stop_tx(struct ast_uart_port *p)
+{
+ if (p->ier & UART_IER_THRI) {
+ p->ier &= ~UART_IER_THRI;
+ ast_serial_out(p, UART_IER, p->ier);
+ }
+}
+
+void ast25xx_uart_stop_tx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", up->port.line);
+ __stop_tx(up);
+}
+
+static void transmit_chars(struct ast_uart_port *up);
+
+static void ast25xx_uart_start_tx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ tasklet_schedule(&up->tx_tasklet);
+}
+
+static void ast25xx_uart_stop_rx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void ast25xx_uart_enable_ms(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ up->ier |= UART_IER_MSI;
+ ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct ast_uart_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ int count;
+
+ if (up->port.x_char) {
+ serial_outp(up, UART_TX, up->port.x_char);
+ up->port.icount.tx++;
+ up->port.x_char = 0;
+ return;
+ }
+ if (uart_tx_stopped(&up->port)) {
+ ast25xx_uart_stop_tx(&up->port);
+ return;
+ }
+ if (uart_circ_empty(xmit)) {
+ __stop_tx(up);
+ return;
+ }
+
+ count = up->tx_loadsz;
+ do {
+ ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit))
+ __stop_tx(up);
+}
+
+static unsigned int check_modem_status(struct ast_uart_port *up)
+{
+ unsigned int status = ast_serial_in(up, UART_MSR);
+
+ trace_printk("line[%d]\n", up->port.line);
+ status |= up->msr_saved_flags;
+ up->msr_saved_flags = 0;
+ if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI
+ && up->port.state != NULL) {
+ if (status & UART_MSR_TERI)
+ up->port.icount.rng++;
+ if (status & UART_MSR_DDSR)
+ up->port.icount.dsr++;
+ if (status & UART_MSR_DDCD)
+ uart_handle_dcd_change(&up->port,
+ status & UART_MSR_DCD);
+ if (status & UART_MSR_DCTS)
+ uart_handle_cts_change(&up->port,
+ status & UART_MSR_CTS);
+
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+ }
+
+ return status;
+}
+
+/*
+ * handles the interrupt from one port.
+ */
+static inline void ast25xx_uart_handle_port(struct ast_uart_port *up)
+{
+ unsigned int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ status = serial_inp(up, UART_LSR);
+
+ trace_printk("status = %x\n", status);
+
+ check_modem_status(up);
+ if (status & UART_LSR_THRE)
+ transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * serial driver's interrupt routine.
+ */
+static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
+{
+ struct irq_info *i = dev_id;
+ int pass_counter = 0, handled = 0, end = 0;
+
+ trace_printk("(%d)-", irq);
+ spin_lock(&i->lock);
+
+ do {
+ struct ast_uart_port *up;
+ unsigned int iir;
+
+ up = (struct ast_uart_port *)(i->up);
+
+ iir = ast_serial_in(up, UART_IIR);
+ if (!(iir & UART_IIR_NO_INT)) {
+ ast25xx_uart_handle_port(up);
+ handled = 1;
+
+ } else
+ end = 1;
+
+ if (pass_counter++ > PASS_LIMIT) {
+ pr_err("ast-uart-dma:too much work for irq%d\n", irq);
+ break;
+ }
+ } while (end);
+
+ spin_unlock(&i->lock);
+
+ trace_printk("-(%d)\n", irq);
+
+ return IRQ_RETVAL(handled);
+}
+
+static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+ unsigned int lsr;
+
+ trace_printk("line[%d]\n", up->port.line);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ lsr = ast_serial_in(up, UART_LSR);
+ up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ast25xx_uart_get_mctrl(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned int status;
+ unsigned int ret;
+
+ status = check_modem_status(up);
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned char mcr = 0;
+
+ mctrl = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+ ast_serial_out(up, UART_MCR, mcr);
+}
+
+static void ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (break_state == -1)
+ up->lcr |= UART_LCR_SBC;
+ else
+ up->lcr &= ~UART_LCR_SBC;
+ ast_serial_out(up, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ast25xx_uart_startup(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ // TX DMA
+ struct circ_buf *xmit = &up->port.state->xmit;
+ unsigned long flags;
+ unsigned char lsr, iir;
+ int retval;
+ int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+ trace_printk("line[%d]\n", port->line);
+ up->capabilities = uart_config[up->port.type].flags;
+ up->mcr = 0;
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in set_termios())
+ */
+ ast25xx_uart_clear_fifos(up);
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void)serial_inp(up, UART_LSR);
+ (void)serial_inp(up, UART_RX);
+ (void)serial_inp(up, UART_IIR);
+ (void)serial_inp(up, UART_MSR);
+
+ ast_uart_irq[0].up = up;
+ retval = request_irq(up->port.irq, ast_uart_interrupt, irq_flags,
+ "ast-uart-dma", ast_uart_irq);
+ if (retval)
+ return retval;
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl |= TIOCM_OUT2;
+
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+
+ /*
+ * Do a quick test to see if we receive an
+ * interrupt when we enable the TX irq.
+ */
+ serial_outp(up, UART_IER, UART_IER_THRI);
+ lsr = ast_serial_in(up, UART_LSR);
+ iir = ast_serial_in(up, UART_IIR);
+ serial_outp(up, UART_IER, 0);
+
+ if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+ if (!(up->bugs & UART_BUG_TXEN)) {
+ up->bugs |= UART_BUG_TXEN;
+ pr_debug("ttyDMA%d-enabling bad tx status\n",
+ port->line);
+ }
+ } else {
+ up->bugs &= ~UART_BUG_TXEN;
+ }
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /*
+ * Clear the interrupt registers again for luck, and clear the
+ * saved flags to avoid getting false values from polling
+ * routines or the previous session.
+ */
+ serial_inp(up, UART_LSR);
+ serial_inp(up, UART_RX);
+ serial_inp(up, UART_IIR);
+ serial_inp(up, UART_MSR);
+ up->lsr_saved_flags = 0;
+ up->msr_saved_flags = 0;
+
+ // RX DMA
+ up->rx_dma_buf.head = 0;
+ up->rx_dma_buf.tail = 0;
+ up->port.icount.rx = 0;
+
+ up->tx_done = 1;
+ up->tx_count = 0;
+
+ up->rx_dma_buf.head = 0;
+ up->rx_dma_buf.tail = 0;
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ up->workaround = 0;
+#endif
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_STOP);
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_TRIGGER);
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ add_timer(&up->rx_timer);
+#endif
+ up->tx_dma_buf.head = 0;
+ up->tx_dma_buf.tail = 0;
+ up->tx_dma_buf.buf = xmit->buf;
+
+ trace_printk("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
+ xmit->head = 0;
+ xmit->tail = 0;
+
+ up->dma_tx_addr = dma_map_single(port->dev, up->tx_dma_buf.buf,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ ast_uart_tx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_STOP);
+ ast_uart_tx_sdma_enqueue(up->dma_channel, up->dma_tx_addr);
+ ast_uart_tx_sdma_update(up->dma_channel, 0);
+ ast_uart_tx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_TRIGGER);
+
+ return 0;
+}
+
+static void ast25xx_uart_shutdown(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+
+ trace_printk("line[%d]\n", port->line);
+
+ up->ier = 0;
+ serial_outp(up, UART_IER, 0);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl &= ~TIOCM_OUT2;
+
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /*
+ * Disable break condition and FIFOs
+ */
+ ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+ ast25xx_uart_clear_fifos(up);
+
+ (void)ast_serial_in(up, UART_RX);
+
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_PAUSE);
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ del_timer_sync(&up->rx_timer);
+#endif
+ free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
+ unsigned int baud)
+{
+ unsigned int quot;
+
+ quot = uart_get_divisor(port, baud);
+ return quot;
+}
+
+static void ast25xx_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+ unsigned int baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (termios->c_cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ quot = ast25xx_uart_get_divisor(port, baud);
+
+ if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+ if (baud < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = uart_config[up->port.type].fcr;
+ }
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ up->port.read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ up->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ up->port.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /*
+ * ignore all characters if CREAD is not set
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ up->port.ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * CTS flow control flag and modem status interrupts
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+
+ ast_serial_out(up, UART_IER, up->ier);
+
+ serial_outp(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+
+ serial_dl_write(up, quot);
+
+ /*
+ * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+ * is written without DLAB set, this mode will be disabled.
+ */
+
+ serial_outp(up, UART_LCR, cval); /* reset DLAB */
+ up->lcr = cval; /* Save LCR */
+ if (fcr & UART_FCR_ENABLE_FIFO) {
+ /* emulated UARTs (Lucent Venus 167x) need two steps */
+ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
+ serial_outp(up, UART_FCR, fcr); /* set fcr */
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static void ast25xx_uart_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct ast_uart_port *p = (struct ast_uart_port *)port;
+
+ if (p->pm)
+ p->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.
+ */
+static int ast25xx_uart_request_std_resource(struct ast_uart_port *up)
+{
+ unsigned int size = 8 << up->port.regshift;
+ int ret = 0;
+
+ if (!up->port.mapbase)
+ return ret;
+
+ if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
+ ret = -EBUSY;
+ return ret;
+ }
+
+ if (up->port.flags & UPF_IOREMAP) {
+ up->port.membase = ioremap_nocache(up->port.mapbase, size);
+ if (!up->port.membase) {
+ release_mem_region(up->port.mapbase, size);
+ ret = -ENOMEM;
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static void ast25xx_uart_release_std_resource(struct ast_uart_port *up)
+{
+ unsigned int size = 8 << up->port.regshift;
+
+ if (!up->port.mapbase)
+ return;
+
+ if (up->port.flags & UPF_IOREMAP) {
+ iounmap(up->port.membase);
+ up->port.membase = NULL;
+ }
+ release_mem_region(up->port.mapbase, size);
+}
+
+static void ast25xx_uart_release_port(struct uart_port *port)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+
+ ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_request_port(struct uart_port *port)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+ int ret;
+
+ ret = ast25xx_uart_request_std_resource(up);
+ if (ret == 0)
+ ast25xx_uart_release_std_resource(up);
+
+ return ret;
+}
+
+static void ast25xx_uart_config_port(struct uart_port *port, int flags)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+ int ret;
+
+ /*
+ * Find the region that we can probe for. This in turn
+ * tells us whether we can probe for the type of port.
+ */
+ ret = ast25xx_uart_request_std_resource(up);
+ if (ret < 0)
+ return;
+
+ if (flags & UART_CONFIG_TYPE)
+ autoconfig(up);
+
+ if (up->port.type == PORT_UNKNOWN)
+ ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ return 0;
+}
+
+static const char *ast25xx_uart_type(struct uart_port *port)
+{
+ int type = port->type;
+
+ if (type >= ARRAY_SIZE(uart_config))
+ type = 0;
+ return uart_config[type].name;
+}
+
+static const struct uart_ops ast25xx_uart_pops = {
+ .tx_empty = ast25xx_uart_tx_empty,
+ .set_mctrl = ast25xx_uart_set_mctrl,
+ .get_mctrl = ast25xx_uart_get_mctrl,
+ .stop_tx = ast25xx_uart_stop_tx,
+ .start_tx = ast25xx_uart_start_tx,
+ .stop_rx = ast25xx_uart_stop_rx,
+ .enable_ms = ast25xx_uart_enable_ms,
+ .break_ctl = ast25xx_uart_break_ctl,
+ .startup = ast25xx_uart_startup,
+ .shutdown = ast25xx_uart_shutdown,
+ .set_termios = ast25xx_uart_set_termios,
+ .pm = ast25xx_uart_pm,
+ .type = ast25xx_uart_type,
+ .release_port = ast25xx_uart_release_port,
+ .request_port = ast25xx_uart_request_port,
+ .config_port = ast25xx_uart_config_port,
+ .verify_port = ast25xx_uart_verify_port,
+};
+
+static void __init ast25xx_uart_isa_init_ports(void)
+{
+ static int first = 1;
+ int i;
+
+ if (!first)
+ return;
+ first = 0;
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ up->port.line = i;
+ spin_lock_init(&up->port.lock);
+
+ /*
+ * ALPHA_KLUDGE_MCR needs to be killed.
+ */
+ up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+ up->mcr_force = ALPHA_KLUDGE_MCR;
+ up->port.ops = &ast25xx_uart_pops;
+ }
+}
+
+static void __init ast25xx_uart_register_ports(struct uart_driver *drv,
+ struct device *dev)
+{
+ int i;
+
+ ast25xx_uart_isa_init_ports();
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ up->port.dev = dev;
+ uart_add_one_port(drv, &up->port);
+ }
+}
+
+
+static struct ast_uart_port *
+ast25xx_uart_find_match_or_unused(struct uart_port *port)
+{
+ int i;
+
+ /*
+ * First, find a port entry which matches.
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (uart_match_port(&ast_uart_ports[i].port, port))
+ return &ast_uart_ports[i];
+
+ /*
+ * We didn't find a matching entry, so look for the first
+ * free entry. We look for one which hasn't been previously
+ * used (indicated by zero iobase).
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (ast_uart_ports[i].port.type == PORT_UNKNOWN &&
+ ast_uart_ports[i].port.iobase == 0)
+ return &ast_uart_ports[i];
+
+ /*
+ * That also failed. Last resort is to find any entry which
+ * doesn't have a real port associated with it.
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (ast_uart_ports[i].port.type == PORT_UNKNOWN)
+ return &ast_uart_ports[i];
+
+ return NULL;
+}
+
+/*
+ * This "device" covers _all_ ISA 8250-compatible serial devices listed
+ * in the table in include/asm/serial.h
+ */
+static struct platform_device *ast25xx_uart_platform_obj_dev;
+
+static struct uart_driver ast25xx_uart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "ast-uart-dma",
+ .dev_name = "ttyDMA",
+ .major = TTY_AST_MAJOR,
+ .minor = TTY_AST_MINOR,
+ .nr = UART_DMA_NR,
+ .cons = SERIAL8250_CONSOLE,
+};
+
+
+/*
+ *Configure the serial port specified by the request. If the
+ *port exists and is in use, it is hung up and unregistered
+ *first.
+ *
+ *The port is then probed and if necessary the IRQ is autodetected
+ *If this fails an error is returned.
+ *
+ *On success the port is ready to use and the line number is returned.
+ */
+int ast_uart_register_port(struct uart_port *port)
+{
+ struct ast_uart_port *up;
+ int ret = -ENOSPC;
+
+ if (port->uartclk == 0)
+ return -EINVAL;
+
+ mutex_lock(&ast_uart_mutex);
+
+ up = ast25xx_uart_find_match_or_unused(port);
+ if (up) {
+ uart_remove_one_port(&ast25xx_uart_reg, &up->port);
+ up->port.iobase = port->iobase;
+ up->port.membase = port->membase;
+ up->port.irq = port->irq;
+ up->port.uartclk = port->uartclk;
+ up->port.fifosize = port->fifosize;
+ up->port.regshift = port->regshift;
+ up->port.iotype = port->iotype;
+ up->port.flags = port->flags | UPF_BOOT_AUTOCONF;
+ up->port.mapbase = port->mapbase;
+ up->port.private_data = port->private_data;
+ if (port->dev)
+ up->port.dev = port->dev;
+
+ ret = uart_add_one_port(&ast25xx_uart_reg, &up->port);
+ if (ret == 0)
+ ret = up->port.line;
+
+ spin_lock_init(&up->lock);
+
+ tasklet_init(&up->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+ (unsigned long)up);
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ tasklet_init(&up->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+ (unsigned long)up);
+#else
+ up->rx_timer.expires = jiffies + (HZ);
+ up->rx_timer.function = ast_uart_rx_timer_func;
+ timer_setup(&up->rx_timer, ast_uart_rx_timer_func, 0);
+#endif
+ }
+
+ mutex_unlock(&ast_uart_mutex);
+ return ret;
+}
+
+void ast_uart_unregister_port(int line)
+{
+ struct ast_uart_port *up = &ast_uart_ports[line];
+
+ mutex_lock(&ast_uart_mutex);
+ uart_remove_one_port(&ast25xx_uart_reg, &up->port);
+ if (ast25xx_uart_platform_obj_dev) {
+ up->port.flags &= ~UPF_BOOT_AUTOCONF;
+ up->port.type = PORT_UNKNOWN;
+ up->port.dev = &ast25xx_uart_platform_obj_dev->dev;
+ uart_add_one_port(&ast25xx_uart_reg, &up->port);
+ } else {
+ up->port.dev = NULL;
+ }
+ mutex_unlock(&ast_uart_mutex);
+}
+
+/*
+ * Register a set of serial devices attached to a platform device. The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+static int port_index;
+struct clk *clk;
+
+static int ast25xx_uart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct uart_port port;
+ struct ast_uart_port *up = NULL;
+
+ int ret = 0;
+ u32 read, dma_channel = 0;
+ struct resource *res;
+
+ if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+ trace_printk("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+
+ up = &ast_uart_ports[port_index];
+ memset(&port, 0, sizeof(struct uart_port));
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "IRQ resource not found");
+ return -ENODEV;
+ }
+ port.irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Register base not found");
+ return -ENODEV;
+ }
+ port.mapbase = res->start;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "missing controller clock");
+ return PTR_ERR(clk);
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed enabling DMA UART Clk: %d\n", ret);
+ return ret;
+ }
+ port.uartclk = clk_get_rate(clk);
+
+ ret = of_property_read_u32(np, "reg-shift", &read);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read reg-shift from DT");
+ return -ret;
+ }
+ port.regshift = read;
+ ret = of_property_read_u32(np, "dma-channel", &read);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read dma-channel from DT");
+ return -ret;
+ }
+ dma_channel = read;
+ up->dma_channel = dma_channel;
+ port.iotype = UPIO_MEM;
+ port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+ port.dev = &pdev->dev;
+ //port.private_data = up->dma_channel;
+ if (share_irqs)
+ port.flags |= UPF_SHARE_IRQ;
+
+ ret = ast_uart_register_port(&port);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "unable to registr port at index%d (IO%lx MEM%llx IRQ%d):%d\n",
+ port_index, port.iobase, (unsigned long long)port.mapbase,
+ port.irq, ret);
+ return ret;
+ }
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ up->rx_dma_buf.buf =
+ ast_uart_rx_sdma_request(up->dma_channel, ast_uart_rx_buffdone,
+ up);
+ if (up->rx_dma_buf.buf < 0) {
+ trace_printk("Error : failed to get rx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+
+#else
+ up->rx_dma_buf.buf = ast_uart_rx_sdma_request(
+ up->dma_channel, up);
+ if (up->rx_dma_buf.buf < 0) {
+ trace_printk("Error : failed to get rx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+#endif
+ ret = ast_uart_tx_sdma_request(up->dma_channel,
+ ast_uart_tx_buffdone, up);
+ if (ret < 0) {
+ pr_debug("Error : failed to get tx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+
+ up->ast_uart_pdev = pdev;
+ port_index++;
+ return 0;
+
+out_ast_uart_unregister_port:
+ up = &ast_uart_ports[port_index];
+
+ if (up->port.dev == &pdev->dev)
+ ast_uart_unregister_port(up->port.line);
+ return ret;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ast25xx_uart_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.dev == &pdev->dev)
+ ast_uart_unregister_port(i);
+ }
+ return 0;
+}
+
+static int ast25xx_uart_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ int i;
+
+ for (i = 0; i < UART_DMA_NR; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &pdev->dev)
+ uart_suspend_port(&ast25xx_uart_reg, &up->port);
+ }
+
+ return 0;
+}
+
+static int ast25xx_uart_resume(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < UART_DMA_NR; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &pdev->dev)
+ serial8250_resume_port(i);
+ }
+
+ return 0;
+}
+static const struct of_device_id ast_serial_dt_ids[] = {
+ { .compatible = "aspeed,ast-sdma-uart", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver ast25xx_uart_ast_dma_driver = {
+ .probe = ast25xx_uart_probe,
+ .remove = ast25xx_uart_remove,
+ .suspend = ast25xx_uart_suspend,
+ .resume = ast25xx_uart_resume,
+ .driver = {
+ .name = "ast2500-uart-dma-drv",
+ .of_match_table = of_match_ptr(ast_serial_dt_ids),
+ },
+};
+
+static int __init ast_uart_init(void)
+{
+ int ret;
+
+ if (nr_uarts > UART_DMA_NR)
+ nr_uarts = UART_DMA_NR;
+ ret = ast_uart_sdma_probe();
+ if (ret) {
+ pr_err("ast_uart_sdma_probe Failed ret = %d\n", ret);
+ goto out;
+ }
+ pr_debug("UART driver with DMA%d ports IRQ sharing %sabled\n",
+ nr_uarts, share_irqs ? "en" : "dis");
+
+ spin_lock_init(&ast_uart_irq[0].lock);
+
+ ret = uart_register_driver(&ast25xx_uart_reg);
+ if (ret)
+ goto out;
+
+ ast25xx_uart_platform_obj_dev =
+ platform_device_alloc("ast-uart-dma", PLAT8250_DEV_LEGACY);
+ if (!ast25xx_uart_platform_obj_dev) {
+ ret = -ENOMEM;
+ goto unreg_uart_drv;
+ }
+
+ ret = platform_device_add(ast25xx_uart_platform_obj_dev);
+ if (ret)
+ goto put_dev;
+
+ ast25xx_uart_register_ports(&ast25xx_uart_reg,
+ &ast25xx_uart_platform_obj_dev->dev);
+
+ ret = platform_driver_register(&ast25xx_uart_ast_dma_driver);
+ if (ret == 0)
+ goto out;
+
+ platform_device_del(ast25xx_uart_platform_obj_dev);
+put_dev:
+ platform_device_put(ast25xx_uart_platform_obj_dev);
+unreg_uart_drv:
+ uart_unregister_driver(&ast25xx_uart_reg);
+out:
+ return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+ struct platform_device *isa_dev = ast25xx_uart_platform_obj_dev;
+
+ ast25xx_uart_platform_obj_dev = NULL;
+ platform_driver_unregister(&ast25xx_uart_ast_dma_driver);
+ platform_device_unregister(isa_dev);
+ uart_unregister_driver(&ast25xx_uart_reg);
+}
+
+module_init(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Aspeed's AST2500 DMA UART driver");
--
2.17.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox