* [PATCH 0/4] Add fixes to STM32 pintrl
From: Alexandre TORGUE @ 2017-04-07 15:10 UTC (permalink / raw)
To: Linus Walleij, Maxime Coquelin, Patrice Chotard, Paul Gortmaker,
Rob Herring
Cc: linux-gpio, linux-kernel, linux-arm-kernel, devicetree
This series add several fixes to STM32 pinctrl:
- Set input mode when PIN is used as interrupt
- Implement .get_direction() gpio_chip callback
- In DT: set each gpio controller as a interrupt controller. User who
wants to use gpio as interrupt will have choice to use either "gpiolib"
interface or "common" interrupt interface.
Regards
Alex
Alexandre TORGUE (4):
pinctrl: stm32: set pin to gpio input when used as interrupt
pinctrl: stm32: replace device_initcall() with arch_initcall()
pinctrl: stm32: Implement .get_direction gpio_chip callback
ARM: dts: stm32: Set gpio controller also as interrupt controller
arch/arm/boot/dts/stm32f429.dtsi | 22 +++++++++++++++
arch/arm/boot/dts/stm32f746.dtsi | 22 +++++++++++++++
drivers/pinctrl/stm32/pinctrl-stm32.c | 47 +++++++++++++++++++++++++++++--
drivers/pinctrl/stm32/pinctrl-stm32.h | 5 +++-
drivers/pinctrl/stm32/pinctrl-stm32f429.c | 6 +++-
drivers/pinctrl/stm32/pinctrl-stm32f746.c | 7 ++++-
drivers/pinctrl/stm32/pinctrl-stm32h743.c | 6 +++-
7 files changed, 109 insertions(+), 6 deletions(-)
--
1.9.1
^ permalink raw reply
* Re: [PATCH V10 06/12] of: device: Fix overflow of coherent_dma_mask
From: Robin Murphy @ 2017-04-07 14:46 UTC (permalink / raw)
To: Frank Rowand, Sricharan R, will.deacon, joro, lorenzo.pieralisi,
iommu, linux-arm-kernel, linux-arm-msm, m.szyprowski, bhelgaas,
linux-pci, linux-acpi, tn, hanjun.guo, okaya, robh+dt, devicetree,
linux-kernel, sudeep.holla, rjw, lenb, catalin.marinas, arnd,
linux-arch, gregkh
In-Reply-To: <58E69831.6010306@gmail.com>
On 06/04/17 20:34, Frank Rowand wrote:
> On 04/06/17 04:01, Sricharan R wrote:
>> Hi Frank,
>>
>> On 4/6/2017 12:31 PM, Frank Rowand wrote:
>>> On 04/04/17 03:18, Sricharan R wrote:
>>>> Size of the dma-range is calculated as coherent_dma_mask + 1
>>>> and passed to arch_setup_dma_ops further. It overflows when
>>>> the coherent_dma_mask is set for full 64 bits 0xFFFFFFFFFFFFFFFF,
>>>> resulting in size getting passed as 0 wrongly. Fix this by
>>>> passsing in max(mask, mask + 1). Note that in this case
>>>> when the mask is set to full 64bits, we will be passing the mask
>>>> itself to arch_setup_dma_ops instead of the size. The real fix
>>>> for this should be to make arch_setup_dma_ops receive the
>>>> mask and handle it, to be done in the future.
>>>>
>>>> Signed-off-by: Sricharan R <sricharan@codeaurora.org>
>>>> ---
>>>> drivers/of/device.c | 2 +-
>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/of/device.c b/drivers/of/device.c
>>>> index c17c19d..c2ae6bb 100644
>>>> --- a/drivers/of/device.c
>>>> +++ b/drivers/of/device.c
>>>> @@ -107,7 +107,7 @@ void of_dma_configure(struct device *dev, struct device_node *np)
>>>> ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
>>>> if (ret < 0) {
>>>> dma_addr = offset = 0;
>>>> - size = dev->coherent_dma_mask + 1;
>>>> + size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1);
>>>> } else {
>>>> offset = PFN_DOWN(paddr - dma_addr);
>>>> dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
>>>>
>>>
>>> NACK.
>>>
>>> Passing an invalid size to arch_setup_dma_ops() is only part of the problem.
>>> size is also used in of_dma_configure() before calling arch_setup_dma_ops():
>>>
>>> dev->coherent_dma_mask = min(dev->coherent_dma_mask,
>>> DMA_BIT_MASK(ilog2(dma_addr + size)));
>>> *dev->dma_mask = min((*dev->dma_mask),
>>> DMA_BIT_MASK(ilog2(dma_addr + size)));
>>>
>>> which would be incorrect for size == 0xffffffffffffffffULL when
>>> dma_addr != 0. So the proposed fix really is not papering over
>>> the base problem very well.
>>>
>>
>> Ok, but with your fix for of_dma_get_range and the above fix,
>> dma_addr will be '0' when size = 0xffffffffffffffffULL,
>> but DMA_BIT_MASK(ilog2(dma_addr + size)) would be wrong though,
>> making coherent_dma_mask to be smaller 0x7fffffffffffffffULL.
>
> Yes, that was my point. Setting size to 0x7fffffffffffffffULL
> affects several places. Another potential location (based only
> on the function header comment, not from reading the code) is
> iommu_dma_init_domain(). The header comment says:
>
> * @base and @size should be exact multiples of IOMMU page granularity to
> * avoid rounding surprises.
That is really only referring to the fact that some of the work done
therein involves truncation to PFNs, so anyone passing in non-exact
values expecting them to round a particular way may get things off by a
page one way or the other. It's not going to have much practical
significance for real devices (in particular since size is used more as
a sanity check than any kind of actual limit there).
> I have not read enough context to really understand of_dma_configure(), but
> it seems there is yet another issue in how the error return case from
> of_dma_get_range() is handled (with the existing code, as well as if
> my patch gets accepted). An error return value can mean _either_
> there is no dma-ranges property _or_ "an other problem occurred". Should
> the "an other problem occurred" case be handled by defaulting size to
> a value based on dev->coherent_dma_mask (the current case) or should the
> attempt to set up the DMA configuration just fail?
There is indeed a lot wrong with of_dma_configure() and
arch_setup_dma_ops(), but fixing those is beyond the scope of this
series. This is just working around a latent bug in the one specific
case where a value is *not* derived from DT. Any DT which worked before
still works; any DT which made of_dma_configure() go wrong before still
makes of_dma_configure() go wrong exactly the same.
Whilst it's not ideal, since a DMA mask basically represents the maximum
size of address that that particular device can be given, I can't see it
making any practical difference for a full 64-bit DMA mask to be trimmed
down to 63 bits upon re-probing - no system is likely to have that many
physical address bits anyway, and I don't think any IOMMUs support that
large an IOVA space either, so as long as it's still big enough to cover
"everything", it'll be OK.
Of course, whether DMA_BIT_MASK(ilog2(dma_addr + size)) is the right
thing to do in the first place is yet another matter, as there are
plenty of cases where it results in something which can't reach the
given range at all, but again, this isn't the place. Much as I'm keen to
get the behaviour of of_dma_configure() sorted out properly, it doesn't
seem reasonable that that should suddenly block this
almost-entirely-orthogonal series that various other work has been
waiting on for some time now. The WIP patch I have for
arch_setup_dma_ops() already touches 3 architectures and 4 other
subsystems...
Robin.
>
>>
>> Regards,
>> Sricharan
>>
>>> I agree that the proper solution involves passing a mask instead
>>> of a size to arch_setup_dma_ops().
>>>
>>
>
^ permalink raw reply
* Re: [PATCH v2 4/4] drm: zte: add VGA driver support
From: Sean Paul @ 2017-04-07 14:43 UTC (permalink / raw)
To: Shawn Guo
Cc: devicetree, Xin Zhou, Daniel Vetter, Baoyou Xie, dri-devel,
Rob Herring, Jun Nie
In-Reply-To: <20170407030231.GH3394@dragon>
On Fri, Apr 07, 2017 at 11:02:33AM +0800, Shawn Guo wrote:
> On Thu, Apr 06, 2017 at 01:12:51PM -0400, Sean Paul wrote:
> > On Thu, Apr 06, 2017 at 11:01:10PM +0800, Shawn Guo wrote:
> > > +static int zx_vga_connector_get_modes(struct drm_connector *connector)
> > > +{
> > > + struct zx_vga *vga = to_zx_vga(connector);
> > > + unsigned long flags;
> > > + struct edid *edid;
> > > + int ret;
> > > +
> > > + /*
> > > + * Clear both detection bits to switch I2C bus from device
> > > + * detecting to EDID reading.
> > > + */
> > > + spin_lock_irqsave(&vga->lock, flags);
> > > + zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
> > > + spin_unlock_irqrestore(&vga->lock, flags);
> > > +
> > > + edid = drm_get_edid(connector, &vga->ddc->adap);
> > > + if (!edid)
> > > + return 0;
> > > +
> > > + /*
> > > + * As edid reading succeeds, device must be connected, so we set
> > > + * up detection bit for unplug interrupt here.
> > > + */
> > > + spin_lock_irqsave(&vga->lock, flags);
> > > + zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
> > > + spin_unlock_irqrestore(&vga->lock, flags);
> > > +
> > > + drm_mode_connector_update_edid_property(connector, edid);
> > > + ret = drm_add_edid_modes(connector, edid);
> > > + kfree(edid);
> > > +
> > > + return ret;
> > > +}
>
> <snip>
>
> > > +static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
> > > +{
> > > + struct zx_vga *vga = dev_id;
> > > + u32 status;
> > > +
> > > + status = zx_readl(vga->mmio + VGA_I2C_STATUS);
> > > +
> > > + /* Clear interrupt status */
> > > + zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
> > > + VGA_CLEAR_IRQ);
> > > +
> > > + if (status & VGA_DEVICE_CONNECTED) {
> > > + /*
> > > + * Since VGA_DETECT_SEL bits need to be reset for switching DDC
> > > + * bus from device detection to EDID read, rather than setting
> > > + * up HAS_DEVICE bit here, we need to do that in .get_modes
> > > + * hook for unplug detecting after EDID read succeeds.
> > > + */
> > > + vga->connected = true;
> > > + return IRQ_WAKE_THREAD;
> > > + }
> > > +
> > > + if (status & VGA_DEVICE_DISCONNECTED) {
> > > + spin_lock(&vga->lock);
> > > + zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
> > > + VGA_DETECT_SEL_NO_DEVICE);
> > > + spin_unlock(&vga->lock);
> >
> > I think you still have the race here. If you get a disconnect between get_edid
> > successfully finishing, and resetting the DETECT_SEL register, you will end up
> > with the device being disconnected and DETECT_SEL == VGA_DETECT_SEL_HAS_DEVICE.
> >
> > In order to close this, you'll need to hold the lock across the edid read.
>
> We cannot hold a spin lock across the EDID read procedure, which does
> sleep.
Yeah, this is a pretty common problem. We usually use a mutex in conjunction
with a work function to handle this type of thing.
>
> When you suggested to have a lock for DETECT_SEL register access, I
> thought we are accessing it in a read-modify-write way and thus agreed
> to add a lock. However, I just found that it's not the case actually.
> All the accesses to the register are single direct write.
>
> And here is my understanding about the condition you described above.
> Once DETECT_SEL register gets reset (both bits cleared), the hardware
> switches DDC bus for EDID read, and hotplug detection will stop working
> right away (this is how ZTE hardware works). That said, if we get a
> disconnect before drm_get_edid() successfully finishes, two points:
>
> - The irq handler will not be triggered, so it will not get a chance to
> update DETECT_SEL register.
Ah, this was the missing piece I needed. I hadn't realized that the first
VGA_AUTO_DETECT_SEL write in get_modes disabled the irq.
> - The drm_get_edid() fails, and .get_modes returns with both detect bits
> kept cleared.
>
> I think what we can do better in this case is that we should set the
> device state into disconnected, before returning from .get_modes,
> something like the code below. But still, I do not see we need a lock
> for that.
Yep, agreed. Can you also please add to your comment in the !edid case,
something like:
* Locking is not required here since the only other place to write this register
* (and connected var) is the irq handler. The irq handler is disabled because
* we've cleared AUTO_DETECT_SEL above.
Thanks for walking me through this.
Sean
>
> Shawn
>
> static int zx_vga_connector_get_modes(struct drm_connector *connector)
> {
> struct zx_vga *vga = to_zx_vga(connector);
> unsigned long flags;
> struct edid *edid;
> int ret;
>
> /*
> * Clear both detection bits to switch I2C bus from device
> * detecting to EDID reading.
> */
> zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
>
> edid = drm_get_edid(connector, &vga->ddc->adap);
> if (!edid) {
> /*
> * If EDID reading fails, we set the device state into
> * disconnected.
> */
> zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
> VGA_DETECT_SEL_NO_DEVICE);
> vga->connected = false;
> return 0;
> }
>
> /*
> * As edid reading succeeds, device must be connected, so we set
> * up detection bit for unplug interrupt here.
> */
> zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
>
> drm_mode_connector_update_edid_property(connector, edid);
> ret = drm_add_edid_modes(connector, edid);
> kfree(edid);
>
> return ret;
> }
--
Sean Paul, Software Engineer, Google / Chromium OS
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* [PATCH v2 3/4] bluetooth: hci_uart: add LL protocol serdev driver support
From: Rob Herring @ 2017-04-07 14:35 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Gustavo Padovan, Johan Hedberg, Mark Rutland, Wei Xu, Eyal Reizer,
Satish Patel, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170405183005.20570-4-robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Turns out that the LL protocol and the TI-ST are the same thing AFAICT.
The TI-ST adds firmware loading, GPIO control, and shared access for
NFC, FM radio, etc. For now, we're only implementing what is needed for
BT. This mirrors other drivers like BCM and Intel, but uses the new
serdev bus.
The firmware loading is greatly simplified by using existing
infrastructure to send commands. It may be a bit slower than the
original code using synchronous functions, but the real bottleneck is
likely doing firmware load at 115.2kbps.
Signed-off-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Marcel Holtmann <marcel-kz+m5ild9QBg9hUCZPvPmw@public.gmane.org>
Cc: Gustavo Padovan <gustavo-THi1TnShQwVAfugRpC6u6w@public.gmane.org>
Cc: Johan Hedberg <johan.hedberg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: linux-bluetooth-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
v2:
- Use IS_ENABLED() to fix module build
drivers/bluetooth/hci_ll.c | 261 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 260 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 02692fe30279..77648adfd5c9 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -34,20 +34,23 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
+#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
-#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
+#include <linux/serdev.h>
#include <linux/skbuff.h>
+#include <linux/ti_wilink_st.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <linux/gpio/consumer.h>
#include "hci_uart.h"
@@ -76,6 +79,12 @@ struct hcill_cmd {
u8 cmd;
} __packed;
+struct ll_device {
+ struct hci_uart hu;
+ struct serdev_device *serdev;
+ struct gpio_desc *enable_gpio;
+};
+
struct ll_struct {
unsigned long rx_state;
unsigned long rx_count;
@@ -136,6 +145,9 @@ static int ll_open(struct hci_uart *hu)
hu->priv = ll;
+ if (hu->serdev)
+ serdev_device_open(hu->serdev);
+
return 0;
}
@@ -164,6 +176,13 @@ static int ll_close(struct hci_uart *hu)
kfree_skb(ll->rx_skb);
+ if (hu->serdev) {
+ struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
+
+ serdev_device_close(hu->serdev);
+ }
+
hu->priv = NULL;
kfree(ll);
@@ -505,9 +524,245 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu)
return skb_dequeue(&ll->txq);
}
+#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
+static int read_local_version(struct hci_dev *hdev)
+{
+ int err = 0;
+ unsigned short version = 0;
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *ver;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Reading TI version information failed (%ld)",
+ PTR_ERR(skb));
+ err = PTR_ERR(skb);
+ goto out;
+ }
+ if (skb->len != sizeof(*ver)) {
+ err = -EILSEQ;
+ goto out;
+ }
+
+ ver = (struct hci_rp_read_local_version *)skb->data;
+ if (le16_to_cpu(ver->manufacturer) != 13) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ version = le16_to_cpu(ver->lmp_subver);
+
+out:
+ if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
+ kfree_skb(skb);
+ return err ? err : version;
+}
+
+/**
+ * download_firmware -
+ * internal function which parses through the .bts firmware
+ * script file intreprets SEND, DELAY actions only as of now
+ */
+static int download_firmware(struct ll_device *lldev)
+{
+ unsigned short chip, min_ver, maj_ver;
+ int version, err, len;
+ unsigned char *ptr, *action_ptr;
+ unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */
+ const struct firmware *fw;
+ struct sk_buff *skb;
+ struct hci_command *cmd;
+
+ version = read_local_version(lldev->hu.hdev);
+ if (version < 0)
+ return version;
+
+ chip = (version & 0x7C00) >> 10;
+ min_ver = (version & 0x007F);
+ maj_ver = (version & 0x0380) >> 7;
+ if (version & 0x8000)
+ maj_ver |= 0x0008;
+
+ snprintf(bts_scr_name, sizeof(bts_scr_name),
+ "ti-connectivity/TIInit_%d.%d.%d.bts",
+ chip, maj_ver, min_ver);
+
+ err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev);
+ if (err || !fw->data || !fw->size) {
+ bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
+ err, bts_scr_name);
+ return -EINVAL;
+ }
+ ptr = (void *)fw->data;
+ len = fw->size;
+ /* bts_header to remove out magic number and
+ * version
+ */
+ ptr += sizeof(struct bts_header);
+ len -= sizeof(struct bts_header);
+
+ while (len > 0 && ptr) {
+ bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ",
+ ((struct bts_action *)ptr)->size,
+ ((struct bts_action *)ptr)->type);
+
+ action_ptr = &(((struct bts_action *)ptr)->data[0]);
+
+ switch (((struct bts_action *)ptr)->type) {
+ case ACTION_SEND_COMMAND: /* action send */
+ bt_dev_dbg(lldev->hu.hdev, "S");
+ cmd = (struct hci_command *)action_ptr;
+ if (cmd->opcode == 0xff36) {
+ /* ignore remote change
+ * baud rate HCI VS command */
+ bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware");
+ break;
+ }
+ if (cmd->prefix != 1)
+ bt_dev_dbg(lldev->hu.hdev, "command type %d\n", cmd->prefix);
+
+ skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(lldev->hu.hdev, "send command failed\n");
+ goto out_rel_fw;
+ }
+ kfree_skb(skb);
+ break;
+ case ACTION_WAIT_EVENT: /* wait */
+ /* no need to wait as command was synchronous */
+ bt_dev_dbg(lldev->hu.hdev, "W");
+ break;
+ case ACTION_DELAY: /* sleep */
+ bt_dev_info(lldev->hu.hdev, "sleep command in scr");
+ mdelay(((struct bts_action_delay *)action_ptr)->msec);
+ break;
+ }
+ len -= (sizeof(struct bts_action) +
+ ((struct bts_action *)ptr)->size);
+ ptr += sizeof(struct bts_action) +
+ ((struct bts_action *)ptr)->size;
+ }
+
+out_rel_fw:
+ /* fw download complete */
+ release_firmware(fw);
+ return err;
+}
+
+static int ll_setup(struct hci_uart *hu)
+{
+ int err, retry = 3;
+ struct ll_device *lldev;
+ struct serdev_device *serdev = hu->serdev;
+ u32 speed;
+
+ if (!serdev)
+ return 0;
+
+ lldev = serdev_device_get_drvdata(serdev);
+
+ serdev_device_set_flow_control(serdev, true);
+
+ do {
+ /* Configure BT_EN to HIGH state */
+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
+ msleep(5);
+ gpiod_set_value_cansleep(lldev->enable_gpio, 1);
+ msleep(100);
+
+ err = download_firmware(lldev);
+ if (!err)
+ break;
+
+ /* Toggle BT_EN and retry */
+ bt_dev_err(hu->hdev, "download firmware failed, retrying...");
+ } while (retry--);
+
+ if (err)
+ return err;
+
+ /* Operational speed if any */
+ if (hu->oper_speed)
+ speed = hu->oper_speed;
+ else if (hu->proto->oper_speed)
+ speed = hu->proto->oper_speed;
+ else
+ speed = 0;
+
+ if (speed) {
+ struct sk_buff *skb = __hci_cmd_sync(hu->hdev, 0xff36, sizeof(speed), &speed, HCI_INIT_TIMEOUT);
+ if (!IS_ERR(skb)) {
+ kfree_skb(skb);
+ serdev_device_set_baudrate(serdev, speed);
+ }
+ }
+
+ return 0;
+}
+
+static const struct hci_uart_proto llp;
+
+static int hci_ti_probe(struct serdev_device *serdev)
+{
+ struct hci_uart *hu;
+ struct ll_device *lldev;
+ u32 max_speed = 3000000;
+
+ lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
+ if (!lldev)
+ return -ENOMEM;
+ hu = &lldev->hu;
+
+ serdev_device_set_drvdata(serdev, lldev);
+ lldev->serdev = hu->serdev = serdev;
+
+ lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(lldev->enable_gpio))
+ return PTR_ERR(lldev->enable_gpio);
+
+ of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
+ hci_uart_set_speeds(hu, 115200, max_speed);
+
+ return hci_uart_register_device(hu, &llp);
+}
+
+static void hci_ti_remove(struct serdev_device *serdev)
+{
+ struct ll_device *lldev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &lldev->hu;
+ struct hci_dev *hdev = hu->hdev;
+
+ cancel_work_sync(&hu->write_work);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ hu->proto->close(hu);
+}
+
+static const struct of_device_id hci_ti_of_match[] = {
+ { .compatible = "ti,wl1831-st" },
+ { .compatible = "ti,wl1835-st" },
+ { .compatible = "ti,wl1837-st" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hci_ti_of_match);
+
+static struct serdev_device_driver hci_ti_drv = {
+ .driver = {
+ .name = "hci-ti",
+ .of_match_table = of_match_ptr(hci_ti_of_match),
+ },
+ .probe = hci_ti_probe,
+ .remove = hci_ti_remove,
+};
+#else
+#define ll_setup NULL
+#endif
+
static const struct hci_uart_proto llp = {
.id = HCI_UART_LL,
.name = "LL",
+ .setup = ll_setup,
.open = ll_open,
.close = ll_close,
.recv = ll_recv,
@@ -518,10 +773,14 @@ static const struct hci_uart_proto llp = {
int __init ll_init(void)
{
+ serdev_device_driver_register(&hci_ti_drv);
+
return hci_uart_register_proto(&llp);
}
int __exit ll_deinit(void)
{
+ serdev_device_driver_unregister(&hci_ti_drv);
+
return hci_uart_unregister_proto(&llp);
}
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH net-next] bindings: net: stmmac: add missing note about LPI interrupt
From: Niklas Cassel @ 2017-04-07 14:30 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller, Joao Pinto,
Alexandre TORGUE, Giuseppe CAVALLARO, Thierry Reding,
Eric Engestrom
Cc: Niklas Cassel, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
From: Niklas Cassel <niklas.cassel-VrBV9hrLPhE@public.gmane.org>
The hardware has a LPI interrupt.
There is already code in the stmmac driver to parse and handle the
interrupt. However, this information was missing from the DT binding.
Signed-off-by: Niklas Cassel <niklas.cassel-VrBV9hrLPhE@public.gmane.org>
---
Documentation/devicetree/bindings/net/stmmac.txt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index f652b0c384ce..8977abc266ac 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -8,8 +8,8 @@ Required properties:
that services interrupts for this device
- interrupts: Should contain the STMMAC interrupts
- interrupt-names: Should contain the interrupt names "macirq"
- "eth_wake_irq" if this interrupt is supported in the "interrupts"
- property
+ "eth_wake_irq" if this interrupt is supported in the "interrupts property
+ "eth_lpi" if this interrupt is supported in the "interrupts" property
- phy-mode: See ethernet.txt file in the same directory.
- snps,reset-gpio gpio number for phy reset.
- snps,reset-active-low boolean flag to indicate if phy reset is active low.
@@ -152,8 +152,8 @@ Examples:
compatible = "st,spear600-gmac";
reg = <0xe0800000 0x8000>;
interrupt-parent = <&vic1>;
- interrupts = <24 23>;
- interrupt-names = "macirq", "eth_wake_irq";
+ interrupts = <24 23 22>;
+ interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
mac-address = [000000000000]; /* Filled in by U-Boot */
max-frame-size = <3800>;
phy-mode = "gmii";
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 2/2] dmaengine: Add DW AXI DMAC driver
From: Eugeniy Paltsev @ 2017-04-07 14:04 UTC (permalink / raw)
To: dmaengine
Cc: devicetree, Andy Shevchenko, Vinod Koul, Alexey Brodkin,
linux-kernel, Rob Herring, Dan Williams, linux-snps-arc,
Eugeniy Paltsev
In-Reply-To: <1491573855-1039-1-git-send-email-Eugeniy.Paltsev@synopsys.com>
This patch adds support for the DW AXI DMAC controller.
DW AXI DMAC is a part of upcoming development board from Synopsys.
In this driver implementation only DMA_MEMCPY and DMA_SG transfers
are supported.
Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
---
drivers/dma/Kconfig | 10 +
drivers/dma/Makefile | 1 +
drivers/dma/axi_dma_platform.c | 1044 ++++++++++++++++++++++++++++++++++++
drivers/dma/axi_dma_platform.h | 119 ++++
drivers/dma/axi_dma_platform_reg.h | 220 ++++++++
5 files changed, 1394 insertions(+)
create mode 100644 drivers/dma/axi_dma_platform.c
create mode 100644 drivers/dma/axi_dma_platform.h
create mode 100644 drivers/dma/axi_dma_platform_reg.h
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fc3435c..0ad946a 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -578,6 +578,16 @@ config ZX_DMA
help
Support the DMA engine for ZTE ZX family platform devices.
+config AXI_DW_DMAC
+ tristate "Synopsys DesignWare AXI DMA support"
+ depends on OF || COMPILE_TEST
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for Synopsys DesignWare AXI DMA controller.
+ NOTE: This driver wasn't tested on 64 bit platform because
+ of lack 64 bit platform with Synopsys DW AXI DMAC.
+
# driver files
source "drivers/dma/bestcomm/Kconfig"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 0b723e9..7f1824d 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
+obj-$(CONFIG_AXI_DW_DMAC) += axi_dma_platform.o
obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
diff --git a/drivers/dma/axi_dma_platform.c b/drivers/dma/axi_dma_platform.c
new file mode 100644
index 0000000..d8d9bf3b
--- /dev/null
+++ b/drivers/dma/axi_dma_platform.c
@@ -0,0 +1,1044 @@
+/*
+ * Synopsys DesignWare AXI DMA Controller driver.
+ *
+ * Copyright (C) 2017 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/types.h>
+
+#include "axi_dma_platform.h"
+#include "axi_dma_platform_reg.h"
+#include "dmaengine.h"
+#include "virt-dma.h"
+
+#define DRV_NAME "axi_dw_dmac"
+
+/*
+ * The set of bus widths supported by the DMA controller. DW AXI DMAC supports
+ * master data bus width up to 512 bits (for both AXI master interfaces), but
+ * it depends on IP block configurarion.
+ */
+#define AXI_DMA_BUSWIDTHS \
+ (DMA_SLAVE_BUSWIDTH_1_BYTE | \
+ DMA_SLAVE_BUSWIDTH_2_BYTES | \
+ DMA_SLAVE_BUSWIDTH_4_BYTES | \
+ DMA_SLAVE_BUSWIDTH_8_BYTES | \
+ DMA_SLAVE_BUSWIDTH_16_BYTES | \
+ DMA_SLAVE_BUSWIDTH_32_BYTES | \
+ DMA_SLAVE_BUSWIDTH_64_BYTES)
+/* TODO: check: do we need to use BIT() macro here? */
+
+static inline void
+axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val)
+{
+ iowrite32(val, chip->regs + reg);
+}
+
+static inline u32 axi_dma_ioread32(struct axi_dma_chip *chip, u32 reg)
+{
+ return ioread32(chip->regs + reg);
+}
+
+static inline void
+axi_chan_iowrite32(struct axi_dma_chan *chan, u32 reg, u32 val)
+{
+ iowrite32(val, chan->chan_regs + reg);
+}
+
+static inline u32 axi_chan_ioread32(struct axi_dma_chan *chan, u32 reg)
+{
+ return ioread32(chan->chan_regs + reg);
+}
+
+static inline void
+axi_chan_iowrite64(struct axi_dma_chan *chan, u32 reg, u64 val)
+{
+ iowrite32(val & 0xFFFFFFFF, chan->chan_regs + reg);
+ iowrite32(val >> 32, chan->chan_regs + reg + 4);
+}
+
+static inline void axi_dma_disable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val &= ~DMAC_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_enable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val |= DMAC_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_irq_disable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val &= ~INT_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_irq_enable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val |= INT_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_chan_irq_disable(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ u32 val;
+
+ if (likely(irq_mask == DWAXIDMAC_IRQ_ALL)) {
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, DWAXIDMAC_IRQ_NONE);
+ } else {
+ val = axi_chan_ioread32(chan, CH_INTSTATUS_ENA);
+ val &= ~irq_mask;
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, val);
+ }
+}
+
+static inline void axi_chan_irq_set(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, irq_mask);
+}
+
+static inline void axi_chan_irq_sig_set(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTSIGNAL_ENA, irq_mask);
+}
+
+static inline void axi_chan_irq_clear(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTCLEAR, irq_mask);
+}
+
+static inline u32 axi_chan_irq_read(struct axi_dma_chan *chan)
+{
+ return axi_chan_ioread32(chan, CH_INTSTATUS);
+}
+
+static inline void axi_chan_disable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT);
+ val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+}
+
+static inline void axi_chan_enable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT |
+ BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+}
+
+static inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+
+ return !!(val & (BIT(chan->id) << DMAC_CHAN_EN_SHIFT));
+}
+
+static void axi_dma_hw_init(struct axi_dma_chip *chip)
+{
+ u32 i;
+
+ for (i = 0; i < chip->dw->hdata->nr_channels; i++) {
+ axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ axi_chan_disable(&chip->dw->chan[i]);
+ }
+}
+
+static u32 axi_chan_get_xfer_width(struct axi_dma_chan *chan, dma_addr_t src,
+ dma_addr_t dst, size_t len)
+{
+ u32 max_width = chan->chip->dw->hdata->m_data_width;
+ size_t sdl = (src | dst | len);
+
+ return min_t(size_t, __ffs(sdl), max_width);
+}
+
+static inline const char *axi_chan_name(struct axi_dma_chan *chan)
+{
+ return dma_chan_name(&chan->vc.chan);
+}
+
+static struct axi_dma_desc *axi_desc_get(struct axi_dma_chan *chan)
+{
+ struct dw_axi_dma *dw = chan->chip->dw;
+ struct axi_dma_desc *desc;
+ dma_addr_t phys;
+
+ desc = dma_pool_zalloc(dw->desc_pool, GFP_NOWAIT, &phys);
+ if (unlikely(!desc)) {
+ dev_err(chan2dev(chan), "%s: not enough descriptors available\n",
+ axi_chan_name(chan));
+ return NULL;
+ }
+
+ atomic_inc(&chan->descs_allocated);
+ INIT_LIST_HEAD(&desc->xfer_list);
+ desc->vd.tx.phys = phys;
+ desc->chan = chan;
+
+ return desc;
+}
+
+static void axi_desc_put(struct axi_dma_desc *desc)
+{
+ struct axi_dma_chan *chan = desc->chan;
+ struct dw_axi_dma *dw = chan->chip->dw;
+ struct axi_dma_desc *child, *_next;
+ unsigned int descs_put = 0;
+
+ list_for_each_entry_safe(child, _next, &desc->xfer_list, xfer_list) {
+ list_del(&child->xfer_list);
+ dma_pool_free(dw->desc_pool, child, child->vd.tx.phys);
+ descs_put++;
+ }
+
+ dma_pool_free(dw->desc_pool, desc, desc->vd.tx.phys);
+ descs_put++;
+
+ atomic_sub(descs_put, &chan->descs_allocated);
+ dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n",
+ axi_chan_name(chan), descs_put,
+ atomic_read(&chan->descs_allocated));
+}
+
+static void vchan_desc_put(struct virt_dma_desc *vdesc)
+{
+ axi_desc_put(vd_to_axi_desc(vdesc));
+}
+
+static enum dma_status
+dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ enum dma_status ret;
+
+ ret = dma_cookie_status(dchan, cookie, txstate);
+
+ if (chan->is_paused && ret == DMA_IN_PROGRESS)
+ return DMA_PAUSED;
+
+ return ret;
+}
+
+static void write_desc_llp(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.llp = cpu_to_le64(adr);
+}
+
+static void write_chan_llp(struct axi_dma_chan *chan, dma_addr_t adr)
+{
+ axi_chan_iowrite64(chan, CH_LLP, adr);
+}
+
+/* Called in chan locked context */
+static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
+ struct axi_dma_desc *first)
+{
+ u32 reg, irq_mask;
+ u8 lms = 0;
+ u32 priority = chan->chip->dw->hdata->priority[chan->id];
+
+ if (unlikely(axi_chan_is_hw_enable(chan))) {
+ dev_err(chan2dev(chan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+
+ return;
+ }
+
+ axi_dma_enable(chan->chip);
+
+ reg = (DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_DST_MULTBLK_TYPE_POS |
+ DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
+ axi_chan_iowrite32(chan, CH_CFG_L, reg);
+
+ reg = (DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC << CH_CFG_H_TT_FC_POS |
+ priority << CH_CFG_H_PRIORITY_POS |
+ DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_DST_POS |
+ DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_SRC_POS);
+ axi_chan_iowrite32(chan, CH_CFG_H, reg);
+
+ write_chan_llp(chan, first->vd.tx.phys | lms);
+
+ irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR;
+ axi_chan_irq_sig_set(chan, irq_mask);
+
+ /* Generate 'suspend' status but don't generate interrupt */
+ irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
+ axi_chan_irq_set(chan, irq_mask);
+
+ axi_chan_enable(chan);
+}
+
+static void axi_chan_start_first_queued(struct axi_dma_chan *chan)
+{
+ struct axi_dma_desc *desc;
+ struct virt_dma_desc *vd;
+
+ vd = vchan_next_desc(&chan->vc);
+ if (!vd)
+ return;
+
+ desc = vd_to_axi_desc(vd);
+ dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan),
+ vd->tx.cookie);
+ axi_chan_block_xfer_start(chan, desc);
+}
+
+static void dma_chan_issue_pending(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+ if (vchan_issue_pending(&chan->vc))
+ axi_chan_start_first_queued(chan);
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+
+ /* ASSERT: channel hw is idle */
+ if (axi_chan_is_hw_enable(chan)) {
+ dev_err(chan2dev(chan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+ return -EBUSY;
+ }
+
+ dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan));
+
+ dma_cookie_init(dchan);
+
+ pm_runtime_get(chan->chip->dev);
+
+ return 0;
+}
+
+static void dma_chan_free_chan_resources(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+
+ /* ASSERT: channel hw is idle */
+ if (axi_chan_is_hw_enable(chan))
+ dev_err(dchan2dev(dchan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+
+ axi_chan_disable(chan);
+ axi_chan_irq_disable(chan, DWAXIDMAC_IRQ_ALL);
+
+ vchan_free_chan_resources(&chan->vc);
+
+ dev_vdbg(dchan2dev(dchan), "%s: %s: descriptor still allocated: %u\n",
+ __func__, axi_chan_name(chan),
+ atomic_read(&chan->descs_allocated));
+
+ pm_runtime_put(chan->chip->dev);
+}
+
+/*
+ * If DW_axi_dmac sees CHx_CTL.ShadowReg_Or_LLI_Last bit of the fetched LLI
+ * as 1, it understands that the current block is the final block in the
+ * transfer and completes the DMA transfer operation at the end of current
+ * block transfer.
+ */
+static void set_desc_last(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ val = le32_to_cpu(desc->lli.ctl_hi);
+ val |= CH_CTL_H_LLI_LAST;
+ desc->lli.ctl_hi = cpu_to_le32(val);
+}
+
+static void write_desc_sar(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.sar = cpu_to_le64(adr);
+}
+
+static void write_desc_dar(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.dar = cpu_to_le64(adr);
+}
+
+static void set_desc_src_master(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ /* Select AXI0 for source master */
+ val = le32_to_cpu(desc->lli.ctl_lo);
+ val &= ~CH_CTL_L_SRC_MAST;
+ desc->lli.ctl_lo = cpu_to_le32(val);
+}
+
+static void set_desc_dest_master(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ /* Select AXI1 for source master if available */
+ val = le32_to_cpu(desc->lli.ctl_lo);
+ if (desc->chan->chip->dw->hdata->nr_masters > 1)
+ val |= CH_CTL_L_DST_MAST;
+ else
+ val &= ~CH_CTL_L_DST_MAST;
+
+ desc->lli.ctl_lo = cpu_to_le32(val);
+}
+
+static struct dma_async_tx_descriptor *
+dma_chan_prep_dma_sg(struct dma_chan *dchan,
+ struct scatterlist *dst_sg, unsigned int dst_nents,
+ struct scatterlist *src_sg, unsigned int src_nents,
+ unsigned long flags)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ struct axi_dma_desc *first = NULL, *desc = NULL, *prev = NULL;
+ size_t dst_len = 0, src_len = 0, xfer_len = 0;
+ dma_addr_t dst_adr = 0, src_adr = 0;
+ u32 src_width, dst_width;
+ size_t block_ts, max_block_ts;
+ u32 reg;
+ u8 lms = 0;
+
+ dev_dbg(chan2dev(chan), "%s: %s: sn: %d dn: %d flags: 0x%lx",
+ __func__, axi_chan_name(chan), src_nents, dst_nents, flags);
+
+ if (unlikely(dst_nents == 0 || src_nents == 0))
+ return NULL;
+
+ if (unlikely(dst_sg == NULL || src_sg == NULL))
+ return NULL;
+
+ max_block_ts = chan->chip->dw->hdata->block_size[chan->id];
+
+ /*
+ * Loop until there is either no more source or no more destination
+ * scatterlist entry.
+ */
+ while ((dst_len || (dst_sg && dst_nents)) &&
+ (src_len || (src_sg && src_nents))) {
+ if (dst_len == 0) {
+ dst_adr = sg_dma_address(dst_sg);
+ dst_len = sg_dma_len(dst_sg);
+
+ dst_sg = sg_next(dst_sg);
+ dst_nents--;
+ }
+
+ /* Process src sg list */
+ if (src_len == 0) {
+ src_adr = sg_dma_address(src_sg);
+ src_len = sg_dma_len(src_sg);
+
+ src_sg = sg_next(src_sg);
+ src_nents--;
+ }
+
+ /* Min of src and dest length will be this xfer length */
+ xfer_len = min_t(size_t, src_len, dst_len);
+ if (xfer_len == 0)
+ continue;
+
+ /* Take care for the alignment */
+ src_width = axi_chan_get_xfer_width(chan, src_adr,
+ dst_adr, xfer_len);
+ /*
+ * Actually src_width and dst_width can be different, but make
+ * them same to be simpler.
+ */
+ dst_width = src_width;
+
+ /*
+ * block_ts indicates the total number of data of width
+ * src_width to be transferred in a DMA block transfer.
+ * BLOCK_TS register should be set to block_ts -1
+ */
+ block_ts = xfer_len >> src_width;
+ if (block_ts > max_block_ts) {
+ block_ts = max_block_ts;
+ xfer_len = max_block_ts << src_width;
+ }
+
+ desc = axi_desc_get(chan);
+ if (unlikely(!desc))
+ goto err_desc_get;
+
+ write_desc_sar(desc, src_adr);
+ write_desc_dar(desc, dst_adr);
+ desc->lli.block_ts_lo = cpu_to_le32(block_ts - 1);
+ desc->lli.ctl_hi = cpu_to_le32(CH_CTL_H_LLI_VALID);
+
+ reg = (DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+ DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS |
+ dst_width << CH_CTL_L_DST_WIDTH_POS |
+ src_width << CH_CTL_L_SRC_WIDTH_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS);
+ desc->lli.ctl_lo = cpu_to_le32(reg);
+
+ set_desc_src_master(desc);
+ set_desc_dest_master(desc);
+
+ /* Manage transfer list (xfer_list) */
+ if (!first) {
+ first = desc;
+ } else {
+ list_add_tail(&desc->xfer_list, &first->xfer_list);
+ write_desc_llp(prev, desc->vd.tx.phys | lms);
+ }
+ prev = desc;
+
+ /* update the lengths and addresses for the next loop cycle */
+ dst_len -= xfer_len;
+ src_len -= xfer_len;
+ dst_adr += xfer_len;
+ src_adr += xfer_len;
+ }
+
+ /* Total len of src/dest sg == 0, so no descriptor were allocated */
+ if (unlikely(!first))
+ return NULL;
+
+ /* Set end-of-link to the last link descriptor of list */
+ set_desc_last(desc);
+
+ return vchan_tx_prep(&chan->vc, &first->vd, flags);
+
+err_desc_get:
+ axi_desc_put(first);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *
+dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ unsigned int nents = 1;
+ struct scatterlist dst_sg;
+ struct scatterlist src_sg;
+
+ sg_init_table(&dst_sg, nents);
+ sg_init_table(&src_sg, nents);
+
+ sg_dma_address(&dst_sg) = dest;
+ sg_dma_address(&src_sg) = src;
+
+ sg_dma_len(&dst_sg) = len;
+ sg_dma_len(&src_sg) = len;
+
+ /* Implement memcpy transfer as sg transfer with single list */
+ return dma_chan_prep_dma_sg(dchan, &dst_sg, nents,
+ &src_sg, nents, flags);
+}
+
+static void axi_chan_dump_lli(struct axi_dma_chan *chan,
+ struct axi_dma_desc *desc)
+{
+ dev_err(dchan2dev(&chan->vc.chan),
+ "SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x",
+ le64_to_cpu(desc->lli.sar),
+ le64_to_cpu(desc->lli.dar),
+ le64_to_cpu(desc->lli.llp),
+ le32_to_cpu(desc->lli.block_ts_lo),
+ le32_to_cpu(desc->lli.ctl_hi),
+ le32_to_cpu(desc->lli.ctl_lo));
+}
+
+static void axi_chan_list_dump_lli(struct axi_dma_chan *chan,
+ struct axi_dma_desc *desc_head)
+{
+ struct axi_dma_desc *desc;
+
+ axi_chan_dump_lli(chan, desc_head);
+ list_for_each_entry(desc, &desc_head->xfer_list, xfer_list)
+ axi_chan_dump_lli(chan, desc);
+}
+
+
+static void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
+{
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ axi_chan_disable(chan);
+
+ /* The bad descriptor currently is in the head of vc list */
+ vd = vchan_next_desc(&chan->vc);
+ /* Remove the completed descriptor from issued list */
+ list_del(&vd->node);
+
+ /* WARN about bad descriptor */
+ dev_err(chan2dev(chan),
+ "Bad descriptor submitted for %s, cookie: %d, irq: 0x%08x\n",
+ axi_chan_name(chan), vd->tx.cookie, status);
+ axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd));
+
+ /* Pretend the bad descriptor completed successfully */
+ vchan_cookie_complete(vd);
+
+ /* Try to restart the controller */
+ axi_chan_start_first_queued(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
+{
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+ if (unlikely(axi_chan_is_hw_enable(chan))) {
+ dev_err(chan2dev(chan), "BUG: %s catched DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n",
+ axi_chan_name(chan));
+ axi_chan_disable(chan);
+ }
+
+ /* The completed descriptor currently is in the head of vc list */
+ vd = vchan_next_desc(&chan->vc);
+ /* Remove the completed descriptor from issued list before completing */
+ list_del(&vd->node);
+ vchan_cookie_complete(vd);
+
+ /* Submit queued descriptors after processing the completed ones */
+ axi_chan_start_first_queued(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static irqreturn_t dw_axi_dma_intretupt(int irq, void *dev_id)
+{
+ struct axi_dma_chip *chip = dev_id;
+ struct dw_axi_dma *dw = chip->dw;
+ struct axi_dma_chan *chan;
+
+ u32 status, i;
+
+ /* Disable DMAC inerrupts. We'll enable them after processing chanels */
+ axi_dma_irq_disable(chip);
+
+ /* Poll, clear and process every chanel interrupt status */
+ for (i = 0; i < dw->hdata->nr_channels; i++) {
+ chan = &dw->chan[i];
+ status = axi_chan_irq_read(chan);
+ axi_chan_irq_clear(chan, status);
+
+ dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n",
+ axi_chan_name(chan), i, status);
+
+ if (status & DWAXIDMAC_IRQ_ALL_ERR)
+ axi_chan_handle_err(chan, status);
+ else if (status & DWAXIDMAC_IRQ_DMA_TRF)
+ axi_chan_block_xfer_complete(chan);
+ }
+
+ /* Re-enable interrupts */
+ axi_dma_irq_enable(chip);
+
+ return IRQ_HANDLED;
+}
+
+static int dma_chan_terminate_all(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ axi_chan_disable(chan);
+
+ vchan_get_all_descriptors(&chan->vc, &head);
+
+ /*
+ * As vchan_dma_desc_free_list can access to desc_allocated list
+ * we need to call it in vc.lock context.
+ */
+ vchan_dma_desc_free_list(&chan->vc, &head);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ dev_vdbg(dchan2dev(dchan), "terminated: %s\n", axi_chan_name(chan));
+
+ return 0;
+}
+
+static int dma_chan_pause(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+ unsigned int timeout = 20; /* timeout iterations */
+ int ret = -EAGAIN;
+ u32 val;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT |
+ BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+
+ while (timeout--) {
+ if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED) {
+ ret = 0;
+ break;
+ }
+ udelay(2);
+ }
+
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+
+ chan->is_paused = true;
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ return ret;
+}
+
+/* Called in chan locked context */
+static inline void axi_chan_resume(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT);
+ val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT);
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+
+ chan->is_paused = false;
+}
+
+static int dma_chan_resume(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ if (chan->is_paused)
+ axi_chan_resume(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ return 0;
+}
+
+static int axi_dma_runtime_suspend(struct device *dev)
+{
+ struct axi_dma_chip *chip = dev_get_drvdata(dev);
+
+ dev_info(dev, "PAL: %s\n", __func__);
+
+ axi_dma_irq_disable(chip);
+ axi_dma_disable(chip);
+
+ clk_disable_unprepare(chip->clk);
+
+ return 0;
+}
+
+static int axi_dma_runtime_resume(struct device *dev)
+{
+ struct axi_dma_chip *chip = dev_get_drvdata(dev);
+ int ret = 0;
+
+ dev_info(dev, "PAL: %s\n", __func__);
+
+ ret = clk_prepare_enable(chip->clk);
+ if (ret < 0)
+ return ret;
+
+ axi_dma_enable(chip);
+ axi_dma_irq_enable(chip);
+
+ return 0;
+}
+
+static int parse_device_properties(struct axi_dma_chip *chip)
+{
+ struct device *dev = chip->dev;
+ u32 tmp, carr[DMAC_MAX_CHANNELS];
+ int ret;
+
+ ret = device_property_read_u32(dev, "dma-channels", &tmp);
+ if (ret)
+ return ret;
+ if (tmp == 0 || tmp > DMAC_MAX_CHANNELS)
+ return -EINVAL;
+
+ chip->dw->hdata->nr_channels = tmp;
+
+ ret = device_property_read_u32(dev, "snps,dma-masters", &tmp);
+ if (ret)
+ return ret;
+ if (tmp == 0 || tmp > DMAC_MAX_MASTERS)
+ return -EINVAL;
+
+ chip->dw->hdata->nr_masters = tmp;
+
+ ret = device_property_read_u32(dev, "snps,data-width", &tmp);
+ if (ret)
+ return ret;
+ if (tmp > DWAXIDMAC_TRANS_WIDTH_MAX)
+ return -EINVAL;
+
+ chip->dw->hdata->m_data_width = tmp;
+
+ ret = device_property_read_u32_array(dev, "snps,block-size", carr,
+ chip->dw->hdata->nr_channels);
+ if (ret)
+ return ret;
+ for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++)
+ if (carr[tmp] == 0 || carr[tmp] > DMAC_MAX_BLK_SIZE)
+ return -EINVAL;
+ else
+ chip->dw->hdata->block_size[tmp] = carr[tmp];
+
+ ret = device_property_read_u32_array(dev, "snps,priority", carr,
+ chip->dw->hdata->nr_channels);
+ if (ret)
+ return ret;
+ /* Priority value must be programmed within [0:nr_channels-1] range */
+ for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++)
+ if (carr[tmp] >= chip->dw->hdata->nr_channels)
+ return -EINVAL;
+ else
+ chip->dw->hdata->priority[tmp] = carr[tmp];
+
+ return 0;
+}
+
+static int dw_probe(struct platform_device *pdev)
+{
+ struct axi_dma_chip *chip;
+ struct resource *mem;
+ struct dw_axi_dma *dw;
+ struct dw_axi_dma_hcfg *hdata;
+ u32 i;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ hdata = devm_kzalloc(&pdev->dev, sizeof(*hdata), GFP_KERNEL);
+ if (!hdata)
+ return -ENOMEM;
+
+ chip->dw = dw;
+ chip->dev = &pdev->dev;
+ chip->dw->hdata = hdata;
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0)
+ return chip->irq;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->regs = devm_ioremap_resource(chip->dev, mem);
+ if (IS_ERR(chip->regs))
+ return PTR_ERR(chip->regs);
+
+ chip->clk = devm_clk_get(chip->dev, NULL);
+ if (IS_ERR(chip->clk))
+ return PTR_ERR(chip->clk);
+
+ ret = parse_device_properties(chip);
+ if (ret)
+ return ret;
+
+ dw->chan = devm_kcalloc(chip->dev, hdata->nr_channels,
+ sizeof(*dw->chan), GFP_KERNEL);
+ if (!dw->chan)
+ return -ENOMEM;
+
+ ret = devm_request_irq(chip->dev, chip->irq, dw_axi_dma_intretupt,
+ IRQF_SHARED, DRV_NAME, chip);
+ if (ret)
+ return ret;
+
+ /* Lli address must be aligned to a 64-byte boundary */
+ dw->desc_pool = dmam_pool_create(DRV_NAME, chip->dev,
+ sizeof(struct axi_dma_desc), 64, 0);
+ if (!dw->desc_pool) {
+ dev_err(chip->dev, "No memory for descriptors dma pool\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&dw->dma.channels);
+ for (i = 0; i < hdata->nr_channels; i++) {
+ struct axi_dma_chan *chan = &dw->chan[i];
+
+ chan->chip = chip;
+ chan->id = (u8)i;
+ chan->chan_regs = chip->regs + COMMON_REG_LEN + i * CHAN_REG_LEN;
+ atomic_set(&chan->descs_allocated, 0);
+
+ chan->vc.desc_free = vchan_desc_put;
+ vchan_init(&chan->vc, &dw->dma);
+ }
+
+ /* Set capabilities */
+ dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
+ dma_cap_set(DMA_SG, dw->dma.cap_mask);
+
+ /* DMA capabilities */
+ dw->dma.chancnt = hdata->nr_channels;
+ dw->dma.src_addr_widths = AXI_DMA_BUSWIDTHS;
+ dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS;
+ dw->dma.directions = BIT(DMA_MEM_TO_MEM);
+ dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+
+ dw->dma.dev = chip->dev;
+ dw->dma.device_tx_status = dma_chan_tx_status;
+ dw->dma.device_issue_pending = dma_chan_issue_pending;
+ dw->dma.device_terminate_all = dma_chan_terminate_all;
+ dw->dma.device_pause = dma_chan_pause;
+ dw->dma.device_resume = dma_chan_resume;
+
+ dw->dma.device_alloc_chan_resources = dma_chan_alloc_chan_resources;
+ dw->dma.device_free_chan_resources = dma_chan_free_chan_resources;
+
+ dw->dma.device_prep_dma_memcpy = dma_chan_prep_dma_memcpy;
+ dw->dma.device_prep_dma_sg = dma_chan_prep_dma_sg;
+
+ platform_set_drvdata(pdev, chip);
+
+ pm_runtime_enable(chip->dev);
+
+ /*
+ * We can't just call pm_runtime_get here instead of
+ * pm_runtime_get_noresume + axi_dma_runtime_resume because we need
+ * driver to work also without Runtime PM.
+ */
+ pm_runtime_get_noresume(chip->dev);
+ ret = axi_dma_runtime_resume(chip->dev);
+ if (ret < 0)
+ goto err_pm_disable;
+
+ axi_dma_hw_init(chip);
+
+ pm_runtime_put(chip->dev);
+
+ ret = dma_async_device_register(&dw->dma);
+ if (ret)
+ goto err_pm_disable;
+
+ dev_info(chip->dev, "DesignWare AXI DMA Controller, %d channels\n",
+ dw->hdata->nr_channels);
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(chip->dev);
+
+ return ret;
+}
+
+static int dw_remove(struct platform_device *pdev)
+{
+ struct axi_dma_chip *chip = platform_get_drvdata(pdev);
+ struct dw_axi_dma *dw = chip->dw;
+ struct axi_dma_chan *chan, *_chan;
+ u32 i;
+
+ /* Enable clk before accessing to registers */
+ clk_prepare_enable(chip->clk);
+ axi_dma_irq_disable(chip);
+ for (i = 0; i < dw->hdata->nr_channels; i++) {
+ axi_chan_disable(&chip->dw->chan[i]);
+ axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ }
+ axi_dma_disable(chip);
+
+ pm_runtime_disable(chip->dev);
+ axi_dma_runtime_suspend(chip->dev);
+
+ devm_free_irq(chip->dev, chip->irq, chip);
+
+ list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
+ vc.chan.device_node) {
+ list_del(&chan->vc.chan.device_node);
+ tasklet_kill(&chan->vc.task);
+ }
+
+ dma_async_device_unregister(&dw->dma);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_axi_dma_pm_ops = {
+ SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
+};
+
+static const struct of_device_id dw_dma_of_id_table[] = {
+ { .compatible = "snps,axi-dma-1.01a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
+
+static struct platform_driver dw_driver = {
+ .probe = dw_probe,
+ .remove = dw_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(dw_dma_of_id_table),
+ .pm = &dw_axi_dma_pm_ops,
+ },
+};
+module_platform_driver(dw_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Synopsys DesignWare AXI DMA Controller platform driver");
+MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
diff --git a/drivers/dma/axi_dma_platform.h b/drivers/dma/axi_dma_platform.h
new file mode 100644
index 0000000..3d644d2
--- /dev/null
+++ b/drivers/dma/axi_dma_platform.h
@@ -0,0 +1,119 @@
+/*
+ * Synopsys DesignWare AXI DMA Controller driver.
+ *
+ * Copyright (C) 2017 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _AXI_DMA_PLATFORM_H
+#define _AXI_DMA_PLATFORM_H
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+#include "virt-dma.h"
+
+#define DMAC_MAX_CHANNELS 8
+#define DMAC_MAX_MASTERS 2
+#define DMAC_MAX_BLK_SIZE 0x200000
+
+struct dw_axi_dma_hcfg {
+ u32 nr_channels;
+ u32 nr_masters;
+ u32 m_data_width;
+ u32 block_size[DMAC_MAX_CHANNELS];
+ u32 priority[DMAC_MAX_CHANNELS];
+};
+
+struct axi_dma_chan {
+ struct axi_dma_chip *chip;
+ void __iomem *chan_regs;
+ u8 id;
+ atomic_t descs_allocated;
+
+ struct virt_dma_chan vc;
+
+ /* these other elements are all protected by vc.lock */
+ bool is_paused;
+};
+
+struct dw_axi_dma {
+ struct dma_device dma;
+ struct dw_axi_dma_hcfg *hdata;
+ struct dma_pool *desc_pool;
+
+ /* channels */
+ struct axi_dma_chan *chan;
+};
+
+struct axi_dma_chip {
+ struct device *dev;
+ int irq;
+ void __iomem *regs;
+ struct clk *clk;
+ struct dw_axi_dma *dw;
+};
+
+/* LLI == Linked List Item */
+struct __attribute__ ((__packed__)) axi_dma_lli {
+ __le64 sar;
+ __le64 dar;
+ __le32 block_ts_lo;
+ __le32 block_ts_hi;
+ __le64 llp;
+ __le32 ctl_lo;
+ __le32 ctl_hi;
+ __le32 sstat;
+ __le32 dstat;
+ __le32 status_lo;
+ __le32 ststus_hi;
+ __le32 reserved_lo;
+ __le32 reserved_hi;
+};
+
+struct axi_dma_desc {
+ struct axi_dma_lli lli;
+
+ struct virt_dma_desc vd;
+ struct axi_dma_chan *chan;
+ struct list_head xfer_list;
+};
+
+static inline struct device *dchan2dev(struct dma_chan *dchan)
+{
+ return &dchan->dev->device;
+}
+
+static inline struct device *chan2dev(struct axi_dma_chan *chan)
+{
+ return &chan->vc.chan.dev->device;
+}
+
+static inline struct axi_dma_desc *vd_to_axi_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct axi_dma_desc, vd);
+}
+
+static inline struct axi_dma_chan *vc_to_axi_dma_chan(struct virt_dma_chan *vc)
+{
+ return container_of(vc, struct axi_dma_chan, vc);
+}
+
+static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
+{
+ return vc_to_axi_dma_chan(to_virt_chan(dchan));
+}
+
+#endif /* _AXI_DMA_PLATFORM_H */
diff --git a/drivers/dma/axi_dma_platform_reg.h b/drivers/dma/axi_dma_platform_reg.h
new file mode 100644
index 0000000..72110cc
--- /dev/null
+++ b/drivers/dma/axi_dma_platform_reg.h
@@ -0,0 +1,220 @@
+/*
+ * Synopsys DesignWare AXI DMA Controller driver.
+ *
+ * Copyright (C) 2017 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _AXI_DMA_PLATFORM_REG_H
+#define _AXI_DMA_PLATFORM_REG_H
+
+#include <linux/bitops.h>
+
+#define COMMON_REG_LEN 0x100
+#define CHAN_REG_LEN 0x100
+
+/* Common registers offset */
+#define DMAC_ID 0x000 /* R DMAC ID */
+#define DMAC_COMPVER 0x008 /* R DMAC Component Version */
+#define DMAC_CFG 0x010 /* R/W DMAC Configuration */
+#define DMAC_CHEN 0x018 /* R/W DMAC Channel Enable */
+#define DMAC_CHEN_L 0x018 /* R/W DMAC Channel Enable 00-31 */
+#define DMAC_CHEN_H 0x01C /* R/W DMAC Channel Enable 32-63 */
+#define DMAC_INTSTATUS 0x030 /* R DMAC Interrupt Status */
+#define DMAC_COMMON_INTCLEAR 0x038 /* W DMAC Interrupt Clear */
+#define DMAC_COMMON_INTSTATUS_ENA 0x040 /* R DMAC Interrupt Status Enable */
+#define DMAC_COMMON_INTSIGNAL_ENA 0x048 /* R/W DMAC Interrupt Signal Enable */
+#define DMAC_COMMON_INTSTATUS 0x050 /* R DMAC Interrupt Status */
+#define DMAC_RESET 0x058 /* R DMAC Reset Register1 */
+
+/* DMA channel registers offset */
+#define CH_SAR 0x000 /* R/W Chan Source Address */
+#define CH_DAR 0x008 /* R/W Chan Destination Address */
+#define CH_BLOCK_TS 0x010 /* R/W Chan Block Transfer Size */
+#define CH_CTL 0x018 /* R/W Chan Control */
+#define CH_CTL_L 0x018 /* R/W Chan Control 00-31 */
+#define CH_CTL_H 0x01C /* R/W Chan Control 32-63 */
+#define CH_CFG 0x020 /* R/W Chan Configuration */
+#define CH_CFG_L 0x020 /* R/W Chan Configuration 00-31 */
+#define CH_CFG_H 0x024 /* R/W Chan Configuration 32-63 */
+#define CH_LLP 0x028 /* R/W Chan Linked List Pointer */
+#define CH_STATUS 0x030 /* R Chan Status */
+#define CH_SWHSSRC 0x038 /* R/W Chan SW Handshake Source */
+#define CH_SWHSDST 0x040 /* R/W Chan SW Handshake Destination */
+#define CH_BLK_TFR_RESUMEREQ 0x048 /* W Chan Block Transfer Resume Req */
+#define CH_AXI_ID 0x050 /* R/W Chan AXI ID */
+#define CH_AXI_QOS 0x058 /* R/W Chan AXI QOS */
+#define CH_SSTAT 0x060 /* R Chan Source Status */
+#define CH_DSTAT 0x068 /* R Chan Destination Status */
+#define CH_SSTATAR 0x070 /* R/W Chan Source Status Fetch Addr */
+#define CH_DSTATAR 0x078 /* R/W Chan Destination Status Fetch Addr */
+#define CH_INTSTATUS_ENA 0x080 /* R/W Chan Interrupt Status Enable */
+#define CH_INTSTATUS 0x088 /* R/W Chan Interrupt Status */
+#define CH_INTSIGNAL_ENA 0x090 /* R/W Chan Interrupt Signal Enable */
+#define CH_INTCLEAR 0x098 /* W Chan Interrupt Clear */
+
+
+/* DMAC_CFG */
+#define DMAC_EN_MASK 0x00000001U
+#define DMAC_EN_POS 0
+
+#define INT_EN_MASK 0x00000002U
+#define INT_EN_POS 1
+
+#define DMAC_CHAN_EN_SHIFT 0
+#define DMAC_CHAN_EN_WE_SHIFT 8
+
+#define DMAC_CHAN_SUSP_SHIFT 16
+#define DMAC_CHAN_SUSP_WE_SHIFT 24
+
+/* CH_CTL_H */
+#define CH_CTL_H_LLI_LAST BIT(30)
+#define CH_CTL_H_LLI_VALID BIT(31)
+
+/* CH_CTL_L */
+#define CH_CTL_L_LAST_WRITE_EN BIT(30)
+
+#define CH_CTL_L_DST_MSIZE_POS 18
+#define CH_CTL_L_SRC_MSIZE_POS 14
+
+enum {
+ DWAXIDMAC_BURST_TRANS_LEN_1 = 0x0,
+ DWAXIDMAC_BURST_TRANS_LEN_4,
+ DWAXIDMAC_BURST_TRANS_LEN_8,
+ DWAXIDMAC_BURST_TRANS_LEN_16,
+ DWAXIDMAC_BURST_TRANS_LEN_32,
+ DWAXIDMAC_BURST_TRANS_LEN_64,
+ DWAXIDMAC_BURST_TRANS_LEN_128,
+ DWAXIDMAC_BURST_TRANS_LEN_256,
+ DWAXIDMAC_BURST_TRANS_LEN_512,
+ DWAXIDMAC_BURST_TRANS_LEN_1024
+};
+
+#define CH_CTL_L_DST_WIDTH_POS 11
+#define CH_CTL_L_SRC_WIDTH_POS 8
+
+#define CH_CTL_L_DST_INC_POS 6
+#define CH_CTL_L_SRC_INC_POS 4
+enum {
+ DWAXIDMAC_CH_CTL_L_INC = 0x0,
+ DWAXIDMAC_CH_CTL_L_NOINC
+};
+
+#define CH_CTL_L_DST_MAST BIT(2)
+#define CH_CTL_L_SRC_MAST BIT(0)
+
+/* CH_CFG_H */
+#define CH_CFG_H_PRIORITY_POS 17
+#define CH_CFG_H_HS_SEL_DST_POS 4
+#define CH_CFG_H_HS_SEL_SRC_POS 3
+enum {
+ DWAXIDMAC_HS_SEL_HW = 0x0,
+ DWAXIDMAC_HS_SEL_SW
+};
+
+#define CH_CFG_H_TT_FC_POS 0
+enum {
+ DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC = 0x0,
+ DWAXIDMAC_TT_FC_MEM_TO_PER_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_MEM_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_PER_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_MEM_SRC,
+ DWAXIDMAC_TT_FC_PER_TO_PER_SRC,
+ DWAXIDMAC_TT_FC_MEM_TO_PER_DST,
+ DWAXIDMAC_TT_FC_PER_TO_PER_DST
+};
+
+/* CH_CFG_L */
+#define CH_CFG_L_DST_MULTBLK_TYPE_POS 2
+#define CH_CFG_L_SRC_MULTBLK_TYPE_POS 0
+enum {
+ DWAXIDMAC_MBLK_TYPE_CONTIGUOUS = 0x0,
+ DWAXIDMAC_MBLK_TYPE_RELOAD,
+ DWAXIDMAC_MBLK_TYPE_SHADOW_REG,
+ DWAXIDMAC_MBLK_TYPE_LL
+};
+
+/**
+ * Dw axi dma channel interrupts
+ *
+ * @DWAXIDMAC_IRQ_NONE: Bitmask of no one interrupt
+ * @DWAXIDMAC_IRQ_BLOCK_TRF: Block transfer complete
+ * @DWAXIDMAC_IRQ_DMA_TRF: Dma transfer complete
+ * @DWAXIDMAC_IRQ_SRC_TRAN: Source transaction complete
+ * @DWAXIDMAC_IRQ_DST_TRAN: Destination transaction complete
+ * @DWAXIDMAC_IRQ_SRC_DEC_ERR: Source decode error
+ * @DWAXIDMAC_IRQ_DST_DEC_ERR: Destination decode error
+ * @DWAXIDMAC_IRQ_SRC_SLV_ERR: Source slave error
+ * @DWAXIDMAC_IRQ_DST_SLV_ERR: Destination slave error
+ * @DWAXIDMAC_IRQ_LLI_RD_DEC_ERR: LLI read decode error
+ * @DWAXIDMAC_IRQ_LLI_WR_DEC_ERR: LLI write decode error
+ * @DWAXIDMAC_IRQ_LLI_RD_SLV_ERR: LLI read slave error
+ * @DWAXIDMAC_IRQ_LLI_WR_SLV_ERR: LLI write slave error
+ * @DWAXIDMAC_IRQ_INVALID_ERR: LLI invalide error or Shadow register error
+ * @DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR: Slave Interface Multiblock type error
+ * @DWAXIDMAC_IRQ_DEC_ERR: Slave Interface decode error
+ * @DWAXIDMAC_IRQ_WR2RO_ERR: Slave Interface write to read only error
+ * @DWAXIDMAC_IRQ_RD2RWO_ERR: Slave Interface read to write only error
+ * @DWAXIDMAC_IRQ_WRONCHEN_ERR: Slave Interface write to channel error
+ * @DWAXIDMAC_IRQ_SHADOWREG_ERR: Slave Interface shadow reg error
+ * @DWAXIDMAC_IRQ_WRONHOLD_ERR: Slave Interface hold error
+ * @DWAXIDMAC_IRQ_LOCK_CLEARED: Lock Cleared Status
+ * @DWAXIDMAC_IRQ_SRC_SUSPENDED: Source Suspended Status
+ * @DWAXIDMAC_IRQ_SUSPENDED: Channel Suspended Status
+ * @DWAXIDMAC_IRQ_DISABLED: Channel Disabled Status
+ * @DWAXIDMAC_IRQ_ABORTED: Channel Aborted Status
+ * @DWAXIDMAC_IRQ_ALL_ERR: Bitmask of all error interrupts
+ * @DWAXIDMAC_IRQ_ALL: Bitmask of all interrupts
+ */
+enum {
+ DWAXIDMAC_IRQ_NONE = 0x0,
+ DWAXIDMAC_IRQ_BLOCK_TRF = BIT(0),
+ DWAXIDMAC_IRQ_DMA_TRF = BIT(1),
+ DWAXIDMAC_IRQ_SRC_TRAN = BIT(3),
+ DWAXIDMAC_IRQ_DST_TRAN = BIT(4),
+ DWAXIDMAC_IRQ_SRC_DEC_ERR = BIT(5),
+ DWAXIDMAC_IRQ_DST_DEC_ERR = BIT(6),
+ DWAXIDMAC_IRQ_SRC_SLV_ERR = BIT(7),
+ DWAXIDMAC_IRQ_DST_SLV_ERR = BIT(8),
+ DWAXIDMAC_IRQ_LLI_RD_DEC_ERR = BIT(9),
+ DWAXIDMAC_IRQ_LLI_WR_DEC_ERR = BIT(10),
+ DWAXIDMAC_IRQ_LLI_RD_SLV_ERR = BIT(11),
+ DWAXIDMAC_IRQ_LLI_WR_SLV_ERR = BIT(12),
+ DWAXIDMAC_IRQ_INVALID_ERR = BIT(13),
+ DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR = BIT(14),
+ DWAXIDMAC_IRQ_DEC_ERR = BIT(16),
+ DWAXIDMAC_IRQ_WR2RO_ERR = BIT(17),
+ DWAXIDMAC_IRQ_RD2RWO_ERR = BIT(18),
+ DWAXIDMAC_IRQ_WRONCHEN_ERR = BIT(19),
+ DWAXIDMAC_IRQ_SHADOWREG_ERR = BIT(20),
+ DWAXIDMAC_IRQ_WRONHOLD_ERR = BIT(21),
+ DWAXIDMAC_IRQ_LOCK_CLEARED = BIT(27),
+ DWAXIDMAC_IRQ_SRC_SUSPENDED = BIT(28),
+ DWAXIDMAC_IRQ_SUSPENDED = BIT(29),
+ DWAXIDMAC_IRQ_DISABLED = BIT(30),
+ DWAXIDMAC_IRQ_ABORTED = BIT(31),
+ DWAXIDMAC_IRQ_ALL_ERR = 0x003F7FE0,
+ DWAXIDMAC_IRQ_ALL = 0xFFFFFFFF
+};
+
+enum {
+ DWAXIDMAC_TRANS_WIDTH_8 = 0x0,
+ DWAXIDMAC_TRANS_WIDTH_16,
+ DWAXIDMAC_TRANS_WIDTH_32,
+ DWAXIDMAC_TRANS_WIDTH_64,
+ DWAXIDMAC_TRANS_WIDTH_128,
+ DWAXIDMAC_TRANS_WIDTH_256,
+ DWAXIDMAC_TRANS_WIDTH_512,
+ DWAXIDMAC_TRANS_WIDTH_MAX = DWAXIDMAC_TRANS_WIDTH_512
+};
+
+#endif /* _AXI_DMA_PLATFORM_H */
--
2.5.5
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: Document the Synopsys DW AXI DMA bindings
From: Eugeniy Paltsev @ 2017-04-07 14:04 UTC (permalink / raw)
To: dmaengine
Cc: devicetree, Andy Shevchenko, Vinod Koul, Alexey Brodkin,
linux-kernel, Rob Herring, Dan Williams, linux-snps-arc,
Eugeniy Paltsev
In-Reply-To: <1491573855-1039-1-git-send-email-Eugeniy.Paltsev@synopsys.com>
This patch adds documentation of device tree bindings for the Synopsys
DesignWare AXI DMA controller.
Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/dma/snps,axi-dw-dmac.txt | 34 ++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/snps,axi-dw-dmac.txt
diff --git a/Documentation/devicetree/bindings/dma/snps,axi-dw-dmac.txt b/Documentation/devicetree/bindings/dma/snps,axi-dw-dmac.txt
new file mode 100644
index 0000000..21666fe
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/snps,axi-dw-dmac.txt
@@ -0,0 +1,34 @@
+* Synopsys DesignWare AXI DMA Controller
+
+Required properties:
+- compatible: "snps,axi-dma-1.01a"
+- reg: Address range of the DMAC registers. This should include
+ all of the per-channel registers.
+- interrupt: Should contain the DMAC interrupt number.
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device.
+- dma-channels: Number of channels supported by hardware.
+- snps,dma-masters: Number of AXI masters supported by the hardware.
+- snps,data-width: Maximum AXI data width supported by hardware.
+ (0 - 8bits, 1 - 16bits, 2 - 32bits, ..., 6 - 512bits)
+- snps,priority: Priority of channel. Array size is equal to the number of
+ dma-channels. Priority value must be programmed within [0:dma-channels-1]
+ range. (0 - minimum priority)
+- snps,block-size: Maximum block size supported by the controller channel.
+ Array size is equal to the number of dma-channels.
+
+Example:
+
+dmac: dma-controller@80000 {
+ compatible = "snps,axi-dma-1.01a";
+ reg = <0x80000 0x400>;
+ clocks = <&core_clk>;
+ interrupt-parent = <&intc>;
+ interrupts = <27>;
+
+ dma-channels = <4>;
+ snps,dma-masters = <2>;
+ snps,data-width = <3>;
+ snps,block-size = <4096 4096 4096 4096>;
+ snps,priority = <0 1 2 3>;
+};
--
2.5.5
^ permalink raw reply related
* [PATCH v2 0/2] dmaengine: Add DW AXI DMAC driver
From: Eugeniy Paltsev @ 2017-04-07 14:04 UTC (permalink / raw)
To: dmaengine-u79uwXL29TY76Z2rM5mHXA
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-snps-arc-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Dan Williams,
Vinod Koul, Rob Herring, Andy Shevchenko, Alexey Brodkin,
Eugeniy Paltsev
This patch series add support for the DW AXI DMAC controller.
DW AXI DMAC is a part of upcoming development board from Synopsys.
In this driver implementation only DMA_MEMCPY and DMA_SG transfers
are supported.
Changes for v2:
* Use async version of runtime PM get/put callbacks.
* Use atomic_t (and corresponding operations) for allocated descriptors
counter.
* Use GFP_NOWAIT flag for allocating dma descriptors in "dma_pool_zalloc"
instead of GFP_ATOMIC flag.
* Add kernel-doc style comments for the irq enum, cleanup.
Changes for v1:
* Implement Runtime PM (the driver can operate with or without
Runtime PM support)
* Move submitting new txn to interrupt handler from tasklet
* Free IRQ manually in driver remove function
* Add 64 bit support
* Rid of subsys_initcall
* Use dev_vdbg instead dev_dbg in some places
* Rid of C99 style comments
* Add IP version to DT compatible string
Note:
* I left "is_paused" variable untouched. I checked the drivers which
have 'enum dma_status' field in their channel data structures -
there is no much sense to add enum to this driver channel data
structure - it will be used only for determinating is channel
paused or not.
* I left preparation of SG list according to max data width and
max block size in dma_chan_prep_dma_sg function.
* I left axi_chan_is_hw_enable assert untouched because it is
HW per-channel assert. It can't be managed by runtime PM.
Changes for v0:
* Switch to virt-dma API (according to previous RFC)
* Small fixies according to previous RFC
* Add DT bindings
Eugeniy Paltsev (2):
dt-bindings: Document the Synopsys DW AXI DMA bindings
dmaengine: Add DW AXI DMAC driver
.../devicetree/bindings/dma/snps,axi-dw-dmac.txt | 34 +
drivers/dma/Kconfig | 10 +
drivers/dma/Makefile | 1 +
drivers/dma/axi_dma_platform.c | 1044 ++++++++++++++++++++
drivers/dma/axi_dma_platform.h | 119 +++
drivers/dma/axi_dma_platform_reg.h | 220 +++++
6 files changed, 1428 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/snps,axi-dw-dmac.txt
create mode 100644 drivers/dma/axi_dma_platform.c
create mode 100644 drivers/dma/axi_dma_platform.h
create mode 100644 drivers/dma/axi_dma_platform_reg.h
--
2.5.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 06/14] MIPS: jz4740: DTS: Add nodes for ingenic pinctrl and gpio drivers
From: Paul Cercueil @ 2017-04-07 13:57 UTC (permalink / raw)
To: Linus Walleij
Cc: Alexandre Courbot, Rob Herring, Mark Rutland, Ralf Baechle,
Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, James Hogan,
linux-gpio, devicetree, linux-kernel, Linux MIPS, linux-mmc,
linux-mtd, linux-pwm, linux-fbdev
In-Reply-To: <CACRpkdbXe1Xxk93jqLXBdEDwWOnWD+CkZrqvok-PcmWxzBbSZA@mail.gmail.com>
Le 2017-04-07 11:44, Linus Walleij a écrit :
> On Sun, Apr 2, 2017 at 10:42 PM, Paul Cercueil <paul@crapouillou.net>
> wrote:
>
>> For a description of the pinctrl devicetree node, please read
>> Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
>>
>> For a description of the gpio devicetree nodes, please read
>> Documentation/devicetree/bindings/gpio/ingenic,gpio.txt
>>
>> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
>> ---
>> arch/mips/boot/dts/ingenic/jz4740.dtsi | 61
>> ++++++++++++++++++++++++++++++++++
>> 1 file changed, 61 insertions(+)
>>
>> v2: Changed the devicetree bindings to match the new driver
>> v3: No changes
>> v4: Update the bindings for the v4 version of the drivers
> (...)
>
>> + pinctrl: ingenic-pinctrl@10010000 {
>> + compatible = "ingenic,jz4740-pinctrl";
>> + reg = <0x10010000 0x400>;
>> +
>> + gpa: gpio-controller@0 {
>> + compatible = "ingenic,gpio-bank-a",
>> "ingenic,jz4740-gpio";
>
> As Sergei and Rob notes, the bank compatible properties look
> a bit strange. Especially if they are all the same essentially.
>
> I like Sergei's idea to simply use the reg property if what you want
> is really a unique ID number. What do you think about this?
>
> Yours,
> Linus Walleij
I think the 'reg' property makes more sense, yes. I'll fix this in the
v5, this
week-end. Do you think it can go in 4.12?
Thanks,
-Paul
^ permalink raw reply
* Re: [PATCH 0/4] arm64: renesas: enable M3ULCB board peripherals
From: Simon Horman @ 2017-04-07 13:52 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Vladimir Barinov, Magnus Damm, Rob Herring, Mark Rutland,
devicetree@vger.kernel.org, Linux-Renesas, Sjoerd Simons
In-Reply-To: <CAMuHMdX9caECvsBnfUQhbQKdW_uGJ_VXAUksMMXw7EAGP8QUCQ@mail.gmail.com>
On Thu, Apr 06, 2017 at 11:53:45AM +0200, Geert Uytterhoeven wrote:
> Hi Simon,
>
> On Fri, Mar 17, 2017 at 11:02 PM, Sjoerd Simons
> <sjoerd.simons@collabora.co.uk> wrote:
> > On Thu, 2017-01-26 at 17:53 +0300, Vladimir Barinov wrote:
> >> This adds the folowing:
> >> - R8A7796 SoC based M3ULCB board peripherals
> >>
> >> Vladimir Barinov (4):
> >> [1/4] arm64: dts: m3ulcb: enable I2C
> >> [2/4] arm64: dts: m3ulcb: Update memory node to 2 GiB map
I have queued up the above two patches.
> >> [3/4] arm64: dts: m3ulcb: enable EthernetAVB
Please update the above patch to reflect the changes made in
ef3f08c83fd1 ("arm64: dts: r8a7796: salvator-x: Fix EthernetAVB PHY timing")
> >> [4/4] arm64: dts: m3ulcb: enable HS200 for eMMC
I will look at queuing this up in the near future
with other HS200 enablement patches.
> > Seems these didn't hit -next just yet, for this series (tested on a
> > M3ULCB)
> >
> > Tested-By: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
>
> It seems this series is still pending? Any reason (not) to apply it?
Sorry for the extended delay.
^ permalink raw reply
* Re: [PATCH v3] clk: stm32h7: Add stm32h743 clock driver
From: kbuild test robot @ 2017-04-07 13:51 UTC (permalink / raw)
Cc: Mark Rutland, amelie.delaunay, Michael Turquette, Lee Jones,
linux-clk, daniel.thompson, Nicolas Pitre, Russell King,
linux-arm-kernel, devicetree, Alexandre Torgue, Arnd Bergmann,
Rob Herring, ludovic.barre, radoslaw.pietrzyk, andrea.merello,
olivier.bideau, Stephen Boyd, linux-kernel, kbuild-all,
Maxime Coquelin, gabriel.fernandez
In-Reply-To: <1491309014-13967-1-git-send-email-gabriel.fernandez@st.com>
[-- Attachment #1: Type: text/plain, Size: 2156 bytes --]
Hi Gabriel,
[auto build test WARNING on clk/clk-next]
[also build test WARNING on v4.11-rc5 next-20170406]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/gabriel-fernandez-st-com/clk-stm32h7-Add-stm32h743-clock-driver/20170405-100654
base: https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-next
config: arm-stm32_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm
All warnings (new ones prefixed by >>):
drivers/clk/clk-stm32h7.c: In function 'ready_gate_clk_disable':
>> drivers/clk/clk-stm32h7.c:250:2: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
if (rgate->backup_domain && dbp_status)
^~
drivers/clk/clk-stm32h7.c:253:3: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the 'if'
clk_gate_ops.disable(hw);
^~~~~~~~~~~~
vim +/if +250 drivers/clk/clk-stm32h7.c
234 return bit_status;
235 }
236
237 static void ready_gate_clk_disable(struct clk_hw *hw)
238 {
239 struct clk_gate *gate = to_clk_gate(hw);
240 struct stm32_ready_gate *rgate = to_ready_gate_clk(gate);
241 int dbp_status;
242 int bit_status;
243 unsigned int timeout = RGATE_TIMEOUT;
244
245 if (!ready_gate_clk_is_enabled(hw))
246 return;
247
248 dbp_status = is_enable_power_domain_write_protection();
249
> 250 if (rgate->backup_domain && dbp_status)
251 disable_power_domain_write_protection();
252
253 clk_gate_ops.disable(hw);
254
255 do {
256 bit_status = !!(readl(gate->reg) & BIT(rgate->bit_rdy));
257
258 if (bit_status)
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 9580 bytes --]
[-- Attachment #3: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] ARM: dts: hi6220: Reset the mmc hosts
From: Daniel Lezcano @ 2017-04-07 13:45 UTC (permalink / raw)
To: xuwei5; +Cc: guodong.xu, linux-arm-kernel, stable, devicetree, linux-kernel
In-Reply-To: <1489673004-11075-1-git-send-email-daniel.lezcano@linaro.org>
On Thu, Mar 16, 2017 at 03:03:24PM +0100, Daniel Lezcano wrote:
> The MMC hosts could be left in an unconsistent or uninitialized state from
> the firmware. Instead of assuming, the firmware did the right things, let's
> reset the host controllers.
>
> This change fixes a bug when the mmc2/sdio is initialized leading to a hung
> task:
>
> [ 242.704294] INFO: task kworker/7:1:675 blocked for more than 120 seconds.
> [ 242.711129] Not tainted 4.9.0-rc8-00017-gcf0251f #3
> [ 242.716571] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> [ 242.724435] kworker/7:1 D 0 675 2 0x00000000
> [ 242.729973] Workqueue: events_freezable mmc_rescan
> [ 242.734796] Call trace:
> [ 242.737269] [<ffff00000808611c>] __switch_to+0xa8/0xb4
> [ 242.742437] [<ffff000008d07c04>] __schedule+0x1c0/0x67c
> [ 242.747689] [<ffff000008d08254>] schedule+0x40/0xa0
> [ 242.752594] [<ffff000008d0b284>] schedule_timeout+0x1c4/0x35c
> [ 242.758366] [<ffff000008d08e38>] wait_for_common+0xd0/0x15c
> [ 242.763964] [<ffff000008d09008>] wait_for_completion+0x28/0x34
> [ 242.769825] [<ffff000008a1a9f4>] mmc_wait_for_req_done+0x40/0x124
> [ 242.775949] [<ffff000008a1ab98>] mmc_wait_for_req+0xc0/0xf8
> [ 242.781549] [<ffff000008a1ac3c>] mmc_wait_for_cmd+0x6c/0x84
> [ 242.787149] [<ffff000008a26610>] mmc_io_rw_direct_host+0x9c/0x114
> [ 242.793270] [<ffff000008a26aa0>] sdio_reset+0x34/0x7c
> [ 242.798347] [<ffff000008a1d46c>] mmc_rescan+0x2fc/0x360
>
> [ ... ]
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
Hi Xu,
gentle ping.
Thanks.
-- Daniel
> arch/arm64/boot/dts/hisilicon/hi6220.dtsi | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
> index 470461d..1e5129b 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
> @@ -774,6 +774,7 @@
> clocks = <&sys_ctrl 2>, <&sys_ctrl 1>;
> clock-names = "ciu", "biu";
> resets = <&sys_ctrl PERIPH_RSTDIS0_MMC0>;
> + reset-names = "reset";
> bus-width = <0x8>;
> vmmc-supply = <&ldo19>;
> pinctrl-names = "default";
> @@ -797,6 +798,7 @@
> clocks = <&sys_ctrl 4>, <&sys_ctrl 3>;
> clock-names = "ciu", "biu";
> resets = <&sys_ctrl PERIPH_RSTDIS0_MMC1>;
> + reset-names = "reset";
> vqmmc-supply = <&ldo7>;
> vmmc-supply = <&ldo10>;
> bus-width = <0x4>;
> @@ -815,6 +817,7 @@
> clocks = <&sys_ctrl HI6220_MMC2_CIUCLK>, <&sys_ctrl HI6220_MMC2_CLK>;
> clock-names = "ciu", "biu";
> resets = <&sys_ctrl PERIPH_RSTDIS0_MMC2>;
> + reset-names = "reset";
> bus-width = <0x4>;
> broken-cd;
> pinctrl-names = "default", "idle";
> --
> 1.9.1
>
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply
* Re: Re: [PATCH v2 1/6] clk: sunxi-ng: Add sun4i/sun7i CCU driver
From: Maxime Ripard @ 2017-04-07 13:38 UTC (permalink / raw)
To: Priit Laes
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng, Russell King,
Chen-Yu Tsai, Mark Rutland, Rob Herring, Stephen Boyd,
Michael Turquette, Philipp Zabel
In-Reply-To: <20170404200919.GA22159-q/aMd4JkU83YtjvyW6yDsg@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 4589 bytes --]
Hi Priit,
On Tue, Apr 04, 2017 at 08:09:19PM +0000, Priit Laes wrote:
> > > +/* Not documented on A10 */
> > > +static SUNXI_CCU_GATE(pll_periph_sata_clk, "pll-periph-sata", "pll-periph",
> > > + 0x028, BIT(14), 0);
> >
> > The rate doesn't come from pll-periph directly, does it?
>
> So it uses hosc (24MHz parent clock) instead of pll-periph?
I never looked too much at this, but it looks more like the input is
pll-periph-sata itself.
> > > +/* Undocumented on A10 */
> > > +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0",
> > > + 0x088, 8, 3, 0);
> > > +/* Undocumented on A10 */
> > > +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0",
> > > + 0x088, 20, 3, 0);
> >
> > The A10 doesn't have them.
>
> Are you sure? Although, they weren't listed in datasheet, they are defined
> in the sun4i-a10.dtsi:
>
> mmc0_clk: clk@01c20088 {
> #clock-cells = <1>;
> compatible = "allwinner,sun4i-a10-mmc-clk";
> reg = <0x01c20088 0x4>;
> clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
> clock-output-names = "mmc0",
> "mmc0_output",
> "mmc0_sample";
> };
Yes, those clocks have been introduced in the A20, but we didn't find
out until much later, which is why there's still left overs in the
DT. We're not using them in the driver for the A10 either (but we do
for the A20, obviously).
> > > +/* TODO: Check whether A10 actually supports osc32k as 4th parent? */
> > > +static const char *const ir_parents_sun4i[] = { "hosc", "pll-periph",
> > > + "pll-ddr-other" };
> >
> > What does the BSP say about this?
>
> sun7i datasheet mentions osc32k, but BSP code for sun4i, sun5i and sun7i
> is identical and supports only 3 first parents without osc32k.
Ok. Leave the TODO for now, we'll fix it if relevant.
> > > +/* Undocumented on A10 */
> > > +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", audio_parents,
> > > + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
> >
> > This doesn't seem to exist at all on the A10
>
> Wasn't listed in datasheet, but it's in BSP and also in sun4i-a10.dtsi:
>
> spdif_clk: clk@01c200c0 {
> #clock-cells = <0>;
> compatible = "allwinner,sun4i-a10-mod1-clk";
> reg = <0x01c200c0 0x4>;
> clocks = <&pll2 SUN4I_A10_PLL2_8X>,
> <&pll2 SUN4I_A10_PLL2_4X>,
> <&pll2 SUN4I_A10_PLL2_2X>,
> <&pll2 SUN4I_A10_PLL2_1X>;
> clock-output-names = "spdif";
> };
Ack.
> > > +/*
> > > + * TODO: SATA clock also supports external clock as parent via BIT(24)
> > > + * The external clock is probably an optional crystal or oscillator
> > > + * that can be connected to the SATA-CLKM / SATA-CLKP pins.
> > > + */
> > > +static SUNXI_CCU_GATE(sata_clk, "sata", "pll-periph-sata",
> > > + 0x0c8, BIT(31), 0);
> >
> > The rate won't be good here either. This is supposed to be 100MHz.
>
> Hmm.. I tested SATA with Cubietruck. Or what do you mean?
As long as you don't have any dependency on the rate itself, as long
as the gate is opened, I expect it to work. But the rate itself will
be reported wrong.
> > > +static const char *const csi_isp_parents[] = { "pll-video0", "pll-ve",
> > > + "pll-ddr-other", "pll-sata" };
> > > +
> > > +static SUNXI_CCU_M_WITH_MUX_GATE(csi_isp_clk, "csi-isp",
> > > + csi_isp_parents,
> > > + 0x120, 0, 4, 24, 2, BIT(31), 0);
> >
> > We've been calling it sclk in the other SoC iirc. Any particular
> > reason to call it differently?
>
> It's called ISP in BSP and A10 manual.
> In A20 it's indeed Special Clock Register (SCLK).
Let's call it SCLK too then, for consistency.
> > > +static const char *const out_parents[] = { "hosc", "osc32k", "hosc" };
> > > +static SUNXI_CCU_MP_WITH_MUX_GATE(out_a_clk, "out-a", out_parents,
> > > + 0x1f0, 8, 5, 20, 2, 24, 2, BIT(31), 0);
> > > +static SUNXI_CCU_MP_WITH_MUX_GATE(out_b_clk, "out-b", out_parents,
> > > + 0x1f4, 8, 5, 20, 2, 24, 2, BIT(31), 0);
> >
> > There's a fixed pre-divider on the first hosc of 750.
>
> Nice catch.
>
> So it should be something like this:
>
> [snip]
> static const char *const out_parents[] = { "osc24M", "osc32k", "osc24M" };
> static const struct ccu_mux_fixed_prediv out_prediv = {
> .index = 0, .div = 750
> };
I think it shoud still be hosc (or at least, the name that you used
for the gate controlling the 24MHz oscillator input).
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH 1/2] leds: Add driver for Qualcomm LPG
From: Pavel Machek @ 2017-04-07 13:32 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Jacek Anaszewski, Rob Herring, Richard Purdie, linux-kernel,
linux-leds, linux-arm-msm, Mark Rutland, devicetree
In-Reply-To: <20170403190032.GX20094@minitux>
[-- Attachment #1: Type: text/plain, Size: 1608 bytes --]
Hi!
> > In both RGB and pattern approaches we should assess
> > if it is acceptable to provide a pattern for trigger name,
> > e.g. blink-pattern-{num_intervals}.
> >
> > If so, then "echo transition-pattern-15" would create a directory
> > e.g. transition_intervals with files interval_0 to interval_14,
> > that could be adjusted by userspace.
>
> Having a RGB-trigger that proxy a accepts a userspace request of a
> brightness-tripple and sets the brightness on the individual associated
> LEDs sounds reasonable - but should probably be generalized to any
> number of LEDs.
Well.. Generalizing for any number of leds would be nice -- because
hardware can do that. OTOH, if we do that, we'll not have a place
where to do "white-adjustment".
> A slightly related matter is the question on how to use a single LED for
> multiple trigger sources, e.g. how do I get a single LED to show
> activity of two MMCs?.
We normally don't do that. We'd either have a trigger for a single
MMC, or trigger of all the MMCs..
> For the patterns I don't know how a trigger for this would look like,
> how would setting the pattern of a trigger be propagated down to the
> hardware?
Well... I'm not sure if we _want_ to do triggers for
patterns. LED triggers change rather quickly (100 times a second?) so
doing them in kernel makes sense. Patterns take 10s of seconds, so we
do not need to handle them in kernel.
Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* Re: [PATCH v3 3/9] ARM: dts: imx6qdl-icore: Add Sound card with codec node
From: Shawn Guo @ 2017-04-07 13:23 UTC (permalink / raw)
To: Jagan Teki
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Fabio Estevam,
Matteo Lisi, Michael Trimarchi, Jagan Teki
In-Reply-To: <1491501735-1649-4-git-send-email-jagan-oRp2ZoJdM/RWk0Htik3J/w@public.gmane.org>
On Thu, Apr 06, 2017 at 11:32:09PM +0530, Jagan Teki wrote:
> From: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
>
> Add support for Sound card and related codec(via i2c1) nodes
> on below Engicam module boards.
> - i.CoreM6 DualLite/Solo Starter kit
> - i.CoreM6 Quad/Dual Starter kit
> - i.CoreM6 Quad/Dual OpenFrame Cap touch 10.1
> - i.CoreM6 Quad/Dual OpenFrame Cap touch 12.3
>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Matteo Lisi <matteo.lisi-4s7YQHO/iPVBDgjK7y7TUQ@public.gmane.org>
> Cc: Michael Trimarchi <michael-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> Signed-off-by: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> ---
> Changes for v3:
> - Replace fsl,imx-audio-sgtl5000 and use simple-audio-card
> Changes for v2:
> - Use proper [label:] node-name[@unit-address] for codec
>
> arch/arm/boot/dts/imx6dl-icore.dts | 11 +++++++
> arch/arm/boot/dts/imx6q-icore-ofcap10.dts | 11 +++++++
> arch/arm/boot/dts/imx6q-icore-ofcap12.dts | 11 +++++++
> arch/arm/boot/dts/imx6q-icore.dts | 11 +++++++
> arch/arm/boot/dts/imx6qdl-icore.dtsi | 48 +++++++++++++++++++++++++++++++
Why cannot you just add sgtl5000 node into dtsi rather than keeping the
same copy for 4 dts files?
Shawn
> 5 files changed, 92 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx6dl-icore.dts b/arch/arm/boot/dts/imx6dl-icore.dts
> index 6de83c7..0562ce4 100644
> --- a/arch/arm/boot/dts/imx6dl-icore.dts
> +++ b/arch/arm/boot/dts/imx6dl-icore.dts
> @@ -57,3 +57,14 @@
> &can2 {
> status = "okay";
> };
> +
> +&i2c3 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6QDL_CLK_CKO>;
> + VDDA-supply = <®_2p5v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +};
> diff --git a/arch/arm/boot/dts/imx6q-icore-ofcap10.dts b/arch/arm/boot/dts/imx6q-icore-ofcap10.dts
> index 49b60ca..f63b87f 100644
> --- a/arch/arm/boot/dts/imx6q-icore-ofcap10.dts
> +++ b/arch/arm/boot/dts/imx6q-icore-ofcap10.dts
> @@ -50,6 +50,17 @@
> compatible = "engicam,imx6-icore", "fsl,imx6q";
> };
>
> +&i2c3 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6QDL_CLK_CKO>;
> + VDDA-supply = <®_2p5v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +};
> +
> &ldb {
> status = "okay";
>
> diff --git a/arch/arm/boot/dts/imx6q-icore-ofcap12.dts b/arch/arm/boot/dts/imx6q-icore-ofcap12.dts
> index 9e230f5..68ca828 100644
> --- a/arch/arm/boot/dts/imx6q-icore-ofcap12.dts
> +++ b/arch/arm/boot/dts/imx6q-icore-ofcap12.dts
> @@ -50,6 +50,17 @@
> compatible = "engicam,imx6-icore", "fsl,imx6q";
> };
>
> +&i2c3 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6QDL_CLK_CKO>;
> + VDDA-supply = <®_2p5v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +};
> +
> &ldb {
> status = "okay";
>
> diff --git a/arch/arm/boot/dts/imx6q-icore.dts b/arch/arm/boot/dts/imx6q-icore.dts
> index 5613dd9..1ae7294 100644
> --- a/arch/arm/boot/dts/imx6q-icore.dts
> +++ b/arch/arm/boot/dts/imx6q-icore.dts
> @@ -67,6 +67,17 @@
> };
> };
>
> +&i2c3 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6QDL_CLK_CKO>;
> + VDDA-supply = <®_2p5v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +};
> +
> &ldb {
> status = "okay";
>
> diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
> index 56d0c5d..1bd7cdb 100644
> --- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
> +++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
> @@ -55,6 +55,25 @@
> default-brightness-level = <7>;
> };
>
> + reg_1p8v: regulator-1p8v {
> + compatible = "regulator-fixed";
> + regulator-name = "1P8V";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> +
> + reg_2p5v: regulator-3p3v {
> + compatible = "regulator-fixed";
> + regulator-name = "2P5V";
> + regulator-min-microvolt = <2500000>;
> + regulator-max-microvolt = <2500000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> reg_3p3v: regulator-3p3v {
> compatible = "regulator-fixed";
> regulator-name = "3P3V";
> @@ -87,6 +106,31 @@
> #clock-cells = <0>;
> clock-frequency = <25000000>; /* 25MHz for example */
> };
> +
> + sound {
> + compatible = "simple-audio-card";
> + simple-audio-card,name = "imx6qdl-icore-sgtl5000";
> + simple-audio-card,format = "i2s";
> + simple-audio-card,bitclock-master = <&dailink_master>;
> + simple-audio-card,frame-master = <&dailink_master>;
> + simple-audio-card,widgets =
> + "Microphone", "Mic Jack",
> + "Line", "Line In",
> + "Line", "Line Out",
> + "Headphone", "Headphone Jack";
> + simple-audio-card,routing =
> + "MIC_IN", "Mic Jack",
> + "Mic Jack", "Mic Bias",
> + "Headphone Jack", "HP_OUT";
> +
> + simple-audio-card,cpu {
> + sound-dai = <&ssi1>;
> + };
> +
> + dailink_master: simple-audio-card,codec {
> + sound-dai = <&sgtl5000>;
> + };
> + };
> };
>
> &can1 {
> @@ -149,6 +193,10 @@
> status = "okay";
> };
>
> +&ssi1 {
> + status = "okay";
> +};
> +
> &uart4 {
> pinctrl-names = "default";
> pinctrl-0 = <&pinctrl_uart4>;
> --
> 1.9.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 2/9] ARM: dts: imx6ul-geam: Add Sound card with codec node
From: Shawn Guo @ 2017-04-07 13:19 UTC (permalink / raw)
To: Jagan Teki
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Fabio Estevam,
Matteo Lisi, Michael Trimarchi, Jagan Teki
In-Reply-To: <1491501735-1649-3-git-send-email-jagan-oRp2ZoJdM/RWk0Htik3J/w@public.gmane.org>
On Thu, Apr 06, 2017 at 11:32:08PM +0530, Jagan Teki wrote:
> From: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
>
> Add support for Sound card and related codec(via i2c1) nodes
> on Engicam GEAM6UL variant module boards.
>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Matteo Lisi <matteo.lisi-4s7YQHO/iPVBDgjK7y7TUQ@public.gmane.org>
> Cc: Michael Trimarchi <michael-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> Signed-off-by: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> ---
> Changes for v3:
> - Replace fsl,imx-audio-sgtl5000 and use simple-audio-card
> Changes for v2:
> - Use proper [label:] node-name[@unit-address] for codec
> - Remove incorrect codec property 'wlf,shared-lrclk'
> - Remove 'gpr' from sound card node
>
> arch/arm/boot/dts/imx6ul-geam-kit.dts | 12 ++++++++++++
> arch/arm/boot/dts/imx6ul-geam.dtsi | 26 ++++++++++++++++++++++++++
Same question here: why is sgtl5000 added to imx6ul-geam-kit.dts, while
sound code that references to sgtl5000 is added to imx6ul-geam.dtsi?
Shawn
> 2 files changed, 38 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx6ul-geam-kit.dts b/arch/arm/boot/dts/imx6ul-geam-kit.dts
> index 142e60c..02edcba 100644
> --- a/arch/arm/boot/dts/imx6ul-geam-kit.dts
> +++ b/arch/arm/boot/dts/imx6ul-geam-kit.dts
> @@ -58,6 +58,18 @@
> status = "okay";
> };
>
> +&i2c1 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6UL_CLK_OSC>;
> + clock-names = "mclk";
> + VDDA-supply = <®_3p3v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +};
> +
> &lcdif {
> display = <&display0>;
> status = "okay";
> diff --git a/arch/arm/boot/dts/imx6ul-geam.dtsi b/arch/arm/boot/dts/imx6ul-geam.dtsi
> index eb94d95..3bc3238 100644
> --- a/arch/arm/boot/dts/imx6ul-geam.dtsi
> +++ b/arch/arm/boot/dts/imx6ul-geam.dtsi
> @@ -87,6 +87,32 @@
> regulator-always-on;
> regulator-boot-on;
> };
> +
> + sound {
> + compatible = "simple-audio-card";
> + simple-audio-card,name = "imx6ul-geam-sgtl5000";
> + simple-audio-card,format = "i2s";
> + simple-audio-card,bitclock-master = <&dailink_master>;
> + simple-audio-card,frame-master = <&dailink_master>;
> + simple-audio-card,widgets =
> + "Microphone", "Mic Jack",
> + "Line", "Line In",
> + "Line", "Line Out",
> + "Headphone", "Headphone Jack";
> + simple-audio-card,routing =
> + "MIC_IN", "Mic Jack",
> + "Mic Jack", "Mic Bias",
> + "Headphone Jack", "HP_OUT";
> +
> + simple-audio-card,cpu {
> + sound-dai = <&sai2>;
> + };
> +
> + dailink_master: simple-audio-card,codec {
> + sound-dai = <&sgtl5000>;
> + clocks = <&clks IMX6UL_CLK_SAI2>;
> + };
> + };
> };
>
> &can1 {
> --
> 1.9.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 1/9] ARM: dts: imx6ul-isiot: Add Sound card with codec node
From: Shawn Guo @ 2017-04-07 13:16 UTC (permalink / raw)
To: Jagan Teki
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Fabio Estevam,
Matteo Lisi, Michael Trimarchi, Jagan Teki
In-Reply-To: <1491501735-1649-2-git-send-email-jagan-oRp2ZoJdM/RWk0Htik3J/w@public.gmane.org>
On Thu, Apr 06, 2017 at 11:32:07PM +0530, Jagan Teki wrote:
> From: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
>
> Add support for Sound card and related codec(via i2c1) nodes
> on Engicam Is.IoT MX6UL variant module boards.
>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Matteo Lisi <matteo.lisi-4s7YQHO/iPVBDgjK7y7TUQ@public.gmane.org>
> Cc: Michael Trimarchi <michael-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> Signed-off-by: Jagan Teki <jagan-dyjBcgdgk7Pe9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
> ---
> Changes for v3:
> - Replace fsl,imx-audio-sgtl5000 and use simple-audio-card
> Changes for v2:
> - Use proper [label:] node-name[@unit-address] for codec
> - Remove incorrect codec property 'wlf,shared-lrclk'
> - Remove 'gpr' from sound card node
>
> arch/arm/boot/dts/imx6ul-isiot-common.dtsi | 10 +++++++
> arch/arm/boot/dts/imx6ul-isiot.dtsi | 44 ++++++++++++++++++++++++++++++
Can you help me understand how these two files are related? Why is
sgtl5000 added into one and sound node added into the other?
Shawn
> 2 files changed, 54 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx6ul-isiot-common.dtsi b/arch/arm/boot/dts/imx6ul-isiot-common.dtsi
> index 2beaab6..d456080 100644
> --- a/arch/arm/boot/dts/imx6ul-isiot-common.dtsi
> +++ b/arch/arm/boot/dts/imx6ul-isiot-common.dtsi
> @@ -41,6 +41,16 @@
> */
>
> &i2c1 {
> + sgtl5000: codec@a {
> + compatible = "fsl,sgtl5000";
> + reg = <0x0a>;
> + clocks = <&clks IMX6UL_CLK_OSC>;
> + clock-names = "mclk";
> + VDDA-supply = <®_3p3v>;
> + VDDIO-supply = <®_3p3v>;
> + VDDD-supply = <®_1p8v>;
> + };
> +
> stmpe811: gpio-expander@44 {
> compatible = "st,stmpe811";
> reg = <0x44>;
> diff --git a/arch/arm/boot/dts/imx6ul-isiot.dtsi b/arch/arm/boot/dts/imx6ul-isiot.dtsi
> index ea30380..7e947e5 100644
> --- a/arch/arm/boot/dts/imx6ul-isiot.dtsi
> +++ b/arch/arm/boot/dts/imx6ul-isiot.dtsi
> @@ -69,6 +69,50 @@
> 100>;
> default-brightness-level = <100>;
> };
> +
> + reg_1p8v: regulator-1p8v {
> + compatible = "regulator-fixed";
> + regulator-name = "1P8V";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + reg_3p3v: regulator-3p3v {
> + compatible = "regulator-fixed";
> + regulator-name = "3P3V";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + sound {
> + compatible = "simple-audio-card";
> + simple-audio-card,name = "imx6ul-isiot-sgtl5000";
> + simple-audio-card,format = "i2s";
> + simple-audio-card,bitclock-master = <&dailink_master>;
> + simple-audio-card,frame-master = <&dailink_master>;
> + simple-audio-card,widgets =
> + "Microphone", "Mic Jack",
> + "Line", "Line In",
> + "Line", "Line Out",
> + "Headphone", "Headphone Jack";
> + simple-audio-card,routing =
> + "MIC_IN", "Mic Jack",
> + "Mic Jack", "Mic Bias",
> + "Headphone Jack", "HP_OUT";
> +
> + simple-audio-card,cpu {
> + sound-dai = <&sai2>;
> + };
> +
> + dailink_master: simple-audio-card,codec {
> + sound-dai = <&sgtl5000>;
> + clocks = <&clks IMX6UL_CLK_SAI2>;
> + };
> + };
> };
>
> &i2c1 {
> --
> 1.9.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/8] v4l: fwnode: Support generic fwnode for parsing standardised properties
From: Sakari Ailus @ 2017-04-07 13:03 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: Sakari Ailus, linux-media, linux-acpi, devicetree
In-Reply-To: <1761689.CzVR5YAybi@avalon>
Hi Laurent,
On Fri, Apr 07, 2017 at 01:54:58PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
>
> On Friday 07 Apr 2017 13:36:34 Sakari Ailus wrote:
> > On Fri, Apr 07, 2017 at 12:44:27PM +0300, Laurent Pinchart wrote:
> > > On Thursday 06 Apr 2017 16:12:04 Sakari Ailus wrote:
> > > > The fwnode_handle is a more generic way than OF device_node to describe
> > > > firmware nodes. Instead of the OF API, use more generic fwnode API to
> > > > obtain the same information.
> > >
> > > I would mention that this is a copy of v4l2-of.c with the OF API replaced
> > > with the fwnode API.
> >
> > I'll add that to the description.
> >
> > > > As the V4L2 fwnode support will be required by a small minority of e.g.
> > > > ACPI based systems (the same might actually go for OF), make this a
> > > > module instead of embedding it in the videodev module.
> > > >
> > > > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > ---
> > > >
> > > > drivers/media/v4l2-core/Kconfig | 3 +
> > > > drivers/media/v4l2-core/Makefile | 1 +
> > > > drivers/media/v4l2-core/v4l2-fwnode.c | 353 +++++++++++++++++++++++++++
> > > > include/media/v4l2-fwnode.h | 104 ++++++++++
> > > > 4 files changed, 461 insertions(+)
> > > > create mode 100644 drivers/media/v4l2-core/v4l2-fwnode.c
> > > > create mode 100644 include/media/v4l2-fwnode.h
>
> [snip]
>
> > > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > b/drivers/media/v4l2-core/v4l2-fwnode.c new file mode 100644
> > > > index 0000000..4f69b11
> > > > --- /dev/null
> > > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > @@ -0,0 +1,353 @@
> > > > +/*
> > > > + * V4L2 fwnode binding parsing library
> > > > + *
> > > > + * Copyright (c) 2016 Intel Corporation.
> > > > + * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > + *
> > > > + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
> > > > + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
> > > > + *
> > > > + * Copyright (C) 2012 Renesas Electronics Corp.
> > > > + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or modify
> > > > + * it under the terms of version 2 of the GNU General Public License as
> > > > + * published by the Free Software Foundation.
> > > > + */
> > > > +#include <linux/acpi.h>
> > > > +#include <linux/kernel.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/of.h>
> > > > +#include <linux/property.h>
> > > > +#include <linux/slab.h>
> > > > +#include <linux/string.h>
> > > > +#include <linux/types.h>
> > > > +
> > > > +#include <media/v4l2-fwnode.h>
> > > > +
> > > > +static int v4l2_fwnode_endpoint_parse_csi_bus(struct fwnode_handle
> > > > *fwn,
> > > > + struct v4l2_fwnode_endpoint
> > > > *vfwn)
> > > > +{
> > > > + struct v4l2_fwnode_bus_mipi_csi2 *bus = &vfwn->bus.mipi_csi2;
> > > > + bool have_clk_lane = false;
> > > > + unsigned int flags = 0, lanes_used = 0;
> > > > + unsigned int i;
> > > > + u32 v;
> > > > + int rval;
> > >
> > > I would have used "ret" instead of "rval" ;-)
> >
> > I know. But
> >
> > 1) there's no established convention in the file and
> >
> > 2) "rval" has the benefit is easier to look up; one doesn't find a plethora
> > of "return something". Therefore it is better than "ret" for the purpose.
>
> The solution to that is
>
> /ret\>
>
> (and, of course, switching to vim :-D)
What's "\>" for?
I don't think you can convince people to switch to vim this way. :-)
>
> [snip]
>
> > > > +/*
> > > > + * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by
> > > > + * v4l2_fwnode_endpoint_alloc_parse()
> > > > + * @fwn - the V4L2 fwnode the resources of which are to be released
> > >
> > > Mayeb "the V4L2 fwnode whose resources are to be released" ?
> > >
> > > > + *
> > > > + * It is safe to call this function with NULL argument or on an
> > >
> > > s/on an/on a/
> >
> > Yes.
> >
> > > > + * V4L2 fwnode the parsing of which failed.
> > >
> > > "whose parsing failed" ?
> >
> > Any particular reason? Do you like "whose"? :-)
>
> "of which" sounds dubious in this context, but please consult a native English
> speaker in case of doubt.
"Whose" is the possessive form of "who". Albeit nowadays it could probably
be used for other purposes as well.
In my opinion "of which" is perfectly appropriate language here.
--
Regards,
Sakari Ailus
e-mail: sakari.ailus@iki.fi XMPP: sailus@retiisi.org.uk
^ permalink raw reply
* [PATCH v2 4/4] gpio: aspeed: Add open-source and open-drain support
From: Andrew Jeffery @ 2017-04-07 12:59 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio, devicetree, linux-kernel, openbmc
In-Reply-To: <20170407125902.15960-1-andrew@aj.id.au>
As per the datasheet, manage the IO and value states to implement
open-source/open-drain, but do this by falling back to gpiolib's
emulation.
This commit simply makes the behaviour explicit for clarity, rather than
relying on the implicit return of -ENOTSUPP to trigger the emulation.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
drivers/gpio/gpio-aspeed.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 3327a48df862..ccea609676ee 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -761,6 +761,10 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
param == PIN_CONFIG_BIAS_PULL_DOWN ||
param == PIN_CONFIG_DRIVE_STRENGTH)
return pinctrl_gpio_set_config(offset, config);
+ else if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN ||
+ param == PIN_CONFIG_DRIVE_OPEN_SOURCE)
+ /* Return -ENOTSUPP to trigger emulation, as per datasheet */
+ return -ENOTSUPP;
return -ENOTSUPP;
}
--
2.9.3
^ permalink raw reply related
* [PATCH v2 3/4] gpio: aspeed: Add debounce support
From: Andrew Jeffery @ 2017-04-07 12:59 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio, devicetree, linux-kernel, openbmc
In-Reply-To: <20170407125902.15960-1-andrew@aj.id.au>
Each GPIO in the Aspeed GPIO controller can choose one of four input
debounce states: to disable debouncing for an input, or select from one
of three programmable debounce timer values. Each GPIO in a
four-bank-set is assigned one bit in each of two debounce configuration
registers dedicated to the set, and selects a debounce state by
configuring the two bits to select one of the four options.
The limitation on debounce timer values is managed by mapping offsets
onto a configured timer value and keeping count of the number of users
a timer has. Timer values are configured on a first-come-first-served
basis.
A small twist in the hardware design is that the debounce configuration
register numbering is reversed with respect to the binary representation
of the debounce timer of interest (i.e. debounce register 1 represents
bit 1, and debounce register 2 represents bit 0 of the timer numbering).
Tested on an AST2500EVB with additional inspection under QEMU's
romulus-bmc machine.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
drivers/gpio/gpio-aspeed.c | 281 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 276 insertions(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index fb16cc771c0d..3327a48df862 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -9,14 +9,18 @@
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <asm/div64.h>
+#include <linux/clk.h>
+#include <linux/gpio/driver.h>
+#include <linux/hashtable.h>
#include <linux/init.h>
#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
struct aspeed_bank_props {
unsigned int bank;
@@ -29,59 +33,85 @@ struct aspeed_gpio_config {
const struct aspeed_bank_props *props;
};
+/*
+ * @offset_timer: Maps an offset to an @timer_users index, or zero if disabled
+ * @timer_users: Tracks the number of users for each timer
+ *
+ * The @timer_users has four elements but the first element is unused. This is
+ * to simplify accounting and indexing, as a zero value in @offset_timer
+ * represents disabled debouncing for the GPIO. Any other value for an element
+ * of @offset_timer is used as an index into @timer_users. This behaviour of
+ * the zero value aligns with the behaviour of zero built from the timer
+ * configuration registers (i.e. debouncing is disabled).
+ */
struct aspeed_gpio {
struct gpio_chip chip;
spinlock_t lock;
void __iomem *base;
int irq;
const struct aspeed_gpio_config *config;
+
+ u8 *offset_timer;
+ unsigned int timer_users[4];
+ struct clk *clk;
};
struct aspeed_gpio_bank {
uint16_t val_regs;
uint16_t irq_regs;
+ uint16_t debounce_regs;
const char names[4][3];
};
+static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
+
static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
{
.val_regs = 0x0000,
.irq_regs = 0x0008,
+ .debounce_regs = 0x0040,
.names = { "A", "B", "C", "D" },
},
{
.val_regs = 0x0020,
.irq_regs = 0x0028,
+ .debounce_regs = 0x0048,
.names = { "E", "F", "G", "H" },
},
{
.val_regs = 0x0070,
.irq_regs = 0x0098,
+ .debounce_regs = 0x00b0,
.names = { "I", "J", "K", "L" },
},
{
.val_regs = 0x0078,
.irq_regs = 0x00e8,
+ .debounce_regs = 0x0100,
.names = { "M", "N", "O", "P" },
},
{
.val_regs = 0x0080,
.irq_regs = 0x0118,
+ .debounce_regs = 0x0130,
.names = { "Q", "R", "S", "T" },
},
{
.val_regs = 0x0088,
.irq_regs = 0x0148,
+ .debounce_regs = 0x0160,
.names = { "U", "V", "W", "X" },
},
{
.val_regs = 0x01E0,
.irq_regs = 0x0178,
+ .debounce_regs = 0x0190,
.names = { "Y", "Z", "AA", "AB" },
},
{
.val_regs = 0x01E8,
.irq_regs = 0x01A8,
+ .debounce_regs = 0x01c0,
.names = { "AC", "", "", "" },
},
};
@@ -99,6 +129,13 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
#define GPIO_IRQ_TYPE2 0x0c
#define GPIO_IRQ_STATUS 0x10
+#define GPIO_DEBOUNCE_SEL1 0x00
+#define GPIO_DEBOUNCE_SEL2 0x04
+
+#define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o))
+#define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1)
+#define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0)
+
static const struct aspeed_gpio_bank *to_bank(unsigned int offset)
{
unsigned int bank = GPIO_BANK(offset);
@@ -144,6 +181,7 @@ static inline bool have_input(struct aspeed_gpio *gpio, unsigned int offset)
}
#define have_irq(g, o) have_input((g), (o))
+#define have_debounce(g, o) have_input((g), (o))
static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
{
@@ -506,6 +544,227 @@ static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset)
pinctrl_free_gpio(chip->base + offset);
}
+static inline void __iomem *bank_debounce_reg(struct aspeed_gpio *gpio,
+ const struct aspeed_gpio_bank *bank,
+ unsigned int reg)
+{
+ return gpio->base + bank->debounce_regs + reg;
+}
+
+static int usecs_to_cycles(struct aspeed_gpio *gpio, unsigned long usecs,
+ u32 *cycles)
+{
+ u64 rate;
+ u64 n;
+ u32 r;
+
+ rate = clk_get_rate(gpio->clk);
+ if (!rate)
+ return -ENOTSUPP;
+
+ n = rate * usecs;
+ r = do_div(n, 1000000);
+
+ if (n >= U32_MAX)
+ return -ERANGE;
+
+ /* At least as long as the requested time */
+ *cycles = n + (!!r);
+
+ return 0;
+}
+
+/* Call under gpio->lock */
+static int register_allocated_timer(struct aspeed_gpio *gpio,
+ unsigned int offset, unsigned int timer)
+{
+ if (WARN(gpio->offset_timer[offset] != 0,
+ "Offset %d already allocated timer %d\n",
+ offset, gpio->offset_timer[offset]))
+ return -EINVAL;
+
+ if (WARN(gpio->timer_users[timer] == UINT_MAX,
+ "Timer user count would overflow\n"))
+ return -EPERM;
+
+ gpio->offset_timer[offset] = timer;
+ gpio->timer_users[timer]++;
+
+ return 0;
+}
+
+/* Call under gpio->lock */
+static int unregister_allocated_timer(struct aspeed_gpio *gpio,
+ unsigned int offset)
+{
+ if (WARN(gpio->offset_timer[offset] == 0,
+ "No timer allocated to offset %d\n", offset))
+ return -EINVAL;
+
+ if (WARN(gpio->timer_users[gpio->offset_timer[offset]] == 0,
+ "No users recorded for timer %d\n",
+ gpio->offset_timer[offset]))
+ return -EINVAL;
+
+ gpio->timer_users[gpio->offset_timer[offset]]--;
+ gpio->offset_timer[offset] = 0;
+
+ return 0;
+}
+
+/* Call under gpio->lock */
+static inline bool timer_allocation_registered(struct aspeed_gpio *gpio,
+ unsigned int offset)
+{
+ return gpio->offset_timer[offset] > 0;
+}
+
+/* Call under gpio->lock */
+static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset,
+ unsigned int timer)
+{
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ const u32 mask = GPIO_BIT(offset);
+ void __iomem *addr;
+ u32 val;
+
+ addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL1);
+ val = ioread32(addr);
+ iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr);
+
+ addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL2);
+ val = ioread32(addr);
+ iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr);
+}
+
+static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
+ unsigned long usecs)
+{
+ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
+ u32 requested_cycles;
+ unsigned long flags;
+ int rc;
+ int i;
+
+ rc = usecs_to_cycles(gpio, usecs, &requested_cycles);
+ if (rc < 0) {
+ dev_warn(chip->parent, "Failed to convert %luus to cycles at %luHz: %d\n",
+ usecs, clk_get_rate(gpio->clk), rc);
+ return rc;
+ }
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ if (timer_allocation_registered(gpio, offset)) {
+ rc = unregister_allocated_timer(gpio, offset);
+ if (rc < 0)
+ goto out;
+ }
+
+ /* Try to find a timer already configured for the debounce period */
+ for (i = 1; i < ARRAY_SIZE(debounce_timers); i++) {
+ u32 cycles;
+
+ cycles = ioread32(gpio->base + debounce_timers[i]);
+ if (requested_cycles == cycles)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(debounce_timers)) {
+ int j;
+
+ /*
+ * As there are no timers configured for the requested debounce
+ * period, find an unused timer instead
+ */
+ for (j = 1; j < ARRAY_SIZE(gpio->timer_users); j++) {
+ if (gpio->timer_users[j] == 0)
+ break;
+ }
+
+ if (j == ARRAY_SIZE(gpio->timer_users)) {
+ dev_warn(chip->parent,
+ "Debounce timers exhausted, cannot debounce for period %luus\n",
+ usecs);
+
+ rc = -EPERM;
+
+ /*
+ * We already adjusted the accounting to remove @offset
+ * as a user of its previous timer, so also configure
+ * the hardware so @offset has timers disabled for
+ * consistency.
+ */
+ configure_timer(gpio, offset, 0);
+ goto out;
+ }
+
+ i = j;
+
+ iowrite32(requested_cycles, gpio->base + debounce_timers[i]);
+ }
+
+ if (WARN(i == 0, "Cannot register index of disabled timer\n")) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ register_allocated_timer(gpio, offset, i);
+ configure_timer(gpio, offset, i);
+
+out:
+ spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return rc;
+}
+
+static int disable_debounce(struct gpio_chip *chip, unsigned int offset)
+{
+ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ rc = unregister_allocated_timer(gpio, offset);
+ if (!rc)
+ configure_timer(gpio, offset, 0);
+
+ spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return rc;
+}
+
+static int set_debounce(struct gpio_chip *chip, unsigned int offset,
+ unsigned long usecs)
+{
+ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
+
+ if (!have_debounce(gpio, offset))
+ return -ENOTSUPP;
+
+ if (usecs)
+ return enable_debounce(chip, offset, usecs);
+
+ return disable_debounce(chip, offset);
+}
+
+static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ unsigned long param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+
+ if (param == PIN_CONFIG_INPUT_DEBOUNCE)
+ return set_debounce(chip, offset, arg);
+ else if (param == PIN_CONFIG_BIAS_DISABLE ||
+ param == PIN_CONFIG_BIAS_PULL_DOWN ||
+ param == PIN_CONFIG_DRIVE_STRENGTH)
+ return pinctrl_gpio_set_config(offset, config);
+
+ return -ENOTSUPP;
+}
+
/*
* Any banks not specified in a struct aspeed_bank_props array are assumed to
* have the properties:
@@ -565,8 +824,16 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
if (!gpio_id)
return -EINVAL;
+ gpio->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (IS_ERR(gpio->clk)) {
+ dev_warn(&pdev->dev,
+ "No HPLL clock phandle provided, debouncing disabled\n");
+ gpio->clk = NULL;
+ }
+
gpio->config = gpio_id->data;
+ gpio->chip.parent = &pdev->dev;
gpio->chip.ngpio = gpio->config->nr_gpios;
gpio->chip.parent = &pdev->dev;
gpio->chip.direction_input = aspeed_gpio_dir_in;
@@ -576,6 +843,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->chip.free = aspeed_gpio_free;
gpio->chip.get = aspeed_gpio_get;
gpio->chip.set = aspeed_gpio_set;
+ gpio->chip.set_config = aspeed_gpio_set_config;
gpio->chip.label = dev_name(&pdev->dev);
gpio->chip.base = -1;
gpio->chip.irq_need_valid_mask = true;
@@ -584,6 +852,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
if (rc < 0)
return rc;
+ gpio->offset_timer =
+ devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL);
+
return aspeed_gpio_setup_irqs(gpio, pdev);
}
--
2.9.3
^ permalink raw reply related
* [PATCH v2 2/4] gpio: aspeed: dt: Add optional clocks property
From: Andrew Jeffery @ 2017-04-07 12:59 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio, devicetree, linux-kernel, openbmc
In-Reply-To: <20170407125902.15960-1-andrew@aj.id.au>
We need a reference to the HPLL to calculate debounce cycles. If the
clocks property is not supplied in the GPIO node then the consumer
should deny any debounce requests.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
Documentation/devicetree/bindings/gpio/gpio-aspeed.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
index 6f30b9a048da..c756afa88cc6 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
@@ -18,6 +18,7 @@ Required properties:
Optional properties:
- interrupt-parent : The parent interrupt controller, optional if inherited
+- clocks : A phandle to the HPLL clock node for debounce timings
The gpio and interrupt properties are further described in their respective
bindings documentation:
--
2.9.3
^ permalink raw reply related
* [PATCH v2 1/4] gpio: aspeed: dt: Fix description alignment in bindings document
From: Andrew Jeffery @ 2017-04-07 12:58 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <20170407125902.15960-1-andrew-zrmu5oMJ5Fs@public.gmane.org>
Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
---
Documentation/devicetree/bindings/gpio/gpio-aspeed.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
index 393bb2ed8a77..6f30b9a048da 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt
@@ -17,7 +17,7 @@ Required properties:
Optional properties:
-- interrupt-parent : The parent interrupt controller, optional if inherited
+- interrupt-parent : The parent interrupt controller, optional if inherited
The gpio and interrupt properties are further described in their respective
bindings documentation:
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 0/4] gpio: aspeed: Add debounce support
From: Andrew Jeffery @ 2017-04-07 12:58 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ
Hi Linus,
v2 of the Aspeed GPIO debounce patch expands it out into a series, resolving a
lack of devicetree binding documentation updates in the initial send, and
completely reworking the debounce timer use tracking for space efficiency.
I do wonder whether there's a more acceptable way to track the use of the
timers than what I've got, but it at least works.
The rework was largely an effort to resolve Joel's review of v1, in the absence
of any other feedback.
Cheers,
Andrew
In v2:
* Update devicetree bindings documentation
* Address Joel's comments
Andrew Jeffery (4):
gpio: aspeed: dt: Fix description alignment in bindings document
gpio: aspeed: dt: Add optional clocks property
gpio: aspeed: Add debounce support
gpio: aspeed: Add open-source and open-drain support
.../devicetree/bindings/gpio/gpio-aspeed.txt | 3 +-
drivers/gpio/gpio-aspeed.c | 285 ++++++++++++++++++++-
2 files changed, 282 insertions(+), 6 deletions(-)
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 4/4] pinctrl: aspeed: g5: Add pinconf support
From: Andrew Jeffery @ 2017-04-07 12:57 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio, devicetree, linux-kernel, openbmc
In-Reply-To: <20170407125713.15678-1-andrew@aj.id.au>
Testing for pinctrl-aspeed-g5 was performed on an AST2500EVB system,
using the strategy outlined in the commit message for the change to the
Aspeed pinctrl core.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 153 ++++++++++++++++++++++++++++-
1 file changed, 152 insertions(+), 1 deletion(-)
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
index 43221a3c7e23..68aa04664a62 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
@@ -2285,6 +2285,146 @@ static const struct aspeed_pin_function aspeed_g5_functions[] = {
ASPEED_PINCTRL_FUNC(WDTRST2),
};
+static struct aspeed_pin_config aspeed_g5_configs[] = {
+ /* GPIOA, GPIOQ */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { B14, B13 }, SCU8C, 16 },
+ { PIN_CONFIG_BIAS_DISABLE, { B14, B13 }, SCU8C, 16 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A11, N20 }, SCU8C, 16 },
+ { PIN_CONFIG_BIAS_DISABLE, { A11, N20 }, SCU8C, 16 },
+
+ /* GPIOB, GPIOR */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { K19, H20 }, SCU8C, 17 },
+ { PIN_CONFIG_BIAS_DISABLE, { K19, H20 }, SCU8C, 17 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AA19, E10 }, SCU8C, 17 },
+ { PIN_CONFIG_BIAS_DISABLE, { AA19, E10 }, SCU8C, 17 },
+
+ /* GPIOC, GPIOS*/
+ { PIN_CONFIG_BIAS_PULL_DOWN, { C12, B11 }, SCU8C, 18 },
+ { PIN_CONFIG_BIAS_DISABLE, { C12, B11 }, SCU8C, 18 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V20, AA20 }, SCU8C, 18 },
+ { PIN_CONFIG_BIAS_DISABLE, { V20, AA20 }, SCU8C, 18 },
+
+ /* GPIOD, GPIOY */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F19, C21 }, SCU8C, 19 },
+ { PIN_CONFIG_BIAS_DISABLE, { F19, C21 }, SCU8C, 19 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { R22, P20 }, SCU8C, 19 },
+ { PIN_CONFIG_BIAS_DISABLE, { R22, P20 }, SCU8C, 19 },
+
+ /* GPIOE, GPIOZ */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { B20, B19 }, SCU8C, 20 },
+ { PIN_CONFIG_BIAS_DISABLE, { B20, B19 }, SCU8C, 20 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { Y20, W21 }, SCU8C, 20 },
+ { PIN_CONFIG_BIAS_DISABLE, { Y20, W21 }, SCU8C, 20 },
+
+ /* GPIOF, GPIOAA */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { J19, H18 }, SCU8C, 21 },
+ { PIN_CONFIG_BIAS_DISABLE, { J19, H18 }, SCU8C, 21 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { Y21, P19 }, SCU8C, 21 },
+ { PIN_CONFIG_BIAS_DISABLE, { Y21, P19 }, SCU8C, 21 },
+
+ /* GPIOG, GPIOAB */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A19, E14 }, SCU8C, 22 },
+ { PIN_CONFIG_BIAS_DISABLE, { A19, E14 }, SCU8C, 22 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N19, R20 }, SCU8C, 22 },
+ { PIN_CONFIG_BIAS_DISABLE, { N19, R20 }, SCU8C, 22 },
+
+ /* GPIOH, GPIOAC */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A18, D18 }, SCU8C, 23 },
+ { PIN_CONFIG_BIAS_DISABLE, { A18, D18 }, SCU8C, 23 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G21, G22 }, SCU8C, 23 },
+ { PIN_CONFIG_BIAS_DISABLE, { G21, G22 }, SCU8C, 23 },
+
+ /* GPIOs [I, P] */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { C18, A15 }, SCU8C, 24 },
+ { PIN_CONFIG_BIAS_DISABLE, { C18, A15 }, SCU8C, 24 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { R2, T3 }, SCU8C, 25 },
+ { PIN_CONFIG_BIAS_DISABLE, { R2, T3 }, SCU8C, 25 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L3, R1 }, SCU8C, 26 },
+ { PIN_CONFIG_BIAS_DISABLE, { L3, R1 }, SCU8C, 26 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { T2, W1 }, SCU8C, 27 },
+ { PIN_CONFIG_BIAS_DISABLE, { T2, W1 }, SCU8C, 27 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { Y1, T5 }, SCU8C, 28 },
+ { PIN_CONFIG_BIAS_DISABLE, { Y1, T5 }, SCU8C, 28 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V2, T4 }, SCU8C, 29 },
+ { PIN_CONFIG_BIAS_DISABLE, { V2, T4 }, SCU8C, 29 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { U5, W4 }, SCU8C, 30 },
+ { PIN_CONFIG_BIAS_DISABLE, { U5, W4 }, SCU8C, 30 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V4, V6 }, SCU8C, 31 },
+ { PIN_CONFIG_BIAS_DISABLE, { V4, V6 }, SCU8C, 31 },
+
+ /* GPIOs T[0-5] (RGMII1 Tx pins) */
+ { PIN_CONFIG_DRIVE_STRENGTH, { B5, B5 }, SCU90, 8 },
+ { PIN_CONFIG_DRIVE_STRENGTH, { E9, A5 }, SCU90, 9 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { B5, D7 }, SCU90, 12 },
+ { PIN_CONFIG_BIAS_DISABLE, { B5, D7 }, SCU90, 12 },
+
+ /* GPIOs T[6-7], U[0-3] (RGMII2 TX pins) */
+ { PIN_CONFIG_DRIVE_STRENGTH, { B2, B2 }, SCU90, 10 },
+ { PIN_CONFIG_DRIVE_STRENGTH, { B1, B3 }, SCU90, 11 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { B2, D4 }, SCU90, 14 },
+ { PIN_CONFIG_BIAS_DISABLE, { B2, D4 }, SCU90, 14 },
+
+ /* GPIOs U[4-7], V[0-1] (RGMII1 Rx pins) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { B4, C4 }, SCU90, 13 },
+ { PIN_CONFIG_BIAS_DISABLE, { B4, C4 }, SCU90, 13 },
+
+ /* GPIOs V[2-7] (RGMII2 Rx pins) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { C2, E6 }, SCU90, 15 },
+ { PIN_CONFIG_BIAS_DISABLE, { C2, E6 }, SCU90, 15 },
+
+ /* ADC pull-downs (SCUA8[19:4]) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F4, F4 }, SCUA8, 4 },
+ { PIN_CONFIG_BIAS_DISABLE, { F4, F4 }, SCUA8, 4 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F5, F5 }, SCUA8, 5 },
+ { PIN_CONFIG_BIAS_DISABLE, { F5, F5 }, SCUA8, 5 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { E2, E2 }, SCUA8, 6 },
+ { PIN_CONFIG_BIAS_DISABLE, { E2, E2 }, SCUA8, 6 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { E1, E1 }, SCUA8, 7 },
+ { PIN_CONFIG_BIAS_DISABLE, { E1, E1 }, SCUA8, 7 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F3, F3 }, SCUA8, 8 },
+ { PIN_CONFIG_BIAS_DISABLE, { F3, F3 }, SCUA8, 8 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { E3, E3 }, SCUA8, 9 },
+ { PIN_CONFIG_BIAS_DISABLE, { E3, E3 }, SCUA8, 9 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G5, G5 }, SCUA8, 10 },
+ { PIN_CONFIG_BIAS_DISABLE, { G5, G5 }, SCUA8, 10 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G4, G4 }, SCUA8, 11 },
+ { PIN_CONFIG_BIAS_DISABLE, { G4, G4 }, SCUA8, 11 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F2, F2 }, SCUA8, 12 },
+ { PIN_CONFIG_BIAS_DISABLE, { F2, F2 }, SCUA8, 12 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G3, G3 }, SCUA8, 13 },
+ { PIN_CONFIG_BIAS_DISABLE, { G3, G3 }, SCUA8, 13 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G2, G2 }, SCUA8, 14 },
+ { PIN_CONFIG_BIAS_DISABLE, { G2, G2 }, SCUA8, 14 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { F1, F1 }, SCUA8, 15 },
+ { PIN_CONFIG_BIAS_DISABLE, { F1, F1 }, SCUA8, 15 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { H5, H5 }, SCUA8, 16 },
+ { PIN_CONFIG_BIAS_DISABLE, { H5, H5 }, SCUA8, 16 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { G1, G1 }, SCUA8, 17 },
+ { PIN_CONFIG_BIAS_DISABLE, { G1, G1 }, SCUA8, 17 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { H3, H3 }, SCUA8, 18 },
+ { PIN_CONFIG_BIAS_DISABLE, { H3, H3 }, SCUA8, 18 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { H4, H4 }, SCUA8, 19 },
+ { PIN_CONFIG_BIAS_DISABLE, { H4, H4 }, SCUA8, 19 },
+
+ /*
+ * Debounce settings for GPIOs D and E passthrough mode are in
+ * SCUA8[27:20] and so are managed by pinctrl. Normal GPIO debounce for
+ * banks D and E is handled by the GPIO driver - GPIO passthrough is
+ * treated like any other non-GPIO mux function. There is a catch
+ * however, in that the debounce period is configured in the GPIO
+ * controller. Due to this tangle between GPIO and pinctrl we don't yet
+ * fully support pass-through debounce.
+ */
+ { PIN_CONFIG_INPUT_DEBOUNCE, { F19, E21 }, SCUA8, 20 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { F20, D20 }, SCUA8, 21 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { D21, E20 }, SCUA8, 22 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { G18, C21 }, SCUA8, 23 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { B20, C20 }, SCUA8, 24 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { F18, F17 }, SCUA8, 25 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { E18, D19 }, SCUA8, 26 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { A20, B19 }, SCUA8, 27 },
+};
+
static struct aspeed_pinctrl_data aspeed_g5_pinctrl_data = {
.pins = aspeed_g5_pins,
.npins = ARRAY_SIZE(aspeed_g5_pins),
@@ -2292,6 +2432,8 @@ static struct aspeed_pinctrl_data aspeed_g5_pinctrl_data = {
.ngroups = ARRAY_SIZE(aspeed_g5_groups),
.functions = aspeed_g5_functions,
.nfunctions = ARRAY_SIZE(aspeed_g5_functions),
+ .configs = aspeed_g5_configs,
+ .nconfigs = ARRAY_SIZE(aspeed_g5_configs),
};
static struct pinmux_ops aspeed_g5_pinmux_ops = {
@@ -2308,16 +2450,25 @@ static struct pinctrl_ops aspeed_g5_pinctrl_ops = {
.get_group_name = aspeed_pinctrl_get_group_name,
.get_group_pins = aspeed_pinctrl_get_group_pins,
.pin_dbg_show = aspeed_pinctrl_pin_dbg_show,
- .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinctrl_utils_free_map,
};
+static struct pinconf_ops aspeed_g5_conf_ops = {
+ .is_generic = true,
+ .pin_config_get = aspeed_pin_config_get,
+ .pin_config_set = aspeed_pin_config_set,
+ .pin_config_group_get = aspeed_pin_config_group_get,
+ .pin_config_group_set = aspeed_pin_config_group_set,
+};
+
static struct pinctrl_desc aspeed_g5_pinctrl_desc = {
.name = "aspeed-g5-pinctrl",
.pins = aspeed_g5_pins,
.npins = ARRAY_SIZE(aspeed_g5_pins),
.pctlops = &aspeed_g5_pinctrl_ops,
.pmxops = &aspeed_g5_pinmux_ops,
+ .confops = &aspeed_g5_conf_ops,
};
static int aspeed_g5_pinctrl_probe(struct platform_device *pdev)
--
2.9.3
^ permalink raw reply related
* [PATCH v2 3/4] pinctrl: aspeed: g4: Add pinconf support
From: Andrew Jeffery @ 2017-04-07 12:57 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Jeffery, Rob Herring, Joel Stanley, Benjamin Herrenschmidt,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <20170407125713.15678-1-andrew-zrmu5oMJ5Fs@public.gmane.org>
Testing for pinctrl-aspeed-g4 was performed on an OpenPOWER Palmetto
system, using the strategy outlined in the commit message for the
change to the Aspeed pinctrl core.
Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
---
drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 117 ++++++++++++++++++++++++++++-
1 file changed, 116 insertions(+), 1 deletion(-)
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
index 7de596e2b9d4..b6a049643555 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
@@ -2234,6 +2234,110 @@ static const struct aspeed_pin_function aspeed_g4_functions[] = {
ASPEED_PINCTRL_FUNC(WDTRST2),
};
+static const struct aspeed_pin_config aspeed_g4_configs[] = {
+ /* GPIO banks ranges [A, B], [D, J], [M, R] */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { D6, D5 }, SCU8C, 16 },
+ { PIN_CONFIG_BIAS_DISABLE, { D6, D5 }, SCU8C, 16 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { J21, E18 }, SCU8C, 17 },
+ { PIN_CONFIG_BIAS_DISABLE, { J21, E18 }, SCU8C, 17 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A18, E15 }, SCU8C, 19 },
+ { PIN_CONFIG_BIAS_DISABLE, { A18, E15 }, SCU8C, 19 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { D15, B14 }, SCU8C, 20 },
+ { PIN_CONFIG_BIAS_DISABLE, { D15, B14 }, SCU8C, 20 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { D18, C17 }, SCU8C, 21 },
+ { PIN_CONFIG_BIAS_DISABLE, { D18, C17 }, SCU8C, 21 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A14, U18 }, SCU8C, 22 },
+ { PIN_CONFIG_BIAS_DISABLE, { A14, U18 }, SCU8C, 22 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A8, E7 }, SCU8C, 23 },
+ { PIN_CONFIG_BIAS_DISABLE, { A8, E7 }, SCU8C, 23 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { C22, E20 }, SCU8C, 24 },
+ { PIN_CONFIG_BIAS_DISABLE, { C22, E20 }, SCU8C, 24 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { J5, T1 }, SCU8C, 25 },
+ { PIN_CONFIG_BIAS_DISABLE, { J5, T1 }, SCU8C, 25 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { U1, U5 }, SCU8C, 26 },
+ { PIN_CONFIG_BIAS_DISABLE, { U1, U5 }, SCU8C, 26 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V3, V5 }, SCU8C, 27 },
+ { PIN_CONFIG_BIAS_DISABLE, { V3, V5 }, SCU8C, 27 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { W4, AB2 }, SCU8C, 28 },
+ { PIN_CONFIG_BIAS_DISABLE, { W4, AB2 }, SCU8C, 28 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V6, V7 }, SCU8C, 29 },
+ { PIN_CONFIG_BIAS_DISABLE, { V6, V7 }, SCU8C, 29 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { Y6, AB7 }, SCU8C, 30 },
+ { PIN_CONFIG_BIAS_DISABLE, { Y6, AB7 }, SCU8C, 30 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { V20, A5 }, SCU8C, 31 },
+ { PIN_CONFIG_BIAS_DISABLE, { V20, A5 }, SCU8C, 31 },
+
+ /* GPIOs T[0-5] (RGMII1 Tx pins) */
+ { PIN_CONFIG_DRIVE_STRENGTH, { A12, A13 }, SCU90, 9 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { A12, A13 }, SCU90, 12 },
+ { PIN_CONFIG_BIAS_DISABLE, { A12, A13 }, SCU90, 12 },
+
+ /* GPIOs T[6-7], U[0-3] (RGMII2 TX pins) */
+ { PIN_CONFIG_DRIVE_STRENGTH, { D9, D10 }, SCU90, 11 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { D9, D10 }, SCU90, 14 },
+ { PIN_CONFIG_BIAS_DISABLE, { D9, D10 }, SCU90, 14 },
+
+ /* GPIOs U[4-7], V[0-1] (RGMII1 Rx pins) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { E11, E10 }, SCU90, 13 },
+ { PIN_CONFIG_BIAS_DISABLE, { E11, E10 }, SCU90, 13 },
+
+ /* GPIOs V[2-7] (RGMII2 Rx pins) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { C9, C8 }, SCU90, 15 },
+ { PIN_CONFIG_BIAS_DISABLE, { C9, C8 }, SCU90, 15 },
+
+ /* ADC pull-downs (SCUA8[19:4]) */
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L5, L5 }, SCUA8, 4 },
+ { PIN_CONFIG_BIAS_DISABLE, { L5, L5 }, SCUA8, 4 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L4, L4 }, SCUA8, 5 },
+ { PIN_CONFIG_BIAS_DISABLE, { L4, L4 }, SCUA8, 5 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L3, L3 }, SCUA8, 6 },
+ { PIN_CONFIG_BIAS_DISABLE, { L3, L3 }, SCUA8, 6 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L2, L2 }, SCUA8, 7 },
+ { PIN_CONFIG_BIAS_DISABLE, { L2, L2 }, SCUA8, 7 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { L1, L1 }, SCUA8, 8 },
+ { PIN_CONFIG_BIAS_DISABLE, { L1, L1 }, SCUA8, 8 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { M5, M5 }, SCUA8, 9 },
+ { PIN_CONFIG_BIAS_DISABLE, { M5, M5 }, SCUA8, 9 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { M4, M4 }, SCUA8, 10 },
+ { PIN_CONFIG_BIAS_DISABLE, { M4, M4 }, SCUA8, 10 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { M3, M3 }, SCUA8, 11 },
+ { PIN_CONFIG_BIAS_DISABLE, { M3, M3 }, SCUA8, 11 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { M2, M2 }, SCUA8, 12 },
+ { PIN_CONFIG_BIAS_DISABLE, { M2, M2 }, SCUA8, 12 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { M1, M1 }, SCUA8, 13 },
+ { PIN_CONFIG_BIAS_DISABLE, { M1, M1 }, SCUA8, 13 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N5, N5 }, SCUA8, 14 },
+ { PIN_CONFIG_BIAS_DISABLE, { N5, N5 }, SCUA8, 14 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N4, N4 }, SCUA8, 15 },
+ { PIN_CONFIG_BIAS_DISABLE, { N4, N4 }, SCUA8, 15 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N3, N3 }, SCUA8, 16 },
+ { PIN_CONFIG_BIAS_DISABLE, { N3, N3 }, SCUA8, 16 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N2, N2 }, SCUA8, 17 },
+ { PIN_CONFIG_BIAS_DISABLE, { N2, N2 }, SCUA8, 17 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { N1, N1 }, SCUA8, 18 },
+ { PIN_CONFIG_BIAS_DISABLE, { N1, N1 }, SCUA8, 18 },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { P5, P5 }, SCUA8, 19 },
+ { PIN_CONFIG_BIAS_DISABLE, { P5, P5 }, SCUA8, 19 },
+
+ /*
+ * Debounce settings for GPIOs D and E passthrough mode are in
+ * SCUA8[27:20] and so are managed by pinctrl. Normal GPIO debounce for
+ * banks D and E is handled by the GPIO driver - GPIO passthrough is
+ * treated like any other non-GPIO mux function. There is a catch
+ * however, in that the debounce period is configured in the GPIO
+ * controller. Due to this tangle between GPIO and pinctrl we don't yet
+ * fully support pass-through debounce.
+ */
+ { PIN_CONFIG_INPUT_DEBOUNCE, { A18, D16 }, SCUA8, 20 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { B17, A17 }, SCUA8, 21 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { C16, B16 }, SCUA8, 22 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { A16, E15 }, SCUA8, 23 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { D15, C15 }, SCUA8, 24 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { B15, A15 }, SCUA8, 25 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { E14, D14 }, SCUA8, 26 },
+ { PIN_CONFIG_INPUT_DEBOUNCE, { C14, B14 }, SCUA8, 27 },
+};
+
static struct aspeed_pinctrl_data aspeed_g4_pinctrl_data = {
.pins = aspeed_g4_pins,
.npins = ARRAY_SIZE(aspeed_g4_pins),
@@ -2241,6 +2345,8 @@ static struct aspeed_pinctrl_data aspeed_g4_pinctrl_data = {
.ngroups = ARRAY_SIZE(aspeed_g4_groups),
.functions = aspeed_g4_functions,
.nfunctions = ARRAY_SIZE(aspeed_g4_functions),
+ .configs = aspeed_g4_configs,
+ .nconfigs = ARRAY_SIZE(aspeed_g4_configs),
};
static struct pinmux_ops aspeed_g4_pinmux_ops = {
@@ -2257,16 +2363,25 @@ static struct pinctrl_ops aspeed_g4_pinctrl_ops = {
.get_group_name = aspeed_pinctrl_get_group_name,
.get_group_pins = aspeed_pinctrl_get_group_pins,
.pin_dbg_show = aspeed_pinctrl_pin_dbg_show,
- .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinctrl_utils_free_map,
};
+static const struct pinconf_ops aspeed_g4_conf_ops = {
+ .is_generic = true,
+ .pin_config_get = aspeed_pin_config_get,
+ .pin_config_set = aspeed_pin_config_set,
+ .pin_config_group_get = aspeed_pin_config_group_get,
+ .pin_config_group_set = aspeed_pin_config_group_set,
+};
+
static struct pinctrl_desc aspeed_g4_pinctrl_desc = {
.name = "aspeed-g4-pinctrl",
.pins = aspeed_g4_pins,
.npins = ARRAY_SIZE(aspeed_g4_pins),
.pctlops = &aspeed_g4_pinctrl_ops,
.pmxops = &aspeed_g4_pinmux_ops,
+ .confops = &aspeed_g4_conf_ops,
};
static int aspeed_g4_pinctrl_probe(struct platform_device *pdev)
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply 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