LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH] serial: 8250_dw: Add DMA support for non-ACPI platforms
[not found] <Ray Jui <rjui@broadcom.com>
@ 2014-10-08 0:35 ` Ray Jui
2014-10-09 13:20 ` Heikki Krogerus
2014-10-08 4:38 ` [PATCH] spi: pl022: Fix broken spidev when DMA is enabled Ray Jui
` (33 subsequent siblings)
34 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-08 0:35 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mika Westerberg, Heikki Krogerus,
Andy Shevchenko, Chen-Yu Tsai, Paul Gortmaker, Loic Poulain
Cc: linux-serial, linux-kernel, JD Zheng, Scott Branden, 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
If DMA is not enabled in device tree, request DMA will fail and the
driver will fall back to PIO
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Tested-by: Scott Branden <sbranden@broadcom.com>
---
drivers/tty/serial/8250/8250_dw.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 4db7987..1038ea8 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -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;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
[not found] <Ray Jui <rjui@broadcom.com>
2014-10-08 0:35 ` [PATCH] serial: 8250_dw: Add DMA support for non-ACPI platforms Ray Jui
@ 2014-10-08 4:38 ` Ray Jui
2014-10-08 11:21 ` Mark Brown
2014-10-09 18:19 ` [PATCH] spi: spidev: Use separate TX and RX bounce buffers Ray Jui
` (32 subsequent siblings)
34 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-08 4:38 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden, 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
The solution is to reverse the sequence so it maps the TX buffer before
the RX buffer
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
Tested-by: Scott Branden <sbranden@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
drivers/spi/spi-pl022.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 1189cfd..edb7298 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -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);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
2014-10-08 4:38 ` [PATCH] spi: pl022: Fix broken spidev when DMA is enabled Ray Jui
@ 2014-10-08 11:21 ` Mark Brown
2014-10-08 16:14 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Mark Brown @ 2014-10-08 11:21 UTC (permalink / raw)
To: Ray Jui; +Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden
[-- Attachment #1: Type: text/plain, Size: 623 bytes --]
On Tue, Oct 07, 2014 at 09:38:47PM -0700, Ray Jui wrote:
> 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.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
2014-10-08 11:21 ` Mark Brown
@ 2014-10-08 16:14 ` Ray Jui
2014-10-08 18:21 ` Mark Brown
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-08 16:14 UTC (permalink / raw)
To: Mark Brown, Grant Likely; +Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden
On 10/8/2014 4:21 AM, Mark Brown wrote:
> On Tue, Oct 07, 2014 at 09:38:47PM -0700, Ray Jui wrote:
>
>> 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.
>
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
2014-10-08 16:14 ` Ray Jui
@ 2014-10-08 18:21 ` Mark Brown
2014-10-08 18:31 ` Ray Jui
2014-10-09 13:59 ` Geert Uytterhoeven
0 siblings, 2 replies; 355+ messages in thread
From: Mark Brown @ 2014-10-08 18:21 UTC (permalink / raw)
To: Ray Jui; +Cc: Grant Likely, linux-spi, linux-kernel, JD Zheng, Scott Branden
[-- Attachment #1: Type: text/plain, Size: 481 bytes --]
On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:
> 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).
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
2014-10-08 18:21 ` Mark Brown
@ 2014-10-08 18:31 ` Ray Jui
2014-10-09 13:59 ` Geert Uytterhoeven
1 sibling, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-08 18:31 UTC (permalink / raw)
To: Mark Brown; +Cc: Grant Likely, linux-spi, linux-kernel, JD Zheng, Scott Branden
On 10/8/2014 11:21 AM, Mark Brown wrote:
> On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:
>
>> 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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] serial: 8250_dw: Add DMA support for non-ACPI platforms
2014-10-08 0:35 ` [PATCH] serial: 8250_dw: Add DMA support for non-ACPI platforms Ray Jui
@ 2014-10-09 13:20 ` Heikki Krogerus
0 siblings, 0 replies; 355+ messages in thread
From: Heikki Krogerus @ 2014-10-09 13:20 UTC (permalink / raw)
To: Ray Jui
Cc: Greg Kroah-Hartman, Jiri Slaby, Mika Westerberg, Andy Shevchenko,
Chen-Yu Tsai, Paul Gortmaker, Loic Poulain, linux-serial,
linux-kernel, JD Zheng, Scott Branden
On Tue, Oct 07, 2014 at 05:35:47PM -0700, Ray Jui wrote:
> 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
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> Tested-by: Scott Branden <sbranden@broadcom.com>
OK by me. FWIW..
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
--
heikki
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix broken spidev when DMA is enabled
2014-10-08 18:21 ` Mark Brown
2014-10-08 18:31 ` Ray Jui
@ 2014-10-09 13:59 ` Geert Uytterhoeven
1 sibling, 0 replies; 355+ messages in thread
From: Geert Uytterhoeven @ 2014-10-09 13:59 UTC (permalink / raw)
To: Mark Brown
Cc: Ray Jui, Grant Likely, linux-spi, linux-kernel, JD Zheng, Scott Branden
On Wed, Oct 8, 2014 at 8:21 PM, Mark Brown <broonie@kernel.org> wrote:
> On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:
>> 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 -- geert@linux-m68k.org
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH] spi: spidev: Use separate TX and RX bounce buffers
[not found] <Ray Jui <rjui@broadcom.com>
2014-10-08 0:35 ` [PATCH] serial: 8250_dw: Add DMA support for non-ACPI platforms Ray Jui
2014-10-08 4:38 ` [PATCH] spi: pl022: Fix broken spidev when DMA is enabled Ray Jui
@ 2014-10-09 18:19 ` Ray Jui
2014-10-13 11:07 ` Mark Brown
2014-10-09 18:44 ` [PATCH] spi: pl022: Fix incorrect dma_unmap_sg Ray Jui
` (31 subsequent siblings)
34 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-09 18:19 UTC (permalink / raw)
To: Mark Brown
Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden,
Geert Uytterhoeven, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
---
drivers/spi/spidev.c | 79 +++++++++++++++++++++++++++++++++-----------------
1 file changed, 52 insertions(+), 27 deletions(-)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index e3bc23b..e50039f 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -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);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH] spi: pl022: Fix incorrect dma_unmap_sg
[not found] <Ray Jui <rjui@broadcom.com>
` (2 preceding siblings ...)
2014-10-09 18:19 ` [PATCH] spi: spidev: Use separate TX and RX bounce buffers Ray Jui
@ 2014-10-09 18:44 ` Ray Jui
2014-10-13 11:08 ` Mark Brown
2014-10-17 0:48 ` [PATCH] dmaengine: pl330: use subsys_initcall Ray Jui
` (30 subsequent siblings)
34 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-09 18:44 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-spi, linux-kernel, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
drivers/spi/spi-pl022.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index f35f723..fc2dd84 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: spidev: Use separate TX and RX bounce buffers
2014-10-09 18:19 ` [PATCH] spi: spidev: Use separate TX and RX bounce buffers Ray Jui
@ 2014-10-13 11:07 ` Mark Brown
2014-10-14 3:05 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Mark Brown @ 2014-10-13 11:07 UTC (permalink / raw)
To: Ray Jui
Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden, Geert Uytterhoeven
[-- Attachment #1: Type: text/plain, Size: 268 bytes --]
On Thu, Oct 09, 2014 at 11:19:25AM -0700, Ray Jui wrote:
> 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.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix incorrect dma_unmap_sg
2014-10-09 18:44 ` [PATCH] spi: pl022: Fix incorrect dma_unmap_sg Ray Jui
@ 2014-10-13 11:08 ` Mark Brown
2014-10-14 3:05 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Mark Brown @ 2014-10-13 11:08 UTC (permalink / raw)
To: Ray Jui; +Cc: linux-spi, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 252 bytes --]
On Thu, Oct 09, 2014 at 11:44:54AM -0700, Ray Jui wrote:
> 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.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: spidev: Use separate TX and RX bounce buffers
2014-10-13 11:07 ` Mark Brown
@ 2014-10-14 3:05 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-14 3:05 UTC (permalink / raw)
To: Mark Brown
Cc: linux-spi, linux-kernel, JD Zheng, Scott Branden, Geert Uytterhoeven
On 10/13/2014 4:07 AM, Mark Brown wrote:
> On Thu, Oct 09, 2014 at 11:19:25AM -0700, Ray Jui wrote:
>> 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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] spi: pl022: Fix incorrect dma_unmap_sg
2014-10-13 11:08 ` Mark Brown
@ 2014-10-14 3:05 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-14 3:05 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-spi, linux-kernel
On 10/13/2014 4:08 AM, Mark Brown wrote:
> On Thu, Oct 09, 2014 at 11:44:54AM -0700, Ray Jui wrote:
>> 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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH] dmaengine: pl330: use subsys_initcall
[not found] <Ray Jui <rjui@broadcom.com>
` (3 preceding siblings ...)
2014-10-09 18:44 ` [PATCH] spi: pl022: Fix incorrect dma_unmap_sg Ray Jui
@ 2014-10-17 0:48 ` Ray Jui
2014-10-17 7:45 ` Lars-Peter Clausen
2014-10-17 9:44 ` Krzysztof Kozłowski
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (29 subsequent siblings)
34 siblings, 2 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-17 0:48 UTC (permalink / raw)
To: Vinod Koul, Dan Williams; +Cc: Scott Branden, dmaengine, linux-kernel, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
drivers/dma/pl330.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index d5149aa..abb4cae 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -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 <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("API Driver for PL330 DMAC");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 7:45 ` Lars-Peter Clausen
@ 2014-10-17 7:35 ` Vinod Koul
2014-10-17 11:15 ` Lars-Peter Clausen
0 siblings, 1 reply; 355+ messages in thread
From: Vinod Koul @ 2014-10-17 7:35 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Ray Jui, Dan Williams, Scott Branden, dmaengine, linux-kernel
On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
> On 10/17/2014 02:48 AM, Ray Jui wrote:
> >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.
--
~Vinod
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 0:48 ` [PATCH] dmaengine: pl330: use subsys_initcall Ray Jui
@ 2014-10-17 7:45 ` Lars-Peter Clausen
2014-10-17 7:35 ` Vinod Koul
2014-10-17 9:44 ` Krzysztof Kozłowski
1 sibling, 1 reply; 355+ messages in thread
From: Lars-Peter Clausen @ 2014-10-17 7:45 UTC (permalink / raw)
To: Ray Jui, Vinod Koul, Dan Williams; +Cc: Scott Branden, dmaengine, linux-kernel
On 10/17/2014 02:48 AM, Ray Jui wrote:
> 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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 0:48 ` [PATCH] dmaengine: pl330: use subsys_initcall Ray Jui
2014-10-17 7:45 ` Lars-Peter Clausen
@ 2014-10-17 9:44 ` Krzysztof Kozłowski
1 sibling, 0 replies; 355+ messages in thread
From: Krzysztof Kozłowski @ 2014-10-17 9:44 UTC (permalink / raw)
To: Ray Jui, Vinod Koul, Dan Williams; +Cc: Scott Branden, dmaengine, linux-kernel
On 17.10.2014 02:48, Ray Jui wrote:
> 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
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> ---
> 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).
Tested-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Best regards,
Krzysztof
>
> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> index d5149aa..abb4cae 100644
> --- a/drivers/dma/pl330.c
> +++ b/drivers/dma/pl330.c
> @@ -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 <jassi.brar@samsung.com>");
> MODULE_DESCRIPTION("API Driver for PL330 DMAC");
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 7:35 ` Vinod Koul
@ 2014-10-17 11:15 ` Lars-Peter Clausen
2014-10-17 16:18 ` Ray Jui
2014-10-21 10:43 ` Vinod Koul
0 siblings, 2 replies; 355+ messages in thread
From: Lars-Peter Clausen @ 2014-10-17 11:15 UTC (permalink / raw)
To: Vinod Koul; +Cc: Ray Jui, Dan Williams, Scott Branden, dmaengine, linux-kernel
On 10/17/2014 09:35 AM, Vinod Koul wrote:
> On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
>> On 10/17/2014 02:48 AM, Ray Jui wrote:
>>> 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.
>
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 11:15 ` Lars-Peter Clausen
@ 2014-10-17 16:18 ` Ray Jui
2014-10-17 16:39 ` Lars-Peter Clausen
2014-10-21 10:43 ` Vinod Koul
1 sibling, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-10-17 16:18 UTC (permalink / raw)
To: Lars-Peter Clausen, Vinod Koul
Cc: Dan Williams, Scott Branden, dmaengine, linux-kernel
On 10/17/2014 4:15 AM, Lars-Peter Clausen wrote:
> On 10/17/2014 09:35 AM, Vinod Koul wrote:
>> On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
>>> On 10/17/2014 02:48 AM, Ray Jui wrote:
>>>> 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.
>>
>
> 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?
>
> 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.
>
> 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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 16:18 ` Ray Jui
@ 2014-10-17 16:39 ` Lars-Peter Clausen
2014-10-17 16:56 ` Ray Jui
2014-10-21 10:45 ` Vinod Koul
0 siblings, 2 replies; 355+ messages in thread
From: Lars-Peter Clausen @ 2014-10-17 16:39 UTC (permalink / raw)
To: Ray Jui, Vinod Koul; +Cc: Dan Williams, Scott Branden, dmaengine, linux-kernel
On 10/17/2014 06:18 PM, Ray Jui wrote:
> On 10/17/2014 4:15 AM, Lars-Peter Clausen wrote:
>> On 10/17/2014 09:35 AM, Vinod Koul wrote:
>>> On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
>>>> On 10/17/2014 02:48 AM, Ray Jui wrote:
>>>>> 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.
>>>
>>
>> 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?
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.
>
>>
>> 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.
>
> 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.
>>
>> 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.
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 16:39 ` Lars-Peter Clausen
@ 2014-10-17 16:56 ` Ray Jui
2014-10-21 10:45 ` Vinod Koul
1 sibling, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-17 16:56 UTC (permalink / raw)
To: Lars-Peter Clausen, Vinod Koul
Cc: Dan Williams, Scott Branden, dmaengine, linux-kernel
On 10/17/2014 9:39 AM, Lars-Peter Clausen wrote:
> On 10/17/2014 06:18 PM, Ray Jui wrote:
>> On 10/17/2014 4:15 AM, Lars-Peter Clausen wrote:
>>> On 10/17/2014 09:35 AM, Vinod Koul wrote:
>>>> On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
>>>>> On 10/17/2014 02:48 AM, Ray Jui wrote:
>>>>>> 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.
>>>>
>>>
>>> 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?
>
>
> 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.
>>
>>>
>>> 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.
>
>>
>> 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.
>
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.
>>>
>>> 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.
>
> 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
>
Thanks, Lars, for providing these information. They are very useful!
Ray
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 11:15 ` Lars-Peter Clausen
2014-10-17 16:18 ` Ray Jui
@ 2014-10-21 10:43 ` Vinod Koul
1 sibling, 0 replies; 355+ messages in thread
From: Vinod Koul @ 2014-10-21 10:43 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Ray Jui, Dan Williams, Scott Branden, dmaengine, linux-kernel
On Fri, Oct 17, 2014 at 01:15:55PM +0200, Lars-Peter Clausen wrote:
> On 10/17/2014 09:35 AM, Vinod Koul wrote:
> >On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
> >>On 10/17/2014 02:48 AM, Ray Jui wrote:
> >>>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.
> >
>
> 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.
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"?
--
~Vinod
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-17 16:39 ` Lars-Peter Clausen
2014-10-17 16:56 ` Ray Jui
@ 2014-10-21 10:45 ` Vinod Koul
2014-10-21 16:17 ` Ray Jui
1 sibling, 1 reply; 355+ messages in thread
From: Vinod Koul @ 2014-10-21 10:45 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Ray Jui, Dan Williams, Scott Branden, dmaengine, linux-kernel
On Fri, Oct 17, 2014 at 06:39:41PM +0200, Lars-Peter Clausen wrote:
> On 10/17/2014 06:18 PM, Ray Jui wrote:
> >On 10/17/2014 4:15 AM, Lars-Peter Clausen wrote:
> >>On 10/17/2014 09:35 AM, Vinod Koul wrote:
> >>>On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
> >>>>On 10/17/2014 02:48 AM, Ray Jui wrote:
> >>>>>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.
> >>>
> >>
> >>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?
>
>
> 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.
--
~Vinod
>
> >
> >>
> >>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.
>
> >
> >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.
>
> >>
> >>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.
>
> 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
>
--
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH] dmaengine: pl330: use subsys_initcall
2014-10-21 10:45 ` Vinod Koul
@ 2014-10-21 16:17 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-10-21 16:17 UTC (permalink / raw)
To: Vinod Koul, Lars-Peter Clausen
Cc: Dan Williams, Scott Branden, dmaengine, linux-kernel
On 10/21/2014 3:45 AM, Vinod Koul wrote:
> On Fri, Oct 17, 2014 at 06:39:41PM +0200, Lars-Peter Clausen wrote:
>> On 10/17/2014 06:18 PM, Ray Jui wrote:
>>> On 10/17/2014 4:15 AM, Lars-Peter Clausen wrote:
>>>> On 10/17/2014 09:35 AM, Vinod Koul wrote:
>>>>> On Fri, Oct 17, 2014 at 09:45:45AM +0200, Lars-Peter Clausen wrote:
>>>>>> On 10/17/2014 02:48 AM, Ray Jui wrote:
>>>>>>> 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.
>>>>>
>>>>
>>>> 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?
>>
>>
>> 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.
>
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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (4 preceding siblings ...)
2014-10-17 0:48 ` [PATCH] dmaengine: pl330: use subsys_initcall Ray Jui
@ 2014-11-27 23:46 ` Ray Jui
2014-11-27 23:46 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
` (3 more replies)
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
` (28 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-27 23:46 UTC (permalink / raw)
To: Grant Likely, Rob Herring, Scott Branden
Cc: linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-11-27 23:46 ` Ray Jui
2015-01-09 10:12 ` Linus Walleij
2014-11-27 23:46 ` [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
` (2 subsequent siblings)
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-11-27 23:46 UTC (permalink / raw)
To: Grant Likely, Rob Herring, Scott Branden
Cc: linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Ray Jui
Device tree binding documentation for Broadcom Cygnus pinctrl driver
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../bindings/pinctrl/brcm,cygnus-pinctrl.txt | 92 ++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
new file mode 100644
index 0000000..86e4579
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-11-27 23:46 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
@ 2014-11-27 23:46 ` Ray Jui
2015-01-09 11:03 ` Linus Walleij
2014-11-27 23:46 ` [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
2014-11-27 23:46 ` [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-11-27 23:46 UTC (permalink / raw)
To: Grant Likely, Rob Herring, Scott Branden
Cc: linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Ray Jui, Fengguang Wu
This adds the initial driver support for the Broadcom Cygnus pinctrl
controller. The Cygnus pinctrl controller supports group based
alternate function configuration
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
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
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d014f22..4549e9f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -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
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c030b3d..4ed8e8a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -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
diff --git a/drivers/pinctrl/pinctrl-bcm-cygnus.c b/drivers/pinctrl/pinctrl-bcm-cygnus.c
new file mode 100644
index 0000000..eb6e27a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-bcm-cygnus.c
@@ -0,0 +1,753 @@
+/*
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+
+/*
+ * Alternate function configuration
+ *
+ * @name: name of the alternate function
+ * @group_names: array of strings of group names that can be supported by this
+ * alternate function
+ * @num_groups: total number of groups that can be supported by this alternate
+ * function
+ * @mux: mux setting for this alternate function to be programed
+ */
+struct cygnus_pin_function {
+ const char *name;
+ const char * const *group_names;
+ const unsigned num_groups;
+ unsigned int mux;
+};
+
+/*
+ * Cygnus allows group based pinmux configuration
+ *
+ * @name: name of the group
+ * @pins: array of pins used by this group
+ * @num_pins: total number of pins used by this group
+ * @offset: register offset for pinmux configuration of this group
+ * @shift: bit shift for pinmux configuration of this group
+ */
+struct cygnus_pin_group {
+ const char *name;
+ const unsigned *pins;
+ const unsigned num_pins;
+ const unsigned int offset;
+ const unsigned int shift;
+};
+
+/*
+ * 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;
+ struct device *dev;
+ void __iomem *base;
+
+ const struct pinctrl_pin_desc *pins;
+ unsigned num_pins;
+
+ const struct cygnus_pin_group *groups;
+ unsigned num_groups;
+
+ const struct cygnus_pin_function *functions;
+ unsigned num_functions;
+};
+
+#define CYGNUS_PIN_GROUP(group_name, off, sh) \
+{ \
+ .name = #group_name, \
+ .pins = group_name##_pins, \
+ .num_pins = ARRAY_SIZE(group_name##_pins), \
+ .offset = off, \
+ .shift = sh, \
+}
+
+/*
+ * The following pin description is based on Cygnus I/O MUX spreadsheet
+ */
+static const struct pinctrl_pin_desc cygnus_pinctrl_pins[] = {
+ PINCTRL_PIN(0, "ext_device_reset_n"),
+ PINCTRL_PIN(1, "chip_mode0"),
+ PINCTRL_PIN(2, "chip_mode1"),
+ PINCTRL_PIN(3, "chip_mode2"),
+ PINCTRL_PIN(4, "chip_mode3"),
+ PINCTRL_PIN(5, "chip_mode4"),
+ PINCTRL_PIN(6, "bsc0_scl"),
+ PINCTRL_PIN(7, "bsc0_sda"),
+ PINCTRL_PIN(8, "bsc1_scl"),
+ PINCTRL_PIN(9, "bsc1_sda"),
+ PINCTRL_PIN(10, "d1w_dq"),
+ PINCTRL_PIN(11, "d1wowstz_l"),
+ PINCTRL_PIN(12, "gpio0"),
+ PINCTRL_PIN(13, "gpio1"),
+ PINCTRL_PIN(14, "gpio2"),
+ PINCTRL_PIN(15, "gpio3"),
+ PINCTRL_PIN(16, "gpio4"),
+ PINCTRL_PIN(17, "gpio5"),
+ PINCTRL_PIN(18, "gpio6"),
+ PINCTRL_PIN(19, "gpio7"),
+ PINCTRL_PIN(20, "gpio8"),
+ PINCTRL_PIN(21, "gpio9"),
+ PINCTRL_PIN(22, "gpio10"),
+ PINCTRL_PIN(23, "gpio11"),
+ PINCTRL_PIN(24, "gpio12"),
+ PINCTRL_PIN(25, "gpio13"),
+ PINCTRL_PIN(26, "gpio14"),
+ PINCTRL_PIN(27, "gpio15"),
+ PINCTRL_PIN(28, "gpio16"),
+ PINCTRL_PIN(29, "gpio17"),
+ PINCTRL_PIN(30, "gpio18"),
+ PINCTRL_PIN(31, "gpio19"),
+ PINCTRL_PIN(32, "gpio20"),
+ PINCTRL_PIN(33, "gpio21"),
+ PINCTRL_PIN(34, "gpio22"),
+ PINCTRL_PIN(35, "gpio23"),
+ PINCTRL_PIN(36, "mdc"),
+ PINCTRL_PIN(37, "mdio"),
+ PINCTRL_PIN(38, "pwm0"),
+ PINCTRL_PIN(39, "pwm1"),
+ PINCTRL_PIN(40, "pwm2"),
+ PINCTRL_PIN(41, "pwm3"),
+ PINCTRL_PIN(42, "sc0_clk"),
+ PINCTRL_PIN(43, "sc0_cmdvcc_l"),
+ PINCTRL_PIN(44, "sc0_detect"),
+ PINCTRL_PIN(45, "sc0_fcb"),
+ PINCTRL_PIN(46, "sc0_io"),
+ PINCTRL_PIN(47, "sc0_rst_l"),
+ PINCTRL_PIN(48, "sc1_clk"),
+ PINCTRL_PIN(49, "sc1_cmdvcc_l"),
+ PINCTRL_PIN(50, "sc1_detect"),
+ PINCTRL_PIN(51, "sc1_fcb"),
+ PINCTRL_PIN(52, "sc1_io"),
+ PINCTRL_PIN(53, "sc1_rst_l"),
+ PINCTRL_PIN(54, "spi0_clk"),
+ PINCTRL_PIN(55, "spi0_mosi"),
+ PINCTRL_PIN(56, "spi0_miso"),
+ PINCTRL_PIN(57, "spi0_ss"),
+ PINCTRL_PIN(58, "spi1_clk"),
+ PINCTRL_PIN(59, "spi1_mosi"),
+ PINCTRL_PIN(60, "spi1_miso"),
+ PINCTRL_PIN(61, "spi1_ss"),
+ PINCTRL_PIN(62, "spi2_clk"),
+ PINCTRL_PIN(63, "spi2_mosi"),
+ PINCTRL_PIN(64, "spi2_miso"),
+ PINCTRL_PIN(65, "spi2_ss"),
+ PINCTRL_PIN(66, "spi3_clk"),
+ PINCTRL_PIN(67, "spi3_mosi"),
+ PINCTRL_PIN(68, "spi3_miso"),
+ PINCTRL_PIN(69, "spi3_ss"),
+ PINCTRL_PIN(70, "uart0_cts"),
+ PINCTRL_PIN(71, "uart0_rts"),
+ PINCTRL_PIN(72, "uart0_rx"),
+ PINCTRL_PIN(73, "uart0_tx"),
+ PINCTRL_PIN(74, "uart1_cts"),
+ PINCTRL_PIN(75, "uart1_dcd"),
+ PINCTRL_PIN(76, "uart1_dsr"),
+ PINCTRL_PIN(77, "uart1_dtr"),
+ PINCTRL_PIN(78, "uart1_ri"),
+ PINCTRL_PIN(79, "uart1_rts"),
+ PINCTRL_PIN(80, "uart1_rx"),
+ PINCTRL_PIN(81, "uart1_tx"),
+ PINCTRL_PIN(82, "uart3_rx"),
+ PINCTRL_PIN(83, "uart3_tx"),
+ PINCTRL_PIN(84, "sdio1_clk_sdcard"),
+ PINCTRL_PIN(85, "sdio1_cmd"),
+ PINCTRL_PIN(86, "sdio1_data0"),
+ PINCTRL_PIN(87, "sdio1_data1"),
+ PINCTRL_PIN(88, "sdio1_data2"),
+ PINCTRL_PIN(89, "sdio1_data3"),
+ PINCTRL_PIN(90, "sdio1_wp_n"),
+ PINCTRL_PIN(91, "sdio1_card_rst"),
+ PINCTRL_PIN(92, "sdio1_led_on"),
+ PINCTRL_PIN(93, "sdio1_cd"),
+ PINCTRL_PIN(94, "sdio0_clk_sdcard"),
+ PINCTRL_PIN(95, "sdio0_cmd"),
+ PINCTRL_PIN(96, "sdio0_data0"),
+ PINCTRL_PIN(97, "sdio0_data1"),
+ PINCTRL_PIN(98, "sdio0_data2"),
+ PINCTRL_PIN(99, "sdio0_data3"),
+ PINCTRL_PIN(100, "sdio0_wp_n"),
+ PINCTRL_PIN(101, "sdio0_card_rst"),
+ PINCTRL_PIN(102, "sdio0_led_on"),
+ PINCTRL_PIN(103, "sdio0_cd"),
+ PINCTRL_PIN(104, "sflash_clk"),
+ PINCTRL_PIN(105, "sflash_cs_l"),
+ PINCTRL_PIN(106, "sflash_mosi"),
+ PINCTRL_PIN(107, "sflash_miso"),
+ PINCTRL_PIN(108, "sflash_wp_n"),
+ PINCTRL_PIN(109, "sflash_hold_n"),
+ PINCTRL_PIN(110, "nand_ale"),
+ PINCTRL_PIN(111, "nand_ce0_l"),
+ PINCTRL_PIN(112, "nand_ce1_l"),
+ PINCTRL_PIN(113, "nand_cle"),
+ PINCTRL_PIN(114, "nand_dq0"),
+ PINCTRL_PIN(115, "nand_dq1"),
+ PINCTRL_PIN(116, "nand_dq2"),
+ PINCTRL_PIN(117, "nand_dq3"),
+ PINCTRL_PIN(118, "nand_dq4"),
+ PINCTRL_PIN(119, "nand_dq5"),
+ PINCTRL_PIN(120, "nand_dq6"),
+ PINCTRL_PIN(121, "nand_dq7"),
+ PINCTRL_PIN(122, "nand_rb_l"),
+ PINCTRL_PIN(123, "nand_re_l"),
+ PINCTRL_PIN(124, "nand_we_l"),
+ PINCTRL_PIN(125, "nand_wp_l"),
+ PINCTRL_PIN(126, "lcd_clac"),
+ PINCTRL_PIN(127, "lcd_clcp"),
+ PINCTRL_PIN(128, "lcd_cld0"),
+ PINCTRL_PIN(129, "lcd_cld1"),
+ PINCTRL_PIN(130, "lcd_cld10"),
+ PINCTRL_PIN(131, "lcd_cld11"),
+ PINCTRL_PIN(132, "lcd_cld12"),
+ PINCTRL_PIN(133, "lcd_cld13"),
+ PINCTRL_PIN(134, "lcd_cld14"),
+ PINCTRL_PIN(135, "lcd_cld15"),
+ PINCTRL_PIN(136, "lcd_cld16"),
+ PINCTRL_PIN(137, "lcd_cld17"),
+ PINCTRL_PIN(138, "lcd_cld18"),
+ PINCTRL_PIN(139, "lcd_cld19"),
+ PINCTRL_PIN(140, "lcd_cld2"),
+ PINCTRL_PIN(141, "lcd_cld20"),
+ PINCTRL_PIN(142, "lcd_cld21"),
+ PINCTRL_PIN(143, "lcd_cld22"),
+ PINCTRL_PIN(144, "lcd_cld23"),
+ PINCTRL_PIN(145, "lcd_cld3"),
+ PINCTRL_PIN(146, "lcd_cld4"),
+ PINCTRL_PIN(147, "lcd_cld5"),
+ PINCTRL_PIN(148, "lcd_cld6"),
+ PINCTRL_PIN(149, "lcd_cld7"),
+ PINCTRL_PIN(150, "lcd_cld8"),
+ PINCTRL_PIN(151, "lcd_cld9"),
+ PINCTRL_PIN(152, "lcd_clfp"),
+ PINCTRL_PIN(153, "lcd_clle"),
+ PINCTRL_PIN(154, "lcd_cllp"),
+ PINCTRL_PIN(155, "lcd_clpower"),
+ PINCTRL_PIN(156, "camera_vsync"),
+ PINCTRL_PIN(157, "camera_trigger"),
+ PINCTRL_PIN(158, "camera_strobe"),
+ PINCTRL_PIN(159, "camera_standby"),
+ PINCTRL_PIN(160, "camera_reset_n"),
+ PINCTRL_PIN(161, "camera_pixdata9"),
+ PINCTRL_PIN(162, "camera_pixdata8"),
+ PINCTRL_PIN(163, "camera_pixdata7"),
+ PINCTRL_PIN(164, "camera_pixdata6"),
+ PINCTRL_PIN(165, "camera_pixdata5"),
+ PINCTRL_PIN(166, "camera_pixdata4"),
+ PINCTRL_PIN(167, "camera_pixdata3"),
+ PINCTRL_PIN(168, "camera_pixdata2"),
+ PINCTRL_PIN(169, "camera_pixdata1"),
+ PINCTRL_PIN(170, "camera_pixdata0"),
+ PINCTRL_PIN(171, "camera_pixclk"),
+ PINCTRL_PIN(172, "camera_hsync"),
+ PINCTRL_PIN(173, "camera_pll_ref_clk"),
+ PINCTRL_PIN(174, "usb_id_indication"),
+ PINCTRL_PIN(175, "usb_vbus_indication"),
+ PINCTRL_PIN(176, "gpio0_3p3"),
+ PINCTRL_PIN(177, "gpio1_3p3"),
+ PINCTRL_PIN(178, "gpio2_3p3"),
+ PINCTRL_PIN(179, "gpio3_3p3"),
+};
+
+/*
+ * 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 };
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };
+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 };
+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 };
+
+/*
+ * 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",
+ "gpio1_3p3",
+ "gpio2_3p3",
+};
+
+/*
+ * List of groups. Need to match the order in cygnus_pin_group_names
+ */
+static const struct cygnus_pin_group cygnus_pin_groups[] = {
+ CYGNUS_PIN_GROUP(gpio0, 0x0, 0),
+ CYGNUS_PIN_GROUP(gpio1, 0x0, 4),
+ CYGNUS_PIN_GROUP(gpio2, 0x0, 8),
+ CYGNUS_PIN_GROUP(gpio3, 0x0, 12),
+ CYGNUS_PIN_GROUP(gpio4, 0x0, 16),
+ CYGNUS_PIN_GROUP(gpio5, 0x0, 20),
+ CYGNUS_PIN_GROUP(gpio6, 0x0, 24),
+ CYGNUS_PIN_GROUP(gpio7, 0x0, 28),
+ CYGNUS_PIN_GROUP(gpio8, 0x4, 0),
+ CYGNUS_PIN_GROUP(gpio9, 0x4, 4),
+ CYGNUS_PIN_GROUP(gpio10, 0x4, 8),
+ CYGNUS_PIN_GROUP(gpio11, 0x4, 12),
+ CYGNUS_PIN_GROUP(gpio12, 0x4, 16),
+ CYGNUS_PIN_GROUP(gpio13, 0x4, 20),
+ CYGNUS_PIN_GROUP(gpio14, 0x4, 24),
+ CYGNUS_PIN_GROUP(gpio15, 0x4, 28),
+ CYGNUS_PIN_GROUP(gpio16, 0x8, 0),
+ CYGNUS_PIN_GROUP(gpio17, 0x8, 4),
+ CYGNUS_PIN_GROUP(gpio18, 0x8, 8),
+ CYGNUS_PIN_GROUP(gpio19, 0x8, 12),
+ CYGNUS_PIN_GROUP(gpio20, 0x8, 16),
+ CYGNUS_PIN_GROUP(gpio21, 0x8, 20),
+ CYGNUS_PIN_GROUP(gpio22, 0x8, 24),
+ CYGNUS_PIN_GROUP(gpio23, 0x8, 28),
+ CYGNUS_PIN_GROUP(pwm0, 0xc, 0),
+ CYGNUS_PIN_GROUP(pwm1, 0xc, 4),
+ CYGNUS_PIN_GROUP(pwm2, 0xc, 8),
+ CYGNUS_PIN_GROUP(pwm3, 0xc, 12),
+ CYGNUS_PIN_GROUP(sdio0, 0xc, 16),
+ CYGNUS_PIN_GROUP(smart_card0, 0xc, 20),
+ CYGNUS_PIN_GROUP(smart_card1, 0xc, 24),
+ CYGNUS_PIN_GROUP(spi0, 0x10, 0),
+ CYGNUS_PIN_GROUP(spi1, 0x10, 4),
+ CYGNUS_PIN_GROUP(spi2, 0x10, 8),
+ CYGNUS_PIN_GROUP(spi3, 0x10, 12),
+ CYGNUS_PIN_GROUP(d1w, 0x10, 16),
+ CYGNUS_PIN_GROUP(lcd, 0x10, 20),
+ CYGNUS_PIN_GROUP(uart0, 0x14, 0),
+ CYGNUS_PIN_GROUP(uart1_dte, 0x14, 4),
+ CYGNUS_PIN_GROUP(uart1, 0x14, 8),
+ CYGNUS_PIN_GROUP(uart3, 0x14, 12),
+ CYGNUS_PIN_GROUP(qspi, 0x14, 16),
+ CYGNUS_PIN_GROUP(nand, 0x14, 20),
+ CYGNUS_PIN_GROUP(sdio0_cd, 0x18, 0),
+ CYGNUS_PIN_GROUP(sdio0_mmc, 0x18, 4),
+ CYGNUS_PIN_GROUP(can0_spi4, 0x18, 8),
+ CYGNUS_PIN_GROUP(can1_spi4, 0x18, 12),
+ CYGNUS_PIN_GROUP(sdio1_cd, 0x18, 16),
+ CYGNUS_PIN_GROUP(sdio1_led, 0x18, 20),
+ CYGNUS_PIN_GROUP(sdio1_mmc, 0x18, 24),
+ CYGNUS_PIN_GROUP(camera_led, 0x1c, 0),
+ CYGNUS_PIN_GROUP(camera_rgmii, 0x1c, 4),
+ CYGNUS_PIN_GROUP(camera_sram_rgmii, 0x1c, 8),
+ CYGNUS_PIN_GROUP(qspi_gpio, 0x1c, 12),
+ CYGNUS_PIN_GROUP(smart_card0_fcb, 0x20, 0),
+ CYGNUS_PIN_GROUP(smart_card1_fcb, 0x20, 4),
+ CYGNUS_PIN_GROUP(gpio0_3p3, 0x28, 0),
+ CYGNUS_PIN_GROUP(gpio1_3p3, 0x28, 4),
+ CYGNUS_PIN_GROUP(gpio2_3p3, 0x28, 8),
+};
+
+#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, \
+}
+
+/*
+ * 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),
+};
+
+static int cygnus_get_groups_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_groups;
+}
+
+static const char *cygnus_get_group_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->groups[selector].name;
+}
+
+static int cygnus_get_group_pins(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *pins = pinctrl->groups[selector].pins;
+ *num_pins = pinctrl->groups[selector].num_pins;
+
+ return 0;
+}
+
+static void cygnus_pin_dbg_show(struct pinctrl_dev *pctrl_dev,
+ struct seq_file *s, unsigned offset)
+{
+ seq_printf(s, " %s", dev_name(pctrl_dev->dev));
+}
+
+static int find_matched_function(const char *function_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cygnus_pin_functions); i++) {
+ if (!strcmp(cygnus_pin_functions[i].name, function_name))
+ return (int)cygnus_pin_functions[i].mux;
+ }
+
+ return -EINVAL;
+}
+
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+ struct device_node *np, struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ int ret, num_groups;
+ unsigned reserved_maps = 0;
+ struct property *prop;
+ const char *group_name, *function_name;
+
+ *map = NULL;
+ *num_maps = 0;
+
+ num_groups = of_property_count_strings(np, "brcm,groups");
+ if (num_groups < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,groups\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string(np, "brcm,function", &function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,function\n");
+ return -EINVAL;
+ }
+
+ /* make sure it's a valid alternate function */
+ ret = find_matched_function(function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev, "invalid function name: %s\n",
+ function_name);
+ }
+
+ ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+ num_maps, num_groups);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "unable to reserve map\n");
+ return ret;
+ }
+
+ of_property_for_each_string(np, "brcm,groups", prop, group_name) {
+ ret = pinctrl_utils_add_map_mux(pctrl_dev, map,
+ &reserved_maps, num_maps, group_name,
+ function_name);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "can't add map: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops cygnus_pinctrl_ops = {
+ .get_groups_count = cygnus_get_groups_count,
+ .get_group_name = cygnus_get_group_name,
+ .get_group_pins = cygnus_get_group_pins,
+ .pin_dbg_show = cygnus_pin_dbg_show,
+ .dt_node_to_map = cygnus_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_functions;
+}
+
+static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->functions[selector].name;
+}
+
+static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *groups = pinctrl->functions[selector].group_names;
+ *num_groups = pinctrl->functions[selector].num_groups;
+
+ return 0;
+}
+
+static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+ unsigned function_selector, unsigned group_selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ const struct cygnus_pin_function *function =
+ &pinctrl->functions[function_selector];
+ const struct cygnus_pin_group *group =
+ &pinctrl->groups[group_selector];
+ u32 val, mask = 0x7;
+
+ dev_dbg(pctrl_dev->dev,
+ "group:%s with offset:0x%08x shift:%u set to function: %s mux:%u\n",
+ group->name, group->offset, group->shift, function->name,
+ function->mux);
+
+ val = readl(pinctrl->base + group->offset);
+ val &= ~(mask << group->shift);
+ val |= function->mux << group->shift;
+ writel(val, pinctrl->base + group->offset);
+
+ return 0;
+}
+
+static struct pinmux_ops cygnus_pinmux_ops = {
+ .get_functions_count = cygnus_get_functions_count,
+ .get_function_name = cygnus_get_function_name,
+ .get_function_groups = cygnus_get_function_groups,
+ .set_mux = cygnus_pinmux_set_mux,
+};
+
+static struct pinctrl_desc cygnus_pinctrl_desc = {
+ .pctlops = &cygnus_pinctrl_ops,
+ .pmxops = &cygnus_pinmux_ops,
+ .owner = THIS_MODULE,
+};
+
+static int cygnus_pinctrl_probe(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl;
+ struct resource *res;
+
+ pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
+ if (!pinctrl) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ pinctrl->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get resource\n");
+ return -ENOENT;
+ }
+
+ pinctrl->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pinctrl->base)) {
+ dev_err(&pdev->dev, "unable to map I/O space\n");
+ return PTR_ERR(pinctrl->base);
+ }
+
+ pinctrl->pins = cygnus_pinctrl_pins;
+ pinctrl->num_pins = ARRAY_SIZE(cygnus_pinctrl_pins);
+ pinctrl->groups = cygnus_pin_groups;
+ pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups);
+ pinctrl->functions = cygnus_pin_functions;
+ pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions);
+
+ cygnus_pinctrl_desc.name = dev_name(&pdev->dev);
+ cygnus_pinctrl_desc.pins = cygnus_pinctrl_pins;
+ cygnus_pinctrl_desc.npins = ARRAY_SIZE(cygnus_pinctrl_pins);
+
+ pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev,
+ pinctrl);
+ if (!pinctrl->pctl) {
+ dev_err(&pdev->dev, "unable to register cygnus pinctrl\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, pinctrl);
+
+ return 0;
+}
+
+static int cygnus_pinctrl_remove(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(pinctrl->pctl);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct of_device_id cygnus_pinctrl_of_match[] = {
+ { .compatible = "brcm,cygnus-pinctrl", },
+ { },
+};
+
+static struct platform_driver cygnus_pinctrl_driver = {
+ .driver = {
+ .name = "cygnus-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = cygnus_pinctrl_of_match,
+ },
+ .probe = cygnus_pinctrl_probe,
+ .remove = cygnus_pinctrl_remove,
+};
+
+static int __init cygnus_pinctrl_init(void)
+{
+ return platform_driver_register(&cygnus_pinctrl_driver);
+}
+arch_initcall(cygnus_pinctrl_init);
+
+static void __exit cygnus_pinctrl_exit(void)
+{
+ platform_driver_unregister(&cygnus_pinctrl_driver);
+}
+module_exit(cygnus_pinctrl_exit);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus pinctrl driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-11-27 23:46 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
2014-11-27 23:46 ` [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
@ 2014-11-27 23:46 ` Ray Jui
2014-11-27 23:46 ` [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-27 23:46 UTC (permalink / raw)
To: Grant Likely, Rob Herring, Scott Branden
Cc: linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Ray Jui
This enables the pinctrl driver for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..b4efff2 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-11-27 23:46 ` [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
@ 2014-11-27 23:46 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-27 23:46 UTC (permalink / raw)
To: Grant Likely, Rob Herring, Scott Branden
Cc: linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Ray Jui
This enables the pinctrl support for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..4c6bf4d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add common clock support for Broadcom iProc architecture
[not found] <Ray Jui <rjui@broadcom.com>
` (5 preceding siblings ...)
2014-11-27 23:46 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-11-28 1:27 ` Ray Jui
2014-11-28 1:27 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
` (3 more replies)
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
` (27 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-28 1:27 UTC (permalink / raw)
To: linux-arm-kernel, Scott Branden
Cc: linux-kernel, bcm-kernel-feedback-list, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
@ 2014-11-28 1:27 ` Ray Jui
2014-11-28 1:27 ` [PATCH 2/4] clk: iproc: add initial common clock support Ray Jui
` (2 subsequent siblings)
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-28 1:27 UTC (permalink / raw)
To: linux-arm-kernel, Scott Branden
Cc: linux-kernel, bcm-kernel-feedback-list, Ray Jui
Document the device tree binding for Broadcom iProc architecture based
clock controller
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
brcm,iproc-clocks.txt | 178 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
create mode 100644 brcm,iproc-clocks.txt
diff --git a/brcm,iproc-clocks.txt b/brcm,iproc-clocks.txt
new file mode 100644
index 0000000..cc64fd2
--- /dev/null
+++ b/brcm,iproc-clocks.txt
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] clk: iproc: add initial common clock support
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
2014-11-28 1:27 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
@ 2014-11-28 1:27 ` Ray Jui
2014-11-28 1:27 ` [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus Ray Jui
2014-11-28 1:27 ` [PATCH 4/4] ARM: dts: enable " Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-28 1:27 UTC (permalink / raw)
To: linux-arm-kernel, Scott Branden
Cc: linux-kernel, bcm-kernel-feedback-list, 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
<jonathar@broadcom.com>
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..eff0213 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -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/
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
index 75506e5..66b5b7f 100644
--- a/drivers/clk/bcm/Kconfig
+++ b/drivers/clk/bcm/Kconfig
@@ -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
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 6297d05..6926636 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -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
diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c
new file mode 100644
index 0000000..ec9b130
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-armpll.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c
new file mode 100644
index 0000000..ab86b8c
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-asiu.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-clk.c b/drivers/clk/bcm/clk-iproc-clk.c
new file mode 100644
index 0000000..be3c42c
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-clk.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c
new file mode 100644
index 0000000..cd3bd38
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-pll.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h
new file mode 100644
index 0000000..4aa0479
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc.h
@@ -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
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
2014-11-28 1:27 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
2014-11-28 1:27 ` [PATCH 2/4] clk: iproc: add initial common clock support Ray Jui
@ 2014-11-28 1:27 ` Ray Jui
2014-11-28 1:27 ` [PATCH 4/4] ARM: dts: enable " Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-28 1:27 UTC (permalink / raw)
To: linux-arm-kernel, Scott Branden
Cc: linux-kernel, bcm-kernel-feedback-list, 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.
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 6926636..afcbe55 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -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
diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c
new file mode 100644
index 0000000..f603d1d
--- /dev/null
+++ b/drivers/clk/bcm/clk-cygnus.c
@@ -0,0 +1,277 @@
+/*
+ * 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 <dt-bindings/clock/bcm-cygnus.h>
+#include "clk-iproc.h"
+
+#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, }
+
+#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \
+ .pwr_shift = ps, .iso_shift = is }
+
+#define asiu_div_val(o, es, hs, hw, ls, lw) \
+ { .offset = o, .en_shift = es, .high_shift = hs, \
+ .high_width = hw, .low_shift = ls, .low_width = lw }
+
+#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \
+ .reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \
+ .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \
+ .ka_width = kaw }
+
+#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo }
+
+#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \
+ .hold_shift = hs, .bypass_shift = bs }
+
+#define asiu_gate_val(o, es) { .offset = o, .en_shift = es }
+
+static const struct iproc_pll_ctrl genpll = {
+ .flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC,
+ .aon = aon_val(0x0, 2, 1, 0),
+ .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3),
+ .ndiv_int = reg_val(0x10, 20, 10),
+ .ndiv_frac = reg_val(0x10, 0, 20),
+ .pdiv = reg_val(0x14, 0, 4),
+ .vco_ctrl = vco_ctrl_val(0x18, 0x1c),
+ .status = reg_val(0x28, 12, 1),
+};
+
+static const struct iproc_pll_ctrl lcpll0 = {
+ .flags = IPROC_CLK_AON,
+ .aon = aon_val(0x0, 2, 5, 4),
+ .reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4),
+ .ndiv_int = reg_val(0x4, 16, 10),
+ .pdiv = reg_val(0x4, 26, 4),
+ .vco_ctrl = vco_ctrl_val(0x10, 0x14),
+ .status = reg_val(0x18, 12, 1),
+};
+
+/*
+ * MIPI PLL VCO frequency parameter table
+ */
+static const struct iproc_pll_vco_freq_param mipipll_vco_params[] = {
+ /* rate (Hz) ndiv_int ndiv_frac pdiv */
+ { 750000000UL, 30, 0, 1 },
+ { 1000000000UL, 40, 0, 1 },
+ { 1350000000ul, 54, 0, 1 },
+ { 2000000000UL, 80, 0, 1 },
+ { 2100000000UL, 84, 0, 1 },
+ { 2250000000UL, 90, 0, 1 },
+ { 2500000000UL, 100, 0, 1 },
+ { 2700000000UL, 54, 0, 0 },
+ { 2975000000UL, 119, 0, 1 },
+ { 3100000000UL, 124, 0, 1 },
+ { 3150000000UL, 126, 0, 1 },
+};
+
+static const struct iproc_pll_ctrl mipipll = {
+ .flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC,
+ .aon = aon_val(0x0, 4, 17, 16),
+ .asiu = asiu_gate_val(0x0, 3),
+ .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4),
+ .ndiv_int = reg_val(0x10, 20, 10),
+ .ndiv_frac = reg_val(0x10, 0, 20),
+ .pdiv = reg_val(0x14, 0, 4),
+ .vco_ctrl = vco_ctrl_val(0x18, 0x1c),
+ .status = reg_val(0x28, 12, 1),
+};
+
+static const struct iproc_clk_ctrl genpll_clk[BCM_CYGNUS_NUM_GENPLL_CLKS] = {
+ [BCM_CYGNUS_GENPLL_AXI21_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_AXI21_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 6, 0, 12),
+ .mdiv = reg_val(0x20, 0, 8),
+ },
+ [BCM_CYGNUS_GENPLL_250MHZ_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_250MHZ_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 7, 1, 13),
+ .mdiv = reg_val(0x20, 10, 8),
+ },
+ [BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 8, 2, 14),
+ .mdiv = reg_val(0x20, 20, 8),
+ },
+ [BCM_CYGNUS_GENPLL_ENET_SW_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 9, 3, 15),
+ .mdiv = reg_val(0x24, 0, 8),
+ },
+ [BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 10, 4, 16),
+ .mdiv = reg_val(0x24, 10, 8),
+ },
+ [BCM_CYGNUS_GENPLL_CAN_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_CAN_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 11, 5, 17),
+ .mdiv = reg_val(0x24, 20, 8),
+ },
+};
+
+static const struct iproc_clk_ctrl lcpll0_clk[BCM_CYGNUS_NUM_LCPLL0_CLKS] = {
+ [BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 7, 1, 13),
+ .mdiv = reg_val(0x8, 0, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 8, 2, 14),
+ .mdiv = reg_val(0x8, 10, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_SDIO_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_SDIO_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 9, 3, 15),
+ .mdiv = reg_val(0x8, 20, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 10, 4, 16),
+ .mdiv = reg_val(0xc, 0, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 11, 5, 17),
+ .mdiv = reg_val(0xc, 10, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_CH5_UNUSED] = {
+ .channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 12, 6, 18),
+ .mdiv = reg_val(0xc, 20, 8),
+ },
+};
+
+static const struct iproc_clk_ctrl mipipll_clk[BCM_CYGNUS_NUM_MIPIPLL_CLKS] = {
+ [BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED,
+ .enable = enable_val(0x4, 12, 6, 18),
+ .mdiv = reg_val(0x20, 0, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH1_LCD] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH1_LCD,
+ .enable = enable_val(0x4, 13, 7, 19),
+ .mdiv = reg_val(0x20, 10, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH2_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH2_UNUSED,
+ .enable = enable_val(0x4, 14, 8, 20),
+ .mdiv = reg_val(0x20, 20, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED,
+ .enable = enable_val(0x4, 15, 9, 21),
+ .mdiv = reg_val(0x24, 0, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED,
+ .enable = enable_val(0x4, 16, 10, 22),
+ .mdiv = reg_val(0x24, 10, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED,
+ .enable = enable_val(0x4, 17, 11, 23),
+ .mdiv = reg_val(0x24, 20, 8),
+ },
+};
+
+static const struct iproc_asiu_div asiu_div[BCM_CYGNUS_NUM_ASIU_CLKS] = {
+ [BCM_CYGNUS_ASIU_KEYPAD_CLK] =
+ asiu_div_val(0x0, 31, 16, 10, 0, 10),
+ [BCM_CYGNUS_ASIU_ADC_CLK] =
+ asiu_div_val(0x4, 31, 16, 10, 0, 10),
+ [BCM_CYGNUS_ASIU_PWM_CLK] =
+ asiu_div_val(0x8, 31, 16, 10, 0, 10),
+};
+
+static const struct iproc_asiu_gate asiu_gate[BCM_CYGNUS_NUM_ASIU_CLKS] = {
+ [BCM_CYGNUS_ASIU_KEYPAD_CLK] =
+ asiu_gate_val(0x0, 7),
+ [BCM_CYGNUS_ASIU_ADC_CLK] =
+ asiu_gate_val(0x0, 9),
+ [BCM_CYGNUS_ASIU_PWM_CLK] =
+ asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0),
+};
+
+static void __init cygnus_armpll_init(struct device_node *node)
+{
+ iproc_armpll_setup(node);
+}
+CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init);
+
+static void __init cygnus_genpll_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &genpll, NULL, 0);
+}
+CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_init);
+
+static void __init cygnus_lcpll0_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &lcpll0, NULL, 0);
+}
+CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_init);
+
+static void __init cygnus_mipipll_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &mipipll, mipipll_vco_params,
+ ARRAY_SIZE(mipipll_vco_params));
+}
+CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_init);
+
+static void __init cygnus_genpll_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, genpll_clk, BCM_CYGNUS_NUM_GENPLL_CLKS);
+}
+CLK_OF_DECLARE(cygnus_genpll_clk, "brcm,cygnus-genpll-clk",
+ cygnus_genpll_clk_init);
+
+static void __init cygnus_lcpll0_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, lcpll0_clk, BCM_CYGNUS_NUM_LCPLL0_CLKS);
+}
+CLK_OF_DECLARE(cygnus_lcpll0_clk, "brcm,cygnus-lcpll0-clk",
+ cygnus_lcpll0_clk_init);
+
+static void __init cygnus_mipipll_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, mipipll_clk, BCM_CYGNUS_NUM_MIPIPLL_CLKS);
+}
+CLK_OF_DECLARE(cygnus_mipipll_clk, "brcm,cygnus-mipipll-clk",
+ cygnus_mipipll_clk_init);
+
+static void __init cygnus_asiu_init(struct device_node *node)
+{
+ iproc_asiu_setup(node, asiu_div, asiu_gate, BCM_CYGNUS_NUM_ASIU_CLKS);
+}
+CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init);
diff --git a/include/dt-bindings/clock/bcm-cygnus.h b/include/dt-bindings/clock/bcm-cygnus.h
new file mode 100644
index 0000000..45948b6
--- /dev/null
+++ b/include/dt-bindings/clock/bcm-cygnus.h
@@ -0,0 +1,77 @@
+/*
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Broadcom Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Broadcom Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLOCK_BCM_CYGNUS_H
+#define _CLOCK_BCM_CYGNUS_H
+
+/* number of GENPLL clocks */
+#define BCM_CYGNUS_NUM_GENPLL_CLKS 6
+
+/* GENPLL clock channel ID */
+#define BCM_CYGNUS_GENPLL_AXI21_CLK 0
+#define BCM_CYGNUS_GENPLL_250MHZ_CLK 1
+#define BCM_CYGNUS_GENPLL_IHOST_SYS_CLK 2
+#define BCM_CYGNUS_GENPLL_ENET_SW_CLK 3
+#define BCM_CYGNUS_GENPLL_AUDIO_125_CLK 4
+#define BCM_CYGNUS_GENPLL_CAN_CLK 5
+
+/* number of LCPLL0 clocks */
+#define BCM_CYGNUS_NUM_LCPLL0_CLKS 6
+
+/* LCPLL0 clock channel ID */
+#define BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK 0
+#define BCM_CYGNUS_LCPLL0_DDR_PHY_CLK 1
+#define BCM_CYGNUS_LCPLL0_SDIO_CLK 2
+#define BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK 3
+#define BCM_CYGNUS_LCPLL0_SMART_CARD_CLK 4
+#define BCM_CYGNUS_LCPLL0_CH5_UNUSED 5
+
+/* number of MIPI PLL clocks */
+#define BCM_CYGNUS_NUM_MIPIPLL_CLKS 6
+
+/* MIPI PLL clock channel ID */
+#define BCM_CYGNUS_MIPIPLL_CH0_UNUSED 0
+#define BCM_CYGNUS_MIPIPLL_CH1_LCD 1
+#define BCM_CYGNUS_MIPIPLL_CH2_UNUSED 2
+#define BCM_CYGNUS_MIPIPLL_CH3_UNUSED 3
+#define BCM_CYGNUS_MIPIPLL_CH4_UNUSED 4
+#define BCM_CYGNUS_MIPIPLL_CH5_UNUSED 5
+
+/* number of ASIU clocks */
+#define BCM_CYGNUS_NUM_ASIU_CLKS 3
+
+/* ASIU clock IDs */
+#define BCM_CYGNUS_ASIU_KEYPAD_CLK 0
+#define BCM_CYGNUS_ASIU_ADC_CLK 1
+#define BCM_CYGNUS_ASIU_PWM_CLK 2
+
+#endif /* _CLOCK_BCM_CYGNUS_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: enable clock support for Broadcom Cygnus
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
` (2 preceding siblings ...)
2014-11-28 1:27 ` [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus Ray Jui
@ 2014-11-28 1:27 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-11-28 1:27 UTC (permalink / raw)
To: linux-arm-kernel, Scott Branden
Cc: linux-kernel, bcm-kernel-feedback-list, Ray Jui
Replace current device tree dummy clocks with real clock support for
Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbraden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus-clock.dtsi | 110 ++++++++++++++++++++++++-------
arch/arm/boot/dts/bcm-cygnus.dtsi | 2 +-
2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/arch/arm/boot/dts/bcm-cygnus-clock.dtsi b/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
index 60d8389..f2da5e2 100644
--- a/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
@@ -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";
};
};
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..2b99e9c 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
};
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add common clock support for Broadcom iProc architecture
[not found] <Ray Jui <rjui@broadcom.com>
` (6 preceding siblings ...)
2014-11-28 1:27 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
@ 2014-12-04 21:43 ` Ray Jui
2014-12-04 21:43 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
` (3 more replies)
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (26 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:43 UTC (permalink / raw)
To: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King
Cc: linux-arm-kernel, devicetree, Scott Branden, linux-kernel,
bcm-kernel-feedback-list, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
@ 2014-12-04 21:43 ` Ray Jui
2014-12-04 21:43 ` [PATCH 2/4] clk: iproc: add initial common clock support Ray Jui
` (2 subsequent siblings)
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:43 UTC (permalink / raw)
To: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King
Cc: linux-arm-kernel, devicetree, Scott Branden, linux-kernel,
bcm-kernel-feedback-list, Ray Jui
Document the device tree binding for Broadcom iProc architecture based
clock controller
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
brcm,iproc-clocks.txt | 178 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
create mode 100644 brcm,iproc-clocks.txt
diff --git a/brcm,iproc-clocks.txt b/brcm,iproc-clocks.txt
new file mode 100644
index 0000000..cc64fd2
--- /dev/null
+++ b/brcm,iproc-clocks.txt
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] clk: iproc: add initial common clock support
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
2014-12-04 21:43 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
@ 2014-12-04 21:43 ` Ray Jui
2014-12-06 22:20 ` Tim Kryger
2014-12-04 21:43 ` [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus Ray Jui
2014-12-04 21:43 ` [PATCH 4/4] ARM: dts: enable " Ray Jui
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:43 UTC (permalink / raw)
To: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King
Cc: linux-arm-kernel, devicetree, Scott Branden, linux-kernel,
bcm-kernel-feedback-list, 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
<jonathar@broadcom.com>
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..eff0213 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -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/
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
index 75506e5..66b5b7f 100644
--- a/drivers/clk/bcm/Kconfig
+++ b/drivers/clk/bcm/Kconfig
@@ -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
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 6297d05..6926636 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -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
diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c
new file mode 100644
index 0000000..ec9b130
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-armpll.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c
new file mode 100644
index 0000000..ab86b8c
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-asiu.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-clk.c b/drivers/clk/bcm/clk-iproc-clk.c
new file mode 100644
index 0000000..be3c42c
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-clk.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c
new file mode 100644
index 0000000..cd3bd38
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc-pll.c
@@ -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);
+}
diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h
new file mode 100644
index 0000000..4aa0479
--- /dev/null
+++ b/drivers/clk/bcm/clk-iproc.h
@@ -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
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
2014-12-04 21:43 ` [PATCH 1/4] clk: iproc: define Broadcom iProc clock binding Ray Jui
2014-12-04 21:43 ` [PATCH 2/4] clk: iproc: add initial common clock support Ray Jui
@ 2014-12-04 21:43 ` Ray Jui
2014-12-04 21:43 ` [PATCH 4/4] ARM: dts: enable " Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:43 UTC (permalink / raw)
To: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King
Cc: linux-arm-kernel, devicetree, Scott Branden, linux-kernel,
bcm-kernel-feedback-list, 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.
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 6926636..afcbe55 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -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
diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c
new file mode 100644
index 0000000..f603d1d
--- /dev/null
+++ b/drivers/clk/bcm/clk-cygnus.c
@@ -0,0 +1,277 @@
+/*
+ * 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 <dt-bindings/clock/bcm-cygnus.h>
+#include "clk-iproc.h"
+
+#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, }
+
+#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \
+ .pwr_shift = ps, .iso_shift = is }
+
+#define asiu_div_val(o, es, hs, hw, ls, lw) \
+ { .offset = o, .en_shift = es, .high_shift = hs, \
+ .high_width = hw, .low_shift = ls, .low_width = lw }
+
+#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \
+ .reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \
+ .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \
+ .ka_width = kaw }
+
+#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo }
+
+#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \
+ .hold_shift = hs, .bypass_shift = bs }
+
+#define asiu_gate_val(o, es) { .offset = o, .en_shift = es }
+
+static const struct iproc_pll_ctrl genpll = {
+ .flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC,
+ .aon = aon_val(0x0, 2, 1, 0),
+ .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3),
+ .ndiv_int = reg_val(0x10, 20, 10),
+ .ndiv_frac = reg_val(0x10, 0, 20),
+ .pdiv = reg_val(0x14, 0, 4),
+ .vco_ctrl = vco_ctrl_val(0x18, 0x1c),
+ .status = reg_val(0x28, 12, 1),
+};
+
+static const struct iproc_pll_ctrl lcpll0 = {
+ .flags = IPROC_CLK_AON,
+ .aon = aon_val(0x0, 2, 5, 4),
+ .reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4),
+ .ndiv_int = reg_val(0x4, 16, 10),
+ .pdiv = reg_val(0x4, 26, 4),
+ .vco_ctrl = vco_ctrl_val(0x10, 0x14),
+ .status = reg_val(0x18, 12, 1),
+};
+
+/*
+ * MIPI PLL VCO frequency parameter table
+ */
+static const struct iproc_pll_vco_freq_param mipipll_vco_params[] = {
+ /* rate (Hz) ndiv_int ndiv_frac pdiv */
+ { 750000000UL, 30, 0, 1 },
+ { 1000000000UL, 40, 0, 1 },
+ { 1350000000ul, 54, 0, 1 },
+ { 2000000000UL, 80, 0, 1 },
+ { 2100000000UL, 84, 0, 1 },
+ { 2250000000UL, 90, 0, 1 },
+ { 2500000000UL, 100, 0, 1 },
+ { 2700000000UL, 54, 0, 0 },
+ { 2975000000UL, 119, 0, 1 },
+ { 3100000000UL, 124, 0, 1 },
+ { 3150000000UL, 126, 0, 1 },
+};
+
+static const struct iproc_pll_ctrl mipipll = {
+ .flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC,
+ .aon = aon_val(0x0, 4, 17, 16),
+ .asiu = asiu_gate_val(0x0, 3),
+ .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4),
+ .ndiv_int = reg_val(0x10, 20, 10),
+ .ndiv_frac = reg_val(0x10, 0, 20),
+ .pdiv = reg_val(0x14, 0, 4),
+ .vco_ctrl = vco_ctrl_val(0x18, 0x1c),
+ .status = reg_val(0x28, 12, 1),
+};
+
+static const struct iproc_clk_ctrl genpll_clk[BCM_CYGNUS_NUM_GENPLL_CLKS] = {
+ [BCM_CYGNUS_GENPLL_AXI21_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_AXI21_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 6, 0, 12),
+ .mdiv = reg_val(0x20, 0, 8),
+ },
+ [BCM_CYGNUS_GENPLL_250MHZ_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_250MHZ_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 7, 1, 13),
+ .mdiv = reg_val(0x20, 10, 8),
+ },
+ [BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 8, 2, 14),
+ .mdiv = reg_val(0x20, 20, 8),
+ },
+ [BCM_CYGNUS_GENPLL_ENET_SW_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 9, 3, 15),
+ .mdiv = reg_val(0x24, 0, 8),
+ },
+ [BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 10, 4, 16),
+ .mdiv = reg_val(0x24, 10, 8),
+ },
+ [BCM_CYGNUS_GENPLL_CAN_CLK] = {
+ .channel = BCM_CYGNUS_GENPLL_CAN_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x4, 11, 5, 17),
+ .mdiv = reg_val(0x24, 20, 8),
+ },
+};
+
+static const struct iproc_clk_ctrl lcpll0_clk[BCM_CYGNUS_NUM_LCPLL0_CLKS] = {
+ [BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 7, 1, 13),
+ .mdiv = reg_val(0x8, 0, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 8, 2, 14),
+ .mdiv = reg_val(0x8, 10, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_SDIO_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_SDIO_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 9, 3, 15),
+ .mdiv = reg_val(0x8, 20, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 10, 4, 16),
+ .mdiv = reg_val(0xc, 0, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = {
+ .channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 11, 5, 17),
+ .mdiv = reg_val(0xc, 10, 8),
+ },
+ [BCM_CYGNUS_LCPLL0_CH5_UNUSED] = {
+ .channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED,
+ .flags = IPROC_CLK_AON,
+ .enable = enable_val(0x0, 12, 6, 18),
+ .mdiv = reg_val(0xc, 20, 8),
+ },
+};
+
+static const struct iproc_clk_ctrl mipipll_clk[BCM_CYGNUS_NUM_MIPIPLL_CLKS] = {
+ [BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED,
+ .enable = enable_val(0x4, 12, 6, 18),
+ .mdiv = reg_val(0x20, 0, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH1_LCD] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH1_LCD,
+ .enable = enable_val(0x4, 13, 7, 19),
+ .mdiv = reg_val(0x20, 10, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH2_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH2_UNUSED,
+ .enable = enable_val(0x4, 14, 8, 20),
+ .mdiv = reg_val(0x20, 20, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED,
+ .enable = enable_val(0x4, 15, 9, 21),
+ .mdiv = reg_val(0x24, 0, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED,
+ .enable = enable_val(0x4, 16, 10, 22),
+ .mdiv = reg_val(0x24, 10, 8),
+ },
+ [BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = {
+ .channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED,
+ .enable = enable_val(0x4, 17, 11, 23),
+ .mdiv = reg_val(0x24, 20, 8),
+ },
+};
+
+static const struct iproc_asiu_div asiu_div[BCM_CYGNUS_NUM_ASIU_CLKS] = {
+ [BCM_CYGNUS_ASIU_KEYPAD_CLK] =
+ asiu_div_val(0x0, 31, 16, 10, 0, 10),
+ [BCM_CYGNUS_ASIU_ADC_CLK] =
+ asiu_div_val(0x4, 31, 16, 10, 0, 10),
+ [BCM_CYGNUS_ASIU_PWM_CLK] =
+ asiu_div_val(0x8, 31, 16, 10, 0, 10),
+};
+
+static const struct iproc_asiu_gate asiu_gate[BCM_CYGNUS_NUM_ASIU_CLKS] = {
+ [BCM_CYGNUS_ASIU_KEYPAD_CLK] =
+ asiu_gate_val(0x0, 7),
+ [BCM_CYGNUS_ASIU_ADC_CLK] =
+ asiu_gate_val(0x0, 9),
+ [BCM_CYGNUS_ASIU_PWM_CLK] =
+ asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0),
+};
+
+static void __init cygnus_armpll_init(struct device_node *node)
+{
+ iproc_armpll_setup(node);
+}
+CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init);
+
+static void __init cygnus_genpll_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &genpll, NULL, 0);
+}
+CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_init);
+
+static void __init cygnus_lcpll0_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &lcpll0, NULL, 0);
+}
+CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_init);
+
+static void __init cygnus_mipipll_init(struct device_node *node)
+{
+ iproc_pll_setup(node, &mipipll, mipipll_vco_params,
+ ARRAY_SIZE(mipipll_vco_params));
+}
+CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_init);
+
+static void __init cygnus_genpll_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, genpll_clk, BCM_CYGNUS_NUM_GENPLL_CLKS);
+}
+CLK_OF_DECLARE(cygnus_genpll_clk, "brcm,cygnus-genpll-clk",
+ cygnus_genpll_clk_init);
+
+static void __init cygnus_lcpll0_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, lcpll0_clk, BCM_CYGNUS_NUM_LCPLL0_CLKS);
+}
+CLK_OF_DECLARE(cygnus_lcpll0_clk, "brcm,cygnus-lcpll0-clk",
+ cygnus_lcpll0_clk_init);
+
+static void __init cygnus_mipipll_clk_init(struct device_node *node)
+{
+ iproc_clk_setup(node, mipipll_clk, BCM_CYGNUS_NUM_MIPIPLL_CLKS);
+}
+CLK_OF_DECLARE(cygnus_mipipll_clk, "brcm,cygnus-mipipll-clk",
+ cygnus_mipipll_clk_init);
+
+static void __init cygnus_asiu_init(struct device_node *node)
+{
+ iproc_asiu_setup(node, asiu_div, asiu_gate, BCM_CYGNUS_NUM_ASIU_CLKS);
+}
+CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init);
diff --git a/include/dt-bindings/clock/bcm-cygnus.h b/include/dt-bindings/clock/bcm-cygnus.h
new file mode 100644
index 0000000..45948b6
--- /dev/null
+++ b/include/dt-bindings/clock/bcm-cygnus.h
@@ -0,0 +1,77 @@
+/*
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Broadcom Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Broadcom Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLOCK_BCM_CYGNUS_H
+#define _CLOCK_BCM_CYGNUS_H
+
+/* number of GENPLL clocks */
+#define BCM_CYGNUS_NUM_GENPLL_CLKS 6
+
+/* GENPLL clock channel ID */
+#define BCM_CYGNUS_GENPLL_AXI21_CLK 0
+#define BCM_CYGNUS_GENPLL_250MHZ_CLK 1
+#define BCM_CYGNUS_GENPLL_IHOST_SYS_CLK 2
+#define BCM_CYGNUS_GENPLL_ENET_SW_CLK 3
+#define BCM_CYGNUS_GENPLL_AUDIO_125_CLK 4
+#define BCM_CYGNUS_GENPLL_CAN_CLK 5
+
+/* number of LCPLL0 clocks */
+#define BCM_CYGNUS_NUM_LCPLL0_CLKS 6
+
+/* LCPLL0 clock channel ID */
+#define BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK 0
+#define BCM_CYGNUS_LCPLL0_DDR_PHY_CLK 1
+#define BCM_CYGNUS_LCPLL0_SDIO_CLK 2
+#define BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK 3
+#define BCM_CYGNUS_LCPLL0_SMART_CARD_CLK 4
+#define BCM_CYGNUS_LCPLL0_CH5_UNUSED 5
+
+/* number of MIPI PLL clocks */
+#define BCM_CYGNUS_NUM_MIPIPLL_CLKS 6
+
+/* MIPI PLL clock channel ID */
+#define BCM_CYGNUS_MIPIPLL_CH0_UNUSED 0
+#define BCM_CYGNUS_MIPIPLL_CH1_LCD 1
+#define BCM_CYGNUS_MIPIPLL_CH2_UNUSED 2
+#define BCM_CYGNUS_MIPIPLL_CH3_UNUSED 3
+#define BCM_CYGNUS_MIPIPLL_CH4_UNUSED 4
+#define BCM_CYGNUS_MIPIPLL_CH5_UNUSED 5
+
+/* number of ASIU clocks */
+#define BCM_CYGNUS_NUM_ASIU_CLKS 3
+
+/* ASIU clock IDs */
+#define BCM_CYGNUS_ASIU_KEYPAD_CLK 0
+#define BCM_CYGNUS_ASIU_ADC_CLK 1
+#define BCM_CYGNUS_ASIU_PWM_CLK 2
+
+#endif /* _CLOCK_BCM_CYGNUS_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: enable clock support for Broadcom Cygnus
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
` (2 preceding siblings ...)
2014-12-04 21:43 ` [PATCH 3/4] clk: cygnus: add clock support for Broadcom Cygnus Ray Jui
@ 2014-12-04 21:43 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:43 UTC (permalink / raw)
To: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King
Cc: linux-arm-kernel, devicetree, Scott Branden, linux-kernel,
bcm-kernel-feedback-list, Ray Jui
Replace current device tree dummy clocks with real clock support for
Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbraden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus-clock.dtsi | 110 ++++++++++++++++++++++++-------
arch/arm/boot/dts/bcm-cygnus.dtsi | 2 +-
2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/arch/arm/boot/dts/bcm-cygnus-clock.dtsi b/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
index 60d8389..f2da5e2 100644
--- a/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus-clock.dtsi
@@ -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";
};
};
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..2b99e9c 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
};
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (7 preceding siblings ...)
2014-12-04 21:43 ` [PATCH 0/4] Add common clock support for Broadcom iProc architecture Ray Jui
@ 2014-12-04 21:56 ` Ray Jui
2014-12-04 21:56 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
` (3 more replies)
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (25 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:56 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-04 21:56 ` Ray Jui
2014-12-04 22:16 ` Belisko Marek
2014-12-04 21:56 ` [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
` (2 subsequent siblings)
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:56 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Device tree binding documentation for Broadcom Cygnus pinctrl driver
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../bindings/pinctrl/brcm,cygnus-pinctrl.txt | 92 ++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
new file mode 100644
index 0000000..86e4579
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-12-04 21:56 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
@ 2014-12-04 21:56 ` Ray Jui
2014-12-04 21:56 ` [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
2014-12-04 21:56 ` [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:56 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui, Fengguang Wu
This adds the initial driver support for the Broadcom Cygnus pinctrl
controller. The Cygnus pinctrl controller supports group based
alternate function configuration
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
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
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d014f22..4549e9f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -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
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c030b3d..4ed8e8a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -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
diff --git a/drivers/pinctrl/pinctrl-bcm-cygnus.c b/drivers/pinctrl/pinctrl-bcm-cygnus.c
new file mode 100644
index 0000000..eb6e27a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-bcm-cygnus.c
@@ -0,0 +1,753 @@
+/*
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+
+/*
+ * Alternate function configuration
+ *
+ * @name: name of the alternate function
+ * @group_names: array of strings of group names that can be supported by this
+ * alternate function
+ * @num_groups: total number of groups that can be supported by this alternate
+ * function
+ * @mux: mux setting for this alternate function to be programed
+ */
+struct cygnus_pin_function {
+ const char *name;
+ const char * const *group_names;
+ const unsigned num_groups;
+ unsigned int mux;
+};
+
+/*
+ * Cygnus allows group based pinmux configuration
+ *
+ * @name: name of the group
+ * @pins: array of pins used by this group
+ * @num_pins: total number of pins used by this group
+ * @offset: register offset for pinmux configuration of this group
+ * @shift: bit shift for pinmux configuration of this group
+ */
+struct cygnus_pin_group {
+ const char *name;
+ const unsigned *pins;
+ const unsigned num_pins;
+ const unsigned int offset;
+ const unsigned int shift;
+};
+
+/*
+ * 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;
+ struct device *dev;
+ void __iomem *base;
+
+ const struct pinctrl_pin_desc *pins;
+ unsigned num_pins;
+
+ const struct cygnus_pin_group *groups;
+ unsigned num_groups;
+
+ const struct cygnus_pin_function *functions;
+ unsigned num_functions;
+};
+
+#define CYGNUS_PIN_GROUP(group_name, off, sh) \
+{ \
+ .name = #group_name, \
+ .pins = group_name##_pins, \
+ .num_pins = ARRAY_SIZE(group_name##_pins), \
+ .offset = off, \
+ .shift = sh, \
+}
+
+/*
+ * The following pin description is based on Cygnus I/O MUX spreadsheet
+ */
+static const struct pinctrl_pin_desc cygnus_pinctrl_pins[] = {
+ PINCTRL_PIN(0, "ext_device_reset_n"),
+ PINCTRL_PIN(1, "chip_mode0"),
+ PINCTRL_PIN(2, "chip_mode1"),
+ PINCTRL_PIN(3, "chip_mode2"),
+ PINCTRL_PIN(4, "chip_mode3"),
+ PINCTRL_PIN(5, "chip_mode4"),
+ PINCTRL_PIN(6, "bsc0_scl"),
+ PINCTRL_PIN(7, "bsc0_sda"),
+ PINCTRL_PIN(8, "bsc1_scl"),
+ PINCTRL_PIN(9, "bsc1_sda"),
+ PINCTRL_PIN(10, "d1w_dq"),
+ PINCTRL_PIN(11, "d1wowstz_l"),
+ PINCTRL_PIN(12, "gpio0"),
+ PINCTRL_PIN(13, "gpio1"),
+ PINCTRL_PIN(14, "gpio2"),
+ PINCTRL_PIN(15, "gpio3"),
+ PINCTRL_PIN(16, "gpio4"),
+ PINCTRL_PIN(17, "gpio5"),
+ PINCTRL_PIN(18, "gpio6"),
+ PINCTRL_PIN(19, "gpio7"),
+ PINCTRL_PIN(20, "gpio8"),
+ PINCTRL_PIN(21, "gpio9"),
+ PINCTRL_PIN(22, "gpio10"),
+ PINCTRL_PIN(23, "gpio11"),
+ PINCTRL_PIN(24, "gpio12"),
+ PINCTRL_PIN(25, "gpio13"),
+ PINCTRL_PIN(26, "gpio14"),
+ PINCTRL_PIN(27, "gpio15"),
+ PINCTRL_PIN(28, "gpio16"),
+ PINCTRL_PIN(29, "gpio17"),
+ PINCTRL_PIN(30, "gpio18"),
+ PINCTRL_PIN(31, "gpio19"),
+ PINCTRL_PIN(32, "gpio20"),
+ PINCTRL_PIN(33, "gpio21"),
+ PINCTRL_PIN(34, "gpio22"),
+ PINCTRL_PIN(35, "gpio23"),
+ PINCTRL_PIN(36, "mdc"),
+ PINCTRL_PIN(37, "mdio"),
+ PINCTRL_PIN(38, "pwm0"),
+ PINCTRL_PIN(39, "pwm1"),
+ PINCTRL_PIN(40, "pwm2"),
+ PINCTRL_PIN(41, "pwm3"),
+ PINCTRL_PIN(42, "sc0_clk"),
+ PINCTRL_PIN(43, "sc0_cmdvcc_l"),
+ PINCTRL_PIN(44, "sc0_detect"),
+ PINCTRL_PIN(45, "sc0_fcb"),
+ PINCTRL_PIN(46, "sc0_io"),
+ PINCTRL_PIN(47, "sc0_rst_l"),
+ PINCTRL_PIN(48, "sc1_clk"),
+ PINCTRL_PIN(49, "sc1_cmdvcc_l"),
+ PINCTRL_PIN(50, "sc1_detect"),
+ PINCTRL_PIN(51, "sc1_fcb"),
+ PINCTRL_PIN(52, "sc1_io"),
+ PINCTRL_PIN(53, "sc1_rst_l"),
+ PINCTRL_PIN(54, "spi0_clk"),
+ PINCTRL_PIN(55, "spi0_mosi"),
+ PINCTRL_PIN(56, "spi0_miso"),
+ PINCTRL_PIN(57, "spi0_ss"),
+ PINCTRL_PIN(58, "spi1_clk"),
+ PINCTRL_PIN(59, "spi1_mosi"),
+ PINCTRL_PIN(60, "spi1_miso"),
+ PINCTRL_PIN(61, "spi1_ss"),
+ PINCTRL_PIN(62, "spi2_clk"),
+ PINCTRL_PIN(63, "spi2_mosi"),
+ PINCTRL_PIN(64, "spi2_miso"),
+ PINCTRL_PIN(65, "spi2_ss"),
+ PINCTRL_PIN(66, "spi3_clk"),
+ PINCTRL_PIN(67, "spi3_mosi"),
+ PINCTRL_PIN(68, "spi3_miso"),
+ PINCTRL_PIN(69, "spi3_ss"),
+ PINCTRL_PIN(70, "uart0_cts"),
+ PINCTRL_PIN(71, "uart0_rts"),
+ PINCTRL_PIN(72, "uart0_rx"),
+ PINCTRL_PIN(73, "uart0_tx"),
+ PINCTRL_PIN(74, "uart1_cts"),
+ PINCTRL_PIN(75, "uart1_dcd"),
+ PINCTRL_PIN(76, "uart1_dsr"),
+ PINCTRL_PIN(77, "uart1_dtr"),
+ PINCTRL_PIN(78, "uart1_ri"),
+ PINCTRL_PIN(79, "uart1_rts"),
+ PINCTRL_PIN(80, "uart1_rx"),
+ PINCTRL_PIN(81, "uart1_tx"),
+ PINCTRL_PIN(82, "uart3_rx"),
+ PINCTRL_PIN(83, "uart3_tx"),
+ PINCTRL_PIN(84, "sdio1_clk_sdcard"),
+ PINCTRL_PIN(85, "sdio1_cmd"),
+ PINCTRL_PIN(86, "sdio1_data0"),
+ PINCTRL_PIN(87, "sdio1_data1"),
+ PINCTRL_PIN(88, "sdio1_data2"),
+ PINCTRL_PIN(89, "sdio1_data3"),
+ PINCTRL_PIN(90, "sdio1_wp_n"),
+ PINCTRL_PIN(91, "sdio1_card_rst"),
+ PINCTRL_PIN(92, "sdio1_led_on"),
+ PINCTRL_PIN(93, "sdio1_cd"),
+ PINCTRL_PIN(94, "sdio0_clk_sdcard"),
+ PINCTRL_PIN(95, "sdio0_cmd"),
+ PINCTRL_PIN(96, "sdio0_data0"),
+ PINCTRL_PIN(97, "sdio0_data1"),
+ PINCTRL_PIN(98, "sdio0_data2"),
+ PINCTRL_PIN(99, "sdio0_data3"),
+ PINCTRL_PIN(100, "sdio0_wp_n"),
+ PINCTRL_PIN(101, "sdio0_card_rst"),
+ PINCTRL_PIN(102, "sdio0_led_on"),
+ PINCTRL_PIN(103, "sdio0_cd"),
+ PINCTRL_PIN(104, "sflash_clk"),
+ PINCTRL_PIN(105, "sflash_cs_l"),
+ PINCTRL_PIN(106, "sflash_mosi"),
+ PINCTRL_PIN(107, "sflash_miso"),
+ PINCTRL_PIN(108, "sflash_wp_n"),
+ PINCTRL_PIN(109, "sflash_hold_n"),
+ PINCTRL_PIN(110, "nand_ale"),
+ PINCTRL_PIN(111, "nand_ce0_l"),
+ PINCTRL_PIN(112, "nand_ce1_l"),
+ PINCTRL_PIN(113, "nand_cle"),
+ PINCTRL_PIN(114, "nand_dq0"),
+ PINCTRL_PIN(115, "nand_dq1"),
+ PINCTRL_PIN(116, "nand_dq2"),
+ PINCTRL_PIN(117, "nand_dq3"),
+ PINCTRL_PIN(118, "nand_dq4"),
+ PINCTRL_PIN(119, "nand_dq5"),
+ PINCTRL_PIN(120, "nand_dq6"),
+ PINCTRL_PIN(121, "nand_dq7"),
+ PINCTRL_PIN(122, "nand_rb_l"),
+ PINCTRL_PIN(123, "nand_re_l"),
+ PINCTRL_PIN(124, "nand_we_l"),
+ PINCTRL_PIN(125, "nand_wp_l"),
+ PINCTRL_PIN(126, "lcd_clac"),
+ PINCTRL_PIN(127, "lcd_clcp"),
+ PINCTRL_PIN(128, "lcd_cld0"),
+ PINCTRL_PIN(129, "lcd_cld1"),
+ PINCTRL_PIN(130, "lcd_cld10"),
+ PINCTRL_PIN(131, "lcd_cld11"),
+ PINCTRL_PIN(132, "lcd_cld12"),
+ PINCTRL_PIN(133, "lcd_cld13"),
+ PINCTRL_PIN(134, "lcd_cld14"),
+ PINCTRL_PIN(135, "lcd_cld15"),
+ PINCTRL_PIN(136, "lcd_cld16"),
+ PINCTRL_PIN(137, "lcd_cld17"),
+ PINCTRL_PIN(138, "lcd_cld18"),
+ PINCTRL_PIN(139, "lcd_cld19"),
+ PINCTRL_PIN(140, "lcd_cld2"),
+ PINCTRL_PIN(141, "lcd_cld20"),
+ PINCTRL_PIN(142, "lcd_cld21"),
+ PINCTRL_PIN(143, "lcd_cld22"),
+ PINCTRL_PIN(144, "lcd_cld23"),
+ PINCTRL_PIN(145, "lcd_cld3"),
+ PINCTRL_PIN(146, "lcd_cld4"),
+ PINCTRL_PIN(147, "lcd_cld5"),
+ PINCTRL_PIN(148, "lcd_cld6"),
+ PINCTRL_PIN(149, "lcd_cld7"),
+ PINCTRL_PIN(150, "lcd_cld8"),
+ PINCTRL_PIN(151, "lcd_cld9"),
+ PINCTRL_PIN(152, "lcd_clfp"),
+ PINCTRL_PIN(153, "lcd_clle"),
+ PINCTRL_PIN(154, "lcd_cllp"),
+ PINCTRL_PIN(155, "lcd_clpower"),
+ PINCTRL_PIN(156, "camera_vsync"),
+ PINCTRL_PIN(157, "camera_trigger"),
+ PINCTRL_PIN(158, "camera_strobe"),
+ PINCTRL_PIN(159, "camera_standby"),
+ PINCTRL_PIN(160, "camera_reset_n"),
+ PINCTRL_PIN(161, "camera_pixdata9"),
+ PINCTRL_PIN(162, "camera_pixdata8"),
+ PINCTRL_PIN(163, "camera_pixdata7"),
+ PINCTRL_PIN(164, "camera_pixdata6"),
+ PINCTRL_PIN(165, "camera_pixdata5"),
+ PINCTRL_PIN(166, "camera_pixdata4"),
+ PINCTRL_PIN(167, "camera_pixdata3"),
+ PINCTRL_PIN(168, "camera_pixdata2"),
+ PINCTRL_PIN(169, "camera_pixdata1"),
+ PINCTRL_PIN(170, "camera_pixdata0"),
+ PINCTRL_PIN(171, "camera_pixclk"),
+ PINCTRL_PIN(172, "camera_hsync"),
+ PINCTRL_PIN(173, "camera_pll_ref_clk"),
+ PINCTRL_PIN(174, "usb_id_indication"),
+ PINCTRL_PIN(175, "usb_vbus_indication"),
+ PINCTRL_PIN(176, "gpio0_3p3"),
+ PINCTRL_PIN(177, "gpio1_3p3"),
+ PINCTRL_PIN(178, "gpio2_3p3"),
+ PINCTRL_PIN(179, "gpio3_3p3"),
+};
+
+/*
+ * 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 };
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };
+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 };
+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 };
+
+/*
+ * 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",
+ "gpio1_3p3",
+ "gpio2_3p3",
+};
+
+/*
+ * List of groups. Need to match the order in cygnus_pin_group_names
+ */
+static const struct cygnus_pin_group cygnus_pin_groups[] = {
+ CYGNUS_PIN_GROUP(gpio0, 0x0, 0),
+ CYGNUS_PIN_GROUP(gpio1, 0x0, 4),
+ CYGNUS_PIN_GROUP(gpio2, 0x0, 8),
+ CYGNUS_PIN_GROUP(gpio3, 0x0, 12),
+ CYGNUS_PIN_GROUP(gpio4, 0x0, 16),
+ CYGNUS_PIN_GROUP(gpio5, 0x0, 20),
+ CYGNUS_PIN_GROUP(gpio6, 0x0, 24),
+ CYGNUS_PIN_GROUP(gpio7, 0x0, 28),
+ CYGNUS_PIN_GROUP(gpio8, 0x4, 0),
+ CYGNUS_PIN_GROUP(gpio9, 0x4, 4),
+ CYGNUS_PIN_GROUP(gpio10, 0x4, 8),
+ CYGNUS_PIN_GROUP(gpio11, 0x4, 12),
+ CYGNUS_PIN_GROUP(gpio12, 0x4, 16),
+ CYGNUS_PIN_GROUP(gpio13, 0x4, 20),
+ CYGNUS_PIN_GROUP(gpio14, 0x4, 24),
+ CYGNUS_PIN_GROUP(gpio15, 0x4, 28),
+ CYGNUS_PIN_GROUP(gpio16, 0x8, 0),
+ CYGNUS_PIN_GROUP(gpio17, 0x8, 4),
+ CYGNUS_PIN_GROUP(gpio18, 0x8, 8),
+ CYGNUS_PIN_GROUP(gpio19, 0x8, 12),
+ CYGNUS_PIN_GROUP(gpio20, 0x8, 16),
+ CYGNUS_PIN_GROUP(gpio21, 0x8, 20),
+ CYGNUS_PIN_GROUP(gpio22, 0x8, 24),
+ CYGNUS_PIN_GROUP(gpio23, 0x8, 28),
+ CYGNUS_PIN_GROUP(pwm0, 0xc, 0),
+ CYGNUS_PIN_GROUP(pwm1, 0xc, 4),
+ CYGNUS_PIN_GROUP(pwm2, 0xc, 8),
+ CYGNUS_PIN_GROUP(pwm3, 0xc, 12),
+ CYGNUS_PIN_GROUP(sdio0, 0xc, 16),
+ CYGNUS_PIN_GROUP(smart_card0, 0xc, 20),
+ CYGNUS_PIN_GROUP(smart_card1, 0xc, 24),
+ CYGNUS_PIN_GROUP(spi0, 0x10, 0),
+ CYGNUS_PIN_GROUP(spi1, 0x10, 4),
+ CYGNUS_PIN_GROUP(spi2, 0x10, 8),
+ CYGNUS_PIN_GROUP(spi3, 0x10, 12),
+ CYGNUS_PIN_GROUP(d1w, 0x10, 16),
+ CYGNUS_PIN_GROUP(lcd, 0x10, 20),
+ CYGNUS_PIN_GROUP(uart0, 0x14, 0),
+ CYGNUS_PIN_GROUP(uart1_dte, 0x14, 4),
+ CYGNUS_PIN_GROUP(uart1, 0x14, 8),
+ CYGNUS_PIN_GROUP(uart3, 0x14, 12),
+ CYGNUS_PIN_GROUP(qspi, 0x14, 16),
+ CYGNUS_PIN_GROUP(nand, 0x14, 20),
+ CYGNUS_PIN_GROUP(sdio0_cd, 0x18, 0),
+ CYGNUS_PIN_GROUP(sdio0_mmc, 0x18, 4),
+ CYGNUS_PIN_GROUP(can0_spi4, 0x18, 8),
+ CYGNUS_PIN_GROUP(can1_spi4, 0x18, 12),
+ CYGNUS_PIN_GROUP(sdio1_cd, 0x18, 16),
+ CYGNUS_PIN_GROUP(sdio1_led, 0x18, 20),
+ CYGNUS_PIN_GROUP(sdio1_mmc, 0x18, 24),
+ CYGNUS_PIN_GROUP(camera_led, 0x1c, 0),
+ CYGNUS_PIN_GROUP(camera_rgmii, 0x1c, 4),
+ CYGNUS_PIN_GROUP(camera_sram_rgmii, 0x1c, 8),
+ CYGNUS_PIN_GROUP(qspi_gpio, 0x1c, 12),
+ CYGNUS_PIN_GROUP(smart_card0_fcb, 0x20, 0),
+ CYGNUS_PIN_GROUP(smart_card1_fcb, 0x20, 4),
+ CYGNUS_PIN_GROUP(gpio0_3p3, 0x28, 0),
+ CYGNUS_PIN_GROUP(gpio1_3p3, 0x28, 4),
+ CYGNUS_PIN_GROUP(gpio2_3p3, 0x28, 8),
+};
+
+#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, \
+}
+
+/*
+ * 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),
+};
+
+static int cygnus_get_groups_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_groups;
+}
+
+static const char *cygnus_get_group_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->groups[selector].name;
+}
+
+static int cygnus_get_group_pins(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *pins = pinctrl->groups[selector].pins;
+ *num_pins = pinctrl->groups[selector].num_pins;
+
+ return 0;
+}
+
+static void cygnus_pin_dbg_show(struct pinctrl_dev *pctrl_dev,
+ struct seq_file *s, unsigned offset)
+{
+ seq_printf(s, " %s", dev_name(pctrl_dev->dev));
+}
+
+static int find_matched_function(const char *function_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cygnus_pin_functions); i++) {
+ if (!strcmp(cygnus_pin_functions[i].name, function_name))
+ return (int)cygnus_pin_functions[i].mux;
+ }
+
+ return -EINVAL;
+}
+
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+ struct device_node *np, struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ int ret, num_groups;
+ unsigned reserved_maps = 0;
+ struct property *prop;
+ const char *group_name, *function_name;
+
+ *map = NULL;
+ *num_maps = 0;
+
+ num_groups = of_property_count_strings(np, "brcm,groups");
+ if (num_groups < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,groups\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string(np, "brcm,function", &function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,function\n");
+ return -EINVAL;
+ }
+
+ /* make sure it's a valid alternate function */
+ ret = find_matched_function(function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev, "invalid function name: %s\n",
+ function_name);
+ }
+
+ ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+ num_maps, num_groups);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "unable to reserve map\n");
+ return ret;
+ }
+
+ of_property_for_each_string(np, "brcm,groups", prop, group_name) {
+ ret = pinctrl_utils_add_map_mux(pctrl_dev, map,
+ &reserved_maps, num_maps, group_name,
+ function_name);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "can't add map: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops cygnus_pinctrl_ops = {
+ .get_groups_count = cygnus_get_groups_count,
+ .get_group_name = cygnus_get_group_name,
+ .get_group_pins = cygnus_get_group_pins,
+ .pin_dbg_show = cygnus_pin_dbg_show,
+ .dt_node_to_map = cygnus_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_functions;
+}
+
+static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->functions[selector].name;
+}
+
+static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *groups = pinctrl->functions[selector].group_names;
+ *num_groups = pinctrl->functions[selector].num_groups;
+
+ return 0;
+}
+
+static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+ unsigned function_selector, unsigned group_selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ const struct cygnus_pin_function *function =
+ &pinctrl->functions[function_selector];
+ const struct cygnus_pin_group *group =
+ &pinctrl->groups[group_selector];
+ u32 val, mask = 0x7;
+
+ dev_dbg(pctrl_dev->dev,
+ "group:%s with offset:0x%08x shift:%u set to function: %s mux:%u\n",
+ group->name, group->offset, group->shift, function->name,
+ function->mux);
+
+ val = readl(pinctrl->base + group->offset);
+ val &= ~(mask << group->shift);
+ val |= function->mux << group->shift;
+ writel(val, pinctrl->base + group->offset);
+
+ return 0;
+}
+
+static struct pinmux_ops cygnus_pinmux_ops = {
+ .get_functions_count = cygnus_get_functions_count,
+ .get_function_name = cygnus_get_function_name,
+ .get_function_groups = cygnus_get_function_groups,
+ .set_mux = cygnus_pinmux_set_mux,
+};
+
+static struct pinctrl_desc cygnus_pinctrl_desc = {
+ .pctlops = &cygnus_pinctrl_ops,
+ .pmxops = &cygnus_pinmux_ops,
+ .owner = THIS_MODULE,
+};
+
+static int cygnus_pinctrl_probe(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl;
+ struct resource *res;
+
+ pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
+ if (!pinctrl) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ pinctrl->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get resource\n");
+ return -ENOENT;
+ }
+
+ pinctrl->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pinctrl->base)) {
+ dev_err(&pdev->dev, "unable to map I/O space\n");
+ return PTR_ERR(pinctrl->base);
+ }
+
+ pinctrl->pins = cygnus_pinctrl_pins;
+ pinctrl->num_pins = ARRAY_SIZE(cygnus_pinctrl_pins);
+ pinctrl->groups = cygnus_pin_groups;
+ pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups);
+ pinctrl->functions = cygnus_pin_functions;
+ pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions);
+
+ cygnus_pinctrl_desc.name = dev_name(&pdev->dev);
+ cygnus_pinctrl_desc.pins = cygnus_pinctrl_pins;
+ cygnus_pinctrl_desc.npins = ARRAY_SIZE(cygnus_pinctrl_pins);
+
+ pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev,
+ pinctrl);
+ if (!pinctrl->pctl) {
+ dev_err(&pdev->dev, "unable to register cygnus pinctrl\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, pinctrl);
+
+ return 0;
+}
+
+static int cygnus_pinctrl_remove(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(pinctrl->pctl);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct of_device_id cygnus_pinctrl_of_match[] = {
+ { .compatible = "brcm,cygnus-pinctrl", },
+ { },
+};
+
+static struct platform_driver cygnus_pinctrl_driver = {
+ .driver = {
+ .name = "cygnus-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = cygnus_pinctrl_of_match,
+ },
+ .probe = cygnus_pinctrl_probe,
+ .remove = cygnus_pinctrl_remove,
+};
+
+static int __init cygnus_pinctrl_init(void)
+{
+ return platform_driver_register(&cygnus_pinctrl_driver);
+}
+arch_initcall(cygnus_pinctrl_init);
+
+static void __exit cygnus_pinctrl_exit(void)
+{
+ platform_driver_unregister(&cygnus_pinctrl_driver);
+}
+module_exit(cygnus_pinctrl_exit);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus pinctrl driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-12-04 21:56 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
2014-12-04 21:56 ` [PATCH 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
@ 2014-12-04 21:56 ` Ray Jui
2014-12-04 21:56 ` [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:56 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables the pinctrl driver for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..b4efff2 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-04 21:56 ` [PATCH 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
@ 2014-12-04 21:56 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 21:56 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables the pinctrl support for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..4c6bf4d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding
2014-12-04 21:56 ` [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
@ 2014-12-04 22:16 ` Belisko Marek
2014-12-04 22:35 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Belisko Marek @ 2014-12-04 22:16 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King, devicetree, Scott Branden, LKML,
linux-gpio, bcm-kernel-feedback-list, linux-arm-kernel
On Thu, Dec 4, 2014 at 10:56 PM, Ray Jui <rjui@broadcom.com> wrote:
> Device tree binding documentation for Broadcom Cygnus pinctrl driver
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> .../bindings/pinctrl/brcm,cygnus-pinctrl.txt | 92 ++++++++++++++++++++
> 1 file changed, 92 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
> new file mode 100644
> index 0000000..86e4579
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
> @@ -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
^^^^ typo - should be I2S_0
> +
> +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
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
BR,
marek
--
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding
2014-12-04 22:16 ` Belisko Marek
@ 2014-12-04 22:35 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-04 22:35 UTC (permalink / raw)
To: Belisko Marek
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King, devicetree, Scott Branden, LKML,
linux-gpio, bcm-kernel-feedback-list, linux-arm-kernel
On 12/4/2014 2:16 PM, Belisko Marek wrote:
> On Thu, Dec 4, 2014 at 10:56 PM, Ray Jui <rjui@broadcom.com> wrote:
>> Device tree binding documentation for Broadcom Cygnus pinctrl driver
>>
>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>> ---
>> .../bindings/pinctrl/brcm,cygnus-pinctrl.txt | 92 ++++++++++++++++++++
>> 1 file changed, 92 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
>>
>> diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
>> new file mode 100644
>> index 0000000..86e4579
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
>> @@ -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
> ^^^^ typo - should be I2S_0
Oh yeah. Will change from I2C_0 to I2S_0. Thanks.
>> +
>> +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
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
> BR,
>
> marek
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (8 preceding siblings ...)
2014-12-04 21:56 ` [PATCH 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-05 19:51 ` Ray Jui
2014-12-05 19:51 ` [PATCH v2 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
` (3 more replies)
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (24 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-05 19:51 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-05 19:51 ` Ray Jui
2014-12-05 19:51 ` [PATCH v2 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
` (2 subsequent siblings)
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-05 19:51 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Device tree binding documentation for Broadcom Cygnus pinctrl driver
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../bindings/pinctrl/brcm,cygnus-pinctrl.txt | 92 ++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
new file mode 100644
index 0000000..4461aaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinctrl.txt
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 2/4] pinctrl: cygnus: add initial pinctrl support
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-12-05 19:51 ` [PATCH v2 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
@ 2014-12-05 19:51 ` Ray Jui
2014-12-05 19:51 ` [PATCH v2 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
2014-12-05 19:51 ` [PATCH v2 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-05 19:51 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui, Fengguang Wu
This adds the initial driver support for the Broadcom Cygnus pinctrl
controller. The Cygnus pinctrl controller supports group based
alternate function configuration
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
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
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d014f22..4549e9f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -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
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c030b3d..4ed8e8a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -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
diff --git a/drivers/pinctrl/pinctrl-bcm-cygnus.c b/drivers/pinctrl/pinctrl-bcm-cygnus.c
new file mode 100644
index 0000000..eb6e27a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-bcm-cygnus.c
@@ -0,0 +1,753 @@
+/*
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+
+/*
+ * Alternate function configuration
+ *
+ * @name: name of the alternate function
+ * @group_names: array of strings of group names that can be supported by this
+ * alternate function
+ * @num_groups: total number of groups that can be supported by this alternate
+ * function
+ * @mux: mux setting for this alternate function to be programed
+ */
+struct cygnus_pin_function {
+ const char *name;
+ const char * const *group_names;
+ const unsigned num_groups;
+ unsigned int mux;
+};
+
+/*
+ * Cygnus allows group based pinmux configuration
+ *
+ * @name: name of the group
+ * @pins: array of pins used by this group
+ * @num_pins: total number of pins used by this group
+ * @offset: register offset for pinmux configuration of this group
+ * @shift: bit shift for pinmux configuration of this group
+ */
+struct cygnus_pin_group {
+ const char *name;
+ const unsigned *pins;
+ const unsigned num_pins;
+ const unsigned int offset;
+ const unsigned int shift;
+};
+
+/*
+ * 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;
+ struct device *dev;
+ void __iomem *base;
+
+ const struct pinctrl_pin_desc *pins;
+ unsigned num_pins;
+
+ const struct cygnus_pin_group *groups;
+ unsigned num_groups;
+
+ const struct cygnus_pin_function *functions;
+ unsigned num_functions;
+};
+
+#define CYGNUS_PIN_GROUP(group_name, off, sh) \
+{ \
+ .name = #group_name, \
+ .pins = group_name##_pins, \
+ .num_pins = ARRAY_SIZE(group_name##_pins), \
+ .offset = off, \
+ .shift = sh, \
+}
+
+/*
+ * The following pin description is based on Cygnus I/O MUX spreadsheet
+ */
+static const struct pinctrl_pin_desc cygnus_pinctrl_pins[] = {
+ PINCTRL_PIN(0, "ext_device_reset_n"),
+ PINCTRL_PIN(1, "chip_mode0"),
+ PINCTRL_PIN(2, "chip_mode1"),
+ PINCTRL_PIN(3, "chip_mode2"),
+ PINCTRL_PIN(4, "chip_mode3"),
+ PINCTRL_PIN(5, "chip_mode4"),
+ PINCTRL_PIN(6, "bsc0_scl"),
+ PINCTRL_PIN(7, "bsc0_sda"),
+ PINCTRL_PIN(8, "bsc1_scl"),
+ PINCTRL_PIN(9, "bsc1_sda"),
+ PINCTRL_PIN(10, "d1w_dq"),
+ PINCTRL_PIN(11, "d1wowstz_l"),
+ PINCTRL_PIN(12, "gpio0"),
+ PINCTRL_PIN(13, "gpio1"),
+ PINCTRL_PIN(14, "gpio2"),
+ PINCTRL_PIN(15, "gpio3"),
+ PINCTRL_PIN(16, "gpio4"),
+ PINCTRL_PIN(17, "gpio5"),
+ PINCTRL_PIN(18, "gpio6"),
+ PINCTRL_PIN(19, "gpio7"),
+ PINCTRL_PIN(20, "gpio8"),
+ PINCTRL_PIN(21, "gpio9"),
+ PINCTRL_PIN(22, "gpio10"),
+ PINCTRL_PIN(23, "gpio11"),
+ PINCTRL_PIN(24, "gpio12"),
+ PINCTRL_PIN(25, "gpio13"),
+ PINCTRL_PIN(26, "gpio14"),
+ PINCTRL_PIN(27, "gpio15"),
+ PINCTRL_PIN(28, "gpio16"),
+ PINCTRL_PIN(29, "gpio17"),
+ PINCTRL_PIN(30, "gpio18"),
+ PINCTRL_PIN(31, "gpio19"),
+ PINCTRL_PIN(32, "gpio20"),
+ PINCTRL_PIN(33, "gpio21"),
+ PINCTRL_PIN(34, "gpio22"),
+ PINCTRL_PIN(35, "gpio23"),
+ PINCTRL_PIN(36, "mdc"),
+ PINCTRL_PIN(37, "mdio"),
+ PINCTRL_PIN(38, "pwm0"),
+ PINCTRL_PIN(39, "pwm1"),
+ PINCTRL_PIN(40, "pwm2"),
+ PINCTRL_PIN(41, "pwm3"),
+ PINCTRL_PIN(42, "sc0_clk"),
+ PINCTRL_PIN(43, "sc0_cmdvcc_l"),
+ PINCTRL_PIN(44, "sc0_detect"),
+ PINCTRL_PIN(45, "sc0_fcb"),
+ PINCTRL_PIN(46, "sc0_io"),
+ PINCTRL_PIN(47, "sc0_rst_l"),
+ PINCTRL_PIN(48, "sc1_clk"),
+ PINCTRL_PIN(49, "sc1_cmdvcc_l"),
+ PINCTRL_PIN(50, "sc1_detect"),
+ PINCTRL_PIN(51, "sc1_fcb"),
+ PINCTRL_PIN(52, "sc1_io"),
+ PINCTRL_PIN(53, "sc1_rst_l"),
+ PINCTRL_PIN(54, "spi0_clk"),
+ PINCTRL_PIN(55, "spi0_mosi"),
+ PINCTRL_PIN(56, "spi0_miso"),
+ PINCTRL_PIN(57, "spi0_ss"),
+ PINCTRL_PIN(58, "spi1_clk"),
+ PINCTRL_PIN(59, "spi1_mosi"),
+ PINCTRL_PIN(60, "spi1_miso"),
+ PINCTRL_PIN(61, "spi1_ss"),
+ PINCTRL_PIN(62, "spi2_clk"),
+ PINCTRL_PIN(63, "spi2_mosi"),
+ PINCTRL_PIN(64, "spi2_miso"),
+ PINCTRL_PIN(65, "spi2_ss"),
+ PINCTRL_PIN(66, "spi3_clk"),
+ PINCTRL_PIN(67, "spi3_mosi"),
+ PINCTRL_PIN(68, "spi3_miso"),
+ PINCTRL_PIN(69, "spi3_ss"),
+ PINCTRL_PIN(70, "uart0_cts"),
+ PINCTRL_PIN(71, "uart0_rts"),
+ PINCTRL_PIN(72, "uart0_rx"),
+ PINCTRL_PIN(73, "uart0_tx"),
+ PINCTRL_PIN(74, "uart1_cts"),
+ PINCTRL_PIN(75, "uart1_dcd"),
+ PINCTRL_PIN(76, "uart1_dsr"),
+ PINCTRL_PIN(77, "uart1_dtr"),
+ PINCTRL_PIN(78, "uart1_ri"),
+ PINCTRL_PIN(79, "uart1_rts"),
+ PINCTRL_PIN(80, "uart1_rx"),
+ PINCTRL_PIN(81, "uart1_tx"),
+ PINCTRL_PIN(82, "uart3_rx"),
+ PINCTRL_PIN(83, "uart3_tx"),
+ PINCTRL_PIN(84, "sdio1_clk_sdcard"),
+ PINCTRL_PIN(85, "sdio1_cmd"),
+ PINCTRL_PIN(86, "sdio1_data0"),
+ PINCTRL_PIN(87, "sdio1_data1"),
+ PINCTRL_PIN(88, "sdio1_data2"),
+ PINCTRL_PIN(89, "sdio1_data3"),
+ PINCTRL_PIN(90, "sdio1_wp_n"),
+ PINCTRL_PIN(91, "sdio1_card_rst"),
+ PINCTRL_PIN(92, "sdio1_led_on"),
+ PINCTRL_PIN(93, "sdio1_cd"),
+ PINCTRL_PIN(94, "sdio0_clk_sdcard"),
+ PINCTRL_PIN(95, "sdio0_cmd"),
+ PINCTRL_PIN(96, "sdio0_data0"),
+ PINCTRL_PIN(97, "sdio0_data1"),
+ PINCTRL_PIN(98, "sdio0_data2"),
+ PINCTRL_PIN(99, "sdio0_data3"),
+ PINCTRL_PIN(100, "sdio0_wp_n"),
+ PINCTRL_PIN(101, "sdio0_card_rst"),
+ PINCTRL_PIN(102, "sdio0_led_on"),
+ PINCTRL_PIN(103, "sdio0_cd"),
+ PINCTRL_PIN(104, "sflash_clk"),
+ PINCTRL_PIN(105, "sflash_cs_l"),
+ PINCTRL_PIN(106, "sflash_mosi"),
+ PINCTRL_PIN(107, "sflash_miso"),
+ PINCTRL_PIN(108, "sflash_wp_n"),
+ PINCTRL_PIN(109, "sflash_hold_n"),
+ PINCTRL_PIN(110, "nand_ale"),
+ PINCTRL_PIN(111, "nand_ce0_l"),
+ PINCTRL_PIN(112, "nand_ce1_l"),
+ PINCTRL_PIN(113, "nand_cle"),
+ PINCTRL_PIN(114, "nand_dq0"),
+ PINCTRL_PIN(115, "nand_dq1"),
+ PINCTRL_PIN(116, "nand_dq2"),
+ PINCTRL_PIN(117, "nand_dq3"),
+ PINCTRL_PIN(118, "nand_dq4"),
+ PINCTRL_PIN(119, "nand_dq5"),
+ PINCTRL_PIN(120, "nand_dq6"),
+ PINCTRL_PIN(121, "nand_dq7"),
+ PINCTRL_PIN(122, "nand_rb_l"),
+ PINCTRL_PIN(123, "nand_re_l"),
+ PINCTRL_PIN(124, "nand_we_l"),
+ PINCTRL_PIN(125, "nand_wp_l"),
+ PINCTRL_PIN(126, "lcd_clac"),
+ PINCTRL_PIN(127, "lcd_clcp"),
+ PINCTRL_PIN(128, "lcd_cld0"),
+ PINCTRL_PIN(129, "lcd_cld1"),
+ PINCTRL_PIN(130, "lcd_cld10"),
+ PINCTRL_PIN(131, "lcd_cld11"),
+ PINCTRL_PIN(132, "lcd_cld12"),
+ PINCTRL_PIN(133, "lcd_cld13"),
+ PINCTRL_PIN(134, "lcd_cld14"),
+ PINCTRL_PIN(135, "lcd_cld15"),
+ PINCTRL_PIN(136, "lcd_cld16"),
+ PINCTRL_PIN(137, "lcd_cld17"),
+ PINCTRL_PIN(138, "lcd_cld18"),
+ PINCTRL_PIN(139, "lcd_cld19"),
+ PINCTRL_PIN(140, "lcd_cld2"),
+ PINCTRL_PIN(141, "lcd_cld20"),
+ PINCTRL_PIN(142, "lcd_cld21"),
+ PINCTRL_PIN(143, "lcd_cld22"),
+ PINCTRL_PIN(144, "lcd_cld23"),
+ PINCTRL_PIN(145, "lcd_cld3"),
+ PINCTRL_PIN(146, "lcd_cld4"),
+ PINCTRL_PIN(147, "lcd_cld5"),
+ PINCTRL_PIN(148, "lcd_cld6"),
+ PINCTRL_PIN(149, "lcd_cld7"),
+ PINCTRL_PIN(150, "lcd_cld8"),
+ PINCTRL_PIN(151, "lcd_cld9"),
+ PINCTRL_PIN(152, "lcd_clfp"),
+ PINCTRL_PIN(153, "lcd_clle"),
+ PINCTRL_PIN(154, "lcd_cllp"),
+ PINCTRL_PIN(155, "lcd_clpower"),
+ PINCTRL_PIN(156, "camera_vsync"),
+ PINCTRL_PIN(157, "camera_trigger"),
+ PINCTRL_PIN(158, "camera_strobe"),
+ PINCTRL_PIN(159, "camera_standby"),
+ PINCTRL_PIN(160, "camera_reset_n"),
+ PINCTRL_PIN(161, "camera_pixdata9"),
+ PINCTRL_PIN(162, "camera_pixdata8"),
+ PINCTRL_PIN(163, "camera_pixdata7"),
+ PINCTRL_PIN(164, "camera_pixdata6"),
+ PINCTRL_PIN(165, "camera_pixdata5"),
+ PINCTRL_PIN(166, "camera_pixdata4"),
+ PINCTRL_PIN(167, "camera_pixdata3"),
+ PINCTRL_PIN(168, "camera_pixdata2"),
+ PINCTRL_PIN(169, "camera_pixdata1"),
+ PINCTRL_PIN(170, "camera_pixdata0"),
+ PINCTRL_PIN(171, "camera_pixclk"),
+ PINCTRL_PIN(172, "camera_hsync"),
+ PINCTRL_PIN(173, "camera_pll_ref_clk"),
+ PINCTRL_PIN(174, "usb_id_indication"),
+ PINCTRL_PIN(175, "usb_vbus_indication"),
+ PINCTRL_PIN(176, "gpio0_3p3"),
+ PINCTRL_PIN(177, "gpio1_3p3"),
+ PINCTRL_PIN(178, "gpio2_3p3"),
+ PINCTRL_PIN(179, "gpio3_3p3"),
+};
+
+/*
+ * 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 };
+static const unsigned gpio20_pins[] = { 32 };
+static const unsigned gpio21_pins[] = { 33 };
+static const unsigned gpio22_pins[] = { 34 };
+static const unsigned gpio23_pins[] = { 35 };
+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 };
+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 };
+
+/*
+ * 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",
+ "gpio1_3p3",
+ "gpio2_3p3",
+};
+
+/*
+ * List of groups. Need to match the order in cygnus_pin_group_names
+ */
+static const struct cygnus_pin_group cygnus_pin_groups[] = {
+ CYGNUS_PIN_GROUP(gpio0, 0x0, 0),
+ CYGNUS_PIN_GROUP(gpio1, 0x0, 4),
+ CYGNUS_PIN_GROUP(gpio2, 0x0, 8),
+ CYGNUS_PIN_GROUP(gpio3, 0x0, 12),
+ CYGNUS_PIN_GROUP(gpio4, 0x0, 16),
+ CYGNUS_PIN_GROUP(gpio5, 0x0, 20),
+ CYGNUS_PIN_GROUP(gpio6, 0x0, 24),
+ CYGNUS_PIN_GROUP(gpio7, 0x0, 28),
+ CYGNUS_PIN_GROUP(gpio8, 0x4, 0),
+ CYGNUS_PIN_GROUP(gpio9, 0x4, 4),
+ CYGNUS_PIN_GROUP(gpio10, 0x4, 8),
+ CYGNUS_PIN_GROUP(gpio11, 0x4, 12),
+ CYGNUS_PIN_GROUP(gpio12, 0x4, 16),
+ CYGNUS_PIN_GROUP(gpio13, 0x4, 20),
+ CYGNUS_PIN_GROUP(gpio14, 0x4, 24),
+ CYGNUS_PIN_GROUP(gpio15, 0x4, 28),
+ CYGNUS_PIN_GROUP(gpio16, 0x8, 0),
+ CYGNUS_PIN_GROUP(gpio17, 0x8, 4),
+ CYGNUS_PIN_GROUP(gpio18, 0x8, 8),
+ CYGNUS_PIN_GROUP(gpio19, 0x8, 12),
+ CYGNUS_PIN_GROUP(gpio20, 0x8, 16),
+ CYGNUS_PIN_GROUP(gpio21, 0x8, 20),
+ CYGNUS_PIN_GROUP(gpio22, 0x8, 24),
+ CYGNUS_PIN_GROUP(gpio23, 0x8, 28),
+ CYGNUS_PIN_GROUP(pwm0, 0xc, 0),
+ CYGNUS_PIN_GROUP(pwm1, 0xc, 4),
+ CYGNUS_PIN_GROUP(pwm2, 0xc, 8),
+ CYGNUS_PIN_GROUP(pwm3, 0xc, 12),
+ CYGNUS_PIN_GROUP(sdio0, 0xc, 16),
+ CYGNUS_PIN_GROUP(smart_card0, 0xc, 20),
+ CYGNUS_PIN_GROUP(smart_card1, 0xc, 24),
+ CYGNUS_PIN_GROUP(spi0, 0x10, 0),
+ CYGNUS_PIN_GROUP(spi1, 0x10, 4),
+ CYGNUS_PIN_GROUP(spi2, 0x10, 8),
+ CYGNUS_PIN_GROUP(spi3, 0x10, 12),
+ CYGNUS_PIN_GROUP(d1w, 0x10, 16),
+ CYGNUS_PIN_GROUP(lcd, 0x10, 20),
+ CYGNUS_PIN_GROUP(uart0, 0x14, 0),
+ CYGNUS_PIN_GROUP(uart1_dte, 0x14, 4),
+ CYGNUS_PIN_GROUP(uart1, 0x14, 8),
+ CYGNUS_PIN_GROUP(uart3, 0x14, 12),
+ CYGNUS_PIN_GROUP(qspi, 0x14, 16),
+ CYGNUS_PIN_GROUP(nand, 0x14, 20),
+ CYGNUS_PIN_GROUP(sdio0_cd, 0x18, 0),
+ CYGNUS_PIN_GROUP(sdio0_mmc, 0x18, 4),
+ CYGNUS_PIN_GROUP(can0_spi4, 0x18, 8),
+ CYGNUS_PIN_GROUP(can1_spi4, 0x18, 12),
+ CYGNUS_PIN_GROUP(sdio1_cd, 0x18, 16),
+ CYGNUS_PIN_GROUP(sdio1_led, 0x18, 20),
+ CYGNUS_PIN_GROUP(sdio1_mmc, 0x18, 24),
+ CYGNUS_PIN_GROUP(camera_led, 0x1c, 0),
+ CYGNUS_PIN_GROUP(camera_rgmii, 0x1c, 4),
+ CYGNUS_PIN_GROUP(camera_sram_rgmii, 0x1c, 8),
+ CYGNUS_PIN_GROUP(qspi_gpio, 0x1c, 12),
+ CYGNUS_PIN_GROUP(smart_card0_fcb, 0x20, 0),
+ CYGNUS_PIN_GROUP(smart_card1_fcb, 0x20, 4),
+ CYGNUS_PIN_GROUP(gpio0_3p3, 0x28, 0),
+ CYGNUS_PIN_GROUP(gpio1_3p3, 0x28, 4),
+ CYGNUS_PIN_GROUP(gpio2_3p3, 0x28, 8),
+};
+
+#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, \
+}
+
+/*
+ * 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),
+};
+
+static int cygnus_get_groups_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_groups;
+}
+
+static const char *cygnus_get_group_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->groups[selector].name;
+}
+
+static int cygnus_get_group_pins(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *pins = pinctrl->groups[selector].pins;
+ *num_pins = pinctrl->groups[selector].num_pins;
+
+ return 0;
+}
+
+static void cygnus_pin_dbg_show(struct pinctrl_dev *pctrl_dev,
+ struct seq_file *s, unsigned offset)
+{
+ seq_printf(s, " %s", dev_name(pctrl_dev->dev));
+}
+
+static int find_matched_function(const char *function_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cygnus_pin_functions); i++) {
+ if (!strcmp(cygnus_pin_functions[i].name, function_name))
+ return (int)cygnus_pin_functions[i].mux;
+ }
+
+ return -EINVAL;
+}
+
+static int cygnus_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+ struct device_node *np, struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ int ret, num_groups;
+ unsigned reserved_maps = 0;
+ struct property *prop;
+ const char *group_name, *function_name;
+
+ *map = NULL;
+ *num_maps = 0;
+
+ num_groups = of_property_count_strings(np, "brcm,groups");
+ if (num_groups < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,groups\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string(np, "brcm,function", &function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev,
+ "could not parse property brcm,function\n");
+ return -EINVAL;
+ }
+
+ /* make sure it's a valid alternate function */
+ ret = find_matched_function(function_name);
+ if (ret < 0) {
+ dev_err(pctrl_dev->dev, "invalid function name: %s\n",
+ function_name);
+ }
+
+ ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+ num_maps, num_groups);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "unable to reserve map\n");
+ return ret;
+ }
+
+ of_property_for_each_string(np, "brcm,groups", prop, group_name) {
+ ret = pinctrl_utils_add_map_mux(pctrl_dev, map,
+ &reserved_maps, num_maps, group_name,
+ function_name);
+ if (ret) {
+ dev_err(pctrl_dev->dev, "can't add map: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops cygnus_pinctrl_ops = {
+ .get_groups_count = cygnus_get_groups_count,
+ .get_group_name = cygnus_get_group_name,
+ .get_group_pins = cygnus_get_group_pins,
+ .pin_dbg_show = cygnus_pin_dbg_show,
+ .dt_node_to_map = cygnus_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->num_functions;
+}
+
+static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev,
+ unsigned selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ return pinctrl->functions[selector].name;
+}
+
+static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+ *groups = pinctrl->functions[selector].group_names;
+ *num_groups = pinctrl->functions[selector].num_groups;
+
+ return 0;
+}
+
+static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+ unsigned function_selector, unsigned group_selector)
+{
+ struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ const struct cygnus_pin_function *function =
+ &pinctrl->functions[function_selector];
+ const struct cygnus_pin_group *group =
+ &pinctrl->groups[group_selector];
+ u32 val, mask = 0x7;
+
+ dev_dbg(pctrl_dev->dev,
+ "group:%s with offset:0x%08x shift:%u set to function: %s mux:%u\n",
+ group->name, group->offset, group->shift, function->name,
+ function->mux);
+
+ val = readl(pinctrl->base + group->offset);
+ val &= ~(mask << group->shift);
+ val |= function->mux << group->shift;
+ writel(val, pinctrl->base + group->offset);
+
+ return 0;
+}
+
+static struct pinmux_ops cygnus_pinmux_ops = {
+ .get_functions_count = cygnus_get_functions_count,
+ .get_function_name = cygnus_get_function_name,
+ .get_function_groups = cygnus_get_function_groups,
+ .set_mux = cygnus_pinmux_set_mux,
+};
+
+static struct pinctrl_desc cygnus_pinctrl_desc = {
+ .pctlops = &cygnus_pinctrl_ops,
+ .pmxops = &cygnus_pinmux_ops,
+ .owner = THIS_MODULE,
+};
+
+static int cygnus_pinctrl_probe(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl;
+ struct resource *res;
+
+ pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
+ if (!pinctrl) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ pinctrl->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get resource\n");
+ return -ENOENT;
+ }
+
+ pinctrl->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pinctrl->base)) {
+ dev_err(&pdev->dev, "unable to map I/O space\n");
+ return PTR_ERR(pinctrl->base);
+ }
+
+ pinctrl->pins = cygnus_pinctrl_pins;
+ pinctrl->num_pins = ARRAY_SIZE(cygnus_pinctrl_pins);
+ pinctrl->groups = cygnus_pin_groups;
+ pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups);
+ pinctrl->functions = cygnus_pin_functions;
+ pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions);
+
+ cygnus_pinctrl_desc.name = dev_name(&pdev->dev);
+ cygnus_pinctrl_desc.pins = cygnus_pinctrl_pins;
+ cygnus_pinctrl_desc.npins = ARRAY_SIZE(cygnus_pinctrl_pins);
+
+ pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev,
+ pinctrl);
+ if (!pinctrl->pctl) {
+ dev_err(&pdev->dev, "unable to register cygnus pinctrl\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, pinctrl);
+
+ return 0;
+}
+
+static int cygnus_pinctrl_remove(struct platform_device *pdev)
+{
+ struct cygnus_pinctrl *pinctrl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(pinctrl->pctl);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct of_device_id cygnus_pinctrl_of_match[] = {
+ { .compatible = "brcm,cygnus-pinctrl", },
+ { },
+};
+
+static struct platform_driver cygnus_pinctrl_driver = {
+ .driver = {
+ .name = "cygnus-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = cygnus_pinctrl_of_match,
+ },
+ .probe = cygnus_pinctrl_probe,
+ .remove = cygnus_pinctrl_remove,
+};
+
+static int __init cygnus_pinctrl_init(void)
+{
+ return platform_driver_register(&cygnus_pinctrl_driver);
+}
+arch_initcall(cygnus_pinctrl_init);
+
+static void __exit cygnus_pinctrl_exit(void)
+{
+ platform_driver_unregister(&cygnus_pinctrl_driver);
+}
+module_exit(cygnus_pinctrl_exit);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus pinctrl driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
2014-12-05 19:51 ` [PATCH v2 1/4] pinctrl: Broadcom Cygnus pinctrl device tree binding Ray Jui
2014-12-05 19:51 ` [PATCH v2 2/4] pinctrl: cygnus: add initial pinctrl support Ray Jui
@ 2014-12-05 19:51 ` Ray Jui
2014-12-05 19:51 ` [PATCH v2 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-05 19:51 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables the pinctrl driver for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..b4efff2 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 4/4] ARM: dts: enable pinctrl for Broadcom Cygnus
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-05 19:51 ` [PATCH v2 3/4] ARM: mach-bcm: enable pinctrl support for Cygnus Ray Jui
@ 2014-12-05 19:51 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-05 19:51 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables the pinctrl support for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..4c6bf4d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (9 preceding siblings ...)
2014-12-05 19:51 ` [PATCH v2 0/4] Add pinctrl support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
2014-12-06 0:40 ` [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
` (4 more replies)
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (23 subsequent siblings)
34 siblings, 5 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
2015-01-13 7:57 ` Linus Walleij
2014-12-06 0:40 ` [PATCH 2/5] gpio: Cygnus: add GPIO driver Ray Jui
` (3 subsequent siblings)
4 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the GPIO device tree binding for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../devicetree/bindings/gpio/brcm,cygnus-gpio.txt | 85 ++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
new file mode 100644
index 0000000..24a1513
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
@@ -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>
+ }
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-06 0:40 ` [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
2014-12-06 1:28 ` Joe Perches
2014-12-06 0:40 ` [PATCH 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
` (2 subsequent siblings)
4 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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")
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 633ec21..3e3b0342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 81755f1..31eb7e0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -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
diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
new file mode 100644
index 0000000..1549ea8
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-cygnus.c
@@ -0,0 +1,719 @@
+/*
+ * 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;
+ 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;
+
+static inline struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(
+ struct gpio_chip *gc)
+{
+ return container_of(gc, struct bcm_cygnus_gpio, gc);
+}
+
+static inline 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 inline unsigned int __gpio_reg_offset(
+ struct bcm_cygnus_gpio *cygnus_gpio,
+ unsigned gpio)
+{
+ return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}
+
+static inline unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+ unsigned gpio)
+{
+ return GPIO_BIT(gpio);
+}
+
+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 */
+ 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) {
+ for_each_set_bit(bit, &val, 32) {
+ 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);
+}
+
+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;
+ 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);
+}
+
+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;
+ 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;
+ 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 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);
+ 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);
+
+ 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);
+
+ 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);
+
+ 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, up;
+ unsigned long flags;
+
+ switch (pull) {
+ case GPIO_PULL_NONE:
+ return;
+ case GPIO_PULL_UP:
+ up = 1;
+ break;
+ case GPIO_PULL_DOWN:
+ up = 0;
+ break;
+ 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 (up)
+ 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;
+
+ if (of_device_is_compatible(dev->of_node, "brcm,cygnus-asiu-gpio")) {
+ base = cygnus_gpio->base;
+ offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+ CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET;
+ } else if (of_device_is_compatible(dev->of_node,
+ "brcm,cygnus-ccm-gpio")) {
+ if (!cygnus_gpio->io_ctrl)
+ return;
+
+ base = cygnus_gpio->io_ctrl;
+ offset = CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET;
+ } else
+ return;
+
+ 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-crmu-gpio" },
+ { .compatible = "brcm,cygnus-asiu-gpio" },
+ { .compatible = "brcm,cygnus-ccm-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;
+
+ match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to find GPIO controller\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio = devm_kzalloc(dev, sizeof(*cygnus_gpio), GFP_KERNEL);
+ if (!cygnus_gpio)
+ return -ENOMEM;
+
+ cygnus_gpio->dev = dev;
+ platform_set_drvdata(pdev, cygnus_gpio);
+
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "missing ngpios device tree property\n");
+ return -ENODEV;
+ }
+ cygnus_gpio->num_banks = (ngpios + NGPIOS_PER_BANK - 1) /
+ NGPIOS_PER_BANK;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get I/O resource");
+ return -ENODEV;
+ }
+
+ cygnus_gpio->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->base)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->base);
+ }
+
+ /*
+ * Only certain types of Cygnus GPIO interfaces have I/O control
+ * registers
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ cygnus_gpio->io_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->io_ctrl)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->io_ctrl);
+ }
+ }
+
+ spin_lock_init(&cygnus_gpio->lock);
+
+ gc = &cygnus_gpio->gc;
+ gc->base = gpio_base_index;
+ gpio_base_index += ngpios;
+ gc->ngpio = ngpios;
+ gc->label = dev_name(dev);
+ gc->dev = dev;
+#ifdef CONFIG_OF_GPIO
+ gc->of_node = dev->of_node;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = bcm_cygnus_gpio_of_xlate;
+#endif
+ gc->direction_input = bcm_cygnus_gpio_direction_input;
+ gc->direction_output = bcm_cygnus_gpio_direction_output;
+ gc->set = bcm_cygnus_gpio_set;
+ gc->get = bcm_cygnus_gpio_get;
+ gc->to_irq = bcm_cygnus_gpio_to_irq;
+
+ ret = gpiochip_add(gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to add GPIO chip\n");
+ goto err_dec_gpio_base;
+ }
+
+ /*
+ * Some of the GPIO interfaces do not have interrupt wired to the main
+ * processor
+ */
+ if (of_find_property(dev->of_node, "no-interrupt", NULL))
+ return 0;
+
+ cygnus_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ gc->ngpio, &bcm_cygnus_irq_ops, cygnus_gpio);
+ if (!cygnus_gpio->irq_domain) {
+ dev_err(&pdev->dev, "unable to allocate IRQ domain\n");
+ ret = -ENXIO;
+ goto err_rm_gpiochip;
+ }
+
+ cygnus_gpio->irq = platform_get_irq(pdev, 0);
+ if (cygnus_gpio->irq < 0) {
+ dev_err(&pdev->dev, "unable to get IRQ\n");
+ ret = cygnus_gpio->irq;
+ goto err_rm_irq_domain;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ int irq = irq_create_mapping(cygnus_gpio->irq_domain, i);
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, cygnus_gpio);
+ irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(cygnus_gpio->irq, bcm_cygnus_gpio_irq_handler);
+ irq_set_handler_data(cygnus_gpio->irq, cygnus_gpio);
+
+ return 0;
+
+err_rm_irq_domain:
+ irq_domain_remove(cygnus_gpio->irq_domain);
+
+err_rm_gpiochip:
+ gpiochip_remove(gc);
+
+err_dec_gpio_base:
+ gpio_base_index -= ngpios;
+ return ret;
+}
+
+static struct platform_driver bcm_cygnus_gpio_driver = {
+ .driver = {
+ .name = "bcm-cygnus-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm_cygnus_gpio_of_match,
+ },
+ .probe = bcm_cygnus_gpio_probe,
+};
+
+module_platform_driver(bcm_cygnus_gpio_driver);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-06 0:40 ` [PATCH 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
2014-12-06 0:40 ` [PATCH 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
2014-12-06 0:40 ` [PATCH 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
2014-12-06 0:40 ` [PATCH 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..5066d5d 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/5] ARM: dts: enable GPIO for Broadcom Cygnus
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-06 0:40 ` [PATCH 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
2014-12-06 0:40 ` [PATCH 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables all 3 GPIO controllers including the ASIU GPIO, the
chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..c7587c1 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 5/5] MAINTAINERS: Entry for Cygnus GPIO driver
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (3 preceding siblings ...)
2014-12-06 0:40 ` [PATCH 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
@ 2014-12-06 0:40 ` Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-06 0:40 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 7b712d8..5d67204 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2174,6 +2174,13 @@ N: bcm9583*
N: bcm583*
N: bcm113*
+BROADCOM CYGNUS GPIO DRIVER
+M: Ray Jui <rjui@broadcom.com>
+L: bcm-kernel-feedback-list@broadcom.com
+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 <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 0:40 ` [PATCH 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-06 1:28 ` Joe Perches
2014-12-06 2:14 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Joe Perches @ 2014-12-06 1:28 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
> 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:
> diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
> +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.
> +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 */
> + 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
> + 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?
> + 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);
> +}
> +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?
> +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?
> +#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; ?
> + unsigned long flags;
> +
> + switch (pull) {
> + case GPIO_PULL_NONE:
> + return;
> + 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
> +static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
> +{
[]
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "unable to get I/O resource");
missing newline
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 1:28 ` Joe Perches
@ 2014-12-06 2:14 ` Ray Jui
2014-12-06 2:34 ` Joe Perches
2014-12-08 1:59 ` Ray Jui
0 siblings, 2 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-06 2:14 UTC (permalink / raw)
To: Joe Perches
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
Thanks for your review, Joe:
On 12/5/2014 5:28 PM, Joe Perches wrote:
> On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
>> 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:
>
>> diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
>
>> +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.
Okay I can remove all inlines.
>
>
>> +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 */
>> + 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.
>> + 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'.
>> + 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);
>> +}
>
>> +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
>> +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.
>> +#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.
>> + unsigned long flags;
>> +
>> + switch (pull) {
>> + case GPIO_PULL_NONE:
>> + return;
>> + 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
>
>
Good suggestion, will do the following:
case GPIO_PULL_NONE:
case GPIO_PULL_INVALID:
default:
return;
>> +static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
>> +{
> []
>> + 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");
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 2:14 ` Ray Jui
@ 2014-12-06 2:34 ` Joe Perches
2014-12-06 3:41 ` Ray Jui
2014-12-08 1:59 ` Ray Jui
1 sibling, 1 reply; 355+ messages in thread
From: Joe Perches @ 2014-12-06 2:34 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On Fri, 2014-12-05 at 18:14 -0800, Ray Jui wrote:
> On 12/5/2014 5:28 PM, Joe Perches wrote:
> > On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
> >> +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 */
> >> + 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.
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.
> 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.
>
> >> + 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'.
All the bit operations only work on long *
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 2:34 ` Joe Perches
@ 2014-12-06 3:41 ` Ray Jui
2014-12-06 4:24 ` Joe Perches
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-06 3:41 UTC (permalink / raw)
To: Joe Perches
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On 12/5/2014 6:34 PM, Joe Perches wrote:
> On Fri, 2014-12-05 at 18:14 -0800, Ray Jui wrote:
>> On 12/5/2014 5:28 PM, Joe Perches wrote:
>>> On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
>>>> +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 */
>>>> + 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.
>
> 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.
>
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.
>> 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.
>>
>>>> + 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'.
>
> 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...
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 3:41 ` Ray Jui
@ 2014-12-06 4:24 ` Joe Perches
2014-12-08 1:34 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Joe Perches @ 2014-12-06 4:24 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On Fri, 2014-12-05 at 19:41 -0800, Ray Jui wrote:
> On 12/5/2014 6:34 PM, Joe Perches wrote:
> > On Fri, 2014-12-05 at 18:14 -0800, Ray Jui wrote:
> >> On 12/5/2014 5:28 PM, Joe Perches wrote:
> >>> On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
> >>>> + for_each_set_bit(bit, &val, 32) {
[]
> 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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] clk: iproc: add initial common clock support
2014-12-04 21:43 ` [PATCH 2/4] clk: iproc: add initial common clock support Ray Jui
@ 2014-12-06 22:20 ` Tim Kryger
2014-12-08 1:38 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Tim Kryger @ 2014-12-06 22:20 UTC (permalink / raw)
To: Ray Jui
Cc: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King, devicetree,
Scott Branden, Linux Kernel Mailing List,
bcm-kernel-feedback-list, linux-arm-kernel
On Thu, Dec 4, 2014 at 1:43 PM, Ray Jui <rjui@broadcom.com> wrote:
> 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
> <jonathar@broadcom.com>
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> 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
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index d5fba5b..eff0213 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -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.
> diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
> index 75506e5..66b5b7f 100644
> --- a/drivers/clk/bcm/Kconfig
> +++ b/drivers/clk/bcm/Kconfig
> @@ -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
> diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
> index 6297d05..6926636 100644
> --- a/drivers/clk/bcm/Makefile
> +++ b/drivers/clk/bcm/Makefile
> @@ -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
> diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c
> new file mode 100644
> index 0000000..ec9b130
> --- /dev/null
> +++ b/drivers/clk/bcm/clk-iproc-armpll.c
> @@ -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);
> +}
> diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c
> new file mode 100644
> index 0000000..ab86b8c
> --- /dev/null
> +++ b/drivers/clk/bcm/clk-iproc-asiu.c
> @@ -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);
> +}
> diff --git a/drivers/clk/bcm/clk-iproc-clk.c b/drivers/clk/bcm/clk-iproc-clk.c
> new file mode 100644
> index 0000000..be3c42c
> --- /dev/null
> +++ b/drivers/clk/bcm/clk-iproc-clk.c
> @@ -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);
> +}
> diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c
> new file mode 100644
> index 0000000..cd3bd38
> --- /dev/null
> +++ b/drivers/clk/bcm/clk-iproc-pll.c
> @@ -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);
> +}
> diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h
> new file mode 100644
> index 0000000..4aa0479
> --- /dev/null
> +++ b/drivers/clk/bcm/clk-iproc.h
> @@ -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@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 4:24 ` Joe Perches
@ 2014-12-08 1:34 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 1:34 UTC (permalink / raw)
To: Joe Perches
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On 12/5/2014 8:24 PM, Joe Perches wrote:
> On Fri, 2014-12-05 at 19:41 -0800, Ray Jui wrote:
>> On 12/5/2014 6:34 PM, Joe Perches wrote:
>>> On Fri, 2014-12-05 at 18:14 -0800, Ray Jui wrote:
>>>> On 12/5/2014 5:28 PM, Joe Perches wrote:
>>>>> On Fri, 2014-12-05 at 16:40 -0800, Ray Jui wrote:
>>>>>> + for_each_set_bit(bit, &val, 32) {
> []
>> 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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] clk: iproc: add initial common clock support
2014-12-06 22:20 ` Tim Kryger
@ 2014-12-08 1:38 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 1:38 UTC (permalink / raw)
To: Tim Kryger
Cc: Mike Turquette, Matt Porter, Alex Elder, Tim Kryger, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Russell King, devicetree,
Scott Branden, Linux Kernel Mailing List,
bcm-kernel-feedback-list, linux-arm-kernel
On 12/6/2014 2:20 PM, Tim Kryger wrote:
> On Thu, Dec 4, 2014 at 1:43 PM, Ray Jui <rjui@broadcom.com> wrote:
>> 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
>> <jonathar@broadcom.com>
>>
>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>> ---
>> 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
>>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index d5fba5b..eff0213 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -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.
>> diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
>> index 75506e5..66b5b7f 100644
>> --- a/drivers/clk/bcm/Kconfig
>> +++ b/drivers/clk/bcm/Kconfig
>> @@ -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
>> diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
>> index 6297d05..6926636 100644
>> --- a/drivers/clk/bcm/Makefile
>> +++ b/drivers/clk/bcm/Makefile
>> @@ -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
>> diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c
>> new file mode 100644
>> index 0000000..ec9b130
>> --- /dev/null
>> +++ b/drivers/clk/bcm/clk-iproc-armpll.c
>> @@ -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);
>> +}
>> diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c
>> new file mode 100644
>> index 0000000..ab86b8c
>> --- /dev/null
>> +++ b/drivers/clk/bcm/clk-iproc-asiu.c
>> @@ -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);
>> +}
>> diff --git a/drivers/clk/bcm/clk-iproc-clk.c b/drivers/clk/bcm/clk-iproc-clk.c
>> new file mode 100644
>> index 0000000..be3c42c
>> --- /dev/null
>> +++ b/drivers/clk/bcm/clk-iproc-clk.c
>> @@ -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);
>> +}
>> diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c
>> new file mode 100644
>> index 0000000..cd3bd38
>> --- /dev/null
>> +++ b/drivers/clk/bcm/clk-iproc-pll.c
>> @@ -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);
>> +}
>> diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h
>> new file mode 100644
>> index 0000000..4aa0479
>> --- /dev/null
>> +++ b/drivers/clk/bcm/clk-iproc.h
>> @@ -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@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/5] gpio: Cygnus: add GPIO driver
2014-12-06 2:14 ` Ray Jui
2014-12-06 2:34 ` Joe Perches
@ 2014-12-08 1:59 ` Ray Jui
1 sibling, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 1:59 UTC (permalink / raw)
To: Joe Perches
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On 12/5/2014 6:14 PM, Ray Jui wrote:
>>> +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
>
>>> +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.
>
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,
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (10 preceding siblings ...)
2014-12-06 0:40 ` [PATCH 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
2014-12-08 2:38 ` [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
` (4 more replies)
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (22 subsequent siblings)
34 siblings, 5 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
2014-12-08 11:22 ` Arnd Bergmann
2014-12-08 2:38 ` [PATCH v2 2/5] gpio: Cygnus: add GPIO driver Ray Jui
` (3 subsequent siblings)
4 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the GPIO device tree binding for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../devicetree/bindings/gpio/brcm,cygnus-gpio.txt | 85 ++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
new file mode 100644
index 0000000..24a1513
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
@@ -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>
+ }
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 2/5] gpio: Cygnus: add GPIO driver
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 2:38 ` [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
2014-12-08 2:38 ` [PATCH v2 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
` (2 subsequent siblings)
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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")
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 633ec21..3e3b0342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 81755f1..31eb7e0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -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
diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
new file mode 100644
index 0000000..873dce2
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-cygnus.c
@@ -0,0 +1,712 @@
+/*
+ * 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;
+ 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;
+
+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,
+ unsigned gpio)
+{
+ return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+ unsigned gpio)
+{
+ return GPIO_BIT(gpio);
+}
+
+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 */
+ 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);
+
+ 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;
+
+ 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);
+}
+
+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;
+ 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);
+}
+
+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;
+ 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;
+ 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 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);
+ 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);
+
+ 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);
+
+ 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);
+
+ 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;
+
+ if (of_device_is_compatible(dev->of_node, "brcm,cygnus-asiu-gpio")) {
+ base = cygnus_gpio->base;
+ offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+ CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET;
+ } else if (of_device_is_compatible(dev->of_node,
+ "brcm,cygnus-ccm-gpio")) {
+ if (!cygnus_gpio->io_ctrl)
+ return;
+
+ base = cygnus_gpio->io_ctrl;
+ offset = CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET;
+ } else
+ return;
+
+ 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-crmu-gpio" },
+ { .compatible = "brcm,cygnus-asiu-gpio" },
+ { .compatible = "brcm,cygnus-ccm-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;
+
+ match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to find GPIO controller\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio = devm_kzalloc(dev, sizeof(*cygnus_gpio), GFP_KERNEL);
+ if (!cygnus_gpio)
+ return -ENOMEM;
+
+ cygnus_gpio->dev = dev;
+ platform_set_drvdata(pdev, cygnus_gpio);
+
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "missing ngpios device tree property\n");
+ return -ENODEV;
+ }
+ cygnus_gpio->num_banks = (ngpios + NGPIOS_PER_BANK - 1) /
+ NGPIOS_PER_BANK;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get I/O resource\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->base)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->base);
+ }
+
+ /*
+ * Only certain types of Cygnus GPIO interfaces have I/O control
+ * registers
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ cygnus_gpio->io_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->io_ctrl)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->io_ctrl);
+ }
+ }
+
+ spin_lock_init(&cygnus_gpio->lock);
+
+ gc = &cygnus_gpio->gc;
+ gc->base = gpio_base_index;
+ gpio_base_index += ngpios;
+ gc->ngpio = ngpios;
+ gc->label = dev_name(dev);
+ gc->dev = dev;
+#ifdef CONFIG_OF_GPIO
+ gc->of_node = dev->of_node;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = bcm_cygnus_gpio_of_xlate;
+#endif
+ gc->direction_input = bcm_cygnus_gpio_direction_input;
+ gc->direction_output = bcm_cygnus_gpio_direction_output;
+ gc->set = bcm_cygnus_gpio_set;
+ gc->get = bcm_cygnus_gpio_get;
+ gc->to_irq = bcm_cygnus_gpio_to_irq;
+
+ ret = gpiochip_add(gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to add GPIO chip\n");
+ goto err_dec_gpio_base;
+ }
+
+ /*
+ * Some of the GPIO interfaces do not have interrupt wired to the main
+ * processor
+ */
+ if (of_find_property(dev->of_node, "no-interrupt", NULL))
+ return 0;
+
+ cygnus_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ gc->ngpio, &bcm_cygnus_irq_ops, cygnus_gpio);
+ if (!cygnus_gpio->irq_domain) {
+ dev_err(&pdev->dev, "unable to allocate IRQ domain\n");
+ ret = -ENXIO;
+ goto err_rm_gpiochip;
+ }
+
+ cygnus_gpio->irq = platform_get_irq(pdev, 0);
+ if (cygnus_gpio->irq < 0) {
+ dev_err(&pdev->dev, "unable to get IRQ\n");
+ ret = cygnus_gpio->irq;
+ goto err_rm_irq_domain;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ int irq = irq_create_mapping(cygnus_gpio->irq_domain, i);
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, cygnus_gpio);
+ irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(cygnus_gpio->irq, bcm_cygnus_gpio_irq_handler);
+ irq_set_handler_data(cygnus_gpio->irq, cygnus_gpio);
+
+ return 0;
+
+err_rm_irq_domain:
+ irq_domain_remove(cygnus_gpio->irq_domain);
+
+err_rm_gpiochip:
+ gpiochip_remove(gc);
+
+err_dec_gpio_base:
+ gpio_base_index -= ngpios;
+ return ret;
+}
+
+static struct platform_driver bcm_cygnus_gpio_driver = {
+ .driver = {
+ .name = "bcm-cygnus-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm_cygnus_gpio_of_match,
+ },
+ .probe = bcm_cygnus_gpio_probe,
+};
+
+module_platform_driver(bcm_cygnus_gpio_driver);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 2:38 ` [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
2014-12-08 2:38 ` [PATCH v2 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
2014-12-08 2:38 ` [PATCH v2 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
2014-12-08 2:38 ` [PATCH v2 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..5066d5d 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 4/5] ARM: dts: enable GPIO for Broadcom Cygnus
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-08 2:38 ` [PATCH v2 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
2014-12-08 2:38 ` [PATCH v2 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables all 3 GPIO controllers including the ASIU GPIO, the
chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..c7587c1 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 5/5] MAINTAINERS: Entry for Cygnus GPIO driver
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (3 preceding siblings ...)
2014-12-08 2:38 ` [PATCH v2 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
@ 2014-12-08 2:38 ` Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 2:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e6bff3a..8473422 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2202,6 +2202,13 @@ N: bcm9583*
N: bcm583*
N: bcm113*
+BROADCOM CYGNUS GPIO DRIVER
+M: Ray Jui <rjui@broadcom.com>
+L: bcm-kernel-feedback-list@broadcom.com
+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 <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 2:38 ` [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-08 11:22 ` Arnd Bergmann
2014-12-08 16:55 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Arnd Bergmann @ 2014-12-08 11:22 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
> +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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 11:22 ` Arnd Bergmann
@ 2014-12-08 16:55 ` Ray Jui
2014-12-08 17:11 ` Arnd Bergmann
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-08 16:55 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On 12/8/2014 3:22 AM, Arnd Bergmann wrote:
> On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
>> +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
>
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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 16:55 ` Ray Jui
@ 2014-12-08 17:11 ` Arnd Bergmann
0 siblings, 0 replies; 355+ messages in thread
From: Arnd Bergmann @ 2014-12-08 17:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ray Jui, Mark Rutland, Alexandre Courbot, Florian Fainelli,
Russell King, Scott Branden, Pawel Moll, Ian Campbell,
Linus Walleij, Christian Daudt, linux-kernel, Matt Porter,
Joe Perches, devicetree, Rob Herring, bcm-kernel-feedback-list,
linux-gpio, Kumar Gala, Grant Likely
On Monday 08 December 2014 08:55:20 Ray Jui wrote:
>
> On 12/8/2014 3:22 AM, Arnd Bergmann wrote:
> > On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
> >> +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
> >
> 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.
> 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.
> 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.
> 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.
> 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
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (11 preceding siblings ...)
2014-12-08 2:38 ` [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 18:47 ` [PATCH v2 " Ray Jui
` (5 more replies)
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (21 subsequent siblings)
34 siblings, 6 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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 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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 18:48 ` Ray Jui
2014-12-08 18:47 ` [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
` (4 subsequent siblings)
5 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 18:47 ` [PATCH v2 " Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 19:38 ` Arnd Bergmann
2014-12-08 18:47 ` [PATCH v3 2/5] gpio: Cygnus: add GPIO driver Ray Jui
` (3 subsequent siblings)
5 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the GPIO device tree binding for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../devicetree/bindings/gpio/brcm,cygnus-gpio.txt | 82 ++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
new file mode 100644
index 0000000..c477271
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
@@ -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>
+ }
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 2/5] gpio: Cygnus: add GPIO driver
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 18:47 ` [PATCH v2 " Ray Jui
2014-12-08 18:47 ` [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 18:47 ` [PATCH v3 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
` (2 subsequent siblings)
5 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 633ec21..3e3b0342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 81755f1..31eb7e0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -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
diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
new file mode 100644
index 0000000..f1f69ce
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-cygnus.c
@@ -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;
+ 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;
+
+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,
+ unsigned gpio)
+{
+ return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+ unsigned gpio)
+{
+ return GPIO_BIT(gpio);
+}
+
+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 */
+ 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);
+
+ 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;
+
+ 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);
+}
+
+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;
+ 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);
+}
+
+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;
+ 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;
+ 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 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);
+ 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);
+
+ 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);
+
+ 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);
+
+ 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-stregnth", 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;
+
+ match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to find GPIO controller\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio = devm_kzalloc(dev, sizeof(*cygnus_gpio), GFP_KERNEL);
+ if (!cygnus_gpio)
+ return -ENOMEM;
+
+ cygnus_gpio->dev = dev;
+ platform_set_drvdata(pdev, cygnus_gpio);
+
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "missing ngpios device tree property\n");
+ return -ENODEV;
+ }
+ cygnus_gpio->num_banks = (ngpios + NGPIOS_PER_BANK - 1) /
+ NGPIOS_PER_BANK;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get I/O resource\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->base)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->base);
+ }
+
+ /*
+ * Only certain types of Cygnus GPIO interfaces have I/O control
+ * registers
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ cygnus_gpio->io_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->io_ctrl)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->io_ctrl);
+ }
+ }
+
+ spin_lock_init(&cygnus_gpio->lock);
+
+ gc = &cygnus_gpio->gc;
+ gc->base = gpio_base_index;
+ gpio_base_index += ngpios;
+ gc->ngpio = ngpios;
+ gc->label = dev_name(dev);
+ gc->dev = dev;
+#ifdef CONFIG_OF_GPIO
+ gc->of_node = dev->of_node;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = bcm_cygnus_gpio_of_xlate;
+#endif
+ gc->direction_input = bcm_cygnus_gpio_direction_input;
+ gc->direction_output = bcm_cygnus_gpio_direction_output;
+ gc->set = bcm_cygnus_gpio_set;
+ gc->get = bcm_cygnus_gpio_get;
+ gc->to_irq = bcm_cygnus_gpio_to_irq;
+
+ ret = gpiochip_add(gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to add GPIO chip\n");
+ goto err_dec_gpio_base;
+ }
+
+ /*
+ * Some of the GPIO interfaces do not have interrupt wired to the main
+ * processor
+ */
+ cygnus_gpio->irq = platform_get_irq(pdev, 0);
+ if (cygnus_gpio->irq < 0) {
+ ret = cygnus_gpio->irq;
+ if (ret == -EPROBE_DEFER)
+ goto err_rm_gpiochip;
+
+ dev_info(&pdev->dev, "no interrupt hook\n");
+ }
+
+ cygnus_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ gc->ngpio, &bcm_cygnus_irq_ops, cygnus_gpio);
+ if (!cygnus_gpio->irq_domain) {
+ dev_err(&pdev->dev, "unable to allocate IRQ domain\n");
+ ret = -ENXIO;
+ goto err_rm_gpiochip;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ int irq = irq_create_mapping(cygnus_gpio->irq_domain, i);
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, cygnus_gpio);
+ irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(cygnus_gpio->irq, bcm_cygnus_gpio_irq_handler);
+ irq_set_handler_data(cygnus_gpio->irq, cygnus_gpio);
+
+ return 0;
+
+err_rm_gpiochip:
+ gpiochip_remove(gc);
+
+err_dec_gpio_base:
+ gpio_base_index -= ngpios;
+ return ret;
+}
+
+static struct platform_driver bcm_cygnus_gpio_driver = {
+ .driver = {
+ .name = "bcm-cygnus-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm_cygnus_gpio_of_match,
+ },
+ .probe = bcm_cygnus_gpio_probe,
+};
+
+module_platform_driver(bcm_cygnus_gpio_driver);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-08 18:47 ` [PATCH v3 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 18:47 ` [PATCH v3 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
2014-12-08 18:47 ` [PATCH v3 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
5 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..5066d5d 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 4/5] ARM: dts: enable GPIO for Broadcom Cygnus
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (3 preceding siblings ...)
2014-12-08 18:47 ` [PATCH v3 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
2014-12-08 18:47 ` [PATCH v3 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
5 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables all 3 GPIO controllers including the ASIU GPIO, the
chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..48339d0 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 5/5] MAINTAINERS: Entry for Cygnus GPIO driver
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (4 preceding siblings ...)
2014-12-08 18:47 ` [PATCH v3 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
@ 2014-12-08 18:47 ` Ray Jui
5 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:47 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e6bff3a..8473422 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2202,6 +2202,13 @@ N: bcm9583*
N: bcm583*
N: bcm113*
+BROADCOM CYGNUS GPIO DRIVER
+M: Ray Jui <rjui@broadcom.com>
+L: bcm-kernel-feedback-list@broadcom.com
+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 <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC
2014-12-08 18:47 ` [PATCH v2 " Ray Jui
@ 2014-12-08 18:48 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 18:48 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
Sorry. Please ignore this particular cover letter. It accidentally got
sent along with other v3 patches.
On 12/8/2014 10:47 AM, Ray Jui wrote:
> 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
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 18:47 ` [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-08 19:38 ` Arnd Bergmann
2014-12-08 19:45 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Arnd Bergmann @ 2014-12-08 19:38 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On Monday 08 December 2014 10:47:44 Ray Jui wrote:
> +
> +- no-drv-stregnth:
> + Specifies the GPIO controller does not support drive strength configuration
> +
>
Typo:
strength, not stregnth
Otherwise looks good.
Arnd
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v3 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 19:38 ` Arnd Bergmann
@ 2014-12-08 19:45 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 19:45 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree
On 12/8/2014 11:38 AM, Arnd Bergmann wrote:
> On Monday 08 December 2014 10:47:44 Ray Jui wrote:
>> +
>> +- no-drv-stregnth:
>> + Specifies the GPIO controller does not support drive strength configuration
>> +
>>
>
> Typo:
>
> 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@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;
};
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC
[not found] <Ray Jui <rjui@broadcom.com>
` (12 preceding siblings ...)
2014-12-08 18:47 ` [PATCH v3 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
2014-12-08 20:41 ` [PATCH v4 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
` (4 more replies)
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
` (20 subsequent siblings)
34 siblings, 5 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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 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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v4 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
2014-12-08 20:41 ` [PATCH v4 2/5] gpio: Cygnus: add GPIO driver Ray Jui
` (3 subsequent siblings)
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the GPIO device tree binding for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../devicetree/bindings/gpio/brcm,cygnus-gpio.txt | 82 ++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
new file mode 100644
index 0000000..dca322a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
@@ -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>
+ }
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v4 2/5] gpio: Cygnus: add GPIO driver
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 20:41 ` [PATCH v4 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
2014-12-10 10:34 ` Alexandre Courbot
2014-12-08 20:41 ` [PATCH v4 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
` (2 subsequent siblings)
4 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 633ec21..3e3b0342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 81755f1..31eb7e0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -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
diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
new file mode 100644
index 0000000..4fd9b73
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-cygnus.c
@@ -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;
+ 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;
+
+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,
+ unsigned gpio)
+{
+ return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+ unsigned gpio)
+{
+ return GPIO_BIT(gpio);
+}
+
+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 */
+ 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);
+
+ 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;
+
+ 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);
+}
+
+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;
+ 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);
+}
+
+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;
+ 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;
+ 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 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);
+ 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);
+
+ 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);
+
+ 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);
+
+ 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;
+
+ match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to find GPIO controller\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio = devm_kzalloc(dev, sizeof(*cygnus_gpio), GFP_KERNEL);
+ if (!cygnus_gpio)
+ return -ENOMEM;
+
+ cygnus_gpio->dev = dev;
+ platform_set_drvdata(pdev, cygnus_gpio);
+
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "missing ngpios device tree property\n");
+ return -ENODEV;
+ }
+ cygnus_gpio->num_banks = (ngpios + NGPIOS_PER_BANK - 1) /
+ NGPIOS_PER_BANK;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get I/O resource\n");
+ return -ENODEV;
+ }
+
+ cygnus_gpio->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->base)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->base);
+ }
+
+ /*
+ * Only certain types of Cygnus GPIO interfaces have I/O control
+ * registers
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ cygnus_gpio->io_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygnus_gpio->io_ctrl)) {
+ dev_err(&pdev->dev, "unable to map I/O memory\n");
+ return PTR_ERR(cygnus_gpio->io_ctrl);
+ }
+ }
+
+ spin_lock_init(&cygnus_gpio->lock);
+
+ gc = &cygnus_gpio->gc;
+ gc->base = gpio_base_index;
+ gpio_base_index += ngpios;
+ gc->ngpio = ngpios;
+ gc->label = dev_name(dev);
+ gc->dev = dev;
+#ifdef CONFIG_OF_GPIO
+ gc->of_node = dev->of_node;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = bcm_cygnus_gpio_of_xlate;
+#endif
+ gc->direction_input = bcm_cygnus_gpio_direction_input;
+ gc->direction_output = bcm_cygnus_gpio_direction_output;
+ gc->set = bcm_cygnus_gpio_set;
+ gc->get = bcm_cygnus_gpio_get;
+ gc->to_irq = bcm_cygnus_gpio_to_irq;
+
+ ret = gpiochip_add(gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to add GPIO chip\n");
+ goto err_dec_gpio_base;
+ }
+
+ /*
+ * Some of the GPIO interfaces do not have interrupt wired to the main
+ * processor
+ */
+ cygnus_gpio->irq = platform_get_irq(pdev, 0);
+ if (cygnus_gpio->irq < 0) {
+ ret = cygnus_gpio->irq;
+ if (ret == -EPROBE_DEFER)
+ goto err_rm_gpiochip;
+
+ dev_info(&pdev->dev, "no interrupt hook\n");
+ }
+
+ cygnus_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ gc->ngpio, &bcm_cygnus_irq_ops, cygnus_gpio);
+ if (!cygnus_gpio->irq_domain) {
+ dev_err(&pdev->dev, "unable to allocate IRQ domain\n");
+ ret = -ENXIO;
+ goto err_rm_gpiochip;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ int irq = irq_create_mapping(cygnus_gpio->irq_domain, i);
+
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, cygnus_gpio);
+ irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ irq_set_chained_handler(cygnus_gpio->irq, bcm_cygnus_gpio_irq_handler);
+ irq_set_handler_data(cygnus_gpio->irq, cygnus_gpio);
+
+ return 0;
+
+err_rm_gpiochip:
+ gpiochip_remove(gc);
+
+err_dec_gpio_base:
+ gpio_base_index -= ngpios;
+ return ret;
+}
+
+static struct platform_driver bcm_cygnus_gpio_driver = {
+ .driver = {
+ .name = "bcm-cygnus-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm_cygnus_gpio_of_match,
+ },
+ .probe = bcm_cygnus_gpio_probe,
+};
+
+module_platform_driver(bcm_cygnus_gpio_driver);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v4 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
2014-12-08 20:41 ` [PATCH v4 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding Ray Jui
2014-12-08 20:41 ` [PATCH v4 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
2014-12-08 20:41 ` [PATCH v4 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
2014-12-08 20:41 ` [PATCH v4 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..5066d5d 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v4 4/5] ARM: dts: enable GPIO for Broadcom Cygnus
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (2 preceding siblings ...)
2014-12-08 20:41 ` [PATCH v4 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
2014-12-08 20:41 ` [PATCH v4 5/5] MAINTAINERS: Entry for Cygnus GPIO driver Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
This enables all 3 GPIO controllers including the ASIU GPIO, the
chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..fbc8257 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v4 5/5] MAINTAINERS: Entry for Cygnus GPIO driver
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (3 preceding siblings ...)
2014-12-08 20:41 ` [PATCH v4 4/5] ARM: dts: enable GPIO for Broadcom Cygnus Ray Jui
@ 2014-12-08 20:41 ` Ray Jui
4 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-08 20:41 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Alexandre Courbot, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Joe Perches,
Arnd Bergmann
Cc: Scott Branden, linux-kernel, linux-arm-kernel, linux-gpio,
bcm-kernel-feedback-list, devicetree, Ray Jui
Signed-off-by: Ray Jui <rjui@broadcom.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e6bff3a..8473422 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2202,6 +2202,13 @@ N: bcm9583*
N: bcm583*
N: bcm113*
+BROADCOM CYGNUS GPIO DRIVER
+M: Ray Jui <rjui@broadcom.com>
+L: bcm-kernel-feedback-list@broadcom.com
+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 <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add PCIe support to Broadcom iProc
[not found] <Ray Jui <rjui@broadcom.com>
` (13 preceding siblings ...)
2014-12-08 20:41 ` [PATCH v4 0/5] Add gpio support to Broadcom Cygnus SoC Ray Jui
@ 2014-12-10 0:04 ` Ray Jui
2014-12-10 0:04 ` [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding Ray Jui
` (3 more replies)
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
` (19 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:04 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
@ 2014-12-10 0:04 ` Ray Jui
2014-12-10 10:30 ` Lucas Stach
2014-12-10 0:04 ` [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver Ray Jui
` (2 subsequent siblings)
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:04 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the PCIe device tree binding for Broadcom iProc family of SoCs
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../devicetree/bindings/pci/brcm,iproc-pcie.txt | 62 ++++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
new file mode 100644
index 0000000..2467628
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -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>;
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
2014-12-10 0:04 ` [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding Ray Jui
@ 2014-12-10 0:04 ` Ray Jui
2014-12-10 11:31 ` Arnd Bergmann
2014-12-10 0:04 ` [PATCH 3/4] ARM: mach-bcm: Enable PCIe support for iProc Ray Jui
2014-12-10 0:04 ` [PATCH 4/4] ARM: dts: enable PCIe for Broadcom Cygnus Ray Jui
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:04 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c4b6568..22322e1 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -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
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 44c2699..1f5e9d2 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -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
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
new file mode 100644
index 0000000..bd9f01c
--- /dev/null
+++ b/drivers/pci/host/pcie-iproc.c
@@ -0,0 +1,896 @@
+/*
+ * 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/pci.h>
+#include <linux/msi.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mbus.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+/* max number of MSI event queues */
+#define MAX_MSI_EQ 6
+#define MAX_IRQS MAX_MSI_EQ
+
+#define MDIO_TIMEOUT_USEC 100
+
+#define OPCODE_WRITE 1
+#define OPCODE_READ 2
+
+#define MII_TA_VAL 2
+#define MII_MDCDIV 62
+
+#define MII_MGMT_CTRL_OFFSET 0x000
+#define MII_MGMT_CTRL_MDCDIV_SHIFT 0
+#define MII_MGMT_CTRL_PRE_SHIFT 7
+#define MII_MGMT_CTRL_BUSY_SHIFT 8
+#define MII_MGMT_CTRL_EXT_SHIFT 9
+#define MII_MGMT_CTRL_BTP_SHIFT 10
+
+#define MII_MGMT_CMD_DATA_OFFSET 0x004
+#define MII_MGMT_CMD_DATA_SHIFT 0
+#define MII_MGMT_CMD_TA_SHIFT 16
+#define MII_MGMT_CMD_RA_SHIFT 18
+#define MII_MGMT_CMD_PA_SHIFT 23
+#define MII_MGMT_CMD_OP_SHIFT 28
+#define MII_MGMT_CMD_SB_SHIFT 30
+#define MII_MGMT_CMD_DATA_MASK 0xFFFF
+
+#define CLK_CONTROL_OFFSET 0x000
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT (1 << EP_PERST_SOURCE_SELECT_SHIFT)
+#define EP_MODE_SURVIVE_PERST_SHIFT 1
+#define EP_MODE_SURVIVE_PERST (1 << EP_MODE_SURVIVE_PERST_SHIFT)
+#define RC_PCIE_RST_OUTPUT_SHIFT 0
+#define RC_PCIE_RST_OUTPUT (1 << RC_PCIE_RST_OUTPUT_SHIFT)
+
+#define CFG_IND_ADDR_OFFSET 0x120
+#define CFG_IND_ADDR_MASK 0x00001FFC
+
+#define CFG_IND_DATA_OFFSET 0x124
+
+#define CFG_ADDR_OFFSET 0x1F8
+#define CFG_ADDR_BUS_NUM_SHIFT 20
+#define CFG_ADDR_BUS_NUM_MASK 0x0FF00000
+#define CFG_ADDR_DEV_NUM_SHIFT 15
+#define CFG_ADDR_DEV_NUM_MASK 0x000F8000
+#define CFG_ADDR_FUNC_NUM_SHIFT 12
+#define CFG_ADDR_FUNC_NUM_MASK 0x00007000
+#define CFG_ADDR_REG_NUM_SHIFT 2
+#define CFG_ADDR_REG_NUM_MASK 0x00000FFC
+#define CFG_ADDR_CFG_TYPE_SHIFT 0
+#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
+
+#define CFG_DATA_OFFSET 0x1FC
+
+#define SYS_EQ_PAGE_OFFSET 0x200
+#define SYS_MSI_PAGE_OFFSET 0x204
+
+#define SYS_MSI_INTS_EN_OFFSET 0x208
+
+#define SYS_MSI_CTRL_0_OFFSET 0x210
+#define SYS_MSI_INTR_EN_SHIFT 11
+#define SYS_MSI_INTR_EN (1 << SYS_MSI_INTR_EN_SHIFT)
+#define SYS_MSI_INT_N_EVENT_SHIFT 1
+#define SYS_MSI_INT_N_EVENT (1 << SYS_MSI_INT_N_EVENT_SHIFT)
+#define SYS_MSI_EQ_EN_SHIFT 0
+#define SYS_MSI_EQ_EN (1 << SYS_MSI_EQ_EN_SHIFT)
+
+#define SYS_EQ_HEAD_0_OFFSET 0x250
+#define SYS_EQ_TAIL_0_OFFSET 0x254
+#define SYS_EQ_TAIL_0_MASK 0x3F
+
+#define SYS_RC_INTX_EN 0x330
+#define SYS_RC_INTX_MASK 0xF
+
+#define SYS_RC_INTX_CSR 0x334
+#define SYS_RC_INTX_MASK 0xF
+
+#define OARR_0_OFFSET 0xD20
+#define OAAR_0_ADDR_MASK 0xF0000000
+#define OAAR_0_VALID_SHIFT 0
+#define OAAR_0_VALID (1 << OAAR_0_VALID_SHIFT)
+#define OAAR_0_UPPER_OFFSET 0xD24
+#define OAAR_0_UPPER_ADDR_MASK 0x0000000F
+
+#define PCIE_SYS_RC_INTX_EN_OFFSET 0x330
+
+#define OMAP_0_LOWER_OFFSET 0xD40
+#define OMAP_0_LOWER_ADDR_MASK 0xF0000000
+#define OMAP_0_UPPER_OFFSET 0x0D44
+
+#define PCIE_LINK_STATUS_OFFSET 0xF0C
+#define PCIE_PHYLINKUP_SHITF 3
+#define PCIE_PHYLINKUP (1 << PCIE_PHYLINKUP_SHITF)
+
+#define STRAP_STATUS_OFFSET 0xF10
+#define STRAP_1LANE_SHIFT 2
+#define STRAP_1LANE (1 << STRAP_1LANE_SHIFT)
+#define STRAP_IF_ENABLE_SHIFT 1
+#define STRAP_IF_ENABLE (1 << STRAP_IF_ENABLE_SHIFT)
+#define STRAP_RC_MODE_SHIFT 0
+#define STRAP_RC_MODE (1 << STRAP_RC_MODE_SHIFT)
+
+struct iproc_pcie;
+
+/**
+ * iProc MSI
+ * @pcie: pointer to the iProc PCIe data structure
+ * @irq_in_use: bitmap of MSI IRQs that are in use
+ * @domain: MSI IRQ domain
+ * @chip: MSI controller
+ * @eq_page: memory page to store the iProc MSI event queue
+ * @msi_page: memory page for MSI posted writes
+ */
+struct iproc_msi {
+ struct iproc_pcie *pcie;
+ DECLARE_BITMAP(irq_in_use, MAX_IRQS);
+ struct irq_domain *domain;
+ struct msi_controller chip;
+ unsigned long eq_page;
+ unsigned long msi_page;
+};
+
+/**
+ * iProc PCIe
+ * @dev: pointer to the device
+ * @mii: MII/MDIO management I/O register base
+ * @reg: PCIe I/O register base
+ * @io: PCIe I/O resource
+ * @mem: PCIe memory resource
+ * @busn: PCIe bus resource
+ * @phy_addr: MIDO PHY address
+ * @irqs: Array that stores IRQs
+ * @msi: MSI related info
+ */
+struct iproc_pcie {
+ struct device *dev;
+
+ void __iomem *mii;
+ void __iomem *reg;
+
+ struct resource io;
+ struct resource mem;
+ struct resource busn;
+
+ u32 phy_addr;
+ int irqs[MAX_IRQS];
+
+ struct iproc_msi msi;
+};
+
+static inline int mdio_wait_idle(struct iproc_pcie *pcie)
+{
+ int timeout = MDIO_TIMEOUT_USEC;
+
+ while (readl(pcie->mii + MII_MGMT_CTRL_OFFSET) &
+ (1 << MII_MGMT_CTRL_BUSY_SHIFT)) {
+ udelay(1);
+ if (timeout-- <= 0)
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void mdio_init(struct iproc_pcie *pcie)
+{
+ u32 val;
+
+ val = MII_MDCDIV << MII_MGMT_CTRL_MDCDIV_SHIFT;
+ val |= (1 << MII_MGMT_CTRL_PRE_SHIFT);
+ writel(val, pcie->mii + MII_MGMT_CTRL_OFFSET);
+
+ WARN_ON(mdio_wait_idle(pcie));
+}
+
+static u16 mdio_read(struct iproc_pcie *pcie, unsigned int phy_addr,
+ unsigned int reg_addr)
+{
+ u32 val;
+
+ WARN_ON(mdio_wait_idle(pcie));
+
+ val = (MII_TA_VAL << MII_MGMT_CMD_TA_SHIFT);
+ val |= (reg_addr << MII_MGMT_CMD_RA_SHIFT);
+ val |= (phy_addr << MII_MGMT_CMD_PA_SHIFT);
+ val |= (OPCODE_READ << MII_MGMT_CMD_OP_SHIFT);
+ val |= (1 << MII_MGMT_CMD_SB_SHIFT);
+ writel(val, pcie->mii + MII_MGMT_CMD_DATA_OFFSET);
+
+ WARN_ON(mdio_wait_idle(pcie));
+
+ val = readl(pcie->mii + MII_MGMT_CMD_DATA_OFFSET) &
+ MII_MGMT_CMD_DATA_MASK;
+
+ return (u16)val;
+}
+
+static void mdio_write(struct iproc_pcie *pcie, unsigned int phy_addr,
+ unsigned int reg_addr, u16 wr_data)
+{
+ u32 val;
+
+ WARN_ON(mdio_wait_idle(pcie));
+
+ val = (MII_TA_VAL << MII_MGMT_CMD_TA_SHIFT);
+ val |= (reg_addr << MII_MGMT_CMD_RA_SHIFT);
+ val |= (phy_addr << MII_MGMT_CMD_PA_SHIFT);
+ val |= (OPCODE_WRITE << MII_MGMT_CMD_OP_SHIFT);
+ val |= (1 << MII_MGMT_CMD_SB_SHIFT);
+ val |= ((u32)wr_data & MII_MGMT_CMD_DATA_MASK);
+ writel(val, pcie->mii + MII_MGMT_CMD_DATA_OFFSET);
+
+ WARN_ON(mdio_wait_idle(pcie));
+}
+
+#define PCIE_PHY_BLK_ADDR_OFFSET 0x1F
+#define PCIE_PHY_BLK_ADDR_MASK 0xFFF0
+#define PCIE_PHY_REG_ADDR_MASK 0xF
+static u16 iproc_pcie_phy_reg_read(struct iproc_pcie *pcie,
+ unsigned int phy_addr, unsigned int reg_addr)
+{
+ u16 val;
+
+ mdio_write(pcie, phy_addr, PCIE_PHY_BLK_ADDR_OFFSET,
+ reg_addr & PCIE_PHY_BLK_ADDR_MASK);
+ val = mdio_read(pcie, phy_addr, reg_addr & PCIE_PHY_REG_ADDR_MASK);
+
+ dev_dbg(pcie->dev, "phy rd: phy: 0x%0x reg: 0x%4x data: 0x%4x\n",
+ phy_addr, reg_addr, val);
+
+ return val;
+}
+
+static void iproc_pcie_phy_reg_write(struct iproc_pcie *pcie,
+ unsigned int phy_addr, unsigned int reg_addr, u16 val)
+{
+ mdio_write(pcie, phy_addr, PCIE_PHY_BLK_ADDR_OFFSET,
+ reg_addr & PCIE_PHY_BLK_ADDR_MASK);
+ mdio_write(pcie, phy_addr, reg_addr & PCIE_PHY_REG_ADDR_MASK, val);
+
+ dev_dbg(pcie->dev, "phy wr: phy: 0x%0x reg: 0x%4x data: 0x%4x\n",
+ phy_addr, reg_addr, val);
+}
+
+static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+static void iproc_pcie_reset(struct iproc_pcie *pcie)
+{
+ u32 val;
+
+ /* send a downstream reset */
+ val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
+ writel(val, pcie->reg + CLK_CONTROL_OFFSET);
+ udelay(250);
+ val &= ~EP_MODE_SURVIVE_PERST;
+ writel(val, pcie->reg + CLK_CONTROL_OFFSET);
+ mdelay(250);
+}
+
+#define INVALID_ACCESS_OFFSET 0xFFFFFFFF
+static u32 iproc_pcie_conf_access(struct iproc_pcie *pcie, struct pci_bus *bus,
+ unsigned int devfn, int where)
+{
+ int busno = bus->number;
+ int slot = PCI_SLOT(devfn);
+ int fn = PCI_FUNC(devfn);
+ u32 val;
+
+ /* root complex access */
+ if (busno == 0) {
+ if (slot)
+ return INVALID_ACCESS_OFFSET;
+ writel(where & CFG_IND_ADDR_MASK,
+ pcie->reg + CFG_IND_ADDR_OFFSET);
+ return CFG_IND_DATA_OFFSET;
+ }
+
+ if (fn > 1)
+ return INVALID_ACCESS_OFFSET;
+
+ /* access of EP device */
+ val = (bus->number << CFG_ADDR_BUS_NUM_SHIFT) |
+ (PCI_SLOT(devfn) << CFG_ADDR_DEV_NUM_SHIFT) |
+ (PCI_FUNC(devfn) << CFG_ADDR_FUNC_NUM_SHIFT) |
+ (where & CFG_ADDR_REG_NUM_MASK) |
+ (1 & CFG_ADDR_CFG_TYPE_MASK);
+ writel(val, pcie->reg + CFG_ADDR_OFFSET);
+
+ return CFG_DATA_OFFSET;
+}
+
+#define INVALID_CFG_RD 0xFFFFFFFF
+static int iproc_pci_read_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ u32 offset;
+ struct iproc_pcie *pcie = sys_to_pcie(bus->sysdata);
+
+ *val = INVALID_CFG_RD;
+
+ if (size != 1 && size != 2 && size != 4)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 2) && (where & 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 4) && (where & 3))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ offset = iproc_pcie_conf_access(pcie, bus, devfn, where);
+ if (offset == INVALID_ACCESS_OFFSET)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ *val = readl(pcie->reg + offset);
+
+ switch (size) {
+ case 4:
+ /* return raw data */
+ break;
+ case 2:
+ *val = (*val >> (8 * (where & 3))) & 0xFFFF;
+ break;
+ case 1:
+ *val = (*val >> (8 * (where & 3))) & 0xFF;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ dev_dbg(pcie->dev, "conf rd: busn=%d devfn=%d where=%d size=%d val=0x%08x\n",
+ bus->number, devfn, where, size, *val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int iproc_pci_write_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ int shift;
+ u32 offset, data;
+ struct iproc_pcie *pcie = sys_to_pcie(bus->sysdata);
+
+ if (size != 1 && size != 2 && size != 4)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 2) && (where & 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 4) && (where & 3))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ offset = iproc_pcie_conf_access(pcie, bus, devfn, where);
+ if (offset == INVALID_ACCESS_OFFSET)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ data = readl(pcie->reg + offset);
+
+ switch (size) {
+ case 4:
+ data = val;
+ break;
+ case 2:
+ shift = 8 * (where & 2);
+ data &= ~(0xFFFF << shift);
+ data |= ((val & 0xFFFF) << shift);
+ break;
+ case 1:
+ shift = 8 * (where & 3);
+ data &= ~(0xFF << shift);
+ data |= ((val & 0xFF) << shift);
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ writel(data, pcie->reg + offset);
+
+ dev_dbg(pcie->dev,
+ "config wr: busn=%d devfn=%d where=%d size=%d data=0x%08x\n",
+ bus->number, devfn, where, size, data);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops iproc_pcie_ops = {
+ .read = iproc_pci_read_conf,
+ .write = iproc_pci_write_conf,
+};
+
+static int iproc_pcie_check_link(struct iproc_pcie *pcie)
+{
+ int ret;
+ u8 nlw;
+ u16 pos, tmp16;
+ u32 val;
+ struct pci_sys_data sys;
+ struct pci_bus bus;
+
+ val = readl(pcie->reg + PCIE_LINK_STATUS_OFFSET);
+ dev_dbg(pcie->dev, "link status: 0x%08x\n", val);
+
+ val = readl(pcie->reg + STRAP_STATUS_OFFSET);
+ dev_dbg(pcie->dev, "strap status: 0x%08x\n", val);
+
+ memset(&sys, 0, sizeof(sys));
+ memset(&bus, 0, sizeof(bus));
+
+ bus.number = 0;
+ bus.ops = &iproc_pcie_ops;
+ bus.sysdata = &sys;
+ sys.private_data = pcie;
+
+ ret = iproc_pci_read_conf(&bus, 0, PCI_HEADER_TYPE, 1, &val);
+ if (ret != PCIBIOS_SUCCESSFUL || val != PCI_HEADER_TYPE_BRIDGE) {
+ dev_err(pcie->dev, "in EP mode, val=0x08%x\n", val);
+ return -EFAULT;
+ }
+
+ /*
+ * Under RC mode, write to function specific register 0x43c, to change
+ * the CLASS code in configuration space
+ *
+ * After this modification, the CLASS code in configuration space would
+ * be read as PCI_CLASS_BRIDGE_PCI(0x0604)
+ */
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43C
+#define PCI_CLASS_BRIDGE_PCI_MASK 0xFF0000FF
+ pci_bus_read_config_dword(&bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, &val);
+ val = (val & PCI_CLASS_BRIDGE_PCI_MASK) | (PCI_CLASS_BRIDGE_PCI << 8);
+ pci_bus_write_config_dword(&bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, val);
+
+ /* check link status to see if link is active */
+ pos = pci_bus_find_capability(&bus, 0, PCI_CAP_ID_EXP);
+ pci_bus_read_config_word(&bus, 0, pos + PCI_EXP_LNKSTA, &tmp16);
+ tmp16 &= PCI_EXP_LNKSTA_DLLLA;
+ nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
+
+ if (nlw == 0) {
+ /* try GEN 1 link speed */
+#define PCI_LINK_STATUS_CTRL_2_OFFSET 0xDC
+#define PCI_TARGET_LINK_SPEED_MASK 0xF
+#define PCI_TARGET_LINK_SPEED_GEN2 0x2
+#define PCI_TARGET_LINK_SPEED_GEN1 0x1
+ pci_bus_read_config_dword(&bus, 0,
+ PCI_LINK_STATUS_CTRL_2_OFFSET, &val);
+ if ((val & PCI_TARGET_LINK_SPEED_MASK) ==
+ PCI_TARGET_LINK_SPEED_GEN2) {
+ val &= ~PCI_TARGET_LINK_SPEED_MASK;
+ val |= PCI_TARGET_LINK_SPEED_GEN1;
+ pci_bus_write_config_dword(&bus, 0,
+ PCI_LINK_STATUS_CTRL_2_OFFSET, val);
+ pci_bus_read_config_dword(&bus, 0,
+ PCI_LINK_STATUS_CTRL_2_OFFSET, &val);
+ mdelay(100);
+
+ pos = pci_bus_find_capability(&bus, 0, PCI_CAP_ID_EXP);
+ pci_bus_read_config_word(&bus, 0, pos + PCI_EXP_LNKSTA,
+ &tmp16);
+ nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >>
+ PCI_EXP_LNKSTA_NLW_SHIFT;
+ }
+ }
+
+ dev_info(pcie->dev, "link: %s\n", nlw ? "UP" : "DOWN");
+
+ return nlw ? 0 : -ENODEV;
+}
+
+static int iproc_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct iproc_pcie *pcie = sys_to_pcie(sys);
+
+ pci_add_resource(&sys->resources, &pcie->io);
+ pci_add_resource(&sys->resources, &pcie->mem);
+ pci_add_resource(&sys->resources, &pcie->busn);
+
+ return 1;
+}
+
+static struct pci_bus *iproc_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct iproc_pcie *pcie = sys->private_data;
+ struct pci_bus *bus;
+
+ bus = pci_create_root_bus(pcie->dev, sys->busnr, &iproc_pcie_ops, sys,
+ &sys->resources);
+ if (!bus)
+ return NULL;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ bus->msi = &pcie->msi.chip;
+
+ pci_scan_child_bus(bus);
+
+ return bus;
+}
+
+static int iproc_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct iproc_pcie *pcie = sys_to_pcie(dev->sysdata);
+
+ /* need to use the 5th IRQ for INTx */
+ return pcie->irqs[4];
+}
+
+static struct hw_pci hw;
+
+static void iproc_pcie_enable(struct iproc_pcie *pcie)
+{
+ hw.nr_controllers = 1;
+ hw.private_data = (void **)&pcie;
+ hw.setup = iproc_pcie_setup;
+ hw.scan = iproc_pcie_scan_bus;
+ hw.map_irq = iproc_pcie_map_irq;
+ hw.ops = &iproc_pcie_ops;
+
+ /* enable root complex INTX */
+ writel(SYS_RC_INTX_MASK, pcie->reg + SYS_RC_INTX_EN);
+
+ pci_common_init_dev(pcie->dev, &hw);
+#ifdef CONFIG_PCI_DOMAINS
+ hw.domain++;
+#endif
+}
+
+#define PCIE_PHY_REG_ADDR 0x2103
+#define PCIE_PHY_DATA 0x2B1C
+static void iproc_pcie_mii_phy_init(struct iproc_pcie *pcie, u32 phy_addr)
+{
+ unsigned int reg_addr;
+ u16 val;
+
+ mdio_init(pcie);
+
+ reg_addr = PCIE_PHY_REG_ADDR;
+ val = PCIE_PHY_DATA;
+ iproc_pcie_phy_reg_write(pcie, phy_addr, reg_addr, val);
+ val = iproc_pcie_phy_reg_read(pcie, phy_addr, reg_addr);
+ dev_info(pcie->dev, "phy: 0x%x reg: 0x%4x val: 0x%4x\n", phy_addr,
+ reg_addr, val);
+}
+
+static inline struct iproc_msi *to_iproc_msi(struct msi_controller *chip)
+{
+ return container_of(chip, struct iproc_msi, chip);
+}
+
+static int iproc_msi_irq_assign(struct iproc_msi *chip)
+{
+ int msi;
+
+ msi = find_first_zero_bit(chip->irq_in_use, MAX_IRQS);
+ if (msi < MAX_IRQS)
+ set_bit(msi, chip->irq_in_use);
+ else
+ msi = -ENOSPC;
+
+ return msi;
+}
+
+static void iproc_msi_irq_free(struct iproc_msi *chip, unsigned long irq)
+{
+ clear_bit(irq, chip->irq_in_use);
+}
+
+static int iproc_msi_setup_irq(struct msi_controller *chip,
+ struct pci_dev *pdev, struct msi_desc *desc)
+{
+ struct iproc_msi *msi = to_iproc_msi(chip);
+ struct iproc_pcie *pcie = msi->pcie;
+ struct msi_msg msg;
+ unsigned int irq;
+ int hwirq;
+
+ hwirq = iproc_msi_irq_assign(msi);
+ if (hwirq < 0)
+ return hwirq;
+
+ irq = irq_create_mapping(msi->domain, hwirq);
+ if (!irq) {
+ iproc_msi_irq_free(msi, hwirq);
+ return -EINVAL;
+ }
+
+ dev_dbg(pcie->dev, "mapped irq:%d\n", irq);
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_lo = virt_to_phys((void *)msi->msi_page) | (hwirq * 4);
+ msg.address_hi = 0x0;
+ msg.data = hwirq;
+
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void iproc_msi_teardown_irq(struct msi_controller *chip,
+ unsigned int irq)
+{
+ struct iproc_msi *msi = to_iproc_msi(chip);
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ iproc_msi_irq_free(msi, data->hwirq);
+}
+
+static struct irq_chip iproc_msi_irq_chip = {
+ .name = "iProc PCIe MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+static int iproc_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &iproc_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops iproc_msi_domain_ops = {
+ .map = iproc_msi_map,
+};
+
+static irqreturn_t iproc_msi_irq(int irq, void *data)
+{
+ struct iproc_pcie *pcie = data;
+ unsigned int eq, head, tail, num_events;
+
+ /* Do not handle INTx interrupt */
+ if ((readl(pcie->reg + SYS_RC_INTX_CSR) & SYS_RC_INTX_MASK) != 0)
+ return IRQ_NONE;
+
+ eq = irq - pcie->irqs[0];
+ BUG_ON(eq >= MAX_MSI_EQ);
+
+ irq = irq_find_mapping(pcie->msi.domain, eq);
+ head = readl(pcie->reg + SYS_EQ_HEAD_0_OFFSET + (eq * 8));
+ do {
+ tail = readl(pcie->reg + SYS_EQ_TAIL_0_OFFSET + (eq * 8));
+ tail &= SYS_EQ_TAIL_0_MASK;
+
+ num_events = (tail < head) ?
+ (64 + (tail - head)) : (tail - head);
+ if (!num_events)
+ break;
+
+ generic_handle_irq(irq);
+
+ head++;
+ head %= 64;
+ writel(head, pcie->reg + SYS_EQ_HEAD_0_OFFSET + (eq * 8));
+ } while (true);
+
+ return IRQ_HANDLED;
+}
+
+static int iproc_pcie_enable_msi(struct iproc_pcie *pcie)
+{
+ struct iproc_msi *msi = &pcie->msi;
+ struct device_node *np = pcie->dev->of_node;
+ int i, ret;
+ u32 val;
+
+ msi->pcie = pcie;
+ msi->chip.dev = pcie->dev;
+ msi->chip.setup_irq = iproc_msi_setup_irq;
+ msi->chip.teardown_irq = iproc_msi_teardown_irq;
+
+ msi->domain = irq_domain_add_linear(pcie->dev->of_node, MAX_IRQS,
+ &iproc_msi_domain_ops, &msi->chip);
+ if (!msi->domain) {
+ dev_err(pcie->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX_IRQS; i++) {
+ ret = devm_request_irq(pcie->dev, pcie->irqs[i],
+ iproc_msi_irq, IRQF_SHARED, "iproc-pcie", pcie);
+ if (ret < 0) {
+ dev_err(pcie->dev, "failed to request IRQ: %d\n",
+ pcie->irqs[i]);
+ goto err_rm_irq_domain;
+ }
+ }
+
+ msi->eq_page = __get_free_pages(GFP_KERNEL, 0);
+ if (!msi->eq_page) {
+ dev_err(pcie->dev,
+ "failed to allocate memory for MSI event queue\n");
+ ret = -ENOMEM;
+ goto err_rm_irq_domain;
+ }
+
+ msi->msi_page = __get_free_pages(GFP_KERNEL, 0);
+ if (!msi->msi_page) {
+ dev_err(pcie->dev,
+ "failed to allocate memory for MSI\n");
+ ret = -ENOMEM;
+ goto err_free_msi_eq_page;
+ }
+
+ writel(virt_to_phys((void *)msi->eq_page),
+ pcie->reg + SYS_EQ_PAGE_OFFSET);
+ writel(virt_to_phys((void *)msi->msi_page),
+ pcie->reg + SYS_MSI_PAGE_OFFSET);
+
+ for (i = 0; i < MAX_MSI_EQ; i++) {
+ /* enable MSI event queue and interrupt */
+ val = SYS_MSI_INTR_EN | SYS_MSI_INT_N_EVENT | SYS_MSI_EQ_EN;
+ writel(val, pcie->reg + SYS_MSI_CTRL_0_OFFSET + (i * 4));
+ /*
+ * To support legacy platforms that require the MSI interrupt
+ * enable register to be set explicitly
+ */
+ if (of_find_property(np, "have-msi-inten-reg", NULL)) {
+ val = readl(pcie->reg + SYS_MSI_INTS_EN_OFFSET);
+ val |= (1 << i);
+ writel(val, pcie->reg + SYS_MSI_INTS_EN_OFFSET);
+ }
+ }
+
+ dev_info(pcie->dev, "MSI enabled\n");
+ return 0;
+
+err_free_msi_eq_page:
+ free_pages(msi->eq_page, 0);
+
+err_rm_irq_domain:
+ irq_domain_remove(msi->domain);
+ return ret;
+}
+
+static int __init iproc_pcie_probe(struct platform_device *pdev)
+{
+ struct iproc_pcie *pcie;
+ struct device_node *np = pdev->dev.of_node;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ struct resource res, regs;
+ int i, ret;
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie),
+ GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcie);
+
+ if (of_pci_parse_bus_range(pdev->dev.of_node, &pcie->busn)) {
+ dev_err(&pdev->dev, "failed to parse bus-range property\n");
+ return -EINVAL;
+ }
+
+ /* PCIE controller registers */
+ ret = of_address_to_resource(np, 0, ®s);
+ if (ret) {
+ dev_err(pcie->dev, "unable to obtain device resources\n");
+ return -ENODEV;
+ }
+
+ pcie->reg = devm_ioremap(pcie->dev, regs.start, resource_size(®s));
+ if (!pcie->reg) {
+ dev_err(pcie->dev, "unable to map device reg resources\n");
+ return -ENOMEM;
+ }
+
+ /* MDIO registers */
+ ret = of_address_to_resource(np, 1, ®s);
+ if (ret) {
+ dev_err(pcie->dev, "unable to obtain device resources\n");
+ return -ENODEV;
+ }
+
+ pcie->mii = devm_ioremap(pcie->dev, regs.start, resource_size(®s));
+ if (!pcie->mii) {
+ dev_err(pcie->dev, "unable to map device mii resources\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX_IRQS; i++) {
+ pcie->irqs[i] = irq_of_parse_and_map(np, i);
+ if (!pcie->irqs[i]) {
+ dev_err(pcie->dev, "unable to parse irq index:%d\n", i);
+ return -ENODEV;
+ }
+ }
+
+ if (of_property_read_u32(np, "phy-addr", &pcie->phy_addr)) {
+ dev_err(pcie->dev, "missing \"phy-addr\" property in DT\n");
+ return -EINVAL;
+ }
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(pcie->dev, "missing \"ranges\" property in DT\n");
+ return -EINVAL;
+ }
+
+ /* Get the PCI memory ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ of_pci_range_to_resource(&range, np, &res);
+
+ switch (res.flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ memcpy(&pcie->io, &res, sizeof(res));
+ pcie->io.name = "I/O";
+ break;
+
+ case IORESOURCE_MEM:
+ memcpy(&pcie->mem, &res, sizeof(res));
+ pcie->mem.name = "MEM";
+ break;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ ret = iproc_pcie_enable_msi(pcie);
+ if (ret < 0) {
+ dev_err(pcie->dev, "failed to enable MSI support\n");
+ return ret;
+ }
+ }
+
+ iproc_pcie_mii_phy_init(pcie, pcie->phy_addr);
+
+ iproc_pcie_reset(pcie);
+
+ ret = iproc_pcie_check_link(pcie);
+ if (ret) {
+ dev_err(pcie->dev, "no PCIe EP device detected\n");
+ return ret;
+ }
+
+ iproc_pcie_enable(pcie);
+ pci_assign_unassigned_resources();
+
+ return 0;
+}
+
+static const struct of_device_id iproc_pcie_of_match_table[] = {
+ { .compatible = "brcm,iproc-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
+
+static struct platform_driver iproc_pcie_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "iproc-pcie",
+ .of_match_table =
+ of_match_ptr(iproc_pcie_of_match_table),
+ },
+};
+
+static int __init iproc_pcie_init(void)
+{
+ return platform_driver_probe(&iproc_pcie_driver,
+ iproc_pcie_probe);
+}
+subsys_initcall(iproc_pcie_init);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iPROC PCIe driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] ARM: mach-bcm: Enable PCIe support for iProc
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
2014-12-10 0:04 ` [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding Ray Jui
2014-12-10 0:04 ` [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver Ray Jui
@ 2014-12-10 0:04 ` Ray Jui
2014-12-10 0:04 ` [PATCH 4/4] ARM: dts: enable PCIe for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:04 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable PCIe driver support for Broadcom iProc family of SoCs by
selecting PCIE_IPROC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..a13a0b2 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: enable PCIe for Broadcom Cygnus
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
` (2 preceding siblings ...)
2014-12-10 0:04 ` [PATCH 3/4] ARM: mach-bcm: Enable PCIe support for iProc Ray Jui
@ 2014-12-10 0:04 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:04 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, 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
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 43 +++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/bcm958300k.dts | 8 +++++++
2 files changed, 51 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..669bb3b 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -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>;
diff --git a/arch/arm/boot/dts/bcm958300k.dts b/arch/arm/boot/dts/bcm958300k.dts
index f1bb36f..c9eb856 100644
--- a/arch/arm/boot/dts/bcm958300k.dts
+++ b/arch/arm/boot/dts/bcm958300k.dts
@@ -47,6 +47,14 @@
bootargs = "console=ttyS0,115200";
};
+ pcie0: pcie@18012000 {
+ status = "okay";
+ };
+
+ pcie1: pcie@18013000 {
+ status = "okay";
+ };
+
uart3: serial@18023000 {
status = "okay";
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 0/4] Add I2C support to Broadcom iProc
[not found] <Ray Jui <rjui@broadcom.com>
` (14 preceding siblings ...)
2014-12-10 0:04 ` [PATCH 0/4] Add PCIe support to Broadcom iProc Ray Jui
@ 2014-12-10 0:54 ` Ray Jui
2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
` (3 more replies)
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
` (18 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
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
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
@ 2014-12-10 0:54 ` Ray Jui
2014-12-10 1:27 ` Varka Bhadram
2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
` (2 subsequent siblings)
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the I2C device tree binding for Broadcom iProc family of
SoCs
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../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
+
+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>;
+ };
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
@ 2014-12-10 0:54 ` Ray Jui
2014-12-10 1:33 ` Varka Bhadram
2014-12-10 0:54 ` [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui
2014-12-10 0:54 ` [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, 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)
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
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
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c1351d9..8a2eb7e 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -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
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5e6c822..216e7be 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -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
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
new file mode 100644
index 0000000..0e6e603
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -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");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev,
+ struct i2c_msg *msg, u8 *addr)
+{
+
+ 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:
+ 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)
+{
+ 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);
+ 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;
+ 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;
+}
+
+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,
+ .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 <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
@ 2014-12-10 0:54 ` Ray Jui
2014-12-10 0:54 ` [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable I2C driver support for Broadcom iProc family of SoCs by
selecting I2C_BCM_IPROC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..86ee90b 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
select PINCTRL
+ select I2C_BCM_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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
` (2 preceding siblings ...)
2014-12-10 0:54 ` [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui
@ 2014-12-10 0:54 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep
them disabled there. Individual I2C devices can be enabled in board
specific dts file when I2C slave devices are enabled in the future
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..f7d6c1d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -70,6 +70,26 @@
};
};
+ 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>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@1800b000 {
+ compatible = "brcm,iproc-i2c";
+ reg = <0x1800b000 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
uart0: serial@18020000 {
compatible = "snps,dw-apb-uart";
reg = <0x18020000 0x100>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding
2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
@ 2014-12-10 1:27 ` Varka Bhadram
2014-12-10 1:35 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Varka Bhadram @ 2014-12-10 1:27 UTC (permalink / raw)
To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
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 <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> .../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
....
--
Thanks and Regards,
Varka Bhadram.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver
2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
@ 2014-12-10 1:33 ` Varka Bhadram
2014-12-10 1:41 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Varka Bhadram @ 2014-12-10 1:33 UTC (permalink / raw)
To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote:
> 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)
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> 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
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index c1351d9..8a2eb7e 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -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
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 5e6c822..216e7be 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -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
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> new file mode 100644
> index 0000000..0e6e603
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -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");
> + 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)
> +
> + 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:
> + 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...
> + 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);
> + 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()
> + 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;
> +}
> +
> +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().
> + .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 <rjui@broadcom.com>");
> +MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
> +MODULE_LICENSE("GPL v2");
--
Thanks and Regards,
Varka Bhadram.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding
2014-12-10 1:27 ` Varka Bhadram
@ 2014-12-10 1:35 ` Ray Jui
[not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg@mail.gmail.com>
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 1:35 UTC (permalink / raw)
To: Varka Bhadram, Wolfram Sang, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
Christian Daudt, Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
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 <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>> ---
>> .../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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver
2014-12-10 1:33 ` Varka Bhadram
@ 2014-12-10 1:41 ` Ray Jui
[not found] ` <CAEUmHyZ+VqjzL4LkQozGJtnictPNXHYWM2qMKvD=LmfQdcT8iQ@mail.gmail.com>
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 1:41 UTC (permalink / raw)
To: Varka Bhadram, Wolfram Sang, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
Christian Daudt, Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 12/9/2014 5:33 PM, Varka Bhadram wrote:
>
> On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote:
>> 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)
>>
>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>> ---
>> 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
>>
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index c1351d9..8a2eb7e 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -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
>> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> index 5e6c822..216e7be 100644
>> --- a/drivers/i2c/busses/Makefile
>> +++ b/drivers/i2c/busses/Makefile
>> @@ -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
>> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c
>> b/drivers/i2c/busses/i2c-bcm-iproc.c
>> new file mode 100644
>> index 0000000..0e6e603
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
>> @@ -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");
>> + 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)
>
>
Okay I can make this change.
>> +
>> + 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:
>> + 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...
>
One more indent? Sure.
>> + 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);
>> + 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()
>
Don't you need to obtain a valid resource and pass it into
devm_ioremap_resource? Without 'res' being assigned a valid resource,
devm_ioremap_resource will reject with "invalid resource".
>> + 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;
>> +}
>> +
>> +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().
>
Okay will get rid of .owner = THIS_MODULES,
>> + .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 <rjui@broadcom.com>");
>> +MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
>> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 0/4] Add I2C support to Broadcom iProc
[not found] <Ray Jui <rjui@broadcom.com>
` (15 preceding siblings ...)
2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui
@ 2014-12-10 2:18 ` Ray Jui
2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
` (3 more replies)
2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui
` (17 subsequent siblings)
34 siblings, 4 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
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)
Changes from v1:
- Fix function argument parenthesis
- Get rid of redundant driver owner field
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 | 502 ++++++++++++++++++++
6 files changed, 570 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
@ 2014-12-10 2:18 ` Ray Jui
2014-12-10 2:18 ` [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
` (2 subsequent siblings)
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the I2C device tree binding for Broadcom iProc family of
SoCs
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../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
+
+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>;
+ };
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
@ 2014-12-10 2:18 ` Ray Jui
2014-12-10 2:18 ` [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui
2014-12-10 2:18 ` [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, 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)
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
drivers/i2c/busses/Kconfig | 9 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-bcm-iproc.c | 502 ++++++++++++++++++++++++++++++++++++
3 files changed, 512 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c1351d9..8a2eb7e 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -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
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5e6c822..216e7be 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -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
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
new file mode 100644
index 0000000..57f5f29
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -0,0 +1,502 @@
+/*
+ * 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");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev,
+ struct i2c_msg *msg, u8 *addr)
+{
+
+ 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:
+ 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)
+{
+ 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);
+ 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;
+ 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;
+}
+
+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",
+ .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 <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
2014-12-10 2:18 ` [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
@ 2014-12-10 2:18 ` Ray Jui
2014-12-10 2:20 ` Florian Fainelli
2014-12-10 2:18 ` [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
3 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Enable I2C driver support for Broadcom iProc family of SoCs by
selecting I2C_BCM_IPROC
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/mach-bcm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..86ee90b 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
select PINCTRL
+ select I2C_BCM_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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
` (2 preceding siblings ...)
2014-12-10 2:18 ` [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui
@ 2014-12-10 2:18 ` Ray Jui
3 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep
them disabled there. Individual I2C devices can be enabled in board
specific dts file when I2C slave devices are enabled in the future
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..f7d6c1d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -70,6 +70,26 @@
};
};
+ 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>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@1800b000 {
+ compatible = "brcm,iproc-i2c";
+ reg = <0x1800b000 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
uart0: serial@18020000 {
compatible = "snps,dw-apb-uart";
reg = <0x18020000 0x100>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 2:18 ` [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui
@ 2014-12-10 2:20 ` Florian Fainelli
2014-12-10 2:24 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Florian Fainelli @ 2014-12-10 2:20 UTC (permalink / raw)
To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 09/12/14 18:18, Ray Jui wrote:
> Enable I2C driver support for Broadcom iProc family of SoCs by
> selecting I2C_BCM_IPROC
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> arch/arm/mach-bcm/Kconfig | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
> index aaeec78..86ee90b 100644
> --- a/arch/arm/mach-bcm/Kconfig
> +++ b/arch/arm/mach-bcm/Kconfig
> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
> select ARCH_REQUIRE_GPIOLIB
> select ARM_AMBA
> select PINCTRL
> + select I2C_BCM_IPROC
One way to avoid having to modify mach-bcm/Kconfig would be to have your
i2c driver Kconfig do this:
default ARCH_BCM_IPROC
would that work?
> help
> This enables support for systems based on Broadcom IPROC architected SoCs.
> The IPROC complex contains one or more ARM CPUs along with common
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 2:20 ` Florian Fainelli
@ 2014-12-10 2:24 ` Ray Jui
2014-12-10 3:20 ` Florian Fainelli
0 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 2:24 UTC (permalink / raw)
To: Florian Fainelli, Wolfram Sang, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
Christian Daudt, Matt Porter, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 12/9/2014 6:20 PM, Florian Fainelli wrote:
> On 09/12/14 18:18, Ray Jui wrote:
>> Enable I2C driver support for Broadcom iProc family of SoCs by
>> selecting I2C_BCM_IPROC
>>
>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>> ---
>> arch/arm/mach-bcm/Kconfig | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
>> index aaeec78..86ee90b 100644
>> --- a/arch/arm/mach-bcm/Kconfig
>> +++ b/arch/arm/mach-bcm/Kconfig
>> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
>> select ARCH_REQUIRE_GPIOLIB
>> select ARM_AMBA
>> select PINCTRL
>> + select I2C_BCM_IPROC
>
> One way to avoid having to modify mach-bcm/Kconfig would be to have your
> i2c driver Kconfig do this:
>
> default ARCH_BCM_IPROC
>
> would that work?
>
Yes. So in which case it is better to select a driver from the
architecture specific Kconfig?
>> help
>> This enables support for systems based on Broadcom IPROC architected SoCs.
>> The IPROC complex contains one or more ARM CPUs along with common
>>
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 2:24 ` Ray Jui
@ 2014-12-10 3:20 ` Florian Fainelli
2014-12-10 3:58 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Florian Fainelli @ 2014-12-10 3:20 UTC (permalink / raw)
To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 09/12/14 18:24, Ray Jui wrote:
>
>
> On 12/9/2014 6:20 PM, Florian Fainelli wrote:
>> On 09/12/14 18:18, Ray Jui wrote:
>>> Enable I2C driver support for Broadcom iProc family of SoCs by
>>> selecting I2C_BCM_IPROC
>>>
>>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>>> ---
>>> arch/arm/mach-bcm/Kconfig | 1 +
>>> 1 file changed, 1 insertion(+)
>>>
>>> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
>>> index aaeec78..86ee90b 100644
>>> --- a/arch/arm/mach-bcm/Kconfig
>>> +++ b/arch/arm/mach-bcm/Kconfig
>>> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
>>> select ARCH_REQUIRE_GPIOLIB
>>> select ARM_AMBA
>>> select PINCTRL
>>> + select I2C_BCM_IPROC
>>
>> One way to avoid having to modify mach-bcm/Kconfig would be to have your
>> i2c driver Kconfig do this:
>>
>> default ARCH_BCM_IPROC
>>
>> would that work?
>>
> Yes. So in which case it is better to select a driver from the
> architecture specific Kconfig?
I suppose if your driver/subsystem is critical for system boot, like
powering a regulator or something that has a critical purpose, a select
is probably more appropriate here. If this is just exposing non-critical
devices, I would go with a depends on/default at the driver Kconfig level.
This is just how I see things, others would definitively have a
different view.
>
>>> help
>>> This enables support for systems based on Broadcom IPROC
>>> architected SoCs.
>>> The IPROC complex contains one or more ARM CPUs along with
>>> common
>>>
>>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding
[not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg@mail.gmail.com>
@ 2014-12-10 3:27 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:27 UTC (permalink / raw)
To: Varka Bhadram
Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 12/9/2014 7:12 PM, Varka Bhadram wrote:
>
>
> On Wed, Dec 10, 2014 at 7:05 AM, Ray Jui <rjui@broadcom.com
> <mailto:rjui@broadcom.com>> 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 <rjui@broadcom.com
> <mailto:rjui@broadcom.com>>
> Reviewed-by: Scott Branden <sbranden@broadcom.com
> <mailto:sbranden@broadcom.com>>
> ---
> .../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.
>
>
> 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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver
[not found] ` <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ@mail.gmail.com>
@ 2014-12-10 3:31 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:31 UTC (permalink / raw)
To: Varka Bhadram
Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 12/9/2014 7:28 PM, Varka Bhadram wrote:
>
>
> On Wed, Dec 10, 2014 at 8:51 AM, Varka Bhadram <varkabhadram@gmail.com
> <mailto:varkabhadram@gmail.com>> wrote:
>
> On Wed, Dec 10, 2014 at 7:11 AM, Ray Jui <rjui@broadcom.com
> <mailto:rjui@broadcom.com>> wrote:
>
>
>
> On 12/9/2014 5:33 PM, Varka Bhadram wrote:
>
>
> On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote:
>
> 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)
>
> Signed-off-by: Ray Jui <rjui@broadcom.com
> <mailto:rjui@broadcom.com>>
> Reviewed-by: Scott Branden <sbranden@broadcom.com
> <mailto:sbranden@broadcom.com>>
> ---
> 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
>
> (...)
>
> +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);
> + 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()
>
> Don't you need to obtain a valid resource and pass it into
> devm_ioremap_resource? Without 'res' being assigned a valid
> resource, devm_ioremap_resource will reject with "invalid resource".
>
> platform_get_resource() will return a resource, checking on this
> resource is happening at
> http://lxr.free-electrons.com/source/lib/devres.c#L115. So no need
> to check it explicitly.
>
> If you check here it will be duplication of check with resource. Two
> times we are checking on
> the resource. No point of doing like that.
>
> Thanks.
Sorry I misunderstood what you meant. Okay I'll get rid of if (!res)
check there. Thanks.
>
>
> See this: http://lxr.free-electrons.com/source/lib/devres.c#L102
>
> --
> Thanks and Regards,
> Varka Bhadram.
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v3 0/3] Add I2C support to Broadcom iProc
[not found] <Ray Jui <rjui@broadcom.com>
` (16 preceding siblings ...)
2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui
@ 2014-12-10 3:57 ` Ray Jui
2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
` (2 more replies)
2014-12-12 0:05 ` [PATCH v5 0/3] Add gpio support to Broadcom Cygnus SoC Ray Jui
` (16 subsequent siblings)
34 siblings, 3 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
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)
Changes from v2:
- Have the I2C driver default to y so it does not need to be selected from
ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still
depends on ARCH_BCM_IPROC
- Get rid of redundant check on resource returned by platform_get_resource
Changes from v1:
- Fix function argument parenthesis
- Get rid of redundant driver owner field
Ray Jui (3):
i2c: iProc: define Broadcom iProc I2C binding
i2c: iproc: Add Broadcom iProc I2C Driver
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 +
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++
5 files changed, 568 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 355+ messages in thread
* [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding
2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui
@ 2014-12-10 3:57 ` Ray Jui
2014-12-10 3:57 ` [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
2014-12-10 3:57 ` [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
2 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Document the I2C device tree binding for Broadcom iProc family of
SoCs
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
.../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
+
+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>;
+ };
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver
2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
@ 2014-12-10 3:57 ` Ray Jui
2015-01-13 22:50 ` Uwe Kleine-König
2014-12-10 3:57 ` [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui
2 siblings, 1 reply; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, 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)
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++++++++++++++++++
3 files changed, 511 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c1351d9..df21366 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -372,6 +372,16 @@ 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
+ default y
+ 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
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5e6c822..216e7be 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -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
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
new file mode 100644
index 0000000..35ac497
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -0,0 +1,500 @@
+/*
+ * 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");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev,
+ struct i2c_msg *msg, u8 *addr)
+{
+
+ 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:
+ 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)
+{
+ 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);
+ 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);
+ 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;
+}
+
+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",
+ .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 <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus
2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui
2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui
2014-12-10 3:57 ` [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui
@ 2014-12-10 3:57 ` Ray Jui
2 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw)
To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, Ray Jui
Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep
them disabled there. Individual I2C devices can be enabled in board
specific dts file when I2C slave devices are enabled in the future
Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..f7d6c1d 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -70,6 +70,26 @@
};
};
+ 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>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@1800b000 {
+ compatible = "brcm,iproc-i2c";
+ reg = <0x1800b000 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
uart0: serial@18020000 {
compatible = "snps,dw-apb-uart";
reg = <0x18020000 0x100>;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 355+ messages in thread
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc
2014-12-10 3:20 ` Florian Fainelli
@ 2014-12-10 3:58 ` Ray Jui
0 siblings, 0 replies; 355+ messages in thread
From: Ray Jui @ 2014-12-10 3:58 UTC (permalink / raw)
To: Florian Fainelli, Wolfram Sang, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
Christian Daudt, Matt Porter, Russell King
Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
On 12/9/2014 7:20 PM, Florian Fainelli wrote:
> On 09/12/14 18:24, Ray Jui wrote:
>>
>>
>> On 12/9/2014 6:20 PM, Florian Fainelli wrote:
>>> On 09/12/14 18:18, Ray Jui wrote:
>>>> Enable I2C driver support for Broadcom iProc family of SoCs by
>>>> selecting I2C_BCM_IPROC
>>>>
>>>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>>>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>>>> ---
>>>> arch/arm/mach-bcm/Kconfig | 1 +
>>>> 1 file changed, 1 insertion(+)
>>>>
>>>> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
>>>> index aaeec78..86ee90b 100644
>>>> --- a/arch/arm/mach-bcm/Kconfig
>>>> +++ b/arch/arm/mach-bcm/Kconfig
>>>> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC
>>>> select ARCH_REQUIRE_GPIOLIB
>>>> select ARM_AMBA
>>>> select PINCTRL
>>>> + select I2C_BCM_IPROC
>>>
>>> One way to avoid having to modify mach-bcm/Kconfig would be to have your
>>> i2c driver Kconfig do this:
>>>
>>> default ARCH_BCM_IPROC
>>>
>>> would that work?
>>>
>> Yes. So in which case it is better to select a driver from the
>> architecture specific Kconfig?
>
> I suppose if your driver/subsystem is critical for system boot, like
> powering a regulator or something that has a critical purpose, a select
> is probably more appropriate here. If this is just exposing non-critical
> devices, I would go with a depends on/default at the driver Kconfig level.
>
> This is just how I see things, others would definitively have a
> different view.
>
Okay. Thanks. I default the driver to y in patchset v3 just like some
other I2C drivers in the same Kconfig. It already depends on
ARCH_BCM_IPROC so it makes sense to set the default to y, i.e., it will
be enabled by default for ARCH_BCM_IPROC platforms.
>>
>>>> help
>>>> This enables support for systems based on Broadcom IPROC
>>>> architected SoCs.
>>>> The IPROC complex contains one or more ARM CPUs along with
>>>> common
>>>>
>>>
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding
2014-12-10 0:04 ` [PATCH 1/4] pci: iProc: define Broadcom iProc PCIe binding Ray Jui
@ 2014-12-10 10:30 ` Lucas Stach
2014-12-11 1:37 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Lucas Stach @ 2014-12-10 10:30 UTC (permalink / raw)
To: Ray Jui
Cc: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree
Am Dienstag, den 09.12.2014, 16:04 -0800 schrieb Ray Jui:
> Document the PCIe device tree binding for Broadcom iProc family of SoCs
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> .../devicetree/bindings/pci/brcm,iproc-pcie.txt | 62 ++++++++++++++++++++
> 1 file changed, 62 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
>
> diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
> new file mode 100644
> index 0000000..2467628
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
> @@ -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>;
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
> + 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>;
> + };
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH v4 2/5] gpio: Cygnus: add GPIO driver
2014-12-08 20:41 ` [PATCH v4 2/5] gpio: Cygnus: add GPIO driver Ray Jui
@ 2014-12-10 10:34 ` Alexandre Courbot
2014-12-11 1:30 ` Ray Jui
0 siblings, 1 reply; 355+ messages in thread
From: Alexandre Courbot @ 2014-12-10 10:34 UTC (permalink / raw)
To: Ray Jui
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Linus Walleij, Grant Likely, Christian Daudt, Matt Porter,
Florian Fainelli, Russell King, Joe Perches, Arnd Bergmann,
Scott Branden, Linux Kernel Mailing List, linux-arm-kernel,
linux-gpio, bcm-kernel-feedback-list, devicetree
On Tue, Dec 9, 2014 at 5:41 AM, Ray Jui <rjui@broadcom.com> wrote:
> 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
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
> 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
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 633ec21..3e3b0342 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -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
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 81755f1..31eb7e0 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -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
> diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
> new file mode 100644
> index 0000000..4fd9b73
> --- /dev/null
> +++ b/drivers/gpio/gpio-bcm-cygnus.c
> @@ -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;
> + 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?
> +
> +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,
> + 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?
> +
> +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 */
> +
> +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 */
> + 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.
> +
> + 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;
> +
> + offset = __gpio_reg_offset(cygnus_gpio, gpio) +
> + CYGNUS_GPIO_INT_CLR_OFFSET;
> + shift = __gpio_bitpos(cygnus_gpio, gpio);
> +
> + val = 1 << shift;
val = BIT(shift);
> + 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;
> + 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);
> + 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;
> + 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.
> + 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);
}
> +
> +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);
> + 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);
> +
> + 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);
> +
> + 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.
> +
> + 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;
> +
> + 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.
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver
2014-12-10 0:04 ` [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver Ray Jui
@ 2014-12-10 11:31 ` Arnd Bergmann
2014-12-10 16:46 ` Scott Branden
0 siblings, 1 reply; 355+ messages in thread
From: Arnd Bergmann @ 2014-12-10 11:31 UTC (permalink / raw)
To: Ray Jui
Cc: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, Scott Branden,
linux-pci, linux-kernel, linux-arm-kernel,
bcm-kernel-feedback-list, devicetree, hauke
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
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
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
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver
2014-12-10 11:31 ` Arnd Bergmann
@ 2014-12-10 16:46 ` Scott Branden
2014-12-10 18:46 ` Florian Fainelli
0 siblings, 1 reply; 355+ messages in thread
From: Scott Branden @ 2014-12-10 16:46 UTC (permalink / raw)
To: Arnd Bergmann, Ray Jui
Cc: Bjorn Helgaas, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt,
Matt Porter, Florian Fainelli, Russell King, linux-pci,
linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, hauke
On 14-12-10 03:31 AM, Arnd Bergmann wrote:
> 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
>>
>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>
> 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.
>
> 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?
>
> Arnd
>
^ permalink raw reply [flat|nested] 355+ messages in thread
* Re: [PATCH 2/4] PCI: iproc: Add Broadcom iProc PCIe driver
2014-12-10 16:46 ` Scott Branden
@ 2014-12-10 18:46 ` Florian Fainelli
2014-12-10 20:26 ` Hauke Mehrtens
2014-12-11 9:44 ` Arend van Spriel
0 siblings, 2 replies; 355+ messages in thread
From: Florian Fainelli @ 2014-12-10 18:46 UTC (permalink / raw)
To: Scott Branden
Cc: Arnd Bergmann, Ray Jui, Bjorn Helgaas, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
Christian Daudt, Matt Porter, Russell King, linux-pci,
linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list,
devicetree, Hauke Mehrtens
2014-12-10 8:46 GMT-08:00 Scott Branden <sbranden@broadcom.com>:
> On 14-12-10 03:31 AM, Arnd Bergmann wrote:
>>
>> 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
>>>
>>> Signed-off-by: Ray Jui <rjui@broadcom.com>
>>> Reviewed-by: Scott Branden <sbranden@broadcom.com>
>>
>>
>> The driver looks suspiciously like the one that Hauke already submitted a
>> while