From: Arnd Bergmann <arnd@arndb.de>
To: linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 3/8] Add a mfd IPUv3 driver
Date: Mon, 28 Feb 2011 17:11:45 +0000 [thread overview]
Message-ID: <201102281811.45277.arnd@arndb.de> (raw)
In-Reply-To: <1298887229-7987-4-git-send-email-s.hauer@pengutronix.de>
Hi Sascha,
I've had a brief look around the driver. It looks reasonable in general,
but the division into subdrivers feels a bit ad-hoc. If all the components
are built into a single module, I don't see the need for separating the
data by functional units by file. It seems simple enough to turn this
into a layered driver with multiple sub-devices each derived from a
platform_device on its own.
On Monday 28 February 2011, Sascha Hauer wrote:
> arch/arm/plat-mxc/include/mach/ipu-v3.h | 49 +++
> drivers/video/Kconfig | 2 +
> drivers/video/Makefile | 1 +
> drivers/video/imx-ipu-v3/Kconfig | 10 +
> drivers/video/imx-ipu-v3/Makefile | 3 +
> drivers/video/imx-ipu-v3/ipu-common.c | 666 +++++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-cpmem.c | 612 ++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dc.c | 364 +++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-di.c | 550 +++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dmfc.c | 355 ++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dp.c | 476 ++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-prv.h | 216 ++++++++++
> include/video/imx-ipu-v3.h | 219 ++++++++++
I wonder if this is something that would fit better in drivers/gpu instead
of drivers/video. We recently discussed the benefits of KMS vs fb drivers,
and I think it would be good to be prepared for making this a KMS driver
in the long run, even if the immediate target has to be a simple frame buffer
driver.
> +#include "ipu-prv.h"
> +
> +static struct clk *ipu_clk;
> +static struct device *ipu_dev;
> +
> +static DEFINE_SPINLOCK(ipu_lock);
> +static DEFINE_MUTEX(ipu_channel_lock);
> +void __iomem *ipu_cm_reg;
> +void __iomem *ipu_idmac_reg;
> +
> +static int ipu_use_count;
> +
> +static struct ipu_channel channels[64];
Keeping these global limits you to just one ipu, and makes
understanding the code a little harder. How about moving
these variables into a struct ipu_data (or similar) that
is referenced from the platform_device?
> +EXPORT_SYMBOL(ipu_idmac_put);
Why not EXPORT_SYMBOL_GPL?
> +static LIST_HEAD(ipu_irq_handlers);
> +
> +static void ipu_irq_update_irq_mask(void)
> +{
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + DECLARE_IPU_IRQ_BITMAP(irqs);
> +
> + bitmap_zero(irqs, IPU_IRQ_COUNT);
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list)
> + bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
> + ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
> +}
> +
> +int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ipu_lock, flags);
> +
> + list_add_tail(&ipuirq->list, &ipu_irq_handlers);
> + ipu_irq_update_irq_mask();
> +
> + spin_unlock_irqrestore(&ipu_lock, flags);
> + return 0;
> +}
> +EXPORT_SYMBOL(ipu_irq_add_handler);
The interrupt logic needs some comments. What are you trying to achieve here?
> +int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
> +{
> + struct ipu_irq_handler handler;
> + DECLARE_COMPLETION_ONSTACK(completion);
> + int ret;
> +
> + bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
> + bitmap_set(handler.ipu_irqs, interrupt, 1);
> +
> + ipu_cm_write(1 << (interrupt % 32), IPU_INT_STAT(interrupt / 32 + 1));
> +
> + handler.handler = ipu_completion_handler;
> + handler.context = &completion;
> + ipu_irq_add_handler(&handler);
> +
> + ret = wait_for_completion_timeout(&completion,
> + msecs_to_jiffies(timeout_ms));
> +
> + ipu_irq_remove_handler(&handler);
> +
> + if (ret > 0)
> + ret = 0;
> + else
> + ret = -ETIMEDOUT;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(ipu_wait_for_interrupt);
If I understand this correctly, this is a very complicated way to implement
something that could be done with a single wait_event_timeout() and
wake_up_interruptible_all() from the irq handler.
> +static irqreturn_t ipu_irq_handler(int irq, void *desc)
> +{
> + DECLARE_IPU_IRQ_BITMAP(status);
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
> + status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
> + ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
> + }
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list) {
> + DECLARE_IPU_IRQ_BITMAP(tmp);
> + if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
> + handler->handler(tmp, handler->context);
> + }
> +
> + return IRQ_HANDLED;
> +}
This needs to take spin_lock_irqsave before walking the ipu_irq_handlers
list, in order to prevent another CPU from modifying the list
while you are in the handler.
> +static int ipu_reset(void)
> +{
> + int timeout = 10000;
> + u32 val;
> +
> + /* hard reset the IPU */
> + val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> + val |= 1 << 3;
> + writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> +
> + ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
> +
> + while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
> + if (!timeout--)
> + return -ETIME;
> + udelay(100);
> + }
You have a timeout of over one second with udelay, which
is not acceptable on many systems. AFAICT, the function
can sleep, so you can replace udelay with msleep(1), and
you should use a better logic to determine the end of the
loop:
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
if (time_after(jiffies, timeout))
return -ETIME;
msleep(1);
}
> +static u32 *ipu_cpmem_base;
> +static struct device *ipu_dev;
> +
> +struct ipu_ch_param_word {
> + u32 data[5];
> + u32 res[3];
> +};
> +
> +struct ipu_ch_param {
> + struct ipu_ch_param_word word[2];
> +};
Same comment as for the previous file
> +
> +static void __iomem *ipu_dc_reg;
> +static void __iomem *ipu_dc_tmpl_reg;
> +static struct device *ipu_dev;
> +
> +struct ipu_dc {
> + unsigned int di; /* The display interface number assigned to this dc channel */
> + unsigned int channel_offset;
> +};
> +
> +static struct ipu_dc dc_channels[10];
And here again.
> +static void ipu_dc_link_event(int chan, int event, int addr, int priority)
> +{
> + u32 reg;
> +
> + reg = __raw_readl(DC_RL_CH(chan, event));
> + reg &= ~(0xFFFF << (16 * (event & 0x1)));
> + reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
> + __raw_writel(reg, DC_RL_CH(chan, event));
> +}
Better use readl/writel instead of __raw_readl/__raw_writel, throughout the
code.
> +int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
> + u32 module, struct clk *ipu_clk);
> +void ipu_di_exit(struct platform_device *pdev, int id);
> +
> +int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
> + struct clk *ipu_clk);
> +void ipu_dmfc_exit(struct platform_device *pdev);
> +
> +int ipu_dp_init(struct platform_device *pdev, unsigned long base);
> +void ipu_dp_exit(struct platform_device *pdev);
> +
> +int ipu_dc_init(struct platform_device *pdev, unsigned long base,
> + unsigned long template_base);
> +void ipu_dc_exit(struct platform_device *pdev);
> +
> +int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
> +void ipu_cpmem_exit(struct platform_device *pdev);
If you make the main driver an mfd device, the sub-drivers could become
real platform drivers, which can structure the layering in a more modular
way.
E.g. instead of a single module init function, each subdriver can be
a module by itself and look at the resources associated with the
platform device it matches.
Arnd
WARNING: multiple messages have this Message-ID (diff)
From: arnd@arndb.de (Arnd Bergmann)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 3/8] Add a mfd IPUv3 driver
Date: Mon, 28 Feb 2011 18:11:45 +0100 [thread overview]
Message-ID: <201102281811.45277.arnd@arndb.de> (raw)
In-Reply-To: <1298887229-7987-4-git-send-email-s.hauer@pengutronix.de>
Hi Sascha,
I've had a brief look around the driver. It looks reasonable in general,
but the division into subdrivers feels a bit ad-hoc. If all the components
are built into a single module, I don't see the need for separating the
data by functional units by file. It seems simple enough to turn this
into a layered driver with multiple sub-devices each derived from a
platform_device on its own.
On Monday 28 February 2011, Sascha Hauer wrote:
> arch/arm/plat-mxc/include/mach/ipu-v3.h | 49 +++
> drivers/video/Kconfig | 2 +
> drivers/video/Makefile | 1 +
> drivers/video/imx-ipu-v3/Kconfig | 10 +
> drivers/video/imx-ipu-v3/Makefile | 3 +
> drivers/video/imx-ipu-v3/ipu-common.c | 666 +++++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-cpmem.c | 612 ++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dc.c | 364 +++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-di.c | 550 +++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dmfc.c | 355 ++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dp.c | 476 ++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-prv.h | 216 ++++++++++
> include/video/imx-ipu-v3.h | 219 ++++++++++
I wonder if this is something that would fit better in drivers/gpu instead
of drivers/video. We recently discussed the benefits of KMS vs fb drivers,
and I think it would be good to be prepared for making this a KMS driver
in the long run, even if the immediate target has to be a simple frame buffer
driver.
> +#include "ipu-prv.h"
> +
> +static struct clk *ipu_clk;
> +static struct device *ipu_dev;
> +
> +static DEFINE_SPINLOCK(ipu_lock);
> +static DEFINE_MUTEX(ipu_channel_lock);
> +void __iomem *ipu_cm_reg;
> +void __iomem *ipu_idmac_reg;
> +
> +static int ipu_use_count;
> +
> +static struct ipu_channel channels[64];
Keeping these global limits you to just one ipu, and makes
understanding the code a little harder. How about moving
these variables into a struct ipu_data (or similar) that
is referenced from the platform_device?
> +EXPORT_SYMBOL(ipu_idmac_put);
Why not EXPORT_SYMBOL_GPL?
> +static LIST_HEAD(ipu_irq_handlers);
> +
> +static void ipu_irq_update_irq_mask(void)
> +{
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + DECLARE_IPU_IRQ_BITMAP(irqs);
> +
> + bitmap_zero(irqs, IPU_IRQ_COUNT);
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list)
> + bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
> + ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
> +}
> +
> +int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ipu_lock, flags);
> +
> + list_add_tail(&ipuirq->list, &ipu_irq_handlers);
> + ipu_irq_update_irq_mask();
> +
> + spin_unlock_irqrestore(&ipu_lock, flags);
> + return 0;
> +}
> +EXPORT_SYMBOL(ipu_irq_add_handler);
The interrupt logic needs some comments. What are you trying to achieve here?
> +int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
> +{
> + struct ipu_irq_handler handler;
> + DECLARE_COMPLETION_ONSTACK(completion);
> + int ret;
> +
> + bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
> + bitmap_set(handler.ipu_irqs, interrupt, 1);
> +
> + ipu_cm_write(1 << (interrupt % 32), IPU_INT_STAT(interrupt / 32 + 1));
> +
> + handler.handler = ipu_completion_handler;
> + handler.context = &completion;
> + ipu_irq_add_handler(&handler);
> +
> + ret = wait_for_completion_timeout(&completion,
> + msecs_to_jiffies(timeout_ms));
> +
> + ipu_irq_remove_handler(&handler);
> +
> + if (ret > 0)
> + ret = 0;
> + else
> + ret = -ETIMEDOUT;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(ipu_wait_for_interrupt);
If I understand this correctly, this is a very complicated way to implement
something that could be done with a single wait_event_timeout() and
wake_up_interruptible_all() from the irq handler.
> +static irqreturn_t ipu_irq_handler(int irq, void *desc)
> +{
> + DECLARE_IPU_IRQ_BITMAP(status);
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
> + status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
> + ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
> + }
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list) {
> + DECLARE_IPU_IRQ_BITMAP(tmp);
> + if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
> + handler->handler(tmp, handler->context);
> + }
> +
> + return IRQ_HANDLED;
> +}
This needs to take spin_lock_irqsave before walking the ipu_irq_handlers
list, in order to prevent another CPU from modifying the list
while you are in the handler.
> +static int ipu_reset(void)
> +{
> + int timeout = 10000;
> + u32 val;
> +
> + /* hard reset the IPU */
> + val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> + val |= 1 << 3;
> + writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> +
> + ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
> +
> + while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
> + if (!timeout--)
> + return -ETIME;
> + udelay(100);
> + }
You have a timeout of over one second with udelay, which
is not acceptable on many systems. AFAICT, the function
can sleep, so you can replace udelay with msleep(1), and
you should use a better logic to determine the end of the
loop:
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
if (time_after(jiffies, timeout))
return -ETIME;
msleep(1);
}
> +static u32 *ipu_cpmem_base;
> +static struct device *ipu_dev;
> +
> +struct ipu_ch_param_word {
> + u32 data[5];
> + u32 res[3];
> +};
> +
> +struct ipu_ch_param {
> + struct ipu_ch_param_word word[2];
> +};
Same comment as for the previous file
> +
> +static void __iomem *ipu_dc_reg;
> +static void __iomem *ipu_dc_tmpl_reg;
> +static struct device *ipu_dev;
> +
> +struct ipu_dc {
> + unsigned int di; /* The display interface number assigned to this dc channel */
> + unsigned int channel_offset;
> +};
> +
> +static struct ipu_dc dc_channels[10];
And here again.
> +static void ipu_dc_link_event(int chan, int event, int addr, int priority)
> +{
> + u32 reg;
> +
> + reg = __raw_readl(DC_RL_CH(chan, event));
> + reg &= ~(0xFFFF << (16 * (event & 0x1)));
> + reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
> + __raw_writel(reg, DC_RL_CH(chan, event));
> +}
Better use readl/writel instead of __raw_readl/__raw_writel, throughout the
code.
> +int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
> + u32 module, struct clk *ipu_clk);
> +void ipu_di_exit(struct platform_device *pdev, int id);
> +
> +int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
> + struct clk *ipu_clk);
> +void ipu_dmfc_exit(struct platform_device *pdev);
> +
> +int ipu_dp_init(struct platform_device *pdev, unsigned long base);
> +void ipu_dp_exit(struct platform_device *pdev);
> +
> +int ipu_dc_init(struct platform_device *pdev, unsigned long base,
> + unsigned long template_base);
> +void ipu_dc_exit(struct platform_device *pdev);
> +
> +int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
> +void ipu_cpmem_exit(struct platform_device *pdev);
If you make the main driver an mfd device, the sub-drivers could become
real platform drivers, which can structure the layering in a more modular
way.
E.g. instead of a single module init function, each subdriver can be
a module by itself and look at the resources associated with the
platform device it matches.
Arnd
WARNING: multiple messages have this Message-ID (diff)
From: Arnd Bergmann <arnd@arndb.de>
To: linux-arm-kernel@lists.infradead.org
Cc: Sascha Hauer <s.hauer@pengutronix.de>,
Paul Mundt <lethal@linux-sh.org>,
linux-kernel@vger.kernel.org, linux-fbdev@vger.kernel.org,
Samuel Ortiz <sameo@linux.intel.com>
Subject: Re: [PATCH 3/8] Add a mfd IPUv3 driver
Date: Mon, 28 Feb 2011 18:11:45 +0100 [thread overview]
Message-ID: <201102281811.45277.arnd@arndb.de> (raw)
In-Reply-To: <1298887229-7987-4-git-send-email-s.hauer@pengutronix.de>
Hi Sascha,
I've had a brief look around the driver. It looks reasonable in general,
but the division into subdrivers feels a bit ad-hoc. If all the components
are built into a single module, I don't see the need for separating the
data by functional units by file. It seems simple enough to turn this
into a layered driver with multiple sub-devices each derived from a
platform_device on its own.
On Monday 28 February 2011, Sascha Hauer wrote:
> arch/arm/plat-mxc/include/mach/ipu-v3.h | 49 +++
> drivers/video/Kconfig | 2 +
> drivers/video/Makefile | 1 +
> drivers/video/imx-ipu-v3/Kconfig | 10 +
> drivers/video/imx-ipu-v3/Makefile | 3 +
> drivers/video/imx-ipu-v3/ipu-common.c | 666 +++++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-cpmem.c | 612 ++++++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dc.c | 364 +++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-di.c | 550 +++++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dmfc.c | 355 ++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-dp.c | 476 ++++++++++++++++++++++
> drivers/video/imx-ipu-v3/ipu-prv.h | 216 ++++++++++
> include/video/imx-ipu-v3.h | 219 ++++++++++
I wonder if this is something that would fit better in drivers/gpu instead
of drivers/video. We recently discussed the benefits of KMS vs fb drivers,
and I think it would be good to be prepared for making this a KMS driver
in the long run, even if the immediate target has to be a simple frame buffer
driver.
> +#include "ipu-prv.h"
> +
> +static struct clk *ipu_clk;
> +static struct device *ipu_dev;
> +
> +static DEFINE_SPINLOCK(ipu_lock);
> +static DEFINE_MUTEX(ipu_channel_lock);
> +void __iomem *ipu_cm_reg;
> +void __iomem *ipu_idmac_reg;
> +
> +static int ipu_use_count;
> +
> +static struct ipu_channel channels[64];
Keeping these global limits you to just one ipu, and makes
understanding the code a little harder. How about moving
these variables into a struct ipu_data (or similar) that
is referenced from the platform_device?
> +EXPORT_SYMBOL(ipu_idmac_put);
Why not EXPORT_SYMBOL_GPL?
> +static LIST_HEAD(ipu_irq_handlers);
> +
> +static void ipu_irq_update_irq_mask(void)
> +{
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + DECLARE_IPU_IRQ_BITMAP(irqs);
> +
> + bitmap_zero(irqs, IPU_IRQ_COUNT);
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list)
> + bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
> + ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
> +}
> +
> +int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ipu_lock, flags);
> +
> + list_add_tail(&ipuirq->list, &ipu_irq_handlers);
> + ipu_irq_update_irq_mask();
> +
> + spin_unlock_irqrestore(&ipu_lock, flags);
> + return 0;
> +}
> +EXPORT_SYMBOL(ipu_irq_add_handler);
The interrupt logic needs some comments. What are you trying to achieve here?
> +int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
> +{
> + struct ipu_irq_handler handler;
> + DECLARE_COMPLETION_ONSTACK(completion);
> + int ret;
> +
> + bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
> + bitmap_set(handler.ipu_irqs, interrupt, 1);
> +
> + ipu_cm_write(1 << (interrupt % 32), IPU_INT_STAT(interrupt / 32 + 1));
> +
> + handler.handler = ipu_completion_handler;
> + handler.context = &completion;
> + ipu_irq_add_handler(&handler);
> +
> + ret = wait_for_completion_timeout(&completion,
> + msecs_to_jiffies(timeout_ms));
> +
> + ipu_irq_remove_handler(&handler);
> +
> + if (ret > 0)
> + ret = 0;
> + else
> + ret = -ETIMEDOUT;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(ipu_wait_for_interrupt);
If I understand this correctly, this is a very complicated way to implement
something that could be done with a single wait_event_timeout() and
wake_up_interruptible_all() from the irq handler.
> +static irqreturn_t ipu_irq_handler(int irq, void *desc)
> +{
> + DECLARE_IPU_IRQ_BITMAP(status);
> + struct ipu_irq_handler *handler;
> + int i;
> +
> + for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
> + status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
> + ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
> + }
> +
> + list_for_each_entry(handler, &ipu_irq_handlers, list) {
> + DECLARE_IPU_IRQ_BITMAP(tmp);
> + if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
> + handler->handler(tmp, handler->context);
> + }
> +
> + return IRQ_HANDLED;
> +}
This needs to take spin_lock_irqsave before walking the ipu_irq_handlers
list, in order to prevent another CPU from modifying the list
while you are in the handler.
> +static int ipu_reset(void)
> +{
> + int timeout = 10000;
> + u32 val;
> +
> + /* hard reset the IPU */
> + val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> + val |= 1 << 3;
> + writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> +
> + ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
> +
> + while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
> + if (!timeout--)
> + return -ETIME;
> + udelay(100);
> + }
You have a timeout of over one second with udelay, which
is not acceptable on many systems. AFAICT, the function
can sleep, so you can replace udelay with msleep(1), and
you should use a better logic to determine the end of the
loop:
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
if (time_after(jiffies, timeout))
return -ETIME;
msleep(1);
}
> +static u32 *ipu_cpmem_base;
> +static struct device *ipu_dev;
> +
> +struct ipu_ch_param_word {
> + u32 data[5];
> + u32 res[3];
> +};
> +
> +struct ipu_ch_param {
> + struct ipu_ch_param_word word[2];
> +};
Same comment as for the previous file
> +
> +static void __iomem *ipu_dc_reg;
> +static void __iomem *ipu_dc_tmpl_reg;
> +static struct device *ipu_dev;
> +
> +struct ipu_dc {
> + unsigned int di; /* The display interface number assigned to this dc channel */
> + unsigned int channel_offset;
> +};
> +
> +static struct ipu_dc dc_channels[10];
And here again.
> +static void ipu_dc_link_event(int chan, int event, int addr, int priority)
> +{
> + u32 reg;
> +
> + reg = __raw_readl(DC_RL_CH(chan, event));
> + reg &= ~(0xFFFF << (16 * (event & 0x1)));
> + reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
> + __raw_writel(reg, DC_RL_CH(chan, event));
> +}
Better use readl/writel instead of __raw_readl/__raw_writel, throughout the
code.
> +int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
> + u32 module, struct clk *ipu_clk);
> +void ipu_di_exit(struct platform_device *pdev, int id);
> +
> +int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
> + struct clk *ipu_clk);
> +void ipu_dmfc_exit(struct platform_device *pdev);
> +
> +int ipu_dp_init(struct platform_device *pdev, unsigned long base);
> +void ipu_dp_exit(struct platform_device *pdev);
> +
> +int ipu_dc_init(struct platform_device *pdev, unsigned long base,
> + unsigned long template_base);
> +void ipu_dc_exit(struct platform_device *pdev);
> +
> +int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
> +void ipu_cpmem_exit(struct platform_device *pdev);
If you make the main driver an mfd device, the sub-drivers could become
real platform drivers, which can structure the layering in a more modular
way.
E.g. instead of a single module init function, each subdriver can be
a module by itself and look at the resources associated with the
platform device it matches.
Arnd
next prev parent reply other threads:[~2011-02-28 17:11 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-28 10:00 [PATCH] i.MX51 Framebuffer support Sascha Hauer
2011-02-28 10:00 ` [PATCH 1/8] fb: export fb mode db table Sascha Hauer
2011-02-28 10:00 ` Sascha Hauer
2011-02-28 10:00 ` Sascha Hauer
2011-02-28 10:00 ` [PATCH 2/8] ARM i.MX51: setup mipi Sascha Hauer
2011-02-28 10:00 ` [PATCH 3/8] Add a mfd IPUv3 driver Sascha Hauer
2011-02-28 10:00 ` Sascha Hauer
2011-02-28 17:11 ` Arnd Bergmann [this message]
2011-02-28 17:11 ` Arnd Bergmann
2011-02-28 17:11 ` Arnd Bergmann
2011-03-01 9:15 ` Sascha Hauer
2011-03-01 9:15 ` Sascha Hauer
2011-03-01 9:15 ` Sascha Hauer
2011-03-01 10:27 ` Arnd Bergmann
2011-03-01 10:27 ` Arnd Bergmann
2011-03-01 10:27 ` Arnd Bergmann
2011-03-01 11:12 ` Sascha Hauer
2011-03-01 11:12 ` Sascha Hauer
2011-03-01 11:12 ` Sascha Hauer
2011-03-01 11:43 ` Arnd Bergmann
2011-03-01 11:43 ` Arnd Bergmann
2011-03-01 11:43 ` Arnd Bergmann
2011-03-01 14:09 ` Thomas Gleixner
2011-03-01 14:09 ` Thomas Gleixner
2011-03-01 14:09 ` Thomas Gleixner
2011-02-28 18:33 ` Thomas Gleixner
2011-02-28 18:33 ` Thomas Gleixner
2011-02-28 18:33 ` Thomas Gleixner
2011-03-01 9:39 ` Sascha Hauer
2011-03-01 9:39 ` Sascha Hauer
2011-03-01 9:39 ` Sascha Hauer
2011-03-01 10:00 ` Thomas Gleixner
2011-03-01 10:00 ` Thomas Gleixner
2011-03-01 10:00 ` Thomas Gleixner
2011-03-01 11:31 ` Sascha Hauer
2011-03-01 11:31 ` Sascha Hauer
2011-03-01 11:31 ` Sascha Hauer
2011-02-28 10:00 ` [PATCH 4/8] Add i.MX5 framebuffer driver Sascha Hauer
2011-02-28 10:00 ` Sascha Hauer
2011-02-28 10:00 ` Sascha Hauer
2011-02-28 10:00 ` [PATCH 5/8] ARM i.MX51: Add IPU device support Sascha Hauer
2011-02-28 10:00 ` [PATCH 6/8] ARM i.MX5: Allow to increase max zone order Sascha Hauer
2011-02-28 10:04 ` Russell King - ARM Linux
2011-02-28 10:18 ` Sascha Hauer
2011-02-28 10:00 ` [PATCH 7/8] ARM i.MX5: increase dma consistent size for IPU support Sascha Hauer
2011-02-28 10:00 ` [PATCH 8/8] ARM i.MX51 babbage: Add framebuffer support Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201102281811.45277.arnd@arndb.de \
--to=arnd@arndb.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.