Skip to contentSkip to conversation

serial: 8250_dw: Add DMA support for non-ACPI platforms

PATCH In review Ray Jui 30 participants 360 messages Oct 8, 2014 – Mar 6, 2015 8 messages not in the archive Page 1 of 3

Subsystems (18)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): Ray Jui <[email protected]

Ray Jui

author maintainer
#1 lore raw

PATCH 1/1 serial: 8250_dw: Add DMA support for non-ACPI platforms

The dma pointer under struct uart_8250_port is currently left unassigned for non-ACPI platforms. It should be pointing to the dma member in struct dw8250_data like how it was done for ACPI, so the core 8250 code will try to request for DMA when registering the port

If DMA is not enabled in device tree, request DMA will fail and the driver will fall back to PIO

---
 drivers/tty/serial/8250/8250_dw.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/tty/serial/8250/8250_dw.c +8 -0
index 4db7987..1038ea8 100644
@@ -293,6 +293,14 @@ static int dw8250_probe_of(struct uart_port *p,
 	if (has_ucv)
 		dw8250_setup_port(up);
 
+	/* if we have a valid fifosize, try hooking up DMA here */
+	if (p->fifosize) {
+		up->dma = &data->dma;
+
+		up->dma->rxconf.src_maxburst = p->fifosize / 4;
+		up->dma->txconf.dst_maxburst = p->fifosize / 4;
+	}
+
 	if (!of_property_read_u32(np, "reg-shift", &val))
 		p->regshift = val;
 
Signature · 1 line
1.7.9.5
Reviewed-by on v1 1/1
#2 lore raw
Quoted 5 lines · Ray Jui

The dma pointer under struct uart_8250_port is currently left unassigned for non-ACPI platforms. It should be pointing to the dma member in struct dw8250_data like how it was done for ACPI, so the core 8250 code will try to request for DMA when registering the port

Quoted 7 lines

If DMA is not enabled in device tree, request DMA will fail and the driver will fall back to PIO

OK by me. FWIW..

Signature · 1 line
heikki
author maintainer
#3 lore raw

PATCH 1/1 spi: pl022: Fix broken spidev when DMA is enabled

In reply to a message not in the archive

The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In most cases, the sequence of the mapping does not matter. But in cases where TX and RX happen to use the same buffer, e.g., spidev, it causes the cached TX data not written to memory, because the same memory has been marked invalid when dma_map_sg on the RX buffer is called

The solution is to reverse the sequence so it maps the TX buffer before the RX buffer

---
 drivers/spi/spi-pl022.c |   20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
drivers/spi/spi-pl022.c +10 -10
index 1189cfd..edb7298 100644
@@ -773,10 +773,10 @@ static void *next_transfer(struct pl022 *pl022)
 static void unmap_free_dma_scatter(struct pl022 *pl022)
 {
 	/* Unmap and free the SG tables */
-	dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl,
-		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	dma_unmap_sg(pl022->dma_rx_channel->device->dev, pl022->sgt_rx.sgl,
 		     pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+	dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl,
+		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	sg_free_table(&pl022->sgt_rx);
 	sg_free_table(&pl022->sgt_tx);
 }
@@ -1026,16 +1026,16 @@ static int configure_dma(struct pl022 *pl022)
 			  pl022->cur_transfer->len, &pl022->sgt_tx);
 
 	/* Map DMA buffers */
-	rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
-			   pl022->sgt_rx.nents, DMA_FROM_DEVICE);
-	if (!rx_sglen)
-		goto err_rx_sgmap;
-
 	tx_sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl,
 			   pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	if (!tx_sglen)
 		goto err_tx_sgmap;
 
+	rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
+			   pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+	if (!rx_sglen)
+		goto err_rx_sgmap;
+
 	/* Send both scatterlists */
 	rxdesc = dmaengine_prep_slave_sg(rxchan,
 				      pl022->sgt_rx.sgl,
@@ -1070,12 +1070,12 @@ err_txdesc:
 	dmaengine_terminate_all(txchan);
 err_rxdesc:
 	dmaengine_terminate_all(rxchan);
+	dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
+		     pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+err_rx_sgmap:
 	dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
 		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 err_tx_sgmap:
-	dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
-		     pl022->sgt_tx.nents, DMA_FROM_DEVICE);
-err_rx_sgmap:
 	sg_free_table(&pl022->sgt_tx);
 err_alloc_tx_sg:
 	sg_free_table(&pl022->sgt_rx);
Signature · 1 line
1.7.9.5

A message that is not in the archive (sent off-list or not yet received): 1412743127-4523-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/[email protected]

maintainer
#5 lore raw

Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled

Quoted 5 lines · Ray Jui

The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In most cases, the sequence of the mapping does not matter. But in cases where TX and RX happen to use the same buffer, e.g., spidev, it causes the cached TX data not written to memory, because the same memory has been marked invalid when dma_map_sg on the RX buffer is called

This seems like it is a bug in spidev, using the same buffer simultaneously for both directions isn't something I'd think would be expected to work reliably unless it was explicitly mapped as bidirectional.

author maintainer
#6 lore raw

Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled

Quoted 8 lines · Mark Brown
Quoted 5 lines · Ray Jui

The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In most cases, the sequence of the mapping does not matter. But in cases where TX and RX happen to use the same buffer, e.g., spidev, it causes the cached TX data not written to memory, because the same memory has been marked invalid when dma_map_sg on the RX buffer is called

Quoted 4 lines

This seems like it is a bug in spidev, using the same buffer simultaneously for both directions isn't something I'd think would be expected to work reliably unless it was explicitly mapped as bidirectional.

Hi Mark,

Thanks for the reply. It looks like you are also the maintainer of spidev. In this case, could you please help to confirm that you expect spidev to use separate buffers for TX and RX? If so, I can go ahead and make the change in spidev.

+ Grant

Thanks,

Ray

A message that is not in the archive (sent off-list or not yet received): 543562EB.8060300-dY08KVG/lbpWk0Htik3J/[email protected]

maintainer
#8 lore raw

Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled

Quoted 4 lines · Ray Jui

Thanks for the reply. It looks like you are also the maintainer of spidev. In this case, could you please help to confirm that you expect spidev to use separate buffers for TX and RX? If so, I can go ahead and make the change in spidev.

Yes, that would be my expectation for maximum robustness (or if it is going to use one buffer it explicitly maps it for mixed use but I'd expect that to be asking for trouble).

author maintainer
#9 lore raw

Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled

Quoted 11 lines · Mark Brown
Quoted 4 lines · Ray Jui

Thanks for the reply. It looks like you are also the maintainer of spidev. In this case, could you please help to confirm that you expect spidev to use separate buffers for TX and RX? If so, I can go ahead and make the change in spidev.

Yes, that would be my expectation for maximum robustness (or if it is going to use one buffer it explicitly maps it for mixed use but I'd expect that to be asking for trouble).

Okay, Mark. I'm going to make the change in the spidev and submit a new patch.

Thanks for the feedback.

Ray

A message that is not in the archive (sent off-list or not yet received): [email protected]

#11 lore raw

Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled

Quoted 9 lines · Mark Brown
Quoted 4 lines · Ray Jui

Thanks for the reply. It looks like you are also the maintainer of spidev. In this case, could you please help to confirm that you expect spidev to use separate buffers for TX and RX? If so, I can go ahead and make the change in spidev.

Yes, that would be my expectation for maximum robustness (or if it is going to use one buffer it explicitly maps it for mixed use but I'd expect that to be asking for trouble).

Having two separate buffers avoids false successes when running "spidev_test --loop" on a buggy SPI master driver. Been there, done that ;-)

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

author maintainer
#12 lore raw

PATCH 1/1 spi: spidev: Use separate TX and RX bounce buffers

In reply to a message not in the archive

By using separate TX and RX bounce buffers, we avoid potential cache flush and invalidation sequence issue that may be encountered when a single bounce buffer is shared between TX and RX

---
 drivers/spi/spidev.c |   79 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 52 insertions(+), 27 deletions(-)
drivers/spi/spidev.c +52 -27
index e3bc23b..e50039f 100644
@@ -82,10 +82,11 @@ struct spidev_data {
 	struct spi_device	*spi;
 	struct list_head	device_entry;
 
-	/* buffer is NULL unless this device is open (users > 0) */
+	/* TX/RX buffers are NULL unless this device is open (users > 0) */
 	struct mutex		buf_lock;
 	unsigned		users;
-	u8			*buffer;
+	u8			*tx_buffer;
+	u8			*rx_buffer;
 };
 
 static LIST_HEAD(device_list);
@@ -135,7 +136,7 @@ static inline ssize_t
 spidev_sync_write(struct spidev_data *spidev, size_t len)
 {
 	struct spi_transfer	t = {
-			.tx_buf		= spidev->buffer,
+			.tx_buf		= spidev->tx_buffer,
 			.len		= len,
 		};
 	struct spi_message	m;
@@ -149,7 +150,7 @@ static inline ssize_t
 spidev_sync_read(struct spidev_data *spidev, size_t len)
 {
 	struct spi_transfer	t = {
-			.rx_buf		= spidev->buffer,
+			.rx_buf		= spidev->rx_buffer,
 			.len		= len,
 		};
 	struct spi_message	m;
@@ -179,7 +180,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 	if (status > 0) {
 		unsigned long	missing;
 
-		missing = copy_to_user(buf, spidev->buffer, status);
+		missing = copy_to_user(buf, spidev->rx_buffer, status);
 		if (missing == status)
 			status = -EFAULT;
 		else
@@ -206,7 +207,7 @@ spidev_write(struct file *filp, const char __user *buf,
 	spidev = filp->private_data;
 
 	mutex_lock(&spidev->buf_lock);
-	missing = copy_from_user(spidev->buffer, buf, count);
+	missing = copy_from_user(spidev->tx_buffer, buf, count);
 	if (missing == 0)
 		status = spidev_sync_write(spidev, count);
 	else
@@ -224,7 +225,7 @@ static int spidev_message(struct spidev_data *spidev,
 	struct spi_transfer	*k_tmp;
 	struct spi_ioc_transfer *u_tmp;
 	unsigned		n, total;
-	u8			*buf;
+	u8			*tx_buf, *rx_buf;
 	int			status = -EFAULT;
 
 	spi_message_init(&msg);
@@ -236,7 +237,8 @@ static int spidev_message(struct spidev_data *spidev,
 	 * We walk the array of user-provided transfers, using each one
 	 * to initialize a kernel version of the same transfer.
 	 */
-	buf = spidev->buffer;
+	tx_buf = spidev->tx_buffer;
+	rx_buf = spidev->rx_buffer;
 	total = 0;
 	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
 			n;
@@ -250,20 +252,21 @@ static int spidev_message(struct spidev_data *spidev,
 		}
 
 		if (u_tmp->rx_buf) {
-			k_tmp->rx_buf = buf;
+			k_tmp->rx_buf = rx_buf;
 			if (!access_ok(VERIFY_WRITE, (u8 __user *)
 						(uintptr_t) u_tmp->rx_buf,
 						u_tmp->len))
 				goto done;
 		}
 		if (u_tmp->tx_buf) {
-			k_tmp->tx_buf = buf;
-			if (copy_from_user(buf, (const u8 __user *)
+			k_tmp->tx_buf = tx_buf;
+			if (copy_from_user(tx_buf, (const u8 __user *)
 						(uintptr_t) u_tmp->tx_buf,
 					u_tmp->len))
 				goto done;
 		}
-		buf += k_tmp->len;
+		tx_buf += k_tmp->len;
+		rx_buf += k_tmp->len;
 
 		k_tmp->cs_change = !!u_tmp->cs_change;
 		k_tmp->tx_nbits = u_tmp->tx_nbits;
@@ -290,17 +293,17 @@ static int spidev_message(struct spidev_data *spidev,
 		goto done;
 
 	/* copy any rx data out of bounce buffer */
-	buf = spidev->buffer;
+	rx_buf = spidev->rx_buffer;
 	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
 		if (u_tmp->rx_buf) {
 			if (__copy_to_user((u8 __user *)
-					(uintptr_t) u_tmp->rx_buf, buf,
+					(uintptr_t) u_tmp->rx_buf, rx_buf,
 					u_tmp->len)) {
 				status = -EFAULT;
 				goto done;
 			}
 		}
-		buf += u_tmp->len;
+		rx_buf += u_tmp->len;
 	}
 	status = total;
 
@@ -508,22 +511,41 @@ static int spidev_open(struct inode *inode, struct file *filp)
 			break;
 		}
 	}
-	if (status == 0) {
-		if (!spidev->buffer) {
-			spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
-			if (!spidev->buffer) {
+
+	if (status) {
+		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
+		goto err_find_dev;
+	}
+
+	if (!spidev->tx_buffer) {
+		spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
+		if (!spidev->tx_buffer) {
 				dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
 				status = -ENOMEM;
+			goto err_find_dev;
 			}
 		}
-		if (status == 0) {
-			spidev->users++;
-			filp->private_data = spidev;
-			nonseekable_open(inode, filp);
+
+	if (!spidev->rx_buffer) {
+		spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
+		if (!spidev->rx_buffer) {
+			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+			status = -ENOMEM;
+			goto err_alloc_rx_buf;
 		}
-	} else
-		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
+	}
+
+	spidev->users++;
+	filp->private_data = spidev;
+	nonseekable_open(inode, filp);
+
+	mutex_unlock(&device_list_lock);
+	return 0;
 
+err_alloc_rx_buf:
+	kfree(spidev->tx_buffer);
+	spidev->tx_buffer = NULL;
+err_find_dev:
 	mutex_unlock(&device_list_lock);
 	return status;
 }
@@ -542,8 +564,11 @@ static int spidev_release(struct inode *inode, struct file *filp)
 	if (!spidev->users) {
 		int		dofree;
 
-		kfree(spidev->buffer);
-		spidev->buffer = NULL;
+		kfree(spidev->tx_buffer);
+		spidev->tx_buffer = NULL;
+
+		kfree(spidev->rx_buffer);
+		spidev->rx_buffer = NULL;
 
 		/* ... after we unbound from the underlying device? */
 		spin_lock_irq(&spidev->spi_lock);
Signature · 1 line
1.7.9.5

A message that is not in the archive (sent off-list or not yet received): 1412878765-29088-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/[email protected]

maintainer Applied
#14 lore raw

Re: [PATCH] spi: spidev: Use separate TX and RX bounce buffers

Quoted 3 lines · Ray Jui

By using separate TX and RX bounce buffers, we avoid potential cache flush and invalidation sequence issue that may be encountered when a single bounce buffer is shared between TX and RX

Applied, thanks.

author maintainer
#15 lore raw

Re: [PATCH] spi: spidev: Use separate TX and RX bounce buffers

Quoted 7 lines · Mark Brown
Quoted 3 lines · Ray Jui

By using separate TX and RX bounce buffers, we avoid potential cache flush and invalidation sequence issue that may be encountered when a single bounce buffer is shared between TX and RX

Applied, thanks.

Thanks, Mark.

author maintainer
#16 lore raw

PATCH 1/1 spi: pl022: Fix incorrect dma_unmap_sg

In reply to a message not in the archive

When mapped RX DMA entries are unmapped in an error condition when DMA is firstly configured in the driver, the number of TX DMA entries was passed in, which is incorrect

---
 drivers/spi/spi-pl022.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/spi/spi-pl022.c +1 -1
index f35f723..fc2dd84 100644
@@ -1106,7 +1106,7 @@ err_rxdesc:
 		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 err_tx_sgmap:
 	dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
-		     pl022->sgt_tx.nents, DMA_FROM_DEVICE);
+		     pl022->sgt_rx.nents, DMA_FROM_DEVICE);
 err_rx_sgmap:
 	sg_free_table(&pl022->sgt_tx);
 err_alloc_tx_sg:
Signature · 1 line
1.7.9.5

A message that is not in the archive (sent off-list or not yet received): 1412880294-9860-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/[email protected]

maintainer Applied
#18 lore raw

Re: [PATCH] spi: pl022: Fix incorrect dma_unmap_sg

Quoted 3 lines · Ray Jui

When mapped RX DMA entries are unmapped in an error condition when DMA is firstly configured in the driver, the number of TX DMA entries was passed in, which is incorrect

Applied, thanks.

author maintainer
#19 lore raw

Re: [PATCH] spi: pl022: Fix incorrect dma_unmap_sg

Quoted 7 lines · Mark Brown
Quoted 3 lines · Ray Jui

When mapped RX DMA entries are unmapped in an error condition when DMA is firstly configured in the driver, the number of TX DMA entries was passed in, which is incorrect

Applied, thanks.

Thanks, Mark.

author maintainer
#20 lore raw

PATCH 1/1 dmaengine: pl330: use subsys_initcall

In reply to a message not in the archive

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

---
 drivers/dma/pl330.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/dma/pl330.c +11 -1
index d5149aa..abb4cae 100644
@@ -2811,7 +2811,17 @@ static struct amba_driver pl330_driver = {
 	.remove = pl330_remove,
 };
 
-module_amba_driver(pl330_driver);
+static int __init pl330_init(void)
+{
+	return amba_driver_register(&pl330_driver);
+}
+subsys_initcall(pl330_init);
+
+static void __exit pl330_exit(void)
+{
+	amba_driver_unregister(&pl330_driver);
+}
+module_exit(pl330_exit);
 
 MODULE_AUTHOR("Jaswinder Singh <[email protected]>");
 MODULE_DESCRIPTION("API Driver for PL330 DMAC");
Signature · 1 line
1.7.9.5
#21 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

- Lars

#22 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start a transaction requested a channel... while dmaengine driver is still getting probed/not probed yet. So SPI driver didnt get a channel.

Signature · 1 line
~Vinod
#23 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 6 lines · Vinod Koul
Quoted 5 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Quoted 9 lines
Quoted 3 lines

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start a transaction requested a channel... while dmaengine driver is still getting probed/not probed yet. So SPI driver didnt get a channel.

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

- Lars

author maintainer
#24 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 19 lines · Lars-Peter Clausen
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

Quoted 8 lines

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start
a transaction requested a channel... while dmaengine driver is still getting
probed/not probed yet. So SPI driver didnt get a channel.

Quoted 5 lines

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

So in the case of the spi-pl022 driver. It requests the channel in probe function. And obviously DMA is not mandatory, so when the channel request fails the probe won't fail and instead it falls back to PIO. In this case, can you recommend a different way to solve this problem without having the DMA driver probed earlier than its slaves?

Quoted 5 lines

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

I think it makes sense to have the DMA driver, as one of the core components in various SoCs that a lot of peripheral drivers depend on, to be registered at the level of subsys_init or somewhere close. We are not changing this just to get SPI to work. We are changing this because we think DMA should be ready before a lot of its slaves, which are typically done at device_initcall.

I have no problem relying on EPROBE_DEFER for this, provided that it works. The issue is, like I mentioned above, for a lot of slave devices DMA is not mandatory, when DMA fails at probe they would fall back to PIO and never use DMA. Another disadvantage I see with EPROBE_DEFER is delayed boot time.

Quoted 9 lines

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

- Lars

There are currently 12 DMA drivers under drivers/dma registering themselves at subsys_init. I don't see why pl330 cannot do the same. Is there any concern that it may not work for some other SoCs when it's done at subsys_init? So far I cannot think of any. The only dependency of pl330 is the ARM apb_pclk, required during AMBA bus probe. But that's usually ready before subsys_init.

Thanks,

Ray

#25 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 25 lines · Ray Jui
Quoted 19 lines · Lars-Peter Clausen
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

Quoted 8 lines

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start
a transaction requested a channel... while dmaengine driver is still getting
probed/not probed yet. So SPI driver didnt get a channel.

Quoted 5 lines

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

Quoted 5 lines

So in the case of the spi-pl022 driver. It requests the channel in probe function. And obviously DMA is not mandatory, so when the channel request fails the probe won't fail and instead it falls back to PIO. In this case, can you recommend a different way to solve this problem without having the DMA driver probed earlier than its slaves?

dma_request_slave_channel() has the problem that we can't differentiate between no channel provided and channel provided but the dma driver hasn't probed yet. The function will return NULL in both cases. But Stephen Warren added dma_request_slave_channel_reason() a while ago to solve this problem. This function returns a ERR_PTR. If it returns ERR_PTR(-EPROBE_DEFER) it means that a channel has been provided but the DMA driver hasn't probed yet. In this case the SPI driver should return -EPROBE_DEFER to try again later. If the function returns a different error code that means that it was not possible to get the DMA channel and it should fall back to PIO.

Quoted 11 lines
Quoted 6 lines

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

I think it makes sense to have the DMA driver, as one of the core components in various SoCs that a lot of peripheral drivers depend on, to be registered at the level of subsys_init or somewhere close. We are not changing this just to get SPI to work. We are changing this because we think DMA should be ready before a lot of its slaves, which are typically done at device_initcall.

But if the DMA driver for example depends on a clock driver do you put the clock driver at a even earlier init level? The problem with using init levels for solving this problem is that there is only a small amount of init levels available and representing the dependency chains is neither possible with it nor were init level ever intended for solving this. EPROBE_DEFER on the other hand is.

Quoted 6 lines

I have no problem relying on EPROBE_DEFER for this, provided that it works. The issue is, like I mentioned above, for a lot of slave devices DMA is not mandatory, when DMA fails at probe they would fall back to PIO and never use DMA. Another disadvantage I see with EPROBE_DEFER is delayed boot time.

Yea, the EPROBE_DEFER implementation is not ideal, but that is a problem that should be solved rather than working around it. I think there are patches somewhere for example that build a device dependency graph from the phandles in the devicetree and than probe devices in the correct order to reduce the number of times probe deferral is necessary.

Quoted 8 lines
Quoted 8 lines

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

Quoted 7 lines
Quoted 1 line

- Lars

There are currently 12 DMA drivers under drivers/dma registering themselves at subsys_init. I don't see why pl330 cannot do the same. Is there any concern that it may not work for some other SoCs when it's done at subsys_init? So far I cannot think of any. The only dependency of pl330 is the ARM apb_pclk, required during AMBA bus probe. But that's usually ready before subsys_init.

Those other drivers should be converted to device_initcall rather than converting the PL330 driver to subsys_init. Using subsys_init for device drivers is a hack which was used to try to solve ordering problems. But it doesn't work that great, especially if you have more than two devices in your dependency chain. The solution that people have come up with to solve this problem in a better way is probe deferral by the means of -EPROBE_DEFER.

- Lars

author maintainer
#26 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 35 lines · Lars-Peter Clausen
Quoted 25 lines · Ray Jui
Quoted 19 lines · Lars-Peter Clausen
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

Quoted 8 lines

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start
a transaction requested a channel... while dmaengine driver is still getting
probed/not probed yet. So SPI driver didnt get a channel.

Quoted 5 lines

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

Quoted 7 lines

So in the case of the spi-pl022 driver. It requests the channel in probe function. And obviously DMA is not mandatory, so when the channel request fails the probe won't fail and instead it falls back to PIO. In this case,
can you recommend a different way to solve this problem without having the
DMA driver probed earlier than its slaves?

Quoted 11 lines

dma_request_slave_channel() has the problem that we can't differentiate between no channel provided and channel provided but the dma driver hasn't probed yet. The function will return NULL in both cases. But Stephen Warren added dma_request_slave_channel_reason() a while ago to solve this problem. This function returns a ERR_PTR. If it returns ERR_PTR(-EPROBE_DEFER) it means that a channel has been provided but the DMA driver hasn't probed yet. In this case the SPI driver should return -EPROBE_DEFER to try again later. If the function returns a different error code that means that it was not possible to get the DMA channel and it should fall back to PIO.

Thanks for the information. This will solve our problem.

Quoted 33 lines
Quoted 6 lines
Quoted 6 lines

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

Quoted 9 lines

I think it makes sense to have the DMA driver, as one of the core components
in various SoCs that a lot of peripheral drivers depend on, to be registered
at the level of subsys_init or somewhere close. We are not changing this just to get SPI to work. We are changing this because we think DMA should be
ready before a lot of its slaves, which are typically done at device_initcall.

But if the DMA driver for example depends on a clock driver do you put the clock driver at a even earlier init level? The problem with using init levels for solving this problem is that there is only a small amount of init levels available and representing the dependency chains is neither possible with it nor were init level ever intended for solving this. EPROBE_DEFER on the other hand is.

Quoted 9 lines

I have no problem relying on EPROBE_DEFER for this, provided that it works.
The issue is, like I mentioned above, for a lot of slave devices DMA is not
mandatory, when DMA fails at probe they would fall back to PIO and never use
DMA. Another disadvantage I see with EPROBE_DEFER is delayed boot time.

Quoted 6 lines

Yea, the EPROBE_DEFER implementation is not ideal, but that is a problem that should be solved rather than working around it. I think there are patches somewhere for example that build a device dependency graph from the phandles in the devicetree and than probe devices in the correct order to reduce the number of times probe deferral is necessary.

Agreed. Yes, it would be very nice if we can eventually describe the dependencies of various components in a system by utilizing the device tree. This way the dependencies can be customized for each individual SoC.

Quoted 27 lines
Quoted 9 lines
Quoted 9 lines

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

- Lars

Quoted 9 lines

There are currently 12 DMA drivers under drivers/dma registering themselves
at subsys_init. I don't see why pl330 cannot do the same. Is there any concern that it may not work for some other SoCs when it's done at subsys_init? So far I cannot think of any. The only dependency of pl330 is
the ARM apb_pclk, required during AMBA bus probe. But that's usually ready
before subsys_init.

Those other drivers should be converted to device_initcall rather than converting the PL330 driver to subsys_init. Using subsys_init for device drivers is a hack which was used to try to solve ordering problems. But it doesn't work that great, especially if you have more than two devices in your dependency chain. The solution that people have come up with to solve this problem in a better way is probe deferral by the means of -EPROBE_DEFER.

Quoted 2 lines

- Lars

Thanks, Lars, for providing these information. They are very useful!

Ray

#27 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

In reply to Lars-Peter Clausen
Quoted 33 lines · Lars-Peter Clausen
Quoted 25 lines · Ray Jui
Quoted 19 lines · Lars-Peter Clausen
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

Quoted 8 lines

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start
a transaction requested a channel... while dmaengine driver is still getting
probed/not probed yet. So SPI driver didnt get a channel.

Quoted 5 lines

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

Quoted 5 lines

So in the case of the spi-pl022 driver. It requests the channel in probe function. And obviously DMA is not mandatory, so when the channel request fails the probe won't fail and instead it falls back to PIO. In this case, can you recommend a different way to solve this problem without having the DMA driver probed earlier than its slaves?

Quoted 11 lines

dma_request_slave_channel() has the problem that we can't differentiate between no channel provided and channel provided but the dma driver hasn't probed yet. The function will return NULL in both cases. But Stephen Warren added dma_request_slave_channel_reason() a while ago to solve this problem. This function returns a ERR_PTR. If it returns ERR_PTR(-EPROBE_DEFER) it means that a channel has been provided but the DMA driver hasn't probed yet. In this case the SPI driver should return -EPROBE_DEFER to try again later. If the function returns a different error code that means that it was not possible to get the DMA channel and it should fall back to PIO.

So when should the SPI here check, if dmaengine is available. The client doesn't grab channel in its probe, so the client cannot return -EPROBE_DEFER.

Signature · 1 line
~Vinod
Quoted 60 lines
Quoted 7 lines
Quoted 7 lines

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

Quoted 5 lines

I think it makes sense to have the DMA driver, as one of the core components in various SoCs that a lot of peripheral drivers depend on, to be registered at the level of subsys_init or somewhere close. We are not changing this just to get SPI to work. We are changing this because we think DMA should be ready before a lot of its slaves, which are typically done at device_initcall.

But if the DMA driver for example depends on a clock driver do you put the clock driver at a even earlier init level? The problem with using init levels for solving this problem is that there is only a small amount of init levels available and representing the dependency chains is neither possible with it nor were init level ever intended for solving this. EPROBE_DEFER on the other hand is.

Quoted 6 lines

I have no problem relying on EPROBE_DEFER for this, provided that it works. The issue is, like I mentioned above, for a lot of slave devices DMA is not mandatory, when DMA fails at probe they would fall back to PIO and never use DMA. Another disadvantage I see with EPROBE_DEFER is delayed boot time.

Yea, the EPROBE_DEFER implementation is not ideal, but that is a problem that should be solved rather than working around it. I think there are patches somewhere for example that build a device dependency graph from the phandles in the devicetree and than probe devices in the correct order to reduce the number of times probe deferral is necessary.

Quoted 8 lines
Quoted 8 lines

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

Quoted 7 lines
Quoted 1 line

- Lars

There are currently 12 DMA drivers under drivers/dma registering themselves at subsys_init. I don't see why pl330 cannot do the same. Is there any concern that it may not work for some other SoCs when it's done at subsys_init? So far I cannot think of any. The only dependency of pl330 is the ARM apb_pclk, required during AMBA bus probe. But that's usually ready before subsys_init.

Those other drivers should be converted to device_initcall rather than converting the PL330 driver to subsys_init. Using subsys_init for device drivers is a hack which was used to try to solve ordering problems. But it doesn't work that great, especially if you have more than two devices in your dependency chain. The solution that people have come up with to solve this problem in a better way is probe deferral by the means of -EPROBE_DEFER.

- Lars

author maintainer
#28 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

Quoted 45 lines · Vinod Koul
Quoted 33 lines · Lars-Peter Clausen
Quoted 25 lines · Ray Jui
Quoted 19 lines · Lars-Peter Clausen
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

Quoted 8 lines

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start
a transaction requested a channel... while dmaengine driver is still getting
probed/not probed yet. So SPI driver didnt get a channel.

Quoted 5 lines

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

Quoted 5 lines

So in the case of the spi-pl022 driver. It requests the channel in probe function. And obviously DMA is not mandatory, so when the channel request fails the probe won't fail and instead it falls back to PIO. In this case, can you recommend a different way to solve this problem without having the DMA driver probed earlier than its slaves?

Quoted 11 lines

dma_request_slave_channel() has the problem that we can't differentiate between no channel provided and channel provided but the dma driver hasn't probed yet. The function will return NULL in both cases. But Stephen Warren added dma_request_slave_channel_reason() a while ago to solve this problem. This function returns a ERR_PTR. If it returns ERR_PTR(-EPROBE_DEFER) it means that a channel has been provided but the DMA driver hasn't probed yet. In this case the SPI driver should return -EPROBE_DEFER to try again later. If the function returns a different error code that means that it was not possible to get the DMA channel and it should fall back to PIO.

Quoted 4 lines

So when should the SPI here check, if dmaengine is available. The client doesn't grab channel in its probe, so the client cannot return -EPROBE_DEFER.

Currently the spi-pl022 driver requests its DMA channel in pl022_dma_autoprobe, called during driver probe. Lars suggested changing the dma_request_slave_channel call to dma_request_slave_channel_reason. From my understanding, dma_request_slave_channel_reason should return -EPROBE_DEFER if the DMA controller (pl330) device node and its channels are declared in device tree but the driver hasn't been probed. The SPI driver can then return -EPROBE_DEFER, which will cause its probe to be done again, at a later time. By then the pl330 driver will be ready.

This will solve our problem, because we don't care when SPI is probed, but we want to use DMA instead of PIO. But for the original problem that Linus tried to solve by moving spi probe to subsys_initcall in 25c8e03b, if one wants spi probe to be done that early, it will not be able to use DMA with the currently proposed change.

#29 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

In reply to Lars-Peter Clausen
Quoted 28 lines · Lars-Peter Clausen
Quoted 6 lines · Vinod Koul
Quoted 5 lines · Lars-Peter Clausen
Quoted 3 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

Quoted 9 lines
Quoted 3 lines

Well, we do have -EPROBE_DEFER these days to handle these kinds of dependencies so we no longer have to these kinds of manual init reordering tricks.

How ould that work?

Consider for example SPI and dmanegine. SPI driver got probed, then to start a transaction requested a channel... while dmaengine driver is still getting probed/not probed yet. So SPI driver didnt get a channel.

Ideally the SPI driver requests the channel in probe function and if the DMA controller is not yet probed returns EPROBE_DEFER. If the SPI driver requests the channel in the transfer handler it needs to deal with being able to fall back to non DMA transfers anyway so this shouldn't be a problem.

But in any case fiddling around with the init sequences is just a quick hack and might makes the problem less likely to appear in some cases, but there is no guarantee that it works. And I think the proper solution at the moment is to use probe deferral.

Quoted 7 lines

Other subsystems have seen patches which moved drivers from using subsys_initcall to device_initcall/module_..._driver/ with the reasoning that this is no longer necessary because of EPROBE_DEFER. So I don't think we should be doing the exact opposite in DMA framework. Also if we'd apply this patch it won't take to long until somebody suggest going back to module_platform_driver() instead of subsys_initcall.

Sure, I don't mind moving the driver to module_ if that solves the problem with defer probe. I am still not able to wrap my head around on how will deferral probe solve the problem, for example in above SPI case.

So when we request & channel is not found, it can be all that channels are given out so nothing to give or controller of interest not available. How do we distinguish between these and how does controller driver say "looks like device might not be probed, let me try later"?

Signature · 1 line
~Vinod
Tested-by on v1 1/1
#30 lore raw

Re: [PATCH] dmaengine: pl330: use subsys_initcall

In reply to Ray Jui
Quoted 8 lines · Ray Jui

As part of subsystem that many slave drivers depend on, it's more appropriate for the pl330 DMA driver to be initialized at subsys_initcall than device_initcall

---
 drivers/dma/pl330.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

For our setup this was not needed but anyway works fine. Tested on Trats2 (Exynos4412) and Gear2 (Exynos3250).

Best regards,
Krzysztof

Quoted 25 lines
drivers/dma/pl330.c +11 -1
index d5149aa..abb4cae 100644
@@ -2811,7 +2811,17 @@ static struct amba_driver pl330_driver = {
 	.remove = pl330_remove,
 };
 
-module_amba_driver(pl330_driver);
+static int __init pl330_init(void)
+{
+	return amba_driver_register(&pl330_driver);
+}
+subsys_initcall(pl330_init);
+
+static void __exit pl330_exit(void)
+{
+	amba_driver_unregister(&pl330_driver);
+}
+module_exit(pl330_exit);
 
 MODULE_AUTHOR("Jaswinder Singh <[email protected]>");
 MODULE_DESCRIPTION("API Driver for PL330 DMAC");
author maintainer
#31 lore raw

cover Add pinctrl support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial pinctrl support for the Broadcom Cygnus SoC. The Cygnus pinctrl controller supports group based alternate function configuration

Ray Jui (4):
pinctrl: Broadcom Cygnus pinctrl device tree binding
pinctrl: cygnus: add initial pinctrl support
ARM: mach-bcm: enable pinctrl support for Cygnus
ARM: dts: enable pinctrl for Broadcom Cygnus

 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |    5 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/pinctrl/Kconfig                            |    7 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c               |  753 ++++++++++++++++++++
 6 files changed, 859 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#32 lore raw

PATCH 1/4 pinctrl: Broadcom Cygnus pinctrl device tree binding

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +92 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers
+
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"
+
+Each child node represents a configuration. Client devices reference the the
+child node to enable the mux configuration.
+
+For example:
+
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+
+		i2s_0: i2s_0 {
+			brcm,groups = "smart_card0", "smart_card0_fcb";
+			brcm,function = "alt2";
+		};
+
+		i2s_1: i2s_1 {
+			brcm,groups = "smart_card1", "smart_card1_fcb";
+			brcm,function = "alt2";
+		};
+
+		spi_0: spi_0 {
+			brcm,groups = "spi0";
+			brcm,function = "alt1";
+		};
+	}
+
+	spi0@18028000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x18028000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-0 = <&spi_0>;
+			clocks = <&axi81_clk>;
+			clock-names = "apb_pclk";
+	};
+
+Consider the following snapshot of Cygnus pinmux table:
+
+number    pin            group              alt1             alt2        alt3        alt4
+------    ---            ----               ----             ----        ----        ----
+42        sc0_clk        smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio24
+43        sc0_cmdvcc_l   smart_card0        SMART CARD0      I2S_0       N/A         STRAP
+44        sc0_detect     smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio25
+45        sc0_fcb        smart_card0_fcb    SMART CARD0_FCB  I2S_0       N/A         chip_gpio26
+46        sc0_io         smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio27
+47        sc0_rst_l      smart_card0        SMART CARD0      SPDIF       N/A         STRAP
+
+Note due to limitation of the Cygnus hardware, pinmux configuration can only
+be group based. To enable I2S_0 function, one needs the following child node
+configuration:
+
+	i2s_0: i2s_0 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt2";
+	};
+
+This tells the Cygnus pin controller to configure groups "smart_card0" and
+"smart_card0_fcb" to I2S_0. With this configuration, pins 42, 43, 44, 45, 46
+become I2C_0, and pin 47 becomes SPDIF
+
+Consider another example, that one wants to configure the above pins as GPIO:
+
+	gpio_24_27: gpio_24_27 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt4";
+	};
+
+With the above configuration, pins 42, 44, 45, 46 become GPIO, and 43 and 47
+become reserved for STRAP
Signature · 1 line
1.7.9.5
#33 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 19 lines · Ray Jui

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +4 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 14

Quoted 10 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +10 -0
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers

The following is subnodes. Indicate clearly in the binding that these are *not* properties of the main node, but individual subnodes.

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 22

Quoted 7 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +7 -0
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"

NAK. We have standardized bindings for groups and functions, and there are pending patches from S?ren Brinkmann adding this to the pinctrl DT parsing core.

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

If unsure of the definitions of group and function, refer to Documentation/pinctrl.txt

Yours,
Linus Walleij

author maintainer
#34 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 26 lines · Linus Walleij
Quoted 24 lines · Ray Jui

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +9 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
Quoted 9 lines

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 14

Quoted 5 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +5 -0
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers

The following is subnodes. Indicate clearly in the binding that these are *not* properties of the main node, but individual subnodes.

Right. I'll fix the document.

Quoted 20 lines

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 22

Quoted 7 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +7 -0
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"

NAK. We have standardized bindings for groups and functions, and there are pending patches from S?ren Brinkmann adding this to the pinctrl DT parsing core.

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Quoted 5 lines

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

Hmmm, I did this by following brcm,bcm11351-pinctrl.txt:
- function: String. Specifies the pin mux selection. Values must be one
of: "alt1", "alt2", "alt3", "alt4"

But you are right, in the pinctrl binding document it describes the generic pin multiplexing nodes use "function" and "group".

Let me try to explain how the Cygnus pinctrl controller works and maybe you can help to comment whether or not it's suitable for what's described in pinctrl-binding.txt for the usage of "function" and "group".

The Cygnus pinctrl contoller has a limitation that the mux configuration can only be group based. "group" here is a real configuration in the Cygnus pinctrl controller registers. One can configure a "group" to alternate function 1, 2, 3, or 4.

For example, the group "lcd" covers 30 pins. When I configure "lcd" to alternate function 1, all 30 pins are muxed to LCD function. When configured to function 2, all pins are muxed to SRAM function. Now, here comes the issue, when configured to function 3, pins 1-15 and 20-30 become GPIO function, but pins 16-19 becomes SPI5 function. When it's configured to function 4, all 30 pins become GPIO.

In some other cases, when I configure a group to other functions, there could be spare pins which become unused (not brought out to the pad). Or, the spare pins may also become a separate function.

Based on the LCD example, I'd assume I would do the following for the default LCD function:

lcd_node {
	group = "lcd_grp";
	function = "lcd";
};

And in the case of function 3, I would call the function "spi5" and assume the rest of pins become either GPIO (or unused)?

spi5_node {
	group = "lcd_grp";
	function = "spi5";
};
Quoted 6 lines

If unsure of the definitions of group and function, refer to Documentation/pinctrl.txt

Yours,
Linus Walleij

#35 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 15 lines · Ray Jui
Quoted 8 lines · Linus Walleij

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Quoted 5 lines

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

Quoted 6 lines

Hmmm, I did this by following brcm,bcm11351-pinctrl.txt:
- function: String. Specifies the pin mux selection. Values must be one
of: "alt1", "alt2", "alt3", "alt4"

But you are right, in the pinctrl binding document it describes the generic pin multiplexing nodes use "function" and "group".

Note "function" and "groups". Note groups is pluralis. You can select multiple groups for a single function.

Quoted 6 lines

For example, the group "lcd" covers 30 pins. When I configure "lcd" to alternate function 1, all 30 pins are muxed to LCD function. When configured to function 2, all pins are muxed to SRAM function. Now, here comes the issue, when configured to function 3, pins 1-15 and 20-30 become GPIO function, but pins 16-19 becomes SPI5 function. When it's configured to function 4, all 30 pins become GPIO.

I would split the use case in two groups for LCD and SRAM, and three groups for GPIO:
"lcd_grp", "sram_grp", "gpio-1-15_grp", "gpio-16-19_grp", "gpio-20-30_grp", "spi5_grp"

Valid combinations become

function = "lcd"
groups = "lcd_grp";

function = "sram"
groups = "sram_grp"

For all GPIO only this:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-16-19_grp", "gpio-20-30_grp"

For a combined GPIO+SPI:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-20-30_grp"

function = "spi5"
groups = "spi5_grp"

The pinctrl runtile will protest if you try to combine spi5_grp with gpio-16-19_grp.

Quoted 3 lines

In some other cases, when I configure a group to other functions, there could be spare pins which become unused (not brought out to the pad). Or, the spare pins may also become a separate function.

That's cool.

Quoted 8 lines

Based on the LCD example, I'd assume I would do the following for the default LCD function:

lcd_node {
        group = "lcd_grp";
        function = "lcd";
};
Quoted 7 lines

And in the case of function 3, I would call the function "spi5" and assume the rest of pins become either GPIO (or unused)?

spi5_node {
        group = "lcd_grp";
        function = "spi5";
};

Looks cool per above.

You need some clever code in the driver to handle double-configuration of registers and so on, but I think it can be done.

Using pin control as a GPIO backend can be a bit tricky and will need some testing and elaboration, but the subsystem will block collisions.

Yours,
Linus Walleij

author maintainer
#36 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 54 lines · Linus Walleij
Quoted 15 lines · Ray Jui
Quoted 8 lines · Linus Walleij

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Quoted 5 lines

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

Quoted 6 lines

Hmmm, I did this by following brcm,bcm11351-pinctrl.txt:
- function: String. Specifies the pin mux selection. Values must be one
of: "alt1", "alt2", "alt3", "alt4"

But you are right, in the pinctrl binding document it describes the generic pin multiplexing nodes use "function" and "group".

Note "function" and "groups". Note groups is pluralis. You can select multiple groups for a single function.

Quoted 6 lines

For example, the group "lcd" covers 30 pins. When I configure "lcd" to alternate function 1, all 30 pins are muxed to LCD function. When configured to function 2, all pins are muxed to SRAM function. Now, here comes the issue, when configured to function 3, pins 1-15 and 20-30 become GPIO function, but pins 16-19 becomes SPI5 function. When it's configured to function 4, all 30 pins become GPIO.

I would split the use case in two groups for LCD and SRAM, and three groups for GPIO:
"lcd_grp", "sram_grp", "gpio-1-15_grp", "gpio-16-19_grp", "gpio-20-30_grp", "spi5_grp"

Valid combinations become

function = "lcd"
groups = "lcd_grp";

function = "sram"
groups = "sram_grp"

For all GPIO only this:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-16-19_grp", "gpio-20-30_grp"

For a combined GPIO+SPI:

Quoted 9 lines

function = "gpio"
groups = "gpio-1-16_grp", "gpio-20-30_grp"

function = "spi5"
groups = "spi5_grp"

The pinctrl runtile will protest if you try to combine spi5_grp with gpio-16-19_grp.

Okay this makes sense. Thanks.

Quoted 22 lines
Quoted 3 lines

In some other cases, when I configure a group to other functions, there could be spare pins which become unused (not brought out to the pad). Or, the spare pins may also become a separate function.

That's cool.

Quoted 8 lines

Based on the LCD example, I'd assume I would do the following for the default LCD function:

lcd_node {
        group = "lcd_grp";
        function = "lcd";
};
Quoted 7 lines

And in the case of function 3, I would call the function "spi5" and assume the rest of pins become either GPIO (or unused)?

spi5_node {
        group = "lcd_grp";
        function = "spi5";
};
Quoted 4 lines

Looks cool per above.

You need some clever code in the driver to handle double-configuration of registers and so on, but I think it can be done.

Let me see what I can come up with.

Quoted 7 lines

Using pin control as a GPIO backend can be a bit tricky and will need some testing and elaboration, but the subsystem will block collisions.

Yours,
Linus Walleij

author maintainer
#37 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

In reply to Linus Walleij
Quoted 86 lines · Linus Walleij
Quoted 15 lines · Ray Jui
Quoted 8 lines · Linus Walleij

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Quoted 5 lines

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

Quoted 6 lines

Hmmm, I did this by following brcm,bcm11351-pinctrl.txt:
- function: String. Specifies the pin mux selection. Values must be one
of: "alt1", "alt2", "alt3", "alt4"

But you are right, in the pinctrl binding document it describes the generic pin multiplexing nodes use "function" and "group".

Note "function" and "groups". Note groups is pluralis. You can select multiple groups for a single function.

Quoted 6 lines

For example, the group "lcd" covers 30 pins. When I configure "lcd" to alternate function 1, all 30 pins are muxed to LCD function. When configured to function 2, all pins are muxed to SRAM function. Now, here comes the issue, when configured to function 3, pins 1-15 and 20-30 become GPIO function, but pins 16-19 becomes SPI5 function. When it's configured to function 4, all 30 pins become GPIO.

I would split the use case in two groups for LCD and SRAM, and three groups for GPIO:
"lcd_grp", "sram_grp", "gpio-1-15_grp", "gpio-16-19_grp", "gpio-20-30_grp", "spi5_grp"

Valid combinations become

function = "lcd"
groups = "lcd_grp";

function = "sram"
groups = "sram_grp"

For all GPIO only this:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-16-19_grp", "gpio-20-30_grp"

For a combined GPIO+SPI:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-20-30_grp"

function = "spi5"
groups = "spi5_grp"

The pinctrl runtile will protest if you try to combine spi5_grp with gpio-16-19_grp.

Quoted 3 lines

In some other cases, when I configure a group to other functions, there could be spare pins which become unused (not brought out to the pad). Or, the spare pins may also become a separate function.

That's cool.

Quoted 8 lines

Based on the LCD example, I'd assume I would do the following for the default LCD function:

lcd_node {
        group = "lcd_grp";
        function = "lcd";
};
Quoted 7 lines

And in the case of function 3, I would call the function "spi5" and assume the rest of pins become either GPIO (or unused)?

spi5_node {
        group = "lcd_grp";
        function = "spi5";
};

Looks cool per above.

Quoted 9 lines

You need some clever code in the driver to handle double-configuration of registers and so on, but I think it can be done.

Using pin control as a GPIO backend can be a bit tricky and will need some testing and elaboration, but the subsystem will block collisions.

Yours,
Linus Walleij

Hi Linus,

I have another question here. In the B0 revision of our Cygnus chip, the ASIC team added a feature to allow individual pins to be muxed to GPIO. The pinmux controller can still only do group-based muxing in general, but at the same time, you can override most (but not all) individual pins to GPIO.

I believe this HW design actually forces us to mix use "groups" and "pins" in DT.

For example, assuming we mux pins 1 - 10 as MMC (one cmd line, one clk line, and 8 data lines). One might make the decision that he only needs 4 data lines instead of 8 data lines, and he wants to free up the 4 data lines and uses as GPIO. Based on this example, is the following DT configuration valid?

sd_node {
    function = "sd";
    groups = "sd_grps";
};
gpio_node {
    function = "gpio";
    pins = "gpio_7", "gpio_8", "gpio_9", "gpio_10"; /* assuming 1:1

mapping between gpio and pin number to make this example simple */ };

Thanks,

Ray

author maintainer
#38 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 124 lines · Ray Jui
Quoted 86 lines · Linus Walleij
Quoted 15 lines · Ray Jui
Quoted 8 lines · Linus Walleij

Just use "groups" and "function" and refer to Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

Then "alt1", "alt2" etc are non-functional names of functions. Use the real function names, like "spi0" or so. This alt-business seems to be just a shortcut to make it simple, don't do that.

Quoted 5 lines

Then you use e.g. "spi0" as a group name. I prefer you call that "spi0_grp" or something to say it is a group of pins associated with spi0, as spi0 is actually the function.

Quoted 6 lines

Hmmm, I did this by following brcm,bcm11351-pinctrl.txt:
- function: String. Specifies the pin mux selection. Values must be one
of: "alt1", "alt2", "alt3", "alt4"

But you are right, in the pinctrl binding document it describes the generic pin multiplexing nodes use "function" and "group".

Note "function" and "groups". Note groups is pluralis. You can select multiple groups for a single function.

Quoted 6 lines

For example, the group "lcd" covers 30 pins. When I configure "lcd" to alternate function 1, all 30 pins are muxed to LCD function. When configured to function 2, all pins are muxed to SRAM function. Now, here comes the issue, when configured to function 3, pins 1-15 and 20-30 become GPIO function, but pins 16-19 becomes SPI5 function. When it's configured to function 4, all 30 pins become GPIO.

I would split the use case in two groups for LCD and SRAM, and three groups for GPIO:
"lcd_grp", "sram_grp", "gpio-1-15_grp", "gpio-16-19_grp", "gpio-20-30_grp", "spi5_grp"

Valid combinations become

function = "lcd"
groups = "lcd_grp";

function = "sram"
groups = "sram_grp"

For all GPIO only this:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-16-19_grp", "gpio-20-30_grp"

For a combined GPIO+SPI:

function = "gpio"
groups = "gpio-1-16_grp", "gpio-20-30_grp"

function = "spi5"
groups = "spi5_grp"

The pinctrl runtile will protest if you try to combine spi5_grp with gpio-16-19_grp.

Quoted 3 lines

In some other cases, when I configure a group to other functions, there could be spare pins which become unused (not brought out to the pad). Or, the spare pins may also become a separate function.

That's cool.

Quoted 8 lines

Based on the LCD example, I'd assume I would do the following for the default LCD function:

lcd_node {
        group = "lcd_grp";
        function = "lcd";
};
Quoted 7 lines

And in the case of function 3, I would call the function "spi5" and assume the rest of pins become either GPIO (or unused)?

spi5_node {
        group = "lcd_grp";
        function = "spi5";
};

Looks cool per above.

Quoted 9 lines

You need some clever code in the driver to handle double-configuration of registers and so on, but I think it can be done.

Using pin control as a GPIO backend can be a bit tricky and will need some testing and elaboration, but the subsystem will block collisions.

Yours,
Linus Walleij

Hi Linus,

I have another question here. In the B0 revision of our Cygnus chip, the ASIC team added a feature to allow individual pins to be muxed to GPIO. The pinmux controller can still only do group-based muxing in general, but at the same time, you can override most (but not all) individual pins to GPIO.

I believe this HW design actually forces us to mix use "groups" and "pins" in DT.

For example, assuming we mux pins 1 - 10 as MMC (one cmd line, one clk line, and 8 data lines). One might make the decision that he only needs 4 data lines instead of 8 data lines, and he wants to free up the 4 data lines and uses as GPIO. Based on this example, is the following DT configuration valid?

sd_node {
    function = "sd";
    groups = "sd_grps";
};
gpio_node {
    function = "gpio";
    pins = "gpio_7", "gpio_8", "gpio_9", "gpio_10"; /* assuming 1:1
Quoted 7 lines

mapping between gpio and pin number to make this example simple */ };

Thanks,

Ray

I dig into the pinctrl framework code a bit more and found that I can use pinctrl_request_gpio from the GPIO driver and implement gpio_request_enable in the pinctrl driver.

The only problem I see now is that these APIs seem to expect the use of global GPIO numbers? And all pinctrl drivers I checked seem to be hard coding the GPIO to pin mapping when declaring the struct pinctrl_gpio_range array. How would this work in a system like ours that have 3 GPIO controllers and was required to use a dynamic GPIO number (i.e., we use gpio->base = -1, so it derives the base from CONFIG_ARCH_NR_GPIO and goes backwards)?

I guess I can do some runtime calculation in my pinctrl driver to figure out the GPIO->pin mapping based on CONFIG_ARCH_NR_GPIO, but this is assuming that we always fix the order of the device nodes of the 3 GPIO controllers.

I hope I'm not missing something here?

Thanks,

Ray

#39 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 3 lines · Ray Jui

I dig into the pinctrl framework code a bit more and found that I can use pinctrl_request_gpio from the GPIO driver and implement gpio_request_enable in the pinctrl driver.

Yep :) ain't it nice.

Quoted 2 lines

The only problem I see now is that these APIs seem to expect the use of global GPIO numbers?

No they don't, only if you use the deprecated pinctrl_add_gpio_range().

Instead, when you register your struct gpio_chip, use gpiochip_add_pin_range() and this will use relative offsets without relying on global GPIO numbers.

This latter call replaces pinctrl_add_gpio_range().

Quoted 1 line

I hope I'm not missing something here?

You're missing gpiochip_add_pin_range() ;)

Yours,
Linus Walleij

author maintainer
#40 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 17 lines · Linus Walleij
Quoted 3 lines · Ray Jui

I dig into the pinctrl framework code a bit more and found that I can use pinctrl_request_gpio from the GPIO driver and implement gpio_request_enable in the pinctrl driver.

Yep :) ain't it nice.

Quoted 2 lines

The only problem I see now is that these APIs seem to expect the use of global GPIO numbers?

No they don't, only if you use the deprecated pinctrl_add_gpio_range().

Instead, when you register your struct gpio_chip, use gpiochip_add_pin_range() and this will use relative offsets without relying on global GPIO numbers.

Quoted 9 lines

This latter call replaces pinctrl_add_gpio_range().

Quoted 1 line

I hope I'm not missing something here?

You're missing gpiochip_add_pin_range() ;)

Yours,
Linus Walleij

Yeah, I realized this while implementing the driver, :)

I'm now in the final testing/cleaning phase of both Cygnus pinmux and gpio/pinconf driver. I really appreciate that the pinctrl framework allows the two to work seamlessly with each other and at the same time provides the necessary interface to bridge the two, :)

I should be able to send out the patches of the two drivers for review sometime next week.

Thanks for the help!

Ray

#41 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

In reply to Ray Jui
Quoted 6 lines · Ray Jui

I have another question here. In the B0 revision of our Cygnus chip, the ASIC team added a feature to allow individual pins to be muxed to GPIO. The pinmux controller can still only do group-based muxing in general, but at the same time, you can override most (but not all) individual pins to GPIO.

Quoted 7 lines

I believe this HW design actually forces us to mix use "groups" and "pins" in DT.

For example, assuming we mux pins 1 - 10 as MMC (one cmd line, one clk line, and 8 data lines). One might make the decision that he only needs 4 data lines instead of 8 data lines, and he wants to free up the 4 data lines and uses as GPIO.

I would split the 8 available data lines in two groups, like "data-1-4" and "data-5-8" since the use case is such that either you use four or eight lines, not 6 or 7, either just "data-1-4" or both "data-1-4" and "data-5-8".

Quoted 8 lines

Based on this example, is the following DT
configuration valid?

sd_node {
    function = "sd";
    groups = "sd_grps";
};
Quoted 5 lines
gpio_node {
    function = "gpio";
    pins = "gpio_7", "gpio_8", "gpio_9", "gpio_10"; /* assuming 1:1

mapping between gpio and pin number to make this example simple */ };

Muxing an individual GPIO from the device tree is seldom a good idea as you realized in your follow-up mail ;)

But this:

sd_node {
     function = "sd";
     groups = "data-1-4", "data-5-8", "other-pin-group";
};

Is perfectly fine. One function, several groups.

Yours,
Linus Walleij

author maintainer
#42 lore raw

PATCH 2/4 pinctrl: cygnus: add initial pinctrl support

In reply to Ray Jui

This adds the initial driver support for the Broadcom Cygnus pinctrl controller. The Cygnus pinctrl controller supports group based alternate function configuration

---
 drivers/pinctrl/Kconfig              |    7 +
 drivers/pinctrl/Makefile             |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c |  753 ++++++++++++++++++++++++++++++++++
 3 files changed, 761 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
drivers/pinctrl/Kconfig +7 -0
index d014f22..4549e9f 100644
@@ -85,6 +85,13 @@ config PINCTRL_BCM281XX
 	  BCM28145, and BCM28155 SoCs.  This driver requires the pinctrl
 	  framework.  GPIO is provided by a separate GPIO driver.
 
+config PINCTRL_BCM_CYGNUS
+	bool "Broadcom Cygnus pinctrl driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+
 config PINCTRL_LANTIQ
 	bool
 	depends on LANTIQ
drivers/pinctrl/Makefile +1 -0
index c030b3d..4ed8e8a 100644
@@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_BF60x)	+= pinctrl-adi2-bf60x.o
 obj-$(CONFIG_PINCTRL_AT91)	+= pinctrl-at91.o
 obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
 obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM_CYGNUS)	+= pinctrl-bcm-cygnus.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +753 -0

753 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#43 lore raw

Re: [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support

Quoted 11 lines · Ray Jui

This adds the initial driver support for the Broadcom Cygnus pinctrl controller. The Cygnus pinctrl controller supports group based alternate function configuration

---
 drivers/pinctrl/Kconfig              |    7 +
 drivers/pinctrl/Makefile             |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c |  753 ++++++++++++++++++++++++++++++++++

With the proliferation of Broadcom drivers, please first send a patch moving pinctrl-bcm281xx.c and pinctrl-bcm2835.c to drivers/pinctrl/broadcom or something, so we can collect them there.

I don't know if the hardware has any similarity though, so invite the authors of the previous drivers to review this code.

drivers/pinctrl/Kconfig config PINCTRL_BCM281XX · line 93

Quoted 6 lines
drivers/pinctrl/Kconfig +6 -0
+config PINCTRL_BCM_CYGNUS
+       bool "Broadcom Cygnus pinctrl driver"
+       depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+       select PINMUX
+       select PINCONF
+       select GENERIC_PINCONF

Nice that you use GENERIC_PINCONF! :)

Quoted 5 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +5 -0
+/*
+ * Cygnus pinctrl core
+ *
+ * @pctl: pointer to pinctrl_dev
+ * @dev: pointer to the device

drivers/pinctrl/pinctrl-bcm-cygnus.c line 76

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+ * @base: I/O register base for Cygnus pinctrl configuration
+ *
+ */
+struct cygnus_pinctrl {
+       struct pinctrl_dev *pctl;
+       struct device *dev;
+       void __iomem *base;
+
+       const struct pinctrl_pin_desc *pins;
+       unsigned num_pins;

Why is this not simply just a part of struct pinctrl_desc? Why does it have to be multiplied here?

Quoted 17 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +17 -0
+/*
+ * List of groups of pins
+ */
+static const unsigned gpio0_pins[] = { 12 };
+static const unsigned gpio1_pins[] = { 13 };
+static const unsigned gpio2_pins[] = { 14 };
+static const unsigned gpio3_pins[] = { 15 };
+static const unsigned gpio4_pins[] = { 16 };
+static const unsigned gpio5_pins[] = { 17 };
+static const unsigned gpio6_pins[] = { 18 };
+static const unsigned gpio7_pins[] = { 19 };
+static const unsigned gpio8_pins[] = { 20 };
+static const unsigned gpio9_pins[] = { 21 };
+static const unsigned gpio10_pins[] = { 22 };
+static const unsigned gpio11_pins[] = { 23 };
+static const unsigned gpio12_pins[] = { 24 };
+static const unsigned gpio13_pins[] = { 25 };

drivers/pinctrl/pinctrl-bcm-cygnus.c line 306

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+static const unsigned gpio14_pins[] = { 26 };
+static const unsigned gpio15_pins[] = { 27 };
+static const unsigned gpio16_pins[] = { 28 };
+static const unsigned gpio17_pins[] = { 29 };
+static const unsigned gpio18_pins[] = { 30 };
+static const unsigned gpio19_pins[] = { 31 };
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };

Have you considered implementing .gpio_request_enable() and .gpio_disable_free() to get around having to have one group for each GPIO line?

Quoted 30 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +30 -0
+static const unsigned pwm0_pins[] = { 38 };
+static const unsigned pwm1_pins[] = { 39 };
+static const unsigned pwm2_pins[] = { 40 };
+static const unsigned pwm3_pins[] = { 41 };
+static const unsigned sdio0_pins[] = { 94, 95, 96, 97, 98, 99 };
+static const unsigned smart_card0_pins[] = { 42, 43, 44, 46, 47 };
+static const unsigned smart_card1_pins[] = { 48, 49, 50, 52, 53 };
+static const unsigned spi0_pins[] = { 54, 55, 56, 57 };
+static const unsigned spi1_pins[] = { 58, 59, 60, 61 };
+static const unsigned spi2_pins[] = { 62, 63, 64, 65 };
+static const unsigned spi3_pins[] = { 66, 67, 68, 69 };
+static const unsigned d1w_pins[] = { 10, 11 };
+static const unsigned lcd_pins[] = { 126, 127, 128, 129, 130, 131, 132,        133,
+       134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+       148, 149, 150, 151, 152, 153, 154, 155 };
+static const unsigned uart0_pins[] = { 70, 71, 72, 73 };
+static const unsigned uart1_dte_pins[] = { 75, 76, 77, 78 };
+static const unsigned uart1_pins[] = { 74, 79, 80, 81 };
+static const unsigned uart3_pins[] = { 82, 83 };
+static const unsigned qspi_pins[] = { 104, 105, 106, 107 };
+static const unsigned nand_pins[] = { 110, 111, 112, 113, 114, 115, 116, 117,
+       118, 119, 120, 121, 122, 123, 124, 125 };
+static const unsigned sdio0_cd_pins[] = { 103 };
+static const unsigned sdio0_mmc_pins[] = { 100, 101, 102 };
+static const unsigned can0_spi4_pins[] = { 86, 87 };
+static const unsigned can1_spi4_pins[] = { 88, 89 };
+static const unsigned sdio1_cd_pins[] = { 93 };
+static const unsigned sdio1_led_pins[] = { 84, 85 };
+static const unsigned sdio1_mmc_pins[] = { 90, 91, 92 };
+static const unsigned camera_led_pins[] = { 156, 157, 158, 159, 160 };

drivers/pinctrl/pinctrl-bcm-cygnus.c line 346

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+static const unsigned camera_rgmii_pins[] = { 169, 170, 171, 169, 170, 171,
+       172, 173 };
+static const unsigned camera_sram_rgmii_pins[] = { 161, 162, 163, 164, 165,
+       166, 167, 168 };
+static const unsigned qspi_gpio_pins[] = { 108, 109 };
+static const unsigned smart_card0_fcb_pins[] = { 45 };
+static const unsigned smart_card1_fcb_pins[] = { 51 };
+static const unsigned gpio0_3p3_pins[] = { 176 };
+static const unsigned gpio1_3p3_pins[] = { 177 };
+static const unsigned gpio2_3p3_pins[] = { 178 };

Looks good...

Quoted 54 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +54 -0
+/*
+ * List of groups names. Need to match the order in cygnus_pin_groups
+ */
+static const char * const cygnus_pin_group_names[] = {
+       "gpio0",
+       "gpio1",
+       "gpio2",
+       "gpio3",
+       "gpio4",
+       "gpio5",
+       "gpio6",
+       "gpio7",
+       "gpio8",
+       "gpio9",
+       "gpio10",
+       "gpio11",
+       "gpio12",
+       "gpio13",
+       "gpio14",
+       "gpio15",
+       "gpio16",
+       "gpio17",
+       "gpio18",
+       "gpio19",
+       "gpio20",
+       "gpio21",
+       "gpio22",
+       "gpio23",
+       "pwm0",
+       "pwm1",
+       "pwm2",
+       "pwm3",
+       "sdio0",
+       "smart_card0",
+       "smart_card1",
+       "spi0",
+       "spi1",
+       "spi2",
+       "spi3",
+       "d1w",
+       "lcd",
+       "uart0",
+       "uart1_dte",
+       "uart1",
+       "uart3",
+       "qspi",
+       "nand",
+       "sdio0_cd",
+       "sdio0_mmc",
+       "can0_spi4",
+       "can1_spi4",
+       "sdio1_cd",
+       "sdio1_led",
+       "sdio1_mmc",

drivers/pinctrl/pinctrl-bcm-cygnus.c line 411

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+       "camera_led",
+       "camera_rgmii",
+       "camera_sram_rgmii",
+       "qspi_gpio",
+       "smart_card0_fcb",
+       "smart_card1_fcb",
+       "gpio0_3p3",
+       "gpio1_3p3",
+       "gpio2_3p3",
+};

This looks very much like function names as noted in the binding. I would say, suffix every group with _grp or something so it's not as confusing. Remember, spi0 is a function of the SoC, pins {1,2} is just a group of pins that it may appear on.

Quoted 8 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +8 -0
+#define CYGNUS_PIN_FUNCTION(fcn_name, mux_val)                 \
+{                                                              \
+       .name = #fcn_name,                                      \
+       .group_names = cygnus_pin_group_names,                  \
+       .num_groups = ARRAY_SIZE(cygnus_pin_group_names),       \
+       .mux = mux_val,                                         \
+}
+

drivers/pinctrl/pinctrl-bcm-cygnus.c line 495

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+/*
+ * Cygnus has 4 alternate functions. All groups can be configured to any of
+ * the 4 alternate functions
+ */
+static const struct cygnus_pin_function cygnus_pin_functions[] = {
+       CYGNUS_PIN_FUNCTION(alt1, 0),
+       CYGNUS_PIN_FUNCTION(alt2, 1),
+       CYGNUS_PIN_FUNCTION(alt3, 2),
+       CYGNUS_PIN_FUNCTION(alt4, 3),
+};

These are not functions. These are per-pin mux ways.

Re-read the documentation of what a function is: it is not something abstract like "alternative something" but something very direct like uart0 or spi0.

drivers/pinctrl/pinctrl-bcm-cygnus.c line 545

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+               struct device_node *np, struct pinctrl_map **map,
+               unsigned *num_maps)
+{

After S?ren Brinkmanns patches youy should be able to use core functions for this and avoid this code altogether.

Quoted 1 line

+ num_groups = of_property_count_strings(np, "brcm,groups");

As mentioned, just "groups".

Quoted 1 line

+ ret = of_property_read_string(np, "brcm,function", &function_name);

As mentioned, just "function".

drivers/pinctrl/pinctrl-bcm-cygnus.c line 576

Quoted 2 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +2 -0
+       ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+                       num_maps, num_groups);

Good use of utilities!

Apart from this things look nice.

The main comment to address is the definition of functions.

Yours,
Linus Walleij

author maintainer
#44 lore raw

Re: [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support

Quoted 14 lines · Linus Walleij
Quoted 11 lines · Ray Jui

This adds the initial driver support for the Broadcom Cygnus pinctrl controller. The Cygnus pinctrl controller supports group based alternate function configuration

---
 drivers/pinctrl/Kconfig              |    7 +
 drivers/pinctrl/Makefile             |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c |  753 ++++++++++++++++++++++++++++++++++
Quoted 5 lines

With the proliferation of Broadcom drivers, please first send a patch moving pinctrl-bcm281xx.c and pinctrl-bcm2835.c to drivers/pinctrl/broadcom or something, so we can collect them there.

Okay. This change will be included as the first patch in the next patch set.

Quoted 3 lines

I don't know if the hardware has any similarity though, so invite the authors of the previous drivers to review this code.

They are completely different. The only similarity between Cygnus and bcm281xx pinctrl is that they use the same concept of alternation functions (1, 2, 3, 4) for mux configuration.

Quoted 19 lines

drivers/pinctrl/Kconfig config PINCTRL_BCM281XX · line 93

Quoted 6 lines
drivers/pinctrl/Kconfig +6 -0
+config PINCTRL_BCM_CYGNUS
+       bool "Broadcom Cygnus pinctrl driver"
+       depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+       select PINMUX
+       select PINCONF
+       select GENERIC_PINCONF

Nice that you use GENERIC_PINCONF! :)

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+/*
+ * Cygnus pinctrl core
+ *
+ * @pctl: pointer to pinctrl_dev
+ * @dev: pointer to the device
+ * @base: I/O register base for Cygnus pinctrl configuration
+ *
+ */
+struct cygnus_pinctrl {
+       struct pinctrl_dev *pctl;
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 76

Quoted 5 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +5 -0
+       struct device *dev;
+       void __iomem *base;
+
+       const struct pinctrl_pin_desc *pins;
+       unsigned num_pins;

Why is this not simply just a part of struct pinctrl_desc? Why does it have to be multiplied here?

Okay. Let me look into this.

Quoted 23 lines
Quoted 23 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +23 -0
+/*
+ * List of groups of pins
+ */
+static const unsigned gpio0_pins[] = { 12 };
+static const unsigned gpio1_pins[] = { 13 };
+static const unsigned gpio2_pins[] = { 14 };
+static const unsigned gpio3_pins[] = { 15 };
+static const unsigned gpio4_pins[] = { 16 };
+static const unsigned gpio5_pins[] = { 17 };
+static const unsigned gpio6_pins[] = { 18 };
+static const unsigned gpio7_pins[] = { 19 };
+static const unsigned gpio8_pins[] = { 20 };
+static const unsigned gpio9_pins[] = { 21 };
+static const unsigned gpio10_pins[] = { 22 };
+static const unsigned gpio11_pins[] = { 23 };
+static const unsigned gpio12_pins[] = { 24 };
+static const unsigned gpio13_pins[] = { 25 };
+static const unsigned gpio14_pins[] = { 26 };
+static const unsigned gpio15_pins[] = { 27 };
+static const unsigned gpio16_pins[] = { 28 };
+static const unsigned gpio17_pins[] = { 29 };
+static const unsigned gpio18_pins[] = { 30 };
+static const unsigned gpio19_pins[] = { 31 };
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 306

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };

Have you considered implementing .gpio_request_enable() and .gpio_disable_free() to get around having to have one group for each GPIO line?

Okay the Cygnus pin controller is really a mess. GPIO 0 ~ GPIO23 are really 23 distinct groups, each with one pin. Then the rest of GPIOs go under other groups. In general, when we set a group to alternate function 4, all pins become GPIO.

Quoted 34 lines
Quoted 34 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +34 -0
+static const unsigned pwm0_pins[] = { 38 };
+static const unsigned pwm1_pins[] = { 39 };
+static const unsigned pwm2_pins[] = { 40 };
+static const unsigned pwm3_pins[] = { 41 };
+static const unsigned sdio0_pins[] = { 94, 95, 96, 97, 98, 99 };
+static const unsigned smart_card0_pins[] = { 42, 43, 44, 46, 47 };
+static const unsigned smart_card1_pins[] = { 48, 49, 50, 52, 53 };
+static const unsigned spi0_pins[] = { 54, 55, 56, 57 };
+static const unsigned spi1_pins[] = { 58, 59, 60, 61 };
+static const unsigned spi2_pins[] = { 62, 63, 64, 65 };
+static const unsigned spi3_pins[] = { 66, 67, 68, 69 };
+static const unsigned d1w_pins[] = { 10, 11 };
+static const unsigned lcd_pins[] = { 126, 127, 128, 129, 130, 131, 132,        133,
+       134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+       148, 149, 150, 151, 152, 153, 154, 155 };
+static const unsigned uart0_pins[] = { 70, 71, 72, 73 };
+static const unsigned uart1_dte_pins[] = { 75, 76, 77, 78 };
+static const unsigned uart1_pins[] = { 74, 79, 80, 81 };
+static const unsigned uart3_pins[] = { 82, 83 };
+static const unsigned qspi_pins[] = { 104, 105, 106, 107 };
+static const unsigned nand_pins[] = { 110, 111, 112, 113, 114, 115, 116, 117,
+       118, 119, 120, 121, 122, 123, 124, 125 };
+static const unsigned sdio0_cd_pins[] = { 103 };
+static const unsigned sdio0_mmc_pins[] = { 100, 101, 102 };
+static const unsigned can0_spi4_pins[] = { 86, 87 };
+static const unsigned can1_spi4_pins[] = { 88, 89 };
+static const unsigned sdio1_cd_pins[] = { 93 };
+static const unsigned sdio1_led_pins[] = { 84, 85 };
+static const unsigned sdio1_mmc_pins[] = { 90, 91, 92 };
+static const unsigned camera_led_pins[] = { 156, 157, 158, 159, 160 };
+static const unsigned camera_rgmii_pins[] = { 169, 170, 171, 169, 170, 171,
+       172, 173 };
+static const unsigned camera_sram_rgmii_pins[] = { 161, 162, 163, 164, 165,
+       166, 167, 168 };
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 346

Quoted 6 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +6 -0
+static const unsigned qspi_gpio_pins[] = { 108, 109 };
+static const unsigned smart_card0_fcb_pins[] = { 45 };
+static const unsigned smart_card1_fcb_pins[] = { 51 };
+static const unsigned gpio0_3p3_pins[] = { 176 };
+static const unsigned gpio1_3p3_pins[] = { 177 };
+static const unsigned gpio2_3p3_pins[] = { 178 };

Looks good...

Note these pins are definitions in the driver that help to describe the pad layout. We can't really configure any individual pins in Cygnus.

Quoted 61 lines
Quoted 61 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +61 -0
+/*
+ * List of groups names. Need to match the order in cygnus_pin_groups
+ */
+static const char * const cygnus_pin_group_names[] = {
+       "gpio0",
+       "gpio1",
+       "gpio2",
+       "gpio3",
+       "gpio4",
+       "gpio5",
+       "gpio6",
+       "gpio7",
+       "gpio8",
+       "gpio9",
+       "gpio10",
+       "gpio11",
+       "gpio12",
+       "gpio13",
+       "gpio14",
+       "gpio15",
+       "gpio16",
+       "gpio17",
+       "gpio18",
+       "gpio19",
+       "gpio20",
+       "gpio21",
+       "gpio22",
+       "gpio23",
+       "pwm0",
+       "pwm1",
+       "pwm2",
+       "pwm3",
+       "sdio0",
+       "smart_card0",
+       "smart_card1",
+       "spi0",
+       "spi1",
+       "spi2",
+       "spi3",
+       "d1w",
+       "lcd",
+       "uart0",
+       "uart1_dte",
+       "uart1",
+       "uart3",
+       "qspi",
+       "nand",
+       "sdio0_cd",
+       "sdio0_mmc",
+       "can0_spi4",
+       "can1_spi4",
+       "sdio1_cd",
+       "sdio1_led",
+       "sdio1_mmc",
+       "camera_led",
+       "camera_rgmii",
+       "camera_sram_rgmii",
+       "qspi_gpio",
+       "smart_card0_fcb",
+       "smart_card1_fcb",
+       "gpio0_3p3",
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 411

Quoted 3 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +3 -0
+       "gpio1_3p3",
+       "gpio2_3p3",
+};

This looks very much like function names as noted in the binding. I would say, suffix every group with _grp or something so it's not as confusing. Remember, spi0 is a function of the SoC, pins {1,2} is just a group of pins that it may appear on.

Yes, suffix every group with _grp helps a lot to clarify the confusion. Will fix this.

Quoted 21 lines
Quoted 8 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +8 -0
+#define CYGNUS_PIN_FUNCTION(fcn_name, mux_val)                 \
+{                                                              \
+       .name = #fcn_name,                                      \
+       .group_names = cygnus_pin_group_names,                  \
+       .num_groups = ARRAY_SIZE(cygnus_pin_group_names),       \
+       .mux = mux_val,                                         \
+}
+

drivers/pinctrl/pinctrl-bcm-cygnus.c line 495

Quoted 10 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +10 -0
+/*
+ * Cygnus has 4 alternate functions. All groups can be configured to any of
+ * the 4 alternate functions
+ */
+static const struct cygnus_pin_function cygnus_pin_functions[] = {
+       CYGNUS_PIN_FUNCTION(alt1, 0),
+       CYGNUS_PIN_FUNCTION(alt2, 1),
+       CYGNUS_PIN_FUNCTION(alt3, 2),
+       CYGNUS_PIN_FUNCTION(alt4, 3),
+};

These are not functions. These are per-pin mux ways.

Quoted 4 lines

Re-read the documentation of what a function is: it is not something abstract like "alternative something" but something very direct like uart0 or spi0.

Yes, agree. Will fix.

Quoted 8 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 545

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+               struct device_node *np, struct pinctrl_map **map,
+               unsigned *num_maps)
+{

After S?ren Brinkmanns patches youy should be able to use core functions for this and avoid this code altogether.

Will that help to take care our case, based on the way we will use "function" and "group"?

Quoted 4 lines
Quoted 1 line

+ num_groups = of_property_count_strings(np, "brcm,groups");

As mentioned, just "groups".

I guess I will use "group"?

Quoted 4 lines
Quoted 1 line

+ ret = of_property_read_string(np, "brcm,function", &function_name);

As mentioned, just "function".

Yes.

Quoted 12 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 576

Quoted 2 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +2 -0
+       ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+                       num_maps, num_groups);

Good use of utilities!

Apart from this things look nice.

The main comment to address is the definition of functions.

Yours,
Linus Walleij

Thanks a lot for the review!!!

#45 lore raw

Re: [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support

Quoted 9 lines · Ray Jui
Quoted 5 lines · Linus Walleij

On Fri, Nov 28, 2014 at 12:46 AM, Ray Jui <[email protected]> wrote:

I don't know if the hardware has any similarity though, so invite the authors of the previous drivers to review this code.

They are completely different. The only similarity between Cygnus and bcm281xx pinctrl is that they use the same concept of alternation functions (1, 2, 3, 4) for mux configuration.

Then you can probably look at that driver for inspiration on how to handle the situation you described earlier with collissions.

Quoted 32 lines
Quoted 23 lines
Quoted 23 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +23 -0
+/*
+ * List of groups of pins
+ */
+static const unsigned gpio0_pins[] = { 12 };
+static const unsigned gpio1_pins[] = { 13 };
+static const unsigned gpio2_pins[] = { 14 };
+static const unsigned gpio3_pins[] = { 15 };
+static const unsigned gpio4_pins[] = { 16 };
+static const unsigned gpio5_pins[] = { 17 };
+static const unsigned gpio6_pins[] = { 18 };
+static const unsigned gpio7_pins[] = { 19 };
+static const unsigned gpio8_pins[] = { 20 };
+static const unsigned gpio9_pins[] = { 21 };
+static const unsigned gpio10_pins[] = { 22 };
+static const unsigned gpio11_pins[] = { 23 };
+static const unsigned gpio12_pins[] = { 24 };
+static const unsigned gpio13_pins[] = { 25 };
+static const unsigned gpio14_pins[] = { 26 };
+static const unsigned gpio15_pins[] = { 27 };
+static const unsigned gpio16_pins[] = { 28 };
+static const unsigned gpio17_pins[] = { 29 };
+static const unsigned gpio18_pins[] = { 30 };
+static const unsigned gpio19_pins[] = { 31 };
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 306

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };

Have you considered implementing .gpio_request_enable() and .gpio_disable_free() to get around having to have one group for each GPIO line?

Quoted 4 lines

Okay the Cygnus pin controller is really a mess. GPIO 0 ~ GPIO23 are really 23 distinct groups, each with one pin. Then the rest of GPIOs go under other groups. In general, when we set a group to alternate function 4, all pins become GPIO.

It will require some complicated code no matter how you handle it I'm afraid. Rely on the pin control subsystem to handle collisions though.

Quoted 11 lines
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 346

Quoted 6 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +6 -0
+static const unsigned qspi_gpio_pins[] = { 108, 109 };
+static const unsigned smart_card0_fcb_pins[] = { 45 };
+static const unsigned smart_card1_fcb_pins[] = { 51 };
+static const unsigned gpio0_3p3_pins[] = { 176 };
+static const unsigned gpio1_3p3_pins[] = { 177 };
+static const unsigned gpio2_3p3_pins[] = { 178 };

Looks good...

Note these pins are definitions in the driver that help to describe the pad layout. We can't really configure any individual pins in Cygnus.

Yeah it's a groupwise controller then, that's similar to e.g. the coh901 driver.

We should be able to accomodate this...

Quoted 10 lines
Quoted 7 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 545

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+               struct device_node *np, struct pinctrl_map **map,
+               unsigned *num_maps)
+{

After S?ren Brinkmanns patches youy should be able to use core functions for this and avoid this code altogether.

Will that help to take care our case, based on the way we will use "function" and "group"?

groupS but yes it will work with your controller, though I think .set_mux and the controller state will need some elaborate code to handle what the framework requests.

Quoted 5 lines
Quoted 4 lines
Quoted 1 line

+ num_groups = of_property_count_strings(np, "brcm,groups");

As mentioned, just "groups".

I guess I will use "group"?

No groups, as with the standard attribute "gpios", this may be a single group too, it's just a standard binding.

Yours,
Linus Walleij

author maintainer
#46 lore raw

Re: [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support

Quoted 64 lines · Linus Walleij
Quoted 9 lines · Ray Jui
Quoted 5 lines · Linus Walleij

On Fri, Nov 28, 2014 at 12:46 AM, Ray Jui <[email protected]> wrote:

I don't know if the hardware has any similarity though, so invite the authors of the previous drivers to review this code.

They are completely different. The only similarity between Cygnus and bcm281xx pinctrl is that they use the same concept of alternation functions (1, 2, 3, 4) for mux configuration.

Then you can probably look at that driver for inspiration on how to handle the situation you described earlier with collissions.

Quoted 32 lines
Quoted 23 lines
Quoted 23 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +23 -0
+/*
+ * List of groups of pins
+ */
+static const unsigned gpio0_pins[] = { 12 };
+static const unsigned gpio1_pins[] = { 13 };
+static const unsigned gpio2_pins[] = { 14 };
+static const unsigned gpio3_pins[] = { 15 };
+static const unsigned gpio4_pins[] = { 16 };
+static const unsigned gpio5_pins[] = { 17 };
+static const unsigned gpio6_pins[] = { 18 };
+static const unsigned gpio7_pins[] = { 19 };
+static const unsigned gpio8_pins[] = { 20 };
+static const unsigned gpio9_pins[] = { 21 };
+static const unsigned gpio10_pins[] = { 22 };
+static const unsigned gpio11_pins[] = { 23 };
+static const unsigned gpio12_pins[] = { 24 };
+static const unsigned gpio13_pins[] = { 25 };
+static const unsigned gpio14_pins[] = { 26 };
+static const unsigned gpio15_pins[] = { 27 };
+static const unsigned gpio16_pins[] = { 28 };
+static const unsigned gpio17_pins[] = { 29 };
+static const unsigned gpio18_pins[] = { 30 };
+static const unsigned gpio19_pins[] = { 31 };
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 306

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };

Have you considered implementing .gpio_request_enable() and .gpio_disable_free() to get around having to have one group for each GPIO line?

Quoted 4 lines

Okay the Cygnus pin controller is really a mess. GPIO 0 ~ GPIO23 are really 23 distinct groups, each with one pin. Then the rest of GPIOs go under other groups. In general, when we set a group to alternate function 4, all pins become GPIO.

It will require some complicated code no matter how you handle it I'm afraid. Rely on the pin control subsystem to handle collisions though.

Quoted 9 lines
Quoted 9 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 346

Quoted 6 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +6 -0
+static const unsigned qspi_gpio_pins[] = { 108, 109 };
+static const unsigned smart_card0_fcb_pins[] = { 45 };
+static const unsigned smart_card1_fcb_pins[] = { 51 };
+static const unsigned gpio0_3p3_pins[] = { 176 };
+static const unsigned gpio1_3p3_pins[] = { 177 };
+static const unsigned gpio2_3p3_pins[] = { 178 };

Looks good...

Quoted 8 lines
Quoted 2 lines

Note these pins are definitions in the driver that help to describe the pad layout. We can't really configure any individual pins in Cygnus.

Yeah it's a groupwise controller then, that's similar to e.g. the coh901 driver.

We should be able to accomodate this...

Okay will check the coh901 driver.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

drivers/pinctrl/pinctrl-bcm-cygnus.c line 545

Quoted 4 lines
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +4 -0
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+               struct device_node *np, struct pinctrl_map **map,
+               unsigned *num_maps)
+{

After S?ren Brinkmanns patches youy should be able to use core functions for this and avoid this code altogether.

Quoted 7 lines
Quoted 2 lines

Will that help to take care our case, based on the way we will use "function" and "group"?

groupS but yes it will work with your controller, though I think .set_mux and the controller state will need some elaborate code to handle what the framework requests.

Thanks.

Quoted 9 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line

+ num_groups = of_property_count_strings(np, "brcm,groups");

As mentioned, just "groups".

I guess I will use "group"?

No groups, as with the standard attribute "gpios", this may be a single group too, it's just a standard binding.

Okay.

Quoted 3 lines

Yours,
Linus Walleij

author maintainer
#47 lore raw

PATCH 3/4 ARM: mach-bcm: enable pinctrl support for Cygnus

In reply to Ray Jui

This enables the pinctrl driver for Broadcom Cygnus SoC

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..b4efff2 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select PINCTRL_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#48 lore raw

PATCH 4/4 ARM: dts: enable pinctrl for Broadcom Cygnus

In reply to Ray Jui

This enables the pinctrl support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |    5 +++++
 1 file changed, 5 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +5 -0
index 5126f9e..4c6bf4d 100644
@@ -54,6 +54,11 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#49 lore raw

cover Add common clock support for Broadcom iProc architecture

In reply to a message not in the archive

This patchset contains the initial common clock support for Broadcom's iProc family of SoCs. The iProc clock architecture comprises of various PLLs, e.g., ARMPLL, GENPLL, LCPLL0, MIPIPLL, and etc. An onboard crystal serves as the basic reference clock for these PLLs. Each PLL may have several leaf clocks. One special group of clocks is the ASIU clocks, which are dervied directly from the crystal reference clock.

This patchset also contains the basic clock support for the Broadcom Cygnus SoC, which implements the iProc clock architecture

Ray Jui (4):
clk: iproc: define Broadcom iProc clock binding
clk: iproc: add initial common clock support
clk: cygnus: add clock support for Broadcom Cygnus
ARM: dts: enable clock support for Broadcom Cygnus

 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 +++++--
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 brcm,iproc-clocks.txt                   |  178 ++++++++++++
 drivers/clk/Makefile                    |    2 +-
 drivers/clk/bcm/Kconfig                 |    9 +
 drivers/clk/bcm/Makefile                |    2 +
 drivers/clk/bcm/clk-cygnus.c            |  277 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-armpll.c      |  286 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c        |  275 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c         |  238 +++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c         |  483 +++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h             |  155 ++++++++++
 include/dt-bindings/clock/bcm-cygnus.h  |   77 +++++
 13 files changed, 2067 insertions(+), 27 deletions(-)
 create mode 100644 brcm,iproc-clocks.txt
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
Signature · 1 line
1.7.9.5
author maintainer
#50 lore raw

PATCH 1/4 clk: iproc: define Broadcom iProc clock binding

Document the device tree binding for Broadcom iProc architecture based clock controller

---
 brcm,iproc-clocks.txt |  178 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 brcm,iproc-clocks.txt
brcm,iproc-clocks.txt new file +178 -0
new file mode 100644
index 0000000..cc64fd2
@@ -0,0 +1,178 @@
+Broadcom iProc Family Clocks
+
+This binding uses the common clock binding:
+    Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The iProc clock controller manages clocks that are common to the iProc family.
+An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL,
+LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL
+comprises of several leaf clocks
+
+Required properties for PLLs:
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>". For example, GENPLL on
+Cygnus has a compatible string of "brcm,cygnus-genpll"
+
+- #clock-cells:
+    Must be <0>
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL
+
+- clocks:
+    The input parent clock phandle for the PLL. For all iProc PLLs, this is an
+onboard crystal with a fixed rate
+
+Optional Properties for PLLs:
+- clock-frequency:
+    PLL frequency in Hz. If specified, PLL will be configured to run at
+<clock-frequency> instead of the default frequency after chip reset, provided
+that <clock-frequency> and its parameters are defined in the SoC specific
+frequency parameter table
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+Required properties for leaf clocks of a PLL:
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>-clk". For example, leaf
+clocks derived from the GENPLL on Cygnus SoC have a compatible string of
+"brcm,cygnus-genpll-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 leaf clock of a
+given PLL
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL leaf clocks
+
+- clocks:
+    The input parent PLL phandle for the leaf clock
+
+- clock-output-names:
+    An ordered list of strings defining the names of the leaf clocks
+
+Example:
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+Required properties for ASIU clocks:
+
+ASIU clocks are a special case. These clocks are derived directly from the
+reference clock of the onboard crystal
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-asiu-clk". For example, ASIU
+clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 ASIU clocks
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for ASIU clocks
+
+- clocks:
+    The input parent clock phandle for the ASIU clock, i.e., the onboard
+crystal
+
+- clock-output-names:
+    An ordered list of strings defining the names of the ASIU clocks
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
+	};
+
+Cygnus
+------
+PLL and leaf clock compatible strings for Cygnus are:
+    "brcm,cygnus-armpll"
+    "brcm,cygnus-genpll"
+    "brcm,cygnus-lcpll0"
+    "brcm,cygnus-mipipll"
+    "brcm,cygnus-genpll-clk"
+    "brcm,cygnus-lcpll0-clk"
+    "brcm,cygnus-mipipll-clk"
+    "brcm,cygnus-asiu-clk"
+
+The following table defines the set of PLL/clock index and ID for Cygnus.
+These clock IDs are defined in:
+    "include/dt-bindings/clock/bcm-cygnus.h"
+
+    Clock      Source           Index   ID
+    ---        -----            -----   ---------
+    crystal    N/A              N/A     N/A
+
+    armpll     crystal          N/A     N/A
+    genpll     crystal          N/A     N/A
+    lcpll0     crystal          N/A     N/A
+    mipipll    crystal          N/A     N/A
+
+    keypad     crystal (ASIU)   0       BCM_CYGNUS_ASIU_KEYPAD_CLK
+    adc/tsc    crystal (ASIU)   1       BCM_CYGNUS_ASIU_ADC_CLK
+    pwm        crystal (ASIU)   2       BCM_CYGNUS_ASIU_PWM_CLK
+
+    axi21      genpll           0       BCM_CYGNUS_GENPLL_AXI21_CLK
+    250mhz     genpll           1       BCM_CYGNUS_GENPLL_250MHZ_CLK
+    ihost_sys  genpll           2       BCM_CYGNUS_GENPLL_IHOST_SYS_CLK
+    enet_sw    genpll           3       BCM_CYGNUS_GENPLL_ENET_SW_CLK
+    audio_125  genpll           4       BCM_CYGNUS_GENPLL_AUDIO_125_CLK
+    can        genpll           5       BCM_CYGNUS_GENPLL_CAN_CLK
+
+    pcie_phy   lcpll0           0       BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK
+    ddr_phy    lcpll0           1       BCM_CYGNUS_LCPLL0_DDR_PHY_CLK
+    sdio       lcpll0           2       BCM_CYGNUS_LCPLL0_SDIO_CLK
+    usb_phy    lcpll0           3       BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK
+    smart_card lcpll0           4       BCM_CYGNUS_LCPLL0_SMART_CARD_CLK
+    ch5        lcpll0           5       BCM_CYGNUS_LCPLL0_CH5_UNUSED
+
+    ch0_unused mipipll          0       BCM_CYGNUS_MIPIPLL_CH0_UNUSED
+    ch1_lcd    mipipll          1       BCM_CYGNUS_MIPIPLL_CH1_LCD
+    ch2_unused mipipll          2       BCM_CYGNUS_MIPIPLL_CH2_UNUSED
+    ch3_unused mipipll          3       BCM_CYGNUS_MIPIPLL_CH3_UNUSED
+    ch4_unused mipipll          4       BCM_CYGNUS_MIPIPLL_CH4_UNUSED
+    ch5_unused mipipll          5       BCM_CYGNUS_MIPIPLL_CH5_UNUSED
Signature · 1 line
1.7.9.5
author maintainer
#51 lore raw

PATCH 2/4 clk: iproc: add initial common clock support

In reply to Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
 drivers/clk/Makefile               |    2 +-
 drivers/clk/bcm/Kconfig            |    9 +
 drivers/clk/bcm/Makefile           |    1 +
 drivers/clk/bcm/clk-iproc-armpll.c |  286 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c   |  275 ++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c    |  238 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c    |  483 ++++++++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h        |  155 ++++++++++++
 8 files changed, 1448 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
drivers/clk/Makefile +1 -1
index d5fba5b..eff0213 100644
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)		+= clk-vt8500.o
 obj-$(CONFIG_COMMON_CLK_WM831X)		+= clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)		+= bcm/
+obj-$(CONFIG_ARCH_BCM)			+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
 obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
 	  Enable common clock framework support for Broadcom SoCs
 	  using "Kona" style clock control units, including those
 	  in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+	bool "Broadcom iProc clock support"
+	depends on ARCH_BCM_IPROC
+	depends on COMMON_CLK
+	default y
+	help
+	  Enable common clock framework support for Broadcom SoCs
+	  based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +286 -0

286 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0

275 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-clk.c new file +238 -0

238 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-pll.c new file +483 -0

483 lines not on this page · raw message

drivers/clk/bcm/clk-iproc.h new file +155 -0

155 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#52 lore raw

PATCH 3/4 clk: cygnus: add clock support for Broadcom Cygnus

In reply to Ray Jui

The Broadcom Cygnus SoC is architected under the iProc architecture. It has the following PLLs: ARMPLL, GENPLL, LCPLL0, MIPIPLL, all dervied from an onboard crystal. Cygnus also has various ASIU clocks that are derived directly from the onboard crystal.

---
 drivers/clk/bcm/Makefile               |    1 +
 drivers/clk/bcm/clk-cygnus.c           |  277 ++++++++++++++++++++++++++++++++
 include/dt-bindings/clock/bcm-cygnus.h |   77 +++++++++
 3 files changed, 355 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
drivers/clk/bcm/Makefile +1 -0
index 6926636..afcbe55 100644
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
 obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
+obj-$(CONFIG_ARCH_BCM_CYGNUS)	+= clk-cygnus.o
drivers/clk/bcm/clk-cygnus.c new file +277 -0

277 lines not on this page · raw message

include/dt-bindings/clock/bcm-cygnus.h new file +77 -0

77 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#53 lore raw

PATCH 4/4 ARM: dts: enable clock support for Broadcom Cygnus

In reply to Ray Jui

Replace current device tree dummy clocks with real clock support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 ++++++++++++++++++++++++-------
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 2 files changed, 86 insertions(+), 26 deletions(-)
arch/arm/boot/dts/bcm-cygnus-clock.dtsi +85 -25
index 60d8389..f2da5e2 100644
@@ -36,56 +36,116 @@ clocks {
 	ranges;
 
 	osc: oscillator {
+		#clock-cells = <0>;
 		compatible = "fixed-clock";
-		#clock-cells = <1>;
 		clock-frequency = <25000000>;
 	};
 
-	apb_clk: apb_clk {
-		compatible = "fixed-clock";
+	/* Cygnus ARM PLL */
+	armpll: armpll {
 		#clock-cells = <0>;
-		clock-frequency = <1000000000>;
+		compatible = "brcm,cygnus-armpll";
+		clocks = <&osc>;
+		reg = <0x19000000 0x1000>;
 	};
 
-	periph_clk: periph_clk {
-		compatible = "fixed-clock";
+	/* peripheral clock for system timer */
+	arm_periph_clk: arm_periph_clk {
 		#clock-cells = <0>;
-		clock-frequency = <500000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	sdio_clk: lcpll_ch2 {
-		compatible = "fixed-clock";
+	/* APB bus clock */
+	apb_clk: apb_clk {
 		#clock-cells = <0>;
-		clock-frequency = <200000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	axi81_clk: axi81_clk {
-		compatible = "fixed-clock";
+	genpll: genpll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	keypad_clk: keypad_clk {
-		compatible = "fixed-clock";
+	/* various clocks running off the GENPLL */
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+	/* always 1/2 of the axi21 clock */
+	axi41_clk: axi41_clk {
 		#clock-cells = <0>;
-		clock-frequency = <31806>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	adc_clk: adc_clk {
-		compatible = "fixed-clock";
+	/* always 1/4 of the axi21 clock */
+	axi81_clk: axi81_clk {
 		#clock-cells = <0>;
-		clock-frequency = <1562500>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	pwm_clk: pwm_clk {
-		compatible = "fixed-clock";
+	lcpll0: lcpll0 {
 		#clock-cells = <0>;
-		clock-frequency = <1000000>;
+		compatible = "brcm,cygnus-lcpll0";
+		reg = <0x0301d02c 0x1c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	lcd_clk: mipipll_ch1 {
-		compatible = "fixed-clock";
+	/* various clocks running off the LCPLL0 */
+	lcpll0_clks: lcpll0_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-lcpll0-clk";
+		reg = <0x0301d02c 0x1c>;
+		clocks = <&lcpll0>;
+		clock-output-names = "pcie_phy", "ddr_phy", "sdio",
+			"usb_phy", "smart_card", "ch5";
+	};
+
+	mipipll: mipipll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-mipipll";
+		reg = <0x180a9800 0x2c>,
+			<0x0301c020 0x4>,
+			<0x180aa024 0x4>;
+		clock-frequency = <1350000000>;
+		clocks = <&osc>;
+	};
+
+	mipipll_clks: mipipll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-mipipll-clk";
+		reg = <0x180a9800 0x2c>;
+		clocks = <&mipipll>;
+		clock-output-names = "ch0_unused", "ch1_lcd", "ch2_unused",
+			"ch3_unused", "ch4_unused", "ch5_unused";
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
 	};
 };
arch/arm/boot/dts/bcm-cygnus.dtsi +1 -1
index 5126f9e..2b99e9c 100644
@@ -134,7 +134,7 @@
 		compatible = "arm,cortex-a9-global-timer";
 		reg = <0x19020200 0x100>;
 		interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&periph_clk>;
+		clocks = <&arm_periph_clk>;
 	};
 
 };
Signature · 1 line
1.7.9.5
author maintainer
#54 lore raw

cover Add common clock support for Broadcom iProc architecture

In reply to a message not in the archive

This patchset contains the initial common clock support for Broadcom's iProc family of SoCs. The iProc clock architecture comprises of various PLLs, e.g., ARMPLL, GENPLL, LCPLL0, MIPIPLL, and etc. An onboard crystal serves as the basic reference clock for these PLLs. Each PLL may have several leaf clocks. One special group of clocks is the ASIU clocks, which are dervied directly from the crystal reference clock.

This patchset also contains the basic clock support for the Broadcom Cygnus SoC, which implements the iProc clock architecture

Ray Jui (4):
clk: iproc: define Broadcom iProc clock binding
clk: iproc: add initial common clock support
clk: cygnus: add clock support for Broadcom Cygnus
ARM: dts: enable clock support for Broadcom Cygnus

 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 +++++--
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 brcm,iproc-clocks.txt                   |  178 ++++++++++++
 drivers/clk/Makefile                    |    2 +-
 drivers/clk/bcm/Kconfig                 |    9 +
 drivers/clk/bcm/Makefile                |    2 +
 drivers/clk/bcm/clk-cygnus.c            |  277 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-armpll.c      |  286 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c        |  275 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c         |  238 +++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c         |  483 +++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h             |  155 ++++++++++
 include/dt-bindings/clock/bcm-cygnus.h  |   77 +++++
 13 files changed, 2067 insertions(+), 27 deletions(-)
 create mode 100644 brcm,iproc-clocks.txt
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
Signature · 1 line
1.7.9.5
author maintainer
#55 lore raw

PATCH 1/4 clk: iproc: define Broadcom iProc clock binding

Document the device tree binding for Broadcom iProc architecture based clock controller

---
 brcm,iproc-clocks.txt |  178 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 brcm,iproc-clocks.txt
brcm,iproc-clocks.txt new file +178 -0
new file mode 100644
index 0000000..cc64fd2
@@ -0,0 +1,178 @@
+Broadcom iProc Family Clocks
+
+This binding uses the common clock binding:
+    Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The iProc clock controller manages clocks that are common to the iProc family.
+An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL,
+LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL
+comprises of several leaf clocks
+
+Required properties for PLLs:
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>". For example, GENPLL on
+Cygnus has a compatible string of "brcm,cygnus-genpll"
+
+- #clock-cells:
+    Must be <0>
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL
+
+- clocks:
+    The input parent clock phandle for the PLL. For all iProc PLLs, this is an
+onboard crystal with a fixed rate
+
+Optional Properties for PLLs:
+- clock-frequency:
+    PLL frequency in Hz. If specified, PLL will be configured to run at
+<clock-frequency> instead of the default frequency after chip reset, provided
+that <clock-frequency> and its parameters are defined in the SoC specific
+frequency parameter table
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+Required properties for leaf clocks of a PLL:
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>-clk". For example, leaf
+clocks derived from the GENPLL on Cygnus SoC have a compatible string of
+"brcm,cygnus-genpll-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 leaf clock of a
+given PLL
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL leaf clocks
+
+- clocks:
+    The input parent PLL phandle for the leaf clock
+
+- clock-output-names:
+    An ordered list of strings defining the names of the leaf clocks
+
+Example:
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+Required properties for ASIU clocks:
+
+ASIU clocks are a special case. These clocks are derived directly from the
+reference clock of the onboard crystal
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-asiu-clk". For example, ASIU
+clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 ASIU clocks
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for ASIU clocks
+
+- clocks:
+    The input parent clock phandle for the ASIU clock, i.e., the onboard
+crystal
+
+- clock-output-names:
+    An ordered list of strings defining the names of the ASIU clocks
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
+	};
+
+Cygnus
+------
+PLL and leaf clock compatible strings for Cygnus are:
+    "brcm,cygnus-armpll"
+    "brcm,cygnus-genpll"
+    "brcm,cygnus-lcpll0"
+    "brcm,cygnus-mipipll"
+    "brcm,cygnus-genpll-clk"
+    "brcm,cygnus-lcpll0-clk"
+    "brcm,cygnus-mipipll-clk"
+    "brcm,cygnus-asiu-clk"
+
+The following table defines the set of PLL/clock index and ID for Cygnus.
+These clock IDs are defined in:
+    "include/dt-bindings/clock/bcm-cygnus.h"
+
+    Clock      Source           Index   ID
+    ---        -----            -----   ---------
+    crystal    N/A              N/A     N/A
+
+    armpll     crystal          N/A     N/A
+    genpll     crystal          N/A     N/A
+    lcpll0     crystal          N/A     N/A
+    mipipll    crystal          N/A     N/A
+
+    keypad     crystal (ASIU)   0       BCM_CYGNUS_ASIU_KEYPAD_CLK
+    adc/tsc    crystal (ASIU)   1       BCM_CYGNUS_ASIU_ADC_CLK
+    pwm        crystal (ASIU)   2       BCM_CYGNUS_ASIU_PWM_CLK
+
+    axi21      genpll           0       BCM_CYGNUS_GENPLL_AXI21_CLK
+    250mhz     genpll           1       BCM_CYGNUS_GENPLL_250MHZ_CLK
+    ihost_sys  genpll           2       BCM_CYGNUS_GENPLL_IHOST_SYS_CLK
+    enet_sw    genpll           3       BCM_CYGNUS_GENPLL_ENET_SW_CLK
+    audio_125  genpll           4       BCM_CYGNUS_GENPLL_AUDIO_125_CLK
+    can        genpll           5       BCM_CYGNUS_GENPLL_CAN_CLK
+
+    pcie_phy   lcpll0           0       BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK
+    ddr_phy    lcpll0           1       BCM_CYGNUS_LCPLL0_DDR_PHY_CLK
+    sdio       lcpll0           2       BCM_CYGNUS_LCPLL0_SDIO_CLK
+    usb_phy    lcpll0           3       BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK
+    smart_card lcpll0           4       BCM_CYGNUS_LCPLL0_SMART_CARD_CLK
+    ch5        lcpll0           5       BCM_CYGNUS_LCPLL0_CH5_UNUSED
+
+    ch0_unused mipipll          0       BCM_CYGNUS_MIPIPLL_CH0_UNUSED
+    ch1_lcd    mipipll          1       BCM_CYGNUS_MIPIPLL_CH1_LCD
+    ch2_unused mipipll          2       BCM_CYGNUS_MIPIPLL_CH2_UNUSED
+    ch3_unused mipipll          3       BCM_CYGNUS_MIPIPLL_CH3_UNUSED
+    ch4_unused mipipll          4       BCM_CYGNUS_MIPIPLL_CH4_UNUSED
+    ch5_unused mipipll          5       BCM_CYGNUS_MIPIPLL_CH5_UNUSED
Signature · 1 line
1.7.9.5
author maintainer
#56 lore raw

PATCH 2/4 clk: iproc: add initial common clock support

In reply to Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
 drivers/clk/Makefile               |    2 +-
 drivers/clk/bcm/Kconfig            |    9 +
 drivers/clk/bcm/Makefile           |    1 +
 drivers/clk/bcm/clk-iproc-armpll.c |  286 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c   |  275 ++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c    |  238 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c    |  483 ++++++++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h        |  155 ++++++++++++
 8 files changed, 1448 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
drivers/clk/Makefile +1 -1
index d5fba5b..eff0213 100644
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)		+= clk-vt8500.o
 obj-$(CONFIG_COMMON_CLK_WM831X)		+= clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)		+= bcm/
+obj-$(CONFIG_ARCH_BCM)			+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
 obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
 	  Enable common clock framework support for Broadcom SoCs
 	  using "Kona" style clock control units, including those
 	  in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+	bool "Broadcom iProc clock support"
+	depends on ARCH_BCM_IPROC
+	depends on COMMON_CLK
+	default y
+	help
+	  Enable common clock framework support for Broadcom SoCs
+	  based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +286 -0

286 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0

275 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-clk.c new file +238 -0

238 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-pll.c new file +483 -0

483 lines not on this page · raw message

drivers/clk/bcm/clk-iproc.h new file +155 -0

155 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#57 lore raw

Re: [PATCH 2/4] clk: iproc: add initial common clock support

Quoted 34 lines · Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
 drivers/clk/Makefile               |    2 +-
 drivers/clk/bcm/Kconfig            |    9 +
 drivers/clk/bcm/Makefile           |    1 +
 drivers/clk/bcm/clk-iproc-armpll.c |  286 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c   |  275 ++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c    |  238 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c    |  483 ++++++++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h        |  155 ++++++++++++
 8 files changed, 1448 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
drivers/clk/Makefile +0 -0
index d5fba5b..eff0213 100644

drivers/clk/Makefile obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o · line 47

Quoted 8 lines
drivers/clk/Makefile +1 -1
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)             += clk-vt8500.o
 obj-$(CONFIG_COMMON_CLK_WM831X)                += clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_XGENE)         += clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)          += at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)          += bcm/
+obj-$(CONFIG_ARCH_BCM)                 += bcm/
 obj-$(CONFIG_ARCH_BERLIN)              += berlin/
 obj-$(CONFIG_ARCH_HI3xxx)              += hisilicon/
 obj-$(CONFIG_ARCH_HIP04)               += hisilicon/

It may be best to move the above change into its own commit.

Quoted 1,501 lines
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
          Enable common clock framework support for Broadcom SoCs
          using "Kona" style clock control units, including those
          in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+       bool "Broadcom iProc clock support"
+       depends on ARCH_BCM_IPROC
+       depends on COMMON_CLK
+       default y
+       help
+         Enable common clock framework support for Broadcom SoCs
+         based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)      += clk-kona.o
 obj-$(CONFIG_CLK_BCM_KONA)     += clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)     += clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)     += clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +286 -0
new file mode 100644
index 0000000..ec9b130
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+
+#define IPROC_CLK_MAX_FREQ_POLICY                    0x3
+#define IPROC_CLK_POLICY_FREQ_OFFSET                 0x008
+#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT      8
+#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK       0x7
+
+#define IPROC_CLK_PLLARMA_OFFSET                     0xc00
+#define IPROC_CLK_PLLARMA_LOCK_SHIFT                 28
+#define IPROC_CLK_PLLARMA_PDIV_SHIFT                 24
+#define IPROC_CLK_PLLARMA_PDIV_MASK                  0xf
+#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT             8
+#define IPROC_CLK_PLLARMA_NDIV_INT_MASK              0x3ff
+
+#define IPROC_CLK_PLLARMB_OFFSET                     0xc04
+#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK             0xfffff
+
+#define IPROC_CLK_PLLARMC_OFFSET                     0xc08
+#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT            8
+#define IPROC_CLK_PLLARMC_MDIV_MASK                  0xff
+
+#define IPROC_CLK_PLLARMCTL5_OFFSET                  0xc20
+#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK             0xff
+
+#define IPROC_CLK_PLLARM_OFFSET_OFFSET               0xc24
+#define IPROC_CLK_PLLARM_SW_CTL_SHIFT                29
+#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT       20
+#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK        0xff
+#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK       0xfffff
+
+#define IPROC_CLK_ARM_DIV_OFFSET                     0xe00
+#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT  4
+#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK        0xf
+
+#define IPROC_CLK_POLICY_DBG_OFFSET                  0xec0
+#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT          12
+#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK           0x7
+
+enum iproc_arm_pll_fid {
+       ARM_PLL_FID_CRYSTAL_CLK   = 0,
+       ARM_PLL_FID_SYS_CLK       = 2,
+       ARM_PLL_FID_CH0_SLOW_CLK  = 6,
+       ARM_PLL_FID_CH1_FAST_CLK  = 7
+};
+
+struct iproc_arm_pll {
+       struct clk_hw hw;
+       void __iomem *base;
+       struct clk_onecell_data clk_data;
+       unsigned long rate;
+};
+
+#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
+
+static unsigned int __get_fid(struct iproc_arm_pll *pll)
+{
+       u32 val;
+       unsigned int policy, fid, active_fid;
+
+       val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET);
+       if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT))
+               policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK;
+       else
+               policy = 0;
+
+       /* something is seriously wrong */
+       BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY);
+
+       val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET);
+       fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) &
+               IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK;
+
+       val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET);
+       active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK &
+               (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT);
+       if (fid != active_fid) {
+               pr_debug("%s: fid override %u->%u\n", __func__, fid,
+                               active_fid);
+               fid = active_fid;
+       }
+
+       pr_debug("%s: active fid: %u\n", __func__, fid);
+
+       return fid;
+}
+
+/*
+ * Determine the mdiv (post divider) based on the frequency ID being used.
+ * There are 4 sources that can be used to derive the output clock rate:
+ *    - 25 MHz Crystal
+ *    - System clock
+ *    - PLL channel 0 (slow clock)
+ *    - PLL channel 1 (fast clock)
+ */
+static int __get_mdiv(struct iproc_arm_pll *pll)
+{
+       unsigned int fid;
+       int mdiv;
+       u32 val;
+
+       fid = __get_fid(pll);
+
+       switch (fid) {
+       case ARM_PLL_FID_CRYSTAL_CLK:
+       case ARM_PLL_FID_SYS_CLK:
+               mdiv = 1;
+               break;
+
+       case ARM_PLL_FID_CH0_SLOW_CLK:
+               val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
+               mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK;
+               if (mdiv == 0)
+                       mdiv = 256;
+               break;
+
+       case ARM_PLL_FID_CH1_FAST_CLK:
+               val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET);
+               mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK;
+               if (mdiv == 0)
+                       mdiv = 256;
+               break;
+
+       default:
+               mdiv = -EFAULT;
+       }
+
+       return mdiv;
+}
+
+static unsigned int __get_ndiv(struct iproc_arm_pll *pll)
+{
+       u32 val;
+       unsigned int ndiv_int, ndiv_frac, ndiv;
+
+       val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET);
+       if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) {
+               /*
+                * offset mode is active. Read the ndiv from the PLLARM OFFSET
+                * register
+                */
+               ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) &
+                       IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK;
+               if (ndiv_int == 0)
+                       ndiv_int = 256;
+
+               ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK;
+       } else {
+               /* offset mode not active */
+               val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
+               ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) &
+                       IPROC_CLK_PLLARMA_NDIV_INT_MASK;
+               if (ndiv_int == 0)
+                       ndiv_int = 1024;
+
+               val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET);
+               ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK;
+       }
+
+       ndiv = (ndiv_int << 20) | ndiv_frac;
+
+       return ndiv;
+}
+
+/*
+ * The output frequency of the ARM PLL is calculated based on the ARM PLL
+ * divider values:
+ *   pdiv = ARM PLL pre-divider
+ *   ndiv = ARM PLL multiplier
+ *   mdiv = ARM PLL post divider
+ *
+ * The frequency is calculated by:
+ *   ((ndiv * parent clock rate) / pdiv) / mdiv
+ */
+static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_arm_pll *pll = to_iproc_arm_pll(hw);
+       u32 val;
+       int mdiv;
+       u64 ndiv;
+       unsigned int pdiv;
+
+       /* in bypass mode, use parent rate */
+       val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
+       if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) {
+               pll->rate = parent_rate;
+               return pll->rate;
+       }
+
+       /* PLL needs to be locked */
+       val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
+       if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) {
+               pll->rate = 0;
+               return 0;
+       }
+
+       pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) &
+               IPROC_CLK_PLLARMA_PDIV_MASK;
+       if (pdiv == 0)
+               pdiv = 16;
+
+       ndiv = __get_ndiv(pll);
+       mdiv = __get_mdiv(pll);
+       if (mdiv <= 0) {
+               pll->rate = 0;
+               return 0;
+       }
+       pll->rate = (ndiv * parent_rate) >> 20;
+       pll->rate = (pll->rate / pdiv) / mdiv;
+
+       pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__,
+                       pll->rate, parent_rate);
+       pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__,
+                       (unsigned int)(ndiv >> 20), pdiv, mdiv);
+
+       return pll->rate;
+}
+
+static const struct clk_ops iproc_arm_pll_ops = {
+       .recalc_rate = iproc_arm_pll_recalc_rate,
+};
+
+void __init iproc_armpll_setup(struct device_node *node)
+{
+       int ret;
+       struct clk *clk;
+       struct iproc_arm_pll *pll;
+       struct clk_init_data init;
+       const char *parent_name;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->base = of_iomap(node, 0);
+       if (WARN_ON(!pll->base))
+               goto err_free_pll;
+
+       init.name = node->name;
+       init.ops = &iproc_arm_pll_ops;
+       init.flags = 0;
+       parent_name = of_clk_get_parent_name(node, 0);
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+       if (WARN_ON(IS_ERR(clk)))
+               goto err_iounmap;
+
+       pll->clk_data.clk_num = 1;
+       pll->clk_data.clks = &clk;
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_unregister;
+
+       return;
+
+err_clk_unregister:
+       clk_unregister(clk);
+err_iounmap:
+       iounmap(pll->base);
+err_free_pll:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0
new file mode 100644
index 0000000..ab86b8c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+struct iproc_asiu;
+
+struct iproc_asiu_clk {
+       struct clk_hw hw;
+       const char *name;
+       struct iproc_asiu *asiu;
+       unsigned long rate;
+       struct iproc_asiu_div div;
+       struct iproc_asiu_gate gate;
+};
+
+struct iproc_asiu {
+       void __iomem *div_base;
+       void __iomem *gate_base;
+
+       struct clk_onecell_data clk_data;
+       struct iproc_asiu_clk *clks;
+};
+
+#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw)
+
+static int iproc_asiu_clk_enable(struct clk_hw *hw)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+
+       /* some clocks at the ASIU level are always enabled */
+       if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
+               return 0;
+
+       val = readl(asiu->gate_base + clk->gate.offset);
+       val |= (1 << clk->gate.en_shift);
+       writel(val, asiu->gate_base + clk->gate.offset);
+
+       return 0;
+}
+
+static void iproc_asiu_clk_disable(struct clk_hw *hw)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+
+       /* some clocks at the ASIU level are always enabled */
+       if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
+               return;
+
+       val = readl(asiu->gate_base + clk->gate.offset);
+       val &= ~(1 << clk->gate.en_shift);
+       writel(val, asiu->gate_base + clk->gate.offset);
+}
+
+static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+       unsigned int div_h, div_l;
+
+       if (parent_rate == 0) {
+               clk->rate = 0;
+               return 0;
+       }
+
+       /* if clock divisor is not enabled, simply return parent rate */
+       val = readl(asiu->div_base + clk->div.offset);
+       if ((val & (1 << clk->div.en_shift)) == 0) {
+               clk->rate = parent_rate;
+               return parent_rate;
+       }
+
+       /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */
+       div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width);
+       div_h++;
+       div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width);
+       div_l++;
+
+       clk->rate = parent_rate / (div_h + div_l);
+       pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n",
+               __func__, clk->rate, parent_rate, div_h, div_l);
+
+       return clk->rate;
+}
+
+static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *parent_rate)
+{
+       unsigned int div;
+
+       if (rate == 0 || *parent_rate == 0)
+               return -EINVAL;
+
+       if (rate == *parent_rate)
+               return *parent_rate;
+
+       div = DIV_ROUND_UP(*parent_rate, rate);
+       if (div < 2)
+               return *parent_rate;
+
+       return *parent_rate / div;
+}
+
+static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       unsigned int div, div_h, div_l;
+       u32 val;
+
+       if (rate == 0 || parent_rate == 0)
+               return -EINVAL;
+
+       /* simply disable the divisor if one wants the same rate as parent */
+       if (rate == parent_rate) {
+               val = readl(asiu->div_base + clk->div.offset);
+               val &= ~(1 << clk->div.en_shift);
+               writel(val, asiu->div_base + clk->div.offset);
+               return 0;
+       }
+
+       div = DIV_ROUND_UP(parent_rate, rate);
+       if (div < 2)
+               return -EINVAL;
+
+       div_h = div_l = div >> 1;
+       div_h--;
+       div_l--;
+
+       val = readl(asiu->div_base + clk->div.offset);
+       val |= 1 << clk->div.en_shift;
+       if (div_h) {
+               val &= ~(bit_mask(clk->div.high_width)
+                               << clk->div.high_shift);
+               val |= div_h << clk->div.high_shift;
+       } else {
+               val &= ~(bit_mask(clk->div.high_width)
+                               << clk->div.high_shift);
+       }
+       if (div_l) {
+               val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
+               val |= div_l << clk->div.low_shift;
+       } else {
+               val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
+       }
+       writel(val, asiu->div_base + clk->div.offset);
+
+       return 0;
+}
+
+static const struct clk_ops iproc_asiu_ops = {
+       .enable = iproc_asiu_clk_enable,
+       .disable = iproc_asiu_clk_disable,
+       .recalc_rate = iproc_asiu_clk_recalc_rate,
+       .round_rate = iproc_asiu_clk_round_rate,
+       .set_rate = iproc_asiu_clk_set_rate,
+};
+
+void __init iproc_asiu_setup(struct device_node *node,
+               const struct iproc_asiu_div *div,
+               const struct iproc_asiu_gate *gate, unsigned int num_clks)
+{
+       int i, ret;
+       struct iproc_asiu *asiu;
+
+       if (WARN_ON(!gate || !div))
+               return;
+
+       asiu = kzalloc(sizeof(*asiu), GFP_KERNEL);
+       if (WARN_ON(!asiu))
+               return;
+
+       asiu->clk_data.clk_num = num_clks;
+       asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks),
+                       GFP_KERNEL);
+       if (WARN_ON(!asiu->clk_data.clks))
+               goto err_clks;
+
+       asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL);
+       if (WARN_ON(!asiu->clks))
+               goto err_asiu_clks;
+
+       asiu->div_base = of_iomap(node, 0);
+       if (WARN_ON(!asiu->div_base))
+               goto err_iomap_div;
+
+       asiu->gate_base = of_iomap(node, 1);
+       if (WARN_ON(!asiu->gate_base))
+               goto err_iomap_gate;
+
+       for (i = 0; i < num_clks; i++) {
+               struct clk_init_data init;
+               struct clk *clk;
+               const char *parent_name;
+               struct iproc_asiu_clk *asiu_clk;
+               const char *clk_name;
+
+               clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
+               if (WARN_ON(!clk_name))
+                       goto err_clk_register;
+
+               ret = of_property_read_string_index(node, "clock-output-names",
+                               i, &clk_name);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+
+               asiu_clk = &asiu->clks[i];
+               asiu_clk->name = clk_name;
+               asiu_clk->asiu = asiu;
+               asiu_clk->div = div[i];
+               asiu_clk->gate = gate[i];
+               init.name = clk_name;
+               init.ops = &iproc_asiu_ops;
+               init.flags = 0;
+               parent_name = of_clk_get_parent_name(node, 0);
+               init.parent_names = (parent_name ? &parent_name : NULL);
+               init.num_parents = (parent_name ? 1 : 0);
+               asiu_clk->hw.init = &init;
+
+               clk = clk_register(NULL, &asiu_clk->hw);
+               if (WARN_ON(IS_ERR(clk)))
+                       goto err_clk_register;
+               asiu->clk_data.clks[i] = clk;
+       }
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get,
+                       &asiu->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_register;
+
+       return;
+
+err_clk_register:
+       for (i = 0; i < num_clks; i++)
+               kfree(asiu->clks[i].name);
+       iounmap(asiu->gate_base);
+
+err_iomap_gate:
+       iounmap(asiu->div_base);
+
+err_iomap_div:
+       kfree(asiu->clks);
+
+err_asiu_clks:
+       kfree(asiu->clk_data.clks);
+
+err_clks:
+       kfree(asiu);
+}
drivers/clk/bcm/clk-iproc-clk.c new file +238 -0
new file mode 100644
index 0000000..be3c42c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+struct iproc_pll;
+
+struct iproc_clk {
+       struct clk_hw hw;
+       const char *name;
+       struct iproc_pll *pll;
+       unsigned long rate;
+       const struct iproc_clk_ctrl *ctrl;
+};
+
+struct iproc_pll {
+       void __iomem *base;
+       struct clk_onecell_data clk_data;
+       struct iproc_clk *clks;
+};
+
+#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw)
+
+static int iproc_clk_enable(struct clk_hw *hw)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+
+       /* channel enable is active low */
+       val = readl(pll->base + ctrl->enable.offset);
+       val &= ~(1 << ctrl->enable.enable_shift);
+       writel(val, pll->base + ctrl->enable.offset);
+
+       /* also make sure channel is not held */
+       val = readl(pll->base + ctrl->enable.offset);
+       val &= ~(1 << ctrl->enable.hold_shift);
+       writel(val, pll->base + ctrl->enable.offset);
+
+       return 0;
+}
+
+static void iproc_clk_disable(struct clk_hw *hw)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+
+       if (ctrl->flags & IPROC_CLK_AON)
+               return;
+
+       val = readl(pll->base + ctrl->enable.offset);
+       val |= 1 << ctrl->enable.enable_shift;
+       writel(val, pll->base + ctrl->enable.offset);
+}
+
+static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+       unsigned int mdiv;
+
+       if (parent_rate == 0)
+               return 0;
+
+       val = readl(pll->base + ctrl->mdiv.offset);
+       mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width);
+       if (mdiv == 0)
+               mdiv = 256;
+
+       clk->rate = parent_rate / mdiv;
+
+       return clk->rate;
+}
+
+static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *parent_rate)
+{
+       unsigned int div;
+
+       if (rate == 0 || *parent_rate == 0)
+               return -EINVAL;
+
+       if (rate == *parent_rate)
+               return *parent_rate;
+
+       div = DIV_ROUND_UP(*parent_rate, rate);
+       if (div < 2)
+               return *parent_rate;
+
+       if (div > 256)
+               div = 256;
+
+       return *parent_rate / div;
+}
+
+static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+       unsigned int div;
+
+       if (rate == 0 || parent_rate == 0)
+               return -EINVAL;
+
+       div = DIV_ROUND_UP(parent_rate, rate);
+       if (div > 256)
+               return -EINVAL;
+
+       val = readl(pll->base + ctrl->mdiv.offset);
+       if (div == 256) {
+               val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
+       } else {
+               val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
+               val |= div << ctrl->mdiv.shift;
+       }
+       writel(val, pll->base + ctrl->mdiv.offset);
+       clk->rate = parent_rate / div;
+
+       return 0;
+}
+
+static const struct clk_ops iproc_clk_ops = {
+       .enable = iproc_clk_enable,
+       .disable = iproc_clk_disable,
+       .recalc_rate = iproc_clk_recalc_rate,
+       .round_rate = iproc_clk_round_rate,
+       .set_rate = iproc_clk_set_rate,
+};
+
+void __init iproc_clk_setup(struct device_node *node,
+               const struct iproc_clk_ctrl *ctrl, unsigned int num_clks)
+{
+       int i, ret;
+       struct iproc_pll *pll;
+
+       if (WARN_ON(!ctrl))
+               return;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->clk_data.clk_num = num_clks;
+       pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks),
+                       GFP_KERNEL);
+       if (WARN_ON(!pll->clk_data.clks))
+               goto err_clks;
+
+       pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL);
+       if (WARN_ON(!pll->clks))
+               goto err_pll_clks;
+
+       pll->base = of_iomap(node, 0);
+       if (WARN_ON(!pll->base))
+               goto err_iomap;
+
+       for (i = 0; i < num_clks; i++) {
+               struct clk_init_data init;
+               struct clk *clk;
+               const char *parent_name;
+               struct iproc_clk *iclk;
+               const char *clk_name;
+
+               clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
+               if (WARN_ON(!clk_name))
+                       goto err_clk_register;
+
+               ret = of_property_read_string_index(node, "clock-output-names",
+                               i, &clk_name);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+
+               iclk = &pll->clks[i];
+               iclk->name = clk_name;
+               iclk->pll = pll;
+               iclk->ctrl = &ctrl[i];
+               init.name = clk_name;
+               init.ops = &iproc_clk_ops;
+               init.flags = 0;
+               parent_name = of_clk_get_parent_name(node, 0);
+               init.parent_names = (parent_name ? &parent_name : NULL);
+               init.num_parents = (parent_name ? 1 : 0);
+               iclk->hw.init = &init;
+
+               clk = clk_register(NULL, &iclk->hw);
+               if (WARN_ON(IS_ERR(clk)))
+                       goto err_clk_register;
+               pll->clk_data.clks[i] = clk;
+       }
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_register;
+
+       return;
+
+err_clk_register:
+       for (i = 0; i < num_clks; i++)
+               kfree(pll->clks[i].name);
+       iounmap(pll->base);
+
+err_iomap:
+       kfree(pll->clks);
+
+err_pll_clks:
+       kfree(pll->clk_data.clks);
+
+err_clks:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc-pll.c new file +483 -0
new file mode 100644
index 0000000..cd3bd38
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+#define PLL_VCO_HIGH_SHIFT 19
+#define PLL_VCO_LOW_SHIFT  30
+
+/* number of delay loops waiting for PLL to lock */
+#define LOCK_DELAY 100
+
+/* number of VCO frequency bands */
+#define NUM_FREQ_BANDS 8
+
+#define NUM_KP_BANDS 3
+enum kp_band {
+       KP_BAND_MID = 0,
+       KP_BAND_HIGH,
+       KP_BAND_HIGH_HIGH
+};
+
+static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = {
+       { 5, 6, 6, 7, 7, 8, 9, 10 },
+       { 4, 4, 5, 5, 6, 7, 8, 9  },
+       { 4, 5, 5, 6, 7, 8, 9, 10 },
+};
+
+static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = {
+       { 10000000,  12500000  },
+       { 12500000,  15000000  },
+       { 15000000,  20000000  },
+       { 20000000,  25000000  },
+       { 25000000,  50000000  },
+       { 50000000,  75000000  },
+       { 75000000,  100000000 },
+       { 100000000, 125000000 },
+};
+
+enum vco_freq_range {
+       VCO_LOW       = 700000000U,
+       VCO_MID       = 1200000000U,
+       VCO_HIGH      = 2200000000U,
+       VCO_HIGH_HIGH = 3100000000U,
+       VCO_MAX       = 4000000000U,
+};
+
+struct iproc_pll {
+       struct clk_hw hw;
+       void __iomem *pll_base;
+       void __iomem *pwr_base;
+       void __iomem *asiu_base;
+       struct clk_onecell_data clk_data;
+       const char *name;
+       const struct iproc_pll_ctrl *ctrl;
+       const struct iproc_pll_vco_freq_param *vco_param;
+       unsigned int num_vco_entries;
+       unsigned long rate;
+};
+
+#define to_iproc_pll(hw) container_of(hw, struct iproc_pll, hw)
+
+/*
+ * Get the clock rate based on name
+ */
+static unsigned long __get_rate(const char *clk_name)
+{
+       struct clk *clk;
+
+       clk = __clk_lookup(clk_name);
+       if (!clk) {
+               pr_err("%s: unable to find clock by name: %s\n", __func__,
+                               clk_name);
+               return 0;
+       }
+
+       return clk_get_rate(clk);
+}
+
+/*
+ * Based on the target frequency, find a match from the VCO frequency parameter
+ * table and return its index
+ */
+static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate)
+{
+       int i;
+
+       for (i = 0; i < pll->num_vco_entries; i++)
+               if (target_rate == pll->vco_param[i].rate)
+                       break;
+
+       if (i >= pll->num_vco_entries)
+               return -EINVAL;
+
+       return i;
+}
+
+static int get_kp(unsigned long ref_freq, enum kp_band kp_index)
+{
+       int i;
+
+       if (ref_freq < ref_freq_table[0][0])
+               return -EINVAL;
+
+       for (i = 0; i < NUM_FREQ_BANDS; i++) {
+               if (ref_freq >= ref_freq_table[i][0] &&
+                       ref_freq < ref_freq_table[i][1])
+                       return kp_table[kp_index][i];
+       }
+       return -EINVAL;
+}
+
+static int pll_wait_for_lock(struct iproc_pll *pll)
+{
+       int i;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+
+       for (i = 0; i < LOCK_DELAY; i++) {
+               u32 val = readl(pll->pll_base + ctrl->status.offset);
+
+               if (val & (1 << ctrl->status.shift))
+                       return 0;
+               udelay(10);
+       }
+
+       return -EIO;
+}
+
+static void __pll_disable(struct iproc_pll *pll)
+{
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               val = readl(pll->asiu_base + ctrl->asiu.offset);
+               val &= ~(1 << ctrl->asiu.en_shift);
+               writel(val, pll->asiu_base + ctrl->asiu.offset);
+       }
+
+       /* latch input value so core power can be shut down */
+       val = readl(pll->pwr_base + ctrl->aon.offset);
+       val |= (1 << ctrl->aon.iso_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+
+       /* power down the core */
+       val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+}
+
+static int __pll_enable(struct iproc_pll *pll)
+{
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+
+       /* power up the PLL and make sure it's not latched */
+       val = readl(pll->pwr_base + ctrl->aon.offset);
+       val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
+       val &= ~(1 << ctrl->aon.iso_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+
+       /* certain PLLs also need to be ungated from the ASIU top level */
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               val = readl(pll->asiu_base + ctrl->asiu.offset);
+               val |= (1 << ctrl->asiu.en_shift);
+               writel(val, pll->asiu_base + ctrl->asiu.offset);
+       }
+
+       return 0;
+}
+
+static void __pll_put_in_reset(struct iproc_pll *pll)
+{
+       u32 val;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
+
+       val = readl(pll->pll_base + reset->offset);
+       val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift);
+       writel(val, pll->pll_base + reset->offset);
+}
+
+static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp,
+               unsigned int ka, unsigned int ki)
+{
+       u32 val;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
+
+       val = readl(pll->pll_base + reset->offset);
+       val &= ~(bit_mask(reset->ki_width) << reset->ki_shift |
+               bit_mask(reset->kp_width) << reset->kp_shift |
+               bit_mask(reset->ka_width) << reset->ka_shift);
+       val |=  ki << reset->ki_shift | kp << reset->kp_shift |
+               ka << reset->ka_shift;
+       val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift;
+       writel(val, pll->pll_base + reset->offset);
+}
+
+static int pll_set_rate(struct iproc_pll *pll, unsigned int rate_index,
+               unsigned long parent_rate)
+{
+       const struct iproc_pll_vco_freq_param *vco =
+                               &pll->vco_param[rate_index];
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       int ka = 0, ki, kp, ret;
+       unsigned long rate = vco->rate;
+       u32 val;
+       enum kp_band kp_index;
+       unsigned long ref_freq;
+
+       /*
+        * reference frequency = parent frequency / PDIV
+        * If PDIV = 0, then it becomes a multiplier (x2)
+        */
+       if (vco->pdiv == 0)
+               ref_freq = parent_rate * 2;
+       else
+               ref_freq = parent_rate / vco->pdiv;
+
+       /* determine Ki and Kp index based on target VCO frequency */
+       if (rate >= VCO_LOW && rate < VCO_HIGH) {
+               ki = 4;
+               kp_index = KP_BAND_MID;
+       } else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) {
+               ki = 3;
+               kp_index = KP_BAND_HIGH;
+       } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
+               ki = 3;
+               kp_index = KP_BAND_HIGH_HIGH;
+       } else {
+               pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
+                               pll->name, rate);
+               return -EINVAL;
+       }
+
+       kp = get_kp(ref_freq, kp_index);
+       if (kp < 0) {
+               pr_err("%s: pll: %s has invalid kp\n", __func__, pll->name);
+               return kp;
+       }
+
+       ret = __pll_enable(pll);
+       if (ret) {
+               pr_err("%s: pll: %s fails to enable\n", __func__, pll->name);
+               return ret;
+       }
+
+       /* put PLL in reset */
+       __pll_put_in_reset(pll);
+
+       writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset);
+       val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset);
+
+       if (rate >= VCO_LOW && rate < VCO_MID)
+               val |= (1 << PLL_VCO_LOW_SHIFT);
+
+       if (rate < VCO_HIGH)
+               val &= ~(1 << PLL_VCO_HIGH_SHIFT);
+       else
+               val |= (1 << PLL_VCO_HIGH_SHIFT);
+
+       writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset);
+
+       /* program integer part of NDIV */
+       val = readl(pll->pll_base + ctrl->ndiv_int.offset);
+       val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
+       val |= vco->ndiv_int << ctrl->ndiv_int.shift;
+       writel(val, pll->pll_base + ctrl->ndiv_int.offset);
+
+       /* program fractional part of NDIV */
+       if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
+               val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
+               val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
+                               ctrl->ndiv_frac.shift);
+               val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
+               writel(val, pll->pll_base + ctrl->ndiv_frac.offset);
+       }
+
+       /* program PDIV */
+       val = readl(pll->pll_base + ctrl->pdiv.offset);
+       val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
+       val |= vco->pdiv << ctrl->pdiv.shift;
+       writel(val, pll->pll_base + ctrl->pdiv.offset);
+
+       __pll_bring_out_reset(pll, kp, ka, ki);
+
+       ret = pll_wait_for_lock(pll);
+       if (ret < 0) {
+               pr_err("%s: pll: %s failed to lock\n", __func__, pll->name);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int iproc_pll_enable(struct clk_hw *hw)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+
+       return __pll_enable(pll);
+}
+
+static void iproc_pll_disable(struct clk_hw *hw)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+
+       if (ctrl->flags & IPROC_CLK_AON)
+               return;
+
+       __pll_disable(pll);
+}
+
+static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw,
+       unsigned long parent_rate)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+       u64 ndiv;
+       unsigned int ndiv_int, ndiv_frac, pdiv;
+
+       if (parent_rate == 0)
+               return 0;
+
+       /* PLL needs to be locked */
+       val = readl(pll->pll_base + ctrl->status.offset);
+       if ((val & (1 << ctrl->status.shift)) == 0) {
+               pll->rate = 0;
+               return 0;
+       }
+
+       /*
+        * PLL output frequency =
+        *
+        * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv)
+        */
+       val = readl(pll->pll_base + ctrl->ndiv_int.offset);
+       ndiv_int = (val >> ctrl->ndiv_int.shift) &
+               bit_mask(ctrl->ndiv_int.width);
+       ndiv = ndiv_int << ctrl->ndiv_int.shift;
+
+       if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
+               val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
+               ndiv_frac = (val >> ctrl->ndiv_frac.shift) &
+                       bit_mask(ctrl->ndiv_frac.width);
+
+               if (ndiv_frac != 0)
+                       ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac;
+       }
+
+       val = readl(pll->pll_base + ctrl->pdiv.offset);
+       pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
+
+       pll->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift;
+
+       if (pdiv == 0)
+               pll->rate *= 2;
+       else
+               pll->rate /= pdiv;
+
+       return pll->rate;
+}
+
+static const struct clk_ops iproc_pll_ops = {
+       .enable = iproc_pll_enable,
+       .disable = iproc_pll_disable,
+       .recalc_rate = iproc_pll_recalc_rate,
+};
+
+void __init iproc_pll_setup(struct device_node *node,
+               const struct iproc_pll_ctrl *ctrl,
+               const struct iproc_pll_vco_freq_param *vco_param,
+               unsigned int num_vco_entries)
+{
+       int ret;
+       struct clk *clk;
+       struct iproc_pll *pll;
+       struct clk_init_data init;
+       const char *parent_name;
+       unsigned int rate;
+
+       if (WARN_ON(!ctrl))
+               return;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->pll_base = of_iomap(node, 0);
+       if (WARN_ON(!pll->pll_base))
+               goto err_pll_iomap;
+
+       pll->pwr_base = of_iomap(node, 1);
+       if (WARN_ON(!pll->pwr_base))
+               goto err_pwr_iomap;
+
+       /* some PLLs require gating control at the top ASIU level */
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               pll->asiu_base = of_iomap(node, 2);
+               if (WARN_ON(!pll->asiu_base))
+                       goto err_asiu_iomap;
+       }
+
+       pll->ctrl = ctrl;
+       pll->name = node->name;
+       init.name = node->name;
+       init.ops = &iproc_pll_ops;
+       init.flags = 0;
+       parent_name = of_clk_get_parent_name(node, 0);
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+       pll->hw.init = &init;
+
+       /* configure the PLL to the desired VCO frequency if specified */
+       ret = of_property_read_u32(node, "clock-frequency", &rate);
+       if (!ret) {
+               unsigned long parent_rate;
+               int rate_index;
+
+               if (WARN_ON(!vco_param))
+                       goto err_clk_register;
+
+               pll->num_vco_entries = num_vco_entries;
+               pll->vco_param = vco_param;
+
+               parent_rate = __get_rate(parent_name);
+               if (WARN_ON(!parent_rate))
+                       goto err_clk_register;
+
+               rate_index = pll_get_rate_index(pll, rate);
+               if (WARN_ON(rate_index < 0))
+                       goto err_clk_register;
+
+               ret = pll_set_rate(pll, rate_index, parent_rate);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+       }
+
+       clk = clk_register(NULL, &pll->hw);
+       if (WARN_ON(IS_ERR(clk)))
+               goto err_clk_register;
+
+       pll->clk_data.clk_num = 1;
+       pll->clk_data.clks = &clk;
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get,
+                       &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_add;
+
+       return;
+
+err_clk_add:
+       clk_unregister(clk);
+err_clk_register:
+       if (pll->asiu_base)
+               iounmap(pll->asiu_base);
+err_asiu_iomap:
+       iounmap(pll->pwr_base);
+err_pwr_iomap:
+       iounmap(pll->pll_base);
+err_pll_iomap:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc.h new file +155 -1
new file mode 100644
index 0000000..4aa0479
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CLK_IPROC_H
+#define _CLK_IPROC_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+
+#define IPROC_CLK_NAME_LEN 25
+#define IPROC_CLK_INVALID_OFFSET 0xffffffff
+#define bit_mask(width) ((1 << (width)) - 1)
+
+/* clock should not be disabled at runtime */
+#define IPROC_CLK_AON BIT(0)
+
+/* PLL requires gating through ASIU */
+#define IPROC_CLK_PLL_ASIU BIT(1)
+
+/* PLL has fractional part of the NDIV */
+#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2)
+
+/*
+ * Parameters for VCO frequency configuration
+ *
+ * VCO frequency =
+ * ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy  / pdiv)
+ */
+struct iproc_pll_vco_freq_param {
+       unsigned long rate;
+       unsigned int ndiv_int;
+       unsigned int ndiv_frac;
+       unsigned int pdiv;
+};
+
+struct iproc_clk_reg_op {
+       unsigned int offset;
+       unsigned int shift;
+       unsigned int width;
+};
+
+/*
+ * Clock gating control at the top ASIU level
+ */
+struct iproc_asiu_gate {
+       unsigned int offset;
+       unsigned int en_shift;
+};
+
+/*
+ * Control of powering on/off of a PLL
+ *
+ * Before powering off a PLL, input isolation (ISO) needs to be enabled
+ */
+struct iproc_pll_aon_pwr_ctrl {
+       unsigned int offset;
+       unsigned int pwr_width;
+       unsigned int pwr_shift;
+       unsigned int iso_shift;
+};
+
+/*
+ * Control of the PLL reset, with Ki, Kp, and Ka parameters
+ */
+struct iproc_pll_reset_ctrl {
+       unsigned int offset;
+       unsigned int reset_shift;
+       unsigned int p_reset_shift;
+       unsigned int ki_shift;
+       unsigned int ki_width;
+       unsigned int kp_shift;
+       unsigned int kp_width;
+       unsigned int ka_shift;
+       unsigned int ka_width;
+};
+
+struct iproc_pll_vco_ctrl {
+       unsigned int u_offset;
+       unsigned int l_offset;
+};
+
+/*
+ * Main PLL control parameters
+ */
+struct iproc_pll_ctrl {
+       unsigned long flags;
+       struct iproc_pll_aon_pwr_ctrl aon;
+       struct iproc_asiu_gate asiu;
+       struct iproc_pll_reset_ctrl reset;
+       struct iproc_clk_reg_op ndiv_int;
+       struct iproc_clk_reg_op ndiv_frac;
+       struct iproc_clk_reg_op pdiv;
+       struct iproc_pll_vco_ctrl vco_ctrl;
+       struct iproc_clk_reg_op status;
+};
+
+/*
+ * Controls enabling/disabling a PLL derived clock
+ */
+struct iproc_clk_enable_ctrl {
+       unsigned int offset;
+       unsigned int enable_shift;
+       unsigned int hold_shift;
+       unsigned int bypass_shift;
+};
+
+/*
+ * Main clock control parameters for clocks derived from the PLLs
+ */
+struct iproc_clk_ctrl {
+       unsigned int channel;
+       unsigned long flags;
+       struct iproc_clk_enable_ctrl enable;
+       struct iproc_clk_reg_op mdiv;
+};
+
+/*
+ * Divisor of the ASIU clocks
+ */
+struct iproc_asiu_div {
+       unsigned int offset;
+       unsigned int en_shift;
+       unsigned int high_shift;
+       unsigned int high_width;
+       unsigned int low_shift;
+       unsigned int low_width;
+};
+
+extern void __init iproc_armpll_setup(struct device_node *node);
+extern void __init iproc_pll_setup(struct device_node *node,
+               const struct iproc_pll_ctrl *ctrl,
+               const struct iproc_pll_vco_freq_param *vco_param,
+               unsigned int num_freqs);
+extern void __init iproc_clk_setup(struct device_node *node,
+               const struct iproc_clk_ctrl *ctrl, unsigned int num_clks);
+extern void __init iproc_asiu_setup(struct device_node *node,
+               const struct iproc_asiu_div *div,
+               const struct iproc_asiu_gate *gate, unsigned int num_clks);
+
+#endif /* _CLK_IPROC_H */
--

1.7.9.5

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

author maintainer
#58 lore raw

Re: [PATCH 2/4] clk: iproc: add initial common clock support

Quoted 30 lines · Tim Kryger
Quoted 29 lines · Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
  drivers/clk/Makefile               |    2 +-
  drivers/clk/bcm/Kconfig            |    9 +
  drivers/clk/bcm/Makefile           |    1 +
  drivers/clk/bcm/clk-iproc-armpll.c |  286 +++++++++++++++++++++
  drivers/clk/bcm/clk-iproc-asiu.c   |  275 ++++++++++++++++++++
  drivers/clk/bcm/clk-iproc-clk.c    |  238 ++++++++++++++++++
  drivers/clk/bcm/clk-iproc-pll.c    |  483 ++++++++++++++++++++++++++++++++++++
  drivers/clk/bcm/clk-iproc.h        |  155 ++++++++++++
  8 files changed, 1448 insertions(+), 1 deletion(-)

create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c create mode 100644 drivers/clk/bcm/clk-iproc-clk.c create mode 100644 drivers/clk/bcm/clk-iproc-pll.c create mode 100644 drivers/clk/bcm/clk-iproc.h

Quoted 16 lines

drivers/clk/Makefile obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o · line 47

Quoted 13 lines
drivers/clk/Makefile +1 -1
index d5fba5b..eff0213 100644
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)             += clk-vt8500.o
  obj-$(CONFIG_COMMON_CLK_WM831X)                += clk-wm831x.o
  obj-$(CONFIG_COMMON_CLK_XGENE)         += clk-xgene.o
  obj-$(CONFIG_COMMON_CLK_AT91)          += at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)          += bcm/
+obj-$(CONFIG_ARCH_BCM)                 += bcm/
  obj-$(CONFIG_ARCH_BERLIN)              += berlin/
  obj-$(CONFIG_ARCH_HI3xxx)              += hisilicon/
  obj-$(CONFIG_ARCH_HIP04)               += hisilicon/

It may be best to move the above change into its own commit.

Okay. Will make this change along with other changes if required. Still waiting for more code review comments at this point.

Quoted 1,501 lines
Quoted 1,501 lines
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
           Enable common clock framework support for Broadcom SoCs
           using "Kona" style clock control units, including those
           in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+       bool "Broadcom iProc clock support"
+       depends on ARCH_BCM_IPROC
+       depends on COMMON_CLK
+       default y
+       help
+         Enable common clock framework support for Broadcom SoCs
+         based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)      += clk-kona.o
  obj-$(CONFIG_CLK_BCM_KONA)     += clk-kona-setup.o
  obj-$(CONFIG_CLK_BCM_KONA)     += clk-bcm281xx.o
  obj-$(CONFIG_CLK_BCM_KONA)     += clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +286 -0
new file mode 100644
index 0000000..ec9b130
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+
+#define IPROC_CLK_MAX_FREQ_POLICY                    0x3
+#define IPROC_CLK_POLICY_FREQ_OFFSET                 0x008
+#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT      8
+#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK       0x7
+
+#define IPROC_CLK_PLLARMA_OFFSET                     0xc00
+#define IPROC_CLK_PLLARMA_LOCK_SHIFT                 28
+#define IPROC_CLK_PLLARMA_PDIV_SHIFT                 24
+#define IPROC_CLK_PLLARMA_PDIV_MASK                  0xf
+#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT             8
+#define IPROC_CLK_PLLARMA_NDIV_INT_MASK              0x3ff
+
+#define IPROC_CLK_PLLARMB_OFFSET                     0xc04
+#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK             0xfffff
+
+#define IPROC_CLK_PLLARMC_OFFSET                     0xc08
+#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT            8
+#define IPROC_CLK_PLLARMC_MDIV_MASK                  0xff
+
+#define IPROC_CLK_PLLARMCTL5_OFFSET                  0xc20
+#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK             0xff
+
+#define IPROC_CLK_PLLARM_OFFSET_OFFSET               0xc24
+#define IPROC_CLK_PLLARM_SW_CTL_SHIFT                29
+#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT       20
+#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK        0xff
+#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK       0xfffff
+
+#define IPROC_CLK_ARM_DIV_OFFSET                     0xe00
+#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT  4
+#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK        0xf
+
+#define IPROC_CLK_POLICY_DBG_OFFSET                  0xec0
+#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT          12
+#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK           0x7
+
+enum iproc_arm_pll_fid {
+       ARM_PLL_FID_CRYSTAL_CLK   = 0,
+       ARM_PLL_FID_SYS_CLK       = 2,
+       ARM_PLL_FID_CH0_SLOW_CLK  = 6,
+       ARM_PLL_FID_CH1_FAST_CLK  = 7
+};
+
+struct iproc_arm_pll {
+       struct clk_hw hw;
+       void __iomem *base;
+       struct clk_onecell_data clk_data;
+       unsigned long rate;
+};
+
+#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
+
+static unsigned int __get_fid(struct iproc_arm_pll *pll)
+{
+       u32 val;
+       unsigned int policy, fid, active_fid;
+
+       val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET);
+       if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT))
+               policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK;
+       else
+               policy = 0;
+
+       /* something is seriously wrong */
+       BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY);
+
+       val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET);
+       fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) &
+               IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK;
+
+       val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET);
+       active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK &
+               (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT);
+       if (fid != active_fid) {
+               pr_debug("%s: fid override %u->%u\n", __func__, fid,
+                               active_fid);
+               fid = active_fid;
+       }
+
+       pr_debug("%s: active fid: %u\n", __func__, fid);
+
+       return fid;
+}
+
+/*
+ * Determine the mdiv (post divider) based on the frequency ID being used.
+ * There are 4 sources that can be used to derive the output clock rate:
+ *    - 25 MHz Crystal
+ *    - System clock
+ *    - PLL channel 0 (slow clock)
+ *    - PLL channel 1 (fast clock)
+ */
+static int __get_mdiv(struct iproc_arm_pll *pll)
+{
+       unsigned int fid;
+       int mdiv;
+       u32 val;
+
+       fid = __get_fid(pll);
+
+       switch (fid) {
+       case ARM_PLL_FID_CRYSTAL_CLK:
+       case ARM_PLL_FID_SYS_CLK:
+               mdiv = 1;
+               break;
+
+       case ARM_PLL_FID_CH0_SLOW_CLK:
+               val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
+               mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK;
+               if (mdiv == 0)
+                       mdiv = 256;
+               break;
+
+       case ARM_PLL_FID_CH1_FAST_CLK:
+               val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET);
+               mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK;
+               if (mdiv == 0)
+                       mdiv = 256;
+               break;
+
+       default:
+               mdiv = -EFAULT;
+       }
+
+       return mdiv;
+}
+
+static unsigned int __get_ndiv(struct iproc_arm_pll *pll)
+{
+       u32 val;
+       unsigned int ndiv_int, ndiv_frac, ndiv;
+
+       val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET);
+       if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) {
+               /*
+                * offset mode is active. Read the ndiv from the PLLARM OFFSET
+                * register
+                */
+               ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) &
+                       IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK;
+               if (ndiv_int == 0)
+                       ndiv_int = 256;
+
+               ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK;
+       } else {
+               /* offset mode not active */
+               val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
+               ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) &
+                       IPROC_CLK_PLLARMA_NDIV_INT_MASK;
+               if (ndiv_int == 0)
+                       ndiv_int = 1024;
+
+               val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET);
+               ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK;
+       }
+
+       ndiv = (ndiv_int << 20) | ndiv_frac;
+
+       return ndiv;
+}
+
+/*
+ * The output frequency of the ARM PLL is calculated based on the ARM PLL
+ * divider values:
+ *   pdiv = ARM PLL pre-divider
+ *   ndiv = ARM PLL multiplier
+ *   mdiv = ARM PLL post divider
+ *
+ * The frequency is calculated by:
+ *   ((ndiv * parent clock rate) / pdiv) / mdiv
+ */
+static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_arm_pll *pll = to_iproc_arm_pll(hw);
+       u32 val;
+       int mdiv;
+       u64 ndiv;
+       unsigned int pdiv;
+
+       /* in bypass mode, use parent rate */
+       val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
+       if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) {
+               pll->rate = parent_rate;
+               return pll->rate;
+       }
+
+       /* PLL needs to be locked */
+       val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
+       if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) {
+               pll->rate = 0;
+               return 0;
+       }
+
+       pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) &
+               IPROC_CLK_PLLARMA_PDIV_MASK;
+       if (pdiv == 0)
+               pdiv = 16;
+
+       ndiv = __get_ndiv(pll);
+       mdiv = __get_mdiv(pll);
+       if (mdiv <= 0) {
+               pll->rate = 0;
+               return 0;
+       }
+       pll->rate = (ndiv * parent_rate) >> 20;
+       pll->rate = (pll->rate / pdiv) / mdiv;
+
+       pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__,
+                       pll->rate, parent_rate);
+       pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__,
+                       (unsigned int)(ndiv >> 20), pdiv, mdiv);
+
+       return pll->rate;
+}
+
+static const struct clk_ops iproc_arm_pll_ops = {
+       .recalc_rate = iproc_arm_pll_recalc_rate,
+};
+
+void __init iproc_armpll_setup(struct device_node *node)
+{
+       int ret;
+       struct clk *clk;
+       struct iproc_arm_pll *pll;
+       struct clk_init_data init;
+       const char *parent_name;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->base = of_iomap(node, 0);
+       if (WARN_ON(!pll->base))
+               goto err_free_pll;
+
+       init.name = node->name;
+       init.ops = &iproc_arm_pll_ops;
+       init.flags = 0;
+       parent_name = of_clk_get_parent_name(node, 0);
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+       if (WARN_ON(IS_ERR(clk)))
+               goto err_iounmap;
+
+       pll->clk_data.clk_num = 1;
+       pll->clk_data.clks = &clk;
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_unregister;
+
+       return;
+
+err_clk_unregister:
+       clk_unregister(clk);
+err_iounmap:
+       iounmap(pll->base);
+err_free_pll:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0
new file mode 100644
index 0000000..ab86b8c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+struct iproc_asiu;
+
+struct iproc_asiu_clk {
+       struct clk_hw hw;
+       const char *name;
+       struct iproc_asiu *asiu;
+       unsigned long rate;
+       struct iproc_asiu_div div;
+       struct iproc_asiu_gate gate;
+};
+
+struct iproc_asiu {
+       void __iomem *div_base;
+       void __iomem *gate_base;
+
+       struct clk_onecell_data clk_data;
+       struct iproc_asiu_clk *clks;
+};
+
+#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw)
+
+static int iproc_asiu_clk_enable(struct clk_hw *hw)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+
+       /* some clocks at the ASIU level are always enabled */
+       if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
+               return 0;
+
+       val = readl(asiu->gate_base + clk->gate.offset);
+       val |= (1 << clk->gate.en_shift);
+       writel(val, asiu->gate_base + clk->gate.offset);
+
+       return 0;
+}
+
+static void iproc_asiu_clk_disable(struct clk_hw *hw)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+
+       /* some clocks at the ASIU level are always enabled */
+       if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
+               return;
+
+       val = readl(asiu->gate_base + clk->gate.offset);
+       val &= ~(1 << clk->gate.en_shift);
+       writel(val, asiu->gate_base + clk->gate.offset);
+}
+
+static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       u32 val;
+       unsigned int div_h, div_l;
+
+       if (parent_rate == 0) {
+               clk->rate = 0;
+               return 0;
+       }
+
+       /* if clock divisor is not enabled, simply return parent rate */
+       val = readl(asiu->div_base + clk->div.offset);
+       if ((val & (1 << clk->div.en_shift)) == 0) {
+               clk->rate = parent_rate;
+               return parent_rate;
+       }
+
+       /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */
+       div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width);
+       div_h++;
+       div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width);
+       div_l++;
+
+       clk->rate = parent_rate / (div_h + div_l);
+       pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n",
+               __func__, clk->rate, parent_rate, div_h, div_l);
+
+       return clk->rate;
+}
+
+static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *parent_rate)
+{
+       unsigned int div;
+
+       if (rate == 0 || *parent_rate == 0)
+               return -EINVAL;
+
+       if (rate == *parent_rate)
+               return *parent_rate;
+
+       div = DIV_ROUND_UP(*parent_rate, rate);
+       if (div < 2)
+               return *parent_rate;
+
+       return *parent_rate / div;
+}
+
+static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct iproc_asiu_clk *clk = to_asiu_clk(hw);
+       struct iproc_asiu *asiu = clk->asiu;
+       unsigned int div, div_h, div_l;
+       u32 val;
+
+       if (rate == 0 || parent_rate == 0)
+               return -EINVAL;
+
+       /* simply disable the divisor if one wants the same rate as parent */
+       if (rate == parent_rate) {
+               val = readl(asiu->div_base + clk->div.offset);
+               val &= ~(1 << clk->div.en_shift);
+               writel(val, asiu->div_base + clk->div.offset);
+               return 0;
+       }
+
+       div = DIV_ROUND_UP(parent_rate, rate);
+       if (div < 2)
+               return -EINVAL;
+
+       div_h = div_l = div >> 1;
+       div_h--;
+       div_l--;
+
+       val = readl(asiu->div_base + clk->div.offset);
+       val |= 1 << clk->div.en_shift;
+       if (div_h) {
+               val &= ~(bit_mask(clk->div.high_width)
+                               << clk->div.high_shift);
+               val |= div_h << clk->div.high_shift;
+       } else {
+               val &= ~(bit_mask(clk->div.high_width)
+                               << clk->div.high_shift);
+       }
+       if (div_l) {
+               val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
+               val |= div_l << clk->div.low_shift;
+       } else {
+               val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
+       }
+       writel(val, asiu->div_base + clk->div.offset);
+
+       return 0;
+}
+
+static const struct clk_ops iproc_asiu_ops = {
+       .enable = iproc_asiu_clk_enable,
+       .disable = iproc_asiu_clk_disable,
+       .recalc_rate = iproc_asiu_clk_recalc_rate,
+       .round_rate = iproc_asiu_clk_round_rate,
+       .set_rate = iproc_asiu_clk_set_rate,
+};
+
+void __init iproc_asiu_setup(struct device_node *node,
+               const struct iproc_asiu_div *div,
+               const struct iproc_asiu_gate *gate, unsigned int num_clks)
+{
+       int i, ret;
+       struct iproc_asiu *asiu;
+
+       if (WARN_ON(!gate || !div))
+               return;
+
+       asiu = kzalloc(sizeof(*asiu), GFP_KERNEL);
+       if (WARN_ON(!asiu))
+               return;
+
+       asiu->clk_data.clk_num = num_clks;
+       asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks),
+                       GFP_KERNEL);
+       if (WARN_ON(!asiu->clk_data.clks))
+               goto err_clks;
+
+       asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL);
+       if (WARN_ON(!asiu->clks))
+               goto err_asiu_clks;
+
+       asiu->div_base = of_iomap(node, 0);
+       if (WARN_ON(!asiu->div_base))
+               goto err_iomap_div;
+
+       asiu->gate_base = of_iomap(node, 1);
+       if (WARN_ON(!asiu->gate_base))
+               goto err_iomap_gate;
+
+       for (i = 0; i < num_clks; i++) {
+               struct clk_init_data init;
+               struct clk *clk;
+               const char *parent_name;
+               struct iproc_asiu_clk *asiu_clk;
+               const char *clk_name;
+
+               clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
+               if (WARN_ON(!clk_name))
+                       goto err_clk_register;
+
+               ret = of_property_read_string_index(node, "clock-output-names",
+                               i, &clk_name);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+
+               asiu_clk = &asiu->clks[i];
+               asiu_clk->name = clk_name;
+               asiu_clk->asiu = asiu;
+               asiu_clk->div = div[i];
+               asiu_clk->gate = gate[i];
+               init.name = clk_name;
+               init.ops = &iproc_asiu_ops;
+               init.flags = 0;
+               parent_name = of_clk_get_parent_name(node, 0);
+               init.parent_names = (parent_name ? &parent_name : NULL);
+               init.num_parents = (parent_name ? 1 : 0);
+               asiu_clk->hw.init = &init;
+
+               clk = clk_register(NULL, &asiu_clk->hw);
+               if (WARN_ON(IS_ERR(clk)))
+                       goto err_clk_register;
+               asiu->clk_data.clks[i] = clk;
+       }
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get,
+                       &asiu->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_register;
+
+       return;
+
+err_clk_register:
+       for (i = 0; i < num_clks; i++)
+               kfree(asiu->clks[i].name);
+       iounmap(asiu->gate_base);
+
+err_iomap_gate:
+       iounmap(asiu->div_base);
+
+err_iomap_div:
+       kfree(asiu->clks);
+
+err_asiu_clks:
+       kfree(asiu->clk_data.clks);
+
+err_clks:
+       kfree(asiu);
+}
drivers/clk/bcm/clk-iproc-clk.c new file +238 -0
new file mode 100644
index 0000000..be3c42c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+struct iproc_pll;
+
+struct iproc_clk {
+       struct clk_hw hw;
+       const char *name;
+       struct iproc_pll *pll;
+       unsigned long rate;
+       const struct iproc_clk_ctrl *ctrl;
+};
+
+struct iproc_pll {
+       void __iomem *base;
+       struct clk_onecell_data clk_data;
+       struct iproc_clk *clks;
+};
+
+#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw)
+
+static int iproc_clk_enable(struct clk_hw *hw)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+
+       /* channel enable is active low */
+       val = readl(pll->base + ctrl->enable.offset);
+       val &= ~(1 << ctrl->enable.enable_shift);
+       writel(val, pll->base + ctrl->enable.offset);
+
+       /* also make sure channel is not held */
+       val = readl(pll->base + ctrl->enable.offset);
+       val &= ~(1 << ctrl->enable.hold_shift);
+       writel(val, pll->base + ctrl->enable.offset);
+
+       return 0;
+}
+
+static void iproc_clk_disable(struct clk_hw *hw)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+
+       if (ctrl->flags & IPROC_CLK_AON)
+               return;
+
+       val = readl(pll->base + ctrl->enable.offset);
+       val |= 1 << ctrl->enable.enable_shift;
+       writel(val, pll->base + ctrl->enable.offset);
+}
+
+static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+       unsigned int mdiv;
+
+       if (parent_rate == 0)
+               return 0;
+
+       val = readl(pll->base + ctrl->mdiv.offset);
+       mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width);
+       if (mdiv == 0)
+               mdiv = 256;
+
+       clk->rate = parent_rate / mdiv;
+
+       return clk->rate;
+}
+
+static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *parent_rate)
+{
+       unsigned int div;
+
+       if (rate == 0 || *parent_rate == 0)
+               return -EINVAL;
+
+       if (rate == *parent_rate)
+               return *parent_rate;
+
+       div = DIV_ROUND_UP(*parent_rate, rate);
+       if (div < 2)
+               return *parent_rate;
+
+       if (div > 256)
+               div = 256;
+
+       return *parent_rate / div;
+}
+
+static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct iproc_clk *clk = to_iproc_clk(hw);
+       const struct iproc_clk_ctrl *ctrl = clk->ctrl;
+       struct iproc_pll *pll = clk->pll;
+       u32 val;
+       unsigned int div;
+
+       if (rate == 0 || parent_rate == 0)
+               return -EINVAL;
+
+       div = DIV_ROUND_UP(parent_rate, rate);
+       if (div > 256)
+               return -EINVAL;
+
+       val = readl(pll->base + ctrl->mdiv.offset);
+       if (div == 256) {
+               val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
+       } else {
+               val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
+               val |= div << ctrl->mdiv.shift;
+       }
+       writel(val, pll->base + ctrl->mdiv.offset);
+       clk->rate = parent_rate / div;
+
+       return 0;
+}
+
+static const struct clk_ops iproc_clk_ops = {
+       .enable = iproc_clk_enable,
+       .disable = iproc_clk_disable,
+       .recalc_rate = iproc_clk_recalc_rate,
+       .round_rate = iproc_clk_round_rate,
+       .set_rate = iproc_clk_set_rate,
+};
+
+void __init iproc_clk_setup(struct device_node *node,
+               const struct iproc_clk_ctrl *ctrl, unsigned int num_clks)
+{
+       int i, ret;
+       struct iproc_pll *pll;
+
+       if (WARN_ON(!ctrl))
+               return;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->clk_data.clk_num = num_clks;
+       pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks),
+                       GFP_KERNEL);
+       if (WARN_ON(!pll->clk_data.clks))
+               goto err_clks;
+
+       pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL);
+       if (WARN_ON(!pll->clks))
+               goto err_pll_clks;
+
+       pll->base = of_iomap(node, 0);
+       if (WARN_ON(!pll->base))
+               goto err_iomap;
+
+       for (i = 0; i < num_clks; i++) {
+               struct clk_init_data init;
+               struct clk *clk;
+               const char *parent_name;
+               struct iproc_clk *iclk;
+               const char *clk_name;
+
+               clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
+               if (WARN_ON(!clk_name))
+                       goto err_clk_register;
+
+               ret = of_property_read_string_index(node, "clock-output-names",
+                               i, &clk_name);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+
+               iclk = &pll->clks[i];
+               iclk->name = clk_name;
+               iclk->pll = pll;
+               iclk->ctrl = &ctrl[i];
+               init.name = clk_name;
+               init.ops = &iproc_clk_ops;
+               init.flags = 0;
+               parent_name = of_clk_get_parent_name(node, 0);
+               init.parent_names = (parent_name ? &parent_name : NULL);
+               init.num_parents = (parent_name ? 1 : 0);
+               iclk->hw.init = &init;
+
+               clk = clk_register(NULL, &iclk->hw);
+               if (WARN_ON(IS_ERR(clk)))
+                       goto err_clk_register;
+               pll->clk_data.clks[i] = clk;
+       }
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_register;
+
+       return;
+
+err_clk_register:
+       for (i = 0; i < num_clks; i++)
+               kfree(pll->clks[i].name);
+       iounmap(pll->base);
+
+err_iomap:
+       kfree(pll->clks);
+
+err_pll_clks:
+       kfree(pll->clk_data.clks);
+
+err_clks:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc-pll.c new file +483 -0
new file mode 100644
index 0000000..cd3bd38
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#include "clk-iproc.h"
+
+#define PLL_VCO_HIGH_SHIFT 19
+#define PLL_VCO_LOW_SHIFT  30
+
+/* number of delay loops waiting for PLL to lock */
+#define LOCK_DELAY 100
+
+/* number of VCO frequency bands */
+#define NUM_FREQ_BANDS 8
+
+#define NUM_KP_BANDS 3
+enum kp_band {
+       KP_BAND_MID = 0,
+       KP_BAND_HIGH,
+       KP_BAND_HIGH_HIGH
+};
+
+static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = {
+       { 5, 6, 6, 7, 7, 8, 9, 10 },
+       { 4, 4, 5, 5, 6, 7, 8, 9  },
+       { 4, 5, 5, 6, 7, 8, 9, 10 },
+};
+
+static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = {
+       { 10000000,  12500000  },
+       { 12500000,  15000000  },
+       { 15000000,  20000000  },
+       { 20000000,  25000000  },
+       { 25000000,  50000000  },
+       { 50000000,  75000000  },
+       { 75000000,  100000000 },
+       { 100000000, 125000000 },
+};
+
+enum vco_freq_range {
+       VCO_LOW       = 700000000U,
+       VCO_MID       = 1200000000U,
+       VCO_HIGH      = 2200000000U,
+       VCO_HIGH_HIGH = 3100000000U,
+       VCO_MAX       = 4000000000U,
+};
+
+struct iproc_pll {
+       struct clk_hw hw;
+       void __iomem *pll_base;
+       void __iomem *pwr_base;
+       void __iomem *asiu_base;
+       struct clk_onecell_data clk_data;
+       const char *name;
+       const struct iproc_pll_ctrl *ctrl;
+       const struct iproc_pll_vco_freq_param *vco_param;
+       unsigned int num_vco_entries;
+       unsigned long rate;
+};
+
+#define to_iproc_pll(hw) container_of(hw, struct iproc_pll, hw)
+
+/*
+ * Get the clock rate based on name
+ */
+static unsigned long __get_rate(const char *clk_name)
+{
+       struct clk *clk;
+
+       clk = __clk_lookup(clk_name);
+       if (!clk) {
+               pr_err("%s: unable to find clock by name: %s\n", __func__,
+                               clk_name);
+               return 0;
+       }
+
+       return clk_get_rate(clk);
+}
+
+/*
+ * Based on the target frequency, find a match from the VCO frequency parameter
+ * table and return its index
+ */
+static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate)
+{
+       int i;
+
+       for (i = 0; i < pll->num_vco_entries; i++)
+               if (target_rate == pll->vco_param[i].rate)
+                       break;
+
+       if (i >= pll->num_vco_entries)
+               return -EINVAL;
+
+       return i;
+}
+
+static int get_kp(unsigned long ref_freq, enum kp_band kp_index)
+{
+       int i;
+
+       if (ref_freq < ref_freq_table[0][0])
+               return -EINVAL;
+
+       for (i = 0; i < NUM_FREQ_BANDS; i++) {
+               if (ref_freq >= ref_freq_table[i][0] &&
+                       ref_freq < ref_freq_table[i][1])
+                       return kp_table[kp_index][i];
+       }
+       return -EINVAL;
+}
+
+static int pll_wait_for_lock(struct iproc_pll *pll)
+{
+       int i;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+
+       for (i = 0; i < LOCK_DELAY; i++) {
+               u32 val = readl(pll->pll_base + ctrl->status.offset);
+
+               if (val & (1 << ctrl->status.shift))
+                       return 0;
+               udelay(10);
+       }
+
+       return -EIO;
+}
+
+static void __pll_disable(struct iproc_pll *pll)
+{
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               val = readl(pll->asiu_base + ctrl->asiu.offset);
+               val &= ~(1 << ctrl->asiu.en_shift);
+               writel(val, pll->asiu_base + ctrl->asiu.offset);
+       }
+
+       /* latch input value so core power can be shut down */
+       val = readl(pll->pwr_base + ctrl->aon.offset);
+       val |= (1 << ctrl->aon.iso_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+
+       /* power down the core */
+       val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+}
+
+static int __pll_enable(struct iproc_pll *pll)
+{
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+
+       /* power up the PLL and make sure it's not latched */
+       val = readl(pll->pwr_base + ctrl->aon.offset);
+       val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
+       val &= ~(1 << ctrl->aon.iso_shift);
+       writel(val, pll->pwr_base + ctrl->aon.offset);
+
+       /* certain PLLs also need to be ungated from the ASIU top level */
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               val = readl(pll->asiu_base + ctrl->asiu.offset);
+               val |= (1 << ctrl->asiu.en_shift);
+               writel(val, pll->asiu_base + ctrl->asiu.offset);
+       }
+
+       return 0;
+}
+
+static void __pll_put_in_reset(struct iproc_pll *pll)
+{
+       u32 val;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
+
+       val = readl(pll->pll_base + reset->offset);
+       val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift);
+       writel(val, pll->pll_base + reset->offset);
+}
+
+static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp,
+               unsigned int ka, unsigned int ki)
+{
+       u32 val;
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
+
+       val = readl(pll->pll_base + reset->offset);
+       val &= ~(bit_mask(reset->ki_width) << reset->ki_shift |
+               bit_mask(reset->kp_width) << reset->kp_shift |
+               bit_mask(reset->ka_width) << reset->ka_shift);
+       val |=  ki << reset->ki_shift | kp << reset->kp_shift |
+               ka << reset->ka_shift;
+       val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift;
+       writel(val, pll->pll_base + reset->offset);
+}
+
+static int pll_set_rate(struct iproc_pll *pll, unsigned int rate_index,
+               unsigned long parent_rate)
+{
+       const struct iproc_pll_vco_freq_param *vco =
+                               &pll->vco_param[rate_index];
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       int ka = 0, ki, kp, ret;
+       unsigned long rate = vco->rate;
+       u32 val;
+       enum kp_band kp_index;
+       unsigned long ref_freq;
+
+       /*
+        * reference frequency = parent frequency / PDIV
+        * If PDIV = 0, then it becomes a multiplier (x2)
+        */
+       if (vco->pdiv == 0)
+               ref_freq = parent_rate * 2;
+       else
+               ref_freq = parent_rate / vco->pdiv;
+
+       /* determine Ki and Kp index based on target VCO frequency */
+       if (rate >= VCO_LOW && rate < VCO_HIGH) {
+               ki = 4;
+               kp_index = KP_BAND_MID;
+       } else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) {
+               ki = 3;
+               kp_index = KP_BAND_HIGH;
+       } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
+               ki = 3;
+               kp_index = KP_BAND_HIGH_HIGH;
+       } else {
+               pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
+                               pll->name, rate);
+               return -EINVAL;
+       }
+
+       kp = get_kp(ref_freq, kp_index);
+       if (kp < 0) {
+               pr_err("%s: pll: %s has invalid kp\n", __func__, pll->name);
+               return kp;
+       }
+
+       ret = __pll_enable(pll);
+       if (ret) {
+               pr_err("%s: pll: %s fails to enable\n", __func__, pll->name);
+               return ret;
+       }
+
+       /* put PLL in reset */
+       __pll_put_in_reset(pll);
+
+       writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset);
+       val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset);
+
+       if (rate >= VCO_LOW && rate < VCO_MID)
+               val |= (1 << PLL_VCO_LOW_SHIFT);
+
+       if (rate < VCO_HIGH)
+               val &= ~(1 << PLL_VCO_HIGH_SHIFT);
+       else
+               val |= (1 << PLL_VCO_HIGH_SHIFT);
+
+       writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset);
+
+       /* program integer part of NDIV */
+       val = readl(pll->pll_base + ctrl->ndiv_int.offset);
+       val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
+       val |= vco->ndiv_int << ctrl->ndiv_int.shift;
+       writel(val, pll->pll_base + ctrl->ndiv_int.offset);
+
+       /* program fractional part of NDIV */
+       if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
+               val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
+               val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
+                               ctrl->ndiv_frac.shift);
+               val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
+               writel(val, pll->pll_base + ctrl->ndiv_frac.offset);
+       }
+
+       /* program PDIV */
+       val = readl(pll->pll_base + ctrl->pdiv.offset);
+       val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
+       val |= vco->pdiv << ctrl->pdiv.shift;
+       writel(val, pll->pll_base + ctrl->pdiv.offset);
+
+       __pll_bring_out_reset(pll, kp, ka, ki);
+
+       ret = pll_wait_for_lock(pll);
+       if (ret < 0) {
+               pr_err("%s: pll: %s failed to lock\n", __func__, pll->name);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int iproc_pll_enable(struct clk_hw *hw)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+
+       return __pll_enable(pll);
+}
+
+static void iproc_pll_disable(struct clk_hw *hw)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+
+       if (ctrl->flags & IPROC_CLK_AON)
+               return;
+
+       __pll_disable(pll);
+}
+
+static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw,
+       unsigned long parent_rate)
+{
+       struct iproc_pll *pll = to_iproc_pll(hw);
+       const struct iproc_pll_ctrl *ctrl = pll->ctrl;
+       u32 val;
+       u64 ndiv;
+       unsigned int ndiv_int, ndiv_frac, pdiv;
+
+       if (parent_rate == 0)
+               return 0;
+
+       /* PLL needs to be locked */
+       val = readl(pll->pll_base + ctrl->status.offset);
+       if ((val & (1 << ctrl->status.shift)) == 0) {
+               pll->rate = 0;
+               return 0;
+       }
+
+       /*
+        * PLL output frequency =
+        *
+        * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv)
+        */
+       val = readl(pll->pll_base + ctrl->ndiv_int.offset);
+       ndiv_int = (val >> ctrl->ndiv_int.shift) &
+               bit_mask(ctrl->ndiv_int.width);
+       ndiv = ndiv_int << ctrl->ndiv_int.shift;
+
+       if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
+               val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
+               ndiv_frac = (val >> ctrl->ndiv_frac.shift) &
+                       bit_mask(ctrl->ndiv_frac.width);
+
+               if (ndiv_frac != 0)
+                       ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac;
+       }
+
+       val = readl(pll->pll_base + ctrl->pdiv.offset);
+       pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
+
+       pll->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift;
+
+       if (pdiv == 0)
+               pll->rate *= 2;
+       else
+               pll->rate /= pdiv;
+
+       return pll->rate;
+}
+
+static const struct clk_ops iproc_pll_ops = {
+       .enable = iproc_pll_enable,
+       .disable = iproc_pll_disable,
+       .recalc_rate = iproc_pll_recalc_rate,
+};
+
+void __init iproc_pll_setup(struct device_node *node,
+               const struct iproc_pll_ctrl *ctrl,
+               const struct iproc_pll_vco_freq_param *vco_param,
+               unsigned int num_vco_entries)
+{
+       int ret;
+       struct clk *clk;
+       struct iproc_pll *pll;
+       struct clk_init_data init;
+       const char *parent_name;
+       unsigned int rate;
+
+       if (WARN_ON(!ctrl))
+               return;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (WARN_ON(!pll))
+               return;
+
+       pll->pll_base = of_iomap(node, 0);
+       if (WARN_ON(!pll->pll_base))
+               goto err_pll_iomap;
+
+       pll->pwr_base = of_iomap(node, 1);
+       if (WARN_ON(!pll->pwr_base))
+               goto err_pwr_iomap;
+
+       /* some PLLs require gating control at the top ASIU level */
+       if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
+               pll->asiu_base = of_iomap(node, 2);
+               if (WARN_ON(!pll->asiu_base))
+                       goto err_asiu_iomap;
+       }
+
+       pll->ctrl = ctrl;
+       pll->name = node->name;
+       init.name = node->name;
+       init.ops = &iproc_pll_ops;
+       init.flags = 0;
+       parent_name = of_clk_get_parent_name(node, 0);
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+       pll->hw.init = &init;
+
+       /* configure the PLL to the desired VCO frequency if specified */
+       ret = of_property_read_u32(node, "clock-frequency", &rate);
+       if (!ret) {
+               unsigned long parent_rate;
+               int rate_index;
+
+               if (WARN_ON(!vco_param))
+                       goto err_clk_register;
+
+               pll->num_vco_entries = num_vco_entries;
+               pll->vco_param = vco_param;
+
+               parent_rate = __get_rate(parent_name);
+               if (WARN_ON(!parent_rate))
+                       goto err_clk_register;
+
+               rate_index = pll_get_rate_index(pll, rate);
+               if (WARN_ON(rate_index < 0))
+                       goto err_clk_register;
+
+               ret = pll_set_rate(pll, rate_index, parent_rate);
+               if (WARN_ON(ret))
+                       goto err_clk_register;
+       }
+
+       clk = clk_register(NULL, &pll->hw);
+       if (WARN_ON(IS_ERR(clk)))
+               goto err_clk_register;
+
+       pll->clk_data.clk_num = 1;
+       pll->clk_data.clks = &clk;
+
+       ret = of_clk_add_provider(node, of_clk_src_onecell_get,
+                       &pll->clk_data);
+       if (WARN_ON(ret))
+               goto err_clk_add;
+
+       return;
+
+err_clk_add:
+       clk_unregister(clk);
+err_clk_register:
+       if (pll->asiu_base)
+               iounmap(pll->asiu_base);
+err_asiu_iomap:
+       iounmap(pll->pwr_base);
+err_pwr_iomap:
+       iounmap(pll->pll_base);
+err_pll_iomap:
+       kfree(pll);
+}
drivers/clk/bcm/clk-iproc.h new file +155 -1
new file mode 100644
index 0000000..4aa0479
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CLK_IPROC_H
+#define _CLK_IPROC_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+
+#define IPROC_CLK_NAME_LEN 25
+#define IPROC_CLK_INVALID_OFFSET 0xffffffff
+#define bit_mask(width) ((1 << (width)) - 1)
+
+/* clock should not be disabled at runtime */
+#define IPROC_CLK_AON BIT(0)
+
+/* PLL requires gating through ASIU */
+#define IPROC_CLK_PLL_ASIU BIT(1)
+
+/* PLL has fractional part of the NDIV */
+#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2)
+
+/*
+ * Parameters for VCO frequency configuration
+ *
+ * VCO frequency =
+ * ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy  / pdiv)
+ */
+struct iproc_pll_vco_freq_param {
+       unsigned long rate;
+       unsigned int ndiv_int;
+       unsigned int ndiv_frac;
+       unsigned int pdiv;
+};
+
+struct iproc_clk_reg_op {
+       unsigned int offset;
+       unsigned int shift;
+       unsigned int width;
+};
+
+/*
+ * Clock gating control at the top ASIU level
+ */
+struct iproc_asiu_gate {
+       unsigned int offset;
+       unsigned int en_shift;
+};
+
+/*
+ * Control of powering on/off of a PLL
+ *
+ * Before powering off a PLL, input isolation (ISO) needs to be enabled
+ */
+struct iproc_pll_aon_pwr_ctrl {
+       unsigned int offset;
+       unsigned int pwr_width;
+       unsigned int pwr_shift;
+       unsigned int iso_shift;
+};
+
+/*
+ * Control of the PLL reset, with Ki, Kp, and Ka parameters
+ */
+struct iproc_pll_reset_ctrl {
+       unsigned int offset;
+       unsigned int reset_shift;
+       unsigned int p_reset_shift;
+       unsigned int ki_shift;
+       unsigned int ki_width;
+       unsigned int kp_shift;
+       unsigned int kp_width;
+       unsigned int ka_shift;
+       unsigned int ka_width;
+};
+
+struct iproc_pll_vco_ctrl {
+       unsigned int u_offset;
+       unsigned int l_offset;
+};
+
+/*
+ * Main PLL control parameters
+ */
+struct iproc_pll_ctrl {
+       unsigned long flags;
+       struct iproc_pll_aon_pwr_ctrl aon;
+       struct iproc_asiu_gate asiu;
+       struct iproc_pll_reset_ctrl reset;
+       struct iproc_clk_reg_op ndiv_int;
+       struct iproc_clk_reg_op ndiv_frac;
+       struct iproc_clk_reg_op pdiv;
+       struct iproc_pll_vco_ctrl vco_ctrl;
+       struct iproc_clk_reg_op status;
+};
+
+/*
+ * Controls enabling/disabling a PLL derived clock
+ */
+struct iproc_clk_enable_ctrl {
+       unsigned int offset;
+       unsigned int enable_shift;
+       unsigned int hold_shift;
+       unsigned int bypass_shift;
+};
+
+/*
+ * Main clock control parameters for clocks derived from the PLLs
+ */
+struct iproc_clk_ctrl {
+       unsigned int channel;
+       unsigned long flags;
+       struct iproc_clk_enable_ctrl enable;
+       struct iproc_clk_reg_op mdiv;
+};
+
+/*
+ * Divisor of the ASIU clocks
+ */
+struct iproc_asiu_div {
+       unsigned int offset;
+       unsigned int en_shift;
+       unsigned int high_shift;
+       unsigned int high_width;
+       unsigned int low_shift;
+       unsigned int low_width;
+};
+
+extern void __init iproc_armpll_setup(struct device_node *node);
+extern void __init iproc_pll_setup(struct device_node *node,
+               const struct iproc_pll_ctrl *ctrl,
+               const struct iproc_pll_vco_freq_param *vco_param,
+               unsigned int num_freqs);
+extern void __init iproc_clk_setup(struct device_node *node,
+               const struct iproc_clk_ctrl *ctrl, unsigned int num_clks);
+extern void __init iproc_asiu_setup(struct device_node *node,
+               const struct iproc_asiu_div *div,
+               const struct iproc_asiu_gate *gate, unsigned int num_clks);
+
+#endif /* _CLK_IPROC_H */
--

1.7.9.5

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

author maintainer
#59 lore raw

PATCH 3/4 clk: cygnus: add clock support for Broadcom Cygnus

In reply to Ray Jui

The Broadcom Cygnus SoC is architected under the iProc architecture. It has the following PLLs: ARMPLL, GENPLL, LCPLL0, MIPIPLL, all dervied from an onboard crystal. Cygnus also has various ASIU clocks that are derived directly from the onboard crystal.

---
 drivers/clk/bcm/Makefile               |    1 +
 drivers/clk/bcm/clk-cygnus.c           |  277 ++++++++++++++++++++++++++++++++
 include/dt-bindings/clock/bcm-cygnus.h |   77 +++++++++
 3 files changed, 355 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
drivers/clk/bcm/Makefile +1 -0
index 6926636..afcbe55 100644
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
 obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
+obj-$(CONFIG_ARCH_BCM_CYGNUS)	+= clk-cygnus.o
drivers/clk/bcm/clk-cygnus.c new file +277 -0

277 lines not on this page · raw message

include/dt-bindings/clock/bcm-cygnus.h new file +77 -0

77 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#60 lore raw

PATCH 4/4 ARM: dts: enable clock support for Broadcom Cygnus

In reply to Ray Jui

Replace current device tree dummy clocks with real clock support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 ++++++++++++++++++++++++-------
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 2 files changed, 86 insertions(+), 26 deletions(-)
arch/arm/boot/dts/bcm-cygnus-clock.dtsi +85 -25
index 60d8389..f2da5e2 100644
@@ -36,56 +36,116 @@ clocks {
 	ranges;
 
 	osc: oscillator {
+		#clock-cells = <0>;
 		compatible = "fixed-clock";
-		#clock-cells = <1>;
 		clock-frequency = <25000000>;
 	};
 
-	apb_clk: apb_clk {
-		compatible = "fixed-clock";
+	/* Cygnus ARM PLL */
+	armpll: armpll {
 		#clock-cells = <0>;
-		clock-frequency = <1000000000>;
+		compatible = "brcm,cygnus-armpll";
+		clocks = <&osc>;
+		reg = <0x19000000 0x1000>;
 	};
 
-	periph_clk: periph_clk {
-		compatible = "fixed-clock";
+	/* peripheral clock for system timer */
+	arm_periph_clk: arm_periph_clk {
 		#clock-cells = <0>;
-		clock-frequency = <500000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	sdio_clk: lcpll_ch2 {
-		compatible = "fixed-clock";
+	/* APB bus clock */
+	apb_clk: apb_clk {
 		#clock-cells = <0>;
-		clock-frequency = <200000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	axi81_clk: axi81_clk {
-		compatible = "fixed-clock";
+	genpll: genpll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	keypad_clk: keypad_clk {
-		compatible = "fixed-clock";
+	/* various clocks running off the GENPLL */
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+	/* always 1/2 of the axi21 clock */
+	axi41_clk: axi41_clk {
 		#clock-cells = <0>;
-		clock-frequency = <31806>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	adc_clk: adc_clk {
-		compatible = "fixed-clock";
+	/* always 1/4 of the axi21 clock */
+	axi81_clk: axi81_clk {
 		#clock-cells = <0>;
-		clock-frequency = <1562500>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	pwm_clk: pwm_clk {
-		compatible = "fixed-clock";
+	lcpll0: lcpll0 {
 		#clock-cells = <0>;
-		clock-frequency = <1000000>;
+		compatible = "brcm,cygnus-lcpll0";
+		reg = <0x0301d02c 0x1c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	lcd_clk: mipipll_ch1 {
-		compatible = "fixed-clock";
+	/* various clocks running off the LCPLL0 */
+	lcpll0_clks: lcpll0_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-lcpll0-clk";
+		reg = <0x0301d02c 0x1c>;
+		clocks = <&lcpll0>;
+		clock-output-names = "pcie_phy", "ddr_phy", "sdio",
+			"usb_phy", "smart_card", "ch5";
+	};
+
+	mipipll: mipipll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-mipipll";
+		reg = <0x180a9800 0x2c>,
+			<0x0301c020 0x4>,
+			<0x180aa024 0x4>;
+		clock-frequency = <1350000000>;
+		clocks = <&osc>;
+	};
+
+	mipipll_clks: mipipll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-mipipll-clk";
+		reg = <0x180a9800 0x2c>;
+		clocks = <&mipipll>;
+		clock-output-names = "ch0_unused", "ch1_lcd", "ch2_unused",
+			"ch3_unused", "ch4_unused", "ch5_unused";
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
 	};
 };
arch/arm/boot/dts/bcm-cygnus.dtsi +1 -1
index 5126f9e..2b99e9c 100644
@@ -134,7 +134,7 @@
 		compatible = "arm,cortex-a9-global-timer";
 		reg = <0x19020200 0x100>;
 		interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&periph_clk>;
+		clocks = <&arm_periph_clk>;
 	};
 
 };
Signature · 1 line
1.7.9.5
author maintainer
#61 lore raw

cover Add pinctrl support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial pinctrl support for the Broadcom Cygnus SoC. The Cygnus pinctrl controller supports group based alternate function configuration

Ray Jui (4):
pinctrl: Broadcom Cygnus pinctrl device tree binding
pinctrl: cygnus: add initial pinctrl support
ARM: mach-bcm: enable pinctrl support for Cygnus
ARM: dts: enable pinctrl for Broadcom Cygnus

 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |    5 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/pinctrl/Kconfig                            |    7 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c               |  753 ++++++++++++++++++++
 6 files changed, 859 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#62 lore raw

PATCH 1/4 pinctrl: Broadcom Cygnus pinctrl device tree binding

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +92 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers
+
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"
+
+Each child node represents a configuration. Client devices reference the the
+child node to enable the mux configuration.
+
+For example:
+
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+
+		i2s_0: i2s_0 {
+			brcm,groups = "smart_card0", "smart_card0_fcb";
+			brcm,function = "alt2";
+		};
+
+		i2s_1: i2s_1 {
+			brcm,groups = "smart_card1", "smart_card1_fcb";
+			brcm,function = "alt2";
+		};
+
+		spi_0: spi_0 {
+			brcm,groups = "spi0";
+			brcm,function = "alt1";
+		};
+	}
+
+	spi0@18028000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x18028000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-0 = <&spi_0>;
+			clocks = <&axi81_clk>;
+			clock-names = "apb_pclk";
+	};
+
+Consider the following snapshot of Cygnus pinmux table:
+
+number    pin            group              alt1             alt2        alt3        alt4
+------    ---            ----               ----             ----        ----        ----
+42        sc0_clk        smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio24
+43        sc0_cmdvcc_l   smart_card0        SMART CARD0      I2S_0       N/A         STRAP
+44        sc0_detect     smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio25
+45        sc0_fcb        smart_card0_fcb    SMART CARD0_FCB  I2S_0       N/A         chip_gpio26
+46        sc0_io         smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio27
+47        sc0_rst_l      smart_card0        SMART CARD0      SPDIF       N/A         STRAP
+
+Note due to limitation of the Cygnus hardware, pinmux configuration can only
+be group based. To enable I2S_0 function, one needs the following child node
+configuration:
+
+	i2s_0: i2s_0 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt2";
+	};
+
+This tells the Cygnus pin controller to configure groups "smart_card0" and
+"smart_card0_fcb" to I2S_0. With this configuration, pins 42, 43, 44, 45, 46
+become I2C_0, and pin 47 becomes SPDIF
+
+Consider another example, that one wants to configure the above pins as GPIO:
+
+	gpio_24_27: gpio_24_27 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt4";
+	};
+
+With the above configuration, pins 42, 44, 45, 46 become GPIO, and 43 and 47
+become reserved for STRAP
Signature · 1 line
1.7.9.5
#63 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 87 lines · Ray Jui

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +72 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers
+
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"
+
+Each child node represents a configuration. Client devices reference the the
+child node to enable the mux configuration.
+
+For example:
+
+       pinctrl: pinctrl at 0x0301d0c8 {
+               compatible = "brcm,cygnus-pinctrl";
+               reg = <0x0301d0c8 0x2c>;
+
+               i2s_0: i2s_0 {
+                       brcm,groups = "smart_card0", "smart_card0_fcb";
+                       brcm,function = "alt2";
+               };
+
+               i2s_1: i2s_1 {
+                       brcm,groups = "smart_card1", "smart_card1_fcb";
+                       brcm,function = "alt2";
+               };
+
+               spi_0: spi_0 {
+                       brcm,groups = "spi0";
+                       brcm,function = "alt1";
+               };
+       }
+
+       spi0 at 18028000 {
+                       compatible = "arm,pl022", "arm,primecell";
+                       reg = <0x18028000 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+                       pinctrl-0 = <&spi_0>;
+                       clocks = <&axi81_clk>;
+                       clock-names = "apb_pclk";
+       };
+
+Consider the following snapshot of Cygnus pinmux table:
+
+number    pin            group              alt1             alt2        alt3        alt4
+------    ---            ----               ----             ----        ----        ----
+42        sc0_clk        smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio24
+43        sc0_cmdvcc_l   smart_card0        SMART CARD0      I2S_0       N/A         STRAP
+44        sc0_detect     smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio25
+45        sc0_fcb        smart_card0_fcb    SMART CARD0_FCB  I2S_0       N/A         chip_gpio26
+46        sc0_io         smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio27
+47        sc0_rst_l      smart_card0        SMART CARD0      SPDIF       N/A         STRAP
+
+Note due to limitation of the Cygnus hardware, pinmux configuration can only
+be group based. To enable I2S_0 function, one needs the following child node

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 82

Quoted 10 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +10 -0
+configuration:
+
+       i2s_0: i2s_0 {
+               brcm,groups = "smart_card0", "smart_card0_fcb";
+               brcm,function = "alt2";
+       };
+
+This tells the Cygnus pin controller to configure groups "smart_card0" and
+"smart_card0_fcb" to I2S_0. With this configuration, pins 42, 43, 44, 45, 46
+become I2C_0, and pin 47 becomes SPDIF
                  ^^^^ typo - should be I2S_0
Quoted 14 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +10 -1
+
+Consider another example, that one wants to configure the above pins as GPIO:
+
+       gpio_24_27: gpio_24_27 {
+               brcm,groups = "smart_card0", "smart_card0_fcb";
+               brcm,function = "alt4";
+       };
+
+With the above configuration, pins 42, 44, 45, 46 become GPIO, and 43 and 47
+become reserved for STRAP
--

1.7.9.5

Quoted 4 lines

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

BR,

marek

Signature · 10 lines
as simple and primitive as possible
-------------------------------------------------
Marek Belisko - OPEN-NANDRA
Freelance Developer

Ruska Nova Ves 219 | Presov, 08005 Slovak Republic
Tel: +421 915 052 184
skype: marekwhite
twitter: #opennandra
web: http://open-nandra.com
author maintainer
#64 lore raw

Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding

Quoted 91 lines · Belisko Marek
Quoted 90 lines · Ray Jui

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
  .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
  1 file changed, 92 insertions(+)

create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +75 -0
new file mode 100644
index 0000000..86e4579
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers
+
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"
+
+Each child node represents a configuration. Client devices reference the the
+child node to enable the mux configuration.
+
+For example:
+
+       pinctrl: pinctrl at 0x0301d0c8 {
+               compatible = "brcm,cygnus-pinctrl";
+               reg = <0x0301d0c8 0x2c>;
+
+               i2s_0: i2s_0 {
+                       brcm,groups = "smart_card0", "smart_card0_fcb";
+                       brcm,function = "alt2";
+               };
+
+               i2s_1: i2s_1 {
+                       brcm,groups = "smart_card1", "smart_card1_fcb";
+                       brcm,function = "alt2";
+               };
+
+               spi_0: spi_0 {
+                       brcm,groups = "spi0";
+                       brcm,function = "alt1";
+               };
+       }
+
+       spi0 at 18028000 {
+                       compatible = "arm,pl022", "arm,primecell";
+                       reg = <0x18028000 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+                       pinctrl-0 = <&spi_0>;
+                       clocks = <&axi81_clk>;
+                       clock-names = "apb_pclk";
+       };
+
+Consider the following snapshot of Cygnus pinmux table:
+
+number    pin            group              alt1             alt2        alt3        alt4
+------    ---            ----               ----             ----        ----        ----
+42        sc0_clk        smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio24
+43        sc0_cmdvcc_l   smart_card0        SMART CARD0      I2S_0       N/A         STRAP
+44        sc0_detect     smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio25
+45        sc0_fcb        smart_card0_fcb    SMART CARD0_FCB  I2S_0       N/A         chip_gpio26
+46        sc0_io         smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio27
+47        sc0_rst_l      smart_card0        SMART CARD0      SPDIF       N/A         STRAP
+
+Note due to limitation of the Cygnus hardware, pinmux configuration can only
+be group based. To enable I2S_0 function, one needs the following child node
+configuration:
+
+       i2s_0: i2s_0 {
Quoted 8 lines

Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt line 82

Quoted 7 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +7 -0
+               brcm,groups = "smart_card0", "smart_card0_fcb";
+               brcm,function = "alt2";
+       };
+
+This tells the Cygnus pin controller to configure groups "smart_card0" and
+"smart_card0_fcb" to I2S_0. With this configuration, pins 42, 43, 44, 45, 46
+become I2C_0, and pin 47 becomes SPDIF
                   ^^^^ typo - should be I2S_0

Oh yeah. Will change from I2C_0 to I2S_0. Thanks.

Quoted 23 lines
Quoted 14 lines
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +10 -1
+
+Consider another example, that one wants to configure the above pins as GPIO:
+
+       gpio_24_27: gpio_24_27 {
+               brcm,groups = "smart_card0", "smart_card0_fcb";
+               brcm,function = "alt4";
+       };
+
+With the above configuration, pins 42, 44, 45, 46 become GPIO, and 43 and 47
+become reserved for STRAP
--

1.7.9.5

Quoted 4 lines

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

BR,

marek

author maintainer
#65 lore raw

PATCH 2/4 pinctrl: cygnus: add initial pinctrl support

In reply to Ray Jui

This adds the initial driver support for the Broadcom Cygnus pinctrl controller. The Cygnus pinctrl controller supports group based alternate function configuration

---
 drivers/pinctrl/Kconfig              |    7 +
 drivers/pinctrl/Makefile             |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c |  753 ++++++++++++++++++++++++++++++++++
 3 files changed, 761 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
drivers/pinctrl/Kconfig +7 -0
index d014f22..4549e9f 100644
@@ -85,6 +85,13 @@ config PINCTRL_BCM281XX
 	  BCM28145, and BCM28155 SoCs.  This driver requires the pinctrl
 	  framework.  GPIO is provided by a separate GPIO driver.
 
+config PINCTRL_BCM_CYGNUS
+	bool "Broadcom Cygnus pinctrl driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+
 config PINCTRL_LANTIQ
 	bool
 	depends on LANTIQ
drivers/pinctrl/Makefile +1 -0
index c030b3d..4ed8e8a 100644
@@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_BF60x)	+= pinctrl-adi2-bf60x.o
 obj-$(CONFIG_PINCTRL_AT91)	+= pinctrl-at91.o
 obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
 obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM_CYGNUS)	+= pinctrl-bcm-cygnus.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +753 -0

753 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#66 lore raw

PATCH 3/4 ARM: mach-bcm: enable pinctrl support for Cygnus

In reply to Ray Jui

This enables the pinctrl driver for Broadcom Cygnus SoC

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..b4efff2 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select PINCTRL_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#67 lore raw

PATCH 4/4 ARM: dts: enable pinctrl for Broadcom Cygnus

In reply to Ray Jui

This enables the pinctrl support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |    5 +++++
 1 file changed, 5 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +5 -0
index 5126f9e..4c6bf4d 100644
@@ -54,6 +54,11 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#68 lore raw

v2 cover Add pinctrl support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial pinctrl support for the Broadcom Cygnus SoC. The Cygnus pinctrl controller supports group based alternate function configuration

Changes from v1:
- Fix a typo in device tree binding document

Ray Jui (4):
pinctrl: Broadcom Cygnus pinctrl device tree binding
pinctrl: cygnus: add initial pinctrl support
ARM: mach-bcm: enable pinctrl support for Cygnus
ARM: dts: enable pinctrl for Broadcom Cygnus

 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |    5 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/pinctrl/Kconfig                            |    7 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c               |  753 ++++++++++++++++++++
 6 files changed, 859 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#69 lore raw

PATCH v2 1/4 pinctrl: Broadcom Cygnus pinctrl device tree binding

Device tree binding documentation for Broadcom Cygnus pinctrl driver

---
 .../bindings/pinctrl/brcm,cygnus-pinctrl.txt       |   92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt new file +92 -0
new file mode 100644
index 0000000..4461aaf
@@ -0,0 +1,92 @@
+Broadcom Cygnus Pin Controller
+
+The Cygnus pin controller supports setting the alternate functions of groups
+of pins. Pinmux configuration on individual pins is not supported by the
+Cygnus A0 SoC.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinctrl"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+pin control registers
+
+- brcm,groups:
+    This can be strings of one or more group names. This defines the group(s)
+that one wants to configure
+
+- brcm,function:
+    This is the alternate function that one wants to configure to. Valid
+alternate functions are "alt1", "alt2", "alt3", "alt4"
+
+Each child node represents a configuration. Client devices reference the the
+child node to enable the mux configuration.
+
+For example:
+
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+
+		i2s_0: i2s_0 {
+			brcm,groups = "smart_card0", "smart_card0_fcb";
+			brcm,function = "alt2";
+		};
+
+		i2s_1: i2s_1 {
+			brcm,groups = "smart_card1", "smart_card1_fcb";
+			brcm,function = "alt2";
+		};
+
+		spi_0: spi_0 {
+			brcm,groups = "spi0";
+			brcm,function = "alt1";
+		};
+	}
+
+	spi0@18028000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x18028000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-0 = <&spi_0>;
+			clocks = <&axi81_clk>;
+			clock-names = "apb_pclk";
+	};
+
+Consider the following snapshot of Cygnus pinmux table:
+
+number    pin            group              alt1             alt2        alt3        alt4
+------    ---            ----               ----             ----        ----        ----
+42        sc0_clk        smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio24
+43        sc0_cmdvcc_l   smart_card0        SMART CARD0      I2S_0       N/A         STRAP
+44        sc0_detect     smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio25
+45        sc0_fcb        smart_card0_fcb    SMART CARD0_FCB  I2S_0       N/A         chip_gpio26
+46        sc0_io         smart_card0        SMART CARD0      I2S_0       N/A         chip_gpio27
+47        sc0_rst_l      smart_card0        SMART CARD0      SPDIF       N/A         STRAP
+
+Note due to limitation of the Cygnus hardware, pinmux configuration can only
+be group based. To enable I2S_0 function, one needs the following child node
+configuration:
+
+	i2s_0: i2s_0 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt2";
+	};
+
+This tells the Cygnus pin controller to configure groups "smart_card0" and
+"smart_card0_fcb" to I2S_0. With this configuration, pins 42, 43, 44, 45, 46
+become I2S_0, and pin 47 becomes SPDIF
+
+Consider another example, that one wants to configure the above pins as GPIO:
+
+	gpio_24_27: gpio_24_27 {
+		brcm,groups = "smart_card0", "smart_card0_fcb";
+		brcm,function = "alt4";
+	};
+
+With the above configuration, pins 42, 44, 45, 46 become GPIO, and 43 and 47
+become reserved for STRAP
Signature · 1 line
1.7.9.5
author maintainer
#70 lore raw

PATCH v2 2/4 pinctrl: cygnus: add initial pinctrl support

In reply to Ray Jui

This adds the initial driver support for the Broadcom Cygnus pinctrl controller. The Cygnus pinctrl controller supports group based alternate function configuration

---
 drivers/pinctrl/Kconfig              |    7 +
 drivers/pinctrl/Makefile             |    1 +
 drivers/pinctrl/pinctrl-bcm-cygnus.c |  753 ++++++++++++++++++++++++++++++++++
 3 files changed, 761 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-bcm-cygnus.c
drivers/pinctrl/Kconfig +7 -0
index d014f22..4549e9f 100644
@@ -85,6 +85,13 @@ config PINCTRL_BCM281XX
 	  BCM28145, and BCM28155 SoCs.  This driver requires the pinctrl
 	  framework.  GPIO is provided by a separate GPIO driver.
 
+config PINCTRL_BCM_CYGNUS
+	bool "Broadcom Cygnus pinctrl driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+
 config PINCTRL_LANTIQ
 	bool
 	depends on LANTIQ
drivers/pinctrl/Makefile +1 -0
index c030b3d..4ed8e8a 100644
@@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_BF60x)	+= pinctrl-adi2-bf60x.o
 obj-$(CONFIG_PINCTRL_AT91)	+= pinctrl-at91.o
 obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
 obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM_CYGNUS)	+= pinctrl-bcm-cygnus.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
drivers/pinctrl/pinctrl-bcm-cygnus.c new file +753 -0

753 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#71 lore raw

PATCH v2 3/4 ARM: mach-bcm: enable pinctrl support for Cygnus

In reply to Ray Jui

This enables the pinctrl driver for Broadcom Cygnus SoC

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..b4efff2 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select PINCTRL_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#72 lore raw

PATCH v2 4/4 ARM: dts: enable pinctrl for Broadcom Cygnus

In reply to Ray Jui

This enables the pinctrl support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |    5 +++++
 1 file changed, 5 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +5 -0
index 5126f9e..4c6bf4d 100644
@@ -54,6 +54,11 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinctrl";
+		reg = <0x0301d0c8 0x2c>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#73 lore raw

cover Add gpio support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  719 ++++++++++++++++++++
 7 files changed, 854 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#74 lore raw

PATCH 1/5 gpio: Cygnus: define Broadcom Cygnus GPIO binding

Document the GPIO device tree binding for Broadcom Cygnus SoC

---
 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 ++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +85 -0
new file mode 100644
index 0000000..24a1513
@@ -0,0 +1,85 @@
+Broadcom Cygnus GPIO Controller
+
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+GPIO controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller. Not all Cygnus GPIO
+interfaces support interrupt, e.g., the CRMU GPIO controller does not have its
+interrupt routed to the main processor's GIC
+
+- interrupts:
+    The interrupt outputs from the GPIO controller.
+
+- no-interrupt:
+    Specifies that the GPIO interface does not support interrupt
+
+Example:
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
+	/*
+	 * Touchscreen that uses the ASIU GPIO 100, with internal pull-up
+	 * enabled
+	 */
+	tsc {
+		...
+		...
+		gpio-event = <&gpio_asiu 100 0x10000>;
+	};
+
+	/* Bluetooth that uses the CRMU GPIO 2, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_crmu 2 1>
+	}
Signature · 1 line
1.7.9.5
#75 lore raw

Re: [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Quoted 4 lines · Ray Jui

Document the GPIO device tree binding for Broadcom Cygnus SoC

(...)

Quoted 5 lines

+- #gpio-cells:

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +4 -0
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 32

Quoted 10 lines
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +10 -0
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA

No. This pull up/down and drive strength is pin controller business, use a pin control backend behind the GPIO driver see Documentation/pinctrl.txt.

Initial states for these configurations can be set up using pin control hogs since pin control and GPIO is orthogonal.

Yours,
Linus Walleij

author maintainer
#76 lore raw

Re: [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Quoted 23 lines · Linus Walleij
Quoted 4 lines · Ray Jui

Document the GPIO device tree binding for Broadcom Cygnus SoC

(...)

Quoted 5 lines

+- #gpio-cells:

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +4 -0
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 32

Quoted 10 lines
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +10 -0
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
Quoted 7 lines

No. This pull up/down and drive strength is pin controller business, use a pin control backend behind the GPIO driver see Documentation/pinctrl.txt.

Initial states for these configurations can be set up using pin control hogs since pin control and GPIO is orthogonal.

Yes, I got it! See my reply in the GPIO driver review. Thanks.

Quoted 3 lines

Yours,
Linus Walleij

author maintainer
#77 lore raw

PATCH 2/5 gpio: Cygnus: add GPIO driver

In reply to Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller ("brcm,cygnus-asiu-gpio"), 2) the chipCommonG GPIO controller ("brcm,cygnus-ccm-gpio"), and 3) the ALWAYS-ON GPIO controller ("brcm,cygnus-crmu-gpio")

---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  719 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 731 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
 	    8 bits:	74244 (Input), 74273 (Output)
 	    16 bits:	741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+	bool "Broadcom Cygnus GPIO support"
+	depends on ARCH_BCM_CYGNUS && OF_GPIO
+	help
+	  Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+	  supported by this driver
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)	+= gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +719 -0

719 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#78 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Quoted 5 lines · Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller ("brcm,cygnus-asiu-gpio"), 2) the chipCommonG GPIO controller ("brcm,cygnus-ccm-gpio"), and 3) the ALWAYS-ON GPIO controller ("brcm,cygnus-crmu-gpio")

trivia:

drivers/gpio/gpio-bcm-cygnus.c line 100

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c +5 -0
 
+static inline struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(
+		struct gpio_chip *gc)
+{
+	return container_of(gc, struct bcm_cygnus_gpio, gc);
+}

Probably all of these inlines can just be static.

The compiler does a pretty good job these days
of inlining where appropriate.

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c +7 -0
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+		struct irq_desc *desc)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int i, bit;
+

drivers/gpio/gpio-bcm-cygnus.c line 139

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c +10 -0
+	chained_irq_enter(chip, desc);
+
+	cygnus_gpio = irq_get_handler_data(irq);
+
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < cygnus_gpio->num_banks; i++) {
+		unsigned long val = readl(cygnus_gpio->base +
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+		if (val) {

This if (val) and indentation isn't really necessary

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line
drivers/gpio/gpio-bcm-cygnus.c +1 -0
+			for_each_set_bit(bit, &val, 32) {

for_each_set_bit will effectively do the if above.

32 bit only code?
otherwise isn't this endian unsafe?

Quoted 37 lines
drivers/gpio/gpio-bcm-cygnus.c +37 -0
+				unsigned pin = NGPIOS_PER_BANK * i + bit;
+				int child_irq =	bcm_cygnus_gpio_to_irq(
+						&cygnus_gpio->gc, pin);
+
+				/*
+				 * Clear the interrupt before invoking the
+				 * handler, so we do not leave any window
+				 */
+				writel(1 << bit,
+					cygnus_gpio->base +
+					(i * GPIO_BANK_SIZE) +
+					CYGNUS_GPIO_INT_CLR_OFFSET);
+
+				generic_handle_irq(child_irq);
+			}
+
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm_cygnus_gpio_irq_ack(struct irq_data *d)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int offset, shift;
+	u32 val;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_CLR_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	val = 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,

drivers/gpio/gpio-bcm-cygnus.c

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c +9 -0
+			offset, shift);
+}
 
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+	.name = "bcm-cygnus-gpio",
+	.irq_ack = bcm_cygnus_gpio_irq_ack,
+	.irq_mask = bcm_cygnus_gpio_irq_mask,
+	.irq_unmask = bcm_cygnus_gpio_irq_unmask,
+	.irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};

const?

drivers/gpio/gpio-bcm-cygnus.c line 452

Quoted 5 lines
drivers/gpio/gpio-bcm-cygnus.c +5 -0
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+	.map = bcm_cygnus_gpio_irq_map,
+	.unmap = bcm_cygnus_gpio_irq_unmap,
+	.xlate = irq_domain_xlate_twocell,
+};

const here too?

drivers/gpio/gpio-bcm-cygnus.c line 459

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c +6 -0
+#ifdef CONFIG_OF_GPIO
+static void bcm_cygnus_gpio_set_pull(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio, enum gpio_pull pull)
+{
+	unsigned int offset, shift;
+	u32 val, up;

bool up; ?

Quoted 5 lines
drivers/gpio/gpio-bcm-cygnus.c +5 -0
+	unsigned long flags;
+
+	switch (pull) {
+	case GPIO_PULL_NONE:
+		return;

drivers/gpio/gpio-bcm-cygnus.c line 474

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c +10 -0
+	case GPIO_PULL_UP:
+		up = 1;
+		break;
+	case GPIO_PULL_DOWN:
+		up = 0;
+		break;
+	case GPIO_PULL_INVALID:
+	default:
+		return;
+	}

Maybe more sensible to group GPIO_PULL_NONE with GPIO_PULL_INVALID

drivers/gpio/gpio-bcm-cygnus.c line 579

Quoted 2 lines
drivers/gpio/gpio-bcm-cygnus.c +2 -0
+static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
+{

[]

drivers/gpio/gpio-bcm-cygnus.c line 610

Quoted 3 lines
drivers/gpio/gpio-bcm-cygnus.c +3 -0
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "unable to get I/O resource");

missing newline

author maintainer
#79 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Thanks for your review, Joe:

Quoted 19 lines · Joe Perches
Quoted 5 lines · Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller ("brcm,cygnus-asiu-gpio"), 2) the chipCommonG GPIO controller ("brcm,cygnus-ccm-gpio"), and 3) the ALWAYS-ON GPIO controller ("brcm,cygnus-crmu-gpio")

trivia:

drivers/gpio/gpio-bcm-cygnus.c line 100

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c +5 -0
 
+static inline struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(
+		struct gpio_chip *gc)
+{
+	return container_of(gc, struct bcm_cygnus_gpio, gc);
+}

Probably all of these inlines can just be static.

Quoted 2 lines

The compiler does a pretty good job these days
of inlining where appropriate.

Okay I can remove all inlines.

Quoted 13 lines
Quoted 13 lines
drivers/gpio/gpio-bcm-cygnus.c +11 -0
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+		struct irq_desc *desc)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(chip, desc);
+
+	cygnus_gpio = irq_get_handler_data(irq);
+
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 139

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c +6 -0
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < cygnus_gpio->num_banks; i++) {
+		unsigned long val = readl(cygnus_gpio->base +
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+		if (val) {

This if (val) and indentation isn't really necessary

Note for_each_set_bit in this case iterates 32 times searching for bits that are set. By having the if (val) check here, it can potentially save some of such processing in the ISR. I agree with you that it introduces one extra indent here but I think it's required.

Quoted 7 lines

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line
drivers/gpio/gpio-bcm-cygnus.c +1 -0
+			for_each_set_bit(bit, &val, 32) {

for_each_set_bit will effectively do the if above.

32 bit only code?
otherwise isn't this endian unsafe?

Will change 'unsigned long val' to 'u32 val'.

Quoted 40 lines
Quoted 39 lines
drivers/gpio/gpio-bcm-cygnus.c +39 -0
+				unsigned pin = NGPIOS_PER_BANK * i + bit;
+				int child_irq =	bcm_cygnus_gpio_to_irq(
+						&cygnus_gpio->gc, pin);
+
+				/*
+				 * Clear the interrupt before invoking the
+				 * handler, so we do not leave any window
+				 */
+				writel(1 << bit,
+					cygnus_gpio->base +
+					(i * GPIO_BANK_SIZE) +
+					CYGNUS_GPIO_INT_CLR_OFFSET);
+
+				generic_handle_irq(child_irq);
+			}
+
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm_cygnus_gpio_irq_ack(struct irq_data *d)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int offset, shift;
+	u32 val;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_CLR_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	val = 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+}
Quoted 10 lines

drivers/gpio/gpio-bcm-cygnus.c line 309

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c +7 -0
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+	.name = "bcm-cygnus-gpio",
+	.irq_ack = bcm_cygnus_gpio_irq_ack,
+	.irq_mask = bcm_cygnus_gpio_irq_mask,
+	.irq_unmask = bcm_cygnus_gpio_irq_unmask,
+	.irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};

const?

Sure, will add const to bcm_cygnus_gpio_irq_chip

Quoted 8 lines

drivers/gpio/gpio-bcm-cygnus.c line 452

Quoted 5 lines
drivers/gpio/gpio-bcm-cygnus.c +5 -0
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+	.map = bcm_cygnus_gpio_irq_map,
+	.unmap = bcm_cygnus_gpio_irq_unmap,
+	.xlate = irq_domain_xlate_twocell,
+};

const here too?

Yes, will make bcm_cygnus_irq_ops const.

Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 459

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c +6 -0
+#ifdef CONFIG_OF_GPIO
+static void bcm_cygnus_gpio_set_pull(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio, enum gpio_pull pull)
+{
+	unsigned int offset, shift;
+	u32 val, up;

bool up; ?

Okay I'll change the name from 'up' to 'pullup' to make it more readable.

Quoted 9 lines
Quoted 9 lines
drivers/gpio/gpio-bcm-cygnus.c +9 -0
+	unsigned long flags;
+
+	switch (pull) {
+	case GPIO_PULL_NONE:
+		return;
+	case GPIO_PULL_UP:
+		up = 1;
+		break;
+	case GPIO_PULL_DOWN:
Quoted 10 lines

drivers/gpio/gpio-bcm-cygnus.c line 474

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c +6 -0
+		up = 0;
+		break;
+	case GPIO_PULL_INVALID:
+	default:
+		return;
+	}

Maybe more sensible to group GPIO_PULL_NONE with GPIO_PULL_INVALID

Good suggestion, will do the following:

case GPIO_PULL_NONE:
case GPIO_PULL_INVALID:
default:

        return;
Quoted 10 lines

drivers/gpio/gpio-bcm-cygnus.c line 579

Quoted 2 lines
drivers/gpio/gpio-bcm-cygnus.c +2 -0
+static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
+{

[]

drivers/gpio/gpio-bcm-cygnus.c line 610

Quoted 3 lines
drivers/gpio/gpio-bcm-cygnus.c +3 -0
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "unable to get I/O resource");

missing newline

Right, will fix it with dev_err(&pdev->dev, "unable to get I/O resource\n");

#80 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Quoted 23 lines · Ray Jui
Quoted 12 lines · Joe Perches
Quoted 11 lines · Ray Jui
drivers/gpio/gpio-bcm-cygnus.c new file +11 -0
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+		struct irq_desc *desc)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(chip, desc);
+
+	cygnus_gpio = irq_get_handler_data(irq);
+
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 139

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < cygnus_gpio->num_banks; i++) {
+		unsigned long val = readl(cygnus_gpio->base +
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+		if (val) {

This if (val) and indentation isn't really necessary

Quoted 2 lines

Note for_each_set_bit in this case iterates 32 times searching for bits that are set.

No it doesn't.

#define for_each_set_bit(bit, addr, size) \
	for ((bit) = find_first_bit((addr), (size));		\
	     (bit) < (size);					\
	     (bit) = find_next_bit((addr), (size), (bit) + 1))

find_first_bit:

* Returns the bit number of the first set bit.
* If no bits are set, returns @size.

Quoted 8 lines

By having the if (val) check here, it can potentially save
some of such processing in the ISR. I agree with you that it introduces one extra indent here but I think it's required.

Quoted 4 lines

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line
drivers/gpio/gpio-bcm-cygnus.c new file +1 -0
+			for_each_set_bit(bit, &val, 32) {

for_each_set_bit will effectively do the if above.

Quoted 5 lines
Quoted 3 lines

32 bit only code?
otherwise isn't this endian unsafe?

Will change 'unsigned long val' to 'u32 val'.

All the bit operations only work on long *

author maintainer
#81 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Quoted 34 lines · Joe Perches
Quoted 23 lines · Ray Jui
Quoted 12 lines · Joe Perches
Quoted 11 lines · Ray Jui
drivers/gpio/gpio-bcm-cygnus.c new file +11 -0
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+		struct irq_desc *desc)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(chip, desc);
+
+	cygnus_gpio = irq_get_handler_data(irq);
+
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 139

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < cygnus_gpio->num_banks; i++) {
+		unsigned long val = readl(cygnus_gpio->base +
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+		if (val) {

This if (val) and indentation isn't really necessary

Quoted 2 lines

Note for_each_set_bit in this case iterates 32 times searching for bits that are set.

No it doesn't.

#define for_each_set_bit(bit, addr, size) \
	for ((bit) = find_first_bit((addr), (size));		\
	     (bit) < (size);					\
	     (bit) = find_next_bit((addr), (size), (bit) + 1))
Quoted 5 lines

find_first_bit:

* Returns the bit number of the first set bit.
* If no bits are set, returns @size.

You are right. I reviewed for_each_set_bit but didn't notice find_next_bit may simply return 32 in our case without doing any iterative processing. I will get rid of the redundant if (val) check below.

Quoted 8 lines
Quoted 8 lines

By having the if (val) check here, it can potentially save
some of such processing in the ISR. I agree with you that it introduces one extra indent here but I think it's required.

Quoted 4 lines

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line
drivers/gpio/gpio-bcm-cygnus.c new file +1 -0
+			for_each_set_bit(bit, &val, 32) {

for_each_set_bit will effectively do the if above.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

32 bit only code?
otherwise isn't this endian unsafe?

Will change 'unsigned long val' to 'u32 val'.

All the bit operations only work on long *

Actually, by reviewing the code more deeply, I'm not sure why using for_each_set_bit here is 'endian unsafe'. Isn't that already taken care of by macros in bitops.h? Sorry if I'm still missing something here...

#82 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Quoted 5 lines · Ray Jui
Quoted 4 lines · Joe Perches
Quoted 3 lines · Ray Jui
Quoted 2 lines · Joe Perches

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line · Ray Jui
drivers/gpio/gpio-bcm-cygnus.c new file +1 -0
+			for_each_set_bit(bit, &val, 32) {

[]

Quoted 2 lines

Actually, by reviewing the code more deeply, I'm not sure why using for_each_set_bit here is 'endian unsafe'.

It's not. The 32 confused me as it was long
and sizeof(long) isn't necessarily 32.

Maybe the 32 should be a #define

author maintainer
#83 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

Quoted 7 lines · Joe Perches
Quoted 5 lines · Ray Jui
Quoted 4 lines · Joe Perches
Quoted 3 lines · Ray Jui
Quoted 2 lines · Joe Perches

drivers/gpio/gpio-bcm-cygnus.c line 140

Quoted 1 line · Ray Jui
drivers/gpio/gpio-bcm-cygnus.c new file +1 -0
+			for_each_set_bit(bit, &val, 32) {

[]

Quoted 10 lines
Quoted 2 lines

Actually, by reviewing the code more deeply, I'm not sure why using for_each_set_bit here is 'endian unsafe'.

It's not. The 32 confused me as it was long
and sizeof(long) isn't necessarily 32.

Maybe the 32 should be a #define

Okay, to improve readability, I will change 32 to NGPIOS_PER_BANK.

author maintainer
#84 lore raw

Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver

In reply to Ray Jui
Quoted 23 lines · Ray Jui
Quoted 10 lines

drivers/gpio/gpio-bcm-cygnus.c line 309

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c new file +7 -0
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+    .name = "bcm-cygnus-gpio",
+    .irq_ack = bcm_cygnus_gpio_irq_ack,
+    .irq_mask = bcm_cygnus_gpio_irq_mask,
+    .irq_unmask = bcm_cygnus_gpio_irq_unmask,
+    .irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};

const?

Sure, will add const to bcm_cygnus_gpio_irq_chip

Quoted 8 lines

drivers/gpio/gpio-bcm-cygnus.c line 452

Quoted 5 lines
drivers/gpio/gpio-bcm-cygnus.c new file +5 -0
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+    .map = bcm_cygnus_gpio_irq_map,
+    .unmap = bcm_cygnus_gpio_irq_unmap,
+    .xlate = irq_domain_xlate_twocell,
+};

const here too?

Quoted 2 lines

Yes, will make bcm_cygnus_irq_ops const.

Actually, I cannot make them const here. Note they are passed into other APIs which can potentially modifies their values internally.

drivers/gpio/gpio-bcm-cygnus.c: In function ?bcm_cygnus_gpio_irq_map?:
drivers/gpio/gpio-bcm-cygnus.c:430:4: warning: passing argument 2 of ?irq_set_chip_and_handler? discards ?const? qualifier from pointer target type [enabled by default]

     handle_simple_irq);
     ^

In file included from drivers/gpio/gpio-bcm-cygnus.c:17:0:
include/linux/irq.h:461:20: note: expected ?struct irq_chip *? but argument is of type ?const struct irq_chip *?
static inline void irq_set_chip_and_handler(unsigned int irq, struct
irq_chip *chip,

                     ^

drivers/gpio/gpio-bcm-cygnus.c: In function ?bcm_cygnus_gpio_probe?:
drivers/gpio/gpio-bcm-cygnus.c:679:5: warning: passing argument 2 of ?irq_set_chip_and_handler? discards ?const? qualifier from pointer target type [enabled by default]

      handle_simple_irq);
      ^

In file included from drivers/gpio/gpio-bcm-cygnus.c:17:0:
include/linux/irq.h:461:20: note: expected ?struct irq_chip *? but argument is of type ?const struct irq_chip *?
static inline void irq_set_chip_and_handler(unsigned int irq, struct
irq_chip *chip,

author maintainer
#85 lore raw

PATCH 3/5 ARM: mach-bcm: Enable GPIO support for Cygnus

In reply to Ray Jui

Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..5066d5d 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select GPIO_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#86 lore raw

PATCH 4/5 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +30 -0
index 5126f9e..c7587c1 100644
@@ -54,6 +54,36 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-ccm-gpio";
+		reg = <0x1800a000 0x50>,
+			<0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#87 lore raw

PATCH 5/5 MAINTAINERS: Entry for Cygnus GPIO driver

In reply to Ray Jui
---
 MAINTAINERS |    7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index 7b712d8..5d67204 100644
@@ -2174,6 +2174,13 @@ N:	bcm9583*
 N:	bcm583*
 N:	bcm113*
 
+BROADCOM CYGNUS GPIO DRIVER
+M:	Ray Jui <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/gpio/gpio-bcm-cygnus.c
+F:	Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:	Ray Jui <[email protected]>
 L:	[email protected]
Signature · 1 line
1.7.9.5
author maintainer
#88 lore raw

v2 cover Add gpio support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  712 ++++++++++++++++++++
 7 files changed, 847 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#89 lore raw

PATCH v2 1/5 gpio: Cygnus: define Broadcom Cygnus GPIO binding

Document the GPIO device tree binding for Broadcom Cygnus SoC

---
 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 ++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +85 -0
new file mode 100644
index 0000000..24a1513
@@ -0,0 +1,85 @@
+Broadcom Cygnus GPIO Controller
+
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+GPIO controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller. Not all Cygnus GPIO
+interfaces support interrupt, e.g., the CRMU GPIO controller does not have its
+interrupt routed to the main processor's GIC
+
+- interrupts:
+    The interrupt outputs from the GPIO controller.
+
+- no-interrupt:
+    Specifies that the GPIO interface does not support interrupt
+
+Example:
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
+	/*
+	 * Touchscreen that uses the ASIU GPIO 100, with internal pull-up
+	 * enabled
+	 */
+	tsc {
+		...
+		...
+		gpio-event = <&gpio_asiu 100 0x10000>;
+	};
+
+	/* Bluetooth that uses the CRMU GPIO 2, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_crmu 2 1>
+	}
Signature · 1 line
1.7.9.5
#90 lore raw

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 9

Quoted 7 lines · On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +7 -0
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller

How different are these? If they are almost the same, would it be better to use the same compatible string for all of them and describe the differences in extra properties?

If they are rather different, maybe you should have a separate binding and driver for each?

Arnd

author maintainer
#91 lore raw

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Quoted 13 lines · Arnd Bergmann

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 9

Quoted 7 lines · On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +7 -0
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller

How different are these? If they are almost the same, would it be better to use the same compatible string for all of them and describe the differences in extra properties?

Quoted 5 lines

If they are rather different, maybe you should have a separate binding and driver for each?

Arnd

They are quite similar with the following minor differences:
1) ChipcommonG GPIO controller uses a separate register block (0x0301d164) to control drive stregnth
2) Cannot control drive strength for the CMRU GPIO
3) CRMU GPIO controller's interrupt is not connected to GIC of A9. Currently that's taken care of by using a "no-interrupt" device tree property

I can change to use the common compatible string "brcm,cygnus-gpio". With an introduction of property "no-drv-stregnth" which should be set for CRMU GPIO controller. For ChipcommonG GPIO, it will have a second register block defined, so we'll know to use that second register block for drive strength configuration. For the rest, we assume normal drive strength configuration (i.e., ASIU in our case).

Looking at this again, it looks like the "no-interrupt" property is really redundant. For GPIO controller without interrupt connected to A9, we can simply leave its interrupt properties not defined. I'll get rid of it along with the above changes.

#92 lore raw

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Quoted 18 lines · On Monday 08 December 2014 08:55:20 Ray Jui wrote:
Quoted 16 lines · Arnd Bergmann

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 9

Quoted 7 lines · On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +7 -0
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller

How different are these? If they are almost the same, would it be better to use the same compatible string for all of them and describe the differences in extra properties?

If they are rather different, maybe you should have a separate binding and driver for each?

Quoted 6 lines
Quoted 2 lines

Arnd

They are quite similar with the following minor differences:
1) ChipcommonG GPIO controller uses a separate register block (0x0301d164) to control drive stregnth
2) Cannot control drive strength for the CMRU GPIO

This can be deducted from having one or two register blocks I assume.

Quoted 3 lines

3) CRMU GPIO controller's interrupt is not connected to GIC of A9. Currently that's taken care of by using a "no-interrupt" device tree property

No need for this property even, just see if there is an "interrupts" property or not.

Quoted 3 lines

I can change to use the common compatible string "brcm,cygnus-gpio". With an introduction of property "no-drv-stregnth" which should be set for CRMU GPIO controller.

Ok.

Quoted 4 lines

For ChipcommonG GPIO, it will have a second
register block defined, so we'll know to use that second register block for drive strength configuration. For the rest, we assume normal drive strength configuration (i.e., ASIU in our case).

Maybe see if something older than cygnus was already using a compatible gpio controller and then use the name of that.

Quoted 4 lines

Looking at this again, it looks like the "no-interrupt" property is really redundant. For GPIO controller without interrupt connected to A9, we can simply leave its interrupt properties not defined. I'll get rid of it along with the above changes.

Right.

Arnd

author maintainer
#93 lore raw

PATCH v2 2/5 gpio: Cygnus: add GPIO driver

In reply to Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller ("brcm,cygnus-asiu-gpio"), 2) the chipCommonG GPIO controller ("brcm,cygnus-ccm-gpio"), and 3) the ALWAYS-ON GPIO controller ("brcm,cygnus-crmu-gpio")

---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  712 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 724 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
 	    8 bits:	74244 (Input), 74273 (Output)
 	    16 bits:	741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+	bool "Broadcom Cygnus GPIO support"
+	depends on ARCH_BCM_CYGNUS && OF_GPIO
+	help
+	  Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+	  supported by this driver
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)	+= gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +712 -0

712 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#94 lore raw

PATCH v2 3/5 ARM: mach-bcm: Enable GPIO support for Cygnus

In reply to Ray Jui

Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..5066d5d 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select GPIO_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#95 lore raw

PATCH v2 4/5 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +30 -0
index 5126f9e..c7587c1 100644
@@ -54,6 +54,36 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-ccm-gpio";
+		reg = <0x1800a000 0x50>,
+			<0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#96 lore raw

PATCH v2 5/5 MAINTAINERS: Entry for Cygnus GPIO driver

In reply to Ray Jui
---
 MAINTAINERS |    7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index e6bff3a..8473422 100644
@@ -2202,6 +2202,13 @@ N:	bcm9583*
 N:	bcm583*
 N:	bcm113*
 
+BROADCOM CYGNUS GPIO DRIVER
+M:	Ray Jui <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/gpio/gpio-bcm-cygnus.c
+F:	Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:	Ray Jui <[email protected]>
 L:	[email protected]
Signature · 1 line
1.7.9.5
author maintainer
#97 lore raw

v3 cover Add gpio support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Changes from v2:
- Consolidate different compatible IDs into "brcm,cygnus-gpio"
- Get rid of redundant "no-interrupt" property

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   82 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  705 ++++++++++++++++++++
 7 files changed, 837 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#98 lore raw

v2 cover Add gpio support to Broadcom Cygnus SoC

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  712 ++++++++++++++++++++
 7 files changed, 847 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#99 lore raw

Re: [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC

Sorry. Please ignore this particular cover letter. It accidentally got sent along with other v3 patches.

Quoted 28 lines · Ray Jui

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

  .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 +++
  MAINTAINERS                                        |    7 +
  arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
  arch/arm/mach-bcm/Kconfig                          |    1 +
  drivers/gpio/Kconfig                               |   11 +
  drivers/gpio/Makefile                              |    1 +
  drivers/gpio/gpio-bcm-cygnus.c                     |  712 ++++++++++++++++++++
  7 files changed, 847 insertions(+)

create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt create mode 100644 drivers/gpio/gpio-bcm-cygnus.c

author maintainer
#100 lore raw

PATCH v3 1/5 gpio: Cygnus: define Broadcom Cygnus GPIO binding

In reply to Ray Jui

Document the GPIO device tree binding for Broadcom Cygnus SoC

---
 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   82 ++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +82 -0
new file mode 100644
index 0000000..c477271
@@ -0,0 +1,82 @@
+Broadcom Cygnus GPIO Controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-gpio"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+GPIO controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller. Not all Cygnus GPIO
+interfaces support interrupt, e.g., the CRMU GPIO controller does not have its
+interrupt routed to the main processor's GIC
+
+- interrupts:
+    The interrupt outputs from the GPIO controller.
+
+- no-drv-stregnth:
+    Specifies the GPIO controller does not support drive strength configuration
+
+Example:
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-drv-stregnth;
+	};
+
+	/*
+	 * Touchscreen that uses the ASIU GPIO 100, with internal pull-up
+	 * enabled
+	 */
+	tsc {
+		...
+		...
+		gpio-event = <&gpio_asiu 100 0x10000>;
+	};
+
+	/* Bluetooth that uses the CRMU GPIO 2, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_crmu 2 1>
+	}
Signature · 1 line
1.7.9.5
#101 lore raw

Re: [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 46

Quoted 5 lines · On Monday 08 December 2014 10:47:44 Ray Jui wrote:
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +4 -0
+
+- no-drv-stregnth:
+    Specifies the GPIO controller does not support drive strength configuration
+

Typo:

strength, not stregnth

Otherwise looks good.

Arnd

author maintainer
#102 lore raw

Re: [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

Quoted 9 lines · Arnd Bergmann

Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt line 46

Quoted 5 lines · On Monday 08 December 2014 10:47:44 Ray Jui wrote:
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +4 -0
+
+- no-drv-stregnth:
+    Specifies the GPIO controller does not support drive strength configuration
+

Typo:

Quoted 6 lines

strength, not stregnth

Otherwise looks good.

Arnd

Right...Let me fix that. Also noticed the following in the device tree binding example that needs to be fixed:

	gpio_crmu: gpio at 03024800 {

		compatible = "brcm,cygnus-crmu-gpio";
The above line needs to be fixed with:
		compatible = "brcm,cygnus-gpio";

		reg = <0x03024800 0x50>;
		ngpios = <6>;
		#gpio-cells = <2>;
		gpio-controller;
		no-drv-stregnth;
	};
author maintainer
#103 lore raw

PATCH v3 2/5 gpio: Cygnus: add GPIO driver

In reply to Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller, 2) the chipCommonG GPIO controller, and 3) the ALWAYS-ON GPIO controller

---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  705 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 717 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
 	    8 bits:	74244 (Input), 74273 (Output)
 	    16 bits:	741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+	bool "Broadcom Cygnus GPIO support"
+	depends on ARCH_BCM_CYGNUS && OF_GPIO
+	help
+	  Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+	  supported by this driver
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)	+= gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +705 -0

705 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#104 lore raw

PATCH v3 3/5 ARM: mach-bcm: Enable GPIO support for Cygnus

In reply to Ray Jui

Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..5066d5d 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select GPIO_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#105 lore raw

PATCH v3 4/5 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +30 -0
index 5126f9e..48339d0 100644
@@ -54,6 +54,36 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+			<0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-drv-stregnth;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#106 lore raw

PATCH v3 5/5 MAINTAINERS: Entry for Cygnus GPIO driver

In reply to Ray Jui
---
 MAINTAINERS |    7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index e6bff3a..8473422 100644
@@ -2202,6 +2202,13 @@ N:	bcm9583*
 N:	bcm583*
 N:	bcm113*
 
+BROADCOM CYGNUS GPIO DRIVER
+M:	Ray Jui <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/gpio/gpio-bcm-cygnus.c
+F:	Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:	Ray Jui <[email protected]>
 L:	[email protected]
Signature · 1 line
1.7.9.5
author maintainer
#107 lore raw

v4 cover Add gpio support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC. Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the same Cygnus GPIO driver

Changes from v3:
- Fix dt property tpyo
- Fix incorrect GPIO compatible ID in device tree binding document example

Changes from v2:
- Consolidate different compatible IDs into "brcm,cygnus-gpio"
- Get rid of redundant "no-interrupt" property

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (5):
gpio: Cygnus: define Broadcom Cygnus GPIO binding
gpio: Cygnus: add GPIO driver
ARM: mach-bcm: Enable GPIO support for Cygnus
ARM: dts: enable GPIO for Broadcom Cygnus
MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   82 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  705 ++++++++++++++++++++
 7 files changed, 837 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
Signature · 1 line
1.7.9.5
author maintainer
#108 lore raw

PATCH v4 1/5 gpio: Cygnus: define Broadcom Cygnus GPIO binding

Document the GPIO device tree binding for Broadcom Cygnus SoC

---
 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   82 ++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt new file +82 -0
new file mode 100644
index 0000000..dca322a
@@ -0,0 +1,82 @@
+Broadcom Cygnus GPIO Controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-gpio"
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+GPIO controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller. Not all Cygnus GPIO
+interfaces support interrupt, e.g., the CRMU GPIO controller does not have its
+interrupt routed to the main processor's GIC
+
+- interrupts:
+    The interrupt outputs from the GPIO controller.
+
+- no-drv-strength:
+    Specifies the GPIO controller does not support drive strength configuration
+
+Example:
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-drv-strength;
+	};
+
+	/*
+	 * Touchscreen that uses the ASIU GPIO 100, with internal pull-up
+	 * enabled
+	 */
+	tsc {
+		...
+		...
+		gpio-event = <&gpio_asiu 100 0x10000>;
+	};
+
+	/* Bluetooth that uses the CRMU GPIO 2, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_crmu 2 1>
+	}
Signature · 1 line
1.7.9.5
author maintainer
#109 lore raw

PATCH v4 2/5 gpio: Cygnus: add GPIO driver

In reply to Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller, 2) the chipCommonG GPIO controller, and 3) the ALWAYS-ON GPIO controller

---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  705 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 717 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
 	    8 bits:	74244 (Input), 74273 (Output)
 	    16 bits:	741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+	bool "Broadcom Cygnus GPIO support"
+	depends on ARCH_BCM_CYGNUS && OF_GPIO
+	help
+	  Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+	  supported by this driver
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)	+= gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +705 -0

705 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#110 lore raw

Re: [PATCH v4 2/5] gpio: Cygnus: add GPIO driver

Quoted 137 lines · Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller, 2) the chipCommonG GPIO controller, and 3) the ALWAYS-ON GPIO controller

---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  705 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 717 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
            8 bits:     74244 (Input), 74273 (Output)
            16 bits:    741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+       bool "Broadcom Cygnus GPIO support"
+       depends on ARCH_BCM_CYGNUS && OF_GPIO
+       help
+         Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+         The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+         GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+         the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+         supported by this driver
+
 config GPIO_CLPS711X
        tristate "CLPS711X GPIO support"
        depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)    += gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)     += gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)     += gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)     += gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)  += gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)       += gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)    += gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +84 -0
new file mode 100644
index 0000000..4fd9b73
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM GPIO */
+#define CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BIT(pin) ((pin) % NGPIOS_PER_BANK)
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define GPIO_FLAG_BIT_MASK           0xffff
+#define GPIO_PULL_BIT_SHIFT          16
+#define GPIO_PULL_BIT_MASK           0x3
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * For GPIO internal pull up/down registers
+ */
+enum gpio_pull {
+       GPIO_PULL_NONE = 0,
+       GPIO_PULL_UP,
+       GPIO_PULL_DOWN,
+       GPIO_PULL_INVALID,
+};
+
+/*
+ * GPIO drive strength
+ */
+enum gpio_drv_strength {
+       GPIO_DRV_STRENGTH_2MA = 0,
+       GPIO_DRV_STRENGTH_4MA,
+       GPIO_DRV_STRENGTH_6MA,
+       GPIO_DRV_STRENGTH_8MA,
+       GPIO_DRV_STRENGTH_10MA,
+       GPIO_DRV_STRENGTH_12MA,
+       GPIO_DRV_STRENGTH_14MA,
+       GPIO_DRV_STRENGTH_16MA,
+       GPIO_DRV_STRENGTH_INVALID,
+};
+
+struct bcm_cygnus_gpio {
+       struct device *dev;

drivers/gpio/gpio-bcm-cygnus.c line 94

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       void __iomem *base;
+       void __iomem *io_ctrl;
+       spinlock_t lock;
+       struct gpio_chip gc;
+       unsigned num_banks;
+       int irq;
+       struct irq_domain *irq_domain;
+};
+
+static unsigned int gpio_base_index;

Nope. What happens if there are other GPIO controllers with conflicting base GPIOs? I guess this adds more weight to that "linux,gpio-base" property I mentioned in
http://www.spinics.net/lists/arm-kernel/msg384847.html .

The best solution would be for users of the GPIOs provided by this driver to not rely on GPIO numbers at all, and exclusively use the gpiod interface. Is that an option for you?

Quoted 8 lines
drivers/gpio/gpio-bcm-cygnus.c new file +8 -0
+
+static struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(struct gpio_chip *gc)
+{
+       return container_of(gc, struct bcm_cygnus_gpio, gc);
+}
+
+static int bcm_cygnus_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{

drivers/gpio/gpio-bcm-cygnus.c line 112

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+
+       return irq_find_mapping(cygnus_gpio->irq_domain, offset);
+}
+
+static unsigned int __gpio_reg_offset(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio)
+{
+       return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}

The cygnus_gpio argument in this function is unused. The compiler is supposed to signal such issues. Have you looked at your compiler output?

drivers/gpio/gpio-bcm-cygnus.c line 118

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio)
+{
+       return GPIO_BIT(gpio);
+}

Same here. Also they are so simple that macros would be more adequate here I believe:

#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + reg) #define CYGNUS_GPIO_SHIFT(pin) (pin % NGPIOS_PER_BANK) /* and remove the GPIO_BIT macro */

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c new file +7 -0
+
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+               struct irq_desc *desc)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       int i, bit;

drivers/gpio/gpio-bcm-cygnus.c line 135

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+
+       chained_irq_enter(chip, desc);
+
+       cygnus_gpio = irq_get_handler_data(irq);
+
+       /* go through the entire GPIO banks and handle all interrupts */
+       for (i = 0; i < cygnus_gpio->num_banks; i++) {
+               unsigned long val = readl(cygnus_gpio->base +
+                               (i * GPIO_BANK_SIZE) +
+                               CYGNUS_GPIO_INT_MSTAT_OFFSET);

Can you add cygnus_readl() and cygnus_writel() functions to avoid explicitly doing this operation on cygnus_gpio->base every time? It would be clearer and less error-prone.

Quoted 23 lines
drivers/gpio/gpio-bcm-cygnus.c new file +23 -0
+
+               for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
+                       unsigned pin = NGPIOS_PER_BANK * i + bit;
+                       int child_irq =
+                               bcm_cygnus_gpio_to_irq(&cygnus_gpio->gc, pin);
+
+                       /*
+                        * Clear the interrupt before invoking the
+                        * handler, so we do not leave any window
+                        */
+                       writel(1 << bit,
+                               cygnus_gpio->base + (i * GPIO_BANK_SIZE) +
+                               CYGNUS_GPIO_INT_CLR_OFFSET);
+
+                       generic_handle_irq(child_irq);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void bcm_cygnus_gpio_irq_ack(struct irq_data *d)
+{

drivers/gpio/gpio-bcm-cygnus.c line 168

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_CLR_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = 1 << shift;

val = BIT(shift);

Quoted 12 lines
drivers/gpio/gpio-bcm-cygnus.c new file +12 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_mask(struct irq_data *d)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;

drivers/gpio/gpio-bcm-cygnus.c line 190

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_MSK_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);

val &= ~BIT(shift);

Quoted 14 lines
drivers/gpio/gpio-bcm-cygnus.c new file +14 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_unmask(struct irq_data *d)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;

drivers/gpio/gpio-bcm-cygnus.c line 214

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_MSK_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;

val |= BIT(shift);

Same remark everywhere it applies in this file.

drivers/gpio/gpio-bcm-cygnus.c line 197

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c new file +7 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}

It seems like you can factorize bcm_cygnus_gpio_irq_mask() and bcm_cygnus_gpio_irq_unmask() into one function which prototype would be, say:

static void bcm_cygnus_gpio_irq_set_mask(struct irq_data *d, int mask);

which would set of clear the bit according to the value of mask. Then your two mask/unmask functions would just need to call this one, reducing the amount of redundant code.

Also I noticed that this driver has lots of readl()/twiddle bit/writel() sequences. Maybe it would make sense to have a cygnus_set_bit(chip, reg, gpio, set) function to factorize this:

void cygnus_set_bit(chip, reg, gpio, set)

{
    unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
    unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
    u32 val;

    val = cygnus_readl(chip, offset);
    if (set)
        val |= BIT(shift);
    else
        val &= ~BIT(shift);
    cygnus_writel(chip, offset, val);
}
Quoted 59 lines
drivers/gpio/gpio-bcm-cygnus.c new file +59 -0
+
+static int bcm_cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int int_type, dual_edge, edge_lvl;
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       switch (type & IRQ_TYPE_SENSE_MASK) {
+       case IRQ_TYPE_EDGE_RISING:
+               int_type = 0;
+               dual_edge = 0;
+               edge_lvl = 1;
+               break;
+
+       case IRQ_TYPE_EDGE_FALLING:
+               int_type = 0;
+               dual_edge = 0;
+               edge_lvl = 0;
+               break;
+
+       case IRQ_TYPE_EDGE_BOTH:
+               int_type = 0;
+               dual_edge = 1;
+               edge_lvl = 0;
+               break;
+
+       case IRQ_TYPE_LEVEL_HIGH:
+               int_type = 1;
+               dual_edge = 0;
+               edge_lvl = 1;
+               break;
+
+       case IRQ_TYPE_LEVEL_LOW:
+               int_type = 1;
+               dual_edge = 0;
+               edge_lvl = 0;
+               break;
+
+       default:
+               dev_err(cygnus_gpio->dev, "invalid GPIO irq type 0x%x\n", type);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_IN_TYPE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       val |= int_type << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_DE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);

drivers/gpio/gpio-bcm-cygnus.c line 290

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       val &= ~(1 << shift);
+       val |= dual_edge << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_EDGE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       val |= edge_lvl << shift;
+       writel(val, cygnus_gpio->base + offset);

With the functions/macros suggested above I think you could change the 3 blocks above into something like:

cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_IN_TYPE_OFFSET, gpio, int_type); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio, edge_lvl);

Quoted 60 lines
drivers/gpio/gpio-bcm-cygnus.c new file +60 -0
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       return 0;
+}
+
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+       .name = "bcm-cygnus-gpio",
+       .irq_ack = bcm_cygnus_gpio_irq_ack,
+       .irq_mask = bcm_cygnus_gpio_irq_mask,
+       .irq_unmask = bcm_cygnus_gpio_irq_unmask,
+       .irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};
+
+static int bcm_cygnus_gpio_direction_input(struct gpio_chip *gc,
+               unsigned gpio)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_OUT_EN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+
+       return 0;
+}
+
+static int bcm_cygnus_gpio_direction_output(struct gpio_chip *gc,
+               unsigned gpio, int value)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_OUT_EN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);

drivers/gpio/gpio-bcm-cygnus.c line 360

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_OUT_OFFSET;
+
+       val = readl(cygnus_gpio->base + offset);
+       if (value)
+               val |= 1 << shift;
+       else
+               val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);

And here you would have:

cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);

Many other sites in this file could be simplified this way.

Quoted 212 lines
drivers/gpio/gpio-bcm-cygnus.c new file +212 -0
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev,
+               "gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+               gpio, offset, shift, val);
+
+       return 0;
+}
+
+static void bcm_cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio,
+               int value)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_OUT_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       if (value)
+               val |= 1 << shift;
+       else
+               val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev,
+               "gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+               gpio, offset, shift, val);
+}
+
+static int bcm_cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_IN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = readl(cygnus_gpio->base + offset);
+       val = (val >> shift) & 1;
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u val:%u\n",
+                       gpio, offset, shift, val);
+
+       return val;
+}
+
+static struct lock_class_key gpio_lock_class;
+
+static int bcm_cygnus_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+                                irq_hw_number_t hwirq)
+{
+       int ret;
+
+       ret = irq_set_chip_data(irq, d->host_data);
+       if (ret < 0)
+               return ret;
+       irq_set_lockdep_class(irq, &gpio_lock_class);
+       irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+                       handle_simple_irq);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+static void bcm_cygnus_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+       irq_set_chip_and_handler(irq, NULL, NULL);
+       irq_set_chip_data(irq, NULL);
+}
+
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+       .map = bcm_cygnus_gpio_irq_map,
+       .unmap = bcm_cygnus_gpio_irq_unmap,
+       .xlate = irq_domain_xlate_twocell,
+};
+
+#ifdef CONFIG_OF_GPIO
+static void bcm_cygnus_gpio_set_pull(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio, enum gpio_pull pull)
+{
+       unsigned int offset, shift;
+       u32 val, pullup;
+       unsigned long flags;
+
+       switch (pull) {
+       case GPIO_PULL_UP:
+               pullup = 1;
+               break;
+       case GPIO_PULL_DOWN:
+               pullup = 0;
+               break;
+       case GPIO_PULL_NONE:
+       case GPIO_PULL_INVALID:
+       default:
+               return;
+       }
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       /* set pull up/down */
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_PAD_RES_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       if (pullup)
+               val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       /* enable pad */
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_RES_EN_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static void bcm_cygnus_gpio_set_strength(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio, enum gpio_drv_strength strength)
+{
+       struct device *dev = cygnus_gpio->dev;
+       void __iomem *base;
+       unsigned int i, offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       /* some GPIO controllers do not support drive strength configuration */
+       if (of_find_property(dev->of_node, "no-drv-strength", NULL))
+               return;
+
+       if (cygnus_gpio->io_ctrl) {
+               base = cygnus_gpio->io_ctrl;
+               offset = CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET;
+       } else {
+               base = cygnus_gpio->base;
+               offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+                       CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET;
+       }
+
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+               val = readl(base + offset);
+               val &= ~(1 << shift);
+               val |= ((strength >> i) & 0x1) << shift;
+               writel(val, base + offset);
+               offset += 4;
+       }
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static int bcm_cygnus_gpio_of_xlate(struct gpio_chip *gc,
+               const struct of_phandle_args *gpiospec, u32 *flags)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       enum gpio_pull pull;
+       enum gpio_drv_strength strength;
+
+       if (gc->of_gpio_n_cells < 2)
+               return -EINVAL;
+
+       if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+               return -EINVAL;
+
+       if (gpiospec->args[0] >= gc->ngpio)
+               return -EINVAL;
+
+       pull = (gpiospec->args[1] >> GPIO_PULL_BIT_SHIFT) & GPIO_PULL_BIT_MASK;
+       if (WARN_ON(pull >= GPIO_PULL_INVALID))
+               return -EINVAL;
+
+       strength = (gpiospec->args[1] >> GPIO_DRV_STRENGTH_BIT_SHIFT) &
+               GPIO_DRV_STRENGTH_BIT_MASK;
+
+       if (flags)
+               *flags = gpiospec->args[1] & GPIO_FLAG_BIT_MASK;
+
+       bcm_cygnus_gpio_set_pull(cygnus_gpio, gpiospec->args[0], pull);
+       bcm_cygnus_gpio_set_strength(cygnus_gpio, gpiospec->args[0], strength);
+
+       return gpiospec->args[0];
+}
+#endif
+
+static const struct of_device_id bcm_cygnus_gpio_of_match[] = {
+       { .compatible = "brcm,cygnus-gpio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, bcm_cygnus_gpio_of_match);
+
+static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+       struct resource *res;

drivers/gpio/gpio-bcm-cygnus.c line 582

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       struct bcm_cygnus_gpio *cygnus_gpio;
+       struct gpio_chip *gc;
+       u32 i, ngpios;
+       int ret;
+
+       match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+       if (!match) {
+               dev_err(&pdev->dev, "failed to find GPIO controller\n");
+               return -ENODEV;
+       }

Do you still need that block of code? match is never used in this function.

... well, I think you get the drill. Let's first start by factorizing as much code as can be to make this driver easier to read (I have a few leads, but I am sure there are other similar factorizations that can be made). Let's also get rid of this static gpio_base_index variable that effectively prevents any other GPIO driver from being used alongside with this one. If you really need to use global GPIO numbers, let's see if Linus agrees for that "linux,gpio-base" DT property that would certainly make many people happy.

author maintainer
#111 lore raw

Re: [PATCH v4 2/5] gpio: Cygnus: add GPIO driver

Quoted 154 lines · Alexandre Courbot
Quoted 137 lines · Ray Jui

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller, 2) the chipCommonG GPIO controller, and 3) the ALWAYS-ON GPIO controller

---
  drivers/gpio/Kconfig           |   11 +
  drivers/gpio/Makefile          |    1 +
  drivers/gpio/gpio-bcm-cygnus.c |  705 ++++++++++++++++++++++++++++++++++++++++
  3 files changed, 717 insertions(+)

create mode 100644 drivers/gpio/gpio-bcm-cygnus.c

drivers/gpio/Kconfig +11 -0
index 633ec21..3e3b0342 100644
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
             8 bits:     74244 (Input), 74273 (Output)
             16 bits:    741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+       bool "Broadcom Cygnus GPIO support"
+       depends on ARCH_BCM_CYGNUS && OF_GPIO
+       help
+         Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+         The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+         GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+         the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+         supported by this driver
+
  config GPIO_CLPS711X
         tristate "CLPS711X GPIO support"
         depends on ARCH_CLPS711X || COMPILE_TEST
drivers/gpio/Makefile +1 -0
index 81755f1..31eb7e0 100644
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)    += gpio-adp5520.o
  obj-$(CONFIG_GPIO_ADP5588)     += gpio-adp5588.o
  obj-$(CONFIG_GPIO_AMD8111)     += gpio-amd8111.o
  obj-$(CONFIG_GPIO_ARIZONA)     += gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)  += gpio-bcm-cygnus.o
  obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
  obj-$(CONFIG_GPIO_BT8XX)       += gpio-bt8xx.o
  obj-$(CONFIG_GPIO_CLPS711X)    += gpio-clps711x.o
drivers/gpio/gpio-bcm-cygnus.c new file +84 -0
new file mode 100644
index 0000000..4fd9b73
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM GPIO */
+#define CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BIT(pin) ((pin) % NGPIOS_PER_BANK)
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define GPIO_FLAG_BIT_MASK           0xffff
+#define GPIO_PULL_BIT_SHIFT          16
+#define GPIO_PULL_BIT_MASK           0x3
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * For GPIO internal pull up/down registers
+ */
+enum gpio_pull {
+       GPIO_PULL_NONE = 0,
+       GPIO_PULL_UP,
+       GPIO_PULL_DOWN,
+       GPIO_PULL_INVALID,
+};
+
+/*
+ * GPIO drive strength
+ */
+enum gpio_drv_strength {
+       GPIO_DRV_STRENGTH_2MA = 0,
+       GPIO_DRV_STRENGTH_4MA,
+       GPIO_DRV_STRENGTH_6MA,
+       GPIO_DRV_STRENGTH_8MA,
+       GPIO_DRV_STRENGTH_10MA,
+       GPIO_DRV_STRENGTH_12MA,
+       GPIO_DRV_STRENGTH_14MA,
+       GPIO_DRV_STRENGTH_16MA,
+       GPIO_DRV_STRENGTH_INVALID,
+};
+
+struct bcm_cygnus_gpio {
+       struct device *dev;

drivers/gpio/gpio-bcm-cygnus.c line 94

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       void __iomem *base;
+       void __iomem *io_ctrl;
+       spinlock_t lock;
+       struct gpio_chip gc;
+       unsigned num_banks;
+       int irq;
+       struct irq_domain *irq_domain;
+};
+
+static unsigned int gpio_base_index;

Nope. What happens if there are other GPIO controllers with conflicting base GPIOs? I guess this adds more weight to that "linux,gpio-base" property I mentioned in
http://www.spinics.net/lists/arm-kernel/msg384847.html .

Quoted 4 lines

The best solution would be for users of the GPIOs provided by this driver to not rely on GPIO numbers at all, and exclusively use the gpiod interface. Is that an option for you?

Doesn't sysfs still rely the global GPIO number? We need to support the sysfs GPIO entries because some of our customers are using that for GPIO configuration.

I can definitely change the code to use device tree property "linux,gpio-base" though.

Quoted 14 lines
Quoted 14 lines
drivers/gpio/gpio-bcm-cygnus.c new file +14 -0
+
+static struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(struct gpio_chip *gc)
+{
+       return container_of(gc, struct bcm_cygnus_gpio, gc);
+}
+
+static int bcm_cygnus_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+
+       return irq_find_mapping(cygnus_gpio->irq_domain, offset);
+}
+
+static unsigned int __gpio_reg_offset(struct bcm_cygnus_gpio *cygnus_gpio,
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 112

Quoted 4 lines
drivers/gpio/gpio-bcm-cygnus.c new file +4 -0
+               unsigned gpio)
+{
+       return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}

The cygnus_gpio argument in this function is unused. The compiler is supposed to signal such issues. Have you looked at your compiler output?

Let me change it to marco GPIO_REG(gpio) as you suggested.

Quoted 10 lines

drivers/gpio/gpio-bcm-cygnus.c line 118

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio)
+{
+       return GPIO_BIT(gpio);
+}

Same here. Also they are so simple that macros would be more adequate here I believe:

Agreed.

Quoted 4 lines

#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + reg) #define CYGNUS_GPIO_SHIFT(pin) (pin % NGPIOS_PER_BANK) /* and remove the GPIO_BIT macro */

Right. The two macros make it simpler. Especially CYGNUS_GPIO_REG that takes the register offset as well. Will make the change.

Quoted 13 lines
Quoted 13 lines
drivers/gpio/gpio-bcm-cygnus.c new file +13 -0
+
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+               struct irq_desc *desc)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       int i, bit;
+
+       chained_irq_enter(chip, desc);
+
+       cygnus_gpio = irq_get_handler_data(irq);
+
+       /* go through the entire GPIO banks and handle all interrupts */
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 135

Quoted 4 lines
drivers/gpio/gpio-bcm-cygnus.c new file +4 -0
+       for (i = 0; i < cygnus_gpio->num_banks; i++) {
+               unsigned long val = readl(cygnus_gpio->base +
+                               (i * GPIO_BANK_SIZE) +
+                               CYGNUS_GPIO_INT_MSTAT_OFFSET);

Can you add cygnus_readl() and cygnus_writel() functions to avoid explicitly doing this operation on cygnus_gpio->base every time? It would be clearer and less error-prone.

Hmmm...But 'base' isn't the only register block that we access in this driver. There's also 'io_ctrl'.

What I can do is to introduce cygnus_readl() and cygnus_writel() like you suggested, and in the particular function bcm_cygnus_gpio_set_strength where io_ctrl register is needed, still use the normal readl and writel functions.

Quoted 27 lines
Quoted 27 lines
drivers/gpio/gpio-bcm-cygnus.c new file +27 -0
+
+               for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
+                       unsigned pin = NGPIOS_PER_BANK * i + bit;
+                       int child_irq =
+                               bcm_cygnus_gpio_to_irq(&cygnus_gpio->gc, pin);
+
+                       /*
+                        * Clear the interrupt before invoking the
+                        * handler, so we do not leave any window
+                        */
+                       writel(1 << bit,
+                               cygnus_gpio->base + (i * GPIO_BANK_SIZE) +
+                               CYGNUS_GPIO_INT_CLR_OFFSET);
+
+                       generic_handle_irq(child_irq);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void bcm_cygnus_gpio_irq_ack(struct irq_data *d)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 168

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_CLR_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = 1 << shift;

val = BIT(shift);

Will change.

Quoted 16 lines
Quoted 16 lines
drivers/gpio/gpio-bcm-cygnus.c new file +16 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_mask(struct irq_data *d)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_MSK_OFFSET;
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 190

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);

val &= ~BIT(shift);

Yes.

Quoted 27 lines
Quoted 14 lines
drivers/gpio/gpio-bcm-cygnus.c new file +14 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_unmask(struct irq_data *d)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int offset, shift;
+       u32 val;

drivers/gpio/gpio-bcm-cygnus.c line 214

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_MSK_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;

val |= BIT(shift);

Quoted 2 lines

Same remark everywhere it applies in this file.

Got it. Will go through the entire driver again and use 'BIT' where it applies.

Quoted 23 lines

drivers/gpio/gpio-bcm-cygnus.c line 197

Quoted 7 lines
drivers/gpio/gpio-bcm-cygnus.c new file +7 -0
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+}

It seems like you can factorize bcm_cygnus_gpio_irq_mask() and bcm_cygnus_gpio_irq_unmask() into one function which prototype would be, say:

static void bcm_cygnus_gpio_irq_set_mask(struct irq_data *d, int mask);

which would set of clear the bit according to the value of mask. Then your two mask/unmask functions would just need to call this one, reducing the amount of redundant code.

Also I noticed that this driver has lots of readl()/twiddle bit/writel() sequences. Maybe it would make sense to have a cygnus_set_bit(chip, reg, gpio, set) function to factorize this:

void cygnus_set_bit(chip, reg, gpio, set)

Quoted 13 lines
{
     unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
     unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
     u32 val;

     val = cygnus_readl(chip, offset);
     if (set)
         val |= BIT(shift);
     else
         val &= ~BIT(shift);
     cygnus_writel(chip, offset, val);
}

Okay. Need to go through the entire driver and make the change.

Quoted 73 lines
Quoted 59 lines
drivers/gpio/gpio-bcm-cygnus.c new file +59 -0
+
+static int bcm_cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+       unsigned gpio = d->hwirq;
+       unsigned int int_type, dual_edge, edge_lvl;
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       switch (type & IRQ_TYPE_SENSE_MASK) {
+       case IRQ_TYPE_EDGE_RISING:
+               int_type = 0;
+               dual_edge = 0;
+               edge_lvl = 1;
+               break;
+
+       case IRQ_TYPE_EDGE_FALLING:
+               int_type = 0;
+               dual_edge = 0;
+               edge_lvl = 0;
+               break;
+
+       case IRQ_TYPE_EDGE_BOTH:
+               int_type = 0;
+               dual_edge = 1;
+               edge_lvl = 0;
+               break;
+
+       case IRQ_TYPE_LEVEL_HIGH:
+               int_type = 1;
+               dual_edge = 0;
+               edge_lvl = 1;
+               break;
+
+       case IRQ_TYPE_LEVEL_LOW:
+               int_type = 1;
+               dual_edge = 0;
+               edge_lvl = 0;
+               break;
+
+       default:
+               dev_err(cygnus_gpio->dev, "invalid GPIO irq type 0x%x\n", type);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_IN_TYPE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       val |= int_type << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_DE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);

drivers/gpio/gpio-bcm-cygnus.c line 290

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+       val &= ~(1 << shift);
+       val |= dual_edge << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_INT_EDGE_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       val |= edge_lvl << shift;
+       writel(val, cygnus_gpio->base + offset);

With the functions/macros suggested above I think you could change the 3 blocks above into something like:

Quoted 4 lines

cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_IN_TYPE_OFFSET, gpio, int_type); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio, edge_lvl);

Yes. Indeed a lot simpler and easier to read. Thanks.

Quoted 73 lines
Quoted 60 lines
drivers/gpio/gpio-bcm-cygnus.c new file +60 -0
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       return 0;
+}
+
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+       .name = "bcm-cygnus-gpio",
+       .irq_ack = bcm_cygnus_gpio_irq_ack,
+       .irq_mask = bcm_cygnus_gpio_irq_mask,
+       .irq_unmask = bcm_cygnus_gpio_irq_unmask,
+       .irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};
+
+static int bcm_cygnus_gpio_direction_input(struct gpio_chip *gc,
+               unsigned gpio)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_OUT_EN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);
+
+       return 0;
+}
+
+static int bcm_cygnus_gpio_direction_output(struct gpio_chip *gc,
+               unsigned gpio, int value)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_OUT_EN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+                       offset, shift);

drivers/gpio/gpio-bcm-cygnus.c line 360

Quoted 10 lines
drivers/gpio/gpio-bcm-cygnus.c new file +10 -0
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_OUT_OFFSET;
+
+       val = readl(cygnus_gpio->base + offset);
+       if (value)
+               val |= 1 << shift;
+       else
+               val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);

And here you would have:

Quoted 5 lines

cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1); cygnus_set_bit(cygnus_gpio, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);

Many other sites in this file could be simplified this way.

Yes. Will make the change.

Quoted 216 lines
Quoted 216 lines
drivers/gpio/gpio-bcm-cygnus.c new file +216 -0
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev,
+               "gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+               gpio, offset, shift, val);
+
+       return 0;
+}
+
+static void bcm_cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio,
+               int value)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_OUT_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       val = readl(cygnus_gpio->base + offset);
+       if (value)
+               val |= 1 << shift;
+       else
+               val &= ~(1 << shift);
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+       dev_dbg(cygnus_gpio->dev,
+               "gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+               gpio, offset, shift, val);
+}
+
+static int bcm_cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       unsigned int offset, shift;
+       u32 val;
+
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_DATA_IN_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = readl(cygnus_gpio->base + offset);
+       val = (val >> shift) & 1;
+
+       dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u val:%u\n",
+                       gpio, offset, shift, val);
+
+       return val;
+}
+
+static struct lock_class_key gpio_lock_class;
+
+static int bcm_cygnus_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+                                irq_hw_number_t hwirq)
+{
+       int ret;
+
+       ret = irq_set_chip_data(irq, d->host_data);
+       if (ret < 0)
+               return ret;
+       irq_set_lockdep_class(irq, &gpio_lock_class);
+       irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+                       handle_simple_irq);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+static void bcm_cygnus_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+       irq_set_chip_and_handler(irq, NULL, NULL);
+       irq_set_chip_data(irq, NULL);
+}
+
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+       .map = bcm_cygnus_gpio_irq_map,
+       .unmap = bcm_cygnus_gpio_irq_unmap,
+       .xlate = irq_domain_xlate_twocell,
+};
+
+#ifdef CONFIG_OF_GPIO
+static void bcm_cygnus_gpio_set_pull(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio, enum gpio_pull pull)
+{
+       unsigned int offset, shift;
+       u32 val, pullup;
+       unsigned long flags;
+
+       switch (pull) {
+       case GPIO_PULL_UP:
+               pullup = 1;
+               break;
+       case GPIO_PULL_DOWN:
+               pullup = 0;
+               break;
+       case GPIO_PULL_NONE:
+       case GPIO_PULL_INVALID:
+       default:
+               return;
+       }
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       /* set pull up/down */
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_PAD_RES_OFFSET;
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       val = readl(cygnus_gpio->base + offset);
+       val &= ~(1 << shift);
+       if (pullup)
+               val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       /* enable pad */
+       offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+               CYGNUS_GPIO_RES_EN_OFFSET;
+       val = readl(cygnus_gpio->base + offset);
+       val |= 1 << shift;
+       writel(val, cygnus_gpio->base + offset);
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static void bcm_cygnus_gpio_set_strength(struct bcm_cygnus_gpio *cygnus_gpio,
+               unsigned gpio, enum gpio_drv_strength strength)
+{
+       struct device *dev = cygnus_gpio->dev;
+       void __iomem *base;
+       unsigned int i, offset, shift;
+       u32 val;
+       unsigned long flags;
+
+       /* some GPIO controllers do not support drive strength configuration */
+       if (of_find_property(dev->of_node, "no-drv-strength", NULL))
+               return;
+
+       if (cygnus_gpio->io_ctrl) {
+               base = cygnus_gpio->io_ctrl;
+               offset = CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET;
+       } else {
+               base = cygnus_gpio->base;
+               offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+                       CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET;
+       }
+
+       shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+       spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+       for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+               val = readl(base + offset);
+               val &= ~(1 << shift);
+               val |= ((strength >> i) & 0x1) << shift;
+               writel(val, base + offset);
+               offset += 4;
+       }
+
+       spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static int bcm_cygnus_gpio_of_xlate(struct gpio_chip *gc,
+               const struct of_phandle_args *gpiospec, u32 *flags)
+{
+       struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+       enum gpio_pull pull;
+       enum gpio_drv_strength strength;
+
+       if (gc->of_gpio_n_cells < 2)
+               return -EINVAL;
+
+       if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+               return -EINVAL;
+
+       if (gpiospec->args[0] >= gc->ngpio)
+               return -EINVAL;
+
+       pull = (gpiospec->args[1] >> GPIO_PULL_BIT_SHIFT) & GPIO_PULL_BIT_MASK;
+       if (WARN_ON(pull >= GPIO_PULL_INVALID))
+               return -EINVAL;
+
+       strength = (gpiospec->args[1] >> GPIO_DRV_STRENGTH_BIT_SHIFT) &
+               GPIO_DRV_STRENGTH_BIT_MASK;
+
+       if (flags)
+               *flags = gpiospec->args[1] & GPIO_FLAG_BIT_MASK;
+
+       bcm_cygnus_gpio_set_pull(cygnus_gpio, gpiospec->args[0], pull);
+       bcm_cygnus_gpio_set_strength(cygnus_gpio, gpiospec->args[0], strength);
+
+       return gpiospec->args[0];
+}
+#endif
+
+static const struct of_device_id bcm_cygnus_gpio_of_match[] = {
+       { .compatible = "brcm,cygnus-gpio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, bcm_cygnus_gpio_of_match);
+
+static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+       struct resource *res;
+       struct bcm_cygnus_gpio *cygnus_gpio;
+       struct gpio_chip *gc;
+       u32 i, ngpios;
+       int ret;
Quoted 9 lines

drivers/gpio/gpio-bcm-cygnus.c line 582

Quoted 6 lines
drivers/gpio/gpio-bcm-cygnus.c new file +6 -0
+
+       match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+       if (!match) {
+               dev_err(&pdev->dev, "failed to find GPIO controller\n");
+               return -ENODEV;
+       }

Do you still need that block of code? match is never used in this function.

Will get rid of it. Pretty much a redundant check.

Quoted 9 lines

... well, I think you get the drill. Let's first start by factorizing as much code as can be to make this driver easier to read (I have a few leads, but I am sure there are other similar factorizations that can be made). Let's also get rid of this static gpio_base_index variable that effectively prevents any other GPIO driver from being used alongside with this one. If you really need to use global GPIO numbers, let's see if Linus agrees for that "linux,gpio-base" DT property that would certainly make many people happy.

Will make the change. Thanks for the review. Very helpful!

We do need to use global GPIO numbers, at least before sysfs GPIO interface is updated to use gpiod. I'm fine with using "linux,gpio-base". Btw, I assume that's per GPIO controller based device tree property, correct?

author maintainer
#112 lore raw

PATCH v4 3/5 ARM: mach-bcm: Enable GPIO support for Cygnus

In reply to Ray Jui

Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..5066d5d 100644
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select GPIO_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
Signature · 1 line
1.7.9.5
author maintainer
#113 lore raw

PATCH v4 4/5 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +30 -0
index 5126f9e..fbc8257 100644
@@ -54,6 +54,36 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+			<0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-drv-strength;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#114 lore raw

PATCH v4 5/5 MAINTAINERS: Entry for Cygnus GPIO driver

In reply to Ray Jui
---
 MAINTAINERS |    7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index e6bff3a..8473422 100644
@@ -2202,6 +2202,13 @@ N:	bcm9583*
 N:	bcm583*
 N:	bcm113*
 
+BROADCOM CYGNUS GPIO DRIVER
+M:	Ray Jui <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/gpio/gpio-bcm-cygnus.c
+F:	Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:	Ray Jui <[email protected]>
 L:	[email protected]
Signature · 1 line
1.7.9.5
author maintainer
#115 lore raw

cover Add PCIe support to Broadcom iProc

In reply to a message not in the archive

This patchset contains the initial PCIe support for Broadcom iProc family of SoCs. This driver has been validated with Cygnus and NSP and is expected to work on other iProc family of SoCs that deploy the same PCIe controller

Ray Jui (4):
pci: iProc: define Broadcom iProc PCIe binding
PCI: iproc: Add Broadcom iProc PCIe driver
ARM: mach-bcm: Enable PCIe support for iProc
ARM: dts: enable PCIe for Broadcom Cygnus

 .../devicetree/bindings/pci/brcm,iproc-pcie.txt    |   62 ++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   43 +
 arch/arm/boot/dts/bcm958300k.dts                   |    8 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/pci/host/Kconfig                           |    9 +
 drivers/pci/host/Makefile                          |    1 +
 drivers/pci/host/pcie-iproc.c                      |  896 ++++++++++++++++++++
 7 files changed, 1020 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
 create mode 100644 drivers/pci/host/pcie-iproc.c
Signature · 1 line
1.7.9.5
author maintainer
#116 lore raw

PATCH 1/4 pci: iProc: define Broadcom iProc PCIe binding

Document the PCIe device tree binding for Broadcom iProc family of SoCs

---
 .../devicetree/bindings/pci/brcm,iproc-pcie.txt    |   62 ++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +62 -0
new file mode 100644
index 0000000..2467628
@@ -0,0 +1,62 @@
+* Broadcom iProc PCIe controller
+
+Required properties:
+- compatible: Must be "brcm,iproc-pcie"
+- reg: base address and length of the PCIe controller and the MDIO interface
+  that controls the PCIe PHY
+- interrupts: interrupt IDs
+- bus-range: PCI bus numbers covered
+- #address-cells: set to <3>
+- #size-cells: set to <2>
+- device_type: set to "pci"
+- ranges: ranges for the PCI memory and I/O regions
+- phy-addr: MDC/MDIO adddress of the PCIe PHY
+- have-msi-inten-reg: Required for legacy iProc PCIe controllers that need the
+  MSI interrupt enable register to be set explicitly
+
+The Broadcom iProc PCie driver adapts the multi-domain structure, i.e., each
+interface has its own domain and therefore has its own device node
+Example:
+
+SoC specific DT Entry:
+
+	pcie0: pcie@18012000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18012000 0x1000>,
+			<0x18002000 0x1000>;
+		interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
+			     <GIC_SPI 97 IRQ_TYPE_NONE>,
+			     <GIC_SPI 98 IRQ_TYPE_NONE>,
+			     <GIC_SPI 99 IRQ_TYPE_NONE>,
+			     <GIC_SPI 100 IRQ_TYPE_NONE>,
+			     <GIC_SPI 101 IRQ_TYPE_NONE>;
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x28000000 0 0x00010000   /* downstream I/O */
+			  0x82000000 0 0x20000000 0x20000000 0 0x04000000>; /* non-prefetchable memory */
+		phy-addr = <5>;
+	};
+
+	pcie1: pcie@18013000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18013000 0x1000>,
+			<0x18002000 0x1000>;
+
+		interrupts = <GIC_SPI 102 IRQ_TYPE_NONE>,
+			     <GIC_SPI 103 IRQ_TYPE_NONE>,
+			     <GIC_SPI 104 IRQ_TYPE_NONE>,
+			     <GIC_SPI 105 IRQ_TYPE_NONE>,
+			     <GIC_SPI 106 IRQ_TYPE_NONE>,
+			     <GIC_SPI 107 IRQ_TYPE_NONE>;
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x48000000 0 0x00010000   /* downstream I/O */
+			  0x82000000 0 0x40000000 0x40000000 0 0x04000000>; /* non-prefetchable memory */
+		phy-addr = <6>;
+	};
Signature · 1 line
1.7.9.5
#117 lore raw

Re: [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding

Am Dienstag, den 09.12.2014, 16:04 -0800 schrieb Ray Jui:

Quoted 37 lines

Document the PCIe device tree binding for Broadcom iProc family of SoCs

---
 .../devicetree/bindings/pci/brcm,iproc-pcie.txt    |   62 ++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +22 -0
new file mode 100644
index 0000000..2467628
@@ -0,0 +1,62 @@
+* Broadcom iProc PCIe controller
+
+Required properties:
+- compatible: Must be "brcm,iproc-pcie"
+- reg: base address and length of the PCIe controller and the MDIO interface
+  that controls the PCIe PHY
+- interrupts: interrupt IDs
+- bus-range: PCI bus numbers covered
+- #address-cells: set to <3>
+- #size-cells: set to <2>
+- device_type: set to "pci"
+- ranges: ranges for the PCI memory and I/O regions
+- phy-addr: MDC/MDIO adddress of the PCIe PHY
+- have-msi-inten-reg: Required for legacy iProc PCIe controllers that need the
+  MSI interrupt enable register to be set explicitly
+
+The Broadcom iProc PCie driver adapts the multi-domain structure, i.e., each
+interface has its own domain and therefore has its own device node
+Example:
+
+SoC specific DT Entry:
+

Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt line 32

Quoted 10 lines
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +10 -0
+	pcie0: pcie at 18012000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18012000 0x1000>,
+			<0x18002000 0x1000>;
+		interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
+			     <GIC_SPI 97 IRQ_TYPE_NONE>,
+			     <GIC_SPI 98 IRQ_TYPE_NONE>,
+			     <GIC_SPI 99 IRQ_TYPE_NONE>,
+			     <GIC_SPI 100 IRQ_TYPE_NONE>,
+			     <GIC_SPI 101 IRQ_TYPE_NONE>;

This is missing the interrupt-map and interrupt-map-mask for the legacy INTx interrupts. If you add this you don't need to have a special map function in your driver, but can just use the standard of_irq_parse_and_map_pci() function.

Regards,
Lucas

Quoted 30 lines
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +30 -0
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x28000000 0 0x00010000   /* downstream I/O */
+			  0x82000000 0 0x20000000 0x20000000 0 0x04000000>; /* non-prefetchable memory */
+		phy-addr = <5>;
+	};
+
+	pcie1: pcie at 18013000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18013000 0x1000>,
+			<0x18002000 0x1000>;
+
+		interrupts = <GIC_SPI 102 IRQ_TYPE_NONE>,
+			     <GIC_SPI 103 IRQ_TYPE_NONE>,
+			     <GIC_SPI 104 IRQ_TYPE_NONE>,
+			     <GIC_SPI 105 IRQ_TYPE_NONE>,
+			     <GIC_SPI 106 IRQ_TYPE_NONE>,
+			     <GIC_SPI 107 IRQ_TYPE_NONE>;
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x48000000 0 0x00010000   /* downstream I/O */
+			  0x82000000 0 0x40000000 0x40000000 0 0x04000000>; /* non-prefetchable memory */
+		phy-addr = <6>;
+	};
Signature · 2 lines
Pengutronix e.K.             | Lucas Stach                 |
Industrial Linux Solutions   | http://www.pengutronix.de/  |
author maintainer
#118 lore raw

Re: [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding

Quoted 20 lines · Lucas Stach
Quoted 4 lines
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +4 -0
+Example:
+
+SoC specific DT Entry:
+

Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt line 32

Quoted 10 lines
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt new file +10 -0
+	pcie0: pcie at 18012000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18012000 0x1000>,
+			<0x18002000 0x1000>;
+		interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
+			     <GIC_SPI 97 IRQ_TYPE_NONE>,
+			     <GIC_SPI 98 IRQ_TYPE_NONE>,
+			     <GIC_SPI 99 IRQ_TYPE_NONE>,
+			     <GIC_SPI 100 IRQ_TYPE_NONE>,
+			     <GIC_SPI 101 IRQ_TYPE_NONE>;

This is missing the interrupt-map and interrupt-map-mask for the legacy INTx interrupts. If you add this you don't need to have a special map function in your driver, but can just use the standard of_irq_parse_and_map_pci() function.

Quoted 3 lines

Regards,
Lucas

Thanks for pointing this out. I will look into this and try it out.

author maintainer
#119 lore raw

PATCH 2/4 PCI: iproc: Add Broadcom iProc PCIe driver

In reply to Ray Jui

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

---
 drivers/pci/host/Kconfig      |    9 +
 drivers/pci/host/Makefile     |    1 +
 drivers/pci/host/pcie-iproc.c |  896 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 906 insertions(+)
 create mode 100644 drivers/pci/host/pcie-iproc.c
drivers/pci/host/Kconfig +9 -0
index c4b6568..22322e1 100644
@@ -102,4 +102,13 @@ config PCI_LAYERSCAPE
 	help
 	  Say Y here if you want PCIe controller support on Layerscape SoCs.
 
+config PCIE_IPROC
+	bool "Broadcom iProc PCIe controller"
+	depends on ARCH_BCM_IPROC
+	help
+	  Say Y here if you want to enable the PCIe controller driver support
+	  on Broadcom's iProc family of SoCs.
+
+	  MSI is also supported in the driver.
+
 endmenu
drivers/pci/host/Makefile +1 -0
index 44c2699..1f5e9d2 100644
@@ -12,3 +12,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
drivers/pci/host/pcie-iproc.c new file +896 -0

896 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#120 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

Quoted 8 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

Arnd

maintainer
#121 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

Quoted 5 lines · Arnd Bergmann
Quoted 4 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

Quoted 8 lines
Quoted 4 lines

The driver also supports MSI

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Could you please be a little more specific. What driver did "Hauke already submitted"? I do not see any driver in the kernel you are talking about.

Quoted 4 lines

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

BCMA seems to be for MIPS architectures. It seems to be quite specific to those architectures using BCMA. I see no use of it in bcm53xx code?

Quoted 3 lines

Arnd

#122 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

2014-12-10 8:46 GMT-08:00 Scott Branden <[email protected]>:

Quoted 14 lines · Arnd Bergmann
Quoted 13 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:
Quoted 9 lines

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

Quoted 6 lines
Quoted 3 lines

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Could you please be a little more specific. What driver did "Hauke already submitted"? I do not see any driver in the kernel you are talking about.

https://www.marc.info/?l=linux-pci&m=141547043110684&w=2

Quoted 8 lines
Quoted 5 lines

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

BCMA seems to be for MIPS architectures. It seems to be quite specific to those architectures using BCMA. I see no use of it in bcm53xx code?

BCMA lives in its own directory in drivers/bcma/ and is not specific to MIPS actually. Older BCM47xx/BCM53xx MIPS-based SoCs traditionally started with a discoverable Silicon Sonics Backplane (drivers/ssb) and progressively migrated to BCMA (drivers/bcma), both subsystems offer a very similar bus/device/driver abstraction and discovery mechanism.

Signature · 1 line
Florian
#123 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

Quoted 15 lines · Florian Fainelli

2014-12-10 8:46 GMT-08:00 Scott Branden <[email protected]>:

Quoted 14 lines · Arnd Bergmann
Quoted 13 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:
Quoted 9 lines

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Could you please be a little more specific. What driver did "Hauke already submitted"? I do not see any driver in the kernel you are talking about.

https://www.marc.info/?l=linux-pci&m=141547043110684&w=2

Yes it also looks similar to me. Your code also contains the same comments as the driver used on Northstar (BCM5301X). Your driver has some more features, but I just have access to the consumer SoC Northstar where the PCIe controller is only used to connect some Broadcom Wifi chips to the SoC. I do not know If this controller does not have these features or the driver I used as a reference does not implement them.

When I find some time I will try this driver on a Northstar device. I think your driver is more advanced then the one I send to the mailing list.

When you want to stay with pure device tree I will send a patch adding additional support for registering to bcma.

Does your SoC also have a third PCIe controller which shares the PHY with the USB 3 controller?

Why is this stuff in the iproc_pcie_check_link() function needed? I think it is strange that the controller driver has to check if the device is there and set the correct speed. When we do not check if the card is there on BCM5301X the device stops working.

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

Quoted 8 lines
Quoted 2 lines

BCMA seems to be for MIPS architectures. It seems to be quite specific to those architectures using BCMA. I see no use of it in bcm53xx code?

BCMA lives in its own directory in drivers/bcma/ and is not specific to MIPS actually. Older BCM47xx/BCM53xx MIPS-based SoCs traditionally started with a discoverable Silicon Sonics Backplane (drivers/ssb) and progressively migrated to BCMA (drivers/bcma), both subsystems offer a very similar bus/device/driver abstraction and discovery mechanism.

With mainline kernel 3.18 you can boot Linux on a BCM5301X SoC and bcma will find all the cores.

Hauke

author maintainer
#124 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

Quoted 23 lines · Hauke Mehrtens
Quoted 22 lines · Florian Fainelli

2014-12-10 8:46 GMT-08:00 Scott Branden <[email protected]>:

Quoted 20 lines · Arnd Bergmann
Quoted 9 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:
Quoted 7 lines

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

Quoted 7 lines
Quoted 2 lines

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Could you please be a little more specific. What driver did "Hauke already submitted"? I do not see any driver in the kernel you are talking about.

Quoted 9 lines
Quoted 1 line

https://www.marc.info/?l=linux-pci&m=141547043110684&w=2

Yes it also looks similar to me. Your code also contains the same comments as the driver used on Northstar (BCM5301X).Your driver has some more features, but I just have access to the consumer SoC Northstar where the PCIe controller is only used to connect some Broadcom Wifi chips to the SoC. I do not know If this controller does not have these features or the driver I used as a reference does not implement them.

Right, I wrote this driver based on some old Broadcom internal PCIe driver from a 3.6 kernel and might have copied some of the comments (especially in the check link function). The 3.6 driver is probably what you received?

Quoted 3 lines

When I find some time I will try this driver on a Northstar device. I think your driver is more advanced then the one I send to the mailing list.

Please do that. I tested this driver on Cygnus and one of my colleagues helped to test it on North Star Plus. We do expect the same driver to work on NorthStar as well.

Quoted 3 lines

When you want to stay with pure device tree I will send a patch adding additional support for registering to bcma.

What exactly is bcma? I guess I'll need to look into it in more details myself.

Quoted 2 lines

Does your SoC also have a third PCIe controller which shares the PHY with the USB 3 controller?

No. Cygnus has only two PCIe controllers, each has its own dedicated PHY.

Quoted 6 lines

Why is this stuff in the iproc_pcie_check_link() function needed? I think it is strange that the controller driver has to check if the device is there and set the correct speed. When we do not check if the card is there on BCM5301X the device stops working.

I need to check with our ASIC engineer on this. In theory we should be able to support hot plug eventually, but maybe not in the initial version of this driver.

Quoted 18 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

Quoted 8 lines
Quoted 2 lines

BCMA seems to be for MIPS architectures. It seems to be quite specific to those architectures using BCMA. I see no use of it in bcm53xx code?

BCMA lives in its own directory in drivers/bcma/ and is not specific to MIPS actually. Older BCM47xx/BCM53xx MIPS-based SoCs traditionally started with a discoverable Silicon Sonics Backplane (drivers/ssb) and progressively migrated to BCMA (drivers/bcma), both subsystems offer a very similar bus/device/driver abstraction and discovery mechanism.

With mainline kernel 3.18 you can boot Linux on a BCM5301X SoC and bcma will find all the cores.

Hauke

#125 lore raw

Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver

In reply to Florian Fainelli

+ Rafal

Quoted 30 lines · Florian Fainelli

2014-12-10 8:46 GMT-08:00 Scott Branden<[email protected]>:

Quoted 14 lines · Arnd Bergmann
Quoted 13 lines · On Tuesday 09 December 2014 16:04:29 Ray Jui wrote:
Quoted 9 lines

Add initial version of the Broadcom iProc PCIe driver. This driver has been tested on NSP and Cygnus and is expected to work on all iProc family of SoCs that deploys the same PCIe host controller

The driver also supports MSI

Quoted 6 lines
Quoted 3 lines

The driver looks suspiciously like the one that Hauke already submitted a while ago for bcm53xx. Please come up with a merged driver that works for both.

Could you please be a little more specific. What driver did "Hauke already submitted"? I do not see any driver in the kernel you are talking about.

https://www.marc.info/?l=linux-pci&m=141547043110684&w=2

Quoted 6 lines
Quoted 5 lines

Are you sure that iProc isn't based on the BCMA bus infrastructure after all? Even the physical address of your PCI host falls into the address range that is used for the internal BCMA bus on the other chips!

Quoted 8 lines
Quoted 2 lines

BCMA seems to be for MIPS architectures. It seems to be quite specific to those architectures using BCMA. I see no use of it in bcm53xx code?

BCMA lives in its own directory in drivers/bcma/ and is not specific to MIPS actually. Older BCM47xx/BCM53xx MIPS-based SoCs traditionally started with a discoverable Silicon Sonics Backplane (drivers/ssb) and progressively migrated to BCMA (drivers/bcma), both subsystems offer a very similar bus/device/driver abstraction and discovery mechanism.

BCMA core is the bus driver for discoverable ARM AXI interconnect. Apart from that it also provides drivers for some cores. For the chips to be discoverable it needs additional IP logic. If that is not used in the iProc family devices, it can not use the BCMA-based PCIe controller driver that Hauke submitted unless BCMA would provide an API to provide the chips' core information statically either per core or a full list.

Regards,
Arend

author maintainer
#126 lore raw

PATCH 3/4 ARM: mach-bcm: Enable PCIe support for iProc

In reply to Ray Jui

Enable PCIe driver support for Broadcom iProc family of SoCs by selecting PCIE_IPROC

---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
arch/arm/mach-bcm/Kconfig +1 -0
index aaeec78..a13a0b2 100644
@@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
 	select ARCH_REQUIRE_GPIOLIB
 	select ARM_AMBA
 	select PINCTRL
+	select PCIE_IPROC
 	help
 	  This enables support for systems based on Broadcom IPROC architected SoCs.
 	  The IPROC complex contains one or more ARM CPUs along with common
Signature · 1 line
1.7.9.5
author maintainer
#127 lore raw

PATCH 4/4 ARM: dts: enable PCIe for Broadcom Cygnus

In reply to Ray Jui

Add PCIe device nodes and its properties in bcm-cygnus.dtsi but keep it disabled there. Only enable it in bcm958300k.dts because PCIe interfaces are only populated on that board

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   43 +++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/bcm958300k.dts  |    8 +++++++
 2 files changed, 51 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +43 -0
index 5126f9e..669bb3b 100644
@@ -70,6 +70,49 @@
 		};
 	};
 
+	pcie0: pcie@18012000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18012000 0x1000>,
+			<0x18002000 0x1000>;
+		interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
+			     <GIC_SPI 97 IRQ_TYPE_NONE>,
+			     <GIC_SPI 98 IRQ_TYPE_NONE>,
+			     <GIC_SPI 99 IRQ_TYPE_NONE>,
+			     <GIC_SPI 100 IRQ_TYPE_NONE>,
+			     <GIC_SPI 101 IRQ_TYPE_NONE>;
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x28000000 0 0x00010000
+			  0x82000000 0 0x20000000 0x20000000 0 0x04000000>;
+		phy-addr = <5>;
+		status = "disabled";
+	};
+
+	pcie1: pcie@18013000 {
+		compatible = "brcm,iproc-pcie";
+		reg = <0x18013000 0x1000>,
+			<0x18002000 0x1000>;
+
+		interrupts = <GIC_SPI 102 IRQ_TYPE_NONE>,
+			     <GIC_SPI 103 IRQ_TYPE_NONE>,
+			     <GIC_SPI 104 IRQ_TYPE_NONE>,
+			     <GIC_SPI 105 IRQ_TYPE_NONE>,
+			     <GIC_SPI 106 IRQ_TYPE_NONE>,
+			     <GIC_SPI 107 IRQ_TYPE_NONE>;
+		bus-range = <0x00 0xFF>;
+
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0	  0x48000000 0 0x00010000
+			  0x82000000 0 0x40000000 0x40000000 0 0x04000000>;
+		phy-addr = <6>;
+		status = "disabled";
+	};
+
 	uart0: serial@18020000 {
 		compatible = "snps,dw-apb-uart";
 		reg = <0x18020000 0x100>;
arch/arm/boot/dts/bcm958300k.dts +8 -0
index f1bb36f..c9eb856 100644
@@ -47,6 +47,14 @@
 		bootargs = "console=ttyS0,115200";
 	};
 
+	pcie0: pcie@18012000 {
+		status = "okay";
+	};
+
+	pcie1: pcie@18013000 {
+		status = "okay";
+	};
+
 	uart3: serial@18023000 {
 		status = "okay";
 	};
Signature · 1 line
1.7.9.5
author maintainer
#128 lore raw

cover Add I2C support to Broadcom iProc

In reply to a message not in the archive

This patchset contains the initial I2C support for Broadcom iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz)

Ray Jui (4):
i2c: iProc: define Broadcom iProc I2C binding
i2c: iproc: Add Broadcom iProc I2C Driver
ARM: mach-bcm: Enable I2C support for iProc
ARM: dts: add I2C device nodes for Broadcom Cygnus

 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   20 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/i2c/busses/Kconfig                         |    9 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c                 |  503 ++++++++++++++++++++
 6 files changed, 571 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
Signature · 1 line
1.7.9.5
author maintainer
#129 lore raw

PATCH 1/4 i2c: iProc: define Broadcom iProc I2C binding

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +37 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+
+Example:
+	i2c0: i2c@18008000 {
+		compatible = "brcm,iproc-i2c";
+		reg = <0x18008000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+
+		codec: wm8750@1a {
+			compatible = "wlf,wm8750";
+			reg = <0x1a>;
+		};
+	};
Signature · 1 line
1.7.9.5
#130 lore raw

Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding

Hi,

Quoted 29 lines · Ray Jui

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
  .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++++++++++++++++++++
  1 file changed, 37 insertions(+)

create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +13 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt line 23

Quoted 10 lines
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +10 -0
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+

All the properties defined with two lines of statements.

Why cant they be with single line statement, like:

compatible: Must be "brcm,iproc-i2c"
reg: Define the base and range of the I/O address space that
contain the iProc I2C controller registers

....

Signature · 2 lines
Thanks and Regards,
Varka Bhadram.
author maintainer
#131 lore raw

Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding

Quoted 49 lines · Varka Bhadram

Hi,

Quoted 33 lines · Ray Jui

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
  .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37
++++++++++++++++++++
  1 file changed, 37 insertions(+)

create mode 100644
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +9 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain

the iProc

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +4 -0
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt line 23

Quoted 10 lines
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +10 -0
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+

All the properties defined with two lines of statements.

Quoted 9 lines

Why cant they be with single line statement, like:

compatible:    Must be "brcm,iproc-i2c"
reg:        Define the base and range of the I/O address space that

contain the iProc I2C controller registers

....

I thought making them two lines are more readable (and obviously that's very subjective, :)). But more importantly, it matches the format of other Broadcom iProc/Cygnus devicetree binding documents that are currently in progress of upstreaming.

#132 lore raw

Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding

Quoted 61 lines · Ray Jui
Quoted 57 lines · Varka Bhadram

Hi,

Quoted 34 lines · Ray Jui

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
  .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37
++++++++++++++++++++
  1 file changed, 37 insertions(+)

create mode 100644
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +9 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain

the iProc

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +4 -0
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt

Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt line 23

Quoted 10 lines
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +10 -0
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+

All the properties defined with two lines of statements.

Why cant they be with single line statement, like:

compatible:    Must be "brcm,iproc-i2c"
reg:        Define the base and range of the I/O address space that

contain the iProc I2C controller registers

Quoted 8 lines
Quoted 4 lines

....

I thought making them two lines are more readable (and obviously that's

very subjective, :)). But more importantly, it matches the format of other Broadcom iProc/Cygnus devicetree binding documents that are currently in progress of upstreaming.

But max of the bindings over the kernel follows single line statements.

Signature · 2 lines
Thanks and Regards,
Varka Bhadram.
author maintainer
#133 lore raw

Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding

Quoted 82 lines · Varka Bhadram

On Wed, Dec 10, 2014 at 7:05 AM, Ray Jui <[email protected] <mailto:[email protected]>> wrote:

    On 12/9/2014 5:27 PM, Varka Bhadram wrote:

        Hi,

        On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote:

            Document the I2C device tree binding for Broadcom iProc
            family of
            SoCs

            Signed-off-by: Ray Jui <[email protected]
            <mailto:[email protected]>>
            Reviewed-by: Scott Branden <[email protected]
            <mailto:[email protected]>>
            ---
               .../devicetree/bindings/i2c/__brcm,iproc-i2c.txt     |   37
            ++++++++++++++++++++
               1 file changed, 37 insertions(+)
               create mode 100644
            Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt

            diff --git
            a/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt
            b/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt
            new file mode 100644
            index 0000000..81f982c
            --- /dev/null
            +++
            b/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt
            @@ -0,0 +1,37 @@
            +Broadcom iProc I2C controller
            +
            +Required properties:
            +
            +- compatible:
            +    Must be "brcm,iproc-i2c"
            +
            +- reg:
            +    Define the base and range of the I/O address space that
            contain
            the iProc
            +    I2C controller registers
            +
            +- interrupts:
            +    Should contain the I2C interrupt
            +
            +- clock-frequency:
            +    This is the I2C bus clock. Need to be either 100000 or
            400000
            +
            +- #address-cells:
            +    Always 1 (for I2C addresses)
            +
            +- #size-cells:
            +    Always 0
            +


        All the properties defined with two lines of statements.

        Why cant they be with single line statement, like:

        compatible:    Must be "brcm,iproc-i2c"
        reg:        Define the base and range of the I/O address space that
                  contain the iProc I2C controller registers

        ....


    I thought making them two lines are more readable (and obviously
    that's very subjective, :)). But more importantly, it matches the
    format of other Broadcom iProc/Cygnus devicetree binding documents
    that are currently in progress of upstreaming.
Quoted 5 lines

But max of the bindings over the kernel follows single line statements.

--
Thanks and Regards,
Varka Bhadram.

Is it a requirement for these property descriptions to be one line? If not, I prefer to stick with the way it is now. Thanks.

author maintainer
#134 lore raw

PATCH 2/4 i2c: iproc: Add Broadcom iProc I2C Driver

In reply to Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

---
 drivers/i2c/busses/Kconfig         |    9 +
 drivers/i2c/busses/Makefile        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c |  503 ++++++++++++++++++++++++++++++++++++
 3 files changed, 513 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
drivers/i2c/busses/Kconfig +9 -0
index c1351d9..8a2eb7e 100644
@@ -372,6 +372,15 @@ config I2C_BCM2835
 	  This support is also available as a module.  If so, the module
 	  will be called i2c-bcm2835.
 
+config I2C_BCM_IPROC
+	tristate "Broadcom iProc I2C controller"
+	depends on ARCH_BCM_IPROC
+	help
+	  If you say yes to this option, support will be included for the
+	  Broadcom iProc I2C controller.
+
+	  If you don't know what to do here, say N.
+
 config I2C_BCM_KONA
 	tristate "BCM Kona I2C adapter"
 	depends on ARCH_BCM_MOBILE
drivers/i2c/busses/Makefile +1 -0
index 5e6c822..216e7be 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
 obj-$(CONFIG_I2C_AXXIA)		+= i2c-axxia.o
 obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
+obj-$(CONFIG_I2C_BCM_IPROC)	+= i2c-bcm-iproc.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CADENCE)	+= i2c-cadence.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
drivers/i2c/busses/i2c-bcm-iproc.c new file +503 -0

503 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#135 lore raw

Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 183 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

---
  drivers/i2c/busses/Kconfig         |    9 +
  drivers/i2c/busses/Makefile        |    1 +
  drivers/i2c/busses/i2c-bcm-iproc.c |  503 ++++++++++++++++++++++++++++++++++++
  3 files changed, 513 insertions(+)

create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c

drivers/i2c/busses/Kconfig +9 -0
index c1351d9..8a2eb7e 100644
@@ -372,6 +372,15 @@ config I2C_BCM2835
  	  This support is also available as a module.  If so, the module
  	  will be called i2c-bcm2835.
 
+config I2C_BCM_IPROC
+	tristate "Broadcom iProc I2C controller"
+	depends on ARCH_BCM_IPROC
+	help
+	  If you say yes to this option, support will be included for the
+	  Broadcom iProc I2C controller.
+
+	  If you don't know what to do here, say N.
+
  config I2C_BCM_KONA
  	tristate "BCM Kona I2C adapter"
  	depends on ARCH_BCM_MOBILE
drivers/i2c/busses/Makefile +1 -0
index 5e6c822..216e7be 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
  obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
  obj-$(CONFIG_I2C_AXXIA)		+= i2c-axxia.o
  obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
+obj-$(CONFIG_I2C_BCM_IPROC)	+= i2c-bcm-iproc.o
  obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
  obj-$(CONFIG_I2C_CADENCE)	+= i2c-cadence.o
  obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
drivers/i2c/busses/i2c-bcm-iproc.c new file +129 -0
new file mode 100644
index 0000000..0e6e603
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#define CFG_OFFSET                   0x00
+#define CFG_RESET_SHIFT              31
+#define CFG_EN_SHIFT                 30
+#define CFG_M_RETRY_CNT_SHIFT        16
+#define CFG_M_RETRY_CNT_MASK         0x0f
+
+#define TIM_CFG_OFFSET               0x04
+#define TIME_CFG_MODE_400_SHIFT      31
+
+#define M_FIFO_CTRL_OFFSET           0x0c
+#define M_FIFO_RX_FLUSH_SHIFT        31
+#define M_FIFO_TX_FLUSH_SHIFT        30
+#define M_FIFO_RX_CNT_SHIFT          16
+#define M_FIFO_RX_CNT_MASK           0x7f
+#define M_FIFO_RX_THLD_SHIFT         8
+#define M_FIFO_RX_THLD_MASK          0x3f
+
+#define M_CMD_OFFSET                 0x30
+#define M_CMD_START_BUSY_SHIFT       31
+#define M_CMD_STATUS_SHIFT           25
+#define M_CMD_STATUS_MASK            0x07
+#define M_CMD_STATUS_SUCCESS         0x0
+#define M_CMD_STATUS_LOST_ARB        0x1
+#define M_CMD_STATUS_NACK_ADDR       0x2
+#define M_CMD_STATUS_NACK_DATA       0x3
+#define M_CMD_STATUS_TIMEOUT         0x4
+#define M_CMD_PROTOCOL_SHIFT         9
+#define M_CMD_PROTOCOL_MASK          0xf
+#define M_CMD_PROTOCOL_BLK_WR        0x7
+#define M_CMD_PROTOCOL_BLK_RD        0x8
+#define M_CMD_PEC_SHIFT              8
+#define M_CMD_RD_CNT_SHIFT           0
+#define M_CMD_RD_CNT_MASK            0xff
+
+#define IE_OFFSET                    0x38
+#define IE_M_RX_FIFO_FULL_SHIFT      31
+#define IE_M_RX_THLD_SHIFT           30
+#define IE_M_START_BUSY_SHIFT        28
+
+#define IS_OFFSET                    0x3c
+#define IS_M_RX_FIFO_FULL_SHIFT      31
+#define IS_M_RX_THLD_SHIFT           30
+#define IS_M_START_BUSY_SHIFT        28
+
+#define M_TX_OFFSET                  0x40
+#define M_TX_WR_STATUS_SHIFT         31
+#define M_TX_DATA_SHIFT              0
+#define M_TX_DATA_MASK               0xff
+
+#define M_RX_OFFSET                  0x44
+#define M_RX_STATUS_SHIFT            30
+#define M_RX_STATUS_MASK             0x03
+#define M_RX_PEC_ERR_SHIFT           29
+#define M_RX_DATA_SHIFT              0
+#define M_RX_DATA_MASK               0xff
+
+#define I2C_TIMEOUT_MESC             100
+#define M_TX_RX_FIFO_SIZE            64
+
+enum bus_speed_index {
+	I2C_SPD_100K = 0,
+	I2C_SPD_400K,
+};
+
+struct bcm_iproc_i2c_dev {
+	struct device *device;
+
+	void __iomem *base;
+	struct i2c_msg *msg;
+
+	struct i2c_adapter adapter;
+
+	struct completion done;
+};
+
+/*
+ * Can be expanded in the future if more interrupt status bits are utilized
+ */
+#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT)
+
+static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
+{
+	struct bcm_iproc_i2c_dev *dev = data;
+	u32 status = readl(dev->base + IS_OFFSET);
+
+	status &= ISR_MASK;
+
+	if (!status)
+		return IRQ_NONE;
+
+	writel(status, dev->base + IS_OFFSET);
+	complete_all(&dev->done);
+
+	return IRQ_HANDLED;
+}
+
+static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC);
+
+	while (readl(dev->base + M_CMD_OFFSET) &
+			(1 << M_CMD_START_BUSY_SHIFT)) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(dev->device, "wait for bus idle timeout\n");

drivers/i2c/busses/i2c-bcm-iproc.c line 139

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev,
+		struct i2c_msg *msg, u8 *addr)
+{

Match open parenthesis..

static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev,
struct i2c_msg *msg, u8 *addr)

Quoted 42 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +42 -0
+
+	if (msg->flags & I2C_M_TEN) {
+		dev_err(dev->device, "no support for 10-bit address\n");
+		return -EINVAL;
+	}
+
+	*addr = (msg->addr << 1) & 0xfe;
+
+	if (msg->flags & I2C_M_RD)
+		*addr |= 1;
+
+	return 0;
+}
+
+static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev)
+{
+	u32 val;
+
+	val = readl(dev->base + M_CMD_OFFSET);
+	val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK;
+
+	switch (val) {
+	case M_CMD_STATUS_SUCCESS:
+		return 0;
+
+	case M_CMD_STATUS_LOST_ARB:
+		dev_err(dev->device, "lost bus arbitration\n");
+		return -EREMOTEIO;
+
+	case M_CMD_STATUS_NACK_ADDR:
+		dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr);
+		return -EREMOTEIO;
+
+	case M_CMD_STATUS_NACK_DATA:
+		dev_err(dev->device, "NAK data\n");
+		return -EREMOTEIO;
+
+	case M_CMD_STATUS_TIMEOUT:
+		dev_err(dev->device, "bus timeout\n");
+		return -ETIMEDOUT;
+
+	default:

drivers/i2c/busses/i2c-bcm-iproc.c line 191

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+		dev_err(dev->device, "unknown error code=%d\n", val);
+		return -EREMOTEIO;
+	}
+
+	return -EREMOTEIO;
+}
+
+static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev,
+		struct i2c_msg *msg)
+{

dto...

Quoted 227 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +227 -0
+	int ret, i;
+	u8 addr;
+	u32 val;
+	unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC);
+
+	if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) {
+		dev_err(dev->device,
+			"supported data length is 1 - %u bytes\n",
+				M_TX_RX_FIFO_SIZE - 1);
+		return -EINVAL;
+	}
+
+	dev->msg = msg;
+	ret = __wait_for_bus_idle(dev);
+	if (ret)
+		return ret;
+
+	ret = bcm_iproc_i2c_format_addr(dev, msg, &addr);
+	if (ret)
+		return ret;
+
+	/* load slave address into the TX FIFO */
+	writel(addr, dev->base + M_TX_OFFSET);
+
+	/* for a write transaction, load data into the TX FIFO */
+	if (!(msg->flags & I2C_M_RD)) {
+		for (i = 0; i < msg->len; i++) {
+			val = msg->buf[i];
+
+			/* mark the last byte */
+			if (i == msg->len - 1)
+				val |= 1 << M_TX_WR_STATUS_SHIFT;
+
+			writel(val, dev->base + M_TX_OFFSET);
+		}
+	}
+
+	/* mark as incomplete before starting the transaction */
+	reinit_completion(&dev->done);
+
+	/*
+	 * Enable the "start busy" interrupt, which will be triggered after
+	 * the transaction is done
+	 */
+	writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET);
+
+	/*
+	 * Now we can activate the transfer. For a read operation, specify the
+	 * number of bytes to read
+	 */
+	val = 1 << M_CMD_START_BUSY_SHIFT;
+	if (msg->flags & I2C_M_RD) {
+		val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
+			(msg->len << M_CMD_RD_CNT_SHIFT);
+	} else {
+		val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
+	}
+	writel(val, dev->base + M_CMD_OFFSET);
+
+	time_left = wait_for_completion_timeout(&dev->done, time_left);
+
+	/* disable all interrupts */
+	writel(0, dev->base + IE_OFFSET);
+
+	if (!time_left) {
+		dev_err(dev->device, "transaction times out\n");
+
+		/* flush FIFOs */
+		val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
+			(1 << M_FIFO_TX_FLUSH_SHIFT);
+		writel(val, dev->base + M_FIFO_CTRL_OFFSET);
+		return -EREMOTEIO;
+	}
+
+	ret = bcm_iproc_i2c_check_status(dev);
+	if (ret) {
+		/* flush both TX/RX FIFOs */
+		val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
+			(1 << M_FIFO_TX_FLUSH_SHIFT);
+		writel(val, dev->base + M_FIFO_CTRL_OFFSET);
+		return ret;
+	}
+
+	/*
+	 * For a read operation, we now need to load the data from FIFO
+	 * into the memory buffer
+	 */
+	if (msg->flags & I2C_M_RD) {
+		for (i = 0; i < msg->len; i++) {
+			msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >>
+					M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
+		}
+	}
+
+	dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n",
+			(msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr,
+			msg->len);
+	dev_dbg(dev->device, "**** data start ****\n");
+	for (i = 0; i < msg->len; i++)
+		dev_dbg(dev->device, "0x%02x ",  msg->buf[i]);
+	dev_dbg(dev->device, "**** data end ****\n");
+
+	return 0;
+}
+
+static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
+			     struct i2c_msg msgs[], int num)
+{
+	struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter);
+	int ret, i;
+
+	/* go through all messages */
+	for (i = 0; i < num; i++) {
+		ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]);
+		if (ret) {
+			dev_err(dev->device, "xfer failed\n");
+			return ret;
+		}
+	}
+
+	return num;
+}
+
+static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm bcm_iproc_algo = {
+	.master_xfer = bcm_iproc_i2c_xfer,
+	.functionality = bcm_iproc_i2c_functionality,
+};
+
+static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev)
+{
+	unsigned int bus_speed, speed_bit;
+	u32 val;
+	int ret = of_property_read_u32(dev->device->of_node, "clock-frequency",
+				       &bus_speed);
+	if (ret < 0) {
+		dev_err(dev->device, "missing clock-frequency property\n");
+		return -ENODEV;
+	}
+
+	switch (bus_speed) {
+	case 100000:
+		speed_bit = 0;
+		break;
+	case 400000:
+		speed_bit = 1;
+		break;
+	default:
+		dev_err(dev->device, "%d Hz bus speed not supported\n",
+				bus_speed);
+		dev_err(dev->device, "valid speeds are 100khz and 400khz\n");
+		return -EINVAL;
+	}
+
+	val = readl(dev->base + TIM_CFG_OFFSET);
+	val &= ~(1 << TIME_CFG_MODE_400_SHIFT);
+	val |= speed_bit << TIME_CFG_MODE_400_SHIFT;
+	writel(val, dev->base + TIM_CFG_OFFSET);
+
+	dev_info(dev->device, "bus set to %u Hz\n", bus_speed);
+
+	return 0;
+}
+
+static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev)
+{
+	u32 val;
+
+	/* put controller in reset */
+	val = readl(dev->base + CFG_OFFSET);
+	val |= 1 << CFG_RESET_SHIFT;
+	val &= ~(1 << CFG_EN_SHIFT);
+	writel(val, dev->base + CFG_OFFSET);
+
+	/* wait 100 usec per spec */
+	udelay(100);
+
+	/* bring controller out of reset */
+	val = readl(dev->base + CFG_OFFSET);
+	val &= ~(1 << CFG_RESET_SHIFT);
+	writel(val, dev->base + CFG_OFFSET);
+
+	/* flush TX/RX FIFOs and set RX FIFO threshold to zero */
+	val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT);
+	writel(val, dev->base + M_FIFO_CTRL_OFFSET);
+
+	/* disable all interrupts */
+	val = 0;
+	writel(val, dev->base + IE_OFFSET);
+
+	/* clear all pending interrupts */
+	val = readl(dev->base + IS_OFFSET);
+	writel(val, dev->base + IS_OFFSET);
+
+	return 0;
+}
+
+static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev)
+{
+	u32 val;
+
+	val = readl(dev->base + CFG_OFFSET);
+	val |= 1 << CFG_EN_SHIFT;
+	writel(val, dev->base + CFG_OFFSET);
+}
+
+static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev)
+{
+	u32 val;
+
+	val = readl(dev->base + CFG_OFFSET);
+	val &= ~(1 << CFG_EN_SHIFT);
+	writel(val, dev->base + CFG_OFFSET);
+}
+
+static int bcm_iproc_i2c_probe(struct platform_device *pdev)
+{
+	int irq, ret = 0;
+	struct bcm_iproc_i2c_dev *dev;
+	struct i2c_adapter *adap;
+	struct resource *res;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);

drivers/i2c/busses/i2c-bcm-iproc.c line 428

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+	if (!dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, dev);
+	dev->device = &pdev->dev;
+	init_completion(&dev->done);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;

We can remove this resource check. This checking will happen with devm_ioremap_resource()

Quoted 55 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +55 -0
+	dev->base = devm_ioremap_resource(dev->device, res);
+	if (IS_ERR(dev->base))
+		return -ENOMEM;
+
+	ret = bcm_iproc_i2c_init(dev);
+	if (ret)
+		return ret;
+
+	ret = bcm_iproc_i2c_cfg_speed(dev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev->device, "no irq resource\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr,
+			IRQF_SHARED, pdev->name, dev);
+	if (ret) {
+		dev_err(dev->device, "unable to request irq %i\n", irq);
+		return ret;
+	}
+
+	bcm_iproc_i2c_enable(dev);
+
+	adap = &dev->adapter;
+	i2c_set_adapdata(adap, dev);
+	strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name));
+	adap->algo = &bcm_iproc_algo;
+	adap->dev.parent = &pdev->dev;
+	adap->dev.of_node = pdev->dev.of_node;
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_err(dev->device, "failed to add adapter\n");
+		return ret;
+	}
+
+	dev_info(dev->device, "device registered successfully\n");
+
+	return 0;
+}
+
+static int bcm_iproc_i2c_remove(struct platform_device *pdev)
+{
+	struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&dev->adapter);
+	bcm_iproc_i2c_disable(dev);
+
+	return 0;
+}
+

drivers/i2c/busses/i2c-bcm-iproc.c line 493

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+static const struct of_device_id bcm_iproc_i2c_of_match[] = {
+	{.compatible = "brcm,iproc-i2c",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match);
+
+static struct platform_driver bcm_iproc_i2c_driver = {
+	.driver = {
+		   .name = "bcm-iproc-i2c",
+		   .owner = THIS_MODULE,

No need to update this field. Its updated by module_platform_driver().

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+		   .of_match_table = bcm_iproc_i2c_of_match,
+		   },
+	.probe = bcm_iproc_i2c_probe,
+	.remove = bcm_iproc_i2c_remove,
+};
+module_platform_driver(bcm_iproc_i2c_driver);
+
+MODULE_AUTHOR("Ray Jui <[email protected]>");
+MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
+MODULE_LICENSE("GPL v2");
Signature · 2 lines
Thanks and Regards,
Varka Bhadram.